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
This commit is contained in:
Yuxuan Shui 2020-04-10 01:44:33 +01:00
parent b1b40ed058
commit 440157f20b
No known key found for this signature in database
GPG Key ID: 37C999F617EA1A47
3 changed files with 47 additions and 29 deletions

View File

@ -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, ps->c, ev->window, XCB_CW_EVENT_MASK,
(const uint32_t[]){determine_evmask(ps, ev->window, WIN_EVMODE_UNKNOWN)}); (const uint32_t[]){determine_evmask(ps, ev->window, WIN_EVMODE_UNKNOWN)});
// Check if the window is an undetected client window if (!wid_has_prop(ps, ev->window, ps->atoms->aWM_STATE)) {
// Firstly, check if it's a known client window log_debug("Window %#010x doesn't have WM_STATE property, it is "
if (!w_top) { "probably not a client window. But we will listen for "
// If not, look for its frame window "property change in case it gains one.",
auto w_real_top = find_managed_window_or_parent(ps, ev->parent); ev->window);
// 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
else {
xcb_change_window_attributes( xcb_change_window_attributes(
ps->c, ev->window, XCB_CW_EVENT_MASK, ps->c, ev->window, XCB_CW_EVENT_MASK,
(const uint32_t[]){ (const uint32_t[]){determine_evmask(ps, ev->window, WIN_EVMODE_UNKNOWN) |
determine_evmask(ps, ev->window, WIN_EVMODE_UNKNOWN) |
XCB_EVENT_MASK_PROPERTY_CHANGE}); XCB_EVENT_MASK_PROPERTY_CHANGE});
} else {
auto w_real_top = find_managed_window_or_parent(ps, ev->parent);
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 {
// 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)}); ps, ev->window, WIN_EVMODE_UNKNOWN)});
auto w_top = find_managed_window_or_parent(ps, ev->window); auto w_top = find_managed_window_or_parent(ps, ev->window);
// Initialize client_win as early as possible // ev->window might have not been managed yet, in that case w_top
if (w_top && // would be NULL.
(!w_top->client_win || w_top->client_win == w_top->base.id) && if (w_top) {
wid_has_prop(ps, ev->window, ps->atoms->aWM_STATE)) { win_set_flags(w_top, WIN_FLAGS_CLIENT_STALE);
w_top->wmwin = false; ps->pending_updates = true;
win_unmark_client(ps, w_top);
win_mark_client(ps, w_top, ev->window);
} }
} }
} }

View File

@ -53,6 +53,8 @@ static const int WIN_GET_LEADER_MAX_RECURSION = 20;
static const int ROUNDED_PIXELS = 1; static const int ROUNDED_PIXELS = 1;
static const double ROUNDED_PERCENT = 0.05; 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 /// Generate a "return by value" function, from a function that returns the
/// region via a region_t pointer argument. /// region via a region_t pointer argument.
/// Function signature has to be (win *, region_t *) /// 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) { void win_process_flags(session_t *ps, struct managed_win *w) {
if (win_check_flags_all(w, WIN_FLAGS_MAPPED)) { if (win_check_flags_all(w, WIN_FLAGS_MAPPED)) {
map_win_start(ps, w); map_win_start(ps, w);
}
win_clear_flags(w, WIN_FLAGS_MAPPED); win_clear_flags(w, WIN_FLAGS_MAPPED);
}
// Not a loop // Not a loop
while (win_check_flags_any(w, WIN_FLAGS_IMAGES_STALE) && 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 // Clear stale image flags
if (win_check_flags_any(w, WIN_FLAGS_IMAGES_STALE)) {
win_clear_flags(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 /// Set flags on a window. Some sanity checks are performed
void win_set_flags(struct managed_win *w, uint64_t flags) { 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)) { if (unlikely(w->state == WSTATE_DESTROYING)) {
log_error("Flags set on a destroyed window %#010x (%s)", w->base.id, w->name); log_error("Flags set on a destroyed window %#010x (%s)", w->base.id, w->name);
return; 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 /// Clear flags on a window. Some sanity checks are performed
void win_clear_flags(struct managed_win *w, uint64_t flags) { 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)) { if (unlikely(w->state == WSTATE_DESTROYING)) {
log_error("Flags cleared on a destroyed window %#010x (%s)", w->base.id, log_error("Flags cleared on a destroyed window %#010x (%s)", w->base.id,
w->name); w->name);

View File

@ -81,6 +81,8 @@ enum win_flags {
WIN_FLAGS_SHADOW_STALE = 8, WIN_FLAGS_SHADOW_STALE = 8,
/// shadow has not been generated /// shadow has not been generated
WIN_FLAGS_SHADOW_NONE = 16, 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 /// the window is mapped by X, we need to call map_win_start for it
WIN_FLAGS_MAPPED = 64, WIN_FLAGS_MAPPED = 64,
}; };