]> git.sur5r.net Git - i3/i3/commitdiff
Use the EWMH support window rather than the root window as an input focus fallback. 1917/head
authorIngo Bürk <ingo.buerk@tngtech.com>
Sat, 12 Sep 2015 20:34:06 +0000 (22:34 +0200)
committerIngo Bürk <ingo.buerk@tngtech.com>
Sun, 13 Sep 2015 18:29:02 +0000 (20:29 +0200)
If no other window is available on the active workspace, we now select the EWMH support window (used to indicate that an EWMH-compliant window manager is preent) as the focus window rather than the root window. The NET_WM_ACTIVE window will still be set to XCB_WINDOW_NONE to pretend that no window is actually focused.
This fixes the issue that when using the root window, a fallback mechanism in X11 takes effect which routes keyboard input to the window under the cursor, independent of whether that window has the input focus. Using the EWMH window instead, we can avoid this behavior. We cannot simply set it to XCB_WINDOW_NONE as this would discard all keyboard events, breaking keybindings.

fixes #1378

include/i3.h
src/ewmh.c
src/x.c
testcases/t/527-focus-fallback.t [new file with mode: 0644]

index f1912fd55eb633df71f999aa3ebe793932bee62a..2a43148602bb0cc44b624fead17bc04e42598b98 100644 (file)
@@ -37,6 +37,15 @@ extern bool debug_build;
 extern int listen_fds;
 extern xcb_connection_t *conn;
 extern int conn_screen;
+/**
+ * The EWMH support window that is used to indicate that an EWMH-compliant
+ * window manager is present. This window is created when i3 starts and
+ * kept alive until i3 exits.
+ * We also use this window as the focused window if no other window is
+ * available to be focused on the active workspace in order to prevent
+ * keyboard focus issues (see #1378).
+ */
+extern xcb_window_t ewmh_window;
 /** The last timestamp we got from X11 (timestamps are included in some events
  * and are used for some things, like determining a unique ID in startup
  * notification). */
index 70cf77189b9cfcea4c861e328482030d56b40c72..d60bbb50ad3f24da648494d515a85dae0fb00be6 100644 (file)
@@ -11,6 +11,8 @@
  */
 #include "all.h"
 
+xcb_window_t ewmh_window;
+
 /*
  * Updates _NET_CURRENT_DESKTOP with the current desktop number.
  *
@@ -227,25 +229,30 @@ void ewmh_setup_hints(void) {
      * present, a child window has to be created (and kept alive as long as the
      * window manager is running) which has the _NET_SUPPORTING_WM_CHECK and
      * _NET_WM_ATOMS. */
-    xcb_window_t child_window = xcb_generate_id(conn);
+    ewmh_window = xcb_generate_id(conn);
+    /* We create the window and put it at (-1, -1) so that it is off-screen. */
     xcb_create_window(
         conn,
         XCB_COPY_FROM_PARENT,        /* depth */
-        child_window,                /* window id */
+        ewmh_window,                 /* window id */
         root,                        /* parent */
-        0, 0, 1, 1,                  /* dimensions (x, y, w, h) */
+        -1, -1, 1, 1,                /* dimensions (x, y, w, h) */
         0,                           /* border */
         XCB_WINDOW_CLASS_INPUT_ONLY, /* window class */
         XCB_COPY_FROM_PARENT,        /* visual */
-        0,
-        NULL);
-    xcb_change_property(conn, XCB_PROP_MODE_REPLACE, child_window, A__NET_SUPPORTING_WM_CHECK, XCB_ATOM_WINDOW, 32, 1, &child_window);
-    xcb_change_property(conn, XCB_PROP_MODE_REPLACE, child_window, A__NET_WM_NAME, A_UTF8_STRING, 8, strlen("i3"), "i3");
-    xcb_change_property(conn, XCB_PROP_MODE_REPLACE, root, A__NET_SUPPORTING_WM_CHECK, XCB_ATOM_WINDOW, 32, 1, &child_window);
+        XCB_CW_OVERRIDE_REDIRECT,
+        (uint32_t[]){1});
+    xcb_change_property(conn, XCB_PROP_MODE_REPLACE, ewmh_window, A__NET_SUPPORTING_WM_CHECK, XCB_ATOM_WINDOW, 32, 1, &ewmh_window);
+    xcb_change_property(conn, XCB_PROP_MODE_REPLACE, ewmh_window, A__NET_WM_NAME, A_UTF8_STRING, 8, strlen("i3"), "i3");
+    xcb_change_property(conn, XCB_PROP_MODE_REPLACE, root, A__NET_SUPPORTING_WM_CHECK, XCB_ATOM_WINDOW, 32, 1, &ewmh_window);
 
     /* I’m not entirely sure if we need to keep _NET_WM_NAME on root. */
     xcb_change_property(conn, XCB_PROP_MODE_REPLACE, root, A__NET_WM_NAME, A_UTF8_STRING, 8, strlen("i3"), "i3");
 
     /* only send the first 31 atoms (last one is _NET_CLOSE_WINDOW) increment that number when adding supported atoms */
     xcb_change_property(conn, XCB_PROP_MODE_REPLACE, root, A__NET_SUPPORTED, XCB_ATOM_ATOM, 32, /* number of atoms */ 31, supported_atoms);
