diff --git a/man/picom.1.asciidoc b/man/picom.1.asciidoc index 36215f10..629184d1 100644 --- a/man/picom.1.asciidoc +++ b/man/picom.1.asciidoc @@ -151,6 +151,9 @@ OPTIONS *--shadow-exclude* 'CONDITION':: Specify a list of conditions of windows that should have no shadow. +*--clip-shadow-above* 'CONDITION':: + Specify a list of conditions of windows that should have no shadow painted over, such as a dock window. + *--fade-exclude* 'CONDITION':: Specify a list of conditions of windows that should not be faded. @@ -362,7 +365,7 @@ Window-type-specific settings are exposed only in configuration file and has the ------------ wintypes: { - WINDOW_TYPE = { fade = BOOL; shadow = BOOL; opacity = FLOAT; focus = BOOL; blur-background = BOOL; full-shadow = BOOL; redir-ignore = BOOL; }; + WINDOW_TYPE = { fade = BOOL; shadow = BOOL; opacity = FLOAT; focus = BOOL; blur-background = BOOL; full-shadow = BOOL; clip-shadow-above = BOOL; redir-ignore = BOOL; }; }; ------------ @@ -385,6 +388,9 @@ Following per window-type options are available: :: full-shadow::: Controls whether shadow is drawn under the parts of the window that you normally won't be able to see. Useful when the window has parts of it transparent, and you want shadows in those areas. + clip-shadow-above::: + Controls wether shadows that would have been drawn above the window should be clipped. Useful for dock windows that should have no shadow painted on top. + redir-ignore::: Controls whether this type of windows should cause screen to become redirected again after been unredirected. If you have *--unredir-if-possible* set, and doesn't want certain window to cause unnecessary screen redirection, you can set this to `true`. diff --git a/picom.sample.conf b/picom.sample.conf index 4d0e1318..797c867a 100644 --- a/picom.sample.conf +++ b/picom.sample.conf @@ -51,6 +51,9 @@ shadow-exclude = [ "_GTK_FRAME_EXTENTS@:c" ]; +# Specify a list of conditions of windows that should have no shadow painted over, such as a dock window. +# clip-shadow-above = [] + # Specify a X geometry that describes the region in which shadow should not # be painted in, such as a dock window region. Use # shadow-exclude-reg = "x10+0+0" @@ -396,6 +399,10 @@ log-level = "warn"; # normally won't be able to see. Useful when the window has parts of it # transparent, and you want shadows in those areas. # +# clip-shadow-above::: +# Controls wether shadows that would have been drawn above the window should +# be clipped. Useful for dock windows that should have no shadow painted on top. +# # redir-ignore::: # Controls whether this type of windows should cause screen to become # redirected again after been unredirected. If you have unredir-if-possible @@ -405,7 +412,7 @@ log-level = "warn"; wintypes: { tooltip = { fade = true; shadow = true; opacity = 0.75; focus = true; full-shadow = false; }; - dock = { shadow = false; } + dock = { shadow = false; clip-shadow-above = true; } dnd = { shadow = false; } popup_menu = { opacity = 0.8; } dropdown_menu = { opacity = 0.8; } diff --git a/src/backend/backend.c b/src/backend/backend.c index faf24431..42ce7b2e 100644 --- a/src/backend/backend.c +++ b/src/backend/backend.c @@ -144,6 +144,10 @@ void paint_all_new(session_t *ps, struct managed_win *t, bool ignore_damage) { pixman_region32_subtract(®_visible, ®_visible, t->reg_ignore); } + // Region on screen we don't want any shadows on + region_t reg_shadow_clip; + pixman_region32_init(®_shadow_clip); + if (ps->backend_data->ops->prepare) { ps->backend_data->ops->prepare(ps->backend_data, ®_paint); } @@ -279,6 +283,10 @@ void paint_all_new(session_t *ps, struct managed_win *t, bool ignore_damage) { pixman_region32_subtract(®_shadow, ®_shadow, &ps->shadow_exclude_reg); } + if (pixman_region32_not_empty(®_shadow_clip)) { + pixman_region32_subtract(®_shadow, ®_shadow, + ®_shadow_clip); + } if (ps->o.xinerama_shadow_crop && w->xinerama_scr >= 0 && w->xinerama_scr < ps->xinerama_nscrs) { @@ -339,6 +347,14 @@ void paint_all_new(session_t *ps, struct managed_win *t, bool ignore_damage) { goto skip; } + if (w->clip_shadow_above) { + // Add window bounds to shadow-clip region + pixman_region32_union(®_shadow_clip, ®_shadow_clip, ®_bound); + } else { + // Remove overlapping window bounds from shadow-clip region + pixman_region32_subtract(®_shadow_clip, ®_shadow_clip, ®_bound); + } + // Draw window on target if (w->frame_opacity == 1) { ps->backend_data->ops->compose(ps->backend_data, w->win_image, @@ -393,6 +409,7 @@ void paint_all_new(session_t *ps, struct managed_win *t, bool ignore_damage) { pixman_region32_fini(®_paint_in_bound); } pixman_region32_fini(®_paint); + pixman_region32_fini(®_shadow_clip); if (ps->o.monitor_repaint) { const struct color DEBUG_COLOR = {0.5, 0, 0, 0.5}; diff --git a/src/config.c b/src/config.c index 1f495fe9..b1abbaf9 100644 --- a/src/config.c +++ b/src/config.c @@ -500,6 +500,10 @@ void set_default_winopts(options_t *opt, win_option_mask_t *mask, bool shadow_en // opacity logic is complicated, and needs an "unset" state opt->wintype_option[i].opacity = NAN; } + if (!mask[i].clip_shadow_above) { + mask[i].clip_shadow_above = true; + opt->wintype_option[i].clip_shadow_above = false; + } } } @@ -537,6 +541,7 @@ char *parse_config(options_t *opt, const char *config_file, bool *shadow_enable, .shadow_blacklist = NULL, .shadow_ignore_shaped = false, .xinerama_shadow_crop = false, + .shadow_clip_list = NULL, .corner_radius = 0, diff --git a/src/config.h b/src/config.h index bd3e08bf..052e38c2 100644 --- a/src/config.h +++ b/src/config.h @@ -45,6 +45,7 @@ typedef struct win_option_mask { bool full_shadow : 1; bool redir_ignore : 1; bool opacity : 1; + bool clip_shadow_above : 1; } win_option_mask_t; typedef struct win_option { @@ -55,6 +56,7 @@ typedef struct win_option { bool full_shadow; bool redir_ignore; double opacity; + bool clip_shadow_above; } win_option_t; enum blur_method { @@ -154,6 +156,8 @@ typedef struct options { bool shadow_ignore_shaped; /// Whether to crop shadow to the very Xinerama screen. bool xinerama_shadow_crop; + /// Don't draw shadow over these windows. A linked list of conditions. + c2_lptr_t *shadow_clip_list; // === Fading === /// How much to fade in in a single fading step. diff --git a/src/config_libconfig.c b/src/config_libconfig.c index d4fa283a..d9b530ce 100644 --- a/src/config_libconfig.c +++ b/src/config_libconfig.c @@ -282,6 +282,10 @@ static inline void parse_wintype_config(const config_t *cfg, const char *member_ o->redir_ignore = ival; mask->redir_ignore = true; } + if (config_setting_lookup_bool(setting, "clip-shadow-above", &ival)) { + o->clip_shadow_above = ival; + mask->clip_shadow_above = true; + } double fval; if (config_setting_lookup_float(setting, "opacity", &fval)) { @@ -513,6 +517,8 @@ char *parse_config_libconfig(options_t *opt, const char *config_file, bool *shad lcfg_lookup_bool(&cfg, "transparent-clipping", &opt->transparent_clipping); // --shadow-exclude parse_cfg_condlst(&cfg, &opt->shadow_blacklist, "shadow-exclude"); + // --clip-shadow-above + parse_cfg_condlst(&cfg, &opt->shadow_clip_list, "clip-shadow-above"); // --fade-exclude parse_cfg_condlst(&cfg, &opt->fade_blacklist, "fade-exclude"); // --focus-exclude diff --git a/src/options.c b/src/options.c index 9ba2adc1..3f228eaa 100644 --- a/src/options.c +++ b/src/options.c @@ -283,6 +283,10 @@ static void usage(const char *argv0, int ret) { " 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" "\n" + "--clip-shadow-above condition\n" + " Specify a list of conditions of windows to not paint a shadow over,\n" + " such as a dock window.\n" + "\n" "--xinerama-shadow-crop\n" " Crop shadow of a window fully on a particular Xinerama screen to the\n" " screen.\n" @@ -449,6 +453,7 @@ static const struct option longopts[] = { {"shadow-color", required_argument, NULL, 332}, {"corner-radius", required_argument, NULL, 333}, {"rounded-corners-exclude", required_argument, NULL, 334}, + {"clip-shadow-above", required_argument, NULL, 335}, {"experimental-backends", no_argument, NULL, 733}, {"monitor-repaint", no_argument, NULL, 800}, {"diagnostics", no_argument, NULL, 801}, @@ -793,7 +798,7 @@ bool get_cfg(options_t *opt, int argc, char *const *argv, bool shadow_enable, free(opt->shadow_exclude_reg_str); opt->shadow_exclude_reg_str = strdup(optarg); log_warn("--shadow-exclude-reg is deprecated. You are likely " - "better off using --shadow-exclude anyway"); + "better off using --clip-shadow-above anyway"); break; case 306: // --paint-exclude @@ -876,6 +881,10 @@ bool get_cfg(options_t *opt, int argc, char *const *argv, bool shadow_enable, // --rounded-corners-exclude condlst_add(&opt->rounded_corners_blacklist, optarg); break; + case 335: + // --clip-shadow-above + condlst_add(&opt->shadow_clip_list, optarg); + break; P_CASEBOOL(733, experimental_backends); P_CASEBOOL(800, monitor_repaint); case 801: opt->print_diagnostics = true; break; diff --git a/src/picom.c b/src/picom.c index 5191d026..7a494384 100644 --- a/src/picom.c +++ b/src/picom.c @@ -1919,6 +1919,7 @@ static session_t *session_init(int argc, char **argv, Display *dpy, if (!(c2_list_postprocess(ps, ps->o.unredir_if_possible_blacklist) && c2_list_postprocess(ps, ps->o.paint_blacklist) && c2_list_postprocess(ps, ps->o.shadow_blacklist) && + c2_list_postprocess(ps, ps->o.shadow_clip_list) && c2_list_postprocess(ps, ps->o.fade_blacklist) && c2_list_postprocess(ps, ps->o.blur_background_blacklist) && c2_list_postprocess(ps, ps->o.invert_color_list) && @@ -2294,6 +2295,7 @@ static void session_destroy(session_t *ps) { // Free blacklists free_wincondlst(&ps->o.shadow_blacklist); + free_wincondlst(&ps->o.shadow_clip_list); free_wincondlst(&ps->o.fade_blacklist); free_wincondlst(&ps->o.focus_blacklist); free_wincondlst(&ps->o.invert_color_list); diff --git a/src/render.c b/src/render.c index 2dd55262..ac9b40ec 100644 --- a/src/render.c +++ b/src/render.c @@ -1046,6 +1046,10 @@ void paint_all(session_t *ps, struct managed_win *t, bool ignore_damage) { reg_paint = ®ion; } + // Region on screen we don't want any shadows on + region_t reg_shadow_clip; + pixman_region32_init(®_shadow_clip); + set_tgt_clip(ps, reg_paint); paint_root(ps, reg_paint); @@ -1074,6 +1078,9 @@ void paint_all(session_t *ps, struct managed_win *t, bool ignore_damage) { if (pixman_region32_not_empty(&ps->shadow_exclude_reg)) pixman_region32_subtract(®_tmp, ®_tmp, &ps->shadow_exclude_reg); + if (pixman_region32_not_empty(®_shadow_clip)) { + pixman_region32_subtract(®_tmp, ®_tmp, ®_shadow_clip); + } // Might be worth while to crop the region to shadow // border @@ -1109,6 +1116,20 @@ void paint_all(session_t *ps, struct managed_win *t, bool ignore_damage) { } } + // Only clip shadows above visible windows + if (w->opacity * MAX_ALPHA >= 1) { + if (w->clip_shadow_above) { + // Add window bounds to shadow-clip region + pixman_region32_union(®_shadow_clip, ®_shadow_clip, + &bshape_corners); + } else { + // Remove overlapping window bounds from shadow-clip + // region + pixman_region32_subtract( + ®_shadow_clip, ®_shadow_clip, &bshape_corners); + } + } + // Calculate the paint region based on the reg_ignore of the current // window and its bounding region. // Remember, reg_ignore is the union of all windows above the current @@ -1160,6 +1181,7 @@ void paint_all(session_t *ps, struct managed_win *t, bool ignore_damage) { // Free up all temporary regions pixman_region32_fini(®_tmp); + pixman_region32_fini(®_shadow_clip); // Move the head of the damage ring ps->damage = ps->damage - 1; diff --git a/src/win.c b/src/win.c index f2ab1f51..af16273b 100644 --- a/src/win.c +++ b/src/win.c @@ -967,6 +967,12 @@ void win_update_prop_shadow(session_t *ps, struct managed_win *w) { } } +static void win_determine_clip_shadow_above(session_t *ps, struct managed_win *w) { + bool should_crop = (ps->o.wintype_option[w->window_type].clip_shadow_above || + c2_match(ps, w, ps->o.shadow_clip_list, NULL)); + w->clip_shadow_above = should_crop; +} + static void win_set_invert_color(session_t *ps, struct managed_win *w, bool invert_color_new) { if (w->invert_color == invert_color_new) { return; @@ -1124,6 +1130,7 @@ void win_on_factor_change(session_t *ps, struct managed_win *w) { win_update_focused(ps, w); win_determine_shadow(ps, w); + win_determine_clip_shadow_above(ps, w); win_determine_invert_color(ps, w); win_determine_blur_background(ps, w); win_determine_rounded_corners(ps, w); @@ -1455,6 +1462,7 @@ struct win *fill_win(session_t *ps, struct win *w) { .shadow_image = NULL, .prev_trans = NULL, .shadow = false, + .clip_shadow_above = false, .xinerama_scr = -1, .mode = WMODE_TRANS, .ever_damaged = false, diff --git a/src/win.h b/src/win.h index add44ec5..aae8d5f8 100644 --- a/src/win.h +++ b/src/win.h @@ -254,6 +254,8 @@ struct managed_win { /// The value of _COMPTON_SHADOW attribute of the window. Below 0 for /// none. long prop_shadow; + /// Do not paint shadow over this window. + bool clip_shadow_above; // Dim-related members /// Whether the window is to be dimmed.