]> git.sur5r.net Git - i3/i3/commitdiff
Bugfix: Only set ENTER_WINDOW event mask for mapped windows (fixes focus problems)
authorMichael Stapelberg <michael@stapelberg.de>
Mon, 4 Jul 2011 11:41:02 +0000 (13:41 +0200)
committerMichael Stapelberg <michael@stapelberg.de>
Mon, 4 Jul 2011 11:41:02 +0000 (13:41 +0200)
Fixes focus problems when switching to empty workspaces or when going in/out of
fullscreen.

src/handlers.c
src/manage.c
src/x.c

index b97dd043ad88397338e53625ed263ed90c3575c0..583b0c5a46443ab45c0be6be14c94a7f64eb0ebc 100644 (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) {
+    // 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);
index e609501a46d324dda80eaa105472599754db7f1c..bcce8ab1fc502fb1779ec1f4147b59ec1a9fc893 100644 (file)
@@ -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 64f5394b62a17613271aac83dec180220e878dce..1201fb9700f60eb371fc1a6a079fa2a699586ff4 100644 (file)
--- 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 */