diff --git a/Makefile b/Makefile index 60a649b6..c5792e24 100644 --- a/Makefile +++ b/Makefile @@ -13,7 +13,7 @@ PACKAGES = x11 xcomposite xfixes xdamage xrender xext xrandr LIBS = -lm -lrt INCS = -OBJS = compton.o config.o +OBJS = compton.o config.o win.o x.o # === Configuration flags === CFG = -std=c11 -D_GNU_SOURCE -Wall -Wextra -Wno-unused-parameter diff --git a/src/common.h b/src/common.h index d5bd593d..8c44c2e1 100644 --- a/src/common.h +++ b/src/common.h @@ -283,7 +283,7 @@ typedef struct { } win_upd_t; /// Structure representing Window property value. -typedef struct { +typedef struct winprop { // All pointers have the same length, right? // I wanted to use anonymous union but it's a GNU extension... union { @@ -1448,6 +1448,11 @@ timespec_subtract(struct timespec *result, 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. */ @@ -2012,6 +2017,43 @@ rect_is_fullscreen(session_t *ps, int x, int y, unsigned wid, unsigned hei) { (y + hei) >= (unsigned int)ps->root_height); } +static void +set_ignore(session_t *ps, unsigned long sequence) { + if (ps->o.show_all_xerrors) + return; + + ignore_t *i = malloc(sizeof(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)); +} + +static inline void +add_damage(session_t *ps, XserverRegion damage) { + // Ignore damage when screen isn't redirected + if (!ps->redirected) + free_region(ps, &damage); + + if (!damage) return; + if (ps->all_damage) { + XFixesUnionRegion(ps->dpy, ps->all_damage, ps->all_damage, damage); + XFixesDestroyRegion(ps->dpy, damage); + } else { + ps->all_damage = damage; + } +} + /** * Check if a window is a fullscreen window. * diff --git a/src/compton.c b/src/compton.c index 125c1632..8f1b1973 100644 --- a/src/compton.c +++ b/src/compton.c @@ -8,10 +8,282 @@ * */ -#include "compton.h" -#include "config.h" #include +#include "compton.h" +#include "win.h" +#include "x.h" +#include "config.h" + +static bool +xr_blur_dst(session_t *ps, Picture tgt_buffer, + int x, int y, int wid, int hei, XFixed **blur_kerns, + XserverRegion reg_clip); + +#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); + +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); + +static void +cxinerama_upd_scrs(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); + +#ifdef CONFIG_XINERAMA +static void +cxinerama_upd_scrs(session_t *ps); +#endif + +static time_ms_t +timeout_get_poll_time(session_t *ps); + +static void +timeout_clear(session_t *ps); + +static bool +tmout_unredir_callback(session_t *ps, timeout_t *tmout); + +static bool +mainloop(session_t *ps); + +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); + +static bool +vsync_opengl_mswc_init(session_t *ps); + +#ifdef CONFIG_OPENGL +static int +vsync_opengl_wait(session_t *ps); + +static int +vsync_opengl_oml_wait(session_t *ps); + +static void +vsync_opengl_swc_deinit(session_t *ps); + +static void +vsync_opengl_mswc_deinit(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 bool +init_overlay(session_t *ps); + +static void +redir_start(session_t *ps); + +static void +redir_stop(session_t *ps); + +static void +discard_ignore(session_t *ps, unsigned long sequence); + +static int +should_ignore(session_t *ps, unsigned long sequence); + +static void +run_fade(session_t *ps, win *w, unsigned steps); + +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); + +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 win * +paint_preprocess(session_t *ps, win *list); + +static void +paint_all(session_t *ps, XserverRegion region, XserverRegion region_real, win *t); + +static void +add_damage(session_t *ps, XserverRegion damage); + +static void +repair_win(session_t *ps, win *w); + +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 double +get_opacity_percent(win *w); + +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, win *w); + +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 +xerror(Display *dpy, XErrorEvent *ev); + +static void +expose_root(session_t *ps, XRectangle *rects, int nrects); + +#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(int ret); + +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); + // === Global constants === /// Name strings for window types. @@ -149,8 +421,7 @@ run_fade(session_t *ps, win *w, unsigned steps) { * * @param exec_callback whether the previous callback is to be executed */ -static void -set_fade_callback(session_t *ps, win *w, +void set_fade_callback(session_t *ps, win *w, void (*callback) (session_t *ps, win *w), bool exec_callback) { void (*old_callback) (session_t *ps, win *w) = w->fade_callback; @@ -575,20 +846,6 @@ discard_ignore(session_t *ps, unsigned long sequence) { } } -static void -set_ignore(session_t *ps, unsigned long sequence) { - if (ps->o.show_all_xerrors) - return; - - ignore_t *i = malloc(sizeof(ignore_t)); - if (!i) return; - - i->sequence = sequence; - i->next = 0; - *ps->ignore_tail = i; - ps->ignore_tail = &i->next; -} - static int should_ignore(session_t *ps, unsigned long sequence) { discard_ignore(ps, sequence); @@ -597,99 +854,10 @@ should_ignore(session_t *ps, unsigned long sequence) { // === Windows === -/** - * 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 ps 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. - */ -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) - && (!rformat || format == rformat) - && (8 == format || 16 == format || 32 == format)) { - return (winprop_t) { - .data.p8 = data, - .nitems = nitems, - .type = type, - .format = format, - }; - } - - cxfree(data); - - return (winprop_t) { - .data.p8 = NULL, - .nitems = 0, - .type = AnyPropertyType, - .format = 0 - }; -} - -/** - * Check if a window has rounded corners. - */ -static void -win_rounded_corners(session_t *ps, win *w) { - w->rounded_corners = false; - - if (!w->bounding_shaped) - return; - - // Fetch its bounding region - if (!w->border_size) - w->border_size = border_size(ps, w, true); - - // Quit if border_size() returns None - if (!w->border_size) - return; - - // Determine the minimum width/height of a rectangle that could mark - // a window as having rounded corners - unsigned short minwidth = max_i(w->widthb * (1 - ROUNDED_PERCENT), - w->widthb - ROUNDED_PIXELS); - unsigned short minheight = max_i(w->heightb * (1 - ROUNDED_PERCENT), - w->heightb - ROUNDED_PIXELS); - - // Get the rectangles in the bounding region - int nrects = 0, i; - XRectangle *rects = XFixesFetchRegion(ps->dpy, w->border_size, &nrects); - if (!rects) - return; - - // Look for a rectangle large enough for this window be considered - // having rounded corners - for (i = 0; i < nrects; ++i) - if (rects[i].width >= minwidth && rects[i].height >= minheight) { - w->rounded_corners = true; - break; - } - - cxfree(rects); -} - /** * Determine the event mask for a window. */ -static long -determine_evmask(session_t *ps, Window wid, win_evmode_t mode) { +long determine_evmask(session_t *ps, Window wid, win_evmode_t mode) { long evmask = NoEventMask; win *w = NULL; @@ -719,8 +887,9 @@ determine_evmask(session_t *ps, Window wid, win_evmode_t mode) { * @param wid window ID * @return struct _win object of the found window, NULL if not found */ -static win * -find_toplevel2(session_t *ps, Window wid) { +win *find_toplevel2(session_t *ps, Window wid) { + // TODO this should probably be an "update tree", then find_toplevel. + // current approach is a bit more "racy" win *w = NULL; // We traverse through its ancestors to find out the frame @@ -864,130 +1033,10 @@ paint_root(session_t *ps, XserverRegion reg_paint) { NULL, ps->root_tile_paint.pict); } -/** - * Get a rectangular region a window occupies, excluding shadow. - */ -static XserverRegion -win_get_region(session_t *ps, win *w, bool use_offset) { - XRectangle r; - - r.x = (use_offset ? w->a.x: 0); - r.y = (use_offset ? w->a.y: 0); - r.width = w->widthb; - r.height = w->heightb; - - return XFixesCreateRegion(ps->dpy, &r, 1); -} - -/** - * Get a rectangular region a window occupies, excluding frame and shadow. - */ -static XserverRegion -win_get_region_noframe(session_t *ps, win *w, bool use_offset) { - const margin_t extents = win_calc_frame_extents(ps, w); - XRectangle r; - - r.x = (use_offset ? w->a.x: 0) + extents.left; - r.y = (use_offset ? w->a.y: 0) + extents.top; - r.width = max_i(w->a.width - extents.left - extents.right, 0); - r.height = max_i(w->a.height - extents.top - extents.bottom, 0); - - if (r.width > 0 && r.height > 0) - return XFixesCreateRegion(ps->dpy, &r, 1); - else - return XFixesCreateRegion(ps->dpy, NULL, 0); -} - -/** - * Get a rectangular region a window (and possibly its shadow) occupies. - * - * Note w->shadow and shadow geometry must be correct before calling this - * function. - */ -static XserverRegion -win_extents(session_t *ps, win *w) { - XRectangle r; - - r.x = w->a.x; - r.y = w->a.y; - r.width = w->widthb; - r.height = w->heightb; - - if (w->shadow) { - XRectangle sr; - - sr.x = w->a.x + w->shadow_dx; - sr.y = w->a.y + w->shadow_dy; - sr.width = w->shadow_width; - sr.height = w->shadow_height; - - if (sr.x < r.x) { - r.width = (r.x + r.width) - sr.x; - r.x = sr.x; - } - - if (sr.y < r.y) { - r.height = (r.y + r.height) - sr.y; - r.y = sr.y; - } - - if (sr.x + sr.width > r.x + r.width) { - r.width = sr.x + sr.width - r.x; - } - - if (sr.y + sr.height > r.y + r.height) { - r.height = sr.y + sr.height - r.y; - } - } - - return XFixesCreateRegion(ps->dpy, &r, 1); -} - -/** - * Retrieve the bounding shape of a window. - */ -static XserverRegion -border_size(session_t *ps, win *w, bool use_offset) { - // Start with the window rectangular region - XserverRegion fin = win_get_region(ps, w, use_offset); - - // Only request for a bounding region if the window is shaped - if (w->bounding_shaped) { - /* - * if window doesn't exist anymore, this will generate an error - * as well as not generate a region. Perhaps a better XFixes - * architecture would be to have a request that copies instead - * of creates, that way you'd just end up with an empty region - * instead of an invalid XID. - */ - - XserverRegion border = XFixesCreateRegionFromWindow( - ps->dpy, w->id, WindowRegionBounding); - - if (!border) - return fin; - - if (use_offset) { - // Translate the region to the correct place - XFixesTranslateRegion(ps->dpy, border, - w->a.x + w->a.border_width, - w->a.y + w->a.border_width); - } - - // Intersect the bounding region we got with the window rectangle, to - // make sure the bounding region is not bigger than the window - // rectangle - XFixesIntersectRegion(ps->dpy, fin, fin, border); - XFixesDestroyRegion(ps->dpy, border); - } - - return fin; -} - /** * Look for the client window of a particular window. */ -static Window +Window find_client_win(session_t *ps, Window w) { if (wid_has_prop(ps, w, ps->atom_client)) { return w; @@ -1012,36 +1061,6 @@ find_client_win(session_t *ps, Window w) { return ret; } -/** - * Retrieve frame extents from a window. - */ -static void -get_frame_extents(session_t *ps, win *w, Window client) { - cmemzero_one(&w->frame_extents); - - winprop_t prop = wid_get_prop(ps, client, ps->atom_frame_extents, - 4L, XA_CARDINAL, 32); - - if (4 == prop.nitems) { - const long * const extents = prop.data.p32; - w->frame_extents.left = extents[0]; - w->frame_extents.right = extents[1]; - w->frame_extents.top = extents[2]; - w->frame_extents.bottom = extents[3]; - - if (ps->o.frame_opacity) - update_reg_ignore_expire(ps, w); - } - -#ifdef DEBUG_FRAME - printf_dbgf("(%#010lx): %d, %d, %d, %d\n", w->id, - w->frame_extents.left, w->frame_extents.right, - w->frame_extents.top, w->frame_extents.bottom); -#endif - - free_winprop(&prop); -} - /** * Get alpha Picture for an opacity in double. */ @@ -1111,8 +1130,8 @@ paint_preprocess(session_t *ps, win *list) { // Update window opacity target and dim state if asked if (WFLAG_OPCT_CHANGE & w->flags) { - calc_opacity(ps, w); - calc_dim(ps, w); + win_calc_opacity(ps, w); + win_calc_dim(ps, w); } // Run fading @@ -1140,7 +1159,7 @@ paint_preprocess(session_t *ps, win *list) { if (to_paint) { // Fetch bounding region if (!w->border_size) - w->border_size = border_size(ps, w, true); + w->border_size = win_border_size(ps, w, true); // Fetch window extents if (!w->extents) @@ -1174,7 +1193,7 @@ paint_preprocess(session_t *ps, win *list) { // Add window to damaged area if its painting status changes // or opacity changes if (to_paint != w->to_paint || w->opacity != opacity_old) - add_damage_win(ps, w); + add_damage_from_win(ps, w); // Destroy all reg_ignore above when window mode changes if ((to_paint && WMODE_SOLID == w->mode) @@ -1472,7 +1491,7 @@ win_blur_background(session_t *ps, win *w, Picture tgt_buffer, // opaque, only the frame is. XserverRegion reg_noframe = None; if (win_is_solid(ps, w)) { - XserverRegion reg_all = border_size(ps, w, false); + XserverRegion reg_all = win_border_size(ps, w, false); reg_noframe = win_get_region_noframe(ps, w, false); XFixesSubtractRegion(ps->dpy, reg_noframe, reg_all, reg_noframe); free_region(ps, ®_all); @@ -1494,7 +1513,7 @@ win_blur_background(session_t *ps, win *w, Picture tgt_buffer, } } -static void +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, @@ -2051,21 +2070,6 @@ paint_all(session_t *ps, XserverRegion region, XserverRegion region_real, win *t } } -static void -add_damage(session_t *ps, XserverRegion damage) { - // Ignore damage when screen isn't redirected - if (!ps->redirected) - free_region(ps, &damage); - - if (!damage) return; - if (ps->all_damage) { - XFixesUnionRegion(ps->dpy, ps->all_damage, ps->all_damage, damage); - XFixesDestroyRegion(ps->dpy, damage); - } else { - ps->all_damage = damage; - } -} - static void repair_win(session_t *ps, win *w) { if (IsViewable != w->a.map_state) @@ -2103,26 +2107,7 @@ repair_win(session_t *ps, win *w) { add_damage(ps, parts); } -static wintype_t -wid_get_prop_wintype(session_t *ps, Window wid) { - set_ignore_next(ps); - winprop_t prop = wid_get_prop(ps, wid, ps->atom_win_type, 32L, XA_ATOM, 32); - - for (unsigned i = 0; i < prop.nitems; ++i) { - for (wintype_t j = 1; j < NUM_WINTYPES; ++j) { - if (ps->atoms_wintypes[j] == (Atom) prop.data.p32[i]) { - free_winprop(&prop); - return j; - } - } - } - - free_winprop(&prop); - - return WINTYPE_UNKNOWN; -} - -static void +void map_win(session_t *ps, Window id) { // Unmap overlay window if it got mapped but we are currently not // in redirected state. @@ -2295,675 +2280,6 @@ unmap_win(session_t *ps, win *w) { #endif } -static bool -wid_get_opacity_prop(session_t *ps, Window wid, opacity_t def, opacity_t *out) { - bool ret = false; - *out = def; - - winprop_t prop = wid_get_prop(ps, wid, ps->atom_opacity, 1L, - XA_CARDINAL, 32); - - if (prop.nitems) { - *out = *prop.data.p32; - ret = true; - } - - free_winprop(&prop); - - return ret; -} - -static double -get_opacity_percent(win *w) { - return ((double) w->opacity) / OPAQUE; -} - -static void -win_determine_mode(session_t *ps, win *w) { - winmode_t mode = WMODE_SOLID; - - if (w->pictfmt && w->pictfmt->type == PictTypeDirect - && w->pictfmt->direct.alphaMask) { - mode = WMODE_ARGB; - } else if (w->opacity != OPAQUE) { - mode = WMODE_TRANS; - } else { - mode = WMODE_SOLID; - } - - w->mode = mode; -} - -/** - * Calculate and set the opacity of a window. - * - * If window is inactive and inactive_opacity_override is set, the - * priority is: (Simulates the old behavior) - * - * inactive_opacity > _NET_WM_WINDOW_OPACITY (if not opaque) - * > window type default opacity - * - * Otherwise: - * - * _NET_WM_WINDOW_OPACITY (if not opaque) - * > window type default opacity (if not opaque) - * > inactive_opacity - * - * @param ps current session - * @param w struct _win object representing the window - */ -static void -calc_opacity(session_t *ps, win *w) { - opacity_t opacity = OPAQUE; - - if (w->destroyed || IsViewable != w->a.map_state) - opacity = 0; - else { - // Try obeying opacity property and window type opacity firstly - if(w->has_opacity_prop) - opacity = w->opacity_prop; - else if (!isnan(ps->o.wintype_opacity[w->window_type])) - opacity = ps->o.wintype_opacity[w->window_type] * OPAQUE; - else { - // Respect active_opacity only when the window is physically focused - if (win_is_focused_real(ps, w)) - opacity = ps->o.active_opacity; - else if (false == w->focused) - // Respect inactive_opacity in some cases - opacity = ps->o.inactive_opacity; - } - - // respect inactive override - if (ps->o.inactive_opacity_override && false == w->focused) - opacity = ps->o.inactive_opacity; - } - - w->opacity_tgt = opacity; -} - -/** - * Determine whether a window is to be dimmed. - */ -static void -calc_dim(session_t *ps, win *w) { - bool dim; - - // Make sure we do nothing if the window is unmapped / destroyed - if (w->destroyed || IsViewable != w->a.map_state) - return; - - if (ps->o.inactive_dim && !(w->focused)) { - dim = true; - } else { - dim = false; - } - - if (dim != w->dim) { - w->dim = dim; - add_damage_win(ps, w); - } -} - -/** - * Determine if a window should fade on opacity change. - */ -static void -win_determine_fade(session_t *ps, win *w) { - // To prevent it from being overwritten by last-paint value if the window is - // unmapped on next frame, write w->fade_last as well - if (UNSET != w->fade_force) - w->fade_last = w->fade = w->fade_force; - else if (ps->o.no_fading_openclose && w->in_openclose) - w->fade_last = w->fade = false; - else if (ps->o.no_fading_destroyed_argb && w->destroyed - && WMODE_ARGB == w->mode && w->client_win && w->client_win != w->id) { - w->fade_last = w->fade = false; - } - // Ignore other possible causes of fading state changes after window - // gets unmapped - else if (IsViewable != w->a.map_state) { - } - else if (win_match(ps, w, ps->o.fade_blacklist, &w->cache_fblst)) - w->fade = false; - else - w->fade = ps->o.wintype_fade[w->window_type]; -} - -/** - * Update window-shape. - */ -static void -win_update_shape_raw(session_t *ps, win *w) { - if (ps->shape_exists) { - w->bounding_shaped = wid_bounding_shaped(ps, w->id); - if (w->bounding_shaped && ps->o.detect_rounded_corners) - win_rounded_corners(ps, w); - } -} - -/** - * Update window-shape related information. - */ -static void -win_update_shape(session_t *ps, win *w) { - if (ps->shape_exists) { - // bool bounding_shaped_old = w->bounding_shaped; - - win_update_shape_raw(ps, w); - - win_on_factor_change(ps, w); - - /* - // If clear_shadow state on the window possibly changed, destroy the old - // shadow_pict - if (ps->o.clear_shadow && w->bounding_shaped != bounding_shaped_old) - free_paint(ps, &w->shadow_paint); - */ - } -} - -/** - * Reread _COMPTON_SHADOW property from a window. - * - * The property must be set on the outermost window, usually the WM frame. - */ -static void -win_update_prop_shadow_raw(session_t *ps, win *w) { - winprop_t prop = wid_get_prop(ps, w->id, ps->atom_compton_shadow, 1, - XA_CARDINAL, 32); - - if (!prop.nitems) { - w->prop_shadow = -1; - } - else { - w->prop_shadow = *prop.data.p32; - } - - free_winprop(&prop); -} - -/** - * Reread _COMPTON_SHADOW property from a window and update related - * things. - */ -static void -win_update_prop_shadow(session_t *ps, win *w) { - long attr_shadow_old = w->prop_shadow; - - win_update_prop_shadow_raw(ps, w); - - if (w->prop_shadow != attr_shadow_old) - win_determine_shadow(ps, w); -} - -static void -win_set_shadow(session_t *ps, win *w, bool shadow_new) { - if (w->shadow == shadow_new) return; - - w->shadow = shadow_new; - - // Window extents need update on shadow state change - // Shadow geometry currently doesn't change on shadow state change - // calc_shadow_geometry(ps, w); - if (w->extents) { - // Mark the old extents as damaged if the shadow is removed - if (!w->shadow) - add_damage(ps, w->extents); - else - free_region(ps, &w->extents); - w->extents = win_extents(ps, w); - // Mark the new extents as damaged if the shadow is added - if (w->shadow) - add_damage_win(ps, w); - } -} - -/** - * Determine if a window should have shadow, and update things depending - * on shadow state. - */ -static void -win_determine_shadow(session_t *ps, win *w) { - bool shadow_new = w->shadow; - - if (UNSET != w->shadow_force) - shadow_new = w->shadow_force; - else if (IsViewable == w->a.map_state) - shadow_new = (ps->o.wintype_shadow[w->window_type] - && !win_match(ps, w, ps->o.shadow_blacklist, &w->cache_sblst) - && !(ps->o.shadow_ignore_shaped && w->bounding_shaped - && !w->rounded_corners) - && !(ps->o.respect_prop_shadow && 0 == w->prop_shadow)); - - win_set_shadow(ps, w, shadow_new); -} - -static void -win_set_invert_color(session_t *ps, win *w, bool invert_color_new) { - if (w->invert_color == invert_color_new) return; - - w->invert_color = invert_color_new; - - add_damage_win(ps, w); -} - -/** - * Determine if a window should have color inverted. - */ -static void -win_determine_invert_color(session_t *ps, win *w) { - bool invert_color_new = w->invert_color; - - if (UNSET != w->invert_color_force) - invert_color_new = w->invert_color_force; - else if (IsViewable == w->a.map_state) - invert_color_new = win_match(ps, w, ps->o.invert_color_list, - &w->cache_ivclst); - - win_set_invert_color(ps, w, invert_color_new); -} - -static void -win_set_blur_background(session_t *ps, win *w, bool blur_background_new) { - if (w->blur_background == blur_background_new) return; - - w->blur_background = blur_background_new; - - // Only consider window damaged if it's previously painted with background - // blurred - if (!win_is_solid(ps, w) - || (ps->o.blur_background_frame && w->frame_opacity)) - add_damage_win(ps, w); -} - -/** - * Determine if a window should have background blurred. - */ -static void -win_determine_blur_background(session_t *ps, win *w) { - if (IsViewable != w->a.map_state) - return; - - bool blur_background_new = ps->o.blur_background - && !win_match(ps, w, ps->o.blur_background_blacklist, &w->cache_bbblst); - - win_set_blur_background(ps, w, blur_background_new); -} - -/** - * Update window opacity according to opacity rules. - */ -static void -win_update_opacity_rule(session_t *ps, win *w) { - if (IsViewable != w->a.map_state) - return; - - opacity_t opacity = OPAQUE; - bool is_set = false; - void *val = NULL; - if (c2_matchd(ps, w, ps->o.opacity_rules, &w->cache_oparule, &val)) { - opacity = ((double) (long) val) / 100.0 * OPAQUE; - is_set = true; - } - - if (is_set == w->opacity_is_set && opacity == w->opacity_set) - return; - - w->opacity_set = opacity; - w->opacity_is_set = is_set; - if (!is_set) - wid_rm_opacity_prop(ps, w->id); - else - wid_set_opacity_prop(ps, w->id, opacity); -} - -/** - * Function to be called on window type changes. - */ -static void -win_on_wtype_change(session_t *ps, win *w) { - win_determine_shadow(ps, w); - win_determine_fade(ps, w); - win_update_focused(ps, w); - if (ps->o.invert_color_list) - win_determine_invert_color(ps, w); - if (ps->o.opacity_rules) - win_update_opacity_rule(ps, w); -} - -/** - * Function to be called on window data changes. - */ -static void -win_on_factor_change(session_t *ps, win *w) { - if (ps->o.shadow_blacklist) - win_determine_shadow(ps, w); - if (ps->o.fade_blacklist) - win_determine_fade(ps, w); - if (ps->o.invert_color_list) - win_determine_invert_color(ps, w); - if (ps->o.focus_blacklist) - win_update_focused(ps, w); - if (ps->o.blur_background_blacklist) - win_determine_blur_background(ps, w); - if (ps->o.opacity_rules) - win_update_opacity_rule(ps, w); - if (IsViewable == w->a.map_state && ps->o.paint_blacklist) - w->paint_excluded = win_match(ps, w, ps->o.paint_blacklist, - &w->cache_pblst); - if (IsViewable == w->a.map_state && ps->o.unredir_if_possible_blacklist) - w->unredir_if_possible_excluded = win_match(ps, w, - ps->o.unredir_if_possible_blacklist, &w->cache_uipblst); -} - -/** - * Update cache data in struct _win that depends on window size. - */ -static void -calc_win_size(session_t *ps, win *w) { - w->widthb = w->a.width + w->a.border_width * 2; - w->heightb = w->a.height + w->a.border_width * 2; - calc_shadow_geometry(ps, w); - w->flags |= WFLAG_SIZE_CHANGE; -} - -/** - * Calculate and update geometry of the shadow of a window. - */ -static void -calc_shadow_geometry(session_t *ps, win *w) { - w->shadow_dx = ps->o.shadow_offset_x; - w->shadow_dy = ps->o.shadow_offset_y; - w->shadow_width = w->widthb + ps->gaussian_map->size; - w->shadow_height = w->heightb + ps->gaussian_map->size; -} - -/** - * Update window type. - */ -static void -win_upd_wintype(session_t *ps, win *w) { - const wintype_t wtype_old = w->window_type; - - // Detect window type here - w->window_type = wid_get_prop_wintype(ps, w->client_win); - - // Conform to EWMH standard, if _NET_WM_WINDOW_TYPE is not present, take - // override-redirect windows or windows without WM_TRANSIENT_FOR as - // _NET_WM_WINDOW_TYPE_NORMAL, otherwise as _NET_WM_WINDOW_TYPE_DIALOG. - if (WINTYPE_UNKNOWN == w->window_type) { - if (w->a.override_redirect - || !wid_has_prop(ps, w->client_win, ps->atom_transient)) - w->window_type = WINTYPE_NORMAL; - else - w->window_type = WINTYPE_DIALOG; - } - - if (w->window_type != wtype_old) - win_on_wtype_change(ps, w); -} - -/** - * Mark a window as the client window of another. - * - * @param ps current session - * @param w struct _win of the parent window - * @param client window ID of the client window - */ -static void -win_mark_client(session_t *ps, win *w, Window client) { - w->client_win = client; - - // If the window isn't mapped yet, stop here, as the function will be - // called in map_win() - if (IsViewable != w->a.map_state) - return; - - XSelectInput(ps->dpy, client, - determine_evmask(ps, client, WIN_EVMODE_CLIENT)); - - // Make sure the XSelectInput() requests are sent - XFlush(ps->dpy); - - win_upd_wintype(ps, w); - - // Get frame widths. The window is in damaged area already. - if (ps->o.frame_opacity) - get_frame_extents(ps, w, client); - - // Get window group - if (ps->o.track_leader) - win_update_leader(ps, w); - - // Get window name and class if we are tracking them - if (ps->o.track_wdata) { - win_get_name(ps, w); - win_get_class(ps, w); - win_get_role(ps, w); - } - - // Update everything related to conditions - win_on_factor_change(ps, w); - - // Update window focus state - win_update_focused(ps, w); -} - -/** - * Unmark current client window of a window. - * - * @param ps current session - * @param w struct _win of the parent window - */ -static void -win_unmark_client(session_t *ps, win *w) { - Window client = w->client_win; - - w->client_win = None; - - // Recheck event mask - XSelectInput(ps->dpy, client, - determine_evmask(ps, client, WIN_EVMODE_UNKNOWN)); -} - -/** - * Recheck client window of a window. - * - * @param ps current session - * @param w struct _win of the parent window - */ -static void -win_recheck_client(session_t *ps, win *w) { - // Initialize wmwin to false - w->wmwin = false; - - // Look for the client window - - // Always recursively look for a window with WM_STATE, as Fluxbox - // sets override-redirect flags on all frame windows. - Window cw = find_client_win(ps, w->id); -#ifdef DEBUG_CLIENTWIN - if (cw) - printf_dbgf("(%#010lx): client %#010lx\n", w->id, cw); -#endif - // Set a window's client window to itself if we couldn't find a - // client window - if (!cw) { - cw = w->id; - w->wmwin = !w->a.override_redirect; -#ifdef DEBUG_CLIENTWIN - printf_dbgf("(%#010lx): client self (%s)\n", w->id, - (w->wmwin ? "wmwin": "override-redirected")); -#endif - } - - // Unmark the old one - if (w->client_win && w->client_win != cw) - win_unmark_client(ps, w); - - // Mark the new one - win_mark_client(ps, w, cw); -} - -static bool -add_win(session_t *ps, Window id, Window prev) { - static const win win_def = { - .next = NULL, - .prev_trans = NULL, - - .id = None, - .a = { }, -#ifdef CONFIG_XINERAMA - .xinerama_scr = -1, -#endif - .pictfmt = NULL, - .mode = WMODE_TRANS, - .damaged = false, - .damage = None, - .pixmap_damaged = false, - .paint = PAINT_INIT, - .border_size = None, - .extents = None, - .flags = 0, - .need_configure = false, - .queue_configure = { }, - .reg_ignore = None, - .widthb = 0, - .heightb = 0, - .destroyed = false, - .bounding_shaped = false, - .rounded_corners = false, - .to_paint = false, - .in_openclose = false, - - .client_win = None, - .window_type = WINTYPE_UNKNOWN, - .wmwin = false, - .leader = None, - .cache_leader = None, - - .focused = false, - .focused_force = UNSET, - - .name = NULL, - .class_instance = NULL, - .class_general = NULL, - .role = NULL, - .cache_sblst = NULL, - .cache_fblst = NULL, - .cache_fcblst = NULL, - .cache_ivclst = NULL, - .cache_bbblst = NULL, - .cache_oparule = NULL, - - .opacity = 0, - .opacity_tgt = 0, - .has_opacity_prop = false, - .opacity_prop = OPAQUE, - .opacity_is_set = false, - .opacity_set = OPAQUE, - - .fade = false, - .fade_force = UNSET, - .fade_callback = NULL, - - .frame_opacity = 0.0, - .frame_extents = MARGIN_INIT, - - .shadow = false, - .shadow_force = UNSET, - .shadow_opacity = 0.0, - .shadow_dx = 0, - .shadow_dy = 0, - .shadow_width = 0, - .shadow_height = 0, - .shadow_paint = PAINT_INIT, - .prop_shadow = -1, - - .dim = false, - - .invert_color = false, - .invert_color_force = UNSET, - - .blur_background = false, - }; - - // Reject overlay window and already added windows - if (id == ps->overlay || find_win(ps, id)) { - return false; - } - - // Allocate and initialize the new win structure - win *new = malloc(sizeof(win)); - -#ifdef DEBUG_EVENTS - printf_dbgf("(%#010lx): %p\n", id, new); -#endif - - if (!new) { - printf_errf("(%#010lx): Failed to allocate memory for the new window.", id); - return false; - } - - memcpy(new, &win_def, sizeof(win)); - - // Find window insertion point - win **p = NULL; - if (prev) { - for (p = &ps->list; *p; p = &(*p)->next) { - if ((*p)->id == prev && !(*p)->destroyed) - break; - } - } else { - p = &ps->list; - } - - // Fill structure - new->id = id; - - set_ignore_next(ps); - if (!XGetWindowAttributes(ps->dpy, id, &new->a) - || IsUnviewable == new->a.map_state) { - // Failed to get window attributes probably means the window is gone - // already. IsUnviewable means the window is already reparented - // elsewhere. - free(new); - return false; - } - - // Delay window mapping - int map_state = new->a.map_state; - assert(IsViewable == map_state || IsUnmapped == map_state); - new->a.map_state = IsUnmapped; - - if (InputOutput == new->a.class) { - // Get window picture format - new->pictfmt = XRenderFindVisualFormat(ps->dpy, new->a.visual); - - // Create Damage for window - set_ignore_next(ps); - new->damage = XDamageCreate(ps->dpy, id, XDamageReportNonEmpty); - } - - calc_win_size(ps, new); - - new->next = *p; - *p = new; - -#ifdef CONFIG_DBUS - // Send D-Bus signal - if (ps->o.dbus) { - cdbus_ev_win_added(ps, new); - } -#endif - - if (IsViewable == map_state) { - map_win(ps, id); - } - - return true; -} - static void restack_win(session_t *ps, win *w, Window new_above) { Window old_above; @@ -3385,367 +2701,6 @@ expose_root(session_t *ps, XRectangle *rects, int nrects) { XserverRegion region = XFixesCreateRegion(ps->dpy, rects, nrects); add_damage(ps, region); } - -/** - * Get the value of a type-Window property of a window. - * - * @return the value if successful, 0 otherwise - */ -static Window -wid_get_prop_window(session_t *ps, Window wid, Atom aprop) { - // Get the attribute - Window p = None; - winprop_t prop = wid_get_prop(ps, wid, aprop, 1L, XA_WINDOW, 32); - - // Return it - if (prop.nitems) { - p = *prop.data.p32; - } - - free_winprop(&prop); - - return p; -} - -/** - * Update focused state of a window. - */ -static void -win_update_focused(session_t *ps, win *w) { - if (UNSET != w->focused_force) { - w->focused = w->focused_force; - } - else { - w->focused = win_is_focused_real(ps, w); - - // Use wintype_focus, and treat WM windows and override-redirected - // windows specially - if (ps->o.wintype_focus[w->window_type] - || (ps->o.mark_wmwin_focused && w->wmwin) - || (ps->o.mark_ovredir_focused - && w->id == w->client_win && !w->wmwin) - || (IsViewable == w->a.map_state && win_match(ps, w, ps->o.focus_blacklist, &w->cache_fcblst))) - w->focused = true; - - // If window grouping detection is enabled, mark the window active if - // its group is - if (ps->o.track_leader && ps->active_leader - && win_get_leader(ps, w) == ps->active_leader) { - w->focused = true; - } - } - - // Always recalculate the window target opacity, since some opacity-related - // options depend on the output value of win_is_focused_real() instead of - // w->focused - w->flags |= WFLAG_OPCT_CHANGE; -} - -/** - * Set real focused state of a window. - */ -static void -win_set_focused(session_t *ps, win *w, bool focused) { - // Unmapped windows will have their focused state reset on map - if (IsUnmapped == w->a.map_state) - return; - - if (win_is_focused_real(ps, w) == focused) return; - - if (focused) { - if (ps->active_win) - win_set_focused(ps, ps->active_win, false); - ps->active_win = w; - } - else if (w == ps->active_win) - ps->active_win = NULL; - - assert(win_is_focused_real(ps, w) == focused); - - win_on_focus_change(ps, w); -} - -/** - * Handle window focus change. - */ -static void -win_on_focus_change(session_t *ps, win *w) { - // If window grouping detection is enabled - if (ps->o.track_leader) { - Window leader = win_get_leader(ps, w); - - // If the window gets focused, replace the old active_leader - if (win_is_focused_real(ps, w) && leader != ps->active_leader) { - Window active_leader_old = ps->active_leader; - - ps->active_leader = leader; - - group_update_focused(ps, active_leader_old); - group_update_focused(ps, leader); - } - // If the group get unfocused, remove it from active_leader - else if (!win_is_focused_real(ps, w) && leader && leader == ps->active_leader - && !group_is_focused(ps, leader)) { - ps->active_leader = None; - group_update_focused(ps, leader); - } - - // The window itself must be updated anyway - win_update_focused(ps, w); - } - // Otherwise, only update the window itself - else { - win_update_focused(ps, w); - } - - // Update everything related to conditions - win_on_factor_change(ps, w); - -#ifdef CONFIG_DBUS - // Send D-Bus signal - if (ps->o.dbus) { - if (win_is_focused_real(ps, w)) - cdbus_ev_win_focusin(ps, w); - else - cdbus_ev_win_focusout(ps, w); - } -#endif -} - -/** - * Update leader of a window. - */ -static void -win_update_leader(session_t *ps, win *w) { - Window leader = None; - - // Read the leader properties - if (ps->o.detect_transient && !leader) - leader = wid_get_prop_window(ps, w->client_win, ps->atom_transient); - - if (ps->o.detect_client_leader && !leader) - leader = wid_get_prop_window(ps, w->client_win, ps->atom_client_leader); - - win_set_leader(ps, w, leader); - -#ifdef DEBUG_LEADER - printf_dbgf("(%#010lx): client %#010lx, leader %#010lx, cache %#010lx\n", w->id, w->client_win, w->leader, win_get_leader(ps, w)); -#endif -} - -/** - * Set leader of a window. - */ -static void -win_set_leader(session_t *ps, win *w, Window nleader) { - // If the leader changes - if (w->leader != nleader) { - Window cache_leader_old = win_get_leader(ps, w); - - w->leader = nleader; - - // Forcefully do this to deal with the case when a child window - // gets mapped before parent, or when the window is a waypoint - clear_cache_win_leaders(ps); - - // Update the old and new window group and active_leader if the window - // could affect their state. - Window cache_leader = win_get_leader(ps, w); - if (win_is_focused_real(ps, w) && cache_leader_old != cache_leader) { - ps->active_leader = cache_leader; - - group_update_focused(ps, cache_leader_old); - group_update_focused(ps, cache_leader); - } - // Otherwise, at most the window itself is affected - else { - win_update_focused(ps, w); - } - - // Update everything related to conditions - win_on_factor_change(ps, w); - } -} - -/** - * Internal function of win_get_leader(). - */ -static Window -win_get_leader_raw(session_t *ps, win *w, int recursions) { - // Rebuild the cache if needed - if (!w->cache_leader && (w->client_win || w->leader)) { - // Leader defaults to client window - if (!(w->cache_leader = w->leader)) - w->cache_leader = w->client_win; - - // If the leader of this window isn't itself, look for its ancestors - if (w->cache_leader && w->cache_leader != w->client_win) { - win *wp = find_toplevel(ps, w->cache_leader); - if (wp) { - // Dead loop? - if (recursions > WIN_GET_LEADER_MAX_RECURSION) - return None; - - w->cache_leader = win_get_leader_raw(ps, wp, recursions + 1); - } - } - } - - return w->cache_leader; -} - -/** - * Get the value of a text property of a window. - */ -bool -wid_get_text_prop(session_t *ps, Window wid, Atom prop, - char ***pstrlst, int *pnstr) { - XTextProperty text_prop = { NULL, None, 0, 0 }; - - if (!(XGetTextProperty(ps->dpy, wid, &text_prop, prop) && text_prop.value)) - return false; - - if (Success != - XmbTextPropertyToTextList(ps->dpy, &text_prop, pstrlst, pnstr) - || !*pnstr) { - *pnstr = 0; - if (*pstrlst) - XFreeStringList(*pstrlst); - cxfree(text_prop.value); - return false; - } - - cxfree(text_prop.value); - return true; -} - -/** - * Get the name of a window from window ID. - */ -static bool -wid_get_name(session_t *ps, Window wid, char **name) { - XTextProperty text_prop = { NULL, None, 0, 0 }; - char **strlst = NULL; - int nstr = 0; - - if (!(wid_get_text_prop(ps, wid, ps->atom_name_ewmh, &strlst, &nstr))) { -#ifdef DEBUG_WINDATA - printf_dbgf("(%#010lx): _NET_WM_NAME unset, falling back to WM_NAME.\n", wid); -#endif - - if (!(XGetWMName(ps->dpy, wid, &text_prop) && text_prop.value)) { - return false; - } - if (Success != - XmbTextPropertyToTextList(ps->dpy, &text_prop, &strlst, &nstr) - || !nstr || !strlst) { - if (strlst) - XFreeStringList(strlst); - cxfree(text_prop.value); - return false; - } - cxfree(text_prop.value); - } - - *name = mstrcpy(strlst[0]); - - XFreeStringList(strlst); - - return true; -} - -/** - * Get the role of a window from window ID. - */ -static bool -wid_get_role(session_t *ps, Window wid, char **role) { - char **strlst = NULL; - int nstr = 0; - - if (!wid_get_text_prop(ps, wid, ps->atom_role, &strlst, &nstr)) { - return false; - } - - *role = mstrcpy(strlst[0]); - - XFreeStringList(strlst); - - return true; -} - -/** - * Retrieve a string property of a window and update its win - * structure. - */ -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)) { - int ret = -1; - char *prop_old = *tgt; - - // Can't do anything if there's no client window - if (!w->client_win) - return false; - - // Get the property - ret = func_wid_get_prop_str(ps, w->client_win, tgt); - - // Return -1 if func_wid_get_prop_str() failed, 0 if the property - // doesn't change, 1 if it changes - if (!ret) - ret = -1; - else if (prop_old && !strcmp(*tgt, prop_old)) - ret = 0; - else - ret = 1; - - // Keep the old property if there's no new one - if (*tgt != prop_old) - free(prop_old); - - return ret; -} - -/** - * Retrieve the WM_CLASS of a window and update its - * win structure. - */ -static bool -win_get_class(session_t *ps, win *w) { - char **strlst = NULL; - int nstr = 0; - - // Can't do anything if there's no client window - if (!w->client_win) - return false; - - // Free and reset old strings - free(w->class_instance); - free(w->class_general); - w->class_instance = NULL; - w->class_general = NULL; - - // Retrieve the property string list - if (!wid_get_text_prop(ps, w->client_win, ps->atom_class, &strlst, &nstr)) - return false; - - // Copy the strings if successful - w->class_instance = mstrcpy(strlst[0]); - - if (nstr > 1) - w->class_general = mstrcpy(strlst[1]); - - XFreeStringList(strlst); - -#ifdef DEBUG_WINDATA - printf_dbgf("(%#010lx): client = %#010lx, " - "instance = \"%s\", general = \"%s\"\n", - w->id, w->client_win, w->class_instance, w->class_general); -#endif - - return true; -} - /** * Force a full-screen repaint. */ @@ -4201,9 +3156,9 @@ ev_property_notify(session_t *ps, XPropertyEvent *ev) { if (ps->o.frame_opacity && ev->atom == ps->atom_frame_extents) { win *w = find_toplevel(ps, ev->window); if (w) { - get_frame_extents(ps, w, ev->window); + win_get_frame_extents(ps, w, ev->window); // If frame extents change, the window needs repaint - add_damage_win(ps, w); + add_damage_from_win(ps, w); } } @@ -4282,7 +3237,7 @@ ev_shape_notify(session_t *ps, XShapeEvent *ev) { // Mark the old border_size as damaged add_damage(ps, w->border_size); - w->border_size = border_size(ps, w, true); + w->border_size = win_border_size(ps, w, true); // Mark the new border_size as damaged add_damage(ps, copy_region(ps, w->border_size)); @@ -6991,7 +5946,7 @@ session_run(session_t *ps) { session_destroy(ps); exit(1); } - add_damage_win(ps, w); + add_damage_from_win(ps, w); } else { force_repaint(ps); diff --git a/src/compton.h b/src/compton.h index 201fd1a5..804100c3 100644 --- a/src/compton.h +++ b/src/compton.h @@ -29,28 +29,33 @@ #include "c2.h" // == Functions == +// TODO move static inline functions that are only used in compton.c, into +// compton.c // inline functions must be made static to compile correctly under clang: // http://clang.llvm.org/compatibility.html#inline -// Helper functions +long determine_evmask(session_t *ps, Window wid, win_evmode_t mode); -static void -discard_ignore(session_t *ps, unsigned long sequence); +Window +find_client_win(session_t *ps, Window w); -static void -set_ignore(session_t *ps, unsigned long sequence); +win *find_toplevel2(session_t *ps, Window wid); -/** - * Ignore X errors caused by next X request. - */ -static inline void -set_ignore_next(session_t *ps) { - set_ignore(ps, NextRequest(ps->dpy)); -} +void set_fade_callback(session_t *ps, win *w, + void (*callback) (session_t *ps, win *w), bool exec_callback); -static int -should_ignore(session_t *ps, unsigned long sequence); +void map_win(session_t *ps, Window id); + +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, const reg_data_t *pcache_reg +#ifdef CONFIG_OPENGL + , const glx_prog_main_t *pprogram +#endif + ); /** * Reset filter on a Picture. @@ -368,13 +373,6 @@ wid_set_text_prop(session_t *ps, Window wid, Atom prop_atom, char *str) { return true; } -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. */ @@ -386,33 +384,6 @@ check_fade_fin(session_t *ps, win *w) { } } -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. */ @@ -455,25 +426,6 @@ wid_get_children(session_t *ps, Window w, 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. @@ -508,18 +460,6 @@ win_calc_frame_extents(session_t *ps, const win *w) { return result; } -static inline void -wid_set_opacity_prop(session_t *ps, Window wid, opacity_t val) { - const unsigned long v = val; - XChangeProperty(ps->dpy, wid, ps->atom_opacity, XA_CARDINAL, 32, - PropModeReplace, (unsigned char *) &v, 1); -} - -static inline void -wid_rm_opacity_prop(session_t *ps, Window wid) { - XDeleteProperty(ps->dpy, wid, ps->atom_opacity); -} - /** * Dump an drawable's info. */ @@ -537,8 +477,6 @@ dump_drawable(session_t *ps, Drawable drawable) { } } -static void -win_rounded_corners(session_t *ps, win *w); /** * Validate a pixmap. @@ -567,29 +505,6 @@ win_validate_pixmap(session_t *ps, win *w) { free_paint(ps, &w->paint); } -/** - * Wrapper of c2_match(). - */ -static inline bool -win_match(session_t *ps, win *w, c2_lptr_t *condlst, const c2_lptr_t **cache) { - return c2_match(ps, w, condlst, cache); -} - -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); - /** * Find matched window. */ @@ -604,79 +519,6 @@ find_win_all(session_t *ps, const Window wid) { return w; } -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 - && win_is_focused_real(ps, w)) - 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, const reg_data_t *pcache_reg -#ifdef CONFIG_OPENGL - , const glx_prog_main_t *pprogram -#endif - ); - #ifdef CONFIG_OPENGL #define \ render(ps, x, y, dx, dy, wid, hei, opacity, argb, neg, pict, ptex, reg_paint, pcache_reg, pprogram) \ @@ -718,11 +560,6 @@ set_tgt_clip(session_t *ps, XserverRegion reg, const reg_data_t *pcache_reg) { } } -static bool -xr_blur_dst(session_t *ps, Picture tgt_buffer, - int x, int y, int wid, int hei, XFixed **blur_kerns, - XserverRegion reg_clip); - /** * Normalize a convolution kernel. */ @@ -736,292 +573,6 @@ normalize_conv_kern(int wid, int hei, XFixed *kern) { kern[i] = XDoubleToFixed(XFixedToDouble(kern[i]) * factor); } -static void -paint_all(session_t *ps, XserverRegion region, XserverRegion region_real, 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 bool -wid_get_opacity_prop(session_t *ps, Window wid, opacity_t def, opacity_t *out); - -/** - * Reread opacity property of a window. - */ -static inline void -win_update_opacity_prop(session_t *ps, win *w) { - // get frame opacity first - w->has_opacity_prop = - wid_get_opacity_prop(ps, w->id, OPAQUE, &w->opacity_prop); - - if (w->has_opacity_prop) - // opacity found - return; - - if (ps->o.detect_client_opacity && w->client_win && w->id == w->client_win) - // checking client opacity not allowed - return; - - // get client opacity - w->has_opacity_prop = - wid_get_opacity_prop(ps, w->client_win, OPAQUE, &w->opacity_prop); -} - -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_on_focus_change(session_t *ps, win *w); - -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_set_shadow(session_t *ps, win *w, bool shadow_new); - -static void -win_determine_shadow(session_t *ps, win *w); - -static void -win_set_invert_color(session_t *ps, win *w, bool invert_color_new); - -static void -win_determine_invert_color(session_t *ps, win *w); - -static void -win_set_blur_background(session_t *ps, win *w, bool blur_background_new); - -static void -win_determine_blur_background(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 -calc_win_size(session_t *ps, win *w); - -static void -calc_shadow_geometry(session_t *ps, win *w); - -static void -win_upd_wintype(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, win *w); - -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 -xerror(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(int ret); - -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. */ @@ -1130,45 +681,6 @@ is_region_empty(const session_t *ps, XserverRegion region, 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); - -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_OPENGL /** * Ensure we have a GLX context. @@ -1183,80 +695,11 @@ ensure_glx_context(session_t *ps) { } #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); - -static bool -vsync_opengl_mswc_init(session_t *ps); - -#ifdef CONFIG_OPENGL -static int -vsync_opengl_wait(session_t *ps); - -static int -vsync_opengl_oml_wait(session_t *ps); - -static void -vsync_opengl_swc_deinit(session_t *ps); - -static void -vsync_opengl_mswc_deinit(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 bool -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 -tmout_unredir_callback(session_t *ps, timeout_t *tmout); - -static bool -mainloop(session_t *ps); - -#ifdef CONFIG_XINERAMA -static void -cxinerama_upd_scrs(session_t *ps); -#endif - /** * Get the Xinerama screen a window is on. * @@ -1277,19 +720,4 @@ cxinerama_win_upd_scr(session_t *ps, win *w) { #endif } -static void -cxinerama_upd_scrs(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); - // vim: set et sw=2 : diff --git a/src/win.c b/src/win.c new file mode 100644 index 00000000..3d74a502 --- /dev/null +++ b/src/win.c @@ -0,0 +1,1261 @@ +#include +#include +#include +#include + +#include "common.h" +#include "compton.h" +#include "c2.h" +#include "x.h" + +#include "win.h" + +/** + * Clear leader cache of all windows. + */ +static inline void +clear_cache_win_leaders(session_t *ps) { + for (win *w = ps->list; w; w = w->next) + w->cache_leader = None; +} + +static inline void +wid_set_opacity_prop(session_t *ps, Window wid, opacity_t val) { + const unsigned long v = val; + XChangeProperty(ps->dpy, wid, ps->atom_opacity, XA_CARDINAL, 32, + PropModeReplace, (unsigned char *) &v, 1); +} + +static inline void +wid_rm_opacity_prop(session_t *ps, Window wid) { + XDeleteProperty(ps->dpy, wid, ps->atom_opacity); +} + +/** + * 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; +} + +/** + * 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 + && win_is_focused_real(ps, w)) + return true; + } + + return false; +} + +/** + * Get a rectangular region a window occupies, excluding shadow. + */ +XserverRegion +win_get_region(session_t *ps, win *w, bool use_offset) { + XRectangle r; + + r.x = (use_offset ? w->a.x: 0); + r.y = (use_offset ? w->a.y: 0); + r.width = w->widthb; + r.height = w->heightb; + + return XFixesCreateRegion(ps->dpy, &r, 1); +} + + +/** + * Get a rectangular region a window occupies, excluding frame and shadow. + */ +XserverRegion +win_get_region_noframe(session_t *ps, win *w, bool use_offset) { + const margin_t extents = win_calc_frame_extents(ps, w); + XRectangle r; + + r.x = (use_offset ? w->a.x: 0) + extents.left; + r.y = (use_offset ? w->a.y: 0) + extents.top; + r.width = max_i(w->a.width - extents.left - extents.right, 0); + r.height = max_i(w->a.height - extents.top - extents.bottom, 0); + + if (r.width > 0 && r.height > 0) + return XFixesCreateRegion(ps->dpy, &r, 1); + else + return XFixesCreateRegion(ps->dpy, NULL, 0); +} + +/** + * Add a window to damaged area. + * + * @param ps current session + * @param w struct _win element representing the window + */ +void add_damage_from_win(session_t *ps, win *w) { + if (w->extents) { + add_damage(ps, copy_region(ps, w->extents)); + } +} + +/** + * Check if a window has rounded corners. + */ +void win_rounded_corners(session_t *ps, win *w) { + w->rounded_corners = false; + + if (!w->bounding_shaped) + return; + + // Fetch its bounding region + if (!w->border_size) + w->border_size = win_border_size(ps, w, true); + + // Quit if border_size() returns None + if (!w->border_size) + return; + + // Determine the minimum width/height of a rectangle that could mark + // a window as having rounded corners + unsigned short minwidth = max_i(w->widthb * (1 - ROUNDED_PERCENT), + w->widthb - ROUNDED_PIXELS); + unsigned short minheight = max_i(w->heightb * (1 - ROUNDED_PERCENT), + w->heightb - ROUNDED_PIXELS); + + // Get the rectangles in the bounding region + int nrects = 0, i; + XRectangle *rects = XFixesFetchRegion(ps->dpy, w->border_size, &nrects); + if (!rects) + return; + + // Look for a rectangle large enough for this window be considered + // having rounded corners + for (i = 0; i < nrects; ++i) + if (rects[i].width >= minwidth && rects[i].height >= minheight) { + w->rounded_corners = true; + break; + } + + cxfree(rects); +} + +int win_get_name(session_t *ps, win *w) { + XTextProperty text_prop = { NULL, None, 0, 0 }; + char **strlst = NULL; + int nstr = 0; + + if (!w->client_win) + return 0; + + if (!(wid_get_text_prop(ps, w->client_win, ps->atom_name_ewmh, &strlst, &nstr))) { +#ifdef DEBUG_WINDATA + printf_dbgf("(%#010lx): _NET_WM_NAME unset, falling back to WM_NAME.\n", wid); +#endif + + if (!(XGetWMName(ps->dpy, w->client_win, &text_prop) && text_prop.value)) { + return -1; + } + if (Success != + XmbTextPropertyToTextList(ps->dpy, &text_prop, &strlst, &nstr) + || !nstr || !strlst) { + if (strlst) + XFreeStringList(strlst); + cxfree(text_prop.value); + return -1; + } + cxfree(text_prop.value); + } + + int ret = 0; + if (strcmp(w->name, strlst[0]) != 0) { + ret = 1; + free(w->name); + w->name = mstrcpy(strlst[0]); + } + + XFreeStringList(strlst); + +#ifdef DEBUG_WINDATA + printf_dbgf("(%#010lx): client = %#010lx, name = \"%s\", " + "ret = %d\n", w->id, w->client_win, w->name, ret); +#endif + return ret; +} + +int win_get_role(session_t *ps, win *w) { + char **strlst = NULL; + int nstr = 0; + + if (!wid_get_text_prop(ps, w->client_win, ps->atom_role, &strlst, &nstr)) + return -1; + + int ret = 0; + if (strcmp(w->role, strlst[0]) != 0) { + ret = 1; + free(w->role); + w->role = mstrcpy(strlst[0]); + } + + XFreeStringList(strlst); + +#ifdef DEBUG_WINDATA + printf_dbgf("(%#010lx): client = %#010lx, role = \"%s\", " + "ret = %d\n", w->id, w->client_win, w->role, ret); +#endif + return ret; +} + +/** + * Check if a window is bounding-shaped. + */ +static inline bool win_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; +} + +wintype_t wid_get_prop_wintype(session_t *ps, Window wid) { + set_ignore_next(ps); + winprop_t prop = wid_get_prop(ps, wid, ps->atom_win_type, 32L, XA_ATOM, 32); + + for (unsigned i = 0; i < prop.nitems; ++i) { + for (wintype_t j = 1; j < NUM_WINTYPES; ++j) { + if (ps->atoms_wintypes[j] == (Atom)prop.data.p32[i]) { + free_winprop(&prop); + return j; + } + } + } + + free_winprop(&prop); + + return WINTYPE_UNKNOWN; +} + +bool wid_get_opacity_prop(session_t *ps, Window wid, opacity_t def, + opacity_t *out) { + bool ret = false; + *out = def; + + winprop_t prop = wid_get_prop(ps, wid, ps->atom_opacity, 1L, XA_CARDINAL, 32); + + if (prop.nitems) { + *out = *prop.data.p32; + ret = true; + } + + free_winprop(&prop); + + return ret; +} + +void win_determine_mode(session_t *ps, win *w) { + winmode_t mode = WMODE_SOLID; + + if (w->pictfmt && w->pictfmt->type == PictTypeDirect && + w->pictfmt->direct.alphaMask) { + mode = WMODE_ARGB; + } else if (w->opacity != OPAQUE) { + mode = WMODE_TRANS; + } else { + mode = WMODE_SOLID; + } + + w->mode = mode; +} + +/** + * Calculate and set the opacity of a window. + * + * If window is inactive and inactive_opacity_override is set, the + * priority is: (Simulates the old behavior) + * + * inactive_opacity > _NET_WM_WINDOW_OPACITY (if not opaque) + * > window type default opacity + * + * Otherwise: + * + * _NET_WM_WINDOW_OPACITY (if not opaque) + * > window type default opacity (if not opaque) + * > inactive_opacity + * + * @param ps current session + * @param w struct _win object representing the window + */ +void win_calc_opacity(session_t *ps, win *w) { + opacity_t opacity = OPAQUE; + + if (w->destroyed || IsViewable != w->a.map_state) + opacity = 0; + else { + // Try obeying opacity property and window type opacity firstly + if (w->has_opacity_prop) + opacity = w->opacity_prop; + else if (!isnan(ps->o.wintype_opacity[w->window_type])) + opacity = ps->o.wintype_opacity[w->window_type] * OPAQUE; + else { + // Respect active_opacity only when the window is physically focused + if (win_is_focused_real(ps, w)) + opacity = ps->o.active_opacity; + else if (false == w->focused) + // Respect inactive_opacity in some cases + opacity = ps->o.inactive_opacity; + } + + // respect inactive override + if (ps->o.inactive_opacity_override && false == w->focused) + opacity = ps->o.inactive_opacity; + } + + w->opacity_tgt = opacity; +} + +/** + * Determine whether a window is to be dimmed. + */ +void win_calc_dim(session_t *ps, win *w) { + bool dim; + + // Make sure we do nothing if the window is unmapped / destroyed + if (w->destroyed || IsViewable != w->a.map_state) + return; + + if (ps->o.inactive_dim && !(w->focused)) { + dim = true; + } else { + dim = false; + } + + if (dim != w->dim) { + w->dim = dim; + add_damage_from_win(ps, w); + } +} + +/** + * Determine if a window should fade on opacity change. + */ +void win_determine_fade(session_t *ps, win *w) { + // To prevent it from being overwritten by last-paint value if the window is + // unmapped on next frame, write w->fade_last as well + if (UNSET != w->fade_force) + w->fade_last = w->fade = w->fade_force; + else if (ps->o.no_fading_openclose && w->in_openclose) + w->fade_last = w->fade = false; + else if (ps->o.no_fading_destroyed_argb && w->destroyed && + WMODE_ARGB == w->mode && w->client_win && w->client_win != w->id) { + w->fade_last = w->fade = false; + } + // Ignore other possible causes of fading state changes after window + // gets unmapped + else if (IsViewable != w->a.map_state) { + } else if (c2_match(ps, w, ps->o.fade_blacklist, &w->cache_fblst)) + w->fade = false; + else + w->fade = ps->o.wintype_fade[w->window_type]; +} + +/** + * Update window-shape. + */ +void win_update_shape_raw(session_t *ps, win *w) { + if (ps->shape_exists) { + w->bounding_shaped = win_bounding_shaped(ps, w->id); + if (w->bounding_shaped && ps->o.detect_rounded_corners) + win_rounded_corners(ps, w); + } +} + +/** + * Update window-shape related information. + */ +void win_update_shape(session_t *ps, win *w) { + if (ps->shape_exists) { + // bool bounding_shaped_old = w->bounding_shaped; + + win_update_shape_raw(ps, w); + + win_on_factor_change(ps, w); + + /* + // If clear_shadow state on the window possibly changed, destroy the old + // shadow_pict + if (ps->o.clear_shadow && w->bounding_shaped != bounding_shaped_old) + free_paint(ps, &w->shadow_paint); + */ + } +} + +/** + * Reread _COMPTON_SHADOW property from a window. + * + * The property must be set on the outermost window, usually the WM frame. + */ +void win_update_prop_shadow_raw(session_t *ps, win *w) { + winprop_t prop = + wid_get_prop(ps, w->id, ps->atom_compton_shadow, 1, XA_CARDINAL, 32); + + if (!prop.nitems) { + w->prop_shadow = -1; + } else { + w->prop_shadow = *prop.data.p32; + } + + free_winprop(&prop); +} + +/** + * Reread _COMPTON_SHADOW property from a window and update related + * things. + */ +void win_update_prop_shadow(session_t *ps, win *w) { + long attr_shadow_old = w->prop_shadow; + + win_update_prop_shadow_raw(ps, w); + + if (w->prop_shadow != attr_shadow_old) + win_determine_shadow(ps, w); +} + +void win_set_shadow(session_t *ps, win *w, bool shadow_new) { + if (w->shadow == shadow_new) + return; + + w->shadow = shadow_new; + + // Window extents need update on shadow state change + // Shadow geometry currently doesn't change on shadow state change + // calc_shadow_geometry(ps, w); + if (w->extents) { + // Mark the old extents as damaged if the shadow is removed + if (!w->shadow) + add_damage(ps, w->extents); + else + free_region(ps, &w->extents); + w->extents = win_extents(ps, w); + // Mark the new extents as damaged if the shadow is added + if (w->shadow) + add_damage_from_win(ps, w); + } +} + +/** + * Determine if a window should have shadow, and update things depending + * on shadow state. + */ +void win_determine_shadow(session_t *ps, win *w) { + bool shadow_new = w->shadow; + + if (UNSET != w->shadow_force) + shadow_new = w->shadow_force; + else if (IsViewable == w->a.map_state) + shadow_new = (ps->o.wintype_shadow[w->window_type] && + !c2_match(ps, w, ps->o.shadow_blacklist, &w->cache_sblst) && + !(ps->o.shadow_ignore_shaped && w->bounding_shaped && + !w->rounded_corners) && + !(ps->o.respect_prop_shadow && 0 == w->prop_shadow)); + + win_set_shadow(ps, w, shadow_new); +} + +void win_set_invert_color(session_t *ps, win *w, bool invert_color_new) { + if (w->invert_color == invert_color_new) + return; + + w->invert_color = invert_color_new; + + add_damage_from_win(ps, w); +} + +/** + * Determine if a window should have color inverted. + */ +void win_determine_invert_color(session_t *ps, win *w) { + bool invert_color_new = w->invert_color; + + if (UNSET != w->invert_color_force) + invert_color_new = w->invert_color_force; + else if (IsViewable == w->a.map_state) + invert_color_new = + c2_match(ps, w, ps->o.invert_color_list, &w->cache_ivclst); + + win_set_invert_color(ps, w, invert_color_new); +} + +void win_set_blur_background(session_t *ps, win *w, bool blur_background_new) { + if (w->blur_background == blur_background_new) + return; + + w->blur_background = blur_background_new; + + // Only consider window damaged if it's previously painted with background + // blurred + if (!win_is_solid(ps, w) || (ps->o.blur_background_frame && w->frame_opacity)) + add_damage_from_win(ps, w); +} + +/** + * Determine if a window should have background blurred. + */ +void win_determine_blur_background(session_t *ps, win *w) { + if (IsViewable != w->a.map_state) + return; + + bool blur_background_new = + ps->o.blur_background && + !c2_match(ps, w, ps->o.blur_background_blacklist, &w->cache_bbblst); + + win_set_blur_background(ps, w, blur_background_new); +} + +/** + * Update window opacity according to opacity rules. + */ +void win_update_opacity_rule(session_t *ps, win *w) { + if (IsViewable != w->a.map_state) + return; + + opacity_t opacity = OPAQUE; + bool is_set = false; + void *val = NULL; + if (c2_matchd(ps, w, ps->o.opacity_rules, &w->cache_oparule, &val)) { + opacity = ((double)(long)val) / 100.0 * OPAQUE; + is_set = true; + } + + if (is_set == w->opacity_is_set && opacity == w->opacity_set) + return; + + w->opacity_set = opacity; + w->opacity_is_set = is_set; + if (!is_set) + wid_rm_opacity_prop(ps, w->id); + else + wid_set_opacity_prop(ps, w->id, opacity); +} + +/** + * Function to be called on window type changes. + */ +void win_on_wtype_change(session_t *ps, win *w) { + win_determine_shadow(ps, w); + win_determine_fade(ps, w); + win_update_focused(ps, w); + if (ps->o.invert_color_list) + win_determine_invert_color(ps, w); + if (ps->o.opacity_rules) + win_update_opacity_rule(ps, w); +} + +/** + * Function to be called on window data changes. + */ +void win_on_factor_change(session_t *ps, win *w) { + if (ps->o.shadow_blacklist) + win_determine_shadow(ps, w); + if (ps->o.fade_blacklist) + win_determine_fade(ps, w); + if (ps->o.invert_color_list) + win_determine_invert_color(ps, w); + if (ps->o.focus_blacklist) + win_update_focused(ps, w); + if (ps->o.blur_background_blacklist) + win_determine_blur_background(ps, w); + if (ps->o.opacity_rules) + win_update_opacity_rule(ps, w); + if (IsViewable == w->a.map_state && ps->o.paint_blacklist) + w->paint_excluded = + c2_match(ps, w, ps->o.paint_blacklist, &w->cache_pblst); + if (IsViewable == w->a.map_state && ps->o.unredir_if_possible_blacklist) + w->unredir_if_possible_excluded = c2_match( + ps, w, ps->o.unredir_if_possible_blacklist, &w->cache_uipblst); +} + +/** + * Update cache data in struct _win that depends on window size. + */ +void calc_win_size(session_t *ps, win *w) { + w->widthb = w->a.width + w->a.border_width * 2; + w->heightb = w->a.height + w->a.border_width * 2; + calc_shadow_geometry(ps, w); + w->flags |= WFLAG_SIZE_CHANGE; +} + +/** + * Calculate and update geometry of the shadow of a window. + */ +void calc_shadow_geometry(session_t *ps, win *w) { + w->shadow_dx = ps->o.shadow_offset_x; + w->shadow_dy = ps->o.shadow_offset_y; + w->shadow_width = w->widthb + ps->gaussian_map->size; + w->shadow_height = w->heightb + ps->gaussian_map->size; +} + +/** + * Update window type. + */ +void win_upd_wintype(session_t *ps, win *w) { + const wintype_t wtype_old = w->window_type; + + // Detect window type here + w->window_type = wid_get_prop_wintype(ps, w->client_win); + + // Conform to EWMH standard, if _NET_WM_WINDOW_TYPE is not present, take + // override-redirect windows or windows without WM_TRANSIENT_FOR as + // _NET_WM_WINDOW_TYPE_NORMAL, otherwise as _NET_WM_WINDOW_TYPE_DIALOG. + if (WINTYPE_UNKNOWN == w->window_type) { + if (w->a.override_redirect || + !wid_has_prop(ps, w->client_win, ps->atom_transient)) + w->window_type = WINTYPE_NORMAL; + else + w->window_type = WINTYPE_DIALOG; + } + + if (w->window_type != wtype_old) + win_on_wtype_change(ps, w); +} + +/** + * Mark a window as the client window of another. + * + * @param ps current session + * @param w struct _win of the parent window + * @param client window ID of the client window + */ +void win_mark_client(session_t *ps, win *w, Window client) { + w->client_win = client; + + // If the window isn't mapped yet, stop here, as the function will be + // called in map_win() + if (IsViewable != w->a.map_state) + return; + + XSelectInput(ps->dpy, client, + determine_evmask(ps, client, WIN_EVMODE_CLIENT)); + + // Make sure the XSelectInput() requests are sent + XFlush(ps->dpy); + + win_upd_wintype(ps, w); + + // Get frame widths. The window is in damaged area already. + if (ps->o.frame_opacity) + win_get_frame_extents(ps, w, client); + + // Get window group + if (ps->o.track_leader) + win_update_leader(ps, w); + + // Get window name and class if we are tracking them + if (ps->o.track_wdata) { + win_get_name(ps, w); + win_get_class(ps, w); + win_get_role(ps, w); + } + + // Update everything related to conditions + win_on_factor_change(ps, w); + + // Update window focus state + win_update_focused(ps, w); +} + +/** + * Unmark current client window of a window. + * + * @param ps current session + * @param w struct _win of the parent window + */ +void win_unmark_client(session_t *ps, win *w) { + Window client = w->client_win; + + w->client_win = None; + + // Recheck event mask + XSelectInput(ps->dpy, client, + determine_evmask(ps, client, WIN_EVMODE_UNKNOWN)); +} + +/** + * Recheck client window of a window. + * + * @param ps current session + * @param w struct _win of the parent window + */ +void win_recheck_client(session_t *ps, win *w) { + // Initialize wmwin to false + w->wmwin = false; + + // Look for the client window + + // Always recursively look for a window with WM_STATE, as Fluxbox + // sets override-redirect flags on all frame windows. + Window cw = find_client_win(ps, w->id); +#ifdef DEBUG_CLIENTWIN + if (cw) + printf_dbgf("(%#010lx): client %#010lx\n", w->id, cw); +#endif + // Set a window's client window to itself if we couldn't find a + // client window + if (!cw) { + cw = w->id; + w->wmwin = !w->a.override_redirect; +#ifdef DEBUG_CLIENTWIN + printf_dbgf("(%#010lx): client self (%s)\n", w->id, + (w->wmwin ? "wmwin" : "override-redirected")); +#endif + } + + // Unmark the old one + if (w->client_win && w->client_win != cw) + win_unmark_client(ps, w); + + // Mark the new one + win_mark_client(ps, w, cw); +} + +// TODO: probably split into win_new (in win.c) and add_win (in compton.c) +bool add_win(session_t *ps, Window id, Window prev) { + static const win win_def = { + .next = NULL, + .prev_trans = NULL, + + .id = None, + .a = {}, +#ifdef CONFIG_XINERAMA + .xinerama_scr = -1, +#endif + .pictfmt = NULL, + .mode = WMODE_TRANS, + .damaged = false, + .damage = None, + .pixmap_damaged = false, + .paint = PAINT_INIT, + .border_size = None, + .extents = None, + .flags = 0, + .need_configure = false, + .queue_configure = {}, + .reg_ignore = None, + .widthb = 0, + .heightb = 0, + .destroyed = false, + .bounding_shaped = false, + .rounded_corners = false, + .to_paint = false, + .in_openclose = false, + + .client_win = None, + .window_type = WINTYPE_UNKNOWN, + .wmwin = false, + .leader = None, + .cache_leader = None, + + .focused = false, + .focused_force = UNSET, + + .name = NULL, + .class_instance = NULL, + .class_general = NULL, + .role = NULL, + .cache_sblst = NULL, + .cache_fblst = NULL, + .cache_fcblst = NULL, + .cache_ivclst = NULL, + .cache_bbblst = NULL, + .cache_oparule = NULL, + + .opacity = 0, + .opacity_tgt = 0, + .has_opacity_prop = false, + .opacity_prop = OPAQUE, + .opacity_is_set = false, + .opacity_set = OPAQUE, + + .fade = false, + .fade_force = UNSET, + .fade_callback = NULL, + + .frame_opacity = 0.0, + .frame_extents = MARGIN_INIT, + + .shadow = false, + .shadow_force = UNSET, + .shadow_opacity = 0.0, + .shadow_dx = 0, + .shadow_dy = 0, + .shadow_width = 0, + .shadow_height = 0, + .shadow_paint = PAINT_INIT, + .prop_shadow = -1, + + .dim = false, + + .invert_color = false, + .invert_color_force = UNSET, + + .blur_background = false, + }; + + // Reject overlay window and already added windows + if (id == ps->overlay || find_win(ps, id)) { + return false; + } + + // Allocate and initialize the new win structure + win *new = malloc(sizeof(win)); + +#ifdef DEBUG_EVENTS + printf_dbgf("(%#010lx): %p\n", id, new); +#endif + + if (!new) { + printf_errf("(%#010lx): Failed to allocate memory for the new window.", id); + return false; + } + + memcpy(new, &win_def, sizeof(win)); + + // Find window insertion point + win **p = NULL; + if (prev) { + for (p = &ps->list; *p; p = &(*p)->next) { + if ((*p)->id == prev && !(*p)->destroyed) + break; + } + } else { + p = &ps->list; + } + + // Fill structure + new->id = id; + + set_ignore_next(ps); + if (!XGetWindowAttributes(ps->dpy, id, &new->a) || + IsUnviewable == new->a.map_state) { + // Failed to get window attributes probably means the window is gone + // already. IsUnviewable means the window is already reparented + // elsewhere. + free(new); + return false; + } + + // Delay window mapping + int map_state = new->a.map_state; + assert(IsViewable == map_state || IsUnmapped == map_state); + new->a.map_state = IsUnmapped; + + if (InputOutput == new->a.class) { + // Get window picture format + new->pictfmt = XRenderFindVisualFormat(ps->dpy, new->a.visual); + + // Create Damage for window + set_ignore_next(ps); + new->damage = XDamageCreate(ps->dpy, id, XDamageReportNonEmpty); + } + + calc_win_size(ps, new); + + new->next = *p; + *p = new; + +#ifdef CONFIG_DBUS + // Send D-Bus signal + if (ps->o.dbus) { + cdbus_ev_win_added(ps, new); + } +#endif + + if (IsViewable == map_state) { + map_win(ps, id); + } + + return true; +} + +/** + * Update focused state of a window. + */ +void win_update_focused(session_t *ps, win *w) { + if (UNSET != w->focused_force) { + w->focused = w->focused_force; + } + else { + w->focused = win_is_focused_real(ps, w); + + // Use wintype_focus, and treat WM windows and override-redirected + // windows specially + if (ps->o.wintype_focus[w->window_type] + || (ps->o.mark_wmwin_focused && w->wmwin) + || (ps->o.mark_ovredir_focused + && w->id == w->client_win && !w->wmwin) + || (IsViewable == w->a.map_state && c2_match(ps, w, ps->o.focus_blacklist, &w->cache_fcblst))) + w->focused = true; + + // If window grouping detection is enabled, mark the window active if + // its group is + if (ps->o.track_leader && ps->active_leader + && win_get_leader(ps, w) == ps->active_leader) { + w->focused = true; + } + } + + // Always recalculate the window target opacity, since some opacity-related + // options depend on the output value of win_is_focused_real() instead of + // w->focused + w->flags |= WFLAG_OPCT_CHANGE; +} + +/** + * Set leader of a window. + */ +static inline void win_set_leader(session_t *ps, win *w, Window nleader) { + // If the leader changes + if (w->leader != nleader) { + Window cache_leader_old = win_get_leader(ps, w); + + w->leader = nleader; + + // Forcefully do this to deal with the case when a child window + // gets mapped before parent, or when the window is a waypoint + clear_cache_win_leaders(ps); + + // Update the old and new window group and active_leader if the window + // could affect their state. + Window cache_leader = win_get_leader(ps, w); + if (win_is_focused_real(ps, w) && cache_leader_old != cache_leader) { + ps->active_leader = cache_leader; + + group_update_focused(ps, cache_leader_old); + group_update_focused(ps, cache_leader); + } + // Otherwise, at most the window itself is affected + else { + win_update_focused(ps, w); + } + + // Update everything related to conditions + win_on_factor_change(ps, w); + } +} + +/** + * Update leader of a window. + */ +void win_update_leader(session_t *ps, win *w) { + Window leader = None; + + // Read the leader properties + if (ps->o.detect_transient && !leader) + leader = wid_get_prop_window(ps, w->client_win, ps->atom_transient); + + if (ps->o.detect_client_leader && !leader) + leader = wid_get_prop_window(ps, w->client_win, ps->atom_client_leader); + + win_set_leader(ps, w, leader); + +#ifdef DEBUG_LEADER + printf_dbgf("(%#010lx): client %#010lx, leader %#010lx, cache %#010lx\n", w->id, w->client_win, w->leader, win_get_leader(ps, w)); +#endif +} + +/** + * Internal function of win_get_leader(). + */ +Window win_get_leader_raw(session_t *ps, win *w, int recursions) { + // Rebuild the cache if needed + if (!w->cache_leader && (w->client_win || w->leader)) { + // Leader defaults to client window + if (!(w->cache_leader = w->leader)) + w->cache_leader = w->client_win; + + // If the leader of this window isn't itself, look for its ancestors + if (w->cache_leader && w->cache_leader != w->client_win) { + win *wp = find_toplevel(ps, w->cache_leader); + if (wp) { + // Dead loop? + if (recursions > WIN_GET_LEADER_MAX_RECURSION) + return None; + + w->cache_leader = win_get_leader_raw(ps, wp, recursions + 1); + } + } + } + + return w->cache_leader; +} + +/** + * Retrieve the WM_CLASS of a window and update its + * win structure. + */ +bool win_get_class(session_t *ps, win *w) { + char **strlst = NULL; + int nstr = 0; + + // Can't do anything if there's no client window + if (!w->client_win) + return false; + + // Free and reset old strings + free(w->class_instance); + free(w->class_general); + w->class_instance = NULL; + w->class_general = NULL; + + // Retrieve the property string list + if (!wid_get_text_prop(ps, w->client_win, ps->atom_class, &strlst, &nstr)) + return false; + + // Copy the strings if successful + w->class_instance = mstrcpy(strlst[0]); + + if (nstr > 1) + w->class_general = mstrcpy(strlst[1]); + + XFreeStringList(strlst); + +#ifdef DEBUG_WINDATA + printf_dbgf("(%#010lx): client = %#010lx, " + "instance = \"%s\", general = \"%s\"\n", + w->id, w->client_win, w->class_instance, w->class_general); +#endif + + return true; +} + + + +/** + * Handle window focus change. + */ +static void +win_on_focus_change(session_t *ps, win *w) { + // If window grouping detection is enabled + if (ps->o.track_leader) { + Window leader = win_get_leader(ps, w); + + // If the window gets focused, replace the old active_leader + if (win_is_focused_real(ps, w) && leader != ps->active_leader) { + Window active_leader_old = ps->active_leader; + + ps->active_leader = leader; + + group_update_focused(ps, active_leader_old); + group_update_focused(ps, leader); + } + // If the group get unfocused, remove it from active_leader + else if (!win_is_focused_real(ps, w) && leader && leader == ps->active_leader + && !group_is_focused(ps, leader)) { + ps->active_leader = None; + group_update_focused(ps, leader); + } + + // The window itself must be updated anyway + win_update_focused(ps, w); + } + // Otherwise, only update the window itself + else { + win_update_focused(ps, w); + } + + // Update everything related to conditions + win_on_factor_change(ps, w); + +#ifdef CONFIG_DBUS + // Send D-Bus signal + if (ps->o.dbus) { + if (win_is_focused_real(ps, w)) + cdbus_ev_win_focusin(ps, w); + else + cdbus_ev_win_focusout(ps, w); + } +#endif +} + +/** + * Set real focused state of a window. + */ +void +win_set_focused(session_t *ps, win *w, bool focused) { + // Unmapped windows will have their focused state reset on map + if (IsUnmapped == w->a.map_state) + return; + + if (win_is_focused_real(ps, w) == focused) return; + + if (focused) { + if (ps->active_win) + win_set_focused(ps, ps->active_win, false); + ps->active_win = w; + } + else if (w == ps->active_win) + ps->active_win = NULL; + + assert(win_is_focused_real(ps, w) == focused); + + win_on_focus_change(ps, w); +} + +/** + * Get a rectangular region a window (and possibly its shadow) occupies. + * + * Note w->shadow and shadow geometry must be correct before calling this + * function. + */ +XserverRegion win_extents(session_t *ps, win *w) { + XRectangle r; + + r.x = w->a.x; + r.y = w->a.y; + r.width = w->widthb; + r.height = w->heightb; + + if (w->shadow) { + XRectangle sr; + + sr.x = w->a.x + w->shadow_dx; + sr.y = w->a.y + w->shadow_dy; + sr.width = w->shadow_width; + sr.height = w->shadow_height; + + if (sr.x < r.x) { + r.width = (r.x + r.width) - sr.x; + r.x = sr.x; + } + + if (sr.y < r.y) { + r.height = (r.y + r.height) - sr.y; + r.y = sr.y; + } + + if (sr.x + sr.width > r.x + r.width) { + r.width = sr.x + sr.width - r.x; + } + + if (sr.y + sr.height > r.y + r.height) { + r.height = sr.y + sr.height - r.y; + } + } + + return XFixesCreateRegion(ps->dpy, &r, 1); +} + +/** + * Retrieve the bounding shape of a window. + */ +XserverRegion +win_border_size(session_t *ps, win *w, bool use_offset) { + // Start with the window rectangular region + XserverRegion fin = win_get_region(ps, w, use_offset); + + // Only request for a bounding region if the window is shaped + if (w->bounding_shaped) { + /* + * if window doesn't exist anymore, this will generate an error + * as well as not generate a region. Perhaps a better XFixes + * architecture would be to have a request that copies instead + * of creates, that way you'd just end up with an empty region + * instead of an invalid XID. + */ + + XserverRegion border = XFixesCreateRegionFromWindow( + ps->dpy, w->id, WindowRegionBounding); + + if (!border) + return fin; + + if (use_offset) { + // Translate the region to the correct place + XFixesTranslateRegion(ps->dpy, border, + w->a.x + w->a.border_width, + w->a.y + w->a.border_width); + } + + // Intersect the bounding region we got with the window rectangle, to + // make sure the bounding region is not bigger than the window + // rectangle + XFixesIntersectRegion(ps->dpy, fin, fin, border); + XFixesDestroyRegion(ps->dpy, border); + } + + return fin; +} + +/** + * Reread opacity property of a window. + */ +void win_update_opacity_prop(session_t *ps, win *w) { + // get frame opacity first + w->has_opacity_prop = + wid_get_opacity_prop(ps, w->id, OPAQUE, &w->opacity_prop); + + if (w->has_opacity_prop) + // opacity found + return; + + if (ps->o.detect_client_opacity && w->client_win && w->id == w->client_win) + // checking client opacity not allowed + return; + + // get client opacity + w->has_opacity_prop = + wid_get_opacity_prop(ps, w->client_win, OPAQUE, &w->opacity_prop); +} + +/** + * Retrieve frame extents from a window. + */ +void +win_get_frame_extents(session_t *ps, win *w, Window client) { + cmemzero_one(&w->frame_extents); + + winprop_t prop = wid_get_prop(ps, client, ps->atom_frame_extents, + 4L, XA_CARDINAL, 32); + + if (4 == prop.nitems) { + const long * const extents = prop.data.p32; + w->frame_extents.left = extents[0]; + w->frame_extents.right = extents[1]; + w->frame_extents.top = extents[2]; + w->frame_extents.bottom = extents[3]; + + if (ps->o.frame_opacity) + update_reg_ignore_expire(ps, w); + } + +#ifdef DEBUG_FRAME + printf_dbgf("(%#010lx): %d, %d, %d, %d\n", w->id, + w->frame_extents.left, w->frame_extents.right, + w->frame_extents.top, w->frame_extents.bottom); +#endif + + free_winprop(&prop); +} diff --git a/src/win.h b/src/win.h new file mode 100644 index 00000000..5aad0f2c --- /dev/null +++ b/src/win.h @@ -0,0 +1,97 @@ +#pragma once +#include +#include +#include + +typedef struct session session_t; +typedef struct win win; + + +int win_get_name(session_t *ps, win *w); +int win_get_role(session_t *ps, win *w); +void win_determine_mode(session_t *ps, win *w); +/** + * Set real focused state of a window. + */ +void win_set_focused(session_t *ps, win *w, bool focused); +void win_determine_fade(session_t *ps, win *w); +void win_update_shape_raw(session_t *ps, win *w); +void win_update_shape(session_t *ps, win *w); +void win_update_prop_shadow_raw(session_t *ps, win *w); +void win_update_prop_shadow(session_t *ps, win *w); +void win_set_shadow(session_t *ps, win *w, bool shadow_new); +void win_determine_shadow(session_t *ps, win *w); +void win_set_invert_color(session_t *ps, win *w, bool invert_color_new); +void win_determine_invert_color(session_t *ps, win *w); +void win_set_blur_background(session_t *ps, win *w, bool blur_background_new); +void win_determine_blur_background(session_t *ps, win *w); +void win_on_wtype_change(session_t *ps, win *w); +void win_on_factor_change(session_t *ps, win *w); +void calc_win_size(session_t *ps, win *w); +void calc_shadow_geometry(session_t *ps, win *w); +void win_upd_wintype(session_t *ps, win *w); +void win_mark_client(session_t *ps, win *w, Window client); +void win_unmark_client(session_t *ps, win *w); +void win_recheck_client(session_t *ps, win *w); +Window win_get_leader_raw(session_t *ps, win *w, int recursions); +bool win_get_class(session_t *ps, win *w); +void win_rounded_corners(session_t *ps, win *w); +void win_calc_opacity(session_t *ps, win *w); +void win_calc_dim(session_t *ps, win *w); +/** + * Reread opacity property of a window. + */ +void win_update_opacity_prop(session_t *ps, win *w); +/** + * Update leader of a window. + */ +void win_update_leader(session_t *ps, win *w); +/** + * Update focused state of a window. + */ +void win_update_focused(session_t *ps, win *w); +/** + * Retrieve the bounding shape of a window. + */ +XserverRegion +win_border_size(session_t *ps, win *w, bool use_offset); +/** + * Get a rectangular region a window (and possibly its shadow) occupies. + * + * Note w->shadow and shadow geometry must be correct before calling this + * function. + */ +XserverRegion win_extents(session_t *ps, win *w); +/** + * Add a window to damaged area. + * + * @param ps current session + * @param w struct _win element representing the window + */ +void add_damage_from_win(session_t *ps, win *w); +/** + * Get a rectangular region a window occupies, excluding shadow. + */ +XserverRegion +win_get_region(session_t *ps, win *w, bool use_offset); +/** + * Get a rectangular region a window occupies, excluding frame and shadow. + */ +XserverRegion +win_get_region_noframe(session_t *ps, win *w, bool use_offset); +/** + * Retrieve frame extents from a window. + */ +void +win_get_frame_extents(session_t *ps, win *w, Window client); +bool add_win(session_t *ps, Window id, Window prev); + +/** + * 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); +} diff --git a/src/x.c b/src/x.c new file mode 100644 index 00000000..2aaa77a1 --- /dev/null +++ b/src/x.c @@ -0,0 +1,95 @@ +#include + +#include "common.h" +#include "x.h" + +/** + * 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 ps 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. + */ +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) + && (!rformat || format == rformat) + && (8 == format || 16 == format || 32 == format)) { + return (winprop_t) { + .data.p8 = data, + .nitems = nitems, + .type = type, + .format = format, + }; + } + + cxfree(data); + + return (winprop_t) { + .data.p8 = NULL, + .nitems = 0, + .type = AnyPropertyType, + .format = 0 + }; +} + +/** + * Get the value of a type-Window property of a window. + * + * @return the value if successful, 0 otherwise + */ +Window +wid_get_prop_window(session_t *ps, Window wid, Atom aprop) { + // Get the attribute + Window p = None; + winprop_t prop = wid_get_prop(ps, wid, aprop, 1L, XA_WINDOW, 32); + + // Return it + if (prop.nitems) { + p = *prop.data.p32; + } + + free_winprop(&prop); + + return p; +} + +/** + * Get the value of a text property of a window. + */ +bool wid_get_text_prop(session_t *ps, Window wid, Atom prop, + char ***pstrlst, int *pnstr) { + XTextProperty text_prop = { NULL, None, 0, 0 }; + + if (!(XGetTextProperty(ps->dpy, wid, &text_prop, prop) && text_prop.value)) + return false; + + if (Success != + XmbTextPropertyToTextList(ps->dpy, &text_prop, pstrlst, pnstr) + || !*pnstr) { + *pnstr = 0; + if (*pstrlst) + XFreeStringList(*pstrlst); + cxfree(text_prop.value); + return false; + } + + cxfree(text_prop.value); + return true; +} diff --git a/src/x.h b/src/x.h new file mode 100644 index 00000000..d2f97f70 --- /dev/null +++ b/src/x.h @@ -0,0 +1,37 @@ +#pragma once +#include +#include +typedef struct session session_t; +typedef struct winprop winprop_t; +/** + * 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 ps 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. + */ +winprop_t +wid_get_prop_adv(const session_t *ps, Window w, Atom atom, long offset, + long length, Atom rtype, int rformat); + +/** + * Get the value of a type-Window property of a window. + * + * @return the value if successful, 0 otherwise + */ +Window +wid_get_prop_window(session_t *ps, Window wid, Atom aprop); + +/** + * Get the value of a text property of a window. + */ +bool wid_get_text_prop(session_t *ps, Window wid, Atom prop, + char ***pstrlst, int *pnstr);