i3 - improved tiling WM


Include workspace con in workspace event

Patch status: merged

Patch by Tony Crisci

Long description:

Send the affected workspace in the "current" property for each workspace
event for any type of workspace event that affects a particular
workspace.

fixes #1411

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

b/docs/ipc

26
@@ -668,15 +668,16 @@ if ($is_event) {
27
 
28
 This event consists of a single serialized map containing a property
29
 +change (string)+ which indicates the type of the change ("focus", "init",
30
-"empty", "urgent").
31
-
32
-Moreover, when the change is "focus", an +old (object)+ and a +current
33
-(object)+ properties will be present with the previous and current
34
-workspace respectively.  When the first switch occurs (when i3 focuses
35
-the workspace visible at the beginning) there is no previous
36
-workspace, and the +old+ property will be set to +null+.  Also note
37
-that if the previous is empty it will get destroyed when switching,
38
-but will still be present in the "old" property.
39
+"empty", "urgent"). A +current (object)+ property will be present with the
40
+affected workspace whenever the type of event affects a workspace (otherwise,
41
+it will be +null).
42
+
43
+When the change is "focus", an +old (object)+ property will be present with the
44
+previous workspace.  When the first switch occurs (when i3 focuses the
45
+workspace visible at the beginning) there is no previous workspace, and the
46
++old+ property will be set to +null+.  Also note that if the previous is empty
47
+it will get destroyed when switching, but will still be present in the "old"
48
+property.
49
 
50
 *Example:*
51
 ---------------------

b/include/ipc.h

56
@@ -89,11 +89,17 @@ void ipc_shutdown(void);
57
 void dump_node(yajl_gen gen, Con *con, bool inplace_restart);
58
 
59
 /**
60
- * For the workspace "focus" event we send, along the usual "change" field,
61
- * also the current and previous workspace, in "current" and "old"
62
- * respectively.
63
+ * Generates a json workspace event. Returns a dynamically allocated yajl
64
+ * generator. Free with yajl_gen_free().
65
  */
66
-void ipc_send_workspace_focus_event(Con *current, Con *old);
67
+yajl_gen ipc_marshal_workspace_event(const char *change, Con *current, Con *old);
68
+
69
+/**
70
+ * For the workspace events we send, along with the usual "change" field, also
71
+ * the workspace container in "current". For focus events, we send the
72
+ * previously focused workspace in "old".
73
+ */
74
+void ipc_send_workspace_event(const char *change, Con *current, Con *old);
75
 
76
 /**
77
  * For the window events we send, along the usual "change" field,

b/src/commands.c

82
@@ -944,7 +944,7 @@ void cmd_append_layout(I3_CMD, char *path) {
83
     restore_open_placeholder_windows(parent);
84
 
85
     if (content == JSON_CONTENT_WORKSPACE)
86
-        ipc_send_event("workspace", I3_IPC_EVENT_WORKSPACE, "{\"change\":\"restored\"}");
87
+        ipc_send_workspace_event("restored", parent, NULL);
88
 
89
     cmd_output->needs_tree_render = true;
90
 }
91
@@ -1313,7 +1313,7 @@ void cmd_move_workspace_to_output(I3_CMD, char *name) {
92
                 create_workspace_on_output(current_output, ws->parent);
93
 
94
             /* notify the IPC listeners */
95
-            ipc_send_event("workspace", I3_IPC_EVENT_WORKSPACE, "{\"change\":\"init\"}");
96
+            ipc_send_workspace_event("init", ws, NULL);
97
         }
98
         DLOG("Detaching\n");
99
 
100
@@ -1334,7 +1334,7 @@ void cmd_move_workspace_to_output(I3_CMD, char *name) {
101
         TAILQ_FOREACH(floating_con, &(ws->floating_head), floating_windows)
102
         floating_fix_coordinates(floating_con, &(old_content->rect), &(content->rect));
103
 
104
-        ipc_send_event("workspace", I3_IPC_EVENT_WORKSPACE, "{\"change\":\"move\"}");
105
+        ipc_send_workspace_event("move", ws, NULL);
106
         if (workspace_was_visible) {
107
             /* Focus the moved workspace on the destination output. */
108
             workspace_show(ws);
109
@@ -1761,7 +1761,7 @@ void cmd_reload(I3_CMD) {
110
     load_configuration(conn, NULL, true);
111
     x_set_i3_atoms();
112
     /* Send an IPC event just in case the ws names have changed */
113
-    ipc_send_event("workspace", I3_IPC_EVENT_WORKSPACE, "{\"change\":\"reload\"}");
114
+    ipc_send_workspace_event("reload", NULL, NULL);
115
     /* Send an update event for the barconfig just in case it has changed */
116
     update_barconfig();
117
 
118
@@ -2040,7 +2040,7 @@ void cmd_rename_workspace(I3_CMD, char *old_name, char *new_name) {
119
     cmd_output->needs_tree_render = true;
120
     ysuccess(true);
121
 
122
-    ipc_send_event("workspace", I3_IPC_EVENT_WORKSPACE, "{\"change\":\"rename\"}");
123
+    ipc_send_workspace_event("rename", workspace, NULL);
124
     ewmh_update_desktop_names();
125
     ewmh_update_desktop_viewport();
126
     ewmh_update_current_desktop();

b/src/con.c

131
@@ -12,6 +12,7 @@
132
  *
133
  */
134
 #include "all.h"
135
+#include "yajl_utils.h"
136
 
137
 static void con_on_remove_child(Con *con);
138
 
139
@@ -1435,8 +1436,15 @@ static void con_on_remove_child(Con *con) {
140
     if (con->type == CT_WORKSPACE) {
141
         if (TAILQ_EMPTY(&(con->focus_head)) && !workspace_is_visible(con)) {
142
             LOG("Closing old workspace (%p / %s), it is empty\n", con, con->name);
143
+            yajl_gen gen = ipc_marshal_workspace_event("empty", con, NULL);
144
             tree_close(con, DONT_KILL_WINDOW, false, false);
145
-            ipc_send_event("workspace", I3_IPC_EVENT_WORKSPACE, "{\"change\":\"empty\"}");
146
+
147
+            const unsigned char *payload;
148
+            ylength length;
149
+            y(get_buf, &payload, &length);
150
+            ipc_send_event("workspace", I3_IPC_EVENT_WORKSPACE, (const char *)payload);
151
+
152
+            y(free);
153
         }
154
         return;
155
     }

b/src/ipc.c

160
@@ -1120,21 +1120,23 @@ int ipc_create_socket(const char *filename) {
161
 }
162
 
163
 /*
164
- * For the workspace "focus" event we send, along the usual "change" field,
165
- * also the current and previous workspace, in "current" and "old"
166
- * respectively.
167
+ * Generates a json workspace event. Returns a dynamically allocated yajl
168
+ * generator. Free with yajl_gen_free().
169
  */
170
-void ipc_send_workspace_focus_event(Con *current, Con *old) {
171
+yajl_gen ipc_marshal_workspace_event(const char *change, Con *current, Con *old) {
172
     setlocale(LC_NUMERIC, "C");
173
     yajl_gen gen = ygenalloc();
174
 
175
     y(map_open);
176
 
177
     ystr("change");
178
-    ystr("focus");
179
+    ystr(change);
180
 
181
     ystr("current");
182
-    dump_node(gen, current, false);
183
+    if (current == NULL)
184
+        y(null);
185
+    else
186
+        dump_node(gen, current, false);
187
 
188
     ystr("old");
189
     if (old == NULL)
190
@@ -1144,13 +1146,26 @@ void ipc_send_workspace_focus_event(Con *current, Con *old) {
191
 
192
     y(map_close);
193
 
194
+    setlocale(LC_NUMERIC, "");
195
+
196
+    return gen;
197
+}
198
+
199
+/*
200
+ * For the workspace events we send, along with the usual "change" field, also
201
+ * the workspace container in "current". For focus events, we send the
202
+ * previously focused workspace in "old".
203
+ */
204
+void ipc_send_workspace_event(const char *change, Con *current, Con *old) {
205
+    yajl_gen gen = ipc_marshal_workspace_event(change, current, old);
206
+
207
     const unsigned char *payload;
208
     ylength length;
209
     y(get_buf, &payload, &length);
210
 
211
     ipc_send_event("workspace", I3_IPC_EVENT_WORKSPACE, (const char *)payload);
212
+
213
     y(free);
214
-    setlocale(LC_NUMERIC, "");
215
 }
216
 
217
 /**

b/src/move.c

222
@@ -128,7 +128,7 @@ static void move_to_output_directed(Con *con, direction_t direction) {
223
 
224
     tree_flatten(croot);
225
 
226
-    ipc_send_workspace_focus_event(ws, old_ws);
227
+    ipc_send_workspace_event("focus", ws, old_ws);
228
 }
229
 
230
 /*

b/src/workspace.c

235
@@ -11,6 +11,7 @@
236
  *
237
  */
238
 #include "all.h"
239
+#include "yajl_utils.h"
240
 
241
 /* Stores a copy of the name of the last used workspace for the workspace
242
  * back-and-forth switching. */
243
@@ -91,7 +92,7 @@ Con *workspace_get(const char *num, bool *created) {
244
 
245
         con_attach(workspace, content, false);
246
 
247
-        ipc_send_event("workspace", I3_IPC_EVENT_WORKSPACE, "{\"change\":\"init\"}");
248
+        ipc_send_workspace_event("init", workspace, NULL);
249
         ewmh_update_number_of_desktops();
250
         ewmh_update_desktop_names();
251
         ewmh_update_desktop_viewport();
252
@@ -409,7 +410,7 @@ static void _workspace_show(Con *workspace) {
253
     } else
254
         con_focus(next);
255
 
256
-    ipc_send_workspace_focus_event(workspace, current);
257
+    ipc_send_workspace_event("focus", workspace, current);
258
 
259
     DLOG("old = %p / %s\n", old, (old ? old->name : "(null)"));
260
     /* Close old workspace if necessary. This must be done *after* doing
261
@@ -421,8 +422,16 @@ static void _workspace_show(Con *workspace) {
262
         /* check if this workspace is currently visible */
263
         if (!workspace_is_visible(old)) {
264
             LOG("Closing old workspace (%p / %s), it is empty\n", old, old->name);
265
+            yajl_gen gen = ipc_marshal_workspace_event("empty", old, NULL);
266
             tree_close(old, DONT_KILL_WINDOW, false, false);
267
-            ipc_send_event("workspace", I3_IPC_EVENT_WORKSPACE, "{\"change\":\"empty\"}");
268
+
269
+            const unsigned char *payload;
270
+            ylength length;
271
+            y(get_buf, &payload, &length);
272
+            ipc_send_event("workspace", I3_IPC_EVENT_WORKSPACE, (const char *)payload);
273
+
274
+            y(free);
275
+
276
             ewmh_update_number_of_desktops();
277
             ewmh_update_desktop_names();
278
             ewmh_update_desktop_viewport();
279
@@ -766,7 +775,7 @@ void workspace_update_urgent_flag(Con *ws) {
280
     DLOG("Workspace urgency flag changed from %d to %d\n", old_flag, ws->urgent);
281
 
282
     if (old_flag != ws->urgent)
283
-        ipc_send_event("workspace", I3_IPC_EVENT_WORKSPACE, "{\"change\":\"urgent\"}");
284
+        ipc_send_workspace_event("urgent", ws, NULL);
285
 }
286
 
287
 /*

b/testcases/t/115-ipc-workspaces.t

292
@@ -23,7 +23,7 @@ $i3->connect()->recv;
293
 # Workspaces requests and events
294
 ################################
295
 
296
-my $focused = get_ws(focused_ws());
297
+my $old_ws = get_ws(focused_ws());
298
 
299
 # Events
300
 
301
@@ -36,15 +36,11 @@ $i3->subscribe({
302
     workspace => sub {
303
         my ($event) = @_;
304
         if ($event->{change} eq 'init') {
305
-            $init->send(1);
306
+            $init->send($event);
307
         } elsif ($event->{change} eq 'focus') {
308
-            # Check that we have the old and new workspace
309
-            $focus->send(
310
-                $event->{current}->{name} == '2' &&
311
-                $event->{old}->{name} == $focused->{name}
312
-            );
313
+            $focus->send($event);
314
         } elsif ($event->{change} eq 'empty') {
315
-            $empty->send(1);
316
+            $empty->send($event);
317
         }
318
     }
319
 })->recv;
320
@@ -61,8 +57,20 @@ $t = AnyEvent->timer(
321
     }
322
 );
323
 
324
-ok($init->recv, 'Workspace "init" event received');
325
-ok($focus->recv, 'Workspace "focus" event received');
326
-ok($empty->recv, 'Workspace "empty" event received');
327
+my $init_event = $init->recv;
328
+my $focus_event = $focus->recv;
329
+my $empty_event = $empty->recv;
330
+
331
+my $current_ws = get_ws(focused_ws());
332
+
333
+ok($init_event, 'workspace "init" event received');
334
+is($init_event->{current}->{id}, $current_ws->{id}, 'the "current" property should contain the initted workspace con');
335
+
336
+ok($focus_event, 'workspace "focus" event received');
337
+is($focus_event->{current}->{id}, $current_ws->{id}, 'the "current" property should contain the focused workspace con');
338
+is($focus_event->{old}->{id}, $old_ws->{id}, 'the "old" property should contain the workspace con that was focused last');
339
+
340
+ok($empty_event, 'workspace "empty" event received');
341
+is($empty_event->{current}->{id}, $old_ws->{id}, 'the "current" property should contain the emptied workspace con');
342
 
343
 done_testing;

b/testcases/t/227-ipc-workspace-empty.t

348
@@ -50,6 +50,7 @@ subtest 'Workspace empty event upon switch', sub {
349
 
350
     my $event = $cond->recv;
351
     is($event->{change}, 'empty', '"Empty" event received upon workspace switch');
352
+    is($event->{current}->{name}, $ws1, '"current" property should be set to the workspace con');
353
 };
354
 
355
 ################################################################################
356
@@ -116,6 +117,7 @@ subtest 'Workspace empty event upon window close', sub {
357
 
358
     my $event = $cond->recv;
359
     is($event->{change}, 'empty', '"Empty" event received upon window close');
360
+    is($event->{current}->{name}, $ws1, '"current" property should be set to the workspace con');
361
 };
362
 
363
 }