From 440157f20b15ab01dd25cb7fe8f74d85bf4800a6 Mon Sep 17 00:00:00 2001 From: Yuxuan Shui Date: Fri, 10 Apr 2020 01:44:33 +0100 Subject: [PATCH] win, event: delayed handling of reparent notify Instead of handling reparent notify on the spot by updating the client windows, setup a flag on the window and call win_recheck_client later. This makes handling of complex scenarios easier. As example, see the case in issue #299. Note this is not a complete fix for #299 --- src/event.c | 59 +++++++++++++++++++++++++++----------------------- src/win.c | 15 +++++++++++-- src/win_defs.h | 2 ++ 3 files changed, 47 insertions(+), 29 deletions(-) diff --git a/src/event.c b/src/event.c index c6e1179..f21e346 100644 --- a/src/event.c +++ b/src/event.c @@ -336,28 +336,35 @@ static inline void ev_reparent_notify(session_t *ps, xcb_reparent_notify_event_t ps->c, ev->window, XCB_CW_EVENT_MASK, (const uint32_t[]){determine_evmask(ps, ev->window, WIN_EVMODE_UNKNOWN)}); - // Check if the window is an undetected client window - // Firstly, check if it's a known client window - if (!w_top) { - // If not, look for its frame window + if (!wid_has_prop(ps, ev->window, ps->atoms->aWM_STATE)) { + log_debug("Window %#010x doesn't have WM_STATE property, it is " + "probably not a client window. But we will listen for " + "property change in case it gains one.", + ev->window); + xcb_change_window_attributes( + ps->c, ev->window, XCB_CW_EVENT_MASK, + (const uint32_t[]){determine_evmask(ps, ev->window, WIN_EVMODE_UNKNOWN) | + XCB_EVENT_MASK_PROPERTY_CHANGE}); + } else { auto w_real_top = find_managed_window_or_parent(ps, ev->parent); - // If found, and the client window has not been determined, or its - // frame may not have a correct client, continue - if (w_real_top && (!w_real_top->client_win || - w_real_top->client_win == w_real_top->base.id)) { - // If it has WM_STATE, mark it the client window - if (wid_has_prop(ps, ev->window, ps->atoms->aWM_STATE)) { - w_real_top->wmwin = false; - win_unmark_client(ps, w_real_top); - win_mark_client(ps, w_real_top, ev->window); - } - // Otherwise, watch for WM_STATE on it + if (w_real_top && w_real_top->state != WSTATE_UNMAPPED && + w_real_top->state != WSTATE_UNMAPPING) { + log_debug("Mark window %#010x (%s) as having a stale " + "client", + w_real_top->base.id, w_real_top->name); + win_set_flags(w_real_top, WIN_FLAGS_CLIENT_STALE); + ps->pending_updates = true; + } else { + if (!w_real_top) + log_debug("parent %#010x not found", ev->parent); else { - xcb_change_window_attributes( - ps->c, ev->window, XCB_CW_EVENT_MASK, - (const uint32_t[]){ - determine_evmask(ps, ev->window, WIN_EVMODE_UNKNOWN) | - XCB_EVENT_MASK_PROPERTY_CHANGE}); + // Window is not currently mapped, unmark its + // client to trigger a client recheck when it is + // mapped later. + win_unmark_client(ps, w_real_top); + log_debug("parent %#010x (%s) is in state %d", + w_real_top->base.id, w_real_top->name, + w_real_top->state); } } } @@ -451,13 +458,11 @@ static inline void ev_property_notify(session_t *ps, xcb_property_notify_event_t ps, ev->window, WIN_EVMODE_UNKNOWN)}); auto w_top = find_managed_window_or_parent(ps, ev->window); - // Initialize client_win as early as possible - if (w_top && - (!w_top->client_win || w_top->client_win == w_top->base.id) && - wid_has_prop(ps, ev->window, ps->atoms->aWM_STATE)) { - w_top->wmwin = false; - win_unmark_client(ps, w_top); - win_mark_client(ps, w_top, ev->window); + // ev->window might have not been managed yet, in that case w_top + // would be NULL. + if (w_top) { + win_set_flags(w_top, WIN_FLAGS_CLIENT_STALE); + ps->pending_updates = true; } } } diff --git a/src/win.c b/src/win.c index 28d84ed..10f4424 100644 --- a/src/win.c +++ b/src/win.c @@ -53,6 +53,8 @@ static const int WIN_GET_LEADER_MAX_RECURSION = 20; static const int ROUNDED_PIXELS = 1; static const double ROUNDED_PERCENT = 0.05; +static void win_recheck_client(session_t *ps, struct managed_win *w); + /// Generate a "return by value" function, from a function that returns the /// region via a region_t pointer argument. /// Function signature has to be (win *, region_t *) @@ -323,8 +325,8 @@ void win_release_images(struct backend_base *backend, struct managed_win *w) { void win_process_flags(session_t *ps, struct managed_win *w) { if (win_check_flags_all(w, WIN_FLAGS_MAPPED)) { map_win_start(ps, w); + win_clear_flags(w, WIN_FLAGS_MAPPED); } - win_clear_flags(w, WIN_FLAGS_MAPPED); // Not a loop while (win_check_flags_any(w, WIN_FLAGS_IMAGES_STALE) && @@ -367,7 +369,14 @@ void win_process_flags(session_t *ps, struct managed_win *w) { } // Clear stale image flags - win_clear_flags(w, WIN_FLAGS_IMAGES_STALE); + if (win_check_flags_any(w, WIN_FLAGS_IMAGES_STALE)) { + win_clear_flags(w, WIN_FLAGS_IMAGES_STALE); + } + + if (win_check_flags_all(w, WIN_FLAGS_CLIENT_STALE)) { + win_recheck_client(ps, w); + win_clear_flags(w, WIN_FLAGS_CLIENT_STALE); + } } /** @@ -2326,6 +2335,7 @@ win_is_fullscreen_xcb(xcb_connection_t *c, const struct atom *a, const xcb_windo /// 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 %lu to window %#010x (%s)", flags, w->base.id, w->name); if (unlikely(w->state == WSTATE_DESTROYING)) { log_error("Flags set on a destroyed window %#010x (%s)", w->base.id, w->name); return; @@ -2336,6 +2346,7 @@ void win_set_flags(struct managed_win *w, uint64_t flags) { /// Clear flags on a window. Some sanity checks are performed void win_clear_flags(struct managed_win *w, uint64_t flags) { + log_debug("Clear flags %lu from window %#010x (%s)", flags, w->base.id, w->name); if (unlikely(w->state == WSTATE_DESTROYING)) { log_error("Flags cleared on a destroyed window %#010x (%s)", w->base.id, w->name); diff --git a/src/win_defs.h b/src/win_defs.h index f8494bb..6099ac2 100644 --- a/src/win_defs.h +++ b/src/win_defs.h @@ -81,6 +81,8 @@ enum win_flags { WIN_FLAGS_SHADOW_STALE = 8, /// shadow has not been generated WIN_FLAGS_SHADOW_NONE = 16, + /// the client window needs to be updated + WIN_FLAGS_CLIENT_STALE = 32, /// the window is mapped by X, we need to call map_win_start for it WIN_FLAGS_MAPPED = 64, };