From 044a5c991cd59c49288de83b3f3baa8f5962e84a Mon Sep 17 00:00:00 2001 From: Richard Grenville Date: Sun, 9 Jun 2013 17:06:35 +0800 Subject: [PATCH] Feature #116: Shadow exclusion region - Add --shadow-exclude-reg, which excludes certain regions on the screen to have shadows painted in. (#116) - Adjust session initialization order. Now X root and screen info and basic X extensions are available in configuration parsing step. --- src/common.h | 12 +++++ src/compton.c | 134 ++++++++++++++++++++++++++++++++++++++++++++------ src/compton.h | 35 +++++++++++++ 3 files changed, 167 insertions(+), 14 deletions(-) diff --git a/src/common.h b/src/common.h index 0b97d50..76a7b34 100644 --- a/src/common.h +++ b/src/common.h @@ -234,6 +234,14 @@ typedef enum { UNSET } switch_t; +/// Structure representing a X geometry. +typedef struct { + int wid; + int hei; + int x; + int y; +} geometry_t; + /// Enumeration type of window painting mode. typedef enum { WMODE_TRANS, @@ -484,6 +492,8 @@ typedef struct { int shadow_offset_x, shadow_offset_y; double shadow_opacity; bool clear_shadow; + /// Geometry of a region in which shadow is not painted on. + geometry_t shadow_exclude_reg_geom; /// Shadow blacklist. A linked list of conditions. c2_lptr_t *shadow_blacklist; /// Whether bounding-shaped window should be ignored. @@ -695,6 +705,8 @@ typedef struct { unsigned char *shadow_corner; /// 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; // === Software-optimization-related === /// Currently used refresh rate. diff --git a/src/compton.c b/src/compton.c index c55d045..74bebfb 100644 --- a/src/compton.c +++ b/src/compton.c @@ -1670,6 +1670,16 @@ rebuild_screen_reg(session_t *ps) { ps->screen_reg = get_screen_region(ps); } +/** + * Rebuild shadow_exclude_reg. + */ +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); +} + static void paint_all(session_t *ps, XserverRegion region, XserverRegion region_real, win *t) { if (!region_real) @@ -1781,6 +1791,10 @@ paint_all(session_t *ps, XserverRegion region, XserverRegion region_real, win *t XFixesIntersectRegion(ps->dpy, reg_paint, region, w->extents); } + if (ps->shadow_exclude_reg) + XFixesSubtractRegion(ps->dpy, reg_paint, reg_paint, + ps->shadow_exclude_reg); + // Might be worthwhile to crop the region to shadow border { XRectangle rec_shadow_border = { @@ -2888,10 +2902,12 @@ configure_win(session_t *ps, XConfigureEvent *ce) { XRenderFreePicture(ps->dpy, ps->tgt_buffer); ps->tgt_buffer = None; } + ps->root_width = ce->width; ps->root_height = ce->height; rebuild_screen_reg(ps); + rebuild_shadow_exclude_reg(ps); #ifdef CONFIG_VSYNC_OPENGL if (BKEND_GLX == ps->o.backend) @@ -4385,6 +4401,11 @@ usage(int ret) { " this. Note we do not distinguish 100% and unset, and we don't make\n" " any guarantee about possible conflicts with other programs that set\n" " _NET_WM_WINDOW_OPACITY on frame or client windows.\n" + "--shadow-exclude-reg geometry\n" + " Specify a X geometry that describes the region in which shadow\n" + " should not be painted in, such as a dock window region.\n" + " Use --shadow-exclude-reg \'x10+0-0\', for example, if the 10 pixels\n" + " on the bottom of the screen should not have shadows painted on.\n" "--backend backend\n" " Choose backend. Possible choices are xrender and glx" WARNING ".\n" "--glx-no-stencil\n" @@ -4719,6 +4740,83 @@ parse_conv_kern_lst(session_t *ps, const char *src, XFixed **dest, int max) { return true; } +/** + * Parse a X geometry. + */ +static inline bool +parse_geometry(session_t *ps, const char *src, geometry_t *dest) { + 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(); + + // Parse width + // Must be base 10, because "0x0..." may appear + if (!('+' == *src || '-' == *src)) { + val = strtol(src, &endptr, 10); + if (endptr && src != endptr) { + geom.wid = val; + assert(geom.wid >= 0); + src = endptr; + } + T_STRIPSPACE(); + } + + // Parse height + if ('x' == *src) { + ++src; + val = strtol(src, &endptr, 10); + if (endptr && src != endptr) { + geom.hei = val; + if (geom.hei < 0) { + printf_errf("(\"%s\"): Invalid height.", src); + return false; + } + src = endptr; + } + T_STRIPSPACE(); + } + + // Parse x + if ('+' == *src || '-' == *src) { + val = strtol(src, &endptr, 10); + if (endptr && src != endptr) { + geom.x = val; + if ('-' == *src && geom.x <= 0) + geom.x -= 2; + src = endptr; + } + T_STRIPSPACE(); + } + + // Parse y + if ('+' == *src || '-' == *src) { + val = strtol(src, &endptr, 10); + if (endptr && src != endptr) { + geom.y = val; + if ('-' == *src && geom.y <= 0) + geom.y -= 2; + src = endptr; + } + T_STRIPSPACE(); + } + + if (*src) { + printf_errf("(\"%s\"): Trailing characters.", src); + return false; + } + +parse_geometry_end: + *dest = geom; + return true; +} + /** * Parse a list of opacity rules. */ @@ -5118,6 +5216,7 @@ get_cfg(session_t *ps, int argc, char *const *argv, bool first_pass) { { "resize-damage", required_argument, NULL, 302 }, { "glx-use-gpushader4", no_argument, NULL, 303 }, { "opacity-rule", required_argument, NULL, 304 }, + { "shadow-exclude-reg", required_argument, NULL, 305 }, // Must terminate with a NULL entry { NULL, 0, NULL, 0 }, }; @@ -5356,6 +5455,11 @@ get_cfg(session_t *ps, int argc, char *const *argv, bool first_pass) { if (!parse_rule_opacity(ps, optarg)) exit(1); break; + case 305: + // --shadow-exclude-reg + if (!parse_geometry(ps, optarg, &ps->o.shadow_exclude_reg_geom)) + exit(1); + break; default: usage(1); break; @@ -5440,6 +5544,8 @@ get_cfg(session_t *ps, int argc, char *const *argv, bool first_pass) { } memcpy(ps->o.blur_kerns[0], &convolution_blur, sizeof(convolution_blur)); } + + rebuild_shadow_exclude_reg(ps); } /** @@ -6434,9 +6540,6 @@ session_init(session_t *ps_old, int argc, char **argv) { } } - // Second pass - get_cfg(ps, argc, argv, false); - XSetErrorHandler(error); if (ps->o.synchronize) { XSynchronize(ps->dpy, 1); @@ -6448,6 +6551,17 @@ session_init(session_t *ps_old, int argc, char **argv) { ps->vis = DefaultVisual(ps->dpy, ps->scr); ps->depth = DefaultDepth(ps->dpy, ps->scr); + // Start listening to events on root earlier to catch all possible + // root geometry changes + XSelectInput(ps->dpy, ps->root, + SubstructureNotifyMask + | ExposureMask + | StructureNotifyMask + | PropertyChangeMask); + + ps->root_width = DisplayWidth(ps->dpy, ps->scr); + ps->root_height = DisplayHeight(ps->dpy, ps->scr); + if (!XRenderQueryExtension(ps->dpy, &ps->render_event, &ps->render_error)) { fprintf(stderr, "No render extension\n"); @@ -6485,6 +6599,9 @@ session_init(session_t *ps_old, int argc, char **argv) { ps->shape_exists = true; } + // Second pass + get_cfg(ps, argc, argv, false); + // Query X RandR if (ps->o.sw_opti && !ps->o.refresh_rate) { if (XRRQueryExtension(ps->dpy, &ps->randr_event, &ps->randr_error)) @@ -6510,17 +6627,6 @@ session_init(session_t *ps_old, int argc, char **argv) { ps->o.dbe = false; } - // Start listening to events on root earlier to catch all possible - // root geometry changes - XSelectInput(ps->dpy, ps->root, - SubstructureNotifyMask - | ExposureMask - | StructureNotifyMask - | PropertyChangeMask); - - ps->root_width = DisplayWidth(ps->dpy, ps->scr); - ps->root_height = DisplayHeight(ps->dpy, ps->scr); - rebuild_screen_reg(ps); // Overlay must be initialized before double buffer, and before creation diff --git a/src/compton.h b/src/compton.h index 334e737..06d48e1 100644 --- a/src/compton.h +++ b/src/compton.h @@ -111,6 +111,41 @@ 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. */