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);