Feature: improve active window request handling
Patch status: merged
Patch by Tony Crisci
Long description:
Allow client requests of type _NET_ACTIVE_WINDOW to switch workspaces if they indicate they are a pager. Otherwise, set the urgency hint on that con to indicate that something happened. This allows task switchers like skippy-xd to work properly. http://standards.freedesktop.org/wm-spec/latest/ar01s03.html#idm140251368127856
To apply this patch, use:
curl http://cr.i3wm.org/patch/534/raw.patch | git am
b/src/handlers.c
| 21 |
@@ -694,7 +694,11 @@ static void handle_client_message(xcb_client_message_event_t *event) {
|
| 22 |
|
| 23 |
tree_render(); |
| 24 |
} else if (event->type == A__NET_ACTIVE_WINDOW) {
|
| 25 |
+ if (event->format != 32) |
| 26 |
+ return; |
| 27 |
+ |
| 28 |
DLOG("_NET_ACTIVE_WINDOW: Window 0x%08x should be activated\n", event->window);
|
| 29 |
+ |
| 30 |
Con *con = con_by_window_id(event->window); |
| 31 |
if (con == NULL) {
|
| 32 |
DLOG("Could not get window for client message\n");
|
| 33 |
@@ -702,8 +706,9 @@ static void handle_client_message(xcb_client_message_event_t *event) {
|
| 34 |
} |
| 35 |
|
| 36 |
Con *ws = con_get_workspace(con); |
| 37 |
- if (!workspace_is_visible(ws)) {
|
| 38 |
- DLOG("Workspace not visible, ignoring _NET_ACTIVE_WINDOW\n");
|
| 39 |
+ |
| 40 |
+ if (ws == NULL) {
|
| 41 |
+ DLOG("Window is not being managed, ignoring _NET_ACTIVE_WINDOW\n");
|
| 42 |
return; |
| 43 |
} |
| 44 |
|
| 45 |
@@ -712,10 +717,26 @@ static void handle_client_message(xcb_client_message_event_t *event) {
|
| 46 |
return; |
| 47 |
} |
| 48 |
|
| 49 |
- if (ws != con_get_workspace(focused)) |
| 50 |
+ /* data32[0] indicates the source of the request (application or pager) */ |
| 51 |
+ if (event->data.data32[0] == 2) {
|
| 52 |
+ /* Always focus the con if it is from a pager, because this is most |
| 53 |
+ * likely from some user action */ |
| 54 |
+ DLOG("This request came from a pager. Focusing con = %p\n", con);
|
| 55 |
workspace_show(ws); |
| 56 |
+ con_focus(con); |
| 57 |
+ } else {
|
| 58 |
+ /* If the request is from an application, only focus if the |
| 59 |
+ * workspace is visible. Otherwise set the urgnecy hint. */ |
| 60 |
+ if (workspace_is_visible(ws)) {
|
| 61 |
+ DLOG("Request to focus con on a visible workspace. Focusing con = %p\n", con);
|
| 62 |
+ workspace_show(ws); |
| 63 |
+ con_focus(con); |
| 64 |
+ } else {
|
| 65 |
+ DLOG("Request to focus con on a hidden workspace. Setting urgent con = %p\n", con);
|
| 66 |
+ con_set_urgency(con, true); |
| 67 |
+ } |
| 68 |
+ } |
| 69 |
|
| 70 |
- con_focus(con); |
| 71 |
tree_render(); |
| 72 |
} else if (event->type == A_I3_SYNC) {
|
| 73 |
xcb_window_t window = event->data.data32[0]; |
b/testcases/t/195-net-active-window.t
| 78 |
@@ -21,7 +21,9 @@ |
| 79 |
use i3test; |
| 80 |
|
| 81 |
sub send_net_active_window {
|
| 82 |
- my ($id) = @_; |
| 83 |
+ my ($id, $source) = @_; |
| 84 |
+ |
| 85 |
+ $source = ($source eq 'pager' ? 2 : 0); |
| 86 |
|
| 87 |
my $msg = pack "CCSLLLLLLL", |
| 88 |
X11::XCB::CLIENT_MESSAGE, # response_type |
| 89 |
@@ -29,7 +31,7 @@ sub send_net_active_window {
|
| 90 |
0, # sequence |
| 91 |
$id, # destination window |
| 92 |
$x->atom(name => '_NET_ACTIVE_WINDOW')->id, |
| 93 |
- 0, |
| 94 |
+ $source, |
| 95 |
0, |
| 96 |
0, |
| 97 |
0, |
| 98 |
@@ -54,7 +56,8 @@ is($x->input_focus, $win1->id, 'window 1 has focus'); |
| 99 |
|
| 100 |
################################################################################ |
| 101 |
# Switch to a different workspace and ensure sending the _NET_ACTIVE_WINDOW |
| 102 |
-# ClientMessage has no effect anymore. |
| 103 |
+# ClientMessage switches to that workspaces only if source indicates it is a |
| 104 |
+# pager and otherwise sets the urgent hint. |
| 105 |
################################################################################ |
| 106 |
|
| 107 |
my $ws2 = fresh_workspace; |
| 108 |
@@ -62,9 +65,36 @@ my $win3 = open_window; |
| 109 |
|
| 110 |
is($x->input_focus, $win3->id, 'window 3 has focus'); |
| 111 |
|
| 112 |
+send_net_active_window($win1->id, 'pager'); |
| 113 |
+ |
| 114 |
+is($x->input_focus, $win1->id, 'focus switched to window 1 when message source was a pager'); |
| 115 |
+ |
| 116 |
+cmd '[id="' . $win3->id . '"] focus'; |
| 117 |
+ |
| 118 |
send_net_active_window($win1->id); |
| 119 |
|
| 120 |
-is($x->input_focus, $win3->id, 'window 3 still has focus'); |
| 121 |
+is($x->input_focus, $win3->id, |
| 122 |
+ 'focus did not switch to window 1 on a hidden workspace when message source was an application'); |
| 123 |
+ |
| 124 |
+ok(get_ws($ws1)->{urgent}, 'urgent hint set on ws 1');
|
| 125 |
+ |
| 126 |
+ |
| 127 |
+################################################################################ |
| 128 |
+# Make sure the ClientMessage only works with managed windows, and specifying a |
| 129 |
+# window that is not managed does not crash i3 (#774) |
| 130 |
+################################################################################ |
| 131 |
+ |
| 132 |
+my $dock = open_window(window_type => $x->atom(name => '_NET_WM_WINDOW_TYPE_DOCK')); |
| 133 |
+ |
| 134 |
+send_net_active_window($dock->id); |
| 135 |
+ |
| 136 |
+does_i3_live; |
| 137 |
+is($x->input_focus, $win3->id, 'dock did not get input focus'); |
| 138 |
+ |
| 139 |
+send_net_active_window($x->get_root_window()); |
| 140 |
+ |
| 141 |
+does_i3_live; |
| 142 |
+is($x->input_focus, $win3->id, 'root window did not get input focus'); |
| 143 |
|
| 144 |
################################################################################ |
| 145 |
# Move a window to the scratchpad, send a _NET_ACTIVE_WINDOW for it and verify |