+
+    /* We need to map this window to be able to set the input focus to it if no other window is available to be focused. */
+    xcb_map_window(conn, ewmh_window);
+    xcb_configure_window(conn, ewmh_window, XCB_CONFIG_WINDOW_STACK_MODE, (uint32_t[]){XCB_STACK_MODE_BELOW});
 }
diff --git a/src/x.c b/src/x.c
index 337e268c3a4f7f8436b5b8c6e6340865c8159e89..f0da9fff5e5caf1897b04f03855aba1a4a90c2b2 100644 (file)
--- a/src/x.c
+++ b/src/x.c
@@ -12,6 +12,8 @@
  */
 #include "all.h"
 
+xcb_window_t ewmh_window;
+
 /* Stores the X11 window ID of the currently focused window */
 xcb_window_t focused_id = XCB_NONE;
 
@@ -1090,10 +1092,12 @@ void x_push_changes(Con *con) {
     }
 
     if (focused_id == XCB_NONE) {
-        DLOG("Still no window focused, better set focus to the root window\n");
-        xcb_set_input_focus(conn, XCB_INPUT_FOCUS_POINTER_ROOT, root, XCB_CURRENT_TIME);
+        /* If we still have no window to focus, we focus the EWMH window instead. We use this rather than the
+         * root window in order to avoid an X11 fallback mechanism causing a ghosting effect (see #1378). */
+        DLOG("Still no window focused, better set focus to the EWMH support window (%d)\n", ewmh_window);
+        xcb_set_input_focus(conn, XCB_INPUT_FOCUS_POINTER_ROOT, ewmh_window, XCB_CURRENT_TIME);
         ewmh_update_active_window(XCB_WINDOW_NONE);
-        focused_id = root;
+        focused_id = ewmh_window;
     }
 
     xcb_flush(conn);
diff --git a/testcases/t/527-focus-fallback.t b/testcases/t/527-focus-fallback.t
new file mode 100644 (file)
index 0000000..5956e67
--- /dev/null
@@ -0,0 +1,45 @@
+#!perl
+# vim:ts=4:sw=4:expandtab
+#
+# Please read the following documents before working on tests:
+# • http://build.i3wm.org/docs/testsuite.html
+#   (or docs/testsuite)
+#
+# • http://build.i3wm.org/docs/lib-i3test.html
+#   (alternatively: perldoc ./testcases/lib/i3test.pm)
+#
+# • http://build.i3wm.org/docs/ipc.html
+#   (or docs/ipc)
+#
+# • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
+#   (unless you are already familiar with Perl)
+#
+# Ticket: #1378
+use i3test;
+use X11::XCB qw(:all);
+
+sub get_ewmh_window {
+    my $cookie = $x->get_property(
+        0,
+        $x->get_root_window(),
+        $x->atom(name => '_NET_SUPPORTING_WM_CHECK')->id,
+        $x->atom(name => 'WINDOW')->id,
+        0,
+        4096
+    );
+
+    my $reply = $x->get_property_reply($cookie->{sequence});
+    my $len = $reply->{length};
+    return undef if $len == 0;
+
+    return unpack("L", $reply->{value});
+}
+
+my $window = open_window;
+sync_with_i3;
+is($x->input_focus, $window->id, 'sanity check: window has input focus');
+cmd 'kill';
+sync_with_i3;
+is($x->input_focus, get_ewmh_window(), 'focus falls back to the EWMH window');
+
+done_testing;