Dont set input focus and send WM_TAKE_FOCUS
Patch status: merged
Patch by Tony Crisci
Long description:
If input focus is set by the window manager, it is not necessary to send WM_TAKE_FOCUS because it has already taken focus. http://tronche.com/gui/x/icccm/sec-4.html#s-4.1.7 > The goal is to support window managers that want to assign the input > focus to a top-level window in such a way that the top-level window > either can assign it to one of its subwindows or can decline the offer > of the focus. For example, a clock or a text editor with no currently > open frames might not want to take focus even though the window > manager generally believes that clients should take the input focus > after being deiconified or raised. Both setting input focus and sending WM_TAKE_FOCUS is effectively setting focus on the window twice which is certainly against the spirit of the spec, if not the letter. fixes #1167
To apply this patch, use:
curl http://cr.i3wm.org/patch/503/raw.patch | git am
b/src/x.c
32 |
@@ -987,20 +987,16 @@ void x_push_changes(Con *con) { |
33 |
/* Invalidate focused_id to correctly focus new windows with the same ID */ |
34 |
focused_id = XCB_NONE; |
35 |
} else { |
36 |
- bool set_focus = true; |
37 |
if (focused->window != NULL && |
38 |
- focused->window->needs_take_focus) { |
39 |
+ focused->window->needs_take_focus && |
40 |
+ focused->window->doesnt_accept_focus) { |
41 |
DLOG("Updating focus by sending WM_TAKE_FOCUS to window 0x%08x (focused: %p / %s)\n", |
42 |
to_focus, focused, focused->name); |
43 |
send_take_focus(to_focus, last_timestamp); |
44 |
- set_focus = !focused->window->doesnt_accept_focus; |
45 |
- DLOG("set_focus = %d\n", set_focus); |
46 |
|
47 |
- if (!set_focus && to_focus != last_focused && is_con_attached(focused)) |
48 |
+ if (to_focus != last_focused && is_con_attached(focused)) |
49 |
ipc_send_window_event("focus", focused); |
50 |
- } |
51 |
- |
52 |
- if (set_focus) { |
53 |
+ } else { |
54 |
DLOG("Updating focus (focused: %p / %s) to X11 window 0x%08x\n", focused, focused->name, to_focus); |
55 |
/* We remove XCB_EVENT_MASK_FOCUS_CHANGE from the event mask to get |
56 |
* no focus change events for our own focus changes. We only want |
b/testcases/t/158-wm_take_focus.t
61 |
@@ -16,12 +16,14 @@ |
62 |
# |
63 |
# Tests if the WM_TAKE_FOCUS protocol is correctly handled by i3 |
64 |
# |
65 |
+# For more information on the protocol and input handling, see: |
66 |
+# http://tronche.com/gui/x/icccm/sec-4.html#s-4.1.7 |
67 |
+# |
68 |
use i3test; |
69 |
|
70 |
-subtest 'Window without WM_TAKE_FOCUS', sub { |
71 |
- fresh_workspace; |
72 |
+sub recv_take_focus { |
73 |
+ my ($window) = @_; |
74 |
|
75 |
- my $window = open_window; |
76 |
# sync_with_i3 will send a ClientMessage to i3 and i3 will send the same |
77 |
# payload back to $window->id. |
78 |
my $myrnd = sync_with_i3( |
79 |
@@ -44,12 +46,20 @@ subtest 'Window without WM_TAKE_FOCUS', sub { |
80 |
return ($rnd == $myrnd); |
81 |
}; |
82 |
|
83 |
- ok($first_event_is_clientmessage, 'did not receive ClientMessage'); |
84 |
+ return !$first_event_is_clientmessage; |
85 |
+} |
86 |
+ |
87 |
+subtest 'Window without WM_TAKE_FOCUS', sub { |
88 |
+ fresh_workspace; |
89 |
+ |
90 |
+ my $window = open_window; |
91 |
+ |
92 |
+ ok(!recv_take_focus($window), 'did not receive ClientMessage'); |
93 |
|
94 |
done_testing; |
95 |
}; |
96 |
|
97 |
-subtest 'Window with WM_TAKE_FOCUS', sub { |
98 |
+subtest 'Window with WM_TAKE_FOCUS and without InputHint', sub { |
99 |
fresh_workspace; |
100 |
|
101 |
my $take_focus = $x->atom(name => 'WM_TAKE_FOCUS'); |
102 |
@@ -59,16 +69,31 @@ subtest 'Window with WM_TAKE_FOCUS', sub { |
103 |
protocols => [ $take_focus ], |
104 |
}); |
105 |
|
106 |
+ # add an (empty) WM_HINTS property without the InputHint |
107 |
+ $window->delete_hint('input'); |
108 |
+ |
109 |
$window->map; |
110 |
|
111 |
- ok(wait_for_event(1, sub { |
112 |
- return 0 unless $_[0]->{response_type} == 161; |
113 |
- my ($data, $time) = unpack("L2", $_[0]->{data}); |
114 |
- return ($data == $take_focus->id); |
115 |
- }), 'got ClientMessage with WM_TAKE_FOCUS atom'); |
116 |
+ ok(recv_take_focus($window), 'got ClientMessage with WM_TAKE_FOCUS atom'); |
117 |
|
118 |
done_testing; |
119 |
}; |
120 |
|
121 |
+# If the InputHint is unspecified, i3 should use the simpler method of focusing |
122 |
+# the window directly rather than using the WM_TAKE_FOCUS protocol. |
123 |
+# XXX: The code paths for an unspecified and set InputHint are |
124 |
+# nearly identical presently, so this is currently used also as a proxy test |
125 |
+# for the latter case. |
126 |
+subtest 'Window with WM_TAKE_FOCUS and unspecified InputHint', sub { |
127 |
+ fresh_workspace; |
128 |
+ |
129 |
+ my $take_focus = $x->atom(name => 'WM_TAKE_FOCUS'); |
130 |
+ |
131 |
+ my $window = open_window({ protocols => [ $take_focus ] }); |
132 |
+ |
133 |
+ ok(!recv_take_focus($window), 'did not receive ClientMessage'); |
134 |
+ |
135 |
+ done_testing; |
136 |
+}; |
137 |
|
138 |
done_testing; |