From 28a2cc62fa7dbe56220feb1f66f43c6d59195f99 Mon Sep 17 00:00:00 2001 From: Yuxuan Shui Date: Sun, 30 Sep 2018 04:56:00 +0100 Subject: [PATCH] Convert XfixesRegion to pixman region Re-did the painting logic, and document it. It is unclear to me what is the previous painting logic. But the current one is basically this: 1. Go through all windows top to bottom, and put visible windows (not unmapped, opacity > 0, etc) into a linked list, from bottom to top 2. Accumulate a region of ignore on each window, which is basically the region of screen that is obscured by all the windows above current one. 3. Paint all the visible windows from bottom to top. Subtract the region of ignore from the painting region. If we need to paint shadow, we subtract the body of the window from the shadow painting region too, because we don't want shadow behind the window. 4. region of ignore is invalidated when window stack change, an window on top moved or changed shape, when window changed between opaque and transparent, etc. Notes: It is unclear whether all the different shapes of a window (extents, noframe, border, bounding shape, etc) are calculated correctly or not. It is unclear if window shape related events are handled correctly or not. Need more testing. Signed-off-by: Yuxuan Shui --- src/common.h | 120 ++---- src/compton.c | 869 +++++++++++++++++++---------------------- src/compton.h | 183 ++------- src/config.c | 43 +- src/config.h | 2 +- src/config_libconfig.c | 5 +- src/opengl.c | 296 ++++---------- src/opengl.h | 11 +- src/region.h | 4 +- src/win.c | 245 +++++------- src/win.h | 24 +- 11 files changed, 691 insertions(+), 1111 deletions(-) diff --git a/src/common.h b/src/common.h index 84cf52c..593480b 100644 --- a/src/common.h +++ b/src/common.h @@ -97,6 +97,7 @@ #ifdef CONFIG_XINERAMA #include #endif +#include // Workarounds for missing definitions in very old versions of X headers, // thanks to consolers for reporting @@ -171,6 +172,9 @@ #include "xrescheck.h" #endif +#include "x.h" +#include "region.h" + // === Constants === /// @brief Length of generic buffers. @@ -509,12 +513,6 @@ typedef struct _latom { struct _latom *next; } latom_t; -/// A representation of raw region data -typedef struct { - XRectangle *rects; - int nrects; -} reg_data_t; - #define REG_DATA_INIT { NULL, 0 } struct _timeout_t; @@ -522,7 +520,7 @@ struct _timeout_t; typedef struct win win; /// Structure representing all options. -typedef struct _options_t { +typedef struct options_t { // === General === /// The configuration file we used. char *config_file; @@ -626,8 +624,8 @@ typedef struct _options_t { int shadow_radius; int shadow_offset_x, shadow_offset_y; double shadow_opacity; - /// Geometry of a region in which shadow is not painted on. - geometry_t shadow_exclude_reg_geom; + /// argument string to shadow-exclude-reg option + char *shadow_exclude_reg_str; /// Shadow blacklist. A linked list of conditions. c2_lptr_t *shadow_blacklist; /// Whether bounding-shaped window should be ignored. @@ -809,7 +807,7 @@ typedef struct session { /// Picture of the root window background. paint_t root_tile_paint; /// A region of the size of the screen. - XserverRegion screen_reg; + region_t screen_reg; /// Picture of root window. Destination of painting in no-DBE painting /// mode. xcb_render_picture_t root_picture; @@ -854,15 +852,13 @@ typedef struct session { /// Program start time. struct timeval time_start; /// The region needs to painted on next paint. - XserverRegion all_damage; + region_t all_damage; /// The region damaged on the last paint. - XserverRegion all_damage_last[CGLX_MAX_BUFFER_AGE]; + region_t all_damage_last[CGLX_MAX_BUFFER_AGE]; /// Whether all windows are currently redirected. bool redirected; /// Pre-generated alpha pictures. xcb_render_picture_t *alpha_picts; - /// Whether all reg_ignore of windows should expire in this paint. - bool reg_ignore_expire; /// Time of last fading. In milliseconds. time_ms_t fade_time; /// Head pointer of the error ignore linked list. @@ -877,7 +873,8 @@ typedef struct session { // === Expose event related === /// Pointer to an array of XRectangle-s of exposed region. - XRectangle *expose_rects; + /// XXX why do we need this array? + rect_t *expose_rects; /// Number of XRectangle-s in expose_rects. int size_expose; /// Index of the next free slot in expose_rects. @@ -913,7 +910,7 @@ typedef struct session { /// Pre-computed color table for a side of shadow. unsigned char *shadow_top; /// A region in which shadow is not painted on. - XserverRegion shadow_exclude_reg; + region_t shadow_exclude_reg; // === Software-optimization-related === /// Currently used refresh rate. @@ -979,7 +976,7 @@ typedef struct session { /// Xinerama screen info. xcb_xinerama_query_screens_reply_t *xinerama_scrs; /// Xinerama screen regions. - XserverRegion *xinerama_scr_regs; + region_t *xinerama_scr_regs; /// Number of Xinerama screens. int xinerama_nscrs; #endif @@ -1036,7 +1033,7 @@ typedef struct session { /// Structure representing a top-level window compton manages. struct win { - /// Pointer to the next structure in the linked list. + /// Pointer to the next lower window in window stack. win *next; /// Pointer to the next higher window to paint. win *prev_trans; @@ -1068,9 +1065,7 @@ struct win { /// Paint info of the window. paint_t paint; /// Bounding shape of the window. - XserverRegion border_size; - /// Region of the whole window, shadow region included. - XserverRegion extents; + region_t bounding_shape; /// Window flags. Definitions above. int_fast16_t flags; /// Whether there's a pending ConfigureNotify happening @@ -1078,11 +1073,15 @@ struct win { bool need_configure; /// Queued ConfigureNotify when the window is unmapped. xcb_configure_notify_event_t queue_configure; - /// Region to be ignored when painting. Basically the region where - /// higher opaque windows will paint upon. Depends on window frame + /// The region of screen that will be obscured when windows above is painted. + /// We use this to reduce the pixels that needed to be paint when painting + /// this window and anything underneath. Depends on window frame /// opacity state, window geometry, window mapped/unmapped state, - /// window mode, of this and all higher windows. - XserverRegion reg_ignore; + /// window mode of the windows above. DOES NOT INCLUDE the body of THIS WINDOW. + /// NULL means reg_ignore has not been calculated for this window. + rc_region_t *reg_ignore; + /// Whether the reg_ignore of all windows beneath this window are valid + bool reg_ignore_valid; /// Cached width/height of the window including border. int widthb, heightb; /// Whether the window has been destroyed. @@ -1279,38 +1278,6 @@ print_backtrace(void) { free(strings); } -#ifdef DEBUG_ALLOC_REG - -/** - * Wrapper of XFixesCreateRegion, for debugging. - */ -static inline XserverRegion -XFixesCreateRegion_(Display *dpy, XRectangle *p, int n, - const char *func, int line) { - XserverRegion reg = XFixesCreateRegion(dpy, p, n); - print_timestamp(ps_g); - printf("%#010lx: XFixesCreateRegion() in %s():%d\n", reg, func, line); - print_backtrace(); - fflush(stdout); - return reg; -} - -/** - * Wrapper of XFixesDestroyRegion, for debugging. - */ -static inline void -XFixesDestroyRegion_(Display *dpy, XserverRegion reg, - const char *func, int line) { - XFixesDestroyRegion(dpy, reg); - print_timestamp(ps_g); - printf("%#010lx: XFixesDestroyRegion() in %s():%d\n", reg, func, line); - fflush(stdout); -} - -#define XFixesCreateRegion(dpy, p, n) XFixesCreateRegion_(dpy, p, n, __func__, __LINE__) -#define XFixesDestroyRegion(dpy, reg) XFixesDestroyRegion_(dpy, reg, __func__, __LINE__) -#endif - #endif // === Functions === @@ -1903,39 +1870,13 @@ find_focused(session_t *ps) { return NULL; } -/** - * Copies a region. - */ -static inline XserverRegion -copy_region(const session_t *ps, XserverRegion oldregion) { - if (!oldregion) - return None; - - XserverRegion region = XFixesCreateRegion(ps->dpy, NULL, 0); - - XFixesCopyRegion(ps->dpy, region, oldregion); - - return region; -} - -/** - * Destroy a XserverRegion. - */ -static inline void -free_region(session_t *ps, XserverRegion *p) { - if (*p) { - XFixesDestroyRegion(ps->dpy, *p); - *p = None; - } -} - /** * Free all regions in ps->all_damage_last . */ static inline void free_all_damage_last(session_t *ps) { for (int i = 0; i < CGLX_MAX_BUFFER_AGE; ++i) - free_region(ps, &ps->all_damage_last[i]); + pixman_region32_clear(&ps->all_damage_last[i]); } #ifdef CONFIG_XSYNC @@ -1958,12 +1899,13 @@ free_fence(session_t *ps, XSyncFence *pfence) { * psrc and pdst cannot be the same. */ static inline void -rect_crop(XRectangle *pdst, const XRectangle *psrc, const XRectangle *pbound) { +rect_crop(rect_t *restrict pdst, const rect_t *psrc, const rect_t *pbound) { assert(psrc != pdst); - pdst->x = max_i(psrc->x, pbound->x); - pdst->y = max_i(psrc->y, pbound->y); - pdst->width = max_i(0, min_i(psrc->x + psrc->width, pbound->x + pbound->width) - pdst->x); - pdst->height = max_i(0, min_i(psrc->y + psrc->height, pbound->y + pbound->height) - pdst->y); + assert(psrc != pbound); + pdst->x1 = max_i(psrc->x1, pbound->x1); + pdst->y1 = max_i(psrc->y1, pbound->y1); + pdst->x2 = min_i(psrc->x2, pbound->x2); + pdst->y2 = min_i(psrc->y2, pbound->y2); } /** diff --git a/src/compton.c b/src/compton.c index 8e645c9..19d744e 100644 --- a/src/compton.c +++ b/src/compton.c @@ -196,6 +196,51 @@ static const char *background_props_str[] = { /// have a pointer to current session passed in. session_t *ps_g = NULL; +static inline void +__attribute__((nonnull(1, 2))) +set_tgt_clip(session_t *ps, region_t *reg) { + switch (ps->o.backend) { + case BKEND_XRENDER: + case BKEND_XR_GLX_HYBRID: + x_set_picture_clip_region(ps, ps->tgt_buffer.pict, 0, 0, reg); + break; +#ifdef CONFIG_OPENGL + case BKEND_GLX: + glx_set_clip(ps, reg); + break; +#endif + default: + assert(false); + } +} + +static inline void +paint_region(session_t *ps, win *w, int x, int y, int wid, int hei, + double opacity, const region_t *reg_paint, xcb_render_picture_t pict) { + const int dx = (w ? w->g.x: 0) + x; + const int dy = (w ? w->g.y: 0) + y; + const bool argb = (w && (win_has_alpha(w) || ps->o.force_win_blend)); + const bool neg = (w && w->invert_color); + + render(ps, x, y, dx, dy, wid, hei, opacity, argb, neg, + pict, (w ? w->paint.ptex: ps->root_tile_paint.ptex), + reg_paint, (w ? &ps->o.glx_prog_win: NULL)); +} + +/** + * Get a region of the screen size. + */ +static inline void +get_screen_region(session_t *ps, region_t *res) { + pixman_box32_t b = { + .x1 = 0, .y1 = 0, + .x2 = ps->root_width, + .y2 = ps->root_height + }; + pixman_region32_fini(res); + pixman_region32_init_rects(res, &b, 1); +} + /** * Check if current backend uses XRender for rendering. */ @@ -226,18 +271,14 @@ paint_isvalid(session_t *ps, const paint_t *ppaint) { return true; } -void add_damage(session_t *ps, XserverRegion damage) { +void add_damage(session_t *ps, const region_t *damage) { // Ignore damage when screen isn't redirected if (!ps->redirected) - free_region(ps, &damage); + return; - 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; - } + if (!damage) + return; + pixman_region32_union(&ps->all_damage, &ps->all_damage, (region_t *)damage); } #define CPY_FDS(key) \ @@ -941,12 +982,12 @@ get_root_tile(session_t *ps) { * Paint root window content. */ static void -paint_root(session_t *ps, XserverRegion reg_paint) { +paint_root(session_t *ps, const region_t *reg_paint) { if (!ps->root_tile_paint.pixmap) get_root_tile(ps); - win_render(ps, NULL, 0, 0, ps->root_width, ps->root_height, 1.0, reg_paint, - NULL, ps->root_tile_paint.pict); + paint_region(ps, NULL, 0, 0, ps->root_width, ps->root_height, 1.0, reg_paint, + ps->root_tile_paint.pict); } /** @@ -1002,9 +1043,10 @@ paint_preprocess(session_t *ps, win *list) { // Fading step calculation time_ms_t steps = 0L; - if (ps->fade_time) { - steps = ((get_time_ms() - ps->fade_time) + FADE_DELTA_TOLERANCE * ps->o.fade_delta) / ps->o.fade_delta; - } + if (ps->fade_time) + steps = (get_time_ms() - ps->fade_time + + FADE_DELTA_TOLERANCE*ps->o.fade_delta) / + ps->o.fade_delta; // Reset fade_time if unset, or there appears to be a time disorder if (!ps->fade_time || steps < 0L) { ps->fade_time = get_time_ms(); @@ -1012,31 +1054,12 @@ paint_preprocess(session_t *ps, win *list) { } ps->fade_time += steps * ps->o.fade_delta; - XserverRegion last_reg_ignore = None; - - bool unredir_possible = false; - // Trace whether it's the highest window to paint - bool is_highest = true; + // First, let's process fading for (win *w = list; w; w = next) { - bool to_paint = true; - const winmode_t mode_old = w->mode; - - // In case calling the fade callback function destroys this window next = w->next; - opacity_t opacity_old = w->opacity; - - // Data expiration - { - // Remove built shadow if needed - if (w->flags & WFLAG_SIZE_CHANGE) - free_paint(ps, &w->shadow_paint); - - // Destroy reg_ignore on all windows if they should expire - if (ps->reg_ignore_expire) - free_region(ps, &w->reg_ignore); - } - - //printf_errf("(): %d %d %s", w->a.map_state, w->ever_damaged, w->name); + const winmode_t mode_old = w->mode; + const bool was_painted = w->to_paint; + const opacity_t opacity_old = w->opacity; // Restore flags from last paint if the window is being faded out if (IsUnmapped == w->a.map_state) { win_set_shadow(ps, w, w->shadow_last); @@ -1054,7 +1077,47 @@ paint_preprocess(session_t *ps, win *list) { // Run fading run_fade(ps, w, steps); - // Opacity will not change, from now on. + if (win_has_frame(w)) + w->frame_opacity = ps->o.frame_opacity; + else + w->frame_opacity = 1.0; + + // Update window mode + win_determine_mode(ps, w); + + // Destroy all reg_ignore above when frame opaque state changes on + // SOLID mode + if (was_painted && w->mode != mode_old) + w->reg_ignore_valid = false; + + // Add window to damaged area if its opacity changes + // If was_painted == false, and to_paint is also false, we don't care + // If was_painted == false, but to_paint is true, damage will be added in the loop below + if (was_painted && w->opacity != opacity_old) + add_damage_from_win(ps, w); + } + + // Opacity will not change, from now on. + rc_region_t *last_reg_ignore = rc_region_new(); + + bool unredir_possible = false; + // Trace whether it's the highest window to paint + bool is_highest = true; + bool reg_ignore_valid = true; + for (win *w = list; w; w = next) { + __label__ skip_window; + bool to_paint = true; + // w->to_paint remembers whether this window is painted last time + const bool was_painted = w->to_paint; + + // In case calling the fade callback function destroys this window + next = w->next; + + // Destroy reg_ignore if some window above us invalidated it + if (!reg_ignore_valid) + rc_region_unref(&w->reg_ignore); + + //printf_errf("(): %d %d %s", w->a.map_state, w->ever_damaged, w->name); // Give up if it's not damaged or invisible, or it's unmapped and its // pixmap is gone (for example due to a ConfigureNotify), or when it's @@ -1067,118 +1130,78 @@ paint_preprocess(session_t *ps, win *list) { || w->paint_excluded) to_paint = false; - // to_paint will never change afterward - - // Determine mode as early as possible - if (to_paint && (!w->to_paint || w->opacity != opacity_old)) - win_determine_mode(ps, w); - - if (to_paint) { - // Fetch bounding region - if (!w->border_size) - w->border_size = win_border_size(ps, w, true); - - // Fetch window extents - if (!w->extents) - w->extents = win_extents(ps, w); - - // Calculate frame_opacity - { - double frame_opacity_old = w->frame_opacity; - - if (ps->o.frame_opacity && 1.0 != ps->o.frame_opacity - && win_has_frame(w)) - w->frame_opacity = get_opacity_percent(w) * - ps->o.frame_opacity; - else - w->frame_opacity = 0.0; - - // Destroy all reg_ignore above when frame opaque state changes on - // SOLID mode - if (w->to_paint && WMODE_SOLID == mode_old - && (0.0 == frame_opacity_old) != (0.0 == w->frame_opacity)) - ps->reg_ignore_expire = true; - } - - // Calculate shadow opacity - if (w->frame_opacity) - w->shadow_opacity = ps->o.shadow_opacity * w->frame_opacity; - else - w->shadow_opacity = ps->o.shadow_opacity * get_opacity_percent(w); - } - // Add window to damaged area if its painting status changes // or opacity changes - if (to_paint != w->to_paint || w->opacity != opacity_old) + if (to_paint != was_painted) { + w->reg_ignore_valid = false; add_damage_from_win(ps, w); + } - // Destroy all reg_ignore above when window mode changes - if ((to_paint && WMODE_SOLID == w->mode) - != (w->to_paint && WMODE_SOLID == mode_old)) - ps->reg_ignore_expire = true; + // to_paint will never change afterward + if (!to_paint) + goto skip_window; - if (to_paint) { - // Generate ignore region for painting to reduce GPU load - if (ps->reg_ignore_expire || !w->to_paint) { - free_region(ps, &w->reg_ignore); + // XXX shouldn't need these + // Fetch bounding region + if (!pixman_region32_not_empty(&w->bounding_shape)) + win_update_bounding_shape(ps, w); - // If the window is solid, we add the window region to the - // ignored region - if (win_is_solid(ps, w)) { - if (!w->frame_opacity) { - if (w->border_size) - w->reg_ignore = copy_region(ps, w->border_size); - else - w->reg_ignore = win_get_region(ps, w, true); - } - else { - w->reg_ignore = win_get_region_noframe(ps, w, true); - if (w->border_size) - XFixesIntersectRegion(ps->dpy, w->reg_ignore, w->reg_ignore, - w->border_size); - } + // Fetch window extents + region_t extents; + pixman_region32_init(&extents); + win_extents(w, &extents); - if (last_reg_ignore) - XFixesUnionRegion(ps->dpy, w->reg_ignore, w->reg_ignore, - last_reg_ignore); - } - // Otherwise we copy the last region over - else if (last_reg_ignore) - w->reg_ignore = copy_region(ps, last_reg_ignore); - else - w->reg_ignore = None; + // Calculate shadow opacity + w->shadow_opacity = ps->o.shadow_opacity * get_opacity_percent(w) * ps->o.frame_opacity; + + // Generate ignore region for painting to reduce GPU load + if (!w->reg_ignore) + w->reg_ignore = rc_region_ref(last_reg_ignore); + + // If the window is solid, we add the window region to the + // ignored region + // Otherwise last_reg_ignore shouldn't change + if (w->mode == WMODE_SOLID && !ps->o.force_win_blend) { + region_t *tmp = rc_region_new(); + if (w->frame_opacity == 1) + copy_region(tmp, &w->bounding_shape); + else { + win_get_region_noframe(ps, w, true, tmp); + pixman_region32_intersect(tmp, tmp, &w->bounding_shape); } - last_reg_ignore = w->reg_ignore; - - // (Un)redirect screen - // We could definitely unredirect the screen when there's no window to - // paint, but this is typically unnecessary, may cause flickering when - // fading is enabled, and could create inconsistency when the wallpaper - // is not correctly set. - if (ps->o.unredir_if_possible && is_highest && to_paint) { - is_highest = false; - if (win_is_solid(ps, w) - && (!w->frame_opacity || !win_has_frame(w)) - && win_is_fullscreen(ps, w) - && !w->unredir_if_possible_excluded) - unredir_possible = true; - } - - // Reset flags - w->flags = 0; + pixman_region32_union(tmp, tmp, last_reg_ignore); + rc_region_unref(&last_reg_ignore); + last_reg_ignore = tmp; } - if (to_paint) { - w->prev_trans = t; - t = w; - } - else { - assert(w->destroyed == (w->fade_callback == finish_destroy_win)); - check_fade_fin(ps, &w); + // (Un)redirect screen + // We could definitely unredirect the screen when there's no window to + // paint, but this is typically unnecessary, may cause flickering when + // fading is enabled, and could create inconsistency when the wallpaper + // is not correctly set. + if (ps->o.unredir_if_possible && is_highest) { + if (win_is_solid(ps, w) + && (w->frame_opacity == 1 || !win_has_frame(w)) + && win_is_fullscreen(ps, w) + && !w->unredir_if_possible_excluded) + unredir_possible = true; } - // Avoid setting w->to_paint if w is to be freed + // Reset flags + w->flags = 0; + w->prev_trans = t; + t = w; + is_highest = false; + + skip_window: + reg_ignore_valid = reg_ignore_valid && w->reg_ignore_valid; + w->reg_ignore_valid = true; + + assert(w->destroyed == (w->fade_callback == finish_destroy_win)); + check_fade_fin(ps, &w); + + // Avoid setting w->to_paint if w is freed if (w) { w->to_paint = to_paint; @@ -1192,14 +1215,14 @@ paint_preprocess(session_t *ps, win *list) { } } + rc_region_unref(&last_reg_ignore); // If possible, unredirect all windows and stop painting if (UNSET != ps->o.redirected_force) unredir_possible = !ps->o.redirected_force; - - // If there's no window to paint, and the screen isn't redirected, - // don't redirect it. - if (ps->o.unredir_if_possible && is_highest && !ps->redirected) + else if (ps->o.unredir_if_possible && is_highest && !ps->redirected) + // If there's no window to paint, and the screen isn't redirected, + // don't redirect it. unredir_possible = true; if (unredir_possible) { if (ps->redirected) { @@ -1223,8 +1246,7 @@ paint_preprocess(session_t *ps, win *list) { * Paint the shadow of a window. */ static inline void -win_paint_shadow(session_t *ps, win *w, - XserverRegion reg_paint, const reg_data_t *pcache_reg) { +win_paint_shadow(session_t *ps, win *w, region_t *reg_paint) { // Bind shadow pixmap to GLX texture if needed paint_bind_tex(ps, &w->shadow_paint, 0, 0, 32, false); @@ -1235,7 +1257,7 @@ win_paint_shadow(session_t *ps, win *w, render(ps, 0, 0, w->g.x + w->shadow_dx, w->g.y + w->shadow_dy, w->shadow_width, w->shadow_height, w->shadow_opacity, true, false, - w->shadow_paint.pict, w->shadow_paint.ptex, reg_paint, pcache_reg, NULL); + w->shadow_paint.pict, w->shadow_paint.ptex, reg_paint, NULL); } /** @@ -1256,7 +1278,7 @@ win_paint_shadow(session_t *ps, win *w, static bool xr_blur_dst(session_t *ps, xcb_render_picture_t tgt_buffer, int x, int y, int wid, int hei, xcb_render_fixed_t **blur_kerns, - XserverRegion reg_clip) { + const region_t *reg_clip) { xcb_connection_t *c = XGetXCBConnection(ps->dpy); assert(blur_kerns[0]); @@ -1270,7 +1292,7 @@ xr_blur_dst(session_t *ps, xcb_render_picture_t tgt_buffer, } if (reg_clip && tmp_picture) - XFixesSetPictureClipRegion(ps->dpy, tmp_picture, reg_clip, 0, 0); + x_set_picture_clip_region(ps, tmp_picture, 0, 0, reg_clip); xcb_render_picture_t src_pict = tgt_buffer, dst_pict = tmp_picture; for (int i = 0; blur_kerns[i]; ++i) { @@ -1325,7 +1347,7 @@ xr_take_screenshot(session_t *ps) { */ static inline void win_blur_background(session_t *ps, win *w, xcb_render_picture_t tgt_buffer, - XserverRegion reg_paint, const reg_data_t *pcache_reg) { + const region_t *reg_paint) { const int x = w->g.x; const int y = w->g.y; const int wid = w->widthb; @@ -1383,23 +1405,28 @@ win_blur_background(session_t *ps, win *w, xcb_render_picture_t tgt_buffer, // Minimize the region we try to blur, if the window itself is not // opaque, only the frame is. - XserverRegion reg_noframe = None; + region_t reg_blur; + pixman_region32_init(®_blur); + pixman_region32_copy(®_blur, &w->bounding_shape); if (win_is_solid(ps, w)) { - 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); + region_t reg_noframe; + pixman_region32_init(®_noframe); + win_get_region_noframe(ps, w, true, ®_noframe); + pixman_region32_subtract(®_blur, ®_blur, ®_noframe); + pixman_region32_fini(®_noframe); } + // Translate global coordinates to local ones + pixman_region32_translate(®_blur, -x, -y); xr_blur_dst(ps, tgt_buffer, x, y, wid, hei, ps->blur_kerns_cache, - reg_noframe); - free_region(ps, ®_noframe); + ®_blur); + pixman_region32_clear(®_blur); } break; #ifdef CONFIG_OPENGL case BKEND_GLX: // TODO: Handle frame opacity glx_blur_dst(ps, x, y, wid, hei, ps->psglx->z - 0.5, factor_center, - reg_paint, pcache_reg, &w->glx_blur_cache); + reg_paint, &w->glx_blur_cache); break; #endif default: @@ -1411,8 +1438,7 @@ void render(session_t *ps, int x, int y, int dx, int dy, int wid, int hei, double opacity, bool argb, bool neg, xcb_render_picture_t pict, glx_texture_t *ptex, - XserverRegion reg_paint, const reg_data_t *pcache_reg, - const glx_prog_main_t *pprogram) + const region_t *reg_paint, const glx_prog_main_t *pprogram) { xcb_connection_t *c = XGetXCBConnection(ps->dpy); switch (ps->o.backend) { @@ -1430,7 +1456,7 @@ render(session_t *ps, int x, int y, int dx, int dy, int wid, int hei, #ifdef CONFIG_OPENGL case BKEND_GLX: glx_render(ps, ptex, x, y, dx, dy, wid, hei, - ps->psglx->z, opacity, argb, neg, reg_paint, pcache_reg, pprogram); + ps->psglx->z, opacity, argb, neg, reg_paint, pprogram); ps->psglx->z += 1; break; #endif @@ -1443,8 +1469,7 @@ render(session_t *ps, int x, int y, int dx, int dy, int wid, int hei, * Paint a window itself and dim it if asked. */ static inline void -win_paint_win(session_t *ps, win *w, XserverRegion reg_paint, - const reg_data_t *pcache_reg) { +paint_one(session_t *ps, win *w, const region_t *reg_paint) { xcb_connection_t *c = XGetXCBConnection(ps->dpy); glx_mark(ps, w->id, true); @@ -1463,14 +1488,12 @@ win_paint_win(session_t *ps, win *w, XserverRegion reg_paint, // XRender: Build picture if (bkend_use_xrender(ps) && !w->paint.pict) { - { - xcb_render_create_picture_value_list_t pa = { - .subwindowmode = IncludeInferiors, - }; + xcb_render_create_picture_value_list_t pa = { + .subwindowmode = IncludeInferiors, + }; - w->paint.pict = x_create_picture_with_pictfmt_and_pixmap(ps, w->pictfmt, - draw, XCB_RENDER_CP_SUBWINDOW_MODE, &pa); - } + w->paint.pict = x_create_picture_with_pictfmt_and_pixmap(ps, w->pictfmt, + draw, XCB_RENDER_CP_SUBWINDOW_MODE, &pa); } if (IsViewable == w->a.map_state) @@ -1504,10 +1527,12 @@ win_paint_win(session_t *ps, win *w, XserverRegion reg_paint, if (newpict) { // Apply clipping region to save some CPU if (reg_paint) { - XserverRegion reg = copy_region(ps, reg_paint); - XFixesTranslateRegion(ps->dpy, reg, -x, -y); - XFixesSetPictureClipRegion(ps->dpy, newpict, 0, 0, reg); - free_region(ps, ®); + region_t reg; + pixman_region32_init(®); + pixman_region32_copy(®, (region_t *)reg_paint); + pixman_region32_translate(®, -x, -y); + // FIXME XFixesSetPictureClipRegion(ps->dpy, newpict, 0, 0, reg); + pixman_region32_fini(®); } xcb_render_composite(c, XCB_RENDER_PICT_OP_SRC, pict, None, @@ -1525,10 +1550,9 @@ win_paint_win(session_t *ps, win *w, XserverRegion reg_paint, const double dopacity = get_opacity_percent(w); - if (!w->frame_opacity) { - win_render(ps, w, 0, 0, wid, hei, dopacity, reg_paint, pcache_reg, pict); - } - else { + if (w->frame_opacity == 1) { + paint_region(ps, w, 0, 0, wid, hei, dopacity, reg_paint, pict); + } else { // Painting parameters const margin_t extents = win_calc_frame_extents(ps, w); const int t = extents.top; @@ -1537,47 +1561,56 @@ win_paint_win(session_t *ps, win *w, XserverRegion reg_paint, const int r = extents.right; #define COMP_BDR(cx, cy, cwid, chei) \ - win_render(ps, w, (cx), (cy), (cwid), (chei), w->frame_opacity, \ - reg_paint, pcache_reg, pict) +paint_region(ps, w, (cx), (cy), (cwid), (chei), w->frame_opacity * dopacity, \ + reg_paint, pict) - // The following complicated logic is required because some broken - // window managers (I'm talking about you, Openbox!) that makes + // Sanitize the margins, in case some broken WM makes // top_width + bottom_width > height in some cases. - // top - int phei = min_i(t, hei); - if (phei > 0) - COMP_BDR(0, 0, wid, phei); + do { + // top + int body_height = hei; + // ctop = checked top + int ctop = min_i(body_height, t); // Make sure top margin is smaller than height + if (ctop > 0) + COMP_BDR(0, 0, wid, ctop); - if (hei > t) { - phei = min_i(hei - t, b); + body_height -= ctop; + if (body_height <= 0) + break; // bottom - if (phei > 0) - COMP_BDR(0, hei - phei, wid, phei); + // cbot = checked bottom + int cbot = min_i(body_height, b); // Make sure bottom margin is not too large + if (cbot > 0) + COMP_BDR(0, hei-cbot, wid, cbot); - phei = hei - t - phei; - if (phei > 0) { - int pwid = min_i(l, wid); - // left - if (pwid > 0) - COMP_BDR(0, t, pwid, phei); + body_height -= cbot; // Height of window exclude the margin + if (body_height <= 0) + break; - if (wid > l) { - pwid = min_i(wid - l, r); + // left + int body_width = wid; + int cleft = min_i(body_width, l); + if (cleft > 0) + COMP_BDR(0, ctop, cleft, body_height); - // right - if (pwid > 0) - COMP_BDR(wid - pwid, t, pwid, phei); + body_width -= cleft; + if (body_width <= 0) + break; - pwid = wid - l - pwid; - if (pwid > 0) { - // body - win_render(ps, w, l, t, pwid, phei, dopacity, reg_paint, pcache_reg, pict); - } - } - } - } + // right + int cright = min_i(body_width, r); + if (cright > 0) + COMP_BDR(wid - cright, ctop, cright, body_height); + + body_width -= cright; + if (body_width <= 0) + break; + + // body + paint_region(ps, w, cleft, ctop, body_width, body_height, dopacity, reg_paint, pict); + } while (0); } #undef COMP_BDR @@ -1616,7 +1649,7 @@ win_paint_win(session_t *ps, win *w, XserverRegion reg_paint, #ifdef CONFIG_OPENGL case BKEND_GLX: glx_dim_dst(ps, x, y, wid, hei, ps->psglx->z - 0.7, dim_opacity, - reg_paint, pcache_reg); + reg_paint); break; #endif default: @@ -1632,9 +1665,7 @@ win_paint_win(session_t *ps, win *w, XserverRegion reg_paint, */ static void rebuild_screen_reg(session_t *ps) { - if (ps->screen_reg) - XFixesDestroyRegion(ps->dpy, ps->screen_reg); - ps->screen_reg = get_screen_region(ps); + get_screen_region(ps, &ps->screen_reg); } /** @@ -1642,40 +1673,17 @@ rebuild_screen_reg(session_t *ps) { */ static void rebuild_shadow_exclude_reg(session_t *ps) { - free_region(ps, &ps->shadow_exclude_reg); - XRectangle rect = geom_to_rect(ps, &ps->o.shadow_exclude_reg_geom, NULL); - ps->shadow_exclude_reg = rect_to_reg(ps, &rect); -} - -/** - * Check if a region is empty. - * - * Keith Packard said this is slow: - * http://lists.freedesktop.org/archives/xorg/2007-November/030467.html - * - * @param ps current session - * @param region region to check for - * @param pcache_rects a place to cache the dumped rectangles - * @param ncache_nrects a place to cache the number of dumped rectangles - */ -static inline bool -is_region_empty(const session_t *ps, XserverRegion region, - reg_data_t *pcache_reg) { - int nrects = 0; - XRectangle *rects = XFixesFetchRegion(ps->dpy, region, &nrects); - - if (pcache_reg) { - pcache_reg->rects = rects; - pcache_reg->nrects = nrects; - } - else - cxfree(rects); - - return !nrects; + bool ret = parse_geometry(ps, ps->o.shadow_exclude_reg_str, + &ps->shadow_exclude_reg); + if (!ret) + exit(1); } +/// paint all windows +/// region = ?? +/// region_real = the damage region static void -paint_all(session_t *ps, XserverRegion region, XserverRegion region_real, win *t) { +paint_all(session_t *ps, region_t *region, const region_t *region_real, win * const t) { xcb_connection_t *c = XGetXCBConnection(ps->dpy); if (!region_real) region_real = region; @@ -1683,22 +1691,18 @@ paint_all(session_t *ps, XserverRegion region, XserverRegion region_real, win *t #ifdef DEBUG_REPAINT static struct timespec last_paint = { 0 }; #endif - XserverRegion reg_paint = None, reg_tmp = None, reg_tmp2 = None; + + if (!region) + region_real = region = &ps->screen_reg; + else + // Remove the damaged area out of screen + pixman_region32_intersect(region, region, &ps->screen_reg); #ifdef CONFIG_OPENGL - if (bkend_use_glx(ps)) { - glx_paint_pre(ps, ®ion); - } + if (bkend_use_glx(ps)) + glx_paint_pre(ps, region); #endif - if (!region) { - region_real = region = get_screen_region(ps); - } - else { - // Remove the damaged area out of screen - XFixesIntersectRegion(ps->dpy, region, region, ps->screen_reg); - } - #ifdef MONITOR_REPAINT // Note: MONITOR_REPAINT cannot work with DBE right now. // Picture old_tgt_buffer = ps->tgt_buffer.pict; @@ -1729,8 +1733,9 @@ paint_all(session_t *ps, XserverRegion region, XserverRegion region_real, win *t } #endif - if (BKEND_XRENDER == ps->o.backend) - XFixesSetPictureClipRegion(ps->dpy, ps->tgt_picture, 0, 0, region_real); + if (BKEND_XRENDER == ps->o.backend) { + x_set_picture_clip_region(ps, ps->tgt_picture, 0, 0, region_real); + } #ifdef MONITOR_REPAINT switch (ps->o.backend) { @@ -1748,138 +1753,89 @@ paint_all(session_t *ps, XserverRegion region, XserverRegion region_real, win *t } #endif - if (t && t->reg_ignore) { + region_t reg_tmp, *reg_paint; + pixman_region32_init(®_tmp); + if (t) { // Calculate the region upon which the root window is to be painted // based on the ignore region of the lowest window, if available - reg_paint = reg_tmp = XFixesCreateRegion(ps->dpy, NULL, 0); - XFixesSubtractRegion(ps->dpy, reg_paint, region, t->reg_ignore); + pixman_region32_subtract(®_tmp, region, t->reg_ignore); + reg_paint = ®_tmp; } else { reg_paint = region; } - set_tgt_clip(ps, reg_paint, NULL); + set_tgt_clip(ps, reg_paint); paint_root(ps, reg_paint); - // Create temporary regions for use during painting - if (!reg_tmp) - reg_tmp = XFixesCreateRegion(ps->dpy, NULL, 0); - reg_tmp2 = XFixesCreateRegion(ps->dpy, NULL, 0); - + // Windows are sorted from bottom to top + // Each window has a reg_ignore, which is the region obscured by all the windows + // on top of that window. This is used to reduce the number of pixels painted. + // + // Whether this is beneficial is to be determined XXX for (win *w = t; w; w = w->prev_trans) { // Painting shadow if (w->shadow) { // Lazy shadow building - if (!w->shadow_paint.pixmap) { + if (!w->shadow_paint.pixmap) if (!win_build_shadow(ps, w, 1)) printf_errf("(): build shadow failed"); - } - // Shadow is to be painted based on the ignore region of current - // window - if (w->reg_ignore) { - if (w == t) { - // If it's the first cycle and reg_tmp2 is not ready, calculate - // the paint region here - reg_paint = reg_tmp; - XFixesSubtractRegion(ps->dpy, reg_paint, region, w->reg_ignore); - } - else { - // Otherwise, used the cached region during last cycle - reg_paint = reg_tmp2; - } - XFixesIntersectRegion(ps->dpy, reg_paint, reg_paint, w->extents); - } - else { - reg_paint = reg_tmp; - XFixesIntersectRegion(ps->dpy, reg_paint, region, w->extents); - } + // Shadow doesn't need to be painted underneath the body of the window + // Because no one can see it + pixman_region32_subtract(®_tmp, region, w->reg_ignore); - if (ps->shadow_exclude_reg) - XFixesSubtractRegion(ps->dpy, reg_paint, reg_paint, - ps->shadow_exclude_reg); + // Mask out the region we don't want shadow on + if (pixman_region32_not_empty(&ps->shadow_exclude_reg)) + pixman_region32_subtract(®_tmp, ®_tmp, &ps->shadow_exclude_reg); - // Might be worthwhile to crop the region to shadow border - { - XRectangle rec_shadow_border = { - .x = w->g.x + w->shadow_dx, - .y = w->g.y + w->shadow_dy, - .width = w->shadow_width, - .height = w->shadow_height - }; - XserverRegion reg_shadow = XFixesCreateRegion(ps->dpy, - &rec_shadow_border, 1); - XFixesIntersectRegion(ps->dpy, reg_paint, reg_paint, reg_shadow); - free_region(ps, ®_shadow); - } + // Might be worth while to crop the region to shadow border + pixman_region32_intersect_rect(®_tmp, ®_tmp, + w->g.x + w->shadow_dx, w->g.y + w->shadow_dy, + w->shadow_width, w->shadow_height); - // Clear the shadow here instead of in make_shadow() for saving GPU - // power and handling shaped windows - if (w->mode != WMODE_SOLID && w->border_size) - XFixesSubtractRegion(ps->dpy, reg_paint, reg_paint, w->border_size); + // Mask out the body of the window from the shadow + // Doing it here instead of in make_shadow() for saving GPU + // power and handling shaped windows (XXX unconfirmed) + if (w->mode != WMODE_SOLID) + pixman_region32_subtract(®_tmp, ®_tmp, &w->bounding_shape); #ifdef CONFIG_XINERAMA if (ps->o.xinerama_shadow_crop && w->xinerama_scr >= 0) - XFixesIntersectRegion(ps->dpy, reg_paint, reg_paint, - ps->xinerama_scr_regs[w->xinerama_scr]); + pixman_region32_intersect(®_tmp, ®_tmp, + &ps->xinerama_scr_regs[w->xinerama_scr]); #endif // Detect if the region is empty before painting - { - reg_data_t cache_reg = REG_DATA_INIT; - if (region == reg_paint - || !is_region_empty(ps, reg_paint, &cache_reg)) { - set_tgt_clip(ps, reg_paint, &cache_reg); - - win_paint_shadow(ps, w, reg_paint, &cache_reg); - } - free_reg_data(&cache_reg); + if (pixman_region32_not_empty(®_tmp)) { + set_tgt_clip(ps, ®_tmp); + win_paint_shadow(ps, w, ®_tmp); } } // Calculate the region based on the reg_ignore of the next (higher) // window and the bounding region - reg_paint = reg_tmp; - if (w->prev_trans && w->prev_trans->reg_ignore) { - XFixesSubtractRegion(ps->dpy, reg_paint, region, - w->prev_trans->reg_ignore); - // Copy the subtracted region to be used for shadow painting in next - // cycle - XFixesCopyRegion(ps->dpy, reg_tmp2, reg_paint); + // XXX XXX + pixman_region32_subtract(®_tmp, region, w->reg_ignore); + pixman_region32_intersect(®_tmp, ®_tmp, &w->bounding_shape); - if (w->border_size) - XFixesIntersectRegion(ps->dpy, reg_paint, reg_paint, w->border_size); - } - else { - if (w->border_size) - XFixesIntersectRegion(ps->dpy, reg_paint, region, w->border_size); - else - reg_paint = region; - } - - { - reg_data_t cache_reg = REG_DATA_INIT; - if (!is_region_empty(ps, reg_paint, &cache_reg)) { - set_tgt_clip(ps, reg_paint, &cache_reg); + if (pixman_region32_not_empty(®_tmp)) { + set_tgt_clip(ps, ®_tmp); // Blur window background if (w->blur_background && (!win_is_solid(ps, w) - || (ps->o.blur_background_frame && w->frame_opacity))) { - win_blur_background(ps, w, ps->tgt_buffer.pict, reg_paint, &cache_reg); - } + || (ps->o.blur_background_frame && w->frame_opacity != 1))) + win_blur_background(ps, w, ps->tgt_buffer.pict, ®_tmp); // Painting the window - win_paint_win(ps, w, reg_paint, &cache_reg); - } - free_reg_data(&cache_reg); + paint_one(ps, w, ®_tmp); } } // Free up all temporary regions - XFixesDestroyRegion(ps->dpy, reg_tmp); - XFixesDestroyRegion(ps->dpy, reg_tmp2); + pixman_region32_fini(®_tmp); // Do this as early as possible if (!ps->o.dbe) - set_tgt_clip(ps, None, NULL); + set_tgt_clip(ps, &ps->screen_reg); if (ps->o.vsync) { // Make sure all previous requests are processed to achieve best @@ -1943,7 +1899,7 @@ paint_all(session_t *ps, XserverRegion region, XserverRegion region_real, win *t glXWaitX(); glx_render(ps, ps->tgt_buffer.ptex, 0, 0, 0, 0, ps->root_width, ps->root_height, 0, 1.0, false, false, - region_real, NULL, NULL); + region_real, NULL); // falls through case BKEND_GLX: glXSwapBuffers(ps->dpy, get_tgt_window(ps)); @@ -1966,8 +1922,6 @@ paint_all(session_t *ps, XserverRegion region, XserverRegion region_real, win *t } #endif - XFixesDestroyRegion(ps->dpy, region); - #ifdef DEBUG_REPAINT print_timestamp(ps); struct timespec now = get_time_timespec(); @@ -1997,20 +1951,22 @@ repair_win(session_t *ps, win *w) { if (IsViewable != w->a.map_state) return; - XserverRegion parts; xcb_connection_t *c = XGetXCBConnection(ps->dpy); + region_t parts; + pixman_region32_init(&parts); if (!w->ever_damaged) { - parts = win_extents(ps, w); + win_extents(w, &parts); set_ignore_cookie(ps, xcb_damage_subtract(c, w->damage, XCB_NONE, XCB_NONE)); } else { - parts = XFixesCreateRegion(ps->dpy, 0, 0); + XserverRegion tmp = XFixesCreateRegion(ps->dpy, NULL, 0); set_ignore_cookie(ps, - xcb_damage_subtract(c, w->damage, XCB_NONE, parts)); - XFixesTranslateRegion(ps->dpy, parts, + xcb_damage_subtract(c, w->damage, XCB_NONE, tmp)); + XFixesTranslateRegion(ps->dpy, tmp, w->g.x + w->g.border_width, w->g.y + w->g.border_width); + x_fetch_region(ps, tmp, &parts); } w->ever_damaged = true; @@ -2019,15 +1975,16 @@ repair_win(session_t *ps, win *w) { // Why care about damage when screen is unredirected? // We will force full-screen repaint on redirection. if (!ps->redirected) { - free_region(ps, &parts); + pixman_region32_fini(&parts); return; } // Remove the part in the damage area that could be ignored - if (!ps->reg_ignore_expire && w->prev_trans && w->prev_trans->reg_ignore) - XFixesSubtractRegion(ps->dpy, parts, parts, w->prev_trans->reg_ignore); + if (w->reg_ignore && win_is_region_ignore_valid(ps, w)) + pixman_region32_subtract(&parts, &parts, w->reg_ignore); - add_damage(ps, parts); + add_damage(ps, &parts); + pixman_region32_fini(&parts); } static void @@ -2099,9 +2056,6 @@ map_win(session_t *ps, Window id) { printf_dbgf("(%#010lx): type %s\n", w->id, WINTYPES[w->window_type]); #endif - // Detect if the window is shaped or has rounded corners - win_update_shape_raw(ps, w); - // FocusIn/Out may be ignored when the window is unmapped, so we must // recheck focus here if (ps->o.track_focus) @@ -2133,9 +2087,8 @@ map_win(session_t *ps, Window id) { /* if any configure events happened while the window was unmapped, then configure the window to its correct place */ - if (w->need_configure) { + if (w->need_configure) configure_win(ps, &w->queue_configure); - } #ifdef CONFIG_DBUS // Send D-Bus signal @@ -2150,17 +2103,12 @@ finish_unmap_win(session_t *ps, win **_w) { win *w = *_w; w->ever_damaged = false; w->in_openclose = false; + w->reg_ignore_valid = false; - update_reg_ignore_expire(ps, w); - - if (w->extents != None) { - /* destroys region */ - add_damage(ps, w->extents); - w->extents = None; - } + /* damage region */ + add_damage_from_win(ps, w); free_wpaint(ps, w); - free_region(ps, &w->border_size); free_paint(ps, &w->shadow_paint); } @@ -2206,8 +2154,6 @@ static void restack_win(session_t *ps, win *w, Window new_above) { Window old_above; - update_reg_ignore_expire(ps, w); - if (w->next) { old_above = w->next->id; } else { @@ -2215,6 +2161,13 @@ restack_win(session_t *ps, win *w, Window new_above) { } if (old_above != new_above) { + w->reg_ignore_valid = false; + rc_region_unref(&w->reg_ignore); + if (w->next) { + w->next->reg_ignore_valid = false; + rc_region_unref(&w->next->reg_ignore); + } + win **prev = NULL, **prev_old = NULL; // unhook @@ -2299,6 +2252,10 @@ configure_win(session_t *ps, xcb_configure_notify_event_t *ce) { redir_start(ps); } + // Invalidate reg_ignore from the top + rc_region_unref(&ps->list->reg_ignore); + ps->list->reg_ignore_valid = false; + #ifdef CONFIG_OPENGL // Reinitialize GLX on root change if (ps->o.glx_reinit_on_root_change && ps->psglx) { @@ -2320,7 +2277,8 @@ configure_win(session_t *ps, xcb_configure_notify_event_t *ce) { // Other window changes win *w = find_win(ps, ce->window); - XserverRegion damage = None; + region_t damage; + pixman_region32_init(&damage); if (!w) return; @@ -2331,30 +2289,20 @@ configure_win(session_t *ps, xcb_configure_notify_event_t *ce) { w->queue_configure = *ce; restack_win(ps, w, ce->above_sibling); } else { - if (!(w->need_configure)) { + if (!w->need_configure) restack_win(ps, w, ce->above_sibling); - } bool factor_change = false; - // Windows restack (including window restacks happened when this - // window is not mapped) could mess up all reg_ignore - ps->reg_ignore_expire = true; - w->need_configure = false; + win_extents(w, &damage); - damage = XFixesCreateRegion(ps->dpy, 0, 0); - if (w->extents != None) { - XFixesCopyRegion(ps->dpy, damage, w->extents); - } - - // If window geometry did not change, don't free extents here + // If window geometry change, free old extents if (w->g.x != ce->x || w->g.y != ce->y || w->g.width != ce->width || w->g.height != ce->height || w->g.border_width != ce->border_width) { factor_change = true; - free_region(ps, &w->extents); - free_region(ps, &w->border_size); + pixman_region32_clear(&w->bounding_shape); } w->g.x = ce->x; @@ -2370,26 +2318,24 @@ configure_win(session_t *ps, xcb_configure_notify_event_t *ce) { w->g.height = ce->height; w->g.border_width = ce->border_width; calc_win_size(ps, w); - - // Rounded corner detection is affected by window size - if (ps->shape_exists && ps->o.shadow_ignore_shaped - && ps->o.detect_rounded_corners && w->bounding_shaped) - win_update_shape(ps, w); + win_update_bounding_shape(ps, w); } - if (damage) { - XserverRegion extents = win_extents(ps, w); - XFixesUnionRegion(ps->dpy, damage, damage, extents); - XFixesDestroyRegion(ps->dpy, extents); - add_damage(ps, damage); - } + region_t new_extents; + pixman_region32_init(&new_extents); + win_extents(w, &new_extents); + pixman_region32_union(&damage, &damage, &new_extents); + pixman_region32_fini(&new_extents); if (factor_change) { + add_damage(ps, &damage); cxinerama_win_upd_scr(ps, w); win_on_factor_change(ps, w); } } + pixman_region32_fini(&damage); + // override_redirect flag cannot be changed after window creation, as far // as I know, so there's no point to re-match windows here. w->a.override_redirect = ce->override_redirect; @@ -2481,15 +2427,7 @@ static inline void root_damaged(session_t *ps) { if (ps->root_tile_paint.pixmap) { XClearArea(ps->dpy, ps->root, 0, 0, 0, 0, true); - // if (ps->root_picture != ps->root_tile) { - free_root_tile(ps); - /* } - if (root_damage) { - xcb_connection_t *c = XGetXCBConnection(ps->dpy); - XserverRegion parts = XFixesCreateRegion(ps->dpy, 0, 0); - xcb_damage_subtract(c, root_damage, XCB_NONE, parts); - add_damage(ps, parts); - } */ + free_root_tile(ps); } // Mark screen damaged @@ -2616,22 +2554,20 @@ ev_xcb_error(session_t __attribute__((unused)) *ps, xcb_generic_error_t *err) { } static void -expose_root(session_t *ps, XRectangle *rects, int nrects) { +expose_root(session_t *ps, const rect_t *rects, int nrects) { free_all_damage_last(ps); - XserverRegion region = XFixesCreateRegion(ps->dpy, rects, nrects); - add_damage(ps, region); + region_t region; + pixman_region32_init_rects(®ion, rects, nrects); + add_damage(ps, ®ion); } /** * Force a full-screen repaint. */ void force_repaint(session_t *ps) { - assert(ps->screen_reg); - XserverRegion reg = None; - if (ps->screen_reg && (reg = copy_region(ps, ps->screen_reg))) { - ps->ev_received = true; - add_damage(ps, reg); - } + assert(pixman_region32_not_empty(&ps->screen_reg)); + ps->ev_received = true; + add_damage(ps, &ps->screen_reg); } #ifdef CONFIG_DBUS @@ -2963,18 +2899,18 @@ ev_expose(session_t *ps, xcb_expose_event_t *ev) { if (ps->n_expose == ps->size_expose) { if (ps->expose_rects) { ps->expose_rects = realloc(ps->expose_rects, - (ps->size_expose + more) * sizeof(XRectangle)); + (ps->size_expose + more) * sizeof(rect_t)); ps->size_expose += more; } else { - ps->expose_rects = malloc(more * sizeof(XRectangle)); + ps->expose_rects = malloc(more * sizeof(rect_t)); ps->size_expose = more; } } - ps->expose_rects[ps->n_expose].x = ev->x; - ps->expose_rects[ps->n_expose].y = ev->y; - ps->expose_rects[ps->n_expose].width = ev->width; - ps->expose_rects[ps->n_expose].height = ev->height; + ps->expose_rects[ps->n_expose].x1 = ev->x; + ps->expose_rects[ps->n_expose].y1 = ev->y; + ps->expose_rects[ps->n_expose].x2 = ev->x + ev->width; + ps->expose_rects[ps->n_expose].y2 = ev->y + ev->height; ps->n_expose++; if (ev->count == 0) { @@ -3070,7 +3006,7 @@ ev_property_notify(session_t *ps, xcb_property_notify_event_t *ev) { if (ps->o.frame_opacity && ev->atom == ps->atom_frame_extents) { win *w = find_toplevel(ps, ev->window); if (w) { - win_get_frame_extents(ps, w, ev->window); + win_update_frame_extents(ps, w, ev->window); // If frame extents change, the window needs repaint add_damage_from_win(ps, w); } @@ -3152,25 +3088,17 @@ ev_shape_notify(session_t *ps, xcb_shape_notify_event_t *ev) { if (!w || IsUnmapped == w->a.map_state) return; /* - * Empty border_size may indicated an + * Empty bounding_shape may indicated an * unmapped/destroyed window, in which case * seemingly BadRegion errors would be triggered * if we attempt to rebuild border_size */ - if (w->border_size) { - // Mark the old border_size as damaged - add_damage(ps, w->border_size); - - 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)); - } - - // Redo bounding shape detection and rounded corner detection - win_update_shape(ps, w); - - update_reg_ignore_expire(ps, w); + // Mark the old border_size as damaged + add_damage(ps, &w->bounding_shape); + win_update_bounding_shape(ps, w); + // Mark the new border_size as damaged + add_damage(ps, &w->bounding_shape); + w->reg_ignore_valid = false; } /** @@ -4182,8 +4110,9 @@ get_cfg(session_t *ps, int argc, char *const *argv, bool first_pass) { break; case 305: // --shadow-exclude-reg - if (!parse_geometry(ps, optarg, &ps->o.shadow_exclude_reg_geom)) - exit(1); + ps->o.shadow_exclude_reg_str = strdup(optarg); + printf_err("--shadow-exclude-reg is deprecated.\n" + "You are likely better off using --shadow-exclude anyway"); break; case 306: // --paint-exclude @@ -5106,6 +5035,7 @@ mainloop(session_t *ps) { static void cxinerama_upd_scrs(session_t *ps) { #ifdef CONFIG_XINERAMA + // XXX Consider deprecating Xinerama, switch to RandR when necessary free_xinerama_info(ps); if (!ps->o.xinerama_shadow_crop || !ps->xinerama_exists) return; @@ -5132,9 +5062,7 @@ cxinerama_upd_scrs(session_t *ps) { * ps->xinerama_nscrs)); for (int i = 0; i < ps->xinerama_nscrs; ++i) { const xcb_xinerama_screen_info_t * const s = &scrs[i]; - XRectangle r = { .x = s->x_org, .y = s->y_org, - .width = s->width, .height = s->height }; - ps->xinerama_scr_regs[i] = XFixesCreateRegion(ps->dpy, &r, 1); + pixman_region32_init_rect(&ps->xinerama_scr_regs[i], s->x_org, s->y_org, s->width, s->height); } #endif } @@ -5161,7 +5089,6 @@ session_init(session_t *ps_old, int argc, char **argv) { .overlay = None, .root_tile_fill = false, .root_tile_paint = PAINT_INIT, - .screen_reg = None, .tgt_picture = None, .tgt_buffer = PAINT_INIT, .root_dbe = None, @@ -5223,7 +5150,7 @@ session_init(session_t *ps_old, int argc, char **argv) { .inactive_opacity = OPAQUE, .inactive_opacity_override = false, .active_opacity = OPAQUE, - .frame_opacity = 0.0, + .frame_opacity = 1.0, .detect_client_opacity = false, .alpha_step = 0.03, @@ -5254,12 +5181,9 @@ session_init(session_t *ps_old, int argc, char **argv) { .nfds_max = 0, .tmout_lst = NULL, - .all_damage = None, - .all_damage_last = { None }, .time_start = { 0, 0 }, .redirected = false, .alpha_picts = NULL, - .reg_ignore_expire = false, .idling = false, .fade_time = 0L, .ignore_head = NULL, @@ -5336,7 +5260,12 @@ session_init(session_t *ps_old, int argc, char **argv) { // Allocate a session and copy default values into it session_t *ps = malloc(sizeof(session_t)); - memcpy(ps, &s_def, sizeof(session_t)); + *ps = s_def; + pixman_region32_init(&ps->screen_reg); + pixman_region32_init(&ps->all_damage); + for (int i = 0; i < CGLX_MAX_BUFFER_AGE; i ++) + pixman_region32_init(&ps->all_damage_last[i]); + ps_g = ps; ps->ignore_tail = &ps->ignore_head; gettimeofday(&ps->time_start, NULL); @@ -5802,10 +5731,10 @@ session_destroy(session_t *ps) { // Free other X resources free_root_tile(ps); - free_region(ps, &ps->screen_reg); - free_region(ps, &ps->all_damage); + pixman_region32_fini(&ps->screen_reg); + pixman_region32_fini(&ps->all_damage); for (int i = 0; i < CGLX_MAX_BUFFER_AGE; ++i) - free_region(ps, &ps->all_damage_last[i]); + pixman_region32_fini(&ps->all_damage_last[i]); free(ps->expose_rects); free(ps->shadow_corner); free(ps->shadow_top); @@ -5895,12 +5824,10 @@ session_run(session_t *ps) { if (ps->o.sw_opti) ps->paint_tm_offset = get_time_timeval().tv_usec; - ps->reg_ignore_expire = true; - t = paint_preprocess(ps, ps->list); if (ps->redirected) - paint_all(ps, None, None, t); + paint_all(ps, NULL, NULL, t); // Initialize idling ps->idling = false; @@ -5935,23 +5862,31 @@ session_run(session_t *ps) { // If the screen is unredirected, free all_damage to stop painting if (!ps->redirected || ON == ps->o.stoppaint_force) - free_region(ps, &ps->all_damage); + pixman_region32_clear(&ps->all_damage); + + if (pixman_region32_not_empty(&ps->all_damage)) { + region_t all_damage_orig, *region_real = NULL; + pixman_region32_init(&all_damage_orig); + + // keep a copy of non-resized all_damage for region_real + if (ps->o.resize_damage > 0) { + copy_region(&all_damage_orig, &ps->all_damage); + resize_region(ps, &ps->all_damage, ps->o.resize_damage); + region_real = &all_damage_orig; + } - XserverRegion all_damage_orig = None; - if (ps->o.resize_damage > 0) - all_damage_orig = copy_region(ps, ps->all_damage); - resize_region(ps, ps->all_damage, ps->o.resize_damage); - if (ps->all_damage && !is_region_empty(ps, ps->all_damage, NULL)) { static int paint = 0; - paint_all(ps, ps->all_damage, all_damage_orig, t); - ps->reg_ignore_expire = false; + paint_all(ps, &ps->all_damage, region_real, t); + + pixman_region32_clear(&ps->all_damage); + pixman_region32_fini(&all_damage_orig); + paint++; if (ps->o.benchmark && paint >= ps->o.benchmark) exit(0); + XSync(ps->dpy, False); - ps->all_damage = None; } - free_region(ps, &all_damage_orig); if (ps->idling) ps->fade_time = 0L; diff --git a/src/compton.h b/src/compton.h index 5ec4fd4..af225e5 100644 --- a/src/compton.h +++ b/src/compton.h @@ -25,10 +25,12 @@ #include #endif +#include #ifdef CONFIG_OPENGL -#include "opengl.h" +#include "opengl.h" // XXX clean up #endif #include "common.h" +#include "x.h" #include "c2.h" // == Functions == @@ -38,7 +40,7 @@ // inline functions must be made static to compile correctly under clang: // http://clang.llvm.org/compatibility.html#inline -void add_damage(session_t *ps, XserverRegion damage); +void add_damage(session_t *ps, const region_t *damage); long determine_evmask(session_t *ps, Window wid, win_evmode_t mode); @@ -53,9 +55,7 @@ void render(session_t *ps, int x, int y, int dx, int dy, int wid, int hei, double opacity, bool argb, bool neg, xcb_render_picture_t pict, glx_texture_t *ptex, - XserverRegion reg_paint, const reg_data_t *pcache_reg - , const glx_prog_main_t *pprogram - ); + const region_t *reg_paint, const glx_prog_main_t *pprogram); /** * Reset filter on a Picture. @@ -108,41 +108,6 @@ array_wid_exists(const Window *arr, int count, Window wid) { return false; } -/** - * Convert a geometry_t value to XRectangle. - */ -static inline XRectangle -geom_to_rect(session_t *ps, const geometry_t *src, const XRectangle *def) { - XRectangle rect_def = { .x = 0, .y = 0, - .width = ps->root_width, .height = ps->root_height }; - if (!def) def = &rect_def; - - XRectangle rect = { .x = src->x, .y = src->y, - .width = src->wid, .height = src->hei }; - if (src->wid < 0) rect.width = def->width; - if (src->hei < 0) rect.height = def->height; - if (-1 == src->x) rect.x = def->x; - else if (src->x < 0) rect.x = ps->root_width + rect.x + 2 - rect.width; - if (-1 == src->y) rect.y = def->y; - else if (src->y < 0) rect.y = ps->root_height + rect.y + 2 - rect.height; - return rect; -} - -/** - * Convert a XRectangle to a XServerRegion. - */ -static inline XserverRegion -rect_to_reg(session_t *ps, const XRectangle *src) { - if (!src) return None; - XRectangle bound = { .x = 0, .y = 0, - .width = ps->root_width, .height = ps->root_height }; - XRectangle res = { }; - rect_crop(&res, src, &bound); - if (res.width && res.height) - return XFixesCreateRegion(ps->dpy, &res, 1); - return None; -} - /** * Destroy a Picture. */ @@ -185,7 +150,7 @@ free_xinerama_info(session_t *ps) { #ifdef CONFIG_XINERAMA if (ps->xinerama_scr_regs) { for (int i = 0; i < ps->xinerama_nscrs; ++i) - free_region(ps, &ps->xinerama_scr_regs[i]); + pixman_region32_fini(&ps->xinerama_scr_regs[i]); free(ps->xinerama_scr_regs); } cxfree(ps->xinerama_scrs); @@ -227,16 +192,6 @@ free_texture(session_t *ps, glx_texture_t **t) { } #endif -/** - * Free data in a reg_data_t. - */ -static inline void -free_reg_data(reg_data_t *pregd) { - cxfree(pregd->rects); - pregd->rects = NULL; - pregd->nrects = 0; -} - /** * Free paint_t. */ @@ -262,12 +217,11 @@ free_wpaint(session_t *ps, win *w) { static inline void free_win_res(session_t *ps, win *w) { free_win_res_glx(ps, w); - free_region(ps, &w->extents); free_paint(ps, &w->paint); - free_region(ps, &w->border_size); + pixman_region32_fini(&w->bounding_shape); free_paint(ps, &w->shadow_paint); free_damage(ps, &w->damage); - free_region(ps, &w->reg_ignore); + rc_region_unref(&w->reg_ignore); free(w->name); free(w->class_instance); free(w->class_general); @@ -389,16 +343,6 @@ wid_get_children(session_t *ps, Window w, return true; } -/** - * Determine if a window change affects reg_ignore and set - * reg_ignore_expire accordingly. - */ -static inline void -update_reg_ignore_expire(session_t *ps, const win *w) { - if (w->to_paint && WMODE_SOLID == w->mode) - ps->reg_ignore_expire = true; -} - /** * Check whether a window has WM frames. */ @@ -482,38 +426,6 @@ find_win_all(session_t *ps, const Window wid) { return w; } -bool win_has_alpha(win *); -static inline void -win_render(session_t *ps, win *w, int x, int y, int wid, int hei, - double opacity, XserverRegion reg_paint, const reg_data_t *pcache_reg, - xcb_render_picture_t pict) { - const int dx = (w ? w->g.x: 0) + x; - const int dy = (w ? w->g.y: 0) + y; - const bool argb = (w && (win_has_alpha(w) || ps->o.force_win_blend)); - const bool neg = (w && w->invert_color); - - render(ps, x, y, dx, dy, wid, hei, opacity, argb, neg, - pict, (w ? w->paint.ptex: ps->root_tile_paint.ptex), - reg_paint, pcache_reg, (w ? &ps->o.glx_prog_win: NULL)); -} - -static inline void -set_tgt_clip(session_t *ps, XserverRegion reg, const reg_data_t *pcache_reg) { - switch (ps->o.backend) { - case BKEND_XRENDER: - case BKEND_XR_GLX_HYBRID: - XFixesSetPictureClipRegion(ps->dpy, ps->tgt_buffer.pict, 0, 0, reg); - break; -#ifdef CONFIG_OPENGL - case BKEND_GLX: - glx_set_clip(ps, reg, pcache_reg); - break; -#endif - default: - assert(false); - } -} - /** * Normalize a convolution kernel. */ @@ -527,87 +439,38 @@ normalize_conv_kern(int wid, int hei, xcb_render_fixed_t *kern) { kern[i] = DOUBLE_TO_XFIXED(XFIXED_TO_DOUBLE(kern[i]) * factor); } -/** - * Get a region of the screen size. - */ -inline static XserverRegion -get_screen_region(session_t *ps) { - XRectangle r; - - r.x = 0; - r.y = 0; - r.width = ps->root_width; - r.height = ps->root_height; - return XFixesCreateRegion(ps->dpy, &r, 1); -} - /** * Resize a region. */ static inline void -resize_region(session_t *ps, XserverRegion region, short mod) { +resize_region(session_t *ps, region_t *region, short mod) { if (!mod || !region) return; - - int nrects = 0, nnewrects = 0; - XRectangle *newrects = NULL; - XRectangle *rects = XFixesFetchRegion(ps->dpy, region, &nrects); - if (!rects || !nrects) - goto resize_region_end; - - // Allocate memory for new rectangle list, because I don't know if it's - // safe to write in the memory Xlib allocates - newrects = calloc(nrects, sizeof(XRectangle)); - if (!newrects) { - printf_errf("(): Failed to allocate memory."); - exit(1); - } - // Loop through all rectangles - for (int i = 0; i < nrects; ++i) { - int x1 = max_i(rects[i].x - mod, 0); - int y1 = max_i(rects[i].y - mod, 0); - int x2 = min_i(rects[i].x + rects[i].width + mod, ps->root_width); - int y2 = min_i(rects[i].y + rects[i].height + mod, ps->root_height); + int nrects; + int nnewrects = 0; + pixman_box32_t *rects = pixman_region32_rectangles(region, &nrects); + pixman_box32_t *newrects = calloc(nrects, sizeof *newrects); + for (int i = 0; i < nrects; i++) { + int x1 = max_i(rects[i].x1 - mod, 0); + int y1 = max_i(rects[i].y1 - mod, 0); + int x2 = min_i(rects[i].x2 + mod, ps->root_width); + int y2 = min_i(rects[i].y2 + mod, ps->root_height); int wid = x2 - x1; int hei = y2 - y1; if (wid <= 0 || hei <= 0) continue; - newrects[nnewrects].x = x1; - newrects[nnewrects].y = y1; - newrects[nnewrects].width = wid; - newrects[nnewrects].height = hei; + newrects[nnewrects] = (pixman_box32_t) { + .x1 = x1, .x2 = x2, .y1 = y1, .y2 = y2 + }; ++nnewrects; } - // Set region - XFixesSetRegion(ps->dpy, region, newrects, nnewrects); + pixman_region32_fini(region); + pixman_region32_init_rects(region, newrects, nnewrects); -resize_region_end: - cxfree(rects); free(newrects); } -/** - * Dump a region. - */ -static inline void -dump_region(const session_t *ps, XserverRegion region) { - int nrects = 0; - XRectangle *rects = NULL; - if (!rects && region) - rects = XFixesFetchRegion(ps->dpy, region, &nrects); - - printf_dbgf("(%#010lx): %d rects\n", region, nrects); - if (!rects) return; - for (int i = 0; i < nrects; ++i) - printf("Rect #%d: %8d, %8d, %8d, %8d\n", i, rects[i].x, rects[i].y, - rects[i].width, rects[i].height); - putchar('\n'); - fflush(stdout); - - cxfree(rects); -} - #ifdef CONFIG_OPENGL /** * Ensure we have a GLX context. diff --git a/src/config.c b/src/config.c index ffcf0cc..8e40947 100644 --- a/src/config.c +++ b/src/config.c @@ -3,6 +3,7 @@ #include "common.h" #include "config.h" +#include "utils.h" #include "c2.h" /** @@ -201,19 +202,24 @@ parse_conv_kern_lst(session_t *ps, const char *src, xcb_render_fixed_t **dest, i /** * Parse a X geometry. + * + * ps->root_width and ps->root_height must be valid */ bool -parse_geometry(session_t *ps, const char *src, geometry_t *dest) { +parse_geometry(session_t *ps, const char *src, region_t *dest) { + pixman_region32_clear(dest); + if (!src) + return true; + if (!ps->root_width || !ps->root_height) + return true; + geometry_t geom = { .wid = -1, .hei = -1, .x = -1, .y = -1 }; long val = 0L; char *endptr = NULL; -#define T_STRIPSPACE() do { \ - while (*src && isspace(*src)) ++src; \ - if (!*src) goto parse_geometry_end; \ -} while(0) - - T_STRIPSPACE(); + src = skip_space(src); + if (!*src) + goto parse_geometry_end; // Parse width // Must be base 10, because "0x0..." may appear @@ -221,10 +227,13 @@ parse_geometry(session_t *ps, const char *src, geometry_t *dest) { val = strtol(src, &endptr, 10); if (endptr && src != endptr) { geom.wid = val; - assert(geom.wid >= 0); + if (geom.wid < 0) { + printf_errf("(\"%s\"): Invalid width.", src); + return false; + } src = endptr; } - T_STRIPSPACE(); + src = skip_space(src); } // Parse height @@ -239,7 +248,7 @@ parse_geometry(session_t *ps, const char *src, geometry_t *dest) { } src = endptr; } - T_STRIPSPACE(); + src = skip_space(src); } // Parse x @@ -247,11 +256,11 @@ parse_geometry(session_t *ps, const char *src, geometry_t *dest) { val = strtol(src, &endptr, 10); if (endptr && src != endptr) { geom.x = val; - if ('-' == *src && geom.x <= 0) - geom.x -= 2; + if (*src == '-') + geom.x += ps->root_width - geom.wid; src = endptr; } - T_STRIPSPACE(); + src = skip_space(src); } // Parse y @@ -259,11 +268,11 @@ parse_geometry(session_t *ps, const char *src, geometry_t *dest) { val = strtol(src, &endptr, 10); if (endptr && src != endptr) { geom.y = val; - if ('-' == *src && geom.y <= 0) - geom.y -= 2; + if (*src == '-') + geom.y += ps->root_height - geom.hei; src = endptr; } - T_STRIPSPACE(); + src = skip_space(src); } if (*src) { @@ -272,7 +281,7 @@ parse_geometry(session_t *ps, const char *src, geometry_t *dest) { } parse_geometry_end: - *dest = geom; + pixman_region32_union_rect(dest, dest, geom.x, geom.y, geom.wid, geom.hei); return true; } diff --git a/src/config.h b/src/config.h index e756b47..1723215 100644 --- a/src/config.h +++ b/src/config.h @@ -13,7 +13,7 @@ const char *parse_matrix_readnum(const char *, double *); xcb_render_fixed_t *parse_matrix(session_t *, const char *, const char **); xcb_render_fixed_t *parse_conv_kern(session_t *, const char *, const char **); bool parse_conv_kern_lst(session_t *, const char *, xcb_render_fixed_t **, int); -bool parse_geometry(session_t *, const char *, geometry_t *); +bool parse_geometry(session_t *, const char *, region_t *); bool parse_rule_opacity(session_t *, const char *); /** diff --git a/src/config_libconfig.c b/src/config_libconfig.c index 864d302..f6012fe 100644 --- a/src/config_libconfig.c +++ b/src/config_libconfig.c @@ -269,9 +269,8 @@ parse_config(session_t *ps, struct options_tmp *pcfgtmp) { // --shadow-blue config_lookup_float(&cfg, "shadow-blue", &ps->o.shadow_blue); // --shadow-exclude-reg - if (config_lookup_string(&cfg, "shadow-exclude-reg", &sval) - && !parse_geometry(ps, sval, &ps->o.shadow_exclude_reg_geom)) - exit(1); + if (config_lookup_string(&cfg, "shadow-exclude-reg", &sval)) + ps->o.shadow_exclude_reg_str = strdup(sval); // --inactive-opacity-override lcfg_lookup_bool(&cfg, "inactive-opacity-override", &ps->o.inactive_opacity_override); diff --git a/src/opengl.c b/src/opengl.c index 67e7aea..3233f6b 100644 --- a/src/opengl.c +++ b/src/opengl.c @@ -900,7 +900,7 @@ glx_release_pixmap(session_t *ps, glx_texture_t *ptex) { * Preprocess function before start painting. */ void -glx_paint_pre(session_t *ps, XserverRegion *preg) { +glx_paint_pre(session_t *ps, region_t *preg) { ps->psglx->z = 0.0; // glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); @@ -908,107 +908,46 @@ glx_paint_pre(session_t *ps, XserverRegion *preg) { bool trace_damage = (ps->o.glx_swap_method < 0 || ps->o.glx_swap_method > 1); // Trace raw damage regions - XserverRegion newdamage = None; - if (trace_damage && *preg) - newdamage = copy_region(ps, *preg); + region_t newdamage; + pixman_region32_init(&newdamage); + if (trace_damage) + copy_region(&newdamage, preg); - // OpenGL doesn't support partial repaint without GLX_MESA_copy_sub_buffer, - // we could redraw the whole screen or copy unmodified pixels from - // front buffer with --glx-copy-from-front. - if (ps->o.glx_use_copysubbuffermesa || !*preg) { + // We use GLX buffer_age extension to decide which pixels in + // the back buffer is reusable, and limit our redrawing + int buffer_age = 0; + + // Query GLX_EXT_buffer_age for buffer age + if (ps->o.glx_swap_method == SWAPM_BUFFER_AGE) { + unsigned val = 0; + glXQueryDrawable(ps->dpy, get_tgt_window(ps), + GLX_BACK_BUFFER_AGE_EXT, &val); + buffer_age = val; } - else { - int buffer_age = ps->o.glx_swap_method; - // Getting buffer age - { - // Query GLX_EXT_buffer_age for buffer age - if (SWAPM_BUFFER_AGE == buffer_age) { - unsigned val = 0; - glXQueryDrawable(ps->dpy, get_tgt_window(ps), - GLX_BACK_BUFFER_AGE_EXT, &val); - buffer_age = val; - } + // Buffer age too high + if (buffer_age > CGLX_MAX_BUFFER_AGE + 1) + buffer_age = 0; - // Buffer age too high - if (buffer_age > CGLX_MAX_BUFFER_AGE + 1) - buffer_age = 0; + assert(buffer_age >= 0); - // Make sure buffer age >= 0 - buffer_age = max_i(buffer_age, 0); - - // Check if we have we have empty regions - if (buffer_age > 1) { - for (int i = 0; i < buffer_age - 1; ++i) - if (!ps->all_damage_last[i]) { buffer_age = 0; break; } - } - } - - // Do nothing for buffer_age 1 (copy) - if (1 != buffer_age) { - // Copy pixels - if (ps->o.glx_copy_from_front) { - // Determine copy area - XserverRegion reg_copy = XFixesCreateRegion(ps->dpy, NULL, 0); - if (!buffer_age) { - XFixesSubtractRegion(ps->dpy, reg_copy, ps->screen_reg, *preg); - } - else { - for (int i = 0; i < buffer_age - 1; ++i) - XFixesUnionRegion(ps->dpy, reg_copy, reg_copy, - ps->all_damage_last[i]); - XFixesSubtractRegion(ps->dpy, reg_copy, reg_copy, *preg); - } - - // Actually copy pixels - { - GLfloat raster_pos[4]; - GLfloat curx = 0.0f, cury = 0.0f; - glGetFloatv(GL_CURRENT_RASTER_POSITION, raster_pos); - glReadBuffer(GL_FRONT); - glRasterPos2f(0.0, 0.0); - { - int nrects = 0; - XRectangle *rects = XFixesFetchRegion(ps->dpy, reg_copy, &nrects); - for (int i = 0; i < nrects; ++i) { - const int x = rects[i].x; - const int y = ps->root_height - rects[i].y - rects[i].height; - // Kwin patch says glRasterPos2f() causes artifacts on bottom - // screen edge with some drivers - glBitmap(0, 0, 0, 0, x - curx, y - cury, NULL); - curx = x; - cury = y; - glCopyPixels(x, y, rects[i].width, rects[i].height, GL_COLOR); - } - cxfree(rects); - } - glReadBuffer(GL_BACK); - glRasterPos4fv(raster_pos); - } - - free_region(ps, ®_copy); - } - - // Determine paint area - if (ps->o.glx_copy_from_front) { } - else if (buffer_age) { - for (int i = 0; i < buffer_age - 1; ++i) - XFixesUnionRegion(ps->dpy, *preg, *preg, ps->all_damage_last[i]); - } - else { - free_region(ps, preg); - } - } - } + if (buffer_age) { + // Determine paint area + for (int i = 0; i < buffer_age - 1; ++i) + pixman_region32_union(preg, preg, &ps->all_damage_last[i]); + } else + // buffer_age == 0 means buffer age is not available, paint everything + copy_region(preg, &ps->screen_reg); if (trace_damage) { - free_region(ps, &ps->all_damage_last[CGLX_MAX_BUFFER_AGE - 1]); + // XXX use a circular queue instead of memmove + pixman_region32_fini(&ps->all_damage_last[CGLX_MAX_BUFFER_AGE - 1]); memmove(ps->all_damage_last + 1, ps->all_damage_last, - (CGLX_MAX_BUFFER_AGE - 1) * sizeof(XserverRegion)); + (CGLX_MAX_BUFFER_AGE - 1) * sizeof(region_t *)); ps->all_damage_last[0] = newdamage; } - glx_set_clip(ps, *preg, NULL); + glx_set_clip(ps, preg); #ifdef DEBUG_GLX_PAINTREG glx_render_color(ps, 0, 0, ps->root_width, ps->root_height, 0, *preg, NULL); @@ -1021,118 +960,46 @@ glx_paint_pre(session_t *ps, XserverRegion *preg) { * Set clipping region on the target window. */ void -glx_set_clip(session_t *ps, XserverRegion reg, const reg_data_t *pcache_reg) { +glx_set_clip(session_t *ps, const region_t *reg) { // Quit if we aren't using stencils if (ps->o.glx_no_stencil) return; - static XRectangle rect_blank = { .x = 0, .y = 0, .width = 0, .height = 0 }; - glDisable(GL_STENCIL_TEST); glDisable(GL_SCISSOR_TEST); if (!reg) return; - int nrects = 0; - XRectangle *rects_free = NULL; - const XRectangle *rects = NULL; - if (pcache_reg) { - rects = pcache_reg->rects; - nrects = pcache_reg->nrects; - } - if (!rects) { - nrects = 0; - rects = rects_free = XFixesFetchRegion(ps->dpy, reg, &nrects); - } - // Use one empty rectangle if the region is empty - if (!nrects) { - cxfree(rects_free); - rects_free = NULL; - nrects = 1; - rects = &rect_blank; - } + int nrects; + const rect_t *rects = pixman_region32_rectangles((region_t *)reg, &nrects); - assert(nrects); - if (1 == nrects) { + if (nrects == 1) { glEnable(GL_SCISSOR_TEST); - glScissor(rects[0].x, ps->root_height - rects[0].y - rects[0].height, - rects[0].width, rects[0].height); + glScissor(rects[0].x1, ps->root_height-rects[0].y2, + rects[0].x2-rects[0].x1, rects[0].y2-rects[0].y1); } - else { - glEnable(GL_STENCIL_TEST); - glClear(GL_STENCIL_BUFFER_BIT); - - glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); - glDepthMask(GL_FALSE); - glStencilOp(GL_REPLACE, GL_KEEP, GL_KEEP); - - glBegin(GL_QUADS); - - for (int i = 0; i < nrects; ++i) { - GLint rx = rects[i].x; - GLint ry = ps->root_height - rects[i].y; - GLint rxe = rx + rects[i].width; - GLint rye = ry - rects[i].height; - GLint z = 0; - -#ifdef DEBUG_GLX - printf_dbgf("(): Rect %d: %d, %d, %d, %d\n", i, rx, ry, rxe, rye); -#endif - - glVertex3i(rx, ry, z); - glVertex3i(rxe, ry, z); - glVertex3i(rxe, rye, z); - glVertex3i(rx, rye, z); - } - - glEnd(); - - glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); - glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); - // glDepthMask(GL_TRUE); - } - - cxfree(rects_free); glx_check_err(ps); } -#define P_PAINTREG_START() \ - XserverRegion reg_new = None; \ - XRectangle rec_all = { .x = dx, .y = dy, .width = width, .height = height }; \ - XRectangle *rects = &rec_all; \ - int nrects = 1; \ - \ - if (ps->o.glx_no_stencil && reg_tgt) { \ - if (pcache_reg) { \ - rects = pcache_reg->rects; \ - nrects = pcache_reg->nrects; \ - } \ - else { \ - reg_new = XFixesCreateRegion(ps->dpy, &rec_all, 1); \ - XFixesIntersectRegion(ps->dpy, reg_new, reg_new, reg_tgt); \ - \ - nrects = 0; \ - rects = XFixesFetchRegion(ps->dpy, reg_new, &nrects); \ - } \ - } \ +#define P_PAINTREG_START(var) \ + region_t reg_new; \ + int nrects; \ + const rect_t *rects; \ + pixman_region32_init_rect(®_new, dx, dy, width, height); \ + pixman_region32_intersect(®_new, ®_new, (region_t *)reg_tgt); \ + rects = pixman_region32_rectangles(®_new, &nrects); \ glBegin(GL_QUADS); \ \ for (int ri = 0; ri < nrects; ++ri) { \ - XRectangle crect; \ - rect_crop(&crect, &rects[ri], &rec_all); \ - \ - if (!crect.width || !crect.height) \ - continue; \ + rect_t var = rects[ri]; #define P_PAINTREG_END() \ } \ glEnd(); \ \ - if (rects && rects != &rec_all && !(pcache_reg && pcache_reg->rects == rects)) \ - cxfree(rects); \ - free_region(ps, ®_new); \ + pixman_region32_fini(®_new); static inline GLuint glx_gen_texture(session_t *ps, GLenum tex_tgt, int width, int height) { @@ -1162,11 +1029,13 @@ glx_copy_region_to_tex(session_t *ps, GLenum tex_tgt, int basex, int basey, /** * Blur contents in a particular region. + * + * XXX seems to be way to complex for what it does */ bool glx_blur_dst(session_t *ps, int dx, int dy, int width, int height, float z, GLfloat factor_center, - XserverRegion reg_tgt, const reg_data_t *pcache_reg, + const region_t *reg_tgt, glx_blur_cache_t *pbc) { assert(ps->psglx->blur_passes[0].prog); const bool more_passes = ps->psglx->blur_passes[1].prog; @@ -1310,23 +1179,19 @@ glx_blur_dst(session_t *ps, int dx, int dy, int width, int height, float z, glUniform1f(ppass->unifm_factor_center, factor_center); { - P_PAINTREG_START(); - { - const GLfloat rx = (crect.x - mdx) * texfac_x; - const GLfloat ry = (mheight - (crect.y - mdy)) * texfac_y; - const GLfloat rxe = rx + crect.width * texfac_x; - const GLfloat rye = ry - crect.height * texfac_y; - GLfloat rdx = crect.x - mdx; - GLfloat rdy = mheight - crect.y + mdy; - GLfloat rdxe = rdx + crect.width; - GLfloat rdye = rdy - crect.height; - + P_PAINTREG_START(crect) { + const GLfloat rx = (crect.x1 - mdx) * texfac_x; + const GLfloat ry = (mheight - (crect.y1 - mdy)) * texfac_y; + const GLfloat rxe = rx + (crect.x2 - crect.x1) * texfac_x; + const GLfloat rye = ry - (crect.y2 - crect.y1) * texfac_y; + GLfloat rdx = crect.x1 - mdx; + GLfloat rdy = mheight - crect.y1 + mdy; if (last_pass) { - rdx = crect.x; - rdy = ps->root_height - crect.y; - rdxe = rdx + crect.width; - rdye = rdy - crect.height; + rdx = crect.x1; + rdy = ps->root_height - crect.y1; } + GLfloat rdxe = rdx + (crect.x2 - crect.x1); + GLfloat rdye = rdy - (crect.y2 - crect.y1); #ifdef DEBUG_GLX printf_dbgf("(): %f, %f, %f, %f -> %f, %f, %f, %f\n", rx, ry, rxe, rye, rdx, rdy, rdxe, rdye); @@ -1343,8 +1208,7 @@ glx_blur_dst(session_t *ps, int dx, int dy, int width, int height, float z, glTexCoord2f(rx, rye); glVertex3f(rdx, rdye, z); - } - P_PAINTREG_END(); + } P_PAINTREG_END(); } glUseProgram(0); @@ -1379,7 +1243,7 @@ glx_blur_dst_end: bool glx_dim_dst(session_t *ps, int dx, int dy, int width, int height, float z, - GLfloat factor, XserverRegion reg_tgt, const reg_data_t *pcache_reg) { + GLfloat factor, const region_t *reg_tgt) { // It's possible to dim in glx_render(), but it would be over-complicated // considering all those mess in color negation and modulation glEnable(GL_BLEND); @@ -1387,12 +1251,12 @@ glx_dim_dst(session_t *ps, int dx, int dy, int width, int height, float z, glColor4f(0.0f, 0.0f, 0.0f, factor); { - P_PAINTREG_START(); - { - GLint rdx = crect.x; - GLint rdy = ps->root_height - crect.y; - GLint rdxe = rdx + crect.width; - GLint rdye = rdy - crect.height; + P_PAINTREG_START(crect) { + // XXX what does all of these variables mean? + GLint rdx = crect.x1; + GLint rdy = ps->root_height - crect.y1; + GLint rdxe = rdx + (crect.x2 - crect.x1); + GLint rdye = rdy - (crect.y2 - crect.y1); glVertex3i(rdx, rdy, z); glVertex3i(rdxe, rdy, z); @@ -1419,8 +1283,7 @@ bool glx_render(session_t *ps, const glx_texture_t *ptex, int x, int y, int dx, int dy, int width, int height, int z, double opacity, bool argb, bool neg, - XserverRegion reg_tgt, const reg_data_t *pcache_reg - , const glx_prog_main_t *pprogram + const region_t *reg_tgt, const glx_prog_main_t *pprogram ) { if (!ptex || !ptex->texture) { printf_errf("(): Missing texture."); @@ -1551,12 +1414,12 @@ glx_render(session_t *ps, const glx_texture_t *ptex, // Painting { - P_PAINTREG_START(); - { - GLfloat rx = (double) (crect.x - dx + x); - GLfloat ry = (double) (crect.y - dy + y); - GLfloat rxe = rx + (double) crect.width; - GLfloat rye = ry + (double) crect.height; + P_PAINTREG_START(crect) { + // XXX explain these variables + GLfloat rx = (double) (crect.x1 - dx + x); + GLfloat ry = (double) (crect.y1 - dy + y); + GLfloat rxe = rx + (double) (crect.x2 - crect.x1); + GLfloat rye = ry + (double) (crect.y2 - crect.y1); // Rectangle textures have [0-w] [0-h] while 2D texture has [0-1] [0-1] // Thanks to amonakov for pointing out! if (GL_TEXTURE_2D == ptex->target) { @@ -1565,10 +1428,10 @@ glx_render(session_t *ps, const glx_texture_t *ptex, rxe = rxe / ptex->width; rye = rye / ptex->height; } - GLint rdx = crect.x; - GLint rdy = ps->root_height - crect.y; - GLint rdxe = rdx + crect.width; - GLint rdye = rdy - crect.height; + GLint rdx = crect.x1; + GLint rdy = ps->root_height - crect.y1; + GLint rdxe = rdx + (crect.x2 - crect.x1); + GLint rdye = rdy - (crect.y2 - crect.y1); // Invert Y if needed, this may not work as expected, though. I don't // have such a FBConfig to test with. @@ -1599,8 +1462,7 @@ glx_render(session_t *ps, const glx_texture_t *ptex, P_TEXCOORD(rx, rye); glVertex3i(rdx, rdye, z); - } - P_PAINTREG_END(); + } P_PAINTREG_END(); } // Cleanup diff --git a/src/opengl.h b/src/opengl.h index 99e7965..4757b4e 100644 --- a/src/opengl.h +++ b/src/opengl.h @@ -124,13 +124,13 @@ glx_hasglext(session_t *ps, const char *ext) { bool glx_dim_dst(session_t *ps, int dx, int dy, int width, int height, float z, - GLfloat factor, XserverRegion reg_tgt, const reg_data_t *); + GLfloat factor, const region_t *reg_tgt); bool glx_render(session_t *ps, const glx_texture_t *ptex, int x, int y, int dx, int dy, int width, int height, int z, double opacity, bool argb, bool neg, - XserverRegion reg_tgt, const reg_data_t *, + const region_t *reg_tgt, const glx_prog_main_t *pprogram); bool @@ -162,7 +162,7 @@ glx_bind_pixmap(session_t *ps, glx_texture_t **pptex, Pixmap pixmap, void glx_release_pixmap(session_t *ps, glx_texture_t *ptex); -void glx_paint_pre(session_t *ps, XserverRegion *preg) +void glx_paint_pre(session_t *ps, region_t *preg) __attribute__((nonnull(1, 2))); /** @@ -175,13 +175,12 @@ glx_tex_binded(const glx_texture_t *ptex, Pixmap pixmap) { } void -glx_set_clip(session_t *ps, XserverRegion reg, const reg_data_t *); +glx_set_clip(session_t *ps, const region_t *reg); bool glx_blur_dst(session_t *ps, int dx, int dy, int width, int height, float z, GLfloat factor_center, - XserverRegion reg_tgt, - const reg_data_t *, + const region_t *reg_tgt, glx_blur_cache_t *pbc); GLuint diff --git a/src/region.h b/src/region.h index 713b4e7..fa1bc1b 100644 --- a/src/region.h +++ b/src/region.h @@ -11,12 +11,12 @@ RC_TYPE(region_t, rc_region, pixman_region32_init, pixman_region32_fini, static /// copy a region_t static inline void -copy_region_(region_t *dst, const region_t *p) { +copy_region(region_t *dst, const region_t *p) { pixman_region32_copy(dst, (region_t *)p); } static inline void -dump_region_(const region_t *x) { +dump_region(const region_t *x) { int nrects; const rect_t *rects = pixman_region32_rectangles((region_t *)x, &nrects); fprintf(stderr, "nrects: %d\n", nrects); diff --git a/src/win.c b/src/win.c index ff912f0..402506c 100644 --- a/src/win.c +++ b/src/win.c @@ -1,5 +1,4 @@ #include -#include #include #include #include @@ -75,36 +74,27 @@ group_is_focused(session_t *ps, Window leader) { /** * 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->g.x: 0); - r.y = (use_offset ? w->g.y: 0); - r.width = w->widthb; - r.height = w->heightb; - - return XFixesCreateRegion(ps->dpy, &r, 1); +void win_get_region(session_t *ps, win *w, bool global, region_t *res) { + pixman_region32_union_rect(res, res, + global ? w->g.x : 0, + global ? w->g.y : 0, + w->widthb, w->heightb); } /** * Get a rectangular region a window occupies, excluding frame and shadow. */ -XserverRegion -win_get_region_noframe(session_t *ps, win *w, bool use_offset) { +void win_get_region_noframe(session_t *ps, win *w, bool global, region_t *res) { const margin_t extents = win_calc_frame_extents(ps, w); - XRectangle r; - r.x = (use_offset ? w->g.x: 0) + extents.left; - r.y = (use_offset ? w->g.y: 0) + extents.top; - r.width = max_i(w->g.width - extents.left - extents.right, 0); - r.height = max_i(w->g.height - extents.top - extents.bottom, 0); + int x = (global ? w->g.x: 0) + extents.left; + int y = (global ? w->g.y: 0) + extents.top; + int width = max_i(w->g.width - extents.left - extents.right, 0); + int height = max_i(w->g.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); + if (width > 0 && height > 0) + pixman_region32_union_rect(res, res, x, y, width, height); } /** @@ -114,13 +104,18 @@ win_get_region_noframe(session_t *ps, win *w, bool use_offset) { * @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)); - } + // XXX there was a cached extents region, investigate + // if that's better + region_t extents; + pixman_region32_init(&extents); + win_extents(w, &extents); + add_damage(ps, &extents); + pixman_region32_fini(&extents); } /** * Check if a window has rounded corners. + * XXX This is really dumb */ void win_rounded_corners(session_t *ps, win *w) { w->rounded_corners = false; @@ -129,11 +124,11 @@ void win_rounded_corners(session_t *ps, win *w) { return; // Fetch its bounding region - if (!w->border_size) - w->border_size = win_border_size(ps, w, true); + if (!pixman_region32_not_empty(&w->bounding_shape)) + win_update_bounding_shape(ps, w); // Quit if border_size() returns None - if (!w->border_size) + if (!pixman_region32_not_empty(&w->bounding_shape)) return; // Determine the minimum width/height of a rectangle that could mark @@ -144,20 +139,16 @@ void win_rounded_corners(session_t *ps, win *w) { 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; + int nrects = 0; + const rect_t *rects = pixman_region32_rectangles(&w->bounding_shape, &nrects); // 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) { + for (int i = 0; i < nrects; ++i) + if (rects[i].x2 - rects[i].x1 >= minwidth && rects[i].y2 - rects[i].y1 >= minheight) { w->rounded_corners = true; break; } - - cxfree(rects); } int win_get_name(session_t *ps, win *w) { @@ -291,7 +282,7 @@ bool win_has_alpha(win *w) { void win_determine_mode(session_t *ps, win *w) { if (win_has_alpha(w) || w->opacity != OPAQUE) { w->mode = WMODE_TRANS; - } else if (w->frame_opacity) { + } else if (w->frame_opacity != 1.0) { w->mode = WMODE_FRAME_TRANS; } else { w->mode = WMODE_SOLID; @@ -389,34 +380,6 @@ void win_determine_fade(session_t *ps, win *w) { 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); - - // XXX Window shape changed, and if we didn't fill in the pixels - // behind the window (not implemented yet), we should rebuild - // the shadow_pict - } -} - /** * Reread _COMPTON_SHADOW property from a window. * @@ -452,22 +415,26 @@ void win_set_shadow(session_t *ps, win *w, bool shadow_new) { if (w->shadow == shadow_new) return; + region_t extents; + pixman_region32_init(&extents); + win_extents(w, &extents); + 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); + // Mark the old extents as damaged if the shadow is removed + if (!w->shadow) + add_damage(ps, &extents); + + pixman_region32_clear(&extents); + // Mark the new extents as damaged if the shadow is added + if (w->shadow) { + win_extents(w, &extents); + add_damage_from_win(ps, w); } + pixman_region32_fini(&extents); } /** @@ -521,7 +488,7 @@ void win_set_blur_background(session_t *ps, win *w, bool 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)) + if (!win_is_solid(ps, w) || (ps->o.blur_background_frame && w->frame_opacity != 1)) add_damage_from_win(ps, w); } @@ -600,6 +567,7 @@ void win_on_factor_change(session_t *ps, win *w) { 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); + w->reg_ignore_valid = false; } /** @@ -610,6 +578,8 @@ void calc_win_size(session_t *ps, win *w) { w->heightb = w->g.height + w->g.border_width * 2; calc_shadow_geometry(ps, w); w->flags |= WFLAG_SIZE_CHANGE; + // Invalidate the shadow we built + free_paint(ps, &w->shadow_paint); } /** @@ -670,8 +640,8 @@ void win_mark_client(session_t *ps, win *w, Window client) { 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); + if (ps->o.frame_opacity != 1) + win_update_frame_extents(ps, w, client); // Get window group if (ps->o.track_leader) @@ -762,12 +732,12 @@ bool add_win(session_t *ps, Window id, Window prev) { .damage = None, .pixmap_damaged = false, .paint = PAINT_INIT, - .border_size = None, - .extents = None, .flags = 0, .need_configure = false, .queue_configure = {}, - .reg_ignore = None, + .reg_ignore = NULL, + .reg_ignore_valid = false, + .widthb = 0, .heightb = 0, .destroyed = false, @@ -807,7 +777,7 @@ bool add_win(session_t *ps, Window id, Window prev) { .fade_force = UNSET, .fade_callback = NULL, - .frame_opacity = 0.0, + .frame_opacity = 1.0, .frame_extents = MARGIN_INIT, .shadow = false, @@ -845,7 +815,8 @@ bool add_win(session_t *ps, Window id, Window prev) { return false; } - memcpy(new, &win_def, sizeof(win)); + *new = win_def; + pixman_region32_init(&new->bounding_shape); // Find window insertion point win **p = NULL; @@ -1154,51 +1125,26 @@ win_set_focused(session_t *ps, win *w, bool focused) { * Note w->shadow and shadow geometry must be correct before calling this * function. */ -XserverRegion win_extents(session_t *ps, win *w) { - XRectangle r; +void win_extents(win *w, region_t *res) { + pixman_region32_clear(res); + pixman_region32_union_rect(res, res, w->g.x, w->g.y, w->widthb, w->heightb); - r.x = w->g.x; - r.y = w->g.y; - r.width = w->widthb; - r.height = w->heightb; - - if (w->shadow) { - XRectangle sr; - - sr.x = w->g.x + w->shadow_dx; - sr.y = w->g.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); + if (w->shadow) + pixman_region32_union_rect(res, res, w->g.x + w->shadow_dx, w->g.y + w->shadow_dy, w->shadow_width, w->shadow_height); } /** - * Retrieve the bounding shape of a window. + * Update the out-dated bounding shape of a window. + * + * Mark the window shape as updated */ -XserverRegion -win_border_size(session_t *ps, win *w, bool use_offset) { +void win_update_bounding_shape(session_t *ps, win *w) { + if (ps->shape_exists) + w->bounding_shaped = win_bounding_shaped(ps, w->id); + + pixman_region32_clear(&w->bounding_shape); // Start with the window rectangular region - XserverRegion fin = win_get_region(ps, w, use_offset); + win_get_region(ps, w, true, &w->bounding_shape); // Only request for a bounding region if the window is shaped if (w->bounding_shaped) { @@ -1214,23 +1160,32 @@ win_border_size(session_t *ps, win *w, bool use_offset) { ps->dpy, w->id, WindowRegionBounding); if (!border) - return fin; + return; - if (use_offset) { - // Translate the region to the correct place - XFixesTranslateRegion(ps->dpy, border, - w->g.x + w->g.border_width, - w->g.y + w->g.border_width); - } + region_t br; + pixman_region32_init(&br); + x_fetch_region(ps, border, &br); + + // Add border width because we are using a different origin. + // X thinks the top left of the inner window is the origin, + // We think the top left of the border is the origin + pixman_region32_translate(&br, w->g.x + w->g.border_width, + w->g.y + w->g.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); + pixman_region32_intersect(&w->bounding_shape, &w->bounding_shape, &br); + pixman_region32_fini(&br); } - return fin; + if (w->bounding_shaped && ps->o.detect_rounded_corners) + win_rounded_corners(ps, w); + + // XXX Window shape changed, and if we didn't fill in the pixels + // behind the window (not implemented yet), we should rebuild + // the shadow_pict + win_on_factor_change(ps, w); } /** @@ -1258,21 +1213,25 @@ void win_update_opacity_prop(session_t *ps, win *w) { * Retrieve frame extents from a window. */ void -win_get_frame_extents(session_t *ps, win *w, Window client) { - cmemzero_one(&w->frame_extents); - +win_update_frame_extents(session_t *ps, win *w, Window client) { winprop_t prop = wid_get_prop(ps, client, ps->atom_frame_extents, 4L, XA_CARDINAL, 32); - if (4 == prop.nitems) { + if (prop.nitems == 4) { const long * const extents = prop.data.p32; + const bool changed = w->frame_extents.left != extents[0] || + w->frame_extents.right != extents[1] || + w->frame_extents.top != extents[2] || + w->frame_extents.bottom != extents[3]; 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); + // If frame_opacity != 1, then frame of this window + // is not included in reg_ignore of underneath windows + if (ps->o.frame_opacity == 1 && changed) + w->reg_ignore_valid = false; } #ifdef DEBUG_FRAME @@ -1283,3 +1242,13 @@ win_get_frame_extents(session_t *ps, win *w, Window client) { free_winprop(&prop); } + +bool win_is_region_ignore_valid(session_t *ps, win *w) { + for(win *i = ps->list; w; w = w->next) { + if (i == w) + break; + if (!i->reg_ignore_valid) + return false; + } + return true; +} diff --git a/src/win.h b/src/win.h index 55969d6..952c5f8 100644 --- a/src/win.h +++ b/src/win.h @@ -1,7 +1,8 @@ #pragma once #include #include -#include + +#include "x.h" typedef struct session session_t; typedef struct win win; @@ -15,8 +16,6 @@ void win_determine_mode(session_t *ps, win *w); */ 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); @@ -53,15 +52,15 @@ 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); +// XXX was win_border_size +void win_update_bounding_shape(session_t *ps, win *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); +void win_extents(win *w, region_t *res); /** * Add a window to damaged area. * @@ -71,19 +70,19 @@ XserverRegion win_extents(session_t *ps, win *w); void add_damage_from_win(session_t *ps, win *w); /** * Get a rectangular region a window occupies, excluding shadow. + * + * global = use global coordinates */ -XserverRegion -win_get_region(session_t *ps, win *w, bool use_offset); +void win_get_region(session_t *ps, win *w, bool global, region_t *); /** * Get a rectangular region a window occupies, excluding frame and shadow. */ -XserverRegion -win_get_region_noframe(session_t *ps, win *w, bool use_offset); +void win_get_region_noframe(session_t *ps, win *w, bool global, region_t *); /** * Retrieve frame extents from a window. */ void -win_get_frame_extents(session_t *ps, win *w, Window client); +win_update_frame_extents(session_t *ps, win *w, Window client); bool add_win(session_t *ps, Window id, Window prev); /** @@ -98,3 +97,6 @@ win_get_leader(session_t *ps, win *w) { /// check if window has ARGB visual bool win_has_alpha(win *w); + +/// check if reg_ignore_valid is true for all windows above us +bool win_is_region_ignore_valid(session_t *ps, win *w);