/** * compton.h */ // Throw everything in here. // === Includes === #include "common.h" #include #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); /** * 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; } /** * 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 XserverRegion. */ inline static void free_region(session_t *ps, XserverRegion *p) { if (*p) { XFixesDestroyRegion(ps->dpy, *p); *p = None; } } /** * 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; } } /** * Destroy a wincond_t. */ inline static void free_wincond(wincond_t *cond) { if (cond->pattern) free(cond->pattern); #ifdef CONFIG_REGEX_PCRE if (cond->regex_pcre_extra) LPCRE_FREE_STUDY(cond->regex_pcre_extra); if (cond->regex_pcre) pcre_free(cond->regex_pcre); #endif free(cond); } /** * Destroy a linked list of wincond_t. */ inline static void free_wincondlst(wincond_t **cond_lst) { wincond_t *next = NULL; for (wincond_t *cond = *cond_lst; cond; cond = next) { next = cond->next; free_wincond(cond); } *cond_lst = NULL; } /** * Destroy all resources in a struct _win. */ inline static void free_win_res(session_t *ps, win *w) { free_region(ps, &w->extents); free_pixmap(ps, &w->pixmap); free_picture(ps, &w->picture); free_region(ps, &w->border_size); free_picture(ps, &w->shadow_pict); 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); } /** * 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) }; } 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 Picture shadow_picture(session_t *ps, double opacity, int width, int height); static Picture solid_picture(session_t *ps, bool argb, double a, double r, double g, double b); /** * Determine if a window has a specific property. * * @param session_t 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)) { XFree(data); if (type) return true; } return false; } /** * Get a specific attribute of a window. * * Returns a blank structure if the returned type and format does not * match the requested type and format. * * @param session_t current session * @param w window * @param atom atom of attribute to fetch * @param length length to read * @param rtype atom of the requested type * @param rformat requested format * @return a winprop_t structure containing the attribute * and number of items. A blank one on failure. */ // TODO: Move to compton.c static winprop_t wid_get_prop_adv(const session_t *ps, Window w, Atom atom, long offset, long length, Atom rtype, int rformat) { Atom type = None; int format = 0; unsigned long nitems = 0, after = 0; unsigned char *data = NULL; if (Success == XGetWindowProperty(ps->dpy, w, atom, offset, length, False, rtype, &type, &format, &nitems, &after, &data) && nitems && (AnyPropertyType == type || type == rtype) && (!format || format == rformat) && (8 == format || 16 == format || 32 == format)) { return (winprop_t) { .data.p8 = data, .nitems = nitems, .type = type, .format = format, }; } XFree(data); return (winprop_t) { .data.p8 = NULL, .nitems = 0, .type = AnyPropertyType, .format = 0 }; } /** * 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; } /** * Free a winprop_t. * * @param pprop pointer to the winprop_t to free. */ static inline void free_winprop(winprop_t *pprop) { // Empty the whole structure to avoid possible issues if (pprop->data.p8) { XFree(pprop->data.p8); pprop->data.p8 = NULL; } pprop->nitems = 0; } /** * 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 session_t 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 (w->a.x <= 0 && w->a.y <= 0 && (w->a.x + w->widthb) >= ps->root_width && (w->a.y + w->heightb) >= ps->root_height && !w->bounding_shaped); } static void win_rounded_corners(session_t *ps, win *w); static bool win_match_once(win *w, const wincond_t *cond); static bool win_match(win *w, wincond_t *condlst, wincond_t * *cache); static bool condlst_add(wincond_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 Picture root_tile_f(session_t *ps); static void paint_root(session_t *ps, Picture tgt_buffer); 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 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, Window id); 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_wdata_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 bool wid_get_text_prop(session_t *ps, Window wid, Atom prop, char ***pstrlst, int *pnstr); 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 void register_cm(session_t *ps, bool want_glxct); 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(const config_t *pcfg, wincond_t **pcondlst, const char *name); static void parse_config(session_t *ps, char *cpath, struct options_tmp *pcfgtmp); #endif static void get_cfg(session_t *ps, int argc, char *const *argv); 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); 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); #ifdef CONFIG_VSYNC_OPENGL static void vsync_opengl_wait(session_t *ps); #endif static void vsync_wait(session_t *ps); static void init_alpha_picts(session_t *ps); static void 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 + (long) (ptmout->interval * TIMEOUT_RUN_TOLERANCE) - ptmout->firstrun) / ptmout->interval, (ptmout->lastrun + 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);