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; |