i3 - improved tiling WM


Abstract wm event sending to wm_events.[ch]

Patch status: rejected

Patch by Tony Crisci

Long description:

Abstract handling of sending general wm events to wm_events.[ch]. These
functions will send notifications to listeners through the i3ipc and by
updating supported EWMH properties on the root window.

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

b/include/all.h

27
@@ -72,6 +72,7 @@
28
 #include "move.h"
29
 #include "output.h"
30
 #include "ewmh.h"
31
+#include "wm_events.h"
32
 #include "assignments.h"
33
 #include "regex.h"
34
 #include "libi3.h"

b/include/wm_events.h

40
@@ -0,0 +1,54 @@
41
+/*
42
+ * vim:ts=4:sw=4:expandtab
43
+ *
44
+ * i3 - an improved dynamic tiling window manager
45
+ * © 2009-2014 Michael Stapelberg and contributors (see also: LICENSE)
46
+ *
47
+ * wm_events.c: Functions to send window manager events through the ipc and
48
+ * update the properties of the root window to indicate changes in window
49
+ * manager state.
50
+ */
51
+#pragma once
52
+
53
+/**
54
+ * The "detail" of an event corresponds to the "change" field in the event to
55
+ * send.
56
+ */
57
+typedef enum {
58
+    EVENT_DETAIL_0,
59
+
60
+    E_CHANGE_FOCUS,
61
+    E_CHANGE_INIT,
62
+    E_CHANGE_RELOAD,
63
+    E_CHANGE_RENAME,
64
+    E_CHANGE_EMPTY,
65
+    E_CHANGE_UNSPECIFIED,
66
+    E_CHANGE_TITLE,
67
+    E_CHANGE_NEW,
68
+    E_CHANGE_URGENT,
69
+    E_CHANGE_MOVE,
70
+    E_CHANGE_FULLSCREEN_MODE,
71
+
72
+    N_EVENT_DETAILS,
73
+} event_detail_t;
74
+
75
+/**
76
+ * Sends a "workspace" event to ipc listeners with the "change" field set to
77
+ * the corresponding detail string and changes supported EWMH "desktop"
78
+ * properties on the root window.
79
+ */
80
+void send_workspace_event(event_detail_t detail, Con *current, Con *old);
81
+
82
+/**
83
+ * Sends a "window" event to ipc listeners with the "change" field set to the
84
+ * corresponding detail string and changes supported EWMH properties on the
85
+ * root window (currently only _NET_ACTIVE_WINDOW is supported).
86
+ */
87
+void send_window_event(event_detail_t detail, Con *con);
88
+
89
+/**
90
+ * Sends an "output" event to ipc listeners with the "change" field set to the
91
+ * corresponding detail string and changes supported EWMH properties on the
92
+ * root window (currently none are supported).
93
+ */
94
+void send_output_event(event_detail_t detail);

b/src/commands.c

99
@@ -1232,7 +1232,7 @@ void cmd_move_workspace_to_output(I3_CMD, char *name) {
100
                 create_workspace_on_output(current_output, ws->parent);
101
 
102
             /* notify the IPC listeners */
103
-            ipc_send_event("workspace", I3_IPC_EVENT_WORKSPACE, "{\"change\":\"init\"}");
104
+            send_workspace_event(E_CHANGE_INIT, NULL, NULL);
105
         }
106
         DLOG("Detaching\n");
107
 
108
@@ -1253,7 +1253,7 @@ void cmd_move_workspace_to_output(I3_CMD, char *name) {
109
         TAILQ_FOREACH(floating_con, &(ws->floating_head), floating_windows)
110
             floating_fix_coordinates(floating_con, &(old_content->rect), &(content->rect));
111
 
112
-        ipc_send_event("workspace", I3_IPC_EVENT_WORKSPACE, "{\"change\":\"move\"}");
113
+        send_workspace_event(E_CHANGE_MOVE, NULL, NULL);
114
         if (workspace_was_visible) {
115
             /* Focus the moved workspace on the destination output. */
116
             workspace_show(ws);
117
@@ -1662,7 +1662,7 @@ void cmd_reload(I3_CMD) {
118
     load_configuration(conn, NULL, true);
119
     x_set_i3_atoms();
120
     /* Send an IPC event just in case the ws names have changed */
121
-    ipc_send_event("workspace", I3_IPC_EVENT_WORKSPACE, "{\"change\":\"reload\"}");
122
+    send_workspace_event(E_CHANGE_RELOAD, NULL, NULL);
123
     /* Send an update event for the barconfig just in case it has changed */
124
     update_barconfig();
125
 
126
@@ -1932,7 +1932,7 @@ void cmd_rename_workspace(I3_CMD, char *old_name, char *new_name) {
127
     cmd_output->needs_tree_render = true;
128
     ysuccess(true);
129
 
130
-    ipc_send_event("workspace", I3_IPC_EVENT_WORKSPACE, "{\"change\":\"rename\"}");
131
+    send_workspace_event(E_CHANGE_RENAME, NULL, NULL);
132
 }
133
 
134
 /*

b/src/con.c

139
@@ -621,7 +621,7 @@ void con_toggle_fullscreen(Con *con, int fullscreen_mode) {
140
     DLOG("mode now: %d\n", con->fullscreen_mode);
141
 
142
     /* Send an ipc window "fullscreen_mode" event */
143
-    ipc_send_window_event("fullscreen_mode", con);
144
+    send_window_event(E_CHANGE_FULLSCREEN_MODE, con);
145
 
146
     /* update _NET_WM_STATE if this container has a window */
147
     /* TODO: when a window is assigned to a container which is already
148
@@ -1385,7 +1385,7 @@ static void con_on_remove_child(Con *con) {
149
         if (TAILQ_EMPTY(&(con->focus_head)) && !workspace_is_visible(con)) {
150
             LOG("Closing old workspace (%p / %s), it is empty\n", con, con->name);
151
             tree_close(con, DONT_KILL_WINDOW, false, false);
152
-            ipc_send_event("workspace", I3_IPC_EVENT_WORKSPACE, "{\"change\":\"empty\"}");
153
+            send_workspace_event(E_CHANGE_EMPTY, NULL, NULL);
154
         }
155
         return;
156
     }

b/src/handlers.c

161
@@ -440,7 +440,7 @@ static void handle_screen_change(xcb_generic_event_t *e) {
162
 
163
     scratchpad_fix_resolution();
164
 
165
-    ipc_send_event("output", I3_IPC_EVENT_OUTPUT, "{\"change\":\"unspecified\"}");
166
+    send_output_event(E_CHANGE_UNSPECIFIED);
167
 
168
     return;
169
 }
170
@@ -556,7 +556,7 @@ static bool handle_windowname_change(void *data, xcb_connection_t *conn, uint8_t
171
     x_push_changes(croot);
172
 
173
     if (window_name_changed(con->window, old_name))
174
-        ipc_send_window_event("title", con);
175
+        send_window_event(E_CHANGE_TITLE, con);
176
 
177
     FREE(old_name);
178
 
179
@@ -581,7 +581,7 @@ static bool handle_windowname_change_legacy(void *data, xcb_connection_t *conn,
180
     x_push_changes(croot);
181
 
182
     if (window_name_changed(con->window, old_name))
183
-        ipc_send_window_event("title", con);
184
+        send_window_event(E_CHANGE_TITLE, con);
185
 
186
     FREE(old_name);
187
 

b/src/manage.c

192
@@ -484,7 +484,7 @@ void manage_window(xcb_window_t window, xcb_get_window_attributes_cookie_t cooki
193
     tree_render();
194
 
195
     /* Send an event about window creation */
196
-    ipc_send_window_event("new", nc);
197
+    send_window_event(E_CHANGE_NEW, nc);
198
 
199
     /* Defer setting focus after the 'new' event has been sent to ensure the
200
      * proper window event sequence. */

b/src/move.c

205
@@ -127,7 +127,7 @@ static void move_to_output_directed(Con *con, direction_t direction) {
206
 
207
     tree_flatten(croot);
208
 
209
-    ipc_send_workspace_focus_event(ws, old_ws);
210
+    send_workspace_event(E_CHANGE_FOCUS, ws, old_ws);
211
 }
212
 
213
 /*

b/src/wm_events.c

219
@@ -0,0 +1,79 @@
220
+/*
221
+ * vim:ts=4:sw=4:expandtab
222
+ *
223
+ * i3 - an improved dynamic tiling window manager
224
+ * © 2009-2014 Michael Stapelberg and contributors (see also: LICENSE)
225
+ *
226
+ * wm_events.c: Functions to send window manager events through the ipc and
227
+ * update the properties of the root window to indicate changes in window
228
+ * manager state.
229
+ */
230
+
231
+#include "all.h"
232
+
233
+static const char *detail_strings[] = {
234
+    NULL,
235
+
236
+    "focus",
237
+    "init",
238
+    "reload",
239
+    "rename",
240
+    "empty",
241
+    "unspecified",
242
+    "title",
243
+    "new",
244
+    "urgent",
245
+    "move",
246
+    "fullscreen_mode",
247
+};
248
+
249
+static const char *detail_to_payload(event_detail_t detail) {
250
+    static char *payloads[N_EVENT_DETAILS];
251
+    char *format = "{\"change\":\"%s\"}";
252
+
253
+    if (payloads[detail] != NULL)
254
+        return payloads[detail];
255
+
256
+    char *payload = smalloc(sizeof(format) + sizeof(detail_strings[detail]));
257
+    sasprintf(&payload, format, detail_strings[detail]);
258
+
259
+    payloads[detail] = payload;
260
+
261
+    return payloads[detail];
262
+}
263
+
264
+/*
265
+ * Sends a "workspace" event to ipc listeners with the "change" field set to
266
+ * the corresponding detail string and changes supported EWMH "desktop"
267
+ * properties on the root window.
268
+ */
269
+void send_workspace_event(event_detail_t detail, Con *current, Con *old) {
270
+    if (detail == E_CHANGE_FOCUS) {
271
+        ipc_send_workspace_focus_event(current, old);
272
+        ewmh_update_current_desktop();
273
+    }
274
+    else {
275
+        ipc_send_event("workspace", I3_IPC_EVENT_WORKSPACE, detail_to_payload(detail));
276
+    }
277
+}
278
+
279
+/*
280
+ * Sends a "window" event to ipc listeners with the "change" field set to the
281
+ * corresponding detail string and changes supported EWMH properties on the
282
+ * root window (currently only _NET_ACTIVE_WINDOW is supported).
283
+ */
284
+void send_window_event(event_detail_t detail, Con *con) {
285
+    if (detail == E_CHANGE_FOCUS && con_has_managed_window(con))
286
+        ewmh_update_active_window(con->window->id);
287
+
288
+    ipc_send_window_event(detail_strings[detail], con);
289
+}
290
+
291
+/*
292
+ * Sends an "output" event to ipc listeners with the "change" field set to the
293
+ * corresponding detail string and changes supported EWMH properties on the
294
+ * root window (currently none are supported).
295
+ */
296
+void send_output_event(event_detail_t detail) {
297
+    ipc_send_event("output", I3_IPC_EVENT_OUTPUT, detail_to_payload(detail));
298
+}

b/src/workspace.c

303
@@ -91,7 +91,7 @@ Con *workspace_get(const char *num, bool *created) {
304
 
305
         con_attach(workspace, content, false);
306
 
307
-        ipc_send_event("workspace", I3_IPC_EVENT_WORKSPACE, "{\"change\":\"init\"}");
308
+        send_workspace_event(E_CHANGE_INIT, NULL, NULL);
309
         if (created != NULL)
310
             *created = true;
311
     }
312
@@ -411,7 +411,7 @@ static void _workspace_show(Con *workspace) {
313
     } else
314
         con_focus(next);
315
 
316
-    ipc_send_workspace_focus_event(workspace, current);
317
+    send_workspace_event(E_CHANGE_FOCUS, workspace, current);
318
 
319
     DLOG("old = %p / %s\n", old, (old ? old->name : "(null)"));
320
     /* Close old workspace if necessary. This must be done *after* doing
321
@@ -424,7 +424,7 @@ static void _workspace_show(Con *workspace) {
322
         if (!workspace_is_visible(old)) {
323
             LOG("Closing old workspace (%p / %s), it is empty\n", old, old->name);
324
             tree_close(old, DONT_KILL_WINDOW, false, false);
325
-            ipc_send_event("workspace", I3_IPC_EVENT_WORKSPACE, "{\"change\":\"empty\"}");
326
+            send_workspace_event(E_CHANGE_EMPTY, NULL, NULL);
327
         }
328
     }
329
 
330
@@ -436,9 +436,6 @@ static void _workspace_show(Con *workspace) {
331
     if (old_output != new_output) {
332
         x_set_warp_to(&next->rect);
333
     }
334
-
335
-    /* Update the EWMH hints */
336
-    ewmh_update_current_desktop();
337
 }
338
 
339
 /*
340
@@ -766,7 +763,7 @@ void workspace_update_urgent_flag(Con *ws) {
341
     DLOG("Workspace urgency flag changed from %d to %d\n", old_flag, ws->urgent);
342
 
343
     if (old_flag != ws->urgent)
344
-        ipc_send_event("workspace", I3_IPC_EVENT_WORKSPACE, "{\"change\":\"urgent\"}");
345
+        send_workspace_event(E_CHANGE_URGENT, NULL, NULL);
346
 }
347
 
348
 /*

b/src/x.c

353
@@ -1014,7 +1014,7 @@ void x_push_changes(Con *con) {
354
                 send_take_focus(to_focus, last_timestamp);
355
 
356
                 if (to_focus != last_focused && is_con_attached(focused))
357
-                   ipc_send_window_event("focus", focused);
358
+                    send_window_event(E_CHANGE_FOCUS, focused);
359
             } else {
360
                 DLOG("Updating focus (focused: %p / %s) to X11 window 0x%08x\n", focused, focused->name, to_focus);
361
                 /* We remove XCB_EVENT_MASK_FOCUS_CHANGE from the event mask to get
362
@@ -1030,10 +1030,8 @@ void x_push_changes(Con *con) {
363
                     xcb_change_window_attributes(conn, focused->window->id, XCB_CW_EVENT_MASK, values);
364
                 }
365
 
366
-                ewmh_update_active_window(to_focus);
367
-
368
                 if (to_focus != XCB_NONE && to_focus != last_focused && focused->window != NULL && is_con_attached(focused))
369
-                   ipc_send_window_event("focus", focused);
370
+                    send_window_event(E_CHANGE_FOCUS, focused);
371
             }
372
 
373
             focused_id = last_focused = to_focus;