i3 - improved tiling WM


Do not send WM_TAKE_FOCUS when InputHint is set

Patch status: needinfo

Patch by Tony Crisci

Long description:

Window managers do not conventionally send WM_TAKE_FOCUS when the
WM_HINTS property of the client has the `input` flag set (which shall be
"the client's input model") ''even when the window claims to support the
protocol''. While the window manager may respond by both setting focus
for the client and sending WM_TAKE_FOCUS, the latter will not fulfil its
intended purpose and may complicate input event handling.

Note that whenever the WM_HINTS property is missing from the window, i3
will treat this window as if the InputHint is set. The window manager is
allowed by the spec to assume convenient values in this case.

dwm seems to follow this convention:
http://lists.suckless.org/dev/1104/7548.html#replies

Here is a second-hand report that Compiz and Metacity follow this
convention:
www.winehq.org/pipermail/wine-devel/2007-July/058255.html

The "models of input handling" can be found here:
http://tronche.com/gui/x/icccm/sec-4.html#s-4.1.7

Future work should go towards improving support for the input handling
models. i3 should implement support for the "no input" model, perhaps
based on _NET_WM_TYPE. For possible implementation ideas see
http://mail.gnome.org/archives/wm-spec-list/2007-March/msg00001.html

fixes #1167

To apply this patch, use:
curl http://cr.i3wm.org/patch/408/raw.patch | git am

b/src/x.c

41
@@ -964,17 +964,13 @@ void x_push_changes(Con *con) {
42
             /* Invalidate focused_id to correctly focus new windows with the same ID */
43
             focused_id = XCB_NONE;
44
         } else {
45
-            bool set_focus = true;
46
             if (focused->window != NULL &&
47
-                focused->window->needs_take_focus) {
48
+                focused->window->needs_take_focus &&
49
+                focused->window->doesnt_accept_focus) {
50
                 DLOG("Updating focus by sending WM_TAKE_FOCUS to window 0x%08x (focused: %p / %s)\n",
51
                      to_focus, focused, focused->name);
52
                 send_take_focus(to_focus);
53
-                set_focus = !focused->window->doesnt_accept_focus;
54
-                DLOG("set_focus = %d\n", set_focus);
55
-            }
56
-
57
-            if (set_focus) {
58
+            } else {
59
                 DLOG("Updating focus (focused: %p / %s) to X11 window 0x%08x\n", focused, focused->name, to_focus);
60
                 /* We remove XCB_EVENT_MASK_FOCUS_CHANGE from the event mask to get
61
                  * no focus change events for our own focus changes. We only want

b/testcases/t/158-wm_take_focus.t

66
@@ -30,7 +30,32 @@ subtest 'Window without WM_TAKE_FOCUS', sub {
67
     done_testing;
68
 };
69
 
70
-subtest 'Window with WM_TAKE_FOCUS', sub {
71
+# Window managers do not conventionally send WM_TAKE_FOCUS to clients with the
72
+# InputHint set (see issue #1167).
73
+subtest 'Window with WM_TAKE_FOCUS and with InputHint set', sub {
74
+    # XXX: This test relies on the fact that whenever a window is opened
75
+    # without setting the WM_HINTS property on the window at all, i3 will treat
76
+    # this window as if the InputHint were set.
77
+
78
+    fresh_workspace;
79
+
80
+    my $take_focus = $x->atom(name => 'WM_TAKE_FOCUS');
81
+
82
+    my $window = open_window({
83
+        dont_map => 1,
84
+        protocols => [ $take_focus ],
85
+    });
86
+
87
+    $window->map;
88
+    # sync_with_i3 will send a ClientMessage to i3 and receive one targeted to
89
+    # $window->id. If it receives WM_TAKE_FOCUS instead, it will return 0, thus
90
+    # the test will fail.
91
+    ok(sync_with_i3(window_id => $window->id), 'did not receive ClientMessage');
92
+
93
+    done_testing;
94
+};
95
+
96
+subtest 'Window with WM_TAKE_FOCUS and without InputHint', sub {
97
     fresh_workspace;
98
 
99
     my $take_focus = $x->atom(name => 'WM_TAKE_FOCUS');
100
@@ -40,6 +65,9 @@ subtest 'Window with WM_TAKE_FOCUS', sub {
101
         protocols => [ $take_focus ],
102
     });
103
 
104
+    # add an empty WM_HINTS property (InputHint will be unset)
105
+    $window->add_hint('');
106
+
107
     $window->map;
108
 
109
     ok(wait_for_event(1, sub {