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.
This commit is contained in:
Michael Stapelberg 2011-07-04 13:41:02 +02:00
parent 23d4917e43
commit 71741d7620
3 changed files with 40 additions and 19 deletions

View File

@ -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) { 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 /* we need to ignore EnterNotify events which will be generated because a
* different window is visible now */ * 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); 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); Con *con = con_by_window_id(event->window);

View File

@ -117,16 +117,14 @@ void manage_window(xcb_window_t window, xcb_get_window_attributes_cookie_t cooki
goto out; goto out;
} }
uint32_t mask = 0;
uint32_t values[1]; uint32_t values[1];
/* Set a temporary event mask for the new window, consisting only of /* Set a temporary event mask for the new window, consisting only of
* PropertyChange. We need to be notified of PropertyChanges because the * PropertyChange. We need to be notified of PropertyChanges because the
* client can change its properties *after* we requested them but *before* * client can change its properties *after* we requested them but *before*
* we actually reparented it and have set our final event mask. */ * we actually reparented it and have set our final event mask. */
mask = XCB_CW_EVENT_MASK;
values[0] = XCB_EVENT_MASK_PROPERTY_CHANGE; 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) #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; goto out;
} }
mask = XCB_CW_EVENT_MASK; values[0] = CHILD_EVENT_MASK & ~XCB_EVENT_MASK_ENTER_WINDOW;
values[0] = CHILD_EVENT_MASK; xcb_change_window_attributes(conn, window, XCB_CW_EVENT_MASK, values);
xcb_change_window_attributes(conn, window, mask, values);
xcb_flush(conn); xcb_flush(conn);
reply = xcb_get_property_reply(conn, state_cookie, NULL); reply = xcb_get_property_reply(conn, state_cookie, NULL);

47
src/x.c
View File

@ -18,6 +18,7 @@ xcb_window_t focused_id = XCB_NONE;
typedef struct con_state { typedef struct con_state {
xcb_window_t id; xcb_window_t id;
bool mapped; bool mapped;
bool unmap_now;
bool child_mapped; bool child_mapped;
/* For reparenting, we have a flag (need_reparent) and the X ID of the old /* 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; mask |= XCB_CW_OVERRIDE_REDIRECT;
values[0] = 1; values[0] = 1;
/* We want to know when… */ /* see include/xcb.h for the FRAME_EVENT_MASK */
mask |= XCB_CW_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 }; Rect dims = { -15, -15, 10, 10 };
con->frame = create_window(conn, dims, XCB_WINDOW_CLASS_INPUT_OUTPUT, XCURSOR_CURSOR_POINTER, false, mask, values); 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); A_WM_STATE, A_WM_STATE, 32, 2, data);
} }
uint32_t values[1];
if (!state->child_mapped && con->window != NULL) { if (!state->child_mapped && con->window != NULL) {
cookie = xcb_map_window(conn, con->window->id); 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); 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; state->child_mapped = true;
} }
cookie = xcb_map_window(conn, con->frame); cookie = xcb_map_window(conn, con->frame);
DLOG("mapping container (serial %d)\n", cookie.sequence);
/* Ignore enter_notifies which are generated when mapping */ values[0] = FRAME_EVENT_MASK;
add_ignore_event(cookie.sequence, 0); 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->mapped = con->mapped;
} }
state->unmap_now = (state->mapped != con->mapped) && !con->mapped;
if (fake_notify) { if (fake_notify) {
DLOG("Sending fake configure notify\n"); DLOG("Sending fake configure notify\n");
fake_absolute_configure_notify(con); 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 /* map/unmap if map state changed, also ensure that the child window
* is changed if we are mapped *and* in initial state (meaning the * is changed if we are mapped *and* in initial state (meaning the
* container was empty before, but now got a child) */ * container was empty before, but now got a child) */
if ((state->mapped != con->mapped || (con->mapped && state->initial)) && if (state->unmap_now) {
!con->mapped) {
xcb_void_cookie_t cookie; xcb_void_cookie_t cookie;
if (con->window != NULL) { if (con->window != NULL) {
/* Set WM_STATE_WITHDRAWN, it seems like Java apps need it */ /* 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++; con->ignore_unmap++;
DLOG("ignore_unmap for con %p (frame 0x%08x) now %d\n", con, con->frame, 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; state->mapped = con->mapped;
} }
@ -731,8 +737,10 @@ void x_push_changes(Con *con) {
state->initial = false; state->initial = false;
} }
//DLOG("Re-enabling EnterNotify\n"); //DLOG("Re-enabling EnterNotify\n");
values[0] = FRAME_EVENT_MASK;
CIRCLEQ_FOREACH_REVERSE(state, &state_head, state) { 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); xcb_change_window_attributes(conn, state->id, XCB_CW_EVENT_MASK, values);
} }
//DLOG("Done, EnterNotify re-enabled\n"); //DLOG("Done, EnterNotify re-enabled\n");
@ -785,6 +793,21 @@ void x_push_changes(Con *con) {
xcb_flush(conn); xcb_flush(conn);
DLOG("\n\n ENDING CHANGES\n\n"); 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); x_push_node_unmaps(con);
/* save the current stack as old stack */ /* save the current stack as old stack */