/* * 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 #include "compton_config.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_SYNC 1 // TDE specific options // #define USE_ENV_HOME 1 #define WRITE_PID_FILE 1 #define _TDE_COMP_MGR_VERSION_ 3.00 #if defined(HAVE_LIBCONFIG_OLD_API) #define CONFIG_LIBCONFIG_LEGACY 1 #endif #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/Xdbe.h> #ifdef CONFIG_XSYNC #include <X11/extensions/sync.h> #endif #ifdef CONFIG_XRANDR #include <X11/extensions/Xrandr.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; #ifdef CONFIG_XRANDR /// 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; #endif // CONFIG_XRANDR #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); }