From 71741d7620825c9176b17bdcbe21b3aac034970b Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Mon, 4 Jul 2011 13:41:02 +0200 Subject: [PATCH] Bugfix: Only set ENTER_WINDOW event mask for mapped windows (fixes focus problems) Fixes focus problems when switching to empty workspaces or when going in/out of fullscreen. --- src/handlers.c | 3 ++- src/manage.c | 9 +++------ src/x.c | 47 +++++++++++++++++++++++++++++++++++------------ 3 files changed, 40 insertions(+), 19 deletions(-) diff --git a/src/handlers.c b/src/handlers.c index b97dd043..583b0c5a 100644 --- a/src/handlers.c +++ b/src/handlers.c @@ -439,9 +439,10 @@ static int handle_screen_change(xcb_generic_event_t *e) { * */ static int handle_unmap_notify_event(xcb_unmap_notify_event_t *event) { + // XXX: this is commented out because in src/x.c we disable EnterNotify events /* we need to ignore EnterNotify events which will be generated because a * different window is visible now */ - add_ignore_event(event->sequence, XCB_ENTER_NOTIFY); + //add_ignore_event(event->sequence, XCB_ENTER_NOTIFY); DLOG("UnmapNotify for 0x%08x (received from 0x%08x), serial %d\n", event->window, event->event, event->sequence); Con *con = con_by_window_id(event->window); diff --git a/src/manage.c b/src/manage.c index e609501a..bcce8ab1 100644 --- a/src/manage.c +++ b/src/manage.c @@ -117,16 +117,14 @@ void manage_window(xcb_window_t window, xcb_get_window_attributes_cookie_t cooki goto out; } - uint32_t mask = 0; uint32_t values[1]; /* Set a temporary event mask for the new window, consisting only of * PropertyChange. We need to be notified of PropertyChanges because the * client can change its properties *after* we requested them but *before* * we actually reparented it and have set our final event mask. */ - mask = XCB_CW_EVENT_MASK; values[0] = XCB_EVENT_MASK_PROPERTY_CHANGE; - xcb_change_window_attributes(conn, window, mask, values); + xcb_change_window_attributes(conn, window, XCB_CW_EVENT_MASK, values); #define GET_PROPERTY(atom, len) xcb_get_property_unchecked(conn, false, window, atom, XCB_GET_PROPERTY_TYPE_ANY, 0, len) @@ -326,9 +324,8 @@ void manage_window(xcb_window_t window, xcb_get_window_attributes_cookie_t cooki goto out; } - mask = XCB_CW_EVENT_MASK; - values[0] = CHILD_EVENT_MASK; - xcb_change_window_attributes(conn, window, mask, values); + values[0] = CHILD_EVENT_MASK & ~XCB_EVENT_MASK_ENTER_WINDOW; + xcb_change_window_attributes(conn, window, XCB_CW_EVENT_MASK, values); xcb_flush(conn); reply = xcb_get_property_reply(conn, state_cookie, NULL); diff --git a/src/x.c b/src/x.c index 64f5394b..1201fb97 100644 --- a/src/x.c +++ b/src/x.c @@ -18,6 +18,7 @@ xcb_window_t focused_id = XCB_NONE; typedef struct con_state { xcb_window_t id; bool mapped; + bool unmap_now; bool child_mapped; /* For reparenting, we have a flag (need_reparent) and the X ID of the old @@ -78,9 +79,9 @@ void x_con_init(Con *con) { mask |= XCB_CW_OVERRIDE_REDIRECT; values[0] = 1; - /* We want to know when… */ + /* see include/xcb.h for the FRAME_EVENT_MASK */ mask |= XCB_CW_EVENT_MASK; - values[1] = FRAME_EVENT_MASK; + values[1] = FRAME_EVENT_MASK & ~XCB_EVENT_MASK_ENTER_WINDOW; Rect dims = { -15, -15, 10, 10 }; con->frame = create_window(conn, dims, XCB_WINDOW_CLASS_INPUT_OUTPUT, XCURSOR_CURSOR_POINTER, false, mask, values); @@ -612,21 +613,29 @@ void x_push_node(Con *con) { A_WM_STATE, A_WM_STATE, 32, 2, data); } + uint32_t values[1]; if (!state->child_mapped && con->window != NULL) { cookie = xcb_map_window(conn, con->window->id); + + /* We are interested in EnterNotifys as soon as the window is + * mapped */ + values[0] = CHILD_EVENT_MASK; + xcb_change_window_attributes(conn, con->window->id, XCB_CW_EVENT_MASK, values); DLOG("mapping child window (serial %d)\n", cookie.sequence); - /* Ignore enter_notifies which are generated when mapping */ - add_ignore_event(cookie.sequence, 0); state->child_mapped = true; } cookie = xcb_map_window(conn, con->frame); - DLOG("mapping container (serial %d)\n", cookie.sequence); - /* Ignore enter_notifies which are generated when mapping */ - add_ignore_event(cookie.sequence, 0); + + values[0] = FRAME_EVENT_MASK; + xcb_change_window_attributes(conn, con->frame, XCB_CW_EVENT_MASK, values); + + DLOG("mapping container %08x (serial %d)\n", con->frame, cookie.sequence); state->mapped = con->mapped; } + state->unmap_now = (state->mapped != con->mapped) && !con->mapped; + if (fake_notify) { DLOG("Sending fake configure notify\n"); fake_absolute_configure_notify(con); @@ -658,8 +667,7 @@ static void x_push_node_unmaps(Con *con) { /* map/unmap if map state changed, also ensure that the child window * is changed if we are mapped *and* in initial state (meaning the * container was empty before, but now got a child) */ - if ((state->mapped != con->mapped || (con->mapped && state->initial)) && - !con->mapped) { + if (state->unmap_now) { xcb_void_cookie_t cookie; if (con->window != NULL) { /* Set WM_STATE_WITHDRAWN, it seems like Java apps need it */ @@ -677,8 +685,6 @@ static void x_push_node_unmaps(Con *con) { con->ignore_unmap++; DLOG("ignore_unmap for con %p (frame 0x%08x) now %d\n", con, con->frame, con->ignore_unmap); } - /* Ignore enter_notifies which are generated when unmapping */ - add_ignore_event(cookie.sequence, 0); state->mapped = con->mapped; } @@ -731,8 +737,10 @@ void x_push_changes(Con *con) { state->initial = false; } //DLOG("Re-enabling EnterNotify\n"); - values[0] = FRAME_EVENT_MASK; CIRCLEQ_FOREACH_REVERSE(state, &state_head, state) { + values[0] = FRAME_EVENT_MASK; + if (!state->mapped) + values[0] &= ~XCB_EVENT_MASK_ENTER_WINDOW; xcb_change_window_attributes(conn, state->id, XCB_CW_EVENT_MASK, values); } //DLOG("Done, EnterNotify re-enabled\n"); @@ -785,6 +793,21 @@ void x_push_changes(Con *con) { xcb_flush(conn); DLOG("\n\n ENDING CHANGES\n\n"); + /* Disable EnterWindow events for windows which will be unmapped in + * x_push_node_unmaps() now. Unmapping windows happens when switching + * workspaces. We want to avoid getting EnterNotifies during that phase + * because they would screw up our focus. One of these cases is having a + * stack with two windows. If the first window is focused and gets + * unmapped, the second one appears under the cursor and therefore gets an + * EnterNotify event. */ + values[0] = FRAME_EVENT_MASK & ~XCB_EVENT_MASK_ENTER_WINDOW; + CIRCLEQ_FOREACH_REVERSE(state, &state_head, state) { + if (!state->unmap_now) + continue; + xcb_change_window_attributes(conn, state->id, XCB_CW_EVENT_MASK, values); + } + + /* Push all pending unmaps */ x_push_node_unmaps(con); /* save the current stack as old stack */