From 84b9ff31487b302a389e87382a860f025497af1e Mon Sep 17 00:00:00 2001 From: Yuxuan Shui Date: Wed, 14 Feb 2024 23:15:26 +0000 Subject: [PATCH 1/3] x: support getting winprop_t items as atoms Signed-off-by: Yuxuan Shui --- src/x.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/x.h b/src/x.h index cd4805d..f465b22 100644 --- a/src/x.h +++ b/src/x.h @@ -27,6 +27,7 @@ typedef struct winprop { int16_t *p16; int32_t *p32; uint32_t *c32; // 32bit cardinal + xcb_atom_t *atom; }; unsigned long nitems; xcb_atom_t type; From 613d179f2d66f040c7843fb4c25a725396ec9d23 Mon Sep 17 00:00:00 2001 From: Yuxuan Shui Date: Wed, 14 Feb 2024 18:38:20 +0000 Subject: [PATCH 2/3] win: cache the EWMH fullscreen property Signed-off-by: Yuxuan Shui --- src/event.c | 7 ++++++ src/win.c | 67 +++++++++++++++++++++++++++++------------------------ src/win.h | 2 ++ 3 files changed, 46 insertions(+), 30 deletions(-) diff --git a/src/event.c b/src/event.c index a1b93fd..17374c3 100644 --- a/src/event.c +++ b/src/event.c @@ -565,6 +565,13 @@ static inline void ev_property_notify(session_t *ps, xcb_property_notify_event_t } } + if (!ps->o.no_ewmh_fullscreen && ev->atom == ps->atoms->a_NET_WM_STATE) { + auto w = find_toplevel(ps, ev->window); + if (w) { + win_set_property_stale(w, ev->atom); + } + } + // Check for other atoms we are tracking for (latom_t *platom = ps->track_atom_lst; platom; platom = platom->next) { if (platom->atom == ev->atom) { diff --git a/src/win.c b/src/win.c index 81660c8..0a1bd81 100644 --- a/src/win.c +++ b/src/win.c @@ -73,6 +73,11 @@ static void win_update_frame_extents(session_t *ps, struct managed_win *w, xcb_window_t client); static void win_update_prop_shadow_raw(session_t *ps, struct managed_win *w); static void win_update_prop_shadow(session_t *ps, struct managed_win *w); +/** + * Update window EWMH fullscreen state. + */ +bool win_update_prop_fullscreen(struct x_connection *c, const struct atom *atoms, + struct managed_win *w); /** * Update leader of a window. */ @@ -457,6 +462,12 @@ static void win_update_properties(session_t *ps, struct managed_win *w) { win_update_prop_shadow(ps, w); } + if (win_fetch_and_unset_property_stale(w, ps->atoms->a_NET_WM_STATE)) { + if (win_update_prop_fullscreen(&ps->c, ps->atoms, w)) { + win_set_flags(w, WIN_FLAGS_FACTOR_CHANGED); + } + } + if (win_fetch_and_unset_property_stale(w, ps->atoms->aWM_CLIENT_LEADER) || win_fetch_and_unset_property_stale(w, ps->atoms->aWM_TRANSIENT_FOR)) { win_update_leader(ps, w); @@ -1012,6 +1023,30 @@ void win_update_prop_shadow(session_t *ps, struct managed_win *w) { } } +/** + * Update window EWMH fullscreen state. + */ +bool win_update_prop_fullscreen(struct x_connection *c, const struct atom *atoms, + struct managed_win *w) { + auto prop = x_get_prop(c, w->client_win, atoms->a_NET_WM_STATE, 12, XCB_ATOM_ATOM, 0); + if (!prop.nitems) { + return false; + } + + bool is_fullscreen = false; + for (uint32_t i = 0; i < prop.nitems; i++) { + if (prop.atom[i] == atoms->a_NET_WM_STATE_FULLSCREEN) { + is_fullscreen = true; + break; + } + } + free_winprop(&prop); + + bool changed = w->is_ewmh_fullscreen != is_fullscreen; + w->is_ewmh_fullscreen = is_fullscreen; + return changed; +} + 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)); @@ -1723,6 +1758,7 @@ struct win *fill_win(session_t *ps, struct win *w) { ps->atoms->a_NET_WM_NAME, ps->atoms->aWM_CLASS, ps->atoms->aWM_WINDOW_ROLE, ps->atoms->a_COMPTON_SHADOW, ps->atoms->aWM_CLIENT_LEADER, ps->atoms->aWM_TRANSIENT_FOR, + ps->atoms->a_NET_WM_STATE, }; win_set_properties_stale(new, init_stale_props, ARR_SIZE(init_stale_props)); @@ -2717,34 +2753,6 @@ static inline bool rect_is_fullscreen(const session_t *ps, int x, int y, int wid return (x <= 0 && y <= 0 && (x + wid) >= ps->root_width && (y + hei) >= ps->root_height); } -/** - * Check if a window is full-screen using EWMH - * - * TODO(yshui) cache this property - */ -static inline bool -win_is_fullscreen_xcb(xcb_connection_t *c, const struct atom *a, const xcb_window_t w) { - xcb_get_property_cookie_t prop = - xcb_get_property(c, 0, w, a->a_NET_WM_STATE, XCB_ATOM_ATOM, 0, 12); - xcb_get_property_reply_t *reply = xcb_get_property_reply(c, prop, NULL); - if (!reply) { - return false; - } - - if (reply->length) { - xcb_atom_t *val = xcb_get_property_value(reply); - for (uint32_t i = 0; i < reply->length; i++) { - if (val[i] != a->a_NET_WM_STATE_FULLSCREEN) { - continue; - } - free(reply); - return true; - } - } - free(reply); - return false; -} - /// Set flags on a window. Some sanity checks are performed void win_set_flags(struct managed_win *w, uint64_t flags) { log_debug("Set flags %" PRIu64 " to window %#010x (%s)", flags, w->base.id, w->name); @@ -2830,8 +2838,7 @@ bool win_check_flags_all(struct managed_win *w, uint64_t flags) { * It's not using w->border_size for performance measures. */ bool win_is_fullscreen(const session_t *ps, const struct managed_win *w) { - if (!ps->o.no_ewmh_fullscreen && - win_is_fullscreen_xcb(ps->c.c, ps->atoms, w->client_win)) { + if (!ps->o.no_ewmh_fullscreen && w->is_ewmh_fullscreen) { return true; } return rect_is_fullscreen(ps, w->g.x, w->g.y, w->widthb, w->heightb) && diff --git a/src/win.h b/src/win.h index c45c7ef..acc96c0 100644 --- a/src/win.h +++ b/src/win.h @@ -199,6 +199,8 @@ struct managed_win { char *class_general; /// WM_WINDOW_ROLE value of the window. char *role; + /// Whether the window sets the EWMH fullscreen property. + bool is_ewmh_fullscreen; // Opacity-related members /// Current window opacity. From 05b1fbff9e40bc3d4e295d5e4320c55c8e7cd3bd Mon Sep 17 00:00:00 2001 From: Yuxuan Shui Date: Wed, 14 Feb 2024 18:54:04 +0000 Subject: [PATCH 3/3] win: remember calculated fullscreen state for window Signed-off-by: Yuxuan Shui --- src/c2.c | 4 +--- src/picom.c | 11 ++++++++++- src/win.c | 28 ++++++++++++++-------------- src/win.h | 13 ++++++------- 4 files changed, 31 insertions(+), 25 deletions(-) diff --git a/src/c2.c b/src/c2.c index 7f1f80e..daee18f 100644 --- a/src/c2.c +++ b/src/c2.c @@ -1380,9 +1380,7 @@ static inline void c2_match_once_leaf(session_t *ps, const struct managed_win *w case C2_L_PWIDTHB: predef_target = w->widthb; break; case C2_L_PHEIGHTB: predef_target = w->heightb; break; case C2_L_PBDW: predef_target = w->g.border_width; break; - case C2_L_PFULLSCREEN: - predef_target = win_is_fullscreen(ps, w); - break; + case C2_L_PFULLSCREEN: predef_target = w->is_fullscreen; break; case C2_L_POVREDIR: predef_target = w->a.override_redirect; break; case C2_L_PARGB: predef_target = win_has_alpha(w); break; case C2_L_PFOCUSED: diff --git a/src/picom.c b/src/picom.c index 6c89e97..c024dd4 100644 --- a/src/picom.c +++ b/src/picom.c @@ -791,6 +791,8 @@ err: /// Handle configure event of the root window static void configure_root(session_t *ps) { + // TODO(yshui) re-initializing backend should be done outside of the + // critical section. Probably set a flag and do it in draw_callback_impl. auto r = XCB_AWAIT(xcb_get_geometry, ps->c.c, ps->c.screen_info->root); if (!r) { log_fatal("Failed to fetch root geometry"); @@ -830,6 +832,13 @@ static void configure_root(session_t *ps) { top_w->reg_ignore_valid = false; } + // Whether a window is fullscreen depends on the new screen + // size. So we need to refresh the fullscreen state of all + // windows. + win_stack_foreach_managed(w, &ps->window_stack) { + win_update_is_fullscreen(ps, w); + } + if (ps->redirected) { for (int i = 0; i < ps->ndamage; i++) { pixman_region32_clear(&ps->damage_ring[i]); @@ -1072,7 +1081,7 @@ static bool paint_preprocess(session_t *ps, bool *fade_running, bool *animation, // is not correctly set. if (ps->o.unredir_if_possible && is_highest) { if (w->mode == WMODE_SOLID && !ps->o.force_win_blend && - win_is_fullscreen(ps, w) && !w->unredir_if_possible_excluded) { + w->is_fullscreen && !w->unredir_if_possible_excluded) { unredir_possible = true; } } diff --git a/src/win.c b/src/win.c index 0a1bd81..aafbe02 100644 --- a/src/win.c +++ b/src/win.c @@ -520,6 +520,9 @@ void win_process_update_flags(session_t *ps, struct managed_win *w) { // Update window geometry w->g = w->pending_g; + // Whether a window is fullscreen changes based on its geometry + win_update_is_fullscreen(ps, w); + if (win_check_flags_all(w, WIN_FLAGS_SIZE_STALE)) { win_on_win_size_change(ps, w); win_update_bounding_shape(ps, w); @@ -1187,7 +1190,7 @@ static void win_determine_rounded_corners(session_t *ps, struct managed_win *w) // Don't round full screen windows & excluded windows, // unless we find a corner override in corner_radius_rules - if (!radius_override && ((w && win_is_fullscreen(ps, w)) || + if (!radius_override && ((w && w->is_fullscreen) || c2_match(ps, w, ps->o.rounded_corners_blacklist, NULL))) { w->corner_radius = 0; log_debug("Not rounding corners for window %#010x", w->base.id); @@ -1254,9 +1257,10 @@ 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 and is_fullscreen needs to be updated first, as other rules might depend + // on the focused state of the window win_update_focused(ps, w); + win_update_is_fullscreen(ps, w); win_determine_shadow(ps, w); win_determine_clip_shadow_above(ps, w); @@ -2746,13 +2750,6 @@ struct managed_win *find_managed_window_or_parent(session_t *ps, xcb_window_t wi return (struct managed_win *)w; } -/** - * Check if a rectangle includes the whole screen. - */ -static inline bool rect_is_fullscreen(const session_t *ps, int x, int y, int wid, int hei) { - return (x <= 0 && y <= 0 && (x + wid) >= ps->root_width && (y + hei) >= ps->root_height); -} - /// Set flags on a window. Some sanity checks are performed void win_set_flags(struct managed_win *w, uint64_t flags) { log_debug("Set flags %" PRIu64 " to window %#010x (%s)", flags, w->base.id, w->name); @@ -2837,12 +2834,15 @@ bool win_check_flags_all(struct managed_win *w, uint64_t flags) { * * It's not using w->border_size for performance measures. */ -bool win_is_fullscreen(const session_t *ps, const struct managed_win *w) { +void win_update_is_fullscreen(const session_t *ps, struct managed_win *w) { if (!ps->o.no_ewmh_fullscreen && w->is_ewmh_fullscreen) { - return true; + w->is_fullscreen = true; + return; } - return rect_is_fullscreen(ps, w->g.x, w->g.y, w->widthb, w->heightb) && - (!w->bounding_shaped || w->rounded_corners); + w->is_fullscreen = w->g.x <= 0 && w->g.y <= 0 && + (w->g.x + w->widthb) >= ps->root_width && + (w->g.y + w->heightb) >= ps->root_height && + (!w->bounding_shaped || w->rounded_corners); } /** diff --git a/src/win.h b/src/win.h index acc96c0..f98cb9a 100644 --- a/src/win.h +++ b/src/win.h @@ -201,6 +201,10 @@ struct managed_win { char *role; /// Whether the window sets the EWMH fullscreen property. bool is_ewmh_fullscreen; + /// Whether the window should be considered fullscreen. Based on + /// `is_ewmh_fullscreen`, or the windows spatial relation with the + /// root window. Which one is used is determined by user configuration. + bool is_fullscreen; // Opacity-related members /// Current window opacity. @@ -351,6 +355,8 @@ void win_update_monitor(struct x_monitors *monitors, struct managed_win *mw); */ // XXX was win_border_size void win_update_bounding_shape(session_t *ps, struct managed_win *w); +/// Recheck if a window is fullscreen +void win_update_is_fullscreen(const session_t *ps, struct managed_win *w); /** * Check if a window has BYPASS_COMPOSITOR property set */ @@ -426,13 +432,6 @@ struct managed_win *find_toplevel(session_t *ps, xcb_window_t id); */ struct managed_win *find_managed_window_or_parent(session_t *ps, xcb_window_t wid); -/** - * Check if a window is a fullscreen window. - * - * It's not using w->border_size for performance measures. - */ -bool attr_pure win_is_fullscreen(const session_t *ps, const struct managed_win *w); - /** * Check if a window is focused, without using any focus rules or forced focus settings */