]> git.sur5r.net Git - i3/i3/commitdiff
Bugfix: unmap windows in a separate step to avoid focus problems with fullscreen...
authorMichael Stapelberg <michael@stapelberg.de>
Sat, 27 Nov 2010 16:45:23 +0000 (17:45 +0100)
committerMichael Stapelberg <michael@stapelberg.de>
Sat, 27 Nov 2010 16:45:23 +0000 (17:45 +0100)
This fixes an ugly bug with Adobe Flash in fullscreen mode, for example on
YouTube. See comments in the diff for some explanation.

src/x.c

diff --git a/src/x.c b/src/x.c
index 88463b1aa4fa62370dc6bb86fb5f0a0089ae4a0e..846591001e925baead191e8a73e6eb5190116853 100644 (file)
--- a/src/x.c
+++ b/src/x.c
@@ -454,51 +454,33 @@ static void x_push_node(Con *con) {
         fake_notify = true;
     }
 
-    /* map/unmap if map state changed, also ensure that the child window
+    /* Map 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)) {
-        if (!con->mapped) {
-            xcb_void_cookie_t cookie;
-            if (con->window != NULL) {
-                /* Set WM_STATE_WITHDRAWN, it seems like Java apps need it */
-                long data[] = { XCB_WM_STATE_WITHDRAWN, XCB_NONE };
-                xcb_change_property(conn, XCB_PROP_MODE_REPLACE, con->window->id,
-                                    atoms[WM_STATE], atoms[WM_STATE], 32, 2, data);
-            }
-
-            cookie = xcb_unmap_window(conn, con->frame);
-            LOG("unmapping container (serial %d)\n", cookie.sequence);
-            /* we need to increase ignore_unmap for this container (if it contains a window) and for every window "under" this one which contains a window */
-            if (con->window != NULL) {
-                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);
-        } else {
-            xcb_void_cookie_t cookie;
-
-            if (con->window != NULL) {
-                /* Set WM_STATE_NORMAL because GTK applications don’t want to
-                 * drag & drop if we don’t. Also, xprop(1) needs it. */
-                long data[] = { XCB_WM_STATE_NORMAL, XCB_NONE };
-                xcb_change_property(conn, XCB_PROP_MODE_REPLACE, con->window->id,
-                                    atoms[WM_STATE], atoms[WM_STATE], 32, 2, data);
-            }
-
-            if (state->initial && con->window != NULL) {
-                cookie = xcb_map_window(conn, con->window->id);
-                LOG("mapping child window (serial %d)\n", cookie.sequence);
-                /* Ignore enter_notifies which are generated when mapping */
-                add_ignore_event(cookie.sequence);
-            }
+     * container was empty before, but now got a child). Unmaps are handled in
+     * x_push_node_unmaps(). */
+    if ((state->mapped != con->mapped || (con->mapped && state->initial)) &&
+        con->mapped) {
+        xcb_void_cookie_t cookie;
+
+        if (con->window != NULL) {
+            /* Set WM_STATE_NORMAL because GTK applications don’t want to
+             * drag & drop if we don’t. Also, xprop(1) needs it. */
+            long data[] = { XCB_WM_STATE_NORMAL, XCB_NONE };
+            xcb_change_property(conn, XCB_PROP_MODE_REPLACE, con->window->id,
+                                atoms[WM_STATE], atoms[WM_STATE], 32, 2, data);
+        }
 
-            cookie = xcb_map_window(conn, con->frame);
-            LOG("mapping container (serial %d)\n", cookie.sequence);
+        if (state->initial && con->window != NULL) {
+            cookie = xcb_map_window(conn, con->window->id);
+            LOG("mapping child window (serial %d)\n", cookie.sequence);
             /* Ignore enter_notifies which are generated when mapping */
             add_ignore_event(cookie.sequence);
         }
+
+        cookie = xcb_map_window(conn, con->frame);
+        LOG("mapping container (serial %d)\n", cookie.sequence);
+        /* Ignore enter_notifies which are generated when mapping */
+        add_ignore_event(cookie.sequence);
         state->mapped = con->mapped;
     }
 
@@ -518,6 +500,57 @@ static void x_push_node(Con *con) {
         x_draw_decoration(con);
 }
 
+/*
+ * Same idea as in x_push_node(), but this function only unmaps windows. It is
+ * necessary to split this up to handle new fullscreen clients properly: The
+ * new window needs to be mapped and focus needs to be set *before* the
+ * underlying windows are unmapped. Otherwise, focus will revert to the
+ * PointerRoot and will then be set to the new window, generating unnecessary
+ * FocusIn/FocusOut events.
+ *
+ */
+static void x_push_node_unmaps(Con *con) {
+    Con *current;
+    con_state *state;
+
+    LOG("Pushing changes (with unmaps) for node %p / %s\n", con, con->name);
+    state = state_for_frame(con->frame);
+
+    /* 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) {
+        xcb_void_cookie_t cookie;
+        if (con->window != NULL) {
+            /* Set WM_STATE_WITHDRAWN, it seems like Java apps need it */
+            long data[] = { XCB_WM_STATE_WITHDRAWN, XCB_NONE };
+            xcb_change_property(conn, XCB_PROP_MODE_REPLACE, con->window->id,
+                                atoms[WM_STATE], atoms[WM_STATE], 32, 2, data);
+        }
+
+        cookie = xcb_unmap_window(conn, con->frame);
+        LOG("unmapping container (serial %d)\n", cookie.sequence);
+        /* we need to increase ignore_unmap for this container (if it
+         * contains a window) and for every window "under" this one which
+         * contains a window */
+        if (con->window != NULL) {
+            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);
+        state->mapped = con->mapped;
+    }
+
+    /* handle all children and floating windows of this node */
+    TAILQ_FOREACH(current, &(con->nodes_head), nodes)
+        x_push_node_unmaps(current);
+
+    TAILQ_FOREACH(current, &(con->floating_head), floating_windows)
+        x_push_node_unmaps(current);
+}
+
 /*
  * Pushes all changes (state of each node, see x_push_node() and the window
  * stack) to X11.
@@ -557,11 +590,14 @@ void x_push_changes(Con *con) {
     if (focused_id != to_focus) {
         LOG("Updating focus (focused: %p / %s)\n", focused, focused->name);
         xcb_set_input_focus(conn, XCB_INPUT_FOCUS_POINTER_ROOT, to_focus, XCB_CURRENT_TIME);
+        focused_id = to_focus;
     }
 
     xcb_flush(conn);
     LOG("\n\n ENDING CHANGES\n\n");
 
+    x_push_node_unmaps(con);
+
     /* save the current stack as old stack */
     CIRCLEQ_FOREACH(state, &state_head, state) {
         CIRCLEQ_REMOVE(&old_state_head, state, old_state);