backend, win: create shadows with shadow_from_mask

Do this for shaped, and rounded windows.

Signed-off-by: Yuxuan Shui <yshuiv7@gmail.com>
This commit is contained in:
Yuxuan Shui 2022-08-25 17:59:19 +01:00
parent a9ec614286
commit 9ac046c2ba
No known key found for this signature in database
GPG Key ID: D3A4405BE6CC17F4
3 changed files with 191 additions and 145 deletions

View File

@ -206,21 +206,11 @@ void paint_all_new(session_t *ps, struct managed_win *t, bool ignore_damage) {
auto reg_bound = win_get_bounding_shape_global_by_val(w);
auto reg_bound_no_corner =
win_get_bounding_shape_global_without_corners_by_val(w);
region_t reg_bound_local;
pixman_region32_init(&reg_bound_local);
pixman_region32_copy(&reg_bound_local, &reg_bound);
pixman_region32_translate(&reg_bound_local, -w->g.x, -w->g.y);
if (!w->mask_image) {
// TODO(yshui) only allocate a mask if the window is shaped or has
// rounded corners.
w->mask_image = ps->backend_data->ops->make_mask(
ps->backend_data,
(geometry_t){.width = w->g.width, .height = w->g.height},
&reg_bound_local);
ps->backend_data->ops->set_image_property(
ps->backend_data, IMAGE_PROPERTY_CORNER_RADIUS, w->mask_image,
(double[]){w->corner_radius});
win_bind_mask(ps->backend_data, w);
}
// The clip region for the current window, in global/target coordinates
@ -455,8 +445,12 @@ void paint_all_new(session_t *ps, struct managed_win *t, bool ignore_damage) {
// reg_visible as a hint. Since window image data outside of the
// damage region won't be painted onto target
region_t reg_visible_local;
region_t reg_bound_local;
{
// The bounding shape, in window local coordinates
pixman_region32_init(&reg_bound_local);
pixman_region32_copy(&reg_bound_local, &reg_bound);
pixman_region32_translate(&reg_bound_local, -w->g.x, -w->g.y);
pixman_region32_init(&reg_visible_local);
pixman_region32_intersect(&reg_visible_local,
@ -483,10 +477,10 @@ void paint_all_new(session_t *ps, struct managed_win *t, bool ignore_damage) {
&reg_paint_in_bound, &reg_visible);
ps->backend_data->ops->release_image(ps->backend_data, new_img);
pixman_region32_fini(&reg_visible_local);
pixman_region32_fini(&reg_bound_local);
}
skip:
pixman_region32_fini(&reg_bound);
pixman_region32_fini(&reg_bound_local);
pixman_region32_fini(&reg_bound_no_corner);
pixman_region32_fini(&reg_paint_in_bound);
}

317
src/win.c
View File

@ -345,13 +345,33 @@ static inline bool win_bind_pixmap(struct backend_base *b, struct managed_win *w
return true;
}
bool win_bind_mask(struct backend_base *b, struct managed_win *w) {
auto reg_bound_local = win_get_bounding_shape_global_by_val(w);
pixman_region32_translate(&reg_bound_local, -w->g.x, -w->g.y);
w->mask_image = b->ops->make_mask(
b, (geometry_t){.width = w->g.width, .height = w->g.height}, &reg_bound_local);
if (!w->mask_image) {
return false;
}
b->ops->set_image_property(b, IMAGE_PROPERTY_CORNER_RADIUS, w->mask_image,
(double[]){w->corner_radius});
return true;
}
bool win_bind_shadow(struct backend_base *b, struct managed_win *w, struct color c,
struct backend_shadow_context *sctx) {
assert(!w->shadow_image);
assert(w->shadow);
w->shadow_image = b->ops->render_shadow(b, w->widthb, w->heightb, sctx, c);
if ((w->corner_radius == 0 && w->bounding_shaped == false) ||
b->ops->shadow_from_mask == NULL) {
w->shadow_image = b->ops->render_shadow(b, w->widthb, w->heightb, sctx, c);
} else {
win_bind_mask(b, w);
w->shadow_image = b->ops->shadow_from_mask(b, w->mask_image, sctx, c);
}
if (!w->shadow_image) {
log_error("Failed to bind shadow image, shadow will be disabled for "
log_error("Failed to bind shadow image, shadow will be disabled "
"for "
"%#010x (%s)",
w->base.id, w->name);
win_set_flags(w, WIN_FLAGS_SHADOW_NONE);
@ -365,10 +385,9 @@ bool win_bind_shadow(struct backend_base *b, struct managed_win *w, struct color
}
void win_release_images(struct backend_base *backend, struct managed_win *w) {
// We don't want to decide what we should do if the image we want to release is
// stale (do we clear the stale flags or not?)
// But if we are not releasing any images anyway, we don't care about the stale
// flags.
// We don't want to decide what we should do if the image we want to
// release is stale (do we clear the stale flags or not?) But if we are
// not releasing any images anyway, we don't care about the stale flags.
if (!win_check_flags_all(w, WIN_FLAGS_PIXMAP_NONE)) {
assert(!win_check_flags_all(w, WIN_FLAGS_PIXMAP_STALE));
@ -383,13 +402,15 @@ void win_release_images(struct backend_base *backend, struct managed_win *w) {
win_release_mask(backend, w);
}
/// Returns true if the `prop` property is stale, as well as clears the stale flag.
/// Returns true if the `prop` property is stale, as well as clears the stale
/// flag.
static bool win_fetch_and_unset_property_stale(struct managed_win *w, xcb_atom_t prop);
/// Returns true if any of the properties are stale, as well as clear all the stale flags.
/// Returns true if any of the properties are stale, as well as clear all the
/// stale flags.
static void win_clear_all_properties_stale(struct managed_win *w);
/// Fetch new window properties from the X server, and run appropriate updates. Might set
/// WIN_FLAGS_FACTOR_CHANGED
/// Fetch new window properties from the X server, and run appropriate updates.
/// Might set WIN_FLAGS_FACTOR_CHANGED
static void win_update_properties(session_t *ps, struct managed_win *w) {
if (win_fetch_and_unset_property_stale(w, ps->atoms->a_NET_WM_WINDOW_TYPE)) {
win_update_wintype(ps, w);
@ -440,8 +461,8 @@ static void win_update_properties(session_t *ps, struct managed_win *w) {
/// Handle non-image flags. This phase might set IMAGES_STALE flags
void win_process_update_flags(session_t *ps, struct managed_win *w) {
// Whether the window was visible before we process the mapped flag. i.e. is the
// window just mapped.
// Whether the window was visible before we process the mapped flag. i.e.
// is the window just mapped.
bool was_visible = win_is_real_visible(w);
log_trace("Processing flags for window %#010x (%s), was visible: %d", w->base.id,
w->name, was_visible);
@ -456,8 +477,8 @@ void win_process_update_flags(session_t *ps, struct managed_win *w) {
return;
}
// Check client first, because later property updates need accurate client window
// information
// Check client first, because later property updates need accurate client
// window information
if (win_check_flags_all(w, WIN_FLAGS_CLIENT_STALE)) {
win_recheck_client(ps, w);
win_clear_flags(w, WIN_FLAGS_CLIENT_STALE);
@ -466,15 +487,15 @@ void win_process_update_flags(session_t *ps, struct managed_win *w) {
bool damaged = false;
if (win_check_flags_any(w, WIN_FLAGS_SIZE_STALE | WIN_FLAGS_POSITION_STALE)) {
if (was_visible) {
// Mark the old extents of this window as damaged. The new extents
// will be marked damaged below, after the window extents are
// updated.
// Mark the old extents of this window as damaged. The new
// extents will be marked damaged below, after the window
// extents are updated.
//
// If the window is just mapped, we don't need to mark the old
// extent as damaged. (It's possible that the window was in fading
// and is interrupted by being mapped. In that case, the fading
// window will be added to damage by map_win_start, so we don't
// need to do it here)
// If the window is just mapped, we don't need to mark the
// old extent as damaged. (It's possible that the window
// was in fading and is interrupted by being mapped. In
// that case, the fading window will be added to damage by
// map_win_start, so we don't need to do it here)
add_damage_from_win(ps, w);
}
@ -501,7 +522,8 @@ void win_process_update_flags(session_t *ps, struct managed_win *w) {
win_clear_flags(w, WIN_FLAGS_PROPERTY_STALE);
}
// Factor change flags could be set by previous stages, so must be handled last
// Factor change flags could be set by previous stages, so must be handled
// last
if (win_check_flags_all(w, WIN_FLAGS_FACTOR_CHANGED)) {
win_on_factor_change(ps, w);
win_clear_flags(w, WIN_FLAGS_FACTOR_CHANGED);
@ -533,9 +555,10 @@ void win_process_image_flags(session_t *ps, struct managed_win *w) {
}
if (win_check_flags_all(w, WIN_FLAGS_PIXMAP_STALE)) {
// Check to make sure the window is still mapped, otherwise we
// won't be able to rebind pixmap after releasing it, yet we might
// still need the pixmap for rendering.
// Check to make sure the window is still mapped,
// otherwise we won't be able to rebind pixmap after
// releasing it, yet we might still need the pixmap for
// rendering.
assert(w->state != WSTATE_UNMAPPING && w->state != WSTATE_DESTROYING);
if (!win_check_flags_all(w, WIN_FLAGS_PIXMAP_NONE)) {
// Must release images first, otherwise breaks
@ -615,7 +638,8 @@ int win_update_name(session_t *ps, struct managed_win *w) {
}
if (!(wid_get_text_prop(ps, w->client_win, ps->atoms->a_NET_WM_NAME, &strlst, &nstr))) {
log_debug("(%#010x): _NET_WM_NAME unset, falling back to WM_NAME.",
log_debug("(%#010x): _NET_WM_NAME unset, falling back to "
"WM_NAME.",
w->client_win);
if (!wid_get_text_prop(ps, w->client_win, ps->atoms->aWM_NAME, &strlst, &nstr)) {
@ -737,8 +761,8 @@ winmode_t win_calc_mode(const struct managed_win *w) {
if (win_has_alpha(w)) {
if (w->client_win == XCB_NONE) {
// This is a window not managed by the WM, and it has alpha,
// so it's transparent. No need to check WM frame.
// This is a window not managed by the WM, and it has
// alpha, so it's transparent. No need to check WM frame.
return WMODE_TRANS;
}
// The WM window has alpha
@ -748,12 +772,12 @@ winmode_t win_calc_mode(const struct managed_win *w) {
return WMODE_TRANS;
}
if (win_has_frame(w)) {
// The client window doesn't have alpha, but we have a WM frame
// window, which has alpha.
// The client window doesn't have alpha, but we have a WM
// frame window, which has alpha.
return WMODE_FRAME_TRANS;
}
// Although the WM window has alpha, the frame window has 0 size, so
// consider the window solid
// Although the WM window has alpha, the frame window has 0 size,
// so consider the window solid
}
if (w->frame_opacity != 1.0 && win_has_frame(w)) {
@ -769,8 +793,9 @@ winmode_t win_calc_mode(const struct managed_win *w) {
*
* The priority of opacity settings are:
*
* inactive_opacity_override (if set, and unfocused) > _NET_WM_WINDOW_OPACITY (if set) >
* opacity-rules (if matched) > window type default opacity > active/inactive opacity
* inactive_opacity_override (if set, and unfocused) > _NET_WM_WINDOW_OPACITY (if
* set) > opacity-rules (if matched) > window type default opacity >
* active/inactive opacity
*
* @param ps current session
* @param w struct _win object representing the window
@ -795,7 +820,8 @@ double win_calc_opacity_target(session_t *ps, const struct managed_win *w) {
} else if (!safe_isnan(ps->o.wintype_option[w->window_type].opacity)) {
opacity = ps->o.wintype_option[w->window_type].opacity;
} else {
// Respect active_opacity only when the window is physically focused
// Respect active_opacity only when the window is physically
// focused
if (win_is_focused_raw(ps, w))
opacity = ps->o.active_opacity;
else if (!w->focused)
@ -831,7 +857,8 @@ bool win_should_dim(session_t *ps, const struct managed_win *w) {
* Determine if a window should fade on opacity change.
*/
bool win_should_fade(session_t *ps, const struct managed_win *w) {
// To prevent it from being overwritten by last-paint value if the window is
// To prevent it from being overwritten by last-paint value if the window
// is
if (w->fade_force != UNSET) {
return w->fade_force;
}
@ -875,7 +902,8 @@ static void win_set_shadow(session_t *ps, struct managed_win *w, bool shadow_new
log_debug("Updating shadow property of window %#010x (%s) to %d", w->base.id,
w->name, shadow_new);
// We don't handle property updates of non-visible windows until they are mapped.
// We don't handle property updates of non-visible windows until they are
// mapped.
assert(w->state != WSTATE_UNMAPPED && w->state != WSTATE_DESTROYING &&
w->state != WSTATE_UNMAPPING);
@ -897,10 +925,10 @@ static void win_set_shadow(session_t *ps, struct managed_win *w, bool shadow_new
// Note: because the release and creation of the shadow images are
// delayed. When multiple shadow changes happen in a row, without
// rendering phase between them, there could be a stale shadow image
// attached to the window even if w->shadow was previously false. And vice
// versa. So we check the STALE flag before asserting the existence of the
// shadow image.
// rendering phase between them, there could be a stale shadow
// image attached to the window even if w->shadow was previously
// false. And vice versa. So we check the STALE flag before
// asserting the existence of the shadow image.
if (w->shadow) {
// Mark the new extents as damaged if the shadow is added
assert(!w->shadow_image ||
@ -910,7 +938,8 @@ static void win_set_shadow(session_t *ps, struct managed_win *w, bool shadow_new
win_extents(w, &extents);
add_damage_from_win(ps, w);
} else {
// Mark the old extents as damaged if the shadow is removed
// Mark the old extents as damaged if the shadow is
// removed
assert(w->shadow_image ||
win_check_flags_all(w, WIN_FLAGS_SHADOW_STALE) ||
ps->o.legacy_backends);
@ -919,11 +948,12 @@ static void win_set_shadow(session_t *ps, struct managed_win *w, bool shadow_new
// Delayed update of shadow image
// By setting WIN_FLAGS_SHADOW_STALE, we ask win_process_flags to
// re-create or release the shaodw in based on whether w->shadow is set.
// re-create or release the shaodw in based on whether w->shadow
// is set.
win_set_flags(w, WIN_FLAGS_SHADOW_STALE);
// Only set pending_updates if we are redirected. Otherwise change of a
// shadow won't have influence on whether we should redirect.
// 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;
}
@ -1055,8 +1085,9 @@ win_set_blur_background(session_t *ps, struct managed_win *w, bool blur_backgrou
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.
// 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);
}
@ -1068,8 +1099,8 @@ win_set_fg_shader(session_t *ps, struct managed_win *w, struct shader_info *shad
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.
// 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);
}
@ -1088,7 +1119,8 @@ static void win_determine_blur_background(session_t *ps, struct managed_win *w)
log_debug("Blur background disabled by wintypes");
blur_background_new = false;
} else if (c2_match(ps, w, ps->o.blur_background_blacklist, NULL)) {
log_debug("Blur background disabled by blur-background-exclude");
log_debug("Blur background disabled by "
"blur-background-exclude");
blur_background_new = false;
}
}
@ -1168,8 +1200,8 @@ void win_update_opacity_rule(session_t *ps, struct managed_win *w) {
*/
void win_on_factor_change(session_t *ps, struct managed_win *w) {
log_debug("Window %#010x (%s) factor change", w->base.id, w->name);
// Focus needs to be updated first, as other rules might depend on the focused
// state of the window
// Focus needs to be updated first, as other rules might depend on the
// focused state of the window
win_update_focused(ps, w);
win_determine_shadow(ps, w);
@ -1207,7 +1239,8 @@ void win_on_win_size_change(session_t *ps, struct managed_win *w) {
w->shadow_width = w->widthb + ps->o.shadow_radius * 2;
w->shadow_height = w->heightb + ps->o.shadow_radius * 2;
// We don't handle property updates of non-visible windows until they are mapped.
// We don't handle property updates of non-visible windows until they are
// mapped.
assert(w->state != WSTATE_UNMAPPED && w->state != WSTATE_DESTROYING &&
w->state != WSTATE_UNMAPPING);
@ -1436,21 +1469,21 @@ struct win *add_win_top(session_t *ps, xcb_window_t id) {
return add_win(ps, id, &ps->window_stack);
}
/// Insert a new window above window with id `below`, if there is no window, add to top
/// New window will be in unmapped state
/// Insert a new window above window with id `below`, if there is no window, add
/// to top New window will be in unmapped state
struct win *add_win_above(session_t *ps, xcb_window_t id, xcb_window_t below) {
struct win *w = NULL;
HASH_FIND_INT(ps->windows, &below, w);
if (!w) {
if (!list_is_empty(&ps->window_stack)) {
// `below` window is not found even if the window stack is not
// empty
// `below` window is not found even if the window stack is
// not empty
return NULL;
}
return add_win_top(ps, id);
} else {
// we found something from the hash table, so if the stack is empty,
// we are in an inconsistent state.
// we found something from the hash table, so if the stack is
// empty, we are in an inconsistent state.
assert(!list_is_empty(&ps->window_stack));
return add_win(ps, id, w->stack_neighbour.prev);
}
@ -1477,7 +1510,8 @@ struct win *fill_win(session_t *ps, struct win *w) {
.in_openclose = true, // set to false after first map is done,
// true here because window is just created
.reg_ignore_valid = false, // set to true when damaged
.flags = WIN_FLAGS_IMAGES_NONE, // updated by property/attributes/etc
.flags = WIN_FLAGS_IMAGES_NONE, // updated by
// property/attributes/etc
// change
.stale_props = NULL,
.stale_props_capacity = 0,
@ -1558,8 +1592,9 @@ struct win *fill_win(session_t *ps, struct win *w) {
auto duplicated_win = find_managed_win(ps, w->id);
if (duplicated_win) {
log_debug("Window %#010x (recorded name: %s) added multiple times", w->id,
duplicated_win->name);
log_debug("Window %#010x (recorded name: %s) added multiple "
"times",
w->id, duplicated_win->name);
return &duplicated_win->base;
}
@ -1571,14 +1606,15 @@ struct win *fill_win(session_t *ps, struct win *w) {
// Failed to get window attributes or geometry probably means
// the window is gone already. Unviewable means the window is
// already reparented elsewhere.
// BTW, we don't care about Input Only windows, except for stacking
// proposes, so we need to keep track of them still.
// BTW, we don't care about Input Only windows, except for
// stacking proposes, so we need to keep track of them still.
free(a);
return w;
}
if (a->_class == XCB_WINDOW_CLASS_INPUT_ONLY) {
// No need to manage this window, but we still keep it on the window stack
// No need to manage this window, but we still keep it on the
// window stack
w->managed = false;
free(a);
return w;
@ -1648,8 +1684,8 @@ struct win *fill_win(session_t *ps, struct win *w) {
assert(replaced == w);
free(w);
// Set all the stale flags on this new window, so it's properties will get updated
// when it's mapped
// Set all the stale flags on this new window, so it's properties will get
// updated when it's mapped
win_set_flags(new, WIN_FLAGS_CLIENT_STALE | WIN_FLAGS_SIZE_STALE |
WIN_FLAGS_POSITION_STALE | WIN_FLAGS_PROPERTY_STALE |
WIN_FLAGS_FACTOR_CHANGED);
@ -1685,8 +1721,8 @@ static inline void win_set_leader(session_t *ps, struct managed_win *w, xcb_wind
// gets mapped before parent, or when the window is a waypoint
clear_cache_win_leaders(ps);
// Update the old and new window group and active_leader if the window
// could affect their state.
// Update the old and new window group and active_leader if the
// window could affect their state.
xcb_window_t cache_leader = win_get_leader(ps, w);
if (win_is_focused_raw(ps, w) && cache_leader_old != cache_leader) {
ps->active_leader = cache_leader;
@ -1733,7 +1769,8 @@ static xcb_window_t win_get_leader_raw(session_t *ps, struct managed_win *w, int
if (!(w->cache_leader = w->leader))
w->cache_leader = w->client_win;
// If the leader of this window isn't itself, look for its ancestors
// If the leader of this window isn't itself, look for its
// ancestors
if (w->cache_leader && w->cache_leader != w->client_win) {
auto wp = find_toplevel(ps, w->cache_leader);
if (wp) {
@ -1881,7 +1918,8 @@ void win_update_bounding_shape(session_t *ps, struct managed_win *w) {
w->bounding_shaped = win_bounding_shaped(ps, w->base.id);
}
// We don't handle property updates of non-visible windows until they are mapped.
// We don't handle property updates of non-visible windows until they are
// mapped.
assert(w->state != WSTATE_UNMAPPED && w->state != WSTATE_DESTROYING &&
w->state != WSTATE_UNMAPPING);
@ -1922,8 +1960,8 @@ void win_update_bounding_shape(session_t *ps, struct managed_win *w) {
// We think the top left of the border is the origin
pixman_region32_translate(&br, w->g.border_width, 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
// Intersect the bounding region we got with the window rectangle,
// to make sure the bounding region is not bigger than the window
// rectangle
pixman_region32_intersect(&w->bounding_shape, &w->bounding_shape, &br);
pixman_region32_fini(&br);
@ -1980,7 +2018,8 @@ void win_update_frame_extents(session_t *ps, struct managed_win *w, xcb_window_t
for (int i = 0; i < 4; i++) {
if (prop.c32[i] > (uint32_t)INT_MAX) {
log_warn("Your window manager sets a absurd "
"_NET_FRAME_EXTENTS value (%u), ignoring it.",
"_NET_FRAME_EXTENTS value (%u), "
"ignoring it.",
prop.c32[i]);
memset(extents, 0, sizeof(extents));
break;
@ -2080,11 +2119,12 @@ static void destroy_win_finish(session_t *ps, struct win *w) {
auto mw = (struct managed_win *)w;
if (mw->state != WSTATE_UNMAPPED) {
// Only UNMAPPED state has window resources freed, otherwise
// we need to call unmap_win_finish to free them.
// XXX actually we unmap_win_finish only frees the rendering
// resources, we still need to call free_win_res. will fix
// later.
// Only UNMAPPED state has window resources freed,
// otherwise we need to call unmap_win_finish to free
// them.
// XXX actually we unmap_win_finish only frees the
// rendering resources, we still need to call free_win_res.
// will fix later.
unmap_win_finish(ps, mw);
}
@ -2107,11 +2147,12 @@ static void destroy_win_finish(session_t *ps, struct win *w) {
}
if (mw == ps->active_win) {
// Usually, the window cannot be the focused at destruction.
// FocusOut should be generated before the window is destroyed. We
// do this check just to be completely sure we don't have dangling
// references.
log_debug("window %#010x (%s) is destroyed while being focused",
// Usually, the window cannot be the focused at
// destruction. FocusOut should be generated before the
// window is destroyed. We do this check just to be
// completely sure we don't have dangling references.
log_debug("window %#010x (%s) is destroyed while being "
"focused",
w->id, mw->name);
ps->active_win = NULL;
}
@ -2144,11 +2185,13 @@ static inline void restack_win(session_t *ps, struct win *w, struct list_node *n
}
if (mw) {
// This invalidates all reg_ignore below the new stack position of `w`
// This invalidates all reg_ignore below the new stack position of
// `w`
mw->reg_ignore_valid = false;
rc_region_unref(&mw->reg_ignore);
// This invalidates all reg_ignore below the old stack position of `w`
// This invalidates all reg_ignore below the old stack position of
// `w`
auto next_w = win_stack_find_next_managed(ps, &w->stack_neighbour);
if (next_w) {
next_w->reg_ignore_valid = false;
@ -2230,39 +2273,42 @@ bool destroy_win_start(session_t *ps, struct win *w) {
log_debug("Destroying %#010x \"%s\", managed = %d", w->id,
(w->managed ? mw->name : NULL), w->managed);
// Delete destroyed window from the hash table, even though the window might still
// be rendered for a while. We need to make sure future window with the same
// window id won't confuse us. Keep the window in the window stack if it's managed
// and mapped, since we might still need to render it (e.g. fading out). Window
// will be removed from the stack when it finishes destroying.
// Delete destroyed window from the hash table, even though the window
// might still be rendered for a while. We need to make sure future window
// with the same window id won't confuse us. Keep the window in the window
// stack if it's managed and mapped, since we might still need to render
// it (e.g. fading out). Window will be removed from the stack when it
// finishes destroying.
HASH_DEL(ps->windows, w);
if (!w->managed || mw->state == WSTATE_UNMAPPED) {
// Window is already unmapped, or is an unmanged window, just destroy it
// Window is already unmapped, or is an unmanged window, just
// destroy it
destroy_win_finish(ps, w);
return true;
}
if (w->managed) {
// Clear IMAGES_STALE flags since the window is destroyed: Clear
// PIXMAP_STALE as there is no pixmap available anymore, so STALE doesn't
// make sense.
// XXX Clear SHADOW_STALE as setting/clearing flags on a destroyed window
// doesn't work leading to an inconsistent state where the shadow is
// refreshed but the flags are stuck in STALE.
// Do this before changing the window state to destroying
// PIXMAP_STALE as there is no pixmap available anymore, so STALE
// doesn't make sense.
// XXX Clear SHADOW_STALE as setting/clearing flags on a destroyed
// window doesn't work leading to an inconsistent state where the
// shadow is refreshed but the flags are stuck in STALE. Do this
// before changing the window state to destroying
win_clear_flags(mw, WIN_FLAGS_IMAGES_STALE);
// If size/shape/position information is stale, win_process_update_flags
// will update them and add the new window extents to damage. Since the
// window has been destroyed, we cannot get the complete information at
// this point, so we just add what we currently have to the damage.
// If size/shape/position information is stale,
// win_process_update_flags will update them and add the new
// window extents to damage. Since the window has been destroyed,
// we cannot get the complete information at this point, so we
// just add what we currently have to the damage.
if (win_check_flags_any(mw, WIN_FLAGS_SIZE_STALE | WIN_FLAGS_POSITION_STALE)) {
add_damage_from_win(ps, mw);
}
// Clear some flags about stale window information. Because now the window
// is destroyed, we can't update them anyway.
// Clear some flags about stale window information. Because now
// the window is destroyed, we can't update them anyway.
win_clear_flags(mw, WIN_FLAGS_SIZE_STALE | WIN_FLAGS_POSITION_STALE |
WIN_FLAGS_PROPERTY_STALE |
WIN_FLAGS_FACTOR_CHANGED | WIN_FLAGS_CLIENT_STALE);
@ -2309,7 +2355,8 @@ void unmap_win_start(session_t *ps, struct managed_win *w) {
// Clear the pending map as this window is now unmapped
win_clear_flags(w, WIN_FLAGS_MAPPED);
} else {
log_warn("Trying to unmapping an already unmapped window %#010x "
log_warn("Trying to unmapping an already unmapped window "
"%#010x "
"\"%s\"",
w->base.id, w->name);
assert(false);
@ -2333,10 +2380,10 @@ void unmap_win_start(session_t *ps, struct managed_win *w) {
#endif
if (!ps->redirected || !was_damaged) {
// If we are not redirected, we skip fading because we aren't rendering
// anything anyway.
// If the window wasn't ever damaged, it shouldn't be painted either. But
// a fading out window is always painted, so we have to skip fading here.
// If we are not redirected, we skip fading because we aren't
// rendering anything anyway. If the window wasn't ever damaged,
// it shouldn't be painted either. But a fading out window is
// always painted, so we have to skip fading here.
CHECK(!win_skip_fading(ps, w));
}
}
@ -2395,14 +2442,16 @@ void win_update_screen(int nscreens, region_t *screens, struct managed_win *w) {
if (e->x1 <= w->g.x && e->y1 <= w->g.y && e->x2 >= w->g.x + w->widthb &&
e->y2 >= w->g.y + w->heightb) {
w->xinerama_scr = i;
log_debug("Window %#010x (%s), %dx%d+%dx%d, is on screen %d "
log_debug("Window %#010x (%s), %dx%d+%dx%d, is on screen "
"%d "
"(%dx%d+%dx%d)",
w->base.id, w->name, w->g.x, w->g.y, w->widthb, w->heightb,
i, e->x1, e->y1, e->x2 - e->x1, e->y2 - e->y1);
return;
}
}
log_debug("Window %#010x (%s), %dx%d+%dx%d, is not contained by any screen",
log_debug("Window %#010x (%s), %dx%d+%dx%d, is not contained by any "
"screen",
w->base.id, w->name, w->g.x, w->g.y, w->g.width, w->g.height);
}
@ -2427,23 +2476,24 @@ void map_win_start(session_t *ps, struct managed_win *w) {
if (w->state == WSTATE_UNMAPPING) {
CHECK(!win_skip_fading(ps, w));
// We skipped the unmapping process, the window was rendered, now it is
// not anymore. So we need to mark the then unmapping window as damaged.
// We skipped the unmapping process, the window was rendered, now
// it is not anymore. So we need to mark the then unmapping window
// as damaged.
//
// Solves problem when, for example, a window is unmapped then mapped in a
// different location
// Solves problem when, for example, a window is unmapped then
// mapped in a different location
add_damage_from_win(ps, w);
assert(w);
}
assert(w->state == WSTATE_UNMAPPED);
// Rant: window size could change after we queried its geometry here and before
// we get its pixmap. Later, when we get back to the event processing loop, we
// will get the notification about size change from Xserver and try to refresh the
// pixmap, while the pixmap is actually already up-to-date (i.e. the notification
// is stale). There is basically no real way to prevent this, aside from grabbing
// the server.
// Rant: window size could change after we queried its geometry here and
// before we get its pixmap. Later, when we get back to the event
// processing loop, we will get the notification about size change from
// Xserver and try to refresh the pixmap, while the pixmap is actually
// already up-to-date (i.e. the notification is stale). There is basically
// no real way to prevent this, aside from grabbing the server.
// XXX Can we assume map_state is always viewable?
w->a.map_state = XCB_MAP_STATE_VIEWABLE;
@ -2463,9 +2513,9 @@ void map_win_start(session_t *ps, struct managed_win *w) {
w->opacity, w->opacity_target);
// Cannot set w->ever_damaged = false here, since window mapping could be
// delayed, so a damage event might have already arrived before this function
// is called. But this should be unnecessary in the first place, since
// ever_damaged is set to false in unmap_win_finish anyway.
// delayed, so a damage event might have already arrived before this
// function is called. But this should be unnecessary in the first place,
// since ever_damaged is set to false in unmap_win_finish anyway.
// Sets the WIN_FLAGS_IMAGES_STALE flag so later in the critical section
// the window's image will be bound
@ -2600,17 +2650,17 @@ struct managed_win *find_toplevel(session_t *ps, xcb_window_t id) {
* @return struct _win object of the found window, NULL if not found
*/
struct managed_win *find_managed_window_or_parent(session_t *ps, xcb_window_t wid) {
// TODO(yshui) this should probably be an "update tree", then find_toplevel.
// current approach is a bit more "racy", as the server state might be ahead of
// our state
// TODO(yshui) this should probably be an "update tree", then
// find_toplevel. current approach is a bit more "racy", as the server
// state might be ahead of our state
struct win *w = NULL;
// We traverse through its ancestors to find out the frame
// Using find_win here because if we found a unmanaged window we know about, we
// can stop early.
// Using find_win here because if we found a unmanaged window we know
// about, we can stop early.
while (wid && wid != ps->root && !(w = find_win(ps, wid))) {
// xcb_query_tree probably fails if you run picom when X is somehow
// initializing (like add it in .xinitrc). In this case
// xcb_query_tree probably fails if you run picom when X is
// somehow initializing (like add it in .xinitrc). In this case
// just leave it alone.
auto reply = xcb_query_tree_reply(ps->c, xcb_query_tree(ps->c, wid), NULL);
if (reply == NULL) {
@ -2776,7 +2826,8 @@ bool win_is_bypassing_compositor(const session_t *ps, const struct managed_win *
}
/**
* Check if a window is focused, without using any focus rules or forced focus settings
* Check if a window is focused, without using any focus rules or forced focus
* settings
*/
bool win_is_focused_raw(const session_t *ps, const struct managed_win *w) {
return w->a.map_state == XCB_MAP_STATE_VIEWABLE && ps->active_win == w;

View File

@ -289,6 +289,7 @@ struct managed_win {
/// section
void win_process_update_flags(session_t *ps, struct managed_win *w);
void win_process_image_flags(session_t *ps, struct managed_win *w);
bool win_bind_mask(struct backend_base *b, struct managed_win *w);
/// Bind a shadow to the window, with color `c` and shadow kernel `kernel`
bool win_bind_shadow(struct backend_base *b, struct managed_win *w, struct color c,
struct backend_shadow_context *kernel);