/** * compton.h */ // Throw everything in here. // === Includes === #include "common.h" #include #include #include #include #include #include #include #ifdef CONFIG_VSYNC_DRM #include // We references some definitions in drm.h, which could also be found in // /usr/src/linux/include/drm/drm.h, but that path is probably even less // reliable than libdrm #include #include #include #endif // == Functions == // inline functions must be made static to compile correctly under clang: // http://clang.llvm.org/compatibility.html#inline // Helper functions static void discard_ignore(session_t *ps, unsigned long sequence); static void set_ignore(session_t *ps, unsigned long sequence); /** * Ignore X errors caused by next X request. */ static inline void set_ignore_next(session_t *ps) { set_ignore(ps, NextRequest(ps->dpy)); } static int should_ignore(session_t *ps, unsigned long sequence); /** * Reset filter on a Picture. */ static inline void xrfilter_reset(session_t *ps, Picture p) { XRenderSetPictureFilter(ps->dpy, p, "Nearest", NULL, 0); } /** * Subtract two unsigned long values. * * Truncate to 0 if the result is negative. */ static inline unsigned long __attribute__((const)) sub_unslong(unsigned long a, unsigned long b) { return (a > b) ? a - b : 0; } /** * Set a bool 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; } } /** * Set a switch_t array of all unset wintypes to true. */ static inline void wintype_arr_enable_unset(switch_t arr[]) { wintype_t i; for (i = 0; i < NUM_WINTYPES; ++i) if (UNSET == arr[i]) arr[i] = ON; } /** * Check if a window ID exists in an array of window IDs. * * @param arr the array of window IDs * @param count amount of elements in the array * @param wid window ID to search for */ static inline bool array_wid_exists(const Window *arr, int count, Window wid) { while (count--) { if (arr[count] == wid) { return true; } } return false; } /** * Destroy a Picture. */ inline static void free_picture(session_t *ps, Picture *p) { if (*p) { XRenderFreePicture(ps->dpy, *p); *p = None; } } /** * Destroy a Pixmap. */ inline static void free_pixmap(session_t *ps, Pixmap *p) { if (*p) { XFreePixmap(ps->dpy, *p); *p = None; } } /** * Destroy a Damage. */ inline static void free_damage(session_t *ps, Damage *p) { if (*p) { // BadDamage will be thrown if the window is destroyed set_ignore_next(ps); XDamageDestroy(ps->dpy, *p); *p = None; } } #ifdef CONFIG_C2 /** * Destroy a condition list. */ static inline void free_wincondlst(c2_lptr_t **pcondlst) { while ((*pcondlst = c2_free_lptr(*pcondlst))) continue; } #endif /** * Check whether a paint_t contains enough data. */ static inline bool paint_isvalid(session_t *ps, const paint_t *ppaint) { // Don't check for presence of Pixmap here, because older X Composite doesn't // provide it if (!ppaint) return false; if (BKEND_XRENDER == ps->o.backend && !ppaint->pict) return false; #ifdef CONFIG_VSYNC_OPENGL if (BKEND_GLX == ps->o.backend && !glx_tex_binded(ppaint->ptex, None)) return false; #endif return true; } /** * Bind texture in paint_t if we are using GLX backend. */ static inline bool paint_bind_tex(session_t *ps, paint_t *ppaint, unsigned wid, unsigned hei, unsigned depth, bool force) { #ifdef CONFIG_VSYNC_OPENGL if (BKEND_GLX == ps->o.backend) { if (!ppaint->pixmap) return false; if (force || !glx_tex_binded(ppaint->ptex, ppaint->pixmap)) return glx_bind_pixmap(ps, &ppaint->ptex, ppaint->pixmap, wid, hei, depth); } #endif return true; } /** * Free paint_t. */ static inline void free_paint(session_t *ps, paint_t *ppaint) { free_texture(ps, &ppaint->ptex); free_picture(ps, &ppaint->pict); free_pixmap(ps, &ppaint->pixmap); } /** * Destroy all resources in a struct _win. */ static inline void free_win_res(session_t *ps, win *w) { free_region(ps, &w->extents); free_paint(ps, &w->paint); free_region(ps, &w->border_size); free_paint(ps, &w->shadow_paint); free_damage(ps, &w->damage); free_region(ps, &w->reg_ignore); free(w->name); free(w->class_instance); free(w->class_general); free(w->role); } /** * Free root tile related things. */ static inline void free_root_tile(session_t *ps) { free_picture(ps, &ps->root_tile_paint.pict); free_texture(ps, &ps->root_tile_paint.ptex); if (ps->root_tile_fill) free_pixmap(ps, &ps->root_tile_paint.pixmap); ps->root_tile_paint.pixmap = None; ps->root_tile_fill = false; } /** * Get current system clock in milliseconds. */ static inline time_ms_t get_time_ms(void) { struct timeval tv; gettimeofday(&tv, NULL); return tv.tv_sec % SEC_WRAP * 1000 + tv.tv_usec / 1000; } /** * Convert time from milliseconds to struct timeval. */ static inline struct timeval ms_to_tv(int timeout) { return (struct timeval) { .tv_sec = timeout / MS_PER_SEC, .tv_usec = timeout % MS_PER_SEC * (US_PER_SEC / MS_PER_SEC) }; } /** * Create a XTextProperty of a single string. */ static inline XTextProperty * make_text_prop(session_t *ps, char *str) { XTextProperty *pprop = malloc(sizeof(XTextProperty)); if (!pprop) printf_errfq(1, "(): Failed to allocate memory."); if (XmbTextListToTextProperty(ps->dpy, &str, 1, XStringStyle, pprop)) { if (pprop->value) XFree(pprop->value); free(pprop); pprop = NULL; } return pprop; } static void run_fade(session_t *ps, win *w, unsigned steps); static void set_fade_callback(session_t *ps, win *w, void (*callback) (session_t *ps, win *w), bool exec_callback); /** * Execute fade callback of a window if fading finished. */ static inline void check_fade_fin(session_t *ps, win *w) { if (w->fade_callback && w->opacity == w->opacity_tgt) { // Must be the last line as the callback could destroy w! set_fade_callback(ps, w, NULL, true); } } static void set_fade_callback(session_t *ps, win *w, void (*callback) (session_t *ps, win *w), bool exec_callback); static double gaussian(double r, double x, double y); static conv * make_gaussian_map(double r); static unsigned char sum_gaussian(conv *map, double opacity, int x, int y, int width, int height); static void presum_gaussian(session_t *ps, conv *map); static XImage * make_shadow(session_t *ps, double opacity, int width, int height); static bool win_build_shadow(session_t *ps, win *w, double opacity); static Picture solid_picture(session_t *ps, bool argb, double a, double r, double g, double b); /** * Stop listening for events on a particular window. */ static inline void win_ev_stop(session_t *ps, win *w) { // Will get BadWindow if the window is destroyed set_ignore_next(ps); XSelectInput(ps->dpy, w->id, 0); if (w->client_win) { set_ignore_next(ps); XSelectInput(ps->dpy, w->client_win, 0); } if (ps->shape_exists) { set_ignore_next(ps); XShapeSelectInput(ps->dpy, w->id, 0); } } /** * Get the children of a window. * * @param ps current session * @param w window to check * @param children [out] an array of child window IDs * @param nchildren [out] number of children * @return 1 if successful, 0 otherwise */ static inline bool wid_get_children(session_t *ps, Window w, Window **children, unsigned *nchildren) { Window troot, tparent; if (!XQueryTree(ps->dpy, w, &troot, &tparent, children, nchildren)) { *nchildren = 0; return false; } return true; } /** * Check if a window is bounding-shaped. */ static inline bool wid_bounding_shaped(const session_t *ps, Window wid) { if (ps->shape_exists) { Bool bounding_shaped = False, clip_shaped = False; int x_bounding, y_bounding, x_clip, y_clip; unsigned int w_bounding, h_bounding, w_clip, h_clip; XShapeQueryExtents(ps->dpy, wid, &bounding_shaped, &x_bounding, &y_bounding, &w_bounding, &h_bounding, &clip_shaped, &x_clip, &y_clip, &w_clip, &h_clip); return bounding_shaped; } return false; } /** * Determine if a window change affects reg_ignore and set * reg_ignore_expire accordingly. */ static inline void update_reg_ignore_expire(session_t *ps, const win *w) { if (w->to_paint && WMODE_SOLID == w->mode) ps->reg_ignore_expire = true; } /** * Check whether a window has WM frames. */ static inline bool __attribute__((const)) win_has_frame(const win *w) { return w->a.border_width || w->top_width || w->left_width || w->right_width || w->bottom_width; } /** * Dump an drawable's info. */ static inline void dump_drawable(session_t *ps, Drawable drawable) { Window rroot = None; int x = 0, y = 0; unsigned width = 0, height = 0, border = 0, depth = 0; if (XGetGeometry(ps->dpy, drawable, &rroot, &x, &y, &width, &height, &border, &depth)) { printf_dbgf("(%#010lx): x = %u, y = %u, wid = %u, hei = %d, b = %u, d = %u\n", drawable, x, y, width, height, border, depth); } else { printf_dbgf("(%#010lx): Failed\n", drawable); } } /** * 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; } static void win_rounded_corners(session_t *ps, win *w); static void win_validate_pixmap(session_t *ps, win *w); /** * Wrapper of c2_match(). */ static inline bool win_match(session_t *ps, win *w, c2_lptr_t *condlst, const c2_lptr_t **cache) { #ifdef CONFIG_C2 return c2_match(ps, w, condlst, cache); #else return false; #endif } static bool condlst_add(session_t *ps, c2_lptr_t **pcondlst, const char *pattern); static long determine_evmask(session_t *ps, Window wid, win_evmode_t mode); /** * Clear leader cache of all windows. */ static void clear_cache_win_leaders(session_t *ps) { for (win *w = ps->list; w; w = w->next) w->cache_leader = None; } static win * find_toplevel2(session_t *ps, Window wid); static Window win_get_leader_raw(session_t *ps, win *w, int recursions); /** * Get the leader of a window. * * This function updates w->cache_leader if necessary. */ static inline Window win_get_leader(session_t *ps, win *w) { return win_get_leader_raw(ps, w, 0); } /** * Return whether a window group is really focused. * * @param leader leader window ID * @return true if the window group is focused, false otherwise */ static inline bool group_is_focused(session_t *ps, Window leader) { if (!leader) return false; for (win *w = ps->list; w; w = w->next) { if (win_get_leader(ps, w) == leader && !w->destroyed && w->focused_real) return true; } return false; } static win * recheck_focus(session_t *ps); static bool get_root_tile(session_t *ps); static void paint_root(session_t *ps, XserverRegion reg_paint); static XserverRegion win_get_region(session_t *ps, win *w, bool use_offset); static XserverRegion win_get_region_noframe(session_t *ps, win *w, bool use_offset); static XserverRegion win_extents(session_t *ps, win *w); static XserverRegion border_size(session_t *ps, win *w, bool use_offset); static Window find_client_win(session_t *ps, Window w); static void get_frame_extents(session_t *ps, win *w, Window client); static win * paint_preprocess(session_t *ps, win *list); static void render(session_t *ps, int x, int y, int dx, int dy, int wid, int hei, double opacity, bool argb, bool neg, Picture pict, glx_texture_t *ptex, XserverRegion reg_paint); static inline void win_render(session_t *ps, win *w, int x, int y, int wid, int hei, double opacity, XserverRegion reg_paint, Picture pict) { const int dx = (w ? w->a.x: 0) + x; const int dy = (w ? w->a.y: 0) + y; const bool argb = (w && w->mode == WMODE_ARGB); const bool neg = (w && w->invert_color); render(ps, x, y, dx, dy, wid, hei, opacity, argb, neg, pict, (w ? w->paint.ptex: ps->root_tile_paint.ptex), reg_paint); } static inline void set_tgt_clip(session_t *ps, XserverRegion reg) { switch (ps->o.backend) { case BKEND_XRENDER: XFixesSetPictureClipRegion(ps->dpy, ps->tgt_buffer, 0, 0, reg); break; #ifdef CONFIG_VSYNC_OPENGL case BKEND_GLX: glx_set_clip(ps, reg); break; #endif } } static void paint_all(session_t *ps, XserverRegion region, win *t); static void add_damage(session_t *ps, XserverRegion damage); static void repair_win(session_t *ps, win *w); static wintype_t wid_get_prop_wintype(session_t *ps, Window w); static void map_win(session_t *ps, Window id); static void finish_map_win(session_t *ps, win *w); static void finish_unmap_win(session_t *ps, win *w); static void unmap_callback(session_t *ps, win *w); static void unmap_win(session_t *ps, win *w); static opacity_t wid_get_opacity_prop(session_t *ps, Window wid, opacity_t def); /** * Reread opacity property of a window. */ static inline void win_update_opacity_prop(session_t *ps, win *w) { w->opacity_prop = wid_get_opacity_prop(ps, w->id, OPAQUE); if (!ps->o.detect_client_opacity || !w->client_win || w->id == w->client_win) w->opacity_prop_client = OPAQUE; else w->opacity_prop_client = wid_get_opacity_prop(ps, w->client_win, OPAQUE); } static double get_opacity_percent(win *w); static void win_determine_mode(session_t *ps, win *w); static void calc_opacity(session_t *ps, win *w); static void calc_dim(session_t *ps, win *w); static Window wid_get_prop_window(session_t *ps, Window wid, Atom aprop); static void win_update_leader(session_t *ps, win *w); static void win_set_leader(session_t *ps, win *w, Window leader); static void win_update_focused(session_t *ps, win *w); /** * Run win_update_focused() on all windows with the same leader window. * * @param leader leader window ID */ static inline void group_update_focused(session_t *ps, Window leader) { if (!leader) return; for (win *w = ps->list; w; w = w->next) { if (win_get_leader(ps, w) == leader && !w->destroyed) win_update_focused(ps, w); } return; } static inline void win_set_focused(session_t *ps, win *w, bool focused); static void win_determine_fade(session_t *ps, win *w); static void win_update_shape_raw(session_t *ps, win *w); static void win_update_shape(session_t *ps, win *w); static void win_update_prop_shadow_raw(session_t *ps, win *w); static void win_update_prop_shadow(session_t *ps, win *w); static void win_determine_shadow(session_t *ps, win *w); static void win_on_wtype_change(session_t *ps, win *w); static void win_on_factor_change(session_t *ps, win *w); static void win_upd_run(session_t *ps, win *w, win_upd_t *pupd); static void calc_win_size(session_t *ps, win *w); static void calc_shadow_geometry(session_t *ps, win *w); static void win_mark_client(session_t *ps, win *w, Window client); static void win_unmark_client(session_t *ps, win *w); static void win_recheck_client(session_t *ps, win *w); static bool add_win(session_t *ps, Window id, Window prev); static void restack_win(session_t *ps, win *w, Window new_above); static void configure_win(session_t *ps, XConfigureEvent *ce); static void circulate_win(session_t *ps, XCirculateEvent *ce); static void finish_destroy_win(session_t *ps, Window id); static void destroy_callback(session_t *ps, win *w); static void destroy_win(session_t *ps, Window id); static void damage_win(session_t *ps, XDamageNotifyEvent *de); static int error(Display *dpy, XErrorEvent *ev); static void expose_root(session_t *ps, XRectangle *rects, int nrects); static Window wid_get_prop_window(session_t *ps, Window wid, Atom aprop); static bool wid_get_name(session_t *ps, Window w, char **name); static bool wid_get_role(session_t *ps, Window w, char **role); static int win_get_prop_str(session_t *ps, win *w, char **tgt, bool (*func_wid_get_prop_str)(session_t *ps, Window wid, char **tgt)); static inline int win_get_name(session_t *ps, win *w) { int ret = win_get_prop_str(ps, w, &w->name, wid_get_name); #ifdef DEBUG_WINDATA printf_dbgf("(%#010lx): client = %#010lx, name = \"%s\", " "ret = %d\n", w->id, w->client_win, w->name, ret); #endif return ret; } static inline int win_get_role(session_t *ps, win *w) { int ret = win_get_prop_str(ps, w, &w->role, wid_get_role); #ifdef DEBUG_WINDATA printf_dbgf("(%#010lx): client = %#010lx, role = \"%s\", " "ret = %d\n", w->id, w->client_win, w->role, ret); #endif return ret; } static bool win_get_class(session_t *ps, win *w); #ifdef DEBUG_EVENTS static int ev_serial(XEvent *ev); static const char * ev_name(session_t *ps, XEvent *ev); static Window ev_window(session_t *ps, XEvent *ev); #endif static void __attribute__ ((noreturn)) usage(void); static bool register_cm(session_t *ps); inline static void ev_focus_in(session_t *ps, XFocusChangeEvent *ev); inline static void ev_focus_out(session_t *ps, XFocusChangeEvent *ev); inline static void ev_create_notify(session_t *ps, XCreateWindowEvent *ev); inline static void ev_configure_notify(session_t *ps, XConfigureEvent *ev); inline static void ev_destroy_notify(session_t *ps, XDestroyWindowEvent *ev); inline static void ev_map_notify(session_t *ps, XMapEvent *ev); inline static void ev_unmap_notify(session_t *ps, XUnmapEvent *ev); inline static void ev_reparent_notify(session_t *ps, XReparentEvent *ev); inline static void ev_circulate_notify(session_t *ps, XCirculateEvent *ev); inline static void ev_expose(session_t *ps, XExposeEvent *ev); static void update_ewmh_active_win(session_t *ps); inline static void ev_property_notify(session_t *ps, XPropertyEvent *ev); inline static void ev_damage_notify(session_t *ps, XDamageNotifyEvent *ev); inline static void ev_shape_notify(session_t *ps, XShapeEvent *ev); /** * Get a region of the screen size. */ inline static XserverRegion get_screen_region(session_t *ps) { XRectangle r; r.x = 0; r.y = 0; r.width = ps->root_width; r.height = ps->root_height; return XFixesCreateRegion(ps->dpy, &r, 1); } /** * Dump a region. */ static inline void dump_region(const session_t *ps, XserverRegion region) { int nrects = 0, i; XRectangle *rects = XFixesFetchRegion(ps->dpy, region, &nrects); if (!rects) return; for (i = 0; i < nrects; ++i) printf("Rect #%d: %8d, %8d, %8d, %8d\n", i, rects[i].x, rects[i].y, rects[i].width, rects[i].height); XFree(rects); } /** * Check if a region is empty. * * Keith Packard said this is slow: * http://lists.freedesktop.org/archives/xorg/2007-November/030467.html * * @param ps current session * @param region region to check for */ static inline bool is_region_empty(const session_t *ps, XserverRegion region) { int nrects = 0; XRectangle *rects = XFixesFetchRegion(ps->dpy, region, &nrects); XFree(rects); return !nrects; } /** * Add a window to damaged area. * * @param ps current session * @param w struct _win element representing the window */ static inline void add_damage_win(session_t *ps, win *w) { if (w->extents) { add_damage(ps, copy_region(ps, w->extents)); } } #if defined(DEBUG_EVENTS) || defined(DEBUG_RESTACK) static bool ev_window_name(session_t *ps, Window wid, char **name); #endif inline static void ev_handle(session_t *ps, XEvent *ev); static bool fork_after(session_t *ps); #ifdef CONFIG_LIBCONFIG /** * Wrapper of libconfig's config_lookup_int. * * To convert int value config_lookup_bool * returns to bool. */ static inline void lcfg_lookup_bool(const config_t *config, const char *path, bool *value) { int ival; if (config_lookup_bool(config, path, &ival)) *value = ival; } /** * Wrapper of libconfig's config_lookup_int. * * To deal with the different value types config_lookup_int * returns in libconfig-1.3 and libconfig-1.4. */ static inline int lcfg_lookup_int(const config_t *config, const char *path, int *value) { #ifndef CONFIG_LIBCONFIG_LEGACY return config_lookup_int(config, path, value); #else long lval; int ret; if ((ret = config_lookup_int(config, path, &lval))) *value = lval; return ret; #endif } static FILE * open_config_file(char *cpath, char **path); static void parse_cfg_condlst(session_t *ps, const config_t *pcfg, c2_lptr_t **pcondlst, const char *name); static void parse_config(session_t *ps, struct options_tmp *pcfgtmp); #endif static void get_cfg(session_t *ps, int argc, char *const *argv, bool first_pass); static void init_atoms(session_t *ps); static void update_refresh_rate(session_t *ps); static bool swopti_init(session_t *ps); static void swopti_handle_timeout(session_t *ps, struct timeval *ptv); #ifdef CONFIG_VSYNC_OPENGL /** * Ensure we have a GLX context. */ static inline bool ensure_glx_context(session_t *ps) { // Create GLX context if (!ps->glx_context) glx_init(ps, false); return ps->glx_context; } #endif static bool vsync_drm_init(session_t *ps); #ifdef CONFIG_VSYNC_DRM static int vsync_drm_wait(session_t *ps); #endif static bool vsync_opengl_init(session_t *ps); static bool vsync_opengl_oml_init(session_t *ps); static bool vsync_opengl_swc_init(session_t *ps); #ifdef CONFIG_VSYNC_OPENGL static int vsync_opengl_wait(session_t *ps); static int vsync_opengl_oml_wait(session_t *ps); #endif static void vsync_wait(session_t *ps); static void init_alpha_picts(session_t *ps); static bool init_dbe(session_t *ps); static void init_overlay(session_t *ps); static void redir_start(session_t *ps); static void redir_stop(session_t *ps); static inline time_ms_t timeout_get_newrun(const timeout_t *ptmout) { return ptmout->firstrun + (max_l((ptmout->lastrun + (time_ms_t) (ptmout->interval * TIMEOUT_RUN_TOLERANCE) - ptmout->firstrun) / ptmout->interval, (ptmout->lastrun + (time_ms_t) (ptmout->interval * (1 - TIMEOUT_RUN_TOLERANCE)) - ptmout->firstrun) / ptmout->interval) + 1) * ptmout->interval; } static time_ms_t timeout_get_poll_time(session_t *ps); static void timeout_clear(session_t *ps); static bool mainloop(session_t *ps); static session_t * session_init(session_t *ps_old, int argc, char **argv); static void session_destroy(session_t *ps); static void session_run(session_t *ps); static void reset_enable(int __attribute__((unused)) signum);