Files
picom/src/common.h
Yuxuan Shui 0c4b690b2b First step of split backend into modules
This commit introduced a new, modular backend interface. The interface
is not very good, since I don't think I fully understand all the
requirements writing a backend have. But this is a good first step.

This commit also includes an initial xrender backend written using the
new interface, and some opengl backend related helper functions, which
are taken from the old opengl backend.

However, there is not integration with the core compton yet. compton
will still use the old backend code. This commit is here so we can get
the automated build test.

What is implemented in the new xrender backend:

* Windows with transparency
* Shadow
* Opacity
* Wallpaper (getting the root pixmap)
* Blur

Known problem with the xrender backend:

* It is slower

Things that still need to be figured out:

* What is the better way to add vsync to the new backends

Signed-off-by: Yuxuan Shui <yshuiv7@gmail.com>
2018-12-22 00:50:25 +00:00

1218 lines
31 KiB
C

// SPDX-License-Identifier: MIT
/*
* Compton - a compositor for X11
*
* Based on `xcompmgr` - Copyright (c) 2003, Keith Packard
*
* Copyright (c) 2011-2013, Christopher Jeffrey
* Copyright (c) 2018, Yuxuan Shui <yshuiv7@gmail.com>
*
* See LICENSE-mit for more information.
*
*/
#pragma once
// === Options ===
// Debug options, enable them using -D in CFLAGS
// #define DEBUG_REPAINT 1
// #define DEBUG_EVENTS 1
// #define DEBUG_RESTACK 1
// #define DEBUG_WINMATCH 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
// 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 to enable DRM VSync support
// #define CONFIG_VSYNC_DRM 1
// Whether to enable OpenGL support (include GLSL, FBO)
// #define CONFIG_OPENGL 1
// Whether to enable DBus support with libdbus.
// #define CONFIG_DBUS 1
// Whether to enable X Sync support.
// #define CONFIG_XSYNC 1
// Whether to enable GLX Sync support.
// #define CONFIG_GLX_XSYNC 1
#ifndef COMPTON_VERSION
#define COMPTON_VERSION "unknown"
#endif
#define MAX_ALPHA (255)
// === Includes ===
// For some special functions
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <inttypes.h>
#include <assert.h>
#include <time.h>
#include <ctype.h>
#include <sys/time.h>
#include <X11/Xlib-xcb.h>
#include <X11/Xlib.h>
#include <X11/extensions/sync.h>
#include <xcb/composite.h>
#include <xcb/render.h>
#include <xcb/damage.h>
#include <xcb/randr.h>
#include <xcb/shape.h>
#ifdef CONFIG_XINERAMA
#include <xcb/xinerama.h>
#endif
#include <ev.h>
#include <pixman.h>
#ifdef CONFIG_OPENGL
// libGL
#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)
// Use #s here to prevent macro expansion
/// Macro used for shortening some debugging code.
#define CASESTRRET(s) case s: return #s
// X resource checker
#ifdef DEBUG_XRC
#include "xrescheck.h"
#endif
// FIXME This list of includes should get shorter
#include "types.h"
#include "win.h"
#include "x.h"
#include "region.h"
#include "log.h"
#include "utils.h"
#include "compiler.h"
#include "kernel.h"
#include "options.h"
// === Constants ===
/// @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 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_GAUSSIAN "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
// 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
// xcb-render specific macros
#define XFIXED_TO_DOUBLE(value) (((double) (value)) / 65536)
#define DOUBLE_TO_XFIXED(value) ((xcb_render_fixed_t) (((double) (value)) * 65536))
// === Types ===
/// Structure representing needed window updates.
typedef struct {
bool shadow : 1;
bool fade : 1;
bool focus : 1;
bool invert_color : 1;
} win_upd_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
/// @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_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);
#ifdef CONFIG_OPENGL
// 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;
xcb_pixmap_t pixmap;
GLenum target;
unsigned width;
unsigned height;
unsigned depth;
bool y_inverted;
};
#ifdef CONFIG_OPENGL
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 glx_prog_main {
/// GLSL program.
GLuint prog;
/// Location of uniform "opacity" in window GLSL program.
GLint unifm_opacity;
/// Location of uniform "invert_color" in blur GLSL program.
GLint unifm_invert_color;
/// Location of uniform "tex" in window GLSL program.
GLint unifm_tex;
} glx_prog_main_t;
#define GLX_PROG_MAIN_INIT { \
.prog = 0, \
.unifm_opacity = -1, \
.unifm_invert_color = -1, \
.unifm_tex = -1, \
}
#endif
#else
struct glx_prog_main { };
#endif
#define PAINT_INIT { .pixmap = None, .pict = None }
/// Linked list type of atoms.
typedef struct _latom {
Atom atom;
struct _latom *next;
} latom_t;
#define REG_DATA_INIT { NULL, 0 }
#ifdef CONFIG_OPENGL
/// Structure containing GLX-dependent data for a compton session.
typedef struct {
// === OpenGL related ===
/// GLX context.
GLXContext context;
/// Whether we have GL_ARB_texture_non_power_of_two.
bool 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 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;
#ifdef DEBUG_GLX_MARK
/// Pointer to StringMarkerGREMEDY function.
f_StringMarkerGREMEDY glStringMarkerGREMEDY;
/// Pointer to FrameTerminatorGREMEDY function.
f_FrameTerminatorGREMEDY glFrameTerminatorGREMEDY;
#endif
/// Current GLX Z value.
int z;
/// FBConfig-s for GLX pixmap of different depths.
glx_fbconfig_t *fbconfigs[OPENGL_MAX_DEPTH + 1];
#ifdef CONFIG_OPENGL
glx_blur_pass_t blur_passes[MAX_BLUR_PASS];
#endif
} glx_session_t;
#define CGLX_SESSION_INIT { .context = NULL }
#endif
/// Structure containing all necessary data for a compton session.
typedef struct session {
// === Event handlers ===
/// ev_io for X connection
ev_io xiow;
/// Timeout for delayed unredirection.
ev_timer unredir_timer;
/// Timer for fading
ev_timer fade_timer;
/// Timer for delayed drawing, right now only used by
/// swopti
ev_timer delayed_draw_timer;
/// Use an ev_idle callback for drawing
/// So we only start drawing when events are processed
ev_idle draw_idle;
/// Called everytime we have timeouts or new data on socket,
/// so we can be sure if xcb read from X socket at anytime during event
/// handling, we will not left any event unhandled in the queue
ev_prepare event_check;
/// Signal handler for SIGUSR1
ev_signal usr1_signal;
/// backend data
void *backend_data;
/// libev mainloop
struct ev_loop *loop;
// === Display related ===
/// Display in use.
Display *dpy;
/// Default screen.
int scr;
/// XCB connection.
xcb_connection_t *c;
/// Default visual.
xcb_visualid_t vis;
/// Pict formats info
xcb_render_query_pict_formats_reply_t *pictfmts;
/// 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.
region_t screen_reg;
/// Picture of root window. Destination of painting in no-DBE painting
/// mode.
xcb_render_picture_t root_picture;
/// A Picture acting as the painting target.
xcb_render_picture_t tgt_picture;
/// Temporary buffer to paint to before sending to display.
paint_t tgt_buffer;
XSyncFence tgt_buffer_fence;
/// Window ID of the window we register as a symbol.
Window reg_win;
#ifdef CONFIG_OPENGL
/// Pointer to GLX data.
glx_session_t *psglx;
/// Custom GLX program used for painting window.
// XXX should be in glx_session_t
glx_prog_main_t glx_prog_win;
#endif
// === Operation related ===
/// Program options.
options_t o;
/// Whether we have hit unredirection timeout.
bool tmout_unredir_hit;
/// Whether we need to redraw the screen
bool redraw_needed;
/// Whether the program is idling. I.e. no fading, no potential window
/// changes.
bool fade_running;
/// Program start time.
struct timeval time_start;
/// The region needs to painted on next paint.
region_t all_damage;
/// The region damaged on the last paint.
region_t all_damage_last[CGLX_MAX_BUFFER_AGE];
/// Whether all windows are currently redirected.
bool redirected;
/// Pre-generated alpha pictures.
xcb_render_picture_t *alpha_picts;
/// Time of last fading. In milliseconds.
unsigned long 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;
// Cached blur convolution kernels.
xcb_render_fixed_t *blur_kerns_cache[MAX_BLUR_PASS];
/// Reset program after next paint.
bool reset;
/// If compton should quit
bool quit;
// === Expose event related ===
/// Pointer to an array of <code>XRectangle</code>-s of exposed region.
/// XXX why do we need this array?
rect_t *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.
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.
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.
xcb_render_picture_t black_picture;
/// 1x1 Picture of the shadow color.
xcb_render_picture_t cshadow_picture;
/// 1x1 white Picture.
xcb_render_picture_t 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.
region_t 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
// === 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;
/// Whether X Present extension exists.
bool present_exists;
#ifdef CONFIG_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
#ifdef CONFIG_XINERAMA
/// Whether X Xinerama extension exists.
bool xinerama_exists;
/// Xinerama screen info.
xcb_xinerama_query_screens_reply_t *xinerama_scrs;
/// Xinerama screen regions.
region_t *xinerama_scr_regs;
/// Number of Xinerama screens.
int xinerama_nscrs;
#endif
/// 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;
/// 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>_COMPTON_SHADOW</code>.
Atom atom_compton_shadow;
/// Atom of property <code>_NET_WM_WINDOW_TYPE</code>.
Atom atom_win_type;
/// 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 ===
void *dbus_data;
#endif
} session_t;
/// 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;
};
/// 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 session_t *ps_g;
// == Debugging code ==
static inline void
print_timestamp(session_t *ps);
void
ev_xcb_error(session_t *ps, xcb_generic_error_t *err);
// === Functions ===
/**
* 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, unsigned long 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;
}
static inline double
get_opacity_percent(win *w) {
return ((double) w->opacity) / OPAQUE;
}
/**
* Get current time in struct timeval.
*/
static inline struct timeval
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
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);
fprintf(stderr, "[ %5ld.%06ld ] ", diff.tv_sec, diff.tv_usec);
}
/**
* 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);
}
_Noreturn static inline void
die(const char *msg) {
puts(msg);
exit(1);
}
/**
* Wrapper of XInternAtom() for convenience.
*/
static inline xcb_atom_t
get_atom(session_t *ps, const char *atom_name) {
xcb_intern_atom_reply_t *reply =
xcb_intern_atom_reply(ps->c,
xcb_intern_atom(ps->c, False, strlen(atom_name), atom_name),
NULL);
xcb_atom_t atom = XCB_NONE;
if (reply) {
log_debug("Atom %s is %d", atom_name, reply->atom);
atom = reply->atom;
free(reply);
} else
die("Failed to intern atoms, bail out");
return atom;
}
/**
* Return the painting target window.
*/
static inline Window
get_tgt_window(session_t *ps) {
return ps->overlay != XCB_NONE ? 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 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 w->a.map_state == XCB_MAP_STATE_VIEWABLE && 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;
}
/**
* 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)
pixman_region32_clear(&ps->all_damage_last[i]);
}
/**
* Free a XSync fence.
*/
static inline void
free_fence(session_t *ps, XSyncFence *pfence) {
if (*pfence)
XSyncDestroyFence(ps->dpy, *pfence);
*pfence = None;
}
/**
* Check if a rectangle includes the whole screen.
*/
static inline bool
rect_is_fullscreen(session_t *ps, int x, int y, int wid, int hei) {
return (x <= 0 && y <= 0 &&
(x + wid) >= ps->root_width &&
(y + hei) >= ps->root_height);
}
static void
set_ignore(session_t *ps, unsigned long sequence) {
if (ps->o.show_all_xerrors)
return;
auto i = cmalloc(ignore_t);
if (!i) return;
i->sequence = sequence;
i->next = 0;
*ps->ignore_tail = i;
ps->ignore_tail = &i->next;
}
/**
* Ignore X errors caused by next X request.
*/
static inline void
set_ignore_next(session_t *ps) {
set_ignore(ps, NextRequest(ps->dpy));
}
/**
* Ignore X errors caused by given X request.
*/
static inline void
set_ignore_cookie(session_t *ps, xcb_void_cookie_t cookie) {
set_ignore(ps, cookie.sequence);
}
/**
* 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->g.x, w->g.y, w->widthb, w->heightb)
&& (!w->bounding_shaped || w->rounded_corners);
}
/**
* Check if a window will be painted solid.
*/
static inline bool
win_is_solid(session_t *ps, const win *w) {
return WMODE_SOLID == w->mode && !ps->o.force_win_blend;
}
/**
* 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;
}
/**
* 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.p8); break;
case 16: tgt = *(prop.p16); break;
case 32: tgt = *(prop.p32); break;
default: assert(0);
break;
}
return tgt;
}
bool
wid_get_text_prop(session_t *ps, Window wid, Atom prop,
char ***pstrlst, int *pnstr);
void
force_repaint(session_t *ps);
bool
vsync_init(session_t *ps);
void
vsync_deinit(session_t *ps);
#ifdef CONFIG_OPENGL
/** @name GLX
*/
///@{
#endif
/**
* 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 (glx_has_context(ps) && ps->psglx->glStringMarkerGREMEDY) {
if (!func) func = "(unknown)";
const char *postfix = (start ? " (start)": " (end)");
auto str = ccalloc((strlen(func) + 12 + 2
+ strlen(postfix) + 5), char);
strcpy(str, func);
sprintf(str + strlen(str), "(%#010lx)%s", xid, postfix);
ps->psglx->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 (glx_has_context(ps) && ps->psglx->glFrameTerminatorGREMEDY)
ps->psglx->glFrameTerminatorGREMEDY();
#endif
}
///@}
/**
* Synchronizes a X Render drawable to ensure all pending painting requests
* are completed.
*/
static inline void
xr_sync(session_t *ps, Drawable d, XSyncFence *pfence) {
if (!ps->o.xrender_sync)
return;
x_sync(ps->c);
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 attr_unused 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 {
log_error("Failed to create X Sync fence for %#010lx", d);
}
free_fence(ps, &tmp_fence);
if (*pfence)
XSyncResetFence(ps->dpy, *pfence);
}
}
/** @name DBus handling
*/
///@{
#ifdef CONFIG_DBUS
/** @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);
//!@}
#endif
/**
* Set a <code>bool</code> array of all wintypes to true.
*/
static inline void
wintype_arr_enable(bool arr[]) {
wintype_t i;
for (i = 0; i < NUM_WINTYPES; ++i) {
arr[i] = true;
}
}