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.
This commit is contained in:
parent
75ebd56f74
commit
044a5c991c
12
src/common.h
12
src/common.h
|
@ -234,6 +234,14 @@ typedef enum {
|
||||||
UNSET
|
UNSET
|
||||||
} switch_t;
|
} 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.
|
/// Enumeration type of window painting mode.
|
||||||
typedef enum {
|
typedef enum {
|
||||||
WMODE_TRANS,
|
WMODE_TRANS,
|
||||||
|
@ -484,6 +492,8 @@ typedef struct {
|
||||||
int shadow_offset_x, shadow_offset_y;
|
int shadow_offset_x, shadow_offset_y;
|
||||||
double shadow_opacity;
|
double shadow_opacity;
|
||||||
bool clear_shadow;
|
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.
|
/// Shadow blacklist. A linked list of conditions.
|
||||||
c2_lptr_t *shadow_blacklist;
|
c2_lptr_t *shadow_blacklist;
|
||||||
/// Whether bounding-shaped window should be ignored.
|
/// Whether bounding-shaped window should be ignored.
|
||||||
|
@ -695,6 +705,8 @@ typedef struct {
|
||||||
unsigned char *shadow_corner;
|
unsigned char *shadow_corner;
|
||||||
/// Pre-computed color table for a side of shadow.
|
/// Pre-computed color table for a side of shadow.
|
||||||
unsigned char *shadow_top;
|
unsigned char *shadow_top;
|
||||||
|
/// A region in which shadow is not painted on.
|
||||||
|
XserverRegion shadow_exclude_reg;
|
||||||
|
|
||||||
// === Software-optimization-related ===
|
// === Software-optimization-related ===
|
||||||
/// Currently used refresh rate.
|
/// Currently used refresh rate.
|
||||||
|
|
134
src/compton.c
134
src/compton.c
|
@ -1670,6 +1670,16 @@ rebuild_screen_reg(session_t *ps) {
|
||||||
ps->screen_reg = get_screen_region(ps);
|
ps->screen_reg = get_screen_region(ps);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Rebuild <code>shadow_exclude_reg</code>.
|
||||||
|
*/
|
||||||
|
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
|
static void
|
||||||
paint_all(session_t *ps, XserverRegion region, XserverRegion region_real, win *t) {
|
paint_all(session_t *ps, XserverRegion region, XserverRegion region_real, win *t) {
|
||||||
if (!region_real)
|
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);
|
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
|
// Might be worthwhile to crop the region to shadow border
|
||||||
{
|
{
|
||||||
XRectangle rec_shadow_border = {
|
XRectangle rec_shadow_border = {
|
||||||
|
@ -2888,10 +2902,12 @@ configure_win(session_t *ps, XConfigureEvent *ce) {
|
||||||
XRenderFreePicture(ps->dpy, ps->tgt_buffer);
|
XRenderFreePicture(ps->dpy, ps->tgt_buffer);
|
||||||
ps->tgt_buffer = None;
|
ps->tgt_buffer = None;
|
||||||
}
|
}
|
||||||
|
|
||||||
ps->root_width = ce->width;
|
ps->root_width = ce->width;
|
||||||
ps->root_height = ce->height;
|
ps->root_height = ce->height;
|
||||||
|
|
||||||
rebuild_screen_reg(ps);
|
rebuild_screen_reg(ps);
|
||||||
|
rebuild_shadow_exclude_reg(ps);
|
||||||
|
|
||||||
#ifdef CONFIG_VSYNC_OPENGL
|
#ifdef CONFIG_VSYNC_OPENGL
|
||||||
if (BKEND_GLX == ps->o.backend)
|
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"
|
" 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"
|
" any guarantee about possible conflicts with other programs that set\n"
|
||||||
" _NET_WM_WINDOW_OPACITY on frame or client windows.\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"
|
"--backend backend\n"
|
||||||
" Choose backend. Possible choices are xrender and glx" WARNING ".\n"
|
" Choose backend. Possible choices are xrender and glx" WARNING ".\n"
|
||||||
"--glx-no-stencil\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;
|
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.
|
* 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 },
|
{ "resize-damage", required_argument, NULL, 302 },
|
||||||
{ "glx-use-gpushader4", no_argument, NULL, 303 },
|
{ "glx-use-gpushader4", no_argument, NULL, 303 },
|
||||||
{ "opacity-rule", required_argument, NULL, 304 },
|
{ "opacity-rule", required_argument, NULL, 304 },
|
||||||
|
{ "shadow-exclude-reg", required_argument, NULL, 305 },
|
||||||
// Must terminate with a NULL entry
|
// Must terminate with a NULL entry
|
||||||
{ NULL, 0, NULL, 0 },
|
{ 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))
|
if (!parse_rule_opacity(ps, optarg))
|
||||||
exit(1);
|
exit(1);
|
||||||
break;
|
break;
|
||||||
|
case 305:
|
||||||
|
// --shadow-exclude-reg
|
||||||
|
if (!parse_geometry(ps, optarg, &ps->o.shadow_exclude_reg_geom))
|
||||||
|
exit(1);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
usage(1);
|
usage(1);
|
||||||
break;
|
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));
|
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);
|
XSetErrorHandler(error);
|
||||||
if (ps->o.synchronize) {
|
if (ps->o.synchronize) {
|
||||||
XSynchronize(ps->dpy, 1);
|
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->vis = DefaultVisual(ps->dpy, ps->scr);
|
||||||
ps->depth = DefaultDepth(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,
|
if (!XRenderQueryExtension(ps->dpy,
|
||||||
&ps->render_event, &ps->render_error)) {
|
&ps->render_event, &ps->render_error)) {
|
||||||
fprintf(stderr, "No render extension\n");
|
fprintf(stderr, "No render extension\n");
|
||||||
|
@ -6485,6 +6599,9 @@ session_init(session_t *ps_old, int argc, char **argv) {
|
||||||
ps->shape_exists = true;
|
ps->shape_exists = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Second pass
|
||||||
|
get_cfg(ps, argc, argv, false);
|
||||||
|
|
||||||
// Query X RandR
|
// Query X RandR
|
||||||
if (ps->o.sw_opti && !ps->o.refresh_rate) {
|
if (ps->o.sw_opti && !ps->o.refresh_rate) {
|
||||||
if (XRRQueryExtension(ps->dpy, &ps->randr_event, &ps->randr_error))
|
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;
|
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);
|
rebuild_screen_reg(ps);
|
||||||
|
|
||||||
// Overlay must be initialized before double buffer, and before creation
|
// Overlay must be initialized before double buffer, and before creation
|
||||||
|
|
|
@ -111,6 +111,41 @@ array_wid_exists(const Window *arr, int count, Window wid) {
|
||||||
return false;
|
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 <code>Picture</code>.
|
* Destroy a <code>Picture</code>.
|
||||||
*/
|
*/
|
||||||
|
|
Loading…
Reference in New Issue