i3 - improved tiling WM


Bugfix: `move <direction>` sends workspace focus

Patch status: merged

Patch by Tony Crisci

Long description:

Make sure the command `move <direction>` properly sends the workspace
focus ipc event required for i3bar to be properly updated and redrawn.

Make `ipc_send_workspace_focus_event publicly available from ipc.h for
more flexible event sending.

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

b/include/ipc.h

23
@@ -80,3 +80,10 @@ void ipc_send_event(const char *event, uint32_t message_type, const char *payloa
24
 void ipc_shutdown(void);
25
 
26
 void dump_node(yajl_gen gen, Con *con, bool inplace_restart);
27
+
28
+/**
29
+ * For the workspace "focus" event we send, along the usual "change" field,
30
+ * also the current and previous workspace, in "current" and "old"
31
+ * respectively.
32
+ */
33
+void ipc_send_workspace_focus_event(Con *current, Con *old);

b/src/ipc.c

38
@@ -1022,3 +1022,37 @@ int ipc_create_socket(const char *filename) {
39
     current_socketpath = resolved;
40
     return sockfd;
41
 }
42
+
43
+/*
44
+ * For the workspace "focus" event we send, along the usual "change" field,
45
+ * also the current and previous workspace, in "current" and "old"
46
+ * respectively.
47
+ */
48
+void ipc_send_workspace_focus_event(Con *current, Con *old) {
49
+    setlocale(LC_NUMERIC, "C");
50
+    yajl_gen gen = ygenalloc();
51
+
52
+    y(map_open);
53
+
54
+    ystr("change");
55
+    ystr("focus");
56
+
57
+    ystr("current");
58
+    dump_node(gen, current, false);
59
+
60
+    ystr("old");
61
+    if (old == NULL)
62
+        y(null);
63
+    else
64
+        dump_node(gen, old, false);
65
+
66
+    y(map_close);
67
+
68
+    const unsigned char *payload;
69
+    ylength length;
70
+    y(get_buf, &payload, &length);
71
+
72
+    ipc_send_event("workspace", I3_IPC_EVENT_WORKSPACE, (const char *)payload);
73
+    y(free);
74
+    setlocale(LC_NUMERIC, "");
75
+}

b/src/move.c

80
@@ -99,6 +99,7 @@ static void attach_to_workspace(Con *con, Con *ws, direction_t direction) {
81
  *
82
  */
83
 static void move_to_output_directed(Con *con, direction_t direction) {
84
+    Con *old_ws = con_get_workspace(con);
85
     Con *current_output_con = con_get_output(con);
86
     Output *current_output = get_output_by_name(current_output_con->name);
87
     Output *output = get_output_next(direction, current_output, CLOSEST_OUTPUT);
88
@@ -117,6 +118,16 @@ static void move_to_output_directed(Con *con, direction_t direction) {
89
     }
90
 
91
     attach_to_workspace(con, ws, direction);
92
+
93
+    /* fix the focus stack */
94
+    con_focus(con);
95
+
96
+    /* force re-painting the indicators */
97
+    FREE(con->deco_render_params);
98
+
99
+    tree_flatten(croot);
100
+
101
+    ipc_send_workspace_focus_event(ws, old_ws);
102
 }
103
 
104
 /*
105
@@ -141,7 +152,7 @@ void tree_move(int direction) {
106
     if (con->parent->type == CT_WORKSPACE && con_num_children(con->parent) == 1) {
107
         /* This is the only con on this workspace */
108
         move_to_output_directed(con, direction);
109
-        goto end;
110
+        return;
111
     }
112
 
113
     orientation_t o = (direction == D_LEFT || direction == D_RIGHT ? HORIZ : VERT);
114
@@ -201,7 +212,7 @@ void tree_move(int direction) {
115
                 /*  If we couldn't find a place to move it on this workspace,
116
                  *  try to move it to a workspace on a different output */
117
                 move_to_output_directed(con, direction);
118
-                goto end;
119
+                return;
120
             }
121
 
122
             /* If there was no con with which we could swap the current one,

b/src/workspace.c

127
@@ -11,9 +11,6 @@
128
  *
129
  */
130
 #include "all.h"
131
-#include "yajl_utils.h"
132
-
133
-#include <yajl/yajl_gen.h>
134
 
135
 /* Stores a copy of the name of the last used workspace for the workspace
136
  * back-and-forth switching. */
137
@@ -335,39 +332,6 @@ static void workspace_defer_update_urgent_hint_cb(EV_P_ ev_timer *w, int revents
138
     FREE(con->urgency_timer);
139
 }
140
 
141
-/*
142
- * For the "focus" event we send, along the usual "change" field, also the
143
- * current and previous workspace, in "current" and "old" respectively.
144
- */
145
-static void ipc_send_workspace_focus_event(Con *current, Con *old) {
146
-    setlocale(LC_NUMERIC, "C");
147
-    yajl_gen gen = ygenalloc();
148
-
149
-    y(map_open);
150
-
151
-    ystr("change");
152
-    ystr("focus");
153
-
154
-    ystr("current");
155
-    dump_node(gen, current, false);
156
-
157
-    ystr("old");
158
-    if (old == NULL)
159
-        y(null);
160
-    else
161
-        dump_node(gen, old, false);
162
-
163
-    y(map_close);
164
-
165
-    const unsigned char *payload;
166
-    ylength length;
167
-    y(get_buf, &payload, &length);
168
-
169
-    ipc_send_event("workspace", I3_IPC_EVENT_WORKSPACE, (const char *)payload);
170
-    y(free);
171
-    setlocale(LC_NUMERIC, "");
172
-}
173
-
174
 static void _workspace_show(Con *workspace) {
175
     Con *current, *old = NULL;
176
 

b/testcases/t/517-regress-move-direction-ipc.t

182
@@ -0,0 +1,79 @@
183
+#!perl
184
+# vim:ts=4:sw=4:expandtab
185
+#
186
+# Please read the following documents before working on tests:
187
+# • http://build.i3wm.org/docs/testsuite.html
188
+#   (or docs/testsuite)
189
+#
190
+# • http://build.i3wm.org/docs/lib-i3test.html
191
+#   (alternatively: perldoc ./testcases/lib/i3test.pm)
192
+#
193
+# • http://build.i3wm.org/docs/ipc.html
194
+#   (or docs/ipc)
195
+#
196
+# • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
197
+#   (unless you are already familiar with Perl)
198
+#
199
+# Make sure the command `move <direction>` properly sends the workspace focus
200
+# ipc event required for i3bar to be properly updated and redrawn.
201
+#
202
+# Bug still in: 4.6-195-g34232b8
203
+use i3test i3_autostart => 0;
204
+
205
+my $config = <<EOT;
206
+# i3 config file (v4)
207
+font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
208
+
209
+fake-outputs 1024x768+0+0,1024x768+1024+0
210
+workspace ws-left output fake-0
211
+workspace ws-right output fake-1
212
+EOT
213
+
214
+my $pid = launch_with_config($config);
215
+
216
+my $i3 = i3(get_socket_path());
217
+$i3->connect()->recv;
218
+
219
+# subscribe to the 'focus' ipc event
220
+my $focus = AnyEvent->condvar;
221
+$i3->subscribe({
222
+    workspace => sub {
223
+        my ($event) = @_;
224
+        if ($event->{change} eq 'focus') {
225
+            $focus->send($event);
226
+        }
227
+    }
228
+})->recv;
229
+
230
+# give up after 0.5 seconds
231
+my $timer = AnyEvent->timer(
232
+    after => 0.5,
233
+    cb => sub {
234
+        $focus->send(0);
235
+    }
236
+);
237
+
238
+# open two windows on the left output
239
+cmd 'workspace ws-left';
240
+open_window;
241
+open_window;
242
+
243
+# move a window over to the right output
244
+cmd 'move right';
245
+my $event = $focus->recv;
246
+
247
+ok($event, 'moving from workspace with two windows triggered focus ipc event');
248
+is($event->{current}->{name}, 'ws-right', 'focus event gave the right workspace');
249
+is(@{$event->{current}->{nodes}}, 1, 'focus event gave the right number of windows on the workspace');
250
+
251
+# reset and try again
252
+$focus = AnyEvent->condvar;
253
+cmd 'workspace ws-left; move right';
254
+$event = $focus->recv;
255
+ok($event, 'moving from workspace with one window triggered focus ipc event');
256
+is($event->{current}->{name}, 'ws-right', 'focus event gave the right workspace');
257
+is(@{$event->{current}->{nodes}}, 2, 'focus event gave the right number of windows on the workspace');
258
+
259
+exit_gracefully($pid);
260
+
261
+done_testing;