diff options
Diffstat (limited to 'twin/compton-tde/common.h')
-rw-r--r-- | twin/compton-tde/common.h | 2413 |
1 files changed, 2413 insertions, 0 deletions
diff --git a/twin/compton-tde/common.h b/twin/compton-tde/common.h new file mode 100644 index 000000000..275b7b671 --- /dev/null +++ b/twin/compton-tde/common.h @@ -0,0 +1,2413 @@ +/* + * Compton - a compositor for X11 + * + * Based on `xcompmgr` - Copyright (c) 2003, Keith Packard + * + * Copyright (c) 2011-2013, Christopher Jeffrey + * Copyright (c) 2014 Timothy Pearson <kb9vqf@pearsoncomputing.net> + * See LICENSE for more information. + * + */ + +#ifndef COMPTON_COMMON_H +#define COMPTON_COMMON_H + +// === Options === + +// Debug options, enable them using -D in CFLAGS +// #define DEBUG_REPAINT 1 +// #define DEBUG_EVENTS 1 +// #define DEBUG_RESTACK 1 +// #define DEBUG_WINTYPE 1 +// #define DEBUG_CLIENTWIN 1 +// #define DEBUG_WINDATA 1 +// #define DEBUG_WINMATCH 1 +// #define DEBUG_REDIR 1 +// #define DEBUG_ALLOC_REG 1 +// #define DEBUG_FRAME 1 +// #define DEBUG_LEADER 1 +// #define DEBUG_C2 1 +// #define DEBUG_GLX 1 +// #define DEBUG_GLX_GLSL 1 +// #define DEBUG_GLX_ERR 1 +// #define DEBUG_GLX_MARK 1 +// #define DEBUG_GLX_PAINTREG 1 +// #define MONITOR_REPAINT 1 + +// Whether to enable PCRE regular expression support in blacklists, enabled +// by default +// #define CONFIG_REGEX_PCRE 1 +// Whether to enable JIT support of libpcre. This may cause problems on PaX +// kernels. +// #define CONFIG_REGEX_PCRE_JIT 1 +// Whether to enable parsing of configuration files using libconfig. +// #define CONFIG_LIBCONFIG 1 +// Whether we are using a legacy version of libconfig (1.3.x). +// #define CONFIG_LIBCONFIG_LEGACY 1 +// Whether to enable DRM VSync support +// #define CONFIG_VSYNC_DRM 1 +// Whether to enable OpenGL support +// #define CONFIG_VSYNC_OPENGL 1 +// Whether to enable GLX GLSL support +// #define CONFIG_VSYNC_OPENGL_GLSL 1 +// Whether to enable GLX FBO support +// #define CONFIG_VSYNC_OPENGL_FBO 1 +// Whether to enable DBus support with libdbus. +// #define CONFIG_DBUS 1 +// Whether to enable condition support. +// #define CONFIG_C2 1 +// Whether to enable X Sync support. +// #define CONFIG_XSYNC 1 +// Whether to enable GLX Sync support. +// #define CONFIG_GLX_XSYNC 1 + +// TDE specific options +// #define USE_ENV_HOME 1 +#define WRITE_PID_FILE 1 +#define _TDE_COMP_MGR_VERSION_ 3.00 + +#if !defined(CONFIG_C2) && defined(DEBUG_C2) +#error Cannot enable c2 debugging without c2 support. +#endif + +#if (!defined(CONFIG_XSYNC) || !defined(CONFIG_VSYNC_OPENGL)) && defined(CONFIG_GLX_SYNC) +#error Cannot enable GL sync without X Sync / OpenGL support. +#endif + +#ifndef COMPTON_VERSION +#define COMPTON_VERSION "unknown" +#endif + +// === Includes === + +// For some special functions +#define _GNU_SOURCE + +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <inttypes.h> +#include <sys/poll.h> +#include <assert.h> +#include <time.h> +#include <ctype.h> +#include <sys/time.h> + +#include <X11/Xlib.h> +#include <X11/Xutil.h> +#include <X11/Xatom.h> +#include <X11/extensions/Xcomposite.h> +#include <X11/extensions/Xdamage.h> +#include <X11/extensions/Xrender.h> +#include <X11/extensions/shape.h> +#include <X11/extensions/Xrandr.h> +#include <X11/extensions/Xdbe.h> +#ifdef CONFIG_XSYNC +#include <X11/extensions/sync.h> +#endif + +#ifdef CONFIG_XINERAMA +#include <X11/extensions/Xinerama.h> +#endif + +// Workarounds for missing definitions in very old versions of X headers, +// thanks to consolers for reporting +#ifndef PictOpDifference +#define PictOpDifference 0x39 +#endif + +// libconfig +#ifdef CONFIG_LIBCONFIG +#include <libgen.h> +#include <libconfig.h> +#endif + +// libdbus +#ifdef CONFIG_DBUS +#include <dbus/dbus.h> +#endif + +// libGL +#ifdef CONFIG_VSYNC_OPENGL +#if defined(CONFIG_VSYNC_OPENGL_GLSL) || defined(CONFIG_VSYNC_OPENGL_FBO) +#define GL_GLEXT_PROTOTYPES +#endif + +#include <GL/glx.h> + +// Workarounds for missing definitions in some broken GL drivers, thanks to +// douglasp and consolers for reporting +#ifndef GL_TEXTURE_RECTANGLE +#define GL_TEXTURE_RECTANGLE 0x84F5 +#endif + +#ifndef GLX_BACK_BUFFER_AGE_EXT +#define GLX_BACK_BUFFER_AGE_EXT 0x20F4 +#endif + +#endif + +// === Macros === + +#define MSTR_(s) #s +#define MSTR(s) MSTR_(s) + +/// @brief Wrapper for gcc branch prediction builtin, for likely branch. +#define likely(x) __builtin_expect(!!(x), 1) + +/// @brief Wrapper for gcc branch prediction builtin, for unlikely branch. +#define unlikely(x) __builtin_expect(!!(x), 0) + +/// Print out an error message. +#define printf_err(format, ...) \ + fprintf(stderr, format "\n", ## __VA_ARGS__) + +/// Print out an error message with function name. +#define printf_errf(format, ...) \ + printf_err("%s" format, __func__, ## __VA_ARGS__) + +/// Print out an error message with function name, and quit with a +/// specific exit code. +#define printf_errfq(code, format, ...) { \ + printf_err("%s" format, __func__, ## __VA_ARGS__); \ + exit(code); \ +} + +/// Print out a debug message. +#define printf_dbg(format, ...) \ + printf(format, ## __VA_ARGS__); \ + fflush(stdout) + +/// Print out a debug message with function name. +#define printf_dbgf(format, ...) \ + printf_dbg("%s" format, __func__, ## __VA_ARGS__) + +// Use #s here to prevent macro expansion +/// Macro used for shortening some debugging code. +#define CASESTRRET(s) case s: return #s + +// === Constants === +#if !(COMPOSITE_MAJOR > 0 || COMPOSITE_MINOR >= 2) +#error libXcomposite version unsupported +#endif + +/// @brief Length of generic buffers. +#define BUF_LEN 80 + +#define ROUNDED_PERCENT 0.05 +#define ROUNDED_PIXELS 10 + +#define OPAQUE 0xffffffff +#define REGISTER_PROP "_NET_WM_CM_S" + +#define TIME_MS_MAX LONG_MAX +#define FADE_DELTA_TOLERANCE 0.2 +#define SWOPTI_TOLERANCE 3000 +#define TIMEOUT_RUN_TOLERANCE 0.05 +#define WIN_GET_LEADER_MAX_RECURSION 20 + +#define SEC_WRAP (15L * 24L * 60L * 60L) + +#define NS_PER_SEC 1000000000L +#define US_PER_SEC 1000000L +#define MS_PER_SEC 1000 + +#define XRFILTER_CONVOLUTION "convolution" +#define XRFILTER_GUASSIAN "gaussian" +#define XRFILTER_BINOMIAL "binomial" + +/// @brief Maximum OpenGL FBConfig depth. +#define OPENGL_MAX_DEPTH 32 + +/// @brief Maximum OpenGL buffer age. +#define CGLX_MAX_BUFFER_AGE 5 + +/// @brief Maximum passes for blur. +#define MAX_BLUR_PASS 5 + +// Window flags + +// Window size is changed +#define WFLAG_SIZE_CHANGE 0x0001 +// Window size/position is changed +#define WFLAG_POS_CHANGE 0x0002 +// Window opacity / dim state changed +#define WFLAG_OPCT_CHANGE 0x0004 + +// === Types === + +typedef uint32_t opacity_t; +typedef long time_ms_t; + +typedef enum { + WINTYPE_UNKNOWN, + WINTYPE_DESKTOP, + WINTYPE_DOCK, + WINTYPE_TOOLBAR, + WINTYPE_MENU, + WINTYPE_UTILITY, + WINTYPE_SPLASH, + WINTYPE_DIALOG, + WINTYPE_NORMAL, + WINTYPE_DROPDOWN_MENU, + WINTYPE_POPUP_MENU, + WINTYPE_TOOLTIP, + WINTYPE_NOTIFY, + WINTYPE_COMBO, + WINTYPE_DND, + NUM_WINTYPES +} wintype_t; + +/// Enumeration type to represent switches. +typedef enum { + OFF, // false + ON, // true + UNSET +} switch_t; + +/// Structure representing a X geometry. +typedef struct { + int wid; + int hei; + int x; + int y; +} geometry_t; + +/// Enumeration type of window painting mode. +typedef enum { + WMODE_TRANS, + WMODE_SOLID, + WMODE_ARGB +} winmode_t; + +/// Structure representing needed window updates. +typedef struct { + bool shadow : 1; + bool fade : 1; + bool focus : 1; + bool invert_color : 1; +} win_upd_t; + +/// Structure representing Window property value. +typedef struct { + // All pointers have the same length, right? + // I wanted to use anonymous union but it's a GNU extension... + union { + unsigned char *p8; + short *p16; + long *p32; + } data; + unsigned long nitems; + Atom type; + int format; +} winprop_t; + +typedef struct _ignore { + struct _ignore *next; + unsigned long sequence; +} ignore_t; + +enum wincond_target { + CONDTGT_NAME, + CONDTGT_CLASSI, + CONDTGT_CLASSG, + CONDTGT_ROLE, +}; + +enum wincond_type { + CONDTP_EXACT, + CONDTP_ANYWHERE, + CONDTP_FROMSTART, + CONDTP_WILDCARD, + CONDTP_REGEX_PCRE, +}; + +#define CONDF_IGNORECASE 0x0001 + +/// VSync modes. +typedef enum { + VSYNC_NONE, + VSYNC_DRM, + VSYNC_OPENGL, + VSYNC_OPENGL_OML, + VSYNC_OPENGL_SWC, + VSYNC_OPENGL_MSWC, + NUM_VSYNC, +} vsync_t; + +/// @brief Possible backends of compton. +enum backend { + BKEND_XRENDER, + BKEND_GLX, + BKEND_XR_GLX_HYBRID, + NUM_BKEND, +}; + +/// @brief Possible swap methods. +enum { + SWAPM_BUFFER_AGE = -1, + SWAPM_UNDEFINED = 0, + SWAPM_COPY = 1, + SWAPM_EXCHANGE = 2, +}; + +typedef struct _glx_texture glx_texture_t; + +#ifdef CONFIG_VSYNC_OPENGL +#ifdef DEBUG_GLX_DEBUG_CONTEXT +typedef GLXContext (*f_glXCreateContextAttribsARB) (Display *dpy, + GLXFBConfig config, GLXContext share_context, Bool direct, + const int *attrib_list); +typedef void (*GLDEBUGPROC) (GLenum source, GLenum type, + GLuint id, GLenum severity, GLsizei length, const GLchar* message, + GLvoid* userParam); +typedef void (*f_DebugMessageCallback) (GLDEBUGPROC, void *userParam); +#endif + +typedef int (*f_WaitVideoSync) (int, int, unsigned *); +typedef int (*f_GetVideoSync) (unsigned *); + +typedef Bool (*f_GetSyncValuesOML) (Display* dpy, GLXDrawable drawable, int64_t* ust, int64_t* msc, int64_t* sbc); +typedef Bool (*f_WaitForMscOML) (Display* dpy, GLXDrawable drawable, int64_t target_msc, int64_t divisor, int64_t remainder, int64_t* ust, int64_t* msc, int64_t* sbc); + +typedef int (*f_SwapIntervalSGI) (int interval); +typedef int (*f_SwapIntervalMESA) (unsigned int interval); + +typedef void (*f_BindTexImageEXT) (Display *display, GLXDrawable drawable, int buffer, const int *attrib_list); +typedef void (*f_ReleaseTexImageEXT) (Display *display, GLXDrawable drawable, int buffer); + +typedef void (*f_CopySubBuffer) (Display *dpy, GLXDrawable drawable, int x, int y, int width, int height); + +#ifdef CONFIG_GLX_SYNC +// Looks like duplicate typedef of the same type is safe? +typedef int64_t GLint64; +typedef uint64_t GLuint64; +typedef struct __GLsync *GLsync; + +#ifndef GL_SYNC_FLUSH_COMMANDS_BIT +#define GL_SYNC_FLUSH_COMMANDS_BIT 0x00000001 +#endif + +#ifndef GL_TIMEOUT_IGNORED +#define GL_TIMEOUT_IGNORED 0xFFFFFFFFFFFFFFFFull +#endif + +#ifndef GL_ALREADY_SIGNALED +#define GL_ALREADY_SIGNALED 0x911A +#endif + +#ifndef GL_TIMEOUT_EXPIRED +#define GL_TIMEOUT_EXPIRED 0x911B +#endif + +#ifndef GL_CONDITION_SATISFIED +#define GL_CONDITION_SATISFIED 0x911C +#endif + +#ifndef GL_WAIT_FAILED +#define GL_WAIT_FAILED 0x911D +#endif + +typedef GLsync (*f_FenceSync) (GLenum condition, GLbitfield flags); +typedef GLboolean (*f_IsSync) (GLsync sync); +typedef void (*f_DeleteSync) (GLsync sync); +typedef GLenum (*f_ClientWaitSync) (GLsync sync, GLbitfield flags, + GLuint64 timeout); +typedef void (*f_WaitSync) (GLsync sync, GLbitfield flags, + GLuint64 timeout); +typedef GLsync (*f_ImportSyncEXT) (GLenum external_sync_type, + GLintptr external_sync, GLbitfield flags); +#endif + +#ifdef DEBUG_GLX_MARK +typedef void (*f_StringMarkerGREMEDY) (GLsizei len, const void *string); +typedef void (*f_FrameTerminatorGREMEDY) (void); +#endif + +/// @brief Wrapper of a GLX FBConfig. +typedef struct { + GLXFBConfig cfg; + GLint texture_fmt; + GLint texture_tgts; + bool y_inverted; +} glx_fbconfig_t; + +/// @brief Wrapper of a binded GLX texture. +struct _glx_texture { + GLuint texture; + GLXPixmap glpixmap; + Pixmap pixmap; + GLenum target; + unsigned width; + unsigned height; + unsigned depth; + bool y_inverted; +}; +#endif + +#ifdef CONFIG_VSYNC_OPENGL_GLSL +typedef struct { + /// Fragment shader for blur. + GLuint frag_shader; + /// GLSL program for blur. + GLuint prog; + /// Location of uniform "offset_x" in blur GLSL program. + GLint unifm_offset_x; + /// Location of uniform "offset_y" in blur GLSL program. + GLint unifm_offset_y; + /// Location of uniform "factor_center" in blur GLSL program. + GLint unifm_factor_center; +} glx_blur_pass_t; + +typedef struct { + /// Framebuffer used for blurring. + GLuint fbo; + /// Textures used for blurring. + GLuint textures[2]; + /// Width of the textures. + int width; + /// Height of the textures. + int height; +} glx_blur_cache_t; +#endif + +typedef struct { + Pixmap pixmap; + Picture pict; + glx_texture_t *ptex; +} paint_t; + +#define PAINT_INIT { .pixmap = None, .pict = None } + +typedef struct { + int size; + double *data; +} conv; + +/// Linked list type of atoms. +typedef struct _latom { + Atom atom; + struct _latom *next; +} latom_t; + +/// A representation of raw region data +typedef struct { + XRectangle *rects; + int nrects; +} reg_data_t; + +#define REG_DATA_INIT { NULL, 0 } + +struct _timeout_t; + +struct _win; + +typedef struct _c2_lptr c2_lptr_t; + +/// Structure representing all options. +typedef struct _options_t { + // === General === + /// The configuration file we used. + char *config_file; + /// Path to write PID to. + char *write_pid_path; + /// The display name we used. NULL means we are using the value of the + /// <code>DISPLAY</code> environment variable. + char *display; + /// Safe representation of display name. + char *display_repr; + /// The backend in use. + enum backend backend; + /// Whether to sync X drawing to avoid certain delay issues with + /// GLX backend. + bool xrender_sync; + /// Whether to sync X drawing with X Sync fence. + bool xrender_sync_fence; + /// Whether to avoid using stencil buffer under GLX backend. Might be + /// unsafe. + bool glx_no_stencil; + /// Whether to copy unmodified regions from front buffer. + bool glx_copy_from_front; + /// Whether to use glXCopySubBufferMESA() to update screen. + bool glx_use_copysubbuffermesa; + /// Whether to avoid rebinding pixmap on window damage. + bool glx_no_rebind_pixmap; + /// GLX swap method we assume OpenGL uses. + int glx_swap_method; + /// Whether to use GL_EXT_gpu_shader4 to (hopefully) accelerates blurring. + bool glx_use_gpushader4; + /// Whether to try to detect WM windows and mark them as focused. + bool mark_wmwin_focused; + /// Whether to mark override-redirect windows as focused. + bool mark_ovredir_focused; + /// Whether to fork to background. + bool fork_after_register; + /// Whether to detect rounded corners. + bool detect_rounded_corners; + /// Whether to paint on X Composite overlay window instead of root + /// window. + bool paint_on_overlay; + /// Resize damage for a specific number of pixels. + int resize_damage; + /// Whether to unredirect all windows if a full-screen opaque window + /// is detected. + bool unredir_if_possible; + /// List of conditions of windows to ignore as a full-screen window + /// when determining if a window could be unredirected. + c2_lptr_t *unredir_if_possible_blacklist; + /// Delay before unredirecting screen. + time_ms_t unredir_if_possible_delay; + /// Forced redirection setting through D-Bus. + switch_t redirected_force; + /// Whether to stop painting. Controlled through D-Bus. + switch_t stoppaint_force; + /// Whether to enable D-Bus support. + bool dbus; + /// Path to log file. + char *logpath; + /// Number of cycles to paint in benchmark mode. 0 for disabled. + int benchmark; + /// Window to constantly repaint in benchmark mode. 0 for full-screen. + Window benchmark_wid; + /// A list of conditions of windows not to paint. + c2_lptr_t *paint_blacklist; + /// Whether to work under synchronized mode for debugging. + bool synchronize; + + // === VSync & software optimization === + /// User-specified refresh rate. + int refresh_rate; + /// Whether to enable refresh-rate-based software optimization. + bool sw_opti; + /// VSync method to use; + vsync_t vsync; + /// Whether to enable double buffer. + bool dbe; + /// Whether to do VSync aggressively. + bool vsync_aggressive; + /// Whether to use glFinish() instead of glFlush() for (possibly) better + /// VSync yet probably higher CPU usage. + bool vsync_use_glfinish; + + // === Shadow === + /// Enable/disable shadow for specific window types. + bool wintype_shadow[NUM_WINTYPES]; + /// Red, green and blue tone of the shadow. + double shadow_red, shadow_green, shadow_blue; + int shadow_radius; + int shadow_offset_x, shadow_offset_y; + double shadow_opacity; + bool clear_shadow; + /// Geometry of a region in which shadow is not painted on. + geometry_t shadow_exclude_reg_geom; + /// Shadow blacklist. A linked list of conditions. + c2_lptr_t *shadow_blacklist; + /// Whether bounding-shaped window should be ignored. + bool shadow_ignore_shaped; + /// Whether to respect _TDE_WM_WINDOW_SHADOW. + bool respect_prop_shadow; + /// Whether to crop shadow to the very Xinerama screen. + bool xinerama_shadow_crop; + + // === Fading === + /// Enable/disable fading for specific window types. + bool wintype_fade[NUM_WINTYPES]; + /// How much to fade in in a single fading step. + opacity_t fade_in_step; + /// How much to fade out in a single fading step. + opacity_t fade_out_step; + /// Fading time delta. In milliseconds. + time_ms_t fade_delta; + /// Whether to disable fading on window open/close. + bool no_fading_openclose; + /// Whether to disable fading on opacity change + bool no_fading_opacitychange; + /// Fading blacklist. A linked list of conditions. + c2_lptr_t *fade_blacklist; + + // === Opacity === + /// Default opacity for specific window types + double wintype_opacity[NUM_WINTYPES]; + /// Default opacity for inactive windows. + /// 32-bit integer with the format of _NET_WM_OPACITY. 0 stands for + /// not enabled, default. + opacity_t inactive_opacity; + /// Default opacity for inactive windows. + opacity_t active_opacity; + /// Whether inactive_opacity overrides the opacity set by window + /// attributes. + bool inactive_opacity_override; + /// Frame opacity. Relative to window opacity, also affects shadow + /// opacity. + double frame_opacity; + /// Whether to detect _NET_WM_OPACITY on client windows. Used on window + /// managers that don't pass _NET_WM_OPACITY to frame windows. + bool detect_client_opacity; + /// Step for pregenerating alpha pictures. 0.01 - 1.0. + double alpha_step; + + // === Other window processing === + /// Whether to blur background of semi-transparent / ARGB windows. + bool blur_background; + /// Whether to blur background when the window frame is not opaque. + /// Implies blur_background. + bool blur_background_frame; + /// Whether to use fixed blur strength instead of adjusting according + /// to window opacity. + bool blur_background_fixed; + /// Background blur blacklist. A linked list of conditions. + c2_lptr_t *blur_background_blacklist; + /// Blur convolution kernel. + XFixed *blur_kerns[MAX_BLUR_PASS]; + /// How much to dim an inactive window. 0.0 - 1.0, 0 to disable. + double inactive_dim; + /// Whether to use fixed inactive dim opacity, instead of deciding + /// based on window opacity. + bool inactive_dim_fixed; + /// Conditions of windows to have inverted colors. + c2_lptr_t *invert_color_list; + /// Rules to change window opacity. + c2_lptr_t *opacity_rules; + + // === Focus related === + /// Consider windows of specific types to be always focused. + bool wintype_focus[NUM_WINTYPES]; + /// Whether to use EWMH _NET_ACTIVE_WINDOW to find active window. + bool use_ewmh_active_win; + /// A list of windows always to be considered focused. + c2_lptr_t *focus_blacklist; + /// Whether to do window grouping with <code>WM_TRANSIENT_FOR</code>. + bool detect_transient; + /// Whether to do window grouping with <code>WM_CLIENT_LEADER</code>. + bool detect_client_leader; + + // === Calculated === + /// Whether compton needs to track focus changes. + bool track_focus; + /// Whether compton needs to track window name and class. + bool track_wdata; + /// Whether compton needs to track window leaders. + bool track_leader; +} options_t; + +/// Structure containing all necessary data for a compton session. +typedef struct _session_t { + // === Display related === + /// Display in use. + Display *dpy; + /// Default screen. + int scr; + /// Default visual. + Visual *vis; + /// Default depth. + int depth; + /// Root window. + Window root; + /// Height of root window. + int root_height; + /// Width of root window. + int root_width; + // Damage of root window. + // Damage root_damage; + /// X Composite overlay window. Used if <code>--paint-on-overlay</code>. + Window overlay; + /// Whether the root tile is filled by compton. + bool root_tile_fill; + /// Picture of the root window background. + paint_t root_tile_paint; + /// A region of the size of the screen. + XserverRegion screen_reg; + /// Picture of root window. Destination of painting in no-DBE painting + /// mode. + Picture root_picture; + /// A Picture acting as the painting target. + Picture tgt_picture; + /// Temporary buffer to paint to before sending to display. + paint_t tgt_buffer; +#ifdef CONFIG_XSYNC + XSyncFence tgt_buffer_fence; +#endif + /// DBE back buffer for root window. Used in DBE painting mode. + XdbeBackBuffer root_dbe; + /// Window ID of the window we register as a symbol. + Window reg_win; + + // === Operation related === + /// Program options. + options_t o; + /// File descriptors to check for reading. + fd_set *pfds_read; + /// File descriptors to check for writing. + fd_set *pfds_write; + /// File descriptors to check for exceptions. + fd_set *pfds_except; + /// Largest file descriptor in fd_set-s above. + int nfds_max; + /// Linked list of all timeouts. + struct _timeout_t *tmout_lst; + /// Timeout for delayed unredirection. + struct _timeout_t *tmout_unredir; + /// Whether we have hit unredirection timeout. + bool tmout_unredir_hit; + /// Whether we have received an event in this cycle. + bool ev_received; + /// Whether the program is idling. I.e. no fading, no potential window + /// changes. + bool idling; + /// Program start time. + struct timeval time_start; + /// The region needs to painted on next paint. + XserverRegion all_damage; + /// The region damaged on the last paint. + XserverRegion all_damage_last[CGLX_MAX_BUFFER_AGE]; + /// Whether all windows are currently redirected. + bool redirected; + /// Pre-generated alpha pictures. + Picture *alpha_picts; + /// Whether all reg_ignore of windows should expire in this paint. + bool reg_ignore_expire; + /// Time of last fading. In milliseconds. + time_ms_t fade_time; + /// Head pointer of the error ignore linked list. + ignore_t *ignore_head; + /// Pointer to the <code>next</code> member of tail element of the error + /// ignore linked list. + ignore_t **ignore_tail; +#ifdef CONFIG_VSYNC_OPENGL + /// Current GLX Z value. + int glx_z; +#endif + // Cached blur convolution kernels. + XFixed *blur_kerns_cache[MAX_BLUR_PASS]; + /// Reset program after next paint. + bool reset; + + // === Expose event related === + /// Pointer to an array of <code>XRectangle</code>-s of exposed region. + XRectangle *expose_rects; + /// Number of <code>XRectangle</code>-s in <code>expose_rects</code>. + int size_expose; + /// Index of the next free slot in <code>expose_rects</code>. + int n_expose; + + // === Window related === + /// Linked list of all windows. + struct _win *list; + /// Pointer to <code>win</code> of current active window. Used by + /// EWMH <code>_NET_ACTIVE_WINDOW</code> focus detection. In theory, + /// it's more reliable to store the window ID directly here, just in + /// case the WM does something extraordinary, but caching the pointer + /// means another layer of complexity. + struct _win *active_win; + /// Window ID of leader window of currently active window. Used for + /// subsidiary window detection. + Window active_leader; + + // === Shadow/dimming related === + /// 1x1 black Picture. + Picture black_picture; + /// 1x1 Picture of the shadow color. + Picture cshadow_picture; + /// 1x1 white Picture. + Picture white_picture; + /// Gaussian map of shadow. + conv *gaussian_map; + // for shadow precomputation + /// Shadow depth on one side. + int cgsize; + /// Pre-computed color table for corners of shadow. + unsigned char *shadow_corner; + /// Pre-computed color table for a side of shadow. + unsigned char *shadow_top; + /// A region in which shadow is not painted on. + XserverRegion shadow_exclude_reg; + + // === Software-optimization-related === + /// Currently used refresh rate. + short refresh_rate; + /// Interval between refresh in nanoseconds. + long refresh_intv; + /// Nanosecond offset of the first painting. + long paint_tm_offset; + +#ifdef CONFIG_VSYNC_DRM + // === DRM VSync related === + /// File descriptor of DRI device file. Used for DRM VSync. + int drm_fd; +#endif + +#ifdef CONFIG_VSYNC_OPENGL + // === OpenGL related === + /// GLX context. + GLXContext glx_context; + /// Whether we have GL_ARB_texture_non_power_of_two. + bool glx_has_texture_non_power_of_two; + /// Pointer to glXGetVideoSyncSGI function. + f_GetVideoSync glXGetVideoSyncSGI; + /// Pointer to glXWaitVideoSyncSGI function. + f_WaitVideoSync glXWaitVideoSyncSGI; + /// Pointer to glXGetSyncValuesOML function. + f_GetSyncValuesOML glXGetSyncValuesOML; + /// Pointer to glXWaitForMscOML function. + f_WaitForMscOML glXWaitForMscOML; + /// Pointer to glXSwapIntervalSGI function. + f_SwapIntervalSGI glXSwapIntervalProc; + /// Pointer to glXSwapIntervalMESA function. + f_SwapIntervalMESA glXSwapIntervalMESAProc; + /// Pointer to glXBindTexImageEXT function. + f_BindTexImageEXT glXBindTexImageProc; + /// Pointer to glXReleaseTexImageEXT function. + f_ReleaseTexImageEXT glXReleaseTexImageProc; + /// Pointer to glXCopySubBufferMESA function. + f_CopySubBuffer glXCopySubBufferProc; +#ifdef CONFIG_GLX_SYNC + /// Pointer to the glFenceSync() function. + f_FenceSync glFenceSyncProc; + /// Pointer to the glIsSync() function. + f_IsSync glIsSyncProc; + /// Pointer to the glDeleteSync() function. + f_DeleteSync glDeleteSyncProc; + /// Pointer to the glClientWaitSync() function. + f_ClientWaitSync glClientWaitSyncProc; + /// Pointer to the glWaitSync() function. + f_WaitSync glWaitSyncProc; + /// Pointer to the glImportSyncEXT() function. + f_ImportSyncEXT glImportSyncEXT; +#endif +#ifdef DEBUG_GLX_MARK + /// Pointer to StringMarkerGREMEDY function. + f_StringMarkerGREMEDY glStringMarkerGREMEDY; + /// Pointer to FrameTerminatorGREMEDY function. + f_FrameTerminatorGREMEDY glFrameTerminatorGREMEDY; +#endif + /// FBConfig-s for GLX pixmap of different depths. + glx_fbconfig_t *glx_fbconfigs[OPENGL_MAX_DEPTH + 1]; +#ifdef CONFIG_VSYNC_OPENGL_GLSL + glx_blur_pass_t glx_blur_passes[MAX_BLUR_PASS]; +#endif +#endif + + // === X extension related === + /// Event base number for X Fixes extension. + int xfixes_event; + /// Error base number for X Fixes extension. + int xfixes_error; + /// Event base number for X Damage extension. + int damage_event; + /// Error base number for X Damage extension. + int damage_error; + /// Event base number for X Render extension. + int render_event; + /// Error base number for X Render extension. + int render_error; + /// Event base number for X Composite extension. + int composite_event; + /// Error base number for X Composite extension. + int composite_error; + /// Major opcode for X Composite extension. + int composite_opcode; + /// Whether X Composite NameWindowPixmap is available. Aka if X + /// Composite version >= 0.2. + bool has_name_pixmap; + /// Whether X Shape extension exists. + bool shape_exists; + /// Event base number for X Shape extension. + int shape_event; + /// Error base number for X Shape extension. + int shape_error; + /// Whether X RandR extension exists. + bool randr_exists; + /// Event base number for X RandR extension. + int randr_event; + /// Error base number for X RandR extension. + int randr_error; +#ifdef CONFIG_VSYNC_OPENGL + /// Whether X GLX extension exists. + bool glx_exists; + /// Event base number for X GLX extension. + int glx_event; + /// Error base number for X GLX extension. + int glx_error; +#endif + /// Whether X DBE extension exists. + bool dbe_exists; +#ifdef CONFIG_XINERAMA + /// Whether X Xinerama extension exists. + bool xinerama_exists; + /// Xinerama screen info. + XineramaScreenInfo *xinerama_scrs; + /// Xinerama screen regions. + XserverRegion *xinerama_scr_regs; + /// Number of Xinerama screens. + int xinerama_nscrs; +#endif +#ifdef CONFIG_XSYNC + /// Whether X Sync extension exists. + bool xsync_exists; + /// Event base number for X Sync extension. + int xsync_event; + /// Error base number for X Sync extension. + int xsync_error; +#endif + /// Whether X Render convolution filter exists. + bool xrfilter_convolution_exists; + + // === Atoms === + /// Atom of property <code>_NET_WM_OPACITY</code>. + Atom atom_opacity; + /// Atom of <code>_NET_FRAME_EXTENTS</code>. + Atom atom_frame_extents; + /// Property atom to identify top-level frame window. Currently + /// <code>WM_STATE</code>. + Atom atom_client; + /// Atom of property <code>WM_NAME</code>. + Atom atom_name; + /// Atom of property <code>_NET_WM_NAME</code>. + Atom atom_name_ewmh; + /// Atom of property <code>WM_CLASS</code>. + Atom atom_class; + /// Atom of property <code>WM_WINDOW_ROLE</code>. + Atom atom_role; + /// Atom of property <code>WM_TRANSIENT_FOR</code>. + Atom atom_transient; + /// Atom of property <code>WM_CLIENT_LEADER</code>. + Atom atom_client_leader; + /// Atom of property <code>_NET_ACTIVE_WINDOW</code>. + Atom atom_ewmh_active_win; + /// Atom of property <code>_TDE_WM_WINDOW_SHADOW</code>. + Atom atom_compton_shadow; + /// Atom of property <code>_NET_WM_WINDOW_TYPE</code>. + Atom atom_win_type; + /// Atom of property <code>_KDE_TRANSPARENT_TO_BLACK</code>. + Atom atom_win_type_tde_transparent_to_black; + /// Atom of property <code>_KDE_TRANSPARENT_TO_DESKTOP</code>. + Atom atom_win_type_tde_transparent_to_desktop; + /// Array of atoms of all possible window types. + Atom atoms_wintypes[NUM_WINTYPES]; + /// Linked list of additional atoms to track. + latom_t *track_atom_lst; + +#ifdef CONFIG_DBUS + // === DBus related === + // DBus connection. + DBusConnection *dbus_conn; + // DBus service name. + char *dbus_service; +#endif +} session_t; + +/// Structure representing a top-level window compton manages. +typedef struct _win { + /// Pointer to the next structure in the linked list. + struct _win *next; + /// Pointer to the next higher window to paint. + struct _win *prev_trans; + + // Core members + /// ID of the top-level frame window. + Window id; + /// Window attributes. + XWindowAttributes a; +#ifdef CONFIG_XINERAMA + /// Xinerama screen this window is on. + int xinerama_scr; +#endif + /// Window visual pict format; + XRenderPictFormat *pictfmt; + /// Window painting mode. + winmode_t mode; + /// Whether the window has been damaged at least once. + bool damaged; +#ifdef CONFIG_XSYNC + /// X Sync fence of drawable. + XSyncFence fence; +#endif + /// Whether the window was damaged after last paint. + bool pixmap_damaged; + /// Damage of the window. + Damage damage; + /// Paint info of the window. + paint_t paint; + /// Bounding shape of the window. + XserverRegion border_size; + /// Region of the whole window, shadow region included. + XserverRegion extents; + /// Window flags. Definitions above. + int_fast16_t flags; + /// Whether there's a pending <code>ConfigureNotify</code> happening + /// when the window is unmapped. + bool need_configure; + /// Queued <code>ConfigureNotify</code> when the window is unmapped. + XConfigureEvent queue_configure; + /// Region to be ignored when painting. Basically the region where + /// higher opaque windows will paint upon. Depends on window frame + /// opacity state, window geometry, window mapped/unmapped state, + /// window mode, of this and all higher windows. + XserverRegion reg_ignore; + /// Cached width/height of the window including border. + int widthb, heightb; + /// Whether the window has been destroyed. + bool destroyed; + /// Whether the window is bounding-shaped. + bool bounding_shaped; + /// Whether the window just have rounded corners. + bool rounded_corners; + /// Whether this window is to be painted. + bool to_paint; + /// Whether the window is painting excluded. + bool paint_excluded; + /// Whether the window is unredirect-if-possible excluded. + bool unredir_if_possible_excluded; + /// Whether this window is in open/close state. + bool in_openclose; + + // Client window related members + /// ID of the top-level client window of the window. + Window client_win; + /// Type of the window. + wintype_t window_type; + /// Whether it looks like a WM window. We consider a window WM window if + /// it does not have a decedent with WM_STATE and it is not override- + /// redirected itself. + bool wmwin; + /// Leader window ID of the window. + Window leader; + /// Cached topmost window ID of the window. + Window cache_leader; + + // Focus-related members + /// Whether the window is to be considered focused. + bool focused; + /// Override value of window focus state. Set by D-Bus method calls. + switch_t focused_force; + + // Blacklist related members + /// Name of the window. + char *name; + /// Window instance class of the window. + char *class_instance; + /// Window general class of the window. + char *class_general; + /// <code>WM_WINDOW_ROLE</code> value of the window. + char *role; + const c2_lptr_t *cache_sblst; + const c2_lptr_t *cache_fblst; + const c2_lptr_t *cache_fcblst; + const c2_lptr_t *cache_ivclst; + const c2_lptr_t *cache_bbblst; + const c2_lptr_t *cache_oparule; + const c2_lptr_t *cache_pblst; + const c2_lptr_t *cache_uipblst; + + // Opacity-related members + /// Current window opacity. + opacity_t opacity; + /// Target window opacity. + opacity_t opacity_tgt; + /// Cached value of opacity window attribute. + opacity_t opacity_prop; + /// Cached value of opacity window attribute on client window. For + /// broken window managers not transferring client window's + /// _NET_WM_OPACITY value + opacity_t opacity_prop_client; + /// Last window opacity value we set. + opacity_t opacity_set; + + // Fading-related members + /// Do not fade if it's false. Change on window type change. + /// Used by fading blacklist in the future. + bool fade; + /// Override value of window fade state. Set by D-Bus method calls. + switch_t fade_force; + /// Callback to be called after fading completed. + void (*fade_callback) (session_t *ps, struct _win *w); + + // Frame-opacity-related members + /// Current window frame opacity. Affected by window opacity. + double frame_opacity; + /// Frame widths. Determined by client window attributes. + unsigned int left_width, right_width, top_width, bottom_width; + + // Shadow-related members + /// Whether a window has shadow. Calculated. + bool shadow; + /// Override value of window shadow state. Set by D-Bus method calls. + switch_t shadow_force; + /// Opacity of the shadow. Affected by window opacity and frame opacity. + double shadow_opacity; + /// X offset of shadow. Affected by commandline argument. + int shadow_dx; + /// Y offset of shadow. Affected by commandline argument. + int shadow_dy; + /// Width of shadow. Affected by window size and commandline argument. + int shadow_width; + /// Height of shadow. Affected by window size and commandline argument. + int shadow_height; + /// Relative size of shadow. + int shadow_size; + /// Picture to render shadow. Affected by window size. + paint_t shadow_paint; + /// The value of _TDE_WM_WINDOW_SHADOW attribute of the window. Below 0 for + /// none. + long prop_shadow; + + // Dim-related members + /// Whether the window is to be dimmed. + bool dim; + + /// Whether to invert window color. + bool invert_color; + /// Override value of window color inversion state. Set by D-Bus method + /// calls. + switch_t invert_color_force; + + /// Whether to blur window background. + bool blur_background; + + /// Whether to show black background + bool show_black_background; + + /// Whether to show desktop background + bool show_root_tile; + +#ifdef CONFIG_VSYNC_OPENGL_GLSL + /// Textures and FBO background blur use. + glx_blur_cache_t glx_blur_cache; +#endif +} win; + +/// Temporary structure used for communication between +/// <code>get_cfg()</code> and <code>parse_config()</code>. +struct options_tmp { + bool no_dock_shadow; + bool no_dnd_shadow; + double menu_opacity; +}; + +/// Structure for a recorded timeout. +typedef struct _timeout_t { + bool enabled; + void *data; + bool (*callback)(session_t *ps, struct _timeout_t *ptmout); + time_ms_t interval; + time_ms_t firstrun; + time_ms_t lastrun; + struct _timeout_t *next; +} timeout_t; + +/// Enumeration for window event hints. +typedef enum { + WIN_EVMODE_UNKNOWN, + WIN_EVMODE_FRAME, + WIN_EVMODE_CLIENT +} win_evmode_t; + +extern const char * const WINTYPES[NUM_WINTYPES]; +extern const char * const VSYNC_STRS[NUM_VSYNC + 1]; +extern const char * const BACKEND_STRS[NUM_BKEND + 1]; +extern session_t *ps_g; + +// == Debugging code == +static inline void +print_timestamp(session_t *ps); + +#ifdef DEBUG_ALLOC_REG + +#include <execinfo.h> +#define BACKTRACE_SIZE 5 + +/** + * Print current backtrace, excluding the first two items. + * + * Stolen from glibc manual. + */ +static inline void +print_backtrace(void) { + void *array[BACKTRACE_SIZE]; + size_t size; + char **strings; + + size = backtrace(array, BACKTRACE_SIZE); + strings = backtrace_symbols(array, size); + + for (size_t i = 2; i < size; i++) + printf ("%s\n", strings[i]); + + free(strings); +} + +/** + * Wrapper of <code>XFixesCreateRegion</code>, for debugging. + */ +static inline XserverRegion +XFixesCreateRegion_(Display *dpy, XRectangle *p, int n, + const char *func, int line) { + XserverRegion reg = XFixesCreateRegion(dpy, p, n); + print_timestamp(ps_g); + printf("%#010lx: XFixesCreateRegion() in %s():%d\n", reg, func, line); + print_backtrace(); + fflush(stdout); + return reg; +} + +/** + * Wrapper of <code>XFixesDestroyRegion</code>, for debugging. + */ +static inline void +XFixesDestroyRegion_(Display *dpy, XserverRegion reg, + const char *func, int line) { + XFixesDestroyRegion(dpy, reg); + print_timestamp(ps_g); + printf("%#010lx: XFixesDestroyRegion() in %s():%d\n", reg, func, line); + fflush(stdout); +} + +#define XFixesCreateRegion(dpy, p, n) XFixesCreateRegion_(dpy, p, n, __func__, __LINE__) +#define XFixesDestroyRegion(dpy, reg) XFixesDestroyRegion_(dpy, reg, __func__, __LINE__) +#endif + +// === Functions === + +/** + * @brief Quit if the passed-in pointer is empty. + */ +static inline void * +allocchk_(const char *func_name, void *ptr) { + if (!ptr) { + printf_err("%s(): Failed to allocate memory.", func_name); + exit(1); + } + return ptr; +} + +/// @brief Wrapper of allocchk_(). +#define allocchk(ptr) allocchk_(__func__, ptr) + +/// @brief Wrapper of malloc(). +#define cmalloc(nmemb, type) ((type *) allocchk(malloc((nmemb) * sizeof(type)))) + +/// @brief Wrapper of calloc(). +#define ccalloc(nmemb, type) ((type *) allocchk(calloc((nmemb), sizeof(type)))) + +/// @brief Wrapper of ealloc(). +#define crealloc(ptr, nmemb, type) ((type *) allocchk(realloc((ptr), (nmemb) * sizeof(type)))) + +/** + * Return whether a struct timeval value is empty. + */ +static inline bool +timeval_isempty(struct timeval *ptv) { + if (!ptv) + return false; + + return ptv->tv_sec <= 0 && ptv->tv_usec <= 0; +} + +/** + * Compare a struct timeval with a time in milliseconds. + * + * @return > 0 if ptv > ms, 0 if ptv == 0, -1 if ptv < ms + */ +static inline int +timeval_ms_cmp(struct timeval *ptv, time_ms_t ms) { + assert(ptv); + + // We use those if statement instead of a - expression because of possible + // truncation problem from long to int. + { + long sec = ms / MS_PER_SEC; + if (ptv->tv_sec > sec) + return 1; + if (ptv->tv_sec < sec) + return -1; + } + + { + long usec = ms % MS_PER_SEC * (US_PER_SEC / MS_PER_SEC); + if (ptv->tv_usec > usec) + return 1; + if (ptv->tv_usec < usec) + return -1; + } + + return 0; +} + +/** + * Subtracting two struct timeval values. + * + * Taken from glibc manual. + * + * Subtract the `struct timeval' values X and Y, + * storing the result in RESULT. + * Return 1 if the difference is negative, otherwise 0. + */ +static inline int +timeval_subtract(struct timeval *result, + struct timeval *x, + struct timeval *y) { + /* Perform the carry for the later subtraction by updating y. */ + if (x->tv_usec < y->tv_usec) { + long nsec = (y->tv_usec - x->tv_usec) / 1000000 + 1; + y->tv_usec -= 1000000 * nsec; + y->tv_sec += nsec; + } + + if (x->tv_usec - y->tv_usec > 1000000) { + long nsec = (x->tv_usec - y->tv_usec) / 1000000; + y->tv_usec += 1000000 * nsec; + y->tv_sec -= nsec; + } + + /* Compute the time remaining to wait. + tv_usec is certainly positive. */ + result->tv_sec = x->tv_sec - y->tv_sec; + result->tv_usec = x->tv_usec - y->tv_usec; + + /* Return 1 if result is negative. */ + return x->tv_sec < y->tv_sec; +} + +/** + * Subtracting two struct timespec values. + * + * Taken from glibc manual. + * + * Subtract the `struct timespec' values X and Y, + * storing the result in RESULT. + * Return 1 if the difference is negative, otherwise 0. + */ +static inline int +timespec_subtract(struct timespec *result, + struct timespec *x, + struct timespec *y) { + /* Perform the carry for the later subtraction by updating y. */ + if (x->tv_nsec < y->tv_nsec) { + long nsec = (y->tv_nsec - x->tv_nsec) / NS_PER_SEC + 1; + y->tv_nsec -= NS_PER_SEC * nsec; + y->tv_sec += nsec; + } + + if (x->tv_nsec - y->tv_nsec > NS_PER_SEC) { + long nsec = (x->tv_nsec - y->tv_nsec) / NS_PER_SEC; + y->tv_nsec += NS_PER_SEC * nsec; + y->tv_sec -= nsec; + } + + /* Compute the time remaining to wait. + tv_nsec is certainly positive. */ + result->tv_sec = x->tv_sec - y->tv_sec; + result->tv_nsec = x->tv_nsec - y->tv_nsec; + + /* Return 1 if result is negative. */ + return x->tv_sec < y->tv_sec; +} + +/** + * Get current time in struct timeval. + */ +static inline struct timeval __attribute__((const)) +get_time_timeval(void) { + struct timeval tv = { 0, 0 }; + + gettimeofday(&tv, NULL); + + // Return a time of all 0 if the call fails + return tv; +} + +/** + * Get current time in struct timespec. + * + * Note its starting time is unspecified. + */ +static inline struct timespec __attribute__((const)) +get_time_timespec(void) { + struct timespec tm = { 0, 0 }; + + clock_gettime(CLOCK_MONOTONIC, &tm); + + // Return a time of all 0 if the call fails + return tm; +} + + +/** + * Print time passed since program starts execution. + * + * Used for debugging. + */ +static inline void +print_timestamp(session_t *ps) { + struct timeval tm, diff; + + if (gettimeofday(&tm, NULL)) return; + + timeval_subtract(&diff, &tm, &ps->time_start); + printf("[ %5ld.%02ld ] ", diff.tv_sec, diff.tv_usec / 10000); +} + +/** + * Allocate the space and copy a string. + */ +static inline char * +mstrcpy(const char *src) { + char *str = cmalloc(strlen(src) + 1, char); + + strcpy(str, src); + + return str; +} + +/** + * Allocate the space and copy a string. + */ +static inline char * +mstrncpy(const char *src, unsigned len) { + char *str = cmalloc(len + 1, char); + + strncpy(str, src, len); + str[len] = '\0'; + + return str; +} + +/** + * Allocate the space and join two strings. + */ +static inline char * +mstrjoin(const char *src1, const char *src2) { + char *str = cmalloc(strlen(src1) + strlen(src2) + 1, char); + + strcpy(str, src1); + strcat(str, src2); + + return str; +} + +/** + * Allocate the space and join two strings; + */ +static inline char * +mstrjoin3(const char *src1, const char *src2, const char *src3) { + char *str = cmalloc(strlen(src1) + strlen(src2) + + strlen(src3) + 1, char); + + strcpy(str, src1); + strcat(str, src2); + strcat(str, src3); + + return str; +} + +/** + * Concatenate a string on heap with another string. + */ +static inline void +mstrextend(char **psrc1, const char *src2) { + *psrc1 = crealloc(*psrc1, (*psrc1 ? strlen(*psrc1): 0) + strlen(src2) + 1, + char); + + strcat(*psrc1, src2); +} + +/** + * Normalize an int value to a specific range. + * + * @param i int value to normalize + * @param min minimal value + * @param max maximum value + * @return normalized value + */ +static inline int __attribute__((const)) +normalize_i_range(int i, int min, int max) { + if (i > max) return max; + if (i < min) return min; + return i; +} + +/** + * Select the larger integer of two. + */ +static inline int __attribute__((const)) +max_i(int a, int b) { + return (a > b ? a : b); +} + +/** + * Select the smaller integer of two. + */ +static inline int __attribute__((const)) +min_i(int a, int b) { + return (a > b ? b : a); +} + +/** + * Select the larger long integer of two. + */ +static inline long __attribute__((const)) +max_l(long a, long b) { + return (a > b ? a : b); +} + +/** + * Select the smaller long integer of two. + */ +static inline long __attribute__((const)) +min_l(long a, long b) { + return (a > b ? b : a); +} + +/** + * Normalize a double value to a specific range. + * + * @param d double value to normalize + * @param min minimal value + * @param max maximum value + * @return normalized value + */ +static inline double __attribute__((const)) +normalize_d_range(double d, double min, double max) { + if (d > max) return max; + if (d < min) return min; + return d; +} + +/** + * Normalize a double value to 0.\ 0 - 1.\ 0. + * + * @param d double value to normalize + * @return normalized value + */ +static inline double __attribute__((const)) +normalize_d(double d) { + return normalize_d_range(d, 0.0, 1.0); +} + +/** + * Parse a VSync option argument. + */ +static inline bool +parse_vsync(session_t *ps, const char *str) { + for (vsync_t i = 0; VSYNC_STRS[i]; ++i) + if (!strcasecmp(str, VSYNC_STRS[i])) { + ps->o.vsync = i; + return true; + } + + printf_errf("(\"%s\"): Invalid vsync argument.", str); + return false; +} + +/** + * Parse a backend option argument. + */ +static inline bool +parse_backend(session_t *ps, const char *str) { + for (enum backend i = 0; BACKEND_STRS[i]; ++i) + if (!strcasecmp(str, BACKEND_STRS[i])) { + ps->o.backend = i; + return true; + } + // Keep compatibility with an old revision containing a spelling mistake... + if (!strcasecmp(str, "xr_glx_hybird")) { + ps->o.backend = BKEND_XR_GLX_HYBRID; + return true; + } + // cju wants to use dashes + if (!strcasecmp(str, "xr-glx-hybrid")) { + ps->o.backend = BKEND_XR_GLX_HYBRID; + return true; + } + printf_errf("(\"%s\"): Invalid backend argument.", str); + return false; +} + +/** + * Parse a glx_swap_method option argument. + */ +static inline bool +parse_glx_swap_method(session_t *ps, const char *str) { + // Parse alias + if (!strcmp("undefined", str)) { + ps->o.glx_swap_method = 0; + return true; + } + + if (!strcmp("copy", str)) { + ps->o.glx_swap_method = 1; + return true; + } + + if (!strcmp("exchange", str)) { + ps->o.glx_swap_method = 2; + return true; + } + + if (!strcmp("buffer-age", str)) { + ps->o.glx_swap_method = -1; + return true; + } + + // Parse number + { + char *pc = NULL; + int age = strtol(str, &pc, 0); + if (!pc || str == pc) { + printf_errf("(\"%s\"): Invalid number.", str); + return false; + } + + for (; *pc; ++pc) + if (!isspace(*pc)) { + printf_errf("(\"%s\"): Trailing characters.", str); + return false; + } + + if (age > CGLX_MAX_BUFFER_AGE + 1 || age < -1) { + printf_errf("(\"%s\"): Number too large / too small.", str); + return false; + } + + ps->o.glx_swap_method = age; + } + + return true; +} + +timeout_t * +timeout_insert(session_t *ps, time_ms_t interval, + bool (*callback)(session_t *ps, timeout_t *ptmout), void *data); + +void +timeout_invoke(session_t *ps, timeout_t *ptmout); + +bool +timeout_drop(session_t *ps, timeout_t *prm); + +void +timeout_reset(session_t *ps, timeout_t *ptmout); + +/** + * Add a file descriptor to a select() fd_set. + */ +static inline bool +fds_insert_select(fd_set **ppfds, int fd) { + assert(fd <= FD_SETSIZE); + + if (!*ppfds) { + if ((*ppfds = malloc(sizeof(fd_set)))) { + FD_ZERO(*ppfds); + } + else { + fprintf(stderr, "Failed to allocate memory for select() fdset.\n"); + exit(1); + } + } + + FD_SET(fd, *ppfds); + + return true; +} + +/** + * Add a new file descriptor to wait for. + */ +static inline bool +fds_insert(session_t *ps, int fd, short events) { + bool result = true; + + ps->nfds_max = max_i(fd + 1, ps->nfds_max); + + if (POLLIN & events) + result = fds_insert_select(&ps->pfds_read, fd) && result; + if (POLLOUT & events) + result = fds_insert_select(&ps->pfds_write, fd) && result; + if (POLLPRI & events) + result = fds_insert_select(&ps->pfds_except, fd) && result; + + return result; +} + +/** + * Delete a file descriptor to wait for. + */ +static inline void +fds_drop(session_t *ps, int fd, short events) { + // Drop fd from respective fd_set-s + if (POLLIN & events && ps->pfds_read) + FD_CLR(fd, ps->pfds_read); + if (POLLOUT & events && ps->pfds_write) + FD_CLR(fd, ps->pfds_write); + if (POLLPRI & events && ps->pfds_except) + FD_CLR(fd, ps->pfds_except); +} + +#define CPY_FDS(key) \ + fd_set * key = NULL; \ + if (ps->key) { \ + key = malloc(sizeof(fd_set)); \ + memcpy(key, ps->key, sizeof(fd_set)); \ + if (!key) { \ + fprintf(stderr, "Failed to allocate memory for copying select() fdset.\n"); \ + exit(1); \ + } \ + } \ + +/** + * Poll for changes. + * + * poll() is much better than select(), but ppoll() does not exist on + * *BSD. + */ +static inline int +fds_poll(session_t *ps, struct timeval *ptv) { + // Copy fds + CPY_FDS(pfds_read); + CPY_FDS(pfds_write); + CPY_FDS(pfds_except); + + int ret = select(ps->nfds_max, pfds_read, pfds_write, pfds_except, ptv); + + free(pfds_read); + free(pfds_write); + free(pfds_except); + + return ret; +} +#undef CPY_FDS + +/** + * Wrapper of XFree() for convenience. + * + * Because a NULL pointer cannot be passed to XFree(), its man page says. + */ +static inline void +cxfree(void *data) { + if (data) + XFree(data); +} + +/** + * Wrapper of XInternAtom() for convenience. + */ +static inline Atom +get_atom(session_t *ps, const char *atom_name) { + return XInternAtom(ps->dpy, atom_name, False); +} + +/** + * Return the painting target window. + */ +static inline Window +get_tgt_window(session_t *ps) { + return ps->o.paint_on_overlay ? ps->overlay: ps->root; +} + +/** + * Find a window from window id in window linked list of the session. + */ +static inline win * +find_win(session_t *ps, Window id) { + if (!id) + return NULL; + + win *w; + + for (w = ps->list; w; w = w->next) { + if (w->id == id && !w->destroyed) + return w; + } + + return 0; +} + +/** + * Find out the WM frame of a client window using existing data. + * + * @param id window ID + * @return struct _win object of the found window, NULL if not found + */ +static inline win * +find_toplevel(session_t *ps, Window id) { + if (!id) + return NULL; + + for (win *w = ps->list; w; w = w->next) { + if (w->client_win == id && !w->destroyed) + return w; + } + + return NULL; +} + + +/** + * Check if current backend uses XRender for rendering. + */ +static inline bool +bkend_use_xrender(session_t *ps) { + return BKEND_XRENDER == ps->o.backend + || BKEND_XR_GLX_HYBRID == ps->o.backend; +} + +/** + * Check if current backend uses GLX. + */ +static inline bool +bkend_use_glx(session_t *ps) { + return BKEND_GLX == ps->o.backend + || BKEND_XR_GLX_HYBRID == ps->o.backend; +} + +/** + * Check if a window is really focused. + */ +static inline bool +win_is_focused_real(session_t *ps, const win *w) { + return IsViewable == w->a.map_state && ps->active_win == w; +} + +/** + * Find out the currently focused window. + * + * @return struct _win object of the found window, NULL if not found + */ +static inline win * +find_focused(session_t *ps) { + if (!ps->o.track_focus) return NULL; + + if (ps->active_win && win_is_focused_real(ps, ps->active_win)) + return ps->active_win; + return NULL; +} + +/** + * Copies a region. + */ +static inline XserverRegion +copy_region(const session_t *ps, XserverRegion oldregion) { + if (!oldregion) + return None; + + XserverRegion region = XFixesCreateRegion(ps->dpy, NULL, 0); + + XFixesCopyRegion(ps->dpy, region, oldregion); + + return region; +} + +/** + * Destroy a <code>XserverRegion</code>. + */ +static inline void +free_region(session_t *ps, XserverRegion *p) { + if (*p) { + XFixesDestroyRegion(ps->dpy, *p); + *p = None; + } +} + +/** + * Free all regions in ps->all_damage_last . + */ +static inline void +free_all_damage_last(session_t *ps) { + for (int i = 0; i < CGLX_MAX_BUFFER_AGE; ++i) + free_region(ps, &ps->all_damage_last[i]); +} + +#ifdef CONFIG_XSYNC +/** + * Free a XSync fence. + */ +static inline void +free_fence(session_t *ps, XSyncFence *pfence) { + if (*pfence) + XSyncDestroyFence(ps->dpy, *pfence); + *pfence = None; +} +#else +#define free_fence(ps, pfence) ((void) 0) +#endif + +/** + * Crop a rectangle by another rectangle. + * + * psrc and pdst cannot be the same. + */ +static inline void +rect_crop(XRectangle *pdst, const XRectangle *psrc, const XRectangle *pbound) { + assert(psrc != pdst); + pdst->x = max_i(psrc->x, pbound->x); + pdst->y = max_i(psrc->y, pbound->y); + pdst->width = max_i(0, min_i(psrc->x + psrc->width, pbound->x + pbound->width) - pdst->x); + pdst->height = max_i(0, min_i(psrc->y + psrc->height, pbound->y + pbound->height) - pdst->y); +} + +/** + * Check if a rectangle includes the whole screen. + */ +static inline bool +rect_is_fullscreen(session_t *ps, int x, int y, unsigned wid, unsigned hei) { + return (x <= 0 && y <= 0 + && (x + wid) >= ps->root_width && (y + hei) >= ps->root_height); +} + +/** + * Check if a window is a fullscreen window. + * + * It's not using w->border_size for performance measures. + */ +static inline bool +win_is_fullscreen(session_t *ps, const win *w) { + return rect_is_fullscreen(ps, w->a.x, w->a.y, w->widthb, w->heightb) + && !w->bounding_shaped; +} + +/** + * Determine if a window has a specific property. + * + * @param ps current session + * @param w window to check + * @param atom atom of property to check + * @return 1 if it has the attribute, 0 otherwise + */ +static inline bool +wid_has_prop(const session_t *ps, Window w, Atom atom) { + Atom type = None; + int format; + unsigned long nitems, after; + unsigned char *data; + + if (Success == XGetWindowProperty(ps->dpy, w, atom, 0, 0, False, + AnyPropertyType, &type, &format, &nitems, &after, &data)) { + cxfree(data); + if (type) return true; + } + + return false; +} + +winprop_t +wid_get_prop_adv(const session_t *ps, Window w, Atom atom, long offset, + long length, Atom rtype, int rformat); + +/** + * Wrapper of wid_get_prop_adv(). + */ +static inline winprop_t +wid_get_prop(const session_t *ps, Window wid, Atom atom, long length, + Atom rtype, int rformat) { + return wid_get_prop_adv(ps, wid, atom, 0L, length, rtype, rformat); +} + +/** + * Get the numeric property value from a win_prop_t. + */ +static inline long +winprop_get_int(winprop_t prop) { + long tgt = 0; + + if (!prop.nitems) + return 0; + + switch (prop.format) { + case 8: tgt = *(prop.data.p8); break; + case 16: tgt = *(prop.data.p16); break; + case 32: tgt = *(prop.data.p32); break; + default: assert(0); + break; + } + + return tgt; +} + +bool +wid_get_text_prop(session_t *ps, Window wid, Atom prop, + char ***pstrlst, int *pnstr); + +/** + * Free a <code>winprop_t</code>. + * + * @param pprop pointer to the <code>winprop_t</code> to free. + */ +static inline void +free_winprop(winprop_t *pprop) { + // Empty the whole structure to avoid possible issues + if (pprop->data.p8) { + cxfree(pprop->data.p8); + pprop->data.p8 = NULL; + } + pprop->nitems = 0; +} + +void +force_repaint(session_t *ps); + +bool +vsync_init(session_t *ps); + +void +vsync_deinit(session_t *ps); + +#ifdef CONFIG_VSYNC_OPENGL +/** @name GLX + */ +///@{ + +#ifdef CONFIG_GLX_SYNC +void +xr_glx_sync(session_t *ps, Drawable d, XSyncFence *pfence); +#endif + +bool +glx_init(session_t *ps, bool need_render); + +void +glx_destroy(session_t *ps); + +void +glx_on_root_change(session_t *ps); + +bool +glx_init_blur(session_t *ps); + +bool +glx_bind_pixmap(session_t *ps, glx_texture_t **pptex, Pixmap pixmap, + unsigned width, unsigned height, unsigned depth); + +void +glx_release_pixmap(session_t *ps, glx_texture_t *ptex); + +void +glx_paint_pre(session_t *ps, XserverRegion *preg); + +/** + * Check if a texture is binded, or is binded to the given pixmap. + */ +static inline bool +glx_tex_binded(const glx_texture_t *ptex, Pixmap pixmap) { + return ptex && ptex->glpixmap && ptex->texture + && (!pixmap || pixmap == ptex->pixmap); +} + +void +glx_set_clip(session_t *ps, XserverRegion reg, const reg_data_t *pcache_reg); + +#ifdef CONFIG_VSYNC_OPENGL_GLSL +bool +glx_blur_dst(session_t *ps, int dx, int dy, int width, int height, float z, + GLfloat factor_center, + XserverRegion reg_tgt, const reg_data_t *pcache_reg, + glx_blur_cache_t *pbc); +#endif + +bool +glx_dim_dst(session_t *ps, int dx, int dy, int width, int height, float z, + GLfloat factor, XserverRegion reg_tgt, const reg_data_t *pcache_reg); + +bool +glx_render(session_t *ps, const glx_texture_t *ptex, + int x, int y, int dx, int dy, int width, int height, int z, + double opacity, bool neg, + XserverRegion reg_tgt, const reg_data_t *pcache_reg); + +bool +glx_render_specified_color(session_t *ps, int color, int dx, int dy, int width, int height, int z, + XserverRegion reg_tgt, const reg_data_t *pcache_reg); + +void +glx_swap_copysubbuffermesa(session_t *ps, XserverRegion reg); + +#ifdef CONFIG_VSYNC_OPENGL_GLSL +GLuint +glx_create_shader(GLenum shader_type, const char *shader_str); + +GLuint +glx_create_program(const GLuint * const shaders, int nshaders); +#endif + +/** + * Free a GLX texture. + */ +static inline void +free_texture_r(session_t *ps, GLuint *ptexture) { + if (*ptexture) { + assert(ps->glx_context); + glDeleteTextures(1, ptexture); + *ptexture = 0; + } +} + +/** + * Free a GLX Framebuffer object. + */ +static inline void +free_glx_fbo(session_t *ps, GLuint *pfbo) { +#ifdef CONFIG_VSYNC_OPENGL_FBO + if (*pfbo) { + glDeleteFramebuffers(1, pfbo); + *pfbo = 0; + } +#endif + assert(!*pfbo); +} + +#ifdef CONFIG_VSYNC_OPENGL_GLSL +/** + * Free data in glx_blur_cache_t on resize. + */ +static inline void +free_glx_bc_resize(session_t *ps, glx_blur_cache_t *pbc) { + free_texture_r(ps, &pbc->textures[0]); + free_texture_r(ps, &pbc->textures[1]); + pbc->width = 0; + pbc->height = 0; +} + +/** + * Free a glx_blur_cache_t + */ +static inline void +free_glx_bc(session_t *ps, glx_blur_cache_t *pbc) { + free_glx_fbo(ps, &pbc->fbo); + free_glx_bc_resize(ps, pbc); +} +#endif +#endif + +/** + * Free a glx_texture_t. + */ +static inline void +free_texture(session_t *ps, glx_texture_t **pptex) { + glx_texture_t *ptex = *pptex; + + // Quit if there's nothing + if (!ptex) + return; + +#ifdef CONFIG_VSYNC_OPENGL + glx_release_pixmap(ps, ptex); + + free_texture_r(ps, &ptex->texture); + + // Free structure itself + free(ptex); + *pptex = NULL; +#endif + assert(!*pptex); +} + +/** + * Add a OpenGL debugging marker. + */ +static inline void +glx_mark_(session_t *ps, const char *func, XID xid, bool start) { +#ifdef DEBUG_GLX_MARK + if (bkend_use_glx(ps) && ps->glStringMarkerGREMEDY) { + if (!func) func = "(unknown)"; + const char *postfix = (start ? " (start)": " (end)"); + char *str = malloc((strlen(func) + 12 + 2 + + strlen(postfix) + 5) * sizeof(char)); + strcpy(str, func); + sprintf(str + strlen(str), "(%#010lx)%s", xid, postfix); + ps->glStringMarkerGREMEDY(strlen(str), str); + free(str); + } +#endif +} + +#define glx_mark(ps, xid, start) glx_mark_(ps, __func__, xid, start) + +/** + * Add a OpenGL debugging marker. + */ +static inline void +glx_mark_frame(session_t *ps) { +#ifdef DEBUG_GLX_MARK + if (bkend_use_glx(ps) && ps->glFrameTerminatorGREMEDY) + ps->glFrameTerminatorGREMEDY(); +#endif +} + +///@} + +#ifdef CONFIG_XSYNC +#define xr_sync(ps, d, pfence) xr_sync_(ps, d, pfence) +#else +#define xr_sync(ps, d, pfence) xr_sync_(ps, d) +#endif + +/** + * Synchronizes a X Render drawable to ensure all pending painting requests + * are completed. + */ +static inline void +xr_sync_(session_t *ps, Drawable d +#ifdef CONFIG_XSYNC + , XSyncFence *pfence +#endif + ) { + if (!ps->o.xrender_sync) + return; + + XSync(ps->dpy, False); +#ifdef CONFIG_XSYNC + if (ps->o.xrender_sync_fence && ps->xsync_exists) { + // TODO: If everybody just follows the rules stated in X Sync prototype, + // we need only one fence per screen, but let's stay a bit cautious right + // now + XSyncFence tmp_fence = None; + if (!pfence) + pfence = &tmp_fence; + assert(pfence); + if (!*pfence) + *pfence = XSyncCreateFence(ps->dpy, d, False); + if (*pfence) { + Bool triggered = False; + /* if (XSyncQueryFence(ps->dpy, *pfence, &triggered) && triggered) + XSyncResetFence(ps->dpy, *pfence); */ + // The fence may fail to be created (e.g. because of died drawable) + assert(!XSyncQueryFence(ps->dpy, *pfence, &triggered) || !triggered); + XSyncTriggerFence(ps->dpy, *pfence); + XSyncAwaitFence(ps->dpy, pfence, 1); + assert(!XSyncQueryFence(ps->dpy, *pfence, &triggered) || triggered); + } + else { + printf_errf("(%#010lx): Failed to create X Sync fence.", d); + } + free_fence(ps, &tmp_fence); + if (*pfence) + XSyncResetFence(ps->dpy, *pfence); + } +#endif +#ifdef CONFIG_GLX_SYNC + xr_glx_sync(ps, d, pfence); +#endif +} + +/** @name DBus handling + */ +///@{ +#ifdef CONFIG_DBUS +/** @name DBus handling + */ +///@{ +bool +cdbus_init(session_t *ps); + +void +cdbus_destroy(session_t *ps); + +void +cdbus_loop(session_t *ps); + +void +cdbus_ev_win_added(session_t *ps, win *w); + +void +cdbus_ev_win_destroyed(session_t *ps, win *w); + +void +cdbus_ev_win_mapped(session_t *ps, win *w); + +void +cdbus_ev_win_unmapped(session_t *ps, win *w); + +void +cdbus_ev_win_focusout(session_t *ps, win *w); + +void +cdbus_ev_win_focusin(session_t *ps, win *w); +//!@} + +/** @name DBus hooks + */ +///@{ +void +win_set_shadow_force(session_t *ps, win *w, switch_t val); + +void +win_set_fade_force(session_t *ps, win *w, switch_t val); + +void +win_set_focused_force(session_t *ps, win *w, switch_t val); + +void +win_set_invert_color_force(session_t *ps, win *w, switch_t val); + +void +opts_init_track_focus(session_t *ps); + +void +opts_set_no_fading_openclose(session_t *ps, bool newval); + +void +opts_set_no_fading_opacitychange(session_t *ps, bool newval); +//!@} +#endif + +#ifdef CONFIG_C2 +/** @name c2 + */ +///@{ + +c2_lptr_t * +c2_parsed(session_t *ps, c2_lptr_t **pcondlst, const char *pattern, + void *data); + +#define c2_parse(ps, pcondlst, pattern) c2_parsed((ps), (pcondlst), (pattern), NULL) + +c2_lptr_t * +c2_free_lptr(c2_lptr_t *lp); + +bool +c2_matchd(session_t *ps, win *w, const c2_lptr_t *condlst, + const c2_lptr_t **cache, void **pdata); + +#define c2_match(ps, w, condlst, cache) c2_matchd((ps), (w), (condlst), \ + (cache), NULL) +#endif + +///@} + +#endif + +/** + * @brief Dump raw bytes in HEX format. + * + * @param data pointer to raw data + * @param len length of data + */ +static inline void +hexdump(const char *data, int len) { + static const int BYTE_PER_LN = 16; + + if (len <= 0) + return; + + // Print header + printf("%10s:", "Offset"); + for (int i = 0; i < BYTE_PER_LN; ++i) + printf(" %2d", i); + putchar('\n'); + + // Dump content + for (int offset = 0; offset < len; ++offset) { + if (!(offset % BYTE_PER_LN)) + printf("0x%08x:", offset); + + printf(" %02hhx", data[offset]); + + if ((BYTE_PER_LN - 1) == offset % BYTE_PER_LN) + putchar('\n'); + } + if (len % BYTE_PER_LN) + putchar('\n'); + + fflush(stdout); +} + |