1
0
Fork 0
mirror of https://github.com/yshui/picom.git synced 2025-02-10 15:45:57 -05:00

wm/win: separate per-window options into their own struct

It's easier to just store them together, since the same group of
information is needed for parsed window rules, and for window rendering.

Signed-off-by: Yuxuan Shui <yshuiv7@gmail.com>
This commit is contained in:
Yuxuan Shui 2024-07-03 23:21:40 +01:00
parent 797e60f618
commit cdf25b07dc
No known key found for this signature in database
GPG key ID: D3A4405BE6CC17F4
16 changed files with 407 additions and 372 deletions

View file

@ -28,6 +28,19 @@ typedef enum {
enum tristate { TRI_FALSE = -1, TRI_UNKNOWN = 0, TRI_TRUE = 1 };
/// Return value if it's not TRI_UNKNOWN, otherwise return fallback.
static inline enum tristate tri_or(enum tristate value, enum tristate fallback) {
return value ?: fallback;
}
static inline bool tri_or_bool(enum tristate value, bool fallback) {
return value == TRI_UNKNOWN ? fallback : value == TRI_TRUE;
}
static inline enum tristate tri_from_bool(bool value) {
return value ? TRI_TRUE : TRI_FALSE;
}
/// A structure representing margins around a rectangle.
typedef struct {
int top;

View file

@ -278,6 +278,8 @@ typedef struct session {
struct wm *wm;
struct window_options window_options_default;
// === Shadow/dimming related ===
/// 1x1 black Picture.
xcb_render_picture_t black_picture;

View file

@ -639,6 +639,8 @@ bool load_plugin(const char *name, const char *include_dir) {
return handle != NULL;
}
struct shader_info null_shader = {0};
bool parse_config(options_t *opt, const char *config_file) {
// clang-format off
*opt = (struct options){

View file

@ -10,6 +10,7 @@
#include <ctype.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include <xcb/render.h> // for xcb_render_fixed_t, XXX
@ -136,6 +137,67 @@ struct included_config_file {
struct list_node siblings;
};
struct window_maybe_options {
/// Radius of rounded window corners, -1 means not set.
int corner_radius;
/// Window opacity, NaN means not set.
double opacity;
/// Name of the custom fragment shader for this window. NULL means not set.
const struct shader_info *shader;
/// Whether transparent clipping is excluded by the rules.
enum tristate transparent_clipping;
/// Whether a window has shadow.
enum tristate shadow;
/// Whether to invert window color.
enum tristate invert_color;
/// Whether to blur window background.
enum tristate blur_background;
/// Whether the window is to be dimmed.
enum tristate dim;
/// Whether this window should fade.
enum tristate fade;
/// Do not paint shadow over this window.
enum tristate clip_shadow_above;
/// Whether the window is to be considered focused.
enum tristate focused;
/// Whether the window is painted.
enum tristate paint;
/// Whether this window should be considered for unredirect-if-possible.
enum tristate unredir_ignore;
};
/// Like `window_maybe_options`, but all fields are guaranteed to be set.
struct window_options {
double opacity;
const struct shader_info *shader;
unsigned int corner_radius;
bool transparent_clipping;
bool shadow;
bool invert_color;
bool blur_background;
bool dim;
bool fade;
bool clip_shadow_above;
bool focused;
bool paint;
bool unredir_ignore;
char padding[2];
};
static inline bool
win_options_eq(const struct window_options *a, const struct window_options *b) {
return memcmp(a, b, offsetof(struct window_options, padding)) == 0;
}
static_assert(offsetof(struct window_options, padding) == 30, "window_options has "
"implicit padding");
extern struct shader_info null_shader;
/// Structure representing all options.
typedef struct options {
// === Config ===

View file

@ -20,6 +20,7 @@
#include "compiler.h"
#include "config.h"
#include "log.h"
#include "picom.h"
#include "utils/misc.h"
#include "utils/str.h"
#include "wm/defs.h"
@ -652,6 +653,14 @@ static DBusHandlerResult cdbus_process_repaint(session_t *ps, DBusMessage *msg a
return DBUS_HANDLER_RESULT_HANDLED;
}
static inline cdbus_enum_t tristate_to_switch(enum tristate val) {
switch (val) {
case TRI_FALSE: return OFF;
case TRI_TRUE: return ON;
default: return UNSET;
}
}
/**
* Process a win_get D-Bus request.
*/
@ -703,6 +712,8 @@ cdbus_process_win_get(session_t *ps, DBusMessage *msg, DBusMessage *reply, DBusE
return DBUS_HANDLER_RESULT_HANDLED;
}
auto w_opts = win_options(w);
append(id, wid, win_id(w));
append(client_win, wid, win_client_id(w, /*fallback_to_self=*/true));
append(map_state, boolean, w->a.map_state);
@ -712,16 +723,20 @@ cdbus_process_win_get(session_t *ps, DBusMessage *msg, DBusMessage *reply, DBusE
append(right_width, int32, w->frame_extents.right);
append(top_width, int32, w->frame_extents.top);
append(bottom_width, int32, w->frame_extents.bottom);
append(fade_force, enum, tristate_to_switch(w->options_override.fade));
append(shadow_force, enum, tristate_to_switch(w->options_override.shadow));
append(focused_force, enum, tristate_to_switch(w->options_override.focused));
append(invert_color_force, enum, tristate_to_switch(w->options_override.invert_color));
append(opacity_is_set, boolean, !safe_isnan(w->options.opacity));
append(shadow, boolean, w_opts.shadow);
append(fade, boolean, w_opts.fade);
append(blur_background, boolean, w_opts.blur_background);
append_win_property(mode, enum);
append_win_property(opacity, double);
append_win_property(ever_damaged, boolean);
append_win_property(window_type, enum);
append_win_property(leader, wid);
append_win_property(fade_force, enum);
append_win_property(shadow_force, enum);
append_win_property(focused_force, enum);
append_win_property(invert_color_force, enum);
append_win_property(name, string);
append_win_property(class_instance, string);
append_win_property(class_general, string);
@ -729,12 +744,8 @@ cdbus_process_win_get(session_t *ps, DBusMessage *msg, DBusMessage *reply, DBusE
append_win_property(opacity, double);
append_win_property(has_opacity_prop, boolean);
append_win_property(opacity_prop, uint32);
append_win_property(opacity_is_set, boolean);
append_win_property(opacity_set, double);
append_win_property(frame_opacity, double);
append_win_property(shadow, boolean);
append_win_property(invert_color, boolean);
append_win_property(blur_background, boolean);
#undef append_win_property
#undef append
@ -778,19 +789,32 @@ cdbus_process_win_set(session_t *ps, DBusMessage *msg, DBusMessage *reply, DBusE
return DBUS_HANDLER_RESULT_HANDLED;
}
bool changed = false;
if (!strcmp("shadow_force", target)) {
win_set_shadow_force(ps, w, val);
w->options_override.shadow =
val == UNSET ? TRI_UNKNOWN : (val == ON ? TRI_TRUE : TRI_FALSE);
changed = true;
} else if (!strcmp("fade_force", target)) {
win_set_fade_force(w, val);
w->options_override.fade =
val == UNSET ? TRI_UNKNOWN : (val == ON ? TRI_TRUE : TRI_FALSE);
changed = true;
} else if (!strcmp("focused_force", target)) {
win_set_focused_force(ps, w, val);
w->options_override.focused =
val == UNSET ? TRI_UNKNOWN : (val == ON ? TRI_TRUE : TRI_FALSE);
changed = true;
} else if (!strcmp("invert_color_force", target)) {
win_set_invert_color_force(ps, w, val);
w->options_override.invert_color =
val == UNSET ? TRI_UNKNOWN : (val == ON ? TRI_TRUE : TRI_FALSE);
changed = true;
} else {
log_debug(CDBUS_ERROR_BADTGT_S, target);
dbus_set_error(err, CDBUS_ERROR_BADTGT, CDBUS_ERROR_BADTGT_S, target);
return DBUS_HANDLER_RESULT_HANDLED;
}
if (changed) {
add_damage_from_win(ps, w);
queue_redraw(ps);
}
if (reply != NULL && !cdbus_append_boolean(reply, true)) {
return DBUS_HANDLER_RESULT_NEED_MEMORY;

View file

@ -388,6 +388,7 @@ static void configure_win(session_t *ps, xcb_configure_notify_event_t *ce) {
auto w = wm_ref_deref(cursor);
if (!w) {
log_debug("Window %#010x is unmanaged.", ce->window);
return;
}

View file

@ -82,7 +82,7 @@ setup_window(struct x_connection *c, struct atom *atoms, struct options *options
}
}
if (wid == win_id(w) || wid == win_client_id(w, /*fallback_to_self=*/false)) {
w->focused = true;
w->options.focused = TRI_TRUE;
}
auto attributes_reply = XCB_AWAIT(xcb_get_window_attributes, c->c, win_id(w));

View file

@ -751,15 +751,6 @@ static bool paint_preprocess(session_t *ps, bool *animation, struct win **out_bo
const winmode_t mode_old = w->mode;
const bool was_painted = w->to_paint;
if (win_should_dim(ps, w) != w->dim) {
w->dim = win_should_dim(ps, w);
add_damage_from_win(ps, w);
}
if (w->fg_shader && (w->fg_shader->attributes & SHADER_ATTRIBUTE_ANIMATED)) {
add_damage_from_win(ps, w);
*animation = true;
}
if (w->running_animation != NULL) {
*animation = true;
}
@ -807,6 +798,11 @@ static bool paint_preprocess(session_t *ps, bool *animation, struct win **out_bo
const bool was_painted = w->to_paint;
const double window_opacity = win_animatable_get(w, WIN_SCRIPT_OPACITY);
const double blur_opacity = win_animatable_get(w, WIN_SCRIPT_BLUR_OPACITY);
auto window_options = win_options(w);
if (window_options.shader->attributes & SHADER_ATTRIBUTE_ANIMATED) {
add_damage_from_win(ps, w);
*animation = true;
}
// Destroy reg_ignore if some window above us invalidated it
if (!reg_ignore_valid) {
@ -837,13 +833,14 @@ static bool paint_preprocess(session_t *ps, bool *animation, struct win **out_bo
log_trace("|- is positioned outside of the screen");
to_paint = false;
} else if (unlikely(window_opacity * MAX_ALPHA < 1 &&
(!w->blur_background || blur_opacity * MAX_ALPHA < 1))) {
(!window_options.blur_background ||
blur_opacity * MAX_ALPHA < 1))) {
// For consistency, even a window has 0 opacity, we would still
// blur its background. (unless it's background is not blurred, or
// the blur opacity is 0)
log_trace("|- has 0 opacity");
to_paint = false;
} else if (w->paint_excluded) {
} else if (!window_options.paint) {
log_trace("|- is excluded from painting");
to_paint = false;
} else if (unlikely((w->flags & WIN_FLAGS_PIXMAP_ERROR) != 0)) {
@ -878,7 +875,7 @@ static bool paint_preprocess(session_t *ps, bool *animation, struct win **out_bo
// we add the window region to the ignored region
// Otherwise last_reg_ignore shouldn't change
if ((w->mode != WMODE_TRANS && !ps->o.force_win_blend) ||
w->transparent_clipping) {
window_options.transparent_clipping) {
// w->mode == WMODE_SOLID or WMODE_FRAME_TRANS
region_t *tmp = rc_region_new();
if (w->mode == WMODE_SOLID) {
@ -903,7 +900,7 @@ static bool paint_preprocess(session_t *ps, bool *animation, struct win **out_bo
// is not correctly set.
if (ps->o.unredir_if_possible && is_highest) {
if (w->mode == WMODE_SOLID && !ps->o.force_win_blend &&
w->is_fullscreen && !w->unredir_if_possible_excluded) {
w->is_fullscreen && !window_options.unredir_ignore) {
unredir_possible = true;
}
}
@ -911,7 +908,7 @@ static bool paint_preprocess(session_t *ps, bool *animation, struct win **out_bo
// Unredirect screen if some window is requesting compositor bypass, even
// if that window is not on the top.
if (ps->o.unredir_if_possible && win_is_bypassing_compositor(ps, w) &&
!w->unredir_if_possible_excluded) {
!window_options.unredir_ignore) {
// Here we deviate from EWMH a bit. EWMH says we must not
// unredirect the screen if the window requesting bypassing would
// look different after unredirecting. Instead we always follow
@ -1543,7 +1540,7 @@ static void handle_new_window_attributes_reply(struct x_connection * /*c*/,
auto w = win_maybe_allocate(ps, toplevel,
(xcb_get_window_attributes_reply_t *)reply_or_error);
if (w != NULL && w->a.map_state == XCB_MAP_STATE_VIEWABLE) {
win_map_start(ps, w);
win_set_flags(w, WIN_FLAGS_MAPPED);
ps->pending_updates = true;
}
}
@ -1990,6 +1987,23 @@ static bool load_shader_source_for_condition(const c2_lptr_t *cond, void *data)
return load_shader_source(data, c2_list_get_data(cond));
}
static struct window_options win_options_from_config(const struct options *opts) {
return (struct window_options){
.blur_background = opts->blur_method != BLUR_METHOD_NONE,
.shadow = opts->shadow_enable,
.corner_radius = (unsigned)opts->corner_radius,
.transparent_clipping = opts->transparent_clipping,
.dim = opts->inactive_dim > 0,
.fade = opts->fading_enable,
.shader = &null_shader,
.focused = false,
.invert_color = false,
.paint = true,
.clip_shadow_above = false,
.unredir_ignore = false,
};
}
/**
* Initialize a session.
*
@ -2173,6 +2187,8 @@ static session_t *session_init(int argc, char **argv, Display *dpy,
return NULL;
}
ps->window_options_default = win_options_from_config(&ps->o);
if (ps->o.window_shader_fg) {
log_debug("Default window shader: \"%s\"", ps->o.window_shader_fg);
}

View file

@ -240,7 +240,7 @@ uint32_t make_rounded_window_shape(xcb_render_trapezoid_t traps[], uint32_t max_
}
void render(session_t *ps, int x, int y, int dx, int dy, int wid, int hei, int fullwid,
int fullhei, double opacity, bool argb, bool neg, int cr,
int fullhei, double opacity, bool argb, bool neg, unsigned int cr,
xcb_render_picture_t pict, glx_texture_t *ptex, const region_t *reg_paint,
const glx_prog_main_t *pprogram, clip_t *clip) {
switch (ps->o.legacy_backend) {
@ -266,7 +266,7 @@ void render(session_t *ps, int x, int y, int dx, int dy, int wid, int hei, int f
xcb_render_trapezoid_t traps[4 * max_ntraps + 3];
uint32_t n = make_rounded_window_shape(
traps, max_ntraps, cr, fullwid, fullhei);
traps, max_ntraps, (int)cr, fullwid, fullhei);
xcb_render_trapezoids(
ps->c.c, XCB_RENDER_PICT_OP_OVER, alpha_pict, p_tmp,
@ -351,12 +351,16 @@ paint_region(session_t *ps, const struct win *w, int x, int y, int wid, int hei,
const int dy = (w ? w->g.y : 0) + y;
const int fullwid = w ? w->widthb : 0;
const int fullhei = w ? w->heightb : 0;
struct window_options w_opts = {};
if (w) {
w_opts = win_options(w);
}
const bool argb = (w && (win_has_alpha(w) || ps->o.force_win_blend));
const bool neg = (w && w->invert_color);
const bool neg = (w && w_opts.invert_color);
render(ps, x, y, dx, dy, wid, hei, fullwid, fullhei, opacity, argb, neg,
w ? w->corner_radius : 0, pict,
(w ? w->paint.ptex : ps->root_tile_paint.ptex), reg_paint,
w_opts.corner_radius, pict, (w ? w->paint.ptex : ps->root_tile_paint.ptex),
reg_paint,
#ifdef CONFIG_OPENGL
w ? &ps->glx_prog_win : NULL
#else
@ -392,7 +396,8 @@ static inline bool paint_isvalid(session_t *ps, const paint_t *ppaint) {
/**
* Paint a window itself and dim it if asked.
*/
void paint_one(session_t *ps, struct win *w, const region_t *reg_paint) {
void paint_one(session_t *ps, struct win *w, const struct window_options *w_opts,
const region_t *reg_paint) {
// Fetch Pixmap
if (!w->paint.pixmap) {
w->paint.pixmap = x_new_id(&ps->c);
@ -442,7 +447,7 @@ void paint_one(session_t *ps, struct win *w, const region_t *reg_paint) {
xcb_render_picture_t pict = w->paint.pict;
// Invert window color, if required
if (bkend_use_xrender(ps) && w->invert_color) {
if (bkend_use_xrender(ps) && w_opts->invert_color) {
xcb_render_picture_t newpict = x_create_picture_with_pictfmt(
&ps->c, wid, hei, w->pictfmt->id, w->pictfmt->depth, 0, NULL);
if (newpict) {
@ -556,7 +561,7 @@ void paint_one(session_t *ps, struct win *w, const region_t *reg_paint) {
}
// Dimming the window if needed
if (w->dim) {
if (w_opts->dim) {
double dim_opacity = ps->o.inactive_dim;
if (!ps->o.inactive_dim_fixed) {
dim_opacity *= window_opacity;
@ -761,7 +766,8 @@ shadow_picture_err:
/**
* Paint the shadow of a window.
*/
static inline void win_paint_shadow(session_t *ps, struct win *w, region_t *reg_paint) {
static inline void win_paint_shadow(session_t *ps, struct win *w,
const struct window_options *w_opts, region_t *reg_paint) {
// Bind shadow pixmap to GLX texture if needed
paint_bind_tex(ps, &w->shadow_paint, 0, 0, false, 32, 0, false);
@ -771,15 +777,16 @@ static inline void win_paint_shadow(session_t *ps, struct win *w, region_t *reg_
}
xcb_render_picture_t td = XCB_NONE;
bool should_clip =
(w->corner_radius > 0) && (!ps->o.wintype_option[w->window_type].full_shadow);
bool should_clip = (w_opts->corner_radius > 0) &&
(!ps->o.wintype_option[w->window_type].full_shadow);
if (should_clip) {
if (ps->o.legacy_backend == BKEND_XRENDER ||
ps->o.legacy_backend == BKEND_XR_GLX_HYBRID) {
uint32_t max_ntraps = to_u32_checked(w->corner_radius);
uint32_t max_ntraps = to_u32_checked(w_opts->corner_radius);
xcb_render_trapezoid_t traps[4 * max_ntraps + 3];
uint32_t n = make_rounded_window_shape(
traps, max_ntraps, w->corner_radius, w->widthb, w->heightb);
uint32_t n = make_rounded_window_shape(traps, max_ntraps,
(int)w_opts->corner_radius,
w->widthb, w->heightb);
td = x_create_picture_with_standard(
&ps->c, w->widthb, w->heightb, XCB_PICT_STANDARD_ARGB_32, 0, 0);
@ -896,13 +903,13 @@ xr_blur_dst(session_t *ps, xcb_render_picture_t tgt_buffer, int16_t x, int16_t y
* Blur the background of a window.
*/
static inline void
win_blur_background(session_t *ps, struct win *w, xcb_render_picture_t tgt_buffer,
const region_t *reg_paint) {
win_blur_background(session_t *ps, struct win *w, const struct window_options *w_opts,
xcb_render_picture_t tgt_buffer, const region_t *reg_paint) {
const int16_t x = w->g.x;
const int16_t y = w->g.y;
auto const wid = to_u16_checked(w->widthb);
auto const hei = to_u16_checked(w->heightb);
const int cr = w ? w->corner_radius : 0;
const int cr = w_opts ? (int)w_opts->corner_radius : 0;
const double window_opacity = win_animatable_get(w, WIN_SCRIPT_OPACITY);
double factor_center = 1.0;
@ -1089,8 +1096,9 @@ void paint_all(session_t *ps, struct win *t) {
region_t bshape_no_corners =
win_get_bounding_shape_global_without_corners_by_val(w);
region_t bshape_corners = win_get_bounding_shape_global_by_val(w);
auto const w_opts = win_options(w);
// Painting shadow
if (w->shadow) {
if (w_opts.shadow) {
// Lazy shadow building
if (!w->shadow_paint.pixmap) {
if (!win_build_shadow(ps, w, 1)) {
@ -1140,13 +1148,13 @@ void paint_all(session_t *ps, struct win *t) {
// Detect if the region is empty before painting
if (pixman_region32_not_empty(&reg_tmp)) {
set_tgt_clip(ps, &reg_tmp);
win_paint_shadow(ps, w, &reg_tmp);
win_paint_shadow(ps, w, &w_opts, &reg_tmp);
}
}
// Only clip shadows above visible windows
if (win_animatable_get(w, WIN_SCRIPT_OPACITY) * MAX_ALPHA >= 1) {
if (w->clip_shadow_above) {
if (w_opts.clip_shadow_above) {
// Add window bounds to shadow-clip region
pixman_region32_union(&reg_shadow_clip, &reg_shadow_clip,
&bshape_corners);
@ -1172,7 +1180,7 @@ void paint_all(session_t *ps, struct win *t) {
#ifdef CONFIG_OPENGL
// If rounded corners backup the region first
if (w->corner_radius > 0 && ps->o.legacy_backend == BKEND_GLX) {
if (w_opts.corner_radius > 0 && ps->o.legacy_backend == BKEND_GLX) {
const int16_t x = w->g.x;
const int16_t y = w->g.y;
auto const wid = to_u16_checked(w->widthb);
@ -1182,26 +1190,27 @@ void paint_all(session_t *ps, struct win *t) {
#endif
// Blur window background
if (w->blur_background &&
if (w_opts.blur_background &&
(w->mode == WMODE_TRANS ||
(ps->o.blur_background_frame && w->mode == WMODE_FRAME_TRANS) ||
ps->o.force_win_blend)) {
win_blur_background(ps, w, ps->tgt_buffer.pict, &reg_tmp);
win_blur_background(ps, w, &w_opts, ps->tgt_buffer.pict,
&reg_tmp);
}
// Painting the window
paint_one(ps, w, &reg_tmp);
paint_one(ps, w, &w_opts, &reg_tmp);
#ifdef CONFIG_OPENGL
// Rounded corners for XRender is implemented inside render()
// Round window corners
if (w->corner_radius > 0 && ps->o.legacy_backend == BKEND_GLX) {
if (w_opts.corner_radius > 0 && ps->o.legacy_backend == BKEND_GLX) {
auto const wid = to_u16_checked(w->widthb);
auto const hei = to_u16_checked(w->heightb);
glx_round_corners_dst(ps, w, w->glx_texture_bg, w->g.x,
w->g.y, wid, hei,
(float)ps->psglx->z - 0.5F,
(float)w->corner_radius, &reg_tmp);
glx_round_corners_dst(
ps, w, w->glx_texture_bg, w->g.x, w->g.y, wid, hei,
(float)ps->psglx->z - 0.5F,
(float)w_opts.corner_radius, &reg_tmp);
}
#endif
}

View file

@ -32,10 +32,9 @@ typedef struct clip {
} clip_t;
void render(session_t *ps, int x, int y, int dx, int dy, int w, int h, int fullw,
int fullh, double opacity, bool argb, bool neg, int cr,
int fullh, double opacity, bool argb, bool neg, unsigned int cr,
xcb_render_picture_t pict, glx_texture_t *ptex, const region_t *reg_paint,
const glx_prog_main_t *pprogram, clip_t *clip);
void paint_one(session_t *ps, struct win *w, const region_t *reg_paint);
void paint_all(session_t *ps, struct win *const t);

View file

@ -23,7 +23,7 @@ commands_for_window_body(struct layer *layer, struct backend_command *cmd,
auto mode = win_calc_mode_raw(layer->win);
int border_width = w->g.border_width;
double dim = 0;
if (w->dim) {
if (layer->options.dim) {
dim = inactive_dim;
if (!inactive_dim_fixed) {
dim *= layer->opacity;
@ -48,7 +48,7 @@ commands_for_window_body(struct layer *layer, struct backend_command *cmd,
frame_region);
}
}
if (w->corner_radius > 0) {
if (layer->options.corner_radius > 0) {
win_region_remove_corners(w, layer->window.origin, &cmd->opaque_region);
}
region_scale(&cmd->target_mask, layer->window.origin, layer->scale);
@ -61,15 +61,16 @@ commands_for_window_body(struct layer *layer, struct backend_command *cmd,
cmd->blit = (struct backend_blit_args){
.border_width = border_width,
.target_mask = &cmd->target_mask,
.corner_radius = w->corner_radius,
.corner_radius = layer->options.corner_radius,
.opacity = layer->opacity,
.dim = dim,
.scale = layer->scale,
.effective_size = layer->window.size,
.shader = w->fg_shader ? w->fg_shader->backend_shader : NULL,
.color_inverted = w->invert_color,
.shader = layer->options.shader->backend_shader,
.color_inverted = layer->options.invert_color,
.source_mask = NULL,
.max_brightness = max_brightness};
.max_brightness = max_brightness,
};
if (w->frame_opacity == 1 || w->frame_opacity == 0) {
return 1;
@ -97,7 +98,7 @@ command_for_shadow(struct layer *layer, struct backend_command *cmd,
const struct win_option *wintype_options,
const struct x_monitors *monitors, const struct backend_command *end) {
auto w = layer->win;
if (!w->shadow) {
if (!layer->options.shadow) {
return 0;
}
@ -143,8 +144,8 @@ command_for_shadow(struct layer *layer, struct backend_command *cmd,
}
}
log_region(TRACE, &cmd->target_mask);
if (w->corner_radius > 0) {
cmd->source_mask.corner_radius = w->corner_radius;
if (layer->options.corner_radius > 0) {
cmd->source_mask.corner_radius = layer->options.corner_radius;
cmd->source_mask.inverted = true;
cmd->source_mask.origin =
ivec2_sub(layer->window.origin, layer->shadow.origin);
@ -156,7 +157,7 @@ command_for_shadow(struct layer *layer, struct backend_command *cmd,
cmd->blit = (struct backend_blit_args){
.opacity = layer->shadow_opacity,
.max_brightness = 1,
.source_mask = w->corner_radius > 0 ? &cmd->source_mask : NULL,
.source_mask = layer->options.corner_radius > 0 ? &cmd->source_mask : NULL,
.scale = layer->shadow_scale,
.effective_size = layer->shadow.size,
.target_mask = &cmd->target_mask,
@ -170,7 +171,7 @@ command_for_blur(struct layer *layer, struct backend_command *cmd,
const region_t *frame_region, bool force_blend, bool blur_frame) {
auto w = layer->win;
auto mode = win_calc_mode_raw(w);
if (!w->blur_background || layer->blur_opacity == 0) {
if (!layer->options.blur_background || layer->blur_opacity == 0) {
return 0;
}
if (force_blend || mode == WMODE_TRANS || layer->opacity < 1.0) {
@ -189,15 +190,15 @@ command_for_blur(struct layer *layer, struct backend_command *cmd,
cmd->op = BACKEND_COMMAND_BLUR;
cmd->origin = (ivec2){};
if (w->corner_radius > 0) {
if (layer->options.corner_radius > 0) {
cmd->source_mask.origin = layer->window.origin;
cmd->source_mask.corner_radius = w->corner_radius;
cmd->source_mask.corner_radius = layer->options.corner_radius;
cmd->source_mask.inverted = false;
}
cmd->blur = (struct backend_blur_args){
.opacity = layer->blur_opacity,
.target_mask = &cmd->target_mask,
.source_mask = w->corner_radius > 0 ? &cmd->source_mask : NULL,
.source_mask = layer->options.corner_radius > 0 ? &cmd->source_mask : NULL,
};
return 1;
}
@ -217,7 +218,7 @@ command_builder_apply_transparent_clipping(struct layout *layout, region_t *scra
auto layer_start = end - layer->number_of_commands;
for (auto i = end; i != begin; i--) {
if (i == layer_start) {
if (layer->win->transparent_clipping) {
if (layer->options.transparent_clipping) {
auto win = layer->win;
auto mode = win_calc_mode_raw(layer->win);
region_t tmp;
@ -263,7 +264,7 @@ command_builder_apply_shadow_clipping(struct layout *layout, region_t *scratch_r
if (i == layer_end) {
layer += 1;
layer_end += layer->number_of_commands;
clip_shadow_above = layer->win->clip_shadow_above;
clip_shadow_above = layer->options.clip_shadow_above;
}
if (i->op == BACKEND_COMMAND_BLUR) {
@ -370,13 +371,13 @@ void command_builder_build(struct command_builder *cb, struct layout *layout, bo
unsigned ncmds = 1;
dynarr_foreach(layout->layers, layer) {
auto mode = win_calc_mode_raw(layer->win);
if (layer->win->blur_background && layer->blur_opacity > 0 &&
if (layer->options.blur_background && layer->blur_opacity > 0 &&
(force_blend || mode == WMODE_TRANS || layer->opacity < 1.0 ||
(blur_frame && mode == WMODE_FRAME_TRANS))) {
// Needs blur
ncmds += 1;
}
if (layer->win->shadow) {
if (layer->options.shadow) {
ncmds += 1;
}
if (layer->win->frame_opacity < 1 && layer->win->frame_opacity > 0) {

View file

@ -41,13 +41,15 @@ struct layout_manager {
/// visible / should not be rendered. `out_layer` is modified either way.
static bool layer_from_window(struct layer *out_layer, struct win *w, ivec2 size) {
bool to_paint = false;
if (!w->ever_damaged || w->paint_excluded) {
auto w_opts = win_options(w);
if (!w->ever_damaged || !w_opts.paint) {
goto out;
}
if (w->win_image == NULL) {
goto out;
}
out_layer->options = w_opts;
out_layer->scale = (vec2){
.x = win_animatable_get(w, WIN_SCRIPT_SCALE_X),
.y = win_animatable_get(w, WIN_SCRIPT_SCALE_Y),
@ -64,7 +66,7 @@ static bool layer_from_window(struct layer *out_layer, struct win *w, ivec2 size
.x = win_animatable_get(w, WIN_SCRIPT_CROP_WIDTH),
.y = win_animatable_get(w, WIN_SCRIPT_CROP_HEIGHT),
});
if (w->shadow) {
if (w_opts.shadow) {
out_layer->shadow_scale = (vec2){
.x = win_animatable_get(w, WIN_SCRIPT_SHADOW_SCALE_X),
.y = win_animatable_get(w, WIN_SCRIPT_SHADOW_SCALE_Y),
@ -108,7 +110,6 @@ static bool layer_from_window(struct layer *out_layer, struct win *w, ivec2 size
// their extent rectangle.
out_layer->is_opaque =
!win_has_alpha(w) && out_layer->opacity == 1.0F && !w->bounding_shaped;
out_layer->is_clipping = w->transparent_clipping;
out_layer->next_rank = -1;
out_layer->prev_rank = -1;
out_layer->key = wm_ref_treeid(w->tree_ref);

View file

@ -8,6 +8,7 @@
#include <picom/types.h>
#include "config.h"
#include "region.h"
#include "wm/wm.h"
@ -18,6 +19,7 @@ struct layer {
/// The window, this is only valid for the current layout. Once
/// a frame has passed, windows could have been freed.
struct win *win;
struct window_options options;
/// Damaged region of this layer, in screen coordinates
region_t damaged;
/// Window rectangle in screen coordinates, before it's scaled.
@ -51,8 +53,6 @@ struct layer {
/// Is this window completely opaque?
bool is_opaque;
/// Is this window clipping the windows beneath it?
bool is_clipping;
// TODO(yshui) make opaqueness/blur finer grained maybe? to support
// things like blur-background-frame

View file

@ -210,8 +210,9 @@ err:
return succeeded;
}
image_handle renderer_shadow_from_mask(struct renderer *r, struct backend_base *backend,
image_handle mask, int corner_radius, ivec2 mask_size) {
image_handle
renderer_shadow_from_mask(struct renderer *r, struct backend_base *backend,
image_handle mask, unsigned int corner_radius, ivec2 mask_size) {
image_handle normalized_mask_image = NULL, shadow_image = NULL,
shadow_color_pixel = NULL;
bool succeeded = false;
@ -366,7 +367,7 @@ renderer_bind_shadow(struct renderer *r, struct backend_base *backend, struct wi
return false;
}
w->shadow_image = renderer_shadow_from_mask(
r, backend, w->mask_image, w->corner_radius,
r, backend, w->mask_image, win_options(w).corner_radius,
(ivec2){.width = w->widthb, .height = w->heightb});
}
if (!w->shadow_image) {

View file

@ -114,30 +114,24 @@ static inline struct wm_ref *win_get_leader(session_t *ps, struct win *w) {
* Update focused state of a window.
*/
static void win_update_focused(session_t *ps, struct win *w) {
if (w->focused_force != UNSET) {
w->focused = w->focused_force;
} else {
bool is_wmwin = win_is_wmwin(w);
w->focused = win_is_focused_raw(w);
bool is_wmwin = win_is_wmwin(w);
w->options.focused = win_is_focused_raw(w) ? TRI_TRUE : TRI_FALSE;
// Use wintype_focus, and treat WM windows and override-redirected
// windows specially
if (ps->o.wintype_option[w->window_type].focus ||
(ps->o.mark_wmwin_focused && is_wmwin) ||
(ps->o.mark_ovredir_focused &&
wm_ref_client_of(w->tree_ref) == NULL && !is_wmwin) ||
(w->a.map_state == XCB_MAP_STATE_VIEWABLE &&
c2_match(ps->c2_state, w, ps->o.focus_blacklist, NULL))) {
w->focused = true;
}
// Use wintype_focus, and treat WM windows and override-redirected
// windows specially
if (ps->o.wintype_option[w->window_type].focus ||
(ps->o.mark_wmwin_focused && is_wmwin) ||
(ps->o.mark_ovredir_focused && wm_ref_client_of(w->tree_ref) == NULL && !is_wmwin) ||
(w->a.map_state == XCB_MAP_STATE_VIEWABLE &&
c2_match(ps->c2_state, w, ps->o.focus_blacklist, NULL))) {
w->options.focused = TRI_TRUE;
}
// If window grouping detection is enabled, mark the window active if
// its group is
auto active_leader = wm_active_leader(ps->wm);
if (ps->o.track_leader && active_leader &&
win_get_leader(ps, w) == active_leader) {
w->focused = true;
}
// If window grouping detection is enabled, mark the window active if
// its group is
auto active_leader = wm_active_leader(ps->wm);
if (ps->o.track_leader && active_leader && win_get_leader(ps, w) == active_leader) {
w->options.focused = TRI_TRUE;
}
}
@ -323,7 +317,6 @@ static inline void win_release_pixmap(backend_t *base, struct win *w) {
static inline void win_release_shadow(backend_t *base, struct win *w) {
log_debug("Releasing shadow of window %#010x (%s)", win_id(w), w->name);
if (w->shadow_image) {
assert(w->shadow);
xcb_pixmap_t pixmap = XCB_NONE;
pixmap = base->ops.release_image(base, w->shadow_image);
w->shadow_image = NULL;
@ -527,6 +520,14 @@ void win_process_secondary_flags(session_t *ps, struct win *w) {
return;
}
auto old_options = win_options(w);
region_t extents;
pixman_region32_init(&extents);
// Save old window extents. If window goes from having a shadow to not
// having a shadow, we need to add the old, having-shadow extents to
// damage.
win_extents(w, &extents);
// Factor change flags could be set by previous stages, so must be handled
// last
if (win_check_flags_all(w, WIN_FLAGS_FACTOR_CHANGED)) {
@ -539,6 +540,19 @@ void win_process_secondary_flags(session_t *ps, struct win *w) {
add_damage_from_win(ps, w);
win_clear_flags(w, WIN_FLAGS_DAMAGED);
}
auto new_options = win_options(w);
if (win_options_eq(&old_options, &new_options)) {
pixman_region32_fini(&extents);
return;
}
add_damage_from_win(ps, w); // Only for legacy backends
if (new_options.shadow != old_options.shadow && !new_options.shadow) {
add_damage(ps, &extents);
win_release_shadow(ps->backend_data, w);
}
pixman_region32_fini(&extents);
}
void win_process_image_flags(session_t *ps, struct win *w) {
@ -801,8 +815,8 @@ static double win_calc_opacity_target(session_t *ps, const struct win *w) {
// Try obeying opacity property and window type opacity firstly
if (w->has_opacity_prop) {
opacity = ((double)w->opacity_prop) / OPAQUE;
} else if (w->opacity_is_set) {
opacity = w->opacity_set;
} else if (!safe_isnan(w->options.opacity)) {
opacity = w->options.opacity;
} else if (!safe_isnan(ps->o.wintype_option[w->window_type].opacity)) {
opacity = ps->o.wintype_option[w->window_type].opacity;
} else {
@ -810,14 +824,14 @@ static double win_calc_opacity_target(session_t *ps, const struct win *w) {
// focused
if (win_is_focused_raw(w)) {
opacity = ps->o.active_opacity;
} else if (!w->focused) {
} else if (!win_options(w).focused) {
// Respect inactive_opacity in some cases
opacity = ps->o.inactive_opacity;
}
}
// respect inactive override
if (ps->o.inactive_opacity_override && !w->focused) {
if (ps->o.inactive_opacity_override && !win_options(w).focused) {
opacity = ps->o.inactive_opacity;
}
@ -852,16 +866,17 @@ void unmap_win_finish(session_t *ps, struct win *w) {
/**
* Determine whether a window is to be dimmed.
*/
bool win_should_dim(session_t *ps, const struct win *w) {
static void win_update_dim(session_t *ps, struct win *w) {
// Make sure we do nothing if the window is unmapped / being destroyed
if (w->state == WSTATE_UNMAPPED) {
return false;
return;
}
if (ps->o.inactive_dim > 0 && !(w->focused)) {
return true;
if (ps->o.inactive_dim > 0 && !win_options(w).focused) {
w->options.dim = TRI_TRUE;
} else {
w->options.dim = TRI_FALSE;
}
return false;
}
/**
@ -882,82 +897,30 @@ void win_update_prop_shadow_raw(struct x_connection *c, struct atom *atoms, stru
free_winprop(&prop);
}
static void win_set_shadow(session_t *ps, struct win *w, bool shadow_new) {
if (w->shadow == shadow_new) {
return;
}
log_debug("Updating shadow property of window %#010x (%s) to %d", win_id(w),
w->name, shadow_new);
// We don't handle property updates of non-visible windows until they are
// mapped.
assert(w->state == WSTATE_MAPPED);
// Keep a copy of window extent before the shadow change. Will be used for
// calculation of damaged region
region_t extents;
pixman_region32_init(&extents);
win_extents(w, &extents);
if (ps->redirected) {
// Add damage for shadow change
// Window extents need update on shadow state change
// Shadow geometry currently doesn't change on shadow state change
// calc_shadow_geometry(ps, w);
if (shadow_new) {
// Mark the new extents as damaged if the shadow is added
assert(!w->shadow_image);
pixman_region32_clear(&extents);
win_extents(w, &extents);
add_damage_from_win(ps, w);
} else {
// Mark the old extents as damaged if the shadow is
// removed
add_damage(ps, &extents);
win_release_shadow(ps->backend_data, w);
}
// Only set pending_updates if we are redirected. Otherwise change
// of a shadow won't have influence on whether we should redirect.
ps->pending_updates = true;
}
w->shadow = shadow_new;
pixman_region32_fini(&extents);
}
/**
* Determine if a window should have shadow, and update things depending
* on shadow state.
*/
static void win_determine_shadow(session_t *ps, struct win *w) {
log_debug("Determining shadow of window %#010x (%s)", win_id(w), w->name);
bool shadow_new = w->shadow;
w->options.shadow = TRI_UNKNOWN;
if (w->shadow_force != UNSET) {
shadow_new = w->shadow_force;
} else if (w->a.map_state == XCB_MAP_STATE_VIEWABLE) {
shadow_new = true;
if (!ps->o.wintype_option[w->window_type].shadow) {
log_debug("Shadow disabled by wintypes");
shadow_new = false;
} else if (c2_match(ps->c2_state, w, ps->o.shadow_blacklist, NULL)) {
log_debug("Shadow disabled by shadow-exclude");
shadow_new = false;
} else if (ps->o.shadow_ignore_shaped && w->bounding_shaped &&
!w->rounded_corners) {
log_debug("Shadow disabled by shadow-ignore-shaped");
shadow_new = false;
} else if (w->prop_shadow == 0) {
log_debug("Shadow disabled by shadow property");
shadow_new = false;
}
if (w->a.map_state != XCB_MAP_STATE_VIEWABLE) {
return;
}
if (!ps->o.wintype_option[w->window_type].shadow) {
log_debug("Shadow disabled by wintypes");
w->options.shadow = TRI_FALSE;
} else if (c2_match(ps->c2_state, w, ps->o.shadow_blacklist, NULL)) {
log_debug("Shadow disabled by shadow-exclude");
w->options.shadow = TRI_FALSE;
} else if (ps->o.shadow_ignore_shaped && w->bounding_shaped && !w->rounded_corners) {
log_debug("Shadow disabled by shadow-ignore-shaped");
w->options.shadow = TRI_FALSE;
} else if (w->prop_shadow == 0) {
log_debug("Shadow disabled by shadow property");
w->options.shadow = TRI_FALSE;
}
win_set_shadow(ps, w, shadow_new);
}
/**
@ -994,99 +957,21 @@ bool win_update_prop_fullscreen(struct x_connection *c, const struct atom *atoms
static void win_determine_clip_shadow_above(session_t *ps, struct win *w) {
bool should_crop = (ps->o.wintype_option[w->window_type].clip_shadow_above ||
c2_match(ps->c2_state, w, ps->o.shadow_clip_list, NULL));
w->clip_shadow_above = should_crop;
}
static void win_set_invert_color(session_t *ps, struct win *w, bool invert_color_new) {
if (w->invert_color == invert_color_new) {
return;
}
w->invert_color = invert_color_new;
add_damage_from_win(ps, w);
w->options.clip_shadow_above = should_crop ? TRI_TRUE : TRI_UNKNOWN;
}
/**
* Determine if a window should have color inverted.
*/
static void win_determine_invert_color(session_t *ps, struct win *w) {
bool invert_color_new = w->invert_color;
if (UNSET != w->invert_color_force) {
invert_color_new = w->invert_color_force;
} else if (w->a.map_state == XCB_MAP_STATE_VIEWABLE) {
invert_color_new = c2_match(ps->c2_state, w, ps->o.invert_color_list, NULL);
}
win_set_invert_color(ps, w, invert_color_new);
}
/**
* Set w->invert_color_force of a window.
*/
void win_set_invert_color_force(session_t *ps, struct win *w, switch_t val) {
if (val != w->invert_color_force) {
w->invert_color_force = val;
win_determine_invert_color(ps, w);
queue_redraw(ps);
}
}
/**
* Set w->fade_force of a window.
*
* Doesn't affect fading already in progress
*/
void win_set_fade_force(struct win *w, switch_t val) {
w->fade_force = val;
}
/**
* Set w->focused_force of a window.
*/
void win_set_focused_force(session_t *ps, struct win *w, switch_t val) {
if (val != w->focused_force) {
w->focused_force = val;
win_on_factor_change(ps, w);
queue_redraw(ps);
}
}
/**
* Set w->shadow_force of a window.
*/
void win_set_shadow_force(session_t *ps, struct win *w, switch_t val) {
if (val != w->shadow_force) {
w->shadow_force = val;
win_determine_shadow(ps, w);
queue_redraw(ps);
}
}
static void win_set_blur_background(session_t *ps, struct win *w, bool blur_background_new) {
if (w->blur_background == blur_background_new) {
w->options.invert_color = TRI_UNKNOWN;
if (w->a.map_state != XCB_MAP_STATE_VIEWABLE) {
return;
}
w->blur_background = blur_background_new;
// This damage might not be absolutely necessary (e.g. when the window is
// opaque), but blur_background changes should be rare, so this should be
// fine.
add_damage_from_win(ps, w);
}
static void win_set_fg_shader(session_t *ps, struct win *w, struct shader_info *shader_new) {
if (w->fg_shader == shader_new) {
return;
if (c2_match(ps->c2_state, w, ps->o.invert_color_list, NULL)) {
w->options.invert_color = TRI_TRUE;
}
w->fg_shader = shader_new;
// A different shader might change how the window is drawn, these changes
// should be rare however, so this should be fine.
add_damage_from_win(ps, w);
}
/**
@ -1094,6 +979,7 @@ static void win_set_fg_shader(session_t *ps, struct win *w, struct shader_info *
*/
static void win_determine_blur_background(session_t *ps, struct win *w) {
log_debug("Determining blur-background of window %#010x (%s)", win_id(w), w->name);
w->options.blur_background = TRI_UNKNOWN;
if (w->a.map_state != XCB_MAP_STATE_VIEWABLE) {
return;
}
@ -1102,15 +988,12 @@ static void win_determine_blur_background(session_t *ps, struct win *w) {
if (blur_background_new) {
if (!ps->o.wintype_option[w->window_type].blur_background) {
log_debug("Blur background disabled by wintypes");
blur_background_new = false;
w->options.blur_background = TRI_FALSE;
} else if (c2_match(ps->c2_state, w, ps->o.blur_background_blacklist, NULL)) {
log_debug("Blur background disabled by "
"blur-background-exclude");
blur_background_new = false;
log_debug("Blur background disabled by blur-background-exclude");
w->options.blur_background = TRI_FALSE;
}
}
win_set_blur_background(ps, w, blur_background_new);
}
/**
@ -1118,27 +1001,28 @@ static void win_determine_blur_background(session_t *ps, struct win *w) {
*/
static void win_determine_rounded_corners(session_t *ps, struct win *w) {
void *radius_override = NULL;
if (c2_match(ps->c2_state, w, ps->o.corner_radius_rules, &radius_override)) {
log_debug("Matched corner rule! %d", w->corner_radius);
bool blacklisted = c2_match(ps->c2_state, w, ps->o.rounded_corners_blacklist, NULL);
if (blacklisted) {
w->options.corner_radius = 0;
return;
}
if (ps->o.corner_radius == 0 && !radius_override) {
w->corner_radius = 0;
return;
bool matched = c2_match(ps->c2_state, w, ps->o.corner_radius_rules, &radius_override);
if (matched) {
log_debug("Window %#010x (%s) matched corner rule! %d", win_id(w),
w->name, (int)(long)radius_override);
}
// Don't round full screen windows & excluded windows,
// unless we find a corner override in corner_radius_rules
if (!radius_override &&
((w && w->is_fullscreen) ||
c2_match(ps->c2_state, w, ps->o.rounded_corners_blacklist, NULL))) {
w->corner_radius = 0;
if (!matched && w && w->is_fullscreen) {
w->options.corner_radius = 0;
log_debug("Not rounding corners for window %#010x", win_id(w));
} else {
if (radius_override) {
w->corner_radius = (int)(long)radius_override;
if (matched) {
w->options.corner_radius = (int)(long)radius_override;
} else {
w->corner_radius = ps->o.corner_radius;
w->options.corner_radius = -1;
}
log_debug("Rounding corners for window %#010x", win_id(w));
@ -1156,18 +1040,13 @@ static void win_determine_fg_shader(session_t *ps, struct win *w) {
return;
}
auto shader_new = ps->o.window_shader_fg;
void *val = NULL;
w->options.shader = NULL;
if (c2_match(ps->c2_state, w, ps->o.window_shader_fg_rules, &val)) {
shader_new = val;
struct shader_info *shader = NULL;
HASH_FIND_STR(ps->shaders, val, shader);
w->options.shader = shader;
}
struct shader_info *shader = NULL;
if (shader_new) {
HASH_FIND_STR(ps->shaders, shader_new, shader);
}
win_set_fg_shader(ps, w, shader);
}
/**
@ -1178,16 +1057,13 @@ void win_update_opacity_rule(session_t *ps, struct win *w) {
return;
}
double opacity = 1.0;
bool is_set = false;
double opacity = NAN;
void *val = NULL;
if (c2_match(ps->c2_state, w, ps->o.opacity_rules, &val)) {
opacity = ((double)(long)val) / 100.0;
is_set = true;
}
w->opacity_set = opacity;
w->opacity_is_set = is_set;
w->options.opacity = opacity;
}
/**
@ -1210,28 +1086,35 @@ void win_on_factor_change(session_t *ps, struct win *w) {
win_determine_blur_background(ps, w);
win_determine_rounded_corners(ps, w);
win_determine_fg_shader(ps, w);
win_update_dim(ps, w);
w->mode = win_calc_mode(w);
log_debug("Window mode changed to %d", w->mode);
win_update_opacity_rule(ps, w);
if (w->a.map_state == XCB_MAP_STATE_VIEWABLE) {
w->paint_excluded = c2_match(ps->c2_state, w, ps->o.paint_blacklist, NULL);
w->options.paint = c2_match(ps->c2_state, w, ps->o.paint_blacklist, NULL)
? TRI_FALSE
: TRI_UNKNOWN;
}
if (w->a.map_state == XCB_MAP_STATE_VIEWABLE) {
w->unredir_if_possible_excluded =
c2_match(ps->c2_state, w, ps->o.unredir_if_possible_blacklist, NULL);
w->options.unredir_ignore =
c2_match(ps->c2_state, w, ps->o.unredir_if_possible_blacklist, NULL)
? TRI_TRUE
: TRI_UNKNOWN;
}
w->fade_excluded = c2_match(ps->c2_state, w, ps->o.fade_blacklist, NULL);
w->options.fade =
c2_match(ps->c2_state, w, ps->o.fade_blacklist, NULL) ? TRI_FALSE : TRI_UNKNOWN;
w->transparent_clipping =
ps->o.transparent_clipping &&
!c2_match(ps->c2_state, w, ps->o.transparent_clipping_blacklist, NULL);
w->options.transparent_clipping =
c2_match(ps->c2_state, w, ps->o.transparent_clipping_blacklist, NULL)
? TRI_FALSE
: TRI_UNKNOWN;
w->reg_ignore_valid = false;
if (ps->debug_window != XCB_NONE &&
(win_id(w) == ps->debug_window ||
(win_client_id(w, /*fallback_to_self=*/false) == ps->debug_window))) {
w->paint_excluded = true;
w->options.paint = TRI_FALSE;
}
}
@ -1378,12 +1261,6 @@ struct win *win_maybe_allocate(session_t *ps, struct wm_ref *cursor,
// property/attributes/etc
// change
// Runtime variables, updated by dbus
.fade_force = UNSET,
.shadow_force = UNSET,
.focused_force = UNSET,
.invert_color_force = UNSET,
.mode = WMODE_TRANS,
.leader = XCB_NONE,
.cache_leader = NULL,
@ -1480,6 +1357,10 @@ struct win *win_maybe_allocate(session_t *ps, struct wm_ref *cursor,
new->pictfmt = x_get_pictform_for_visual(&ps->c, new->a.visual);
new->client_pictfmt = NULL;
new->tree_ref = cursor;
new->options = WIN_MAYBE_OPTIONS_DEFAULT;
new->options_override = WIN_MAYBE_OPTIONS_DEFAULT;
new->options_default = &ps->window_options_default;
new->options.focused = TRI_FALSE;
// Set all the stale flags on this new window, so it's properties will get
// updated when it's mapped
@ -1691,9 +1572,12 @@ void win_set_focused(session_t *ps, struct win *w) {
*/
void win_extents(const struct win *w, region_t *res) {
pixman_region32_clear(res);
pixman_region32_union_rect(res, res, w->g.x, w->g.y, (uint)w->widthb, (uint)w->heightb);
if (w->state != WSTATE_MAPPED) {
return;
}
if (w->shadow) {
pixman_region32_union_rect(res, res, w->g.x, w->g.y, (uint)w->widthb, (uint)w->heightb);
if (win_options(w).shadow) {
assert(w->shadow_width >= 0 && w->shadow_height >= 0);
pixman_region32_union_rect(res, res, w->g.x + w->shadow_dx,
w->g.y + w->shadow_dy, (uint)w->shadow_width,

View file

@ -14,6 +14,7 @@
#include "c2.h"
#include "compiler.h"
#include "config.h"
#include "defs.h"
#include "region.h"
#include "render.h"
@ -159,10 +160,6 @@ struct win {
bool rounded_corners;
/// Whether this window is to be painted.
bool to_paint;
/// Whether the window is painting excluded.
bool paint_excluded;
/// Whether the window is unredirect-if-possible excluded.
bool unredir_if_possible_excluded;
/// Whether this window is in open/close state.
bool in_openclose;
@ -174,12 +171,6 @@ struct win {
/// Cached topmost window ID of the leader window.
struct wm_ref *cache_leader;
// Focus-related members
/// Whether the window is to be considered focused.
bool focused;
/// Override value of window focus state. Set by D-Bus method calls.
switch_t focused_force;
// Blacklist related members
/// Name of the window.
char *name;
@ -199,7 +190,7 @@ struct win {
bool is_focused;
// Opacity-related members
/// Window opacity
/// The final window opacity if no animation is running
double opacity;
/// true if window (or client window, for broken window managers
/// not transferring client window's _NET_WM_WINDOW_OPACITY value) has opacity
@ -207,24 +198,11 @@ struct win {
bool has_opacity_prop;
/// Cached value of opacity window attribute.
opacity_t opacity_prop;
/// true if opacity is set by some rules
bool opacity_is_set;
/// Last window opacity value set by the rules.
double opacity_set;
/// Radius of rounded window corners
int corner_radius;
float border_col[4];
// Fading-related members
/// Override value of window fade state. Set by D-Bus method calls.
switch_t fade_force;
/// Whether fading is excluded by the rules. Calculated.
bool fade_excluded;
/// Whether transparent clipping is excluded by the rules.
bool transparent_clipping;
// Frame-opacity-related members
/// Current window frame opacity. Affected by window opacity.
double frame_opacity;
@ -232,10 +210,6 @@ struct win {
margin_t frame_extents;
// Shadow-related members
/// Whether a window has shadow. Calculated.
bool shadow;
/// Override value of window shadow state. Set by D-Bus method calls.
switch_t shadow_force;
/// Window specific shadow factor. The final shadow opacity is a combination of
/// this, the window opacity, and the window frame opacity.
double shadow_opacity;
@ -252,24 +226,6 @@ struct win {
/// The value of _COMPTON_SHADOW attribute of the window. Below 0 for
/// none.
long long prop_shadow;
/// Do not paint shadow over this window.
bool clip_shadow_above;
// Dim-related members
/// Whether the window is to be dimmed.
bool dim;
/// Whether to invert window color.
bool invert_color;
/// Override value of window color inversion state. Set by D-Bus method
/// calls.
switch_t invert_color_force;
/// Whether to blur window background.
bool blur_background;
/// The custom window shader to use when rendering.
struct shader_info *fg_shader;
struct c2_window_state c2_state;
@ -285,6 +241,13 @@ struct win {
/// The damaged region of the window, in window local coordinates.
region_t damaged;
/// Per-window options coming from rules
struct window_maybe_options options;
/// Override of per-window options, used by dbus interface
struct window_maybe_options options_override;
/// Global per-window options default
const struct window_options *options_default;
/// Previous state of the window before state changed. This is used
/// by `win_process_animation_and_state_change` to trigger appropriate
/// animations.
@ -334,6 +297,68 @@ static const struct script_output_info win_script_outputs[] = {
[NUM_OF_WIN_SCRIPT_OUTPUTS] = {NULL},
};
static const struct window_maybe_options WIN_MAYBE_OPTIONS_DEFAULT = {
.blur_background = TRI_UNKNOWN,
.clip_shadow_above = TRI_UNKNOWN,
.shadow = TRI_UNKNOWN,
.fade = TRI_UNKNOWN,
.invert_color = TRI_UNKNOWN,
.paint = TRI_UNKNOWN,
.dim = TRI_UNKNOWN,
.opacity = NAN,
.shader = NULL,
.corner_radius = -1,
.unredir_ignore = TRI_UNKNOWN,
};
/// Combine two window options. The `upper` value has higher priority, the `lower` value
/// will only be used if the corresponding value in `upper` is not set (e.g. it is
/// TRI_UNKNOWN for tristate values, NaN for opacity, -1 for corner_radius).
static inline struct window_maybe_options __attribute__((always_inline))
win_maybe_options_fold(struct window_maybe_options upper, struct window_maybe_options lower) {
return (struct window_maybe_options){
.unredir_ignore = tri_or(upper.unredir_ignore, lower.unredir_ignore),
.blur_background = tri_or(upper.blur_background, lower.blur_background),
.clip_shadow_above = tri_or(upper.clip_shadow_above, lower.clip_shadow_above),
.shadow = tri_or(upper.shadow, lower.shadow),
.dim = tri_or(upper.dim, lower.dim),
.fade = tri_or(upper.fade, lower.fade),
.focused = tri_or(upper.focused, lower.focused),
.invert_color = tri_or(upper.invert_color, lower.invert_color),
.paint = tri_or(upper.paint, lower.paint),
.opacity = !safe_isnan(upper.opacity) ? upper.opacity : lower.opacity,
.shader = upper.shader ? upper.shader : lower.shader,
.corner_radius = upper.corner_radius >= 0 ? upper.corner_radius : lower.corner_radius,
};
}
/// Unwrap a `window_maybe_options` to a `window_options`, using the default value for
/// values that are not set in the `window_maybe_options`.
static inline struct window_options __attribute__((always_inline))
win_maybe_options_or(struct window_maybe_options maybe, struct window_options def) {
return (struct window_options){
.unredir_ignore = tri_or_bool(maybe.unredir_ignore, def.unredir_ignore),
.blur_background = tri_or_bool(maybe.blur_background, def.blur_background),
.clip_shadow_above = tri_or_bool(maybe.clip_shadow_above, def.clip_shadow_above),
.shadow = tri_or_bool(maybe.shadow, def.shadow),
.corner_radius = maybe.corner_radius >= 0 ? (unsigned int)maybe.corner_radius
: def.corner_radius,
.dim = tri_or_bool(maybe.dim, def.dim),
.fade = tri_or_bool(maybe.fade, def.fade),
.focused = tri_or_bool(maybe.focused, def.focused),
.invert_color = tri_or_bool(maybe.invert_color, def.invert_color),
.paint = tri_or_bool(maybe.paint, def.paint),
.opacity = !safe_isnan(maybe.opacity) ? maybe.opacity : def.opacity,
.shader = maybe.shader ? maybe.shader : def.shader,
};
}
static inline struct window_options __attribute__((always_inline))
win_options(const struct win *w) {
return win_maybe_options_or(
win_maybe_options_fold(w->options_override, w->options), *w->options_default);
}
/// Process pending updates/images flags on a window. Has to be called in X critical
/// section. Returns true if the window had an animation running and it has just finished,
/// or if the window's states just changed and there is no animation defined for this
@ -358,10 +383,6 @@ void win_release_images(struct backend_base *backend, struct win *w);
winmode_t attr_pure win_calc_mode_raw(const struct win *w);
// TODO(yshui) `win_calc_mode` is only used by legacy backends
winmode_t attr_pure win_calc_mode(const struct win *w);
void win_set_shadow_force(session_t *ps, struct win *w, switch_t val);
void win_set_fade_force(struct win *w, switch_t val);
void win_set_focused_force(session_t *ps, struct win *w, switch_t val);
void win_set_invert_color_force(session_t *ps, struct win *w, switch_t val);
/**
* Set real focused state of a window.
*/
@ -369,8 +390,6 @@ void win_set_focused(session_t *ps, struct win *w);
void win_on_factor_change(session_t *ps, struct win *w);
void win_on_client_update(session_t *ps, struct win *w);
bool attr_pure win_should_dim(session_t *ps, const struct win *w);
int attr_pure win_find_monitor(const struct x_monitors *monitors, const struct win *mw);
/// Recheck if a window is fullscreen
@ -529,14 +548,15 @@ win_region_remove_corners(const struct win *w, ivec2 origin, region_t *res) {
{1, 0},
{1, 1},
};
int corner_radius = (int)win_options(w).corner_radius;
rect_t rectangles[4];
for (size_t i = 0; i < ARR_SIZE(corner_index); i++) {
rectangles[i] = (rect_t){
.x1 = origin.x + corner_index[i][0] * (w->widthb - w->corner_radius),
.y1 = origin.y + corner_index[i][1] * (w->heightb - w->corner_radius),
.x1 = origin.x + corner_index[i][0] * (w->widthb - corner_radius),
.y1 = origin.y + corner_index[i][1] * (w->heightb - corner_radius),
};
rectangles[i].x2 = rectangles[i].x1 + w->corner_radius;
rectangles[i].y2 = rectangles[i].y1 + w->corner_radius;
rectangles[i].x2 = rectangles[i].x1 + corner_radius;
rectangles[i].y2 = rectangles[i].y1 + corner_radius;
}
region_t corners;
pixman_region32_init_rects(&corners, rectangles, 4);