i3 - improved tiling WM


Add support for the _NET_CLIENT_LIST root window property.

Patch status: needinfo

Patch by Steve Jones

Long description:

This sets the the _NET_CLIENT_LIST property in x_push_changes when the
client list changes. Changes to the client list are tracked by the
client_list_changed flag which is updated by x_con_init and x_con_kill.
The client list is maintained in the order of connecting by the TAILQ
with head client_head.

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

b/include/atoms.xmacro

22
@@ -12,6 +12,7 @@ xmacro(_NET_WM_WINDOW_TYPE_TOOLBAR)
23
 xmacro(_NET_WM_WINDOW_TYPE_SPLASH)
24
 xmacro(_NET_WM_DESKTOP)
25
 xmacro(_NET_WM_STRUT_PARTIAL)
26
+xmacro(_NET_CLIENT_LIST)
27
 xmacro(_NET_CLIENT_LIST_STACKING)
28
 xmacro(_NET_CURRENT_DESKTOP)
29
 xmacro(_NET_ACTIVE_WINDOW)

b/include/ewmh.h

34
@@ -28,6 +28,11 @@ void ewmh_update_current_desktop(void);
35
 void ewmh_update_active_window(xcb_window_t window);
36
 
37
 /**
38
+ * Updates the _NET_CLIENT_LIST hint. Used for window listers.
39
+ */
40
+void ewmh_update_client_list(xcb_window_t *list, int num_windows);
41
+
42
+/**
43
  * Updates the _NET_CLIENT_LIST_STACKING hint. Necessary to move tabs in
44
  * Chromium correctly.
45
  *

b/src/ewmh.c

50
@@ -69,6 +69,22 @@ void ewmh_update_workarea(void) {
51
 }
52
 
53
 /*
54
+ * Updates the _NET_CLIENT_LIST hint.
55
+ *
56
+ */
57
+void ewmh_update_client_list(xcb_window_t *list, int num_windows) {
58
+    xcb_change_property(
59
+        conn,
60
+        XCB_PROP_MODE_REPLACE,
61
+        root,
62
+        A__NET_CLIENT_LIST,
63
+        XCB_ATOM_WINDOW,
64
+        32,
65
+        num_windows,
66
+        list);
67
+}
68
+
69
+/*
70
  * Updates the _NET_CLIENT_LIST_STACKING hint.
71
  *
72
  */

b/src/x.c

77
@@ -20,6 +20,9 @@ xcb_window_t focused_id = XCB_NONE;
78
 static xcb_window_t *btt_stack;
79
 static int btt_stack_num;
80
 
81
+/* Flags new clients created or destroyed, used in x_push_changes */
82
+static bool client_list_changed = false;
83
+
84
 /* Stores coordinates to warp mouse pointer to if set */
85
 static Rect *warp_to;
86
 
87
@@ -55,6 +58,7 @@ typedef struct con_state {
88
 
89
     CIRCLEQ_ENTRY(con_state) state;
90
     CIRCLEQ_ENTRY(con_state) old_state;
91
+    TAILQ_ENTRY(con_state) client_order;
92
 } con_state;
93
 
94
 CIRCLEQ_HEAD(state_head, con_state) state_head =
95
@@ -63,6 +67,9 @@ CIRCLEQ_HEAD(state_head, con_state) state_head =
96
 CIRCLEQ_HEAD(old_state_head, con_state) old_state_head =
97
     CIRCLEQ_HEAD_INITIALIZER(old_state_head);
98
 
99
+TAILQ_HEAD(client_head, con_state) client_head =
100
+    TAILQ_HEAD_INITIALIZER(client_head);
101
+
102
 /*
103
  * Returns the container state for the given frame. This function always
104
  * returns a container state (otherwise, there is a bug in the code and the
105
@@ -146,8 +153,11 @@ void x_con_init(Con *con, uint16_t depth) {
106
     state->id = con->frame;
107
     state->mapped = false;
108
     state->initial = true;
109
+    DLOG("Adding window 0x%08x to lists\n", state->id);
110
     CIRCLEQ_INSERT_HEAD(&state_head, state, state);
111
     CIRCLEQ_INSERT_HEAD(&old_state_head, state, old_state);
112
+    TAILQ_INSERT_TAIL(&client_head, state, client_order);
113
+    client_list_changed = true;
114
     DLOG("adding new state for window id 0x%08x\n", state->id);
115
 }
116
 
117
@@ -228,6 +238,8 @@ void x_con_kill(Con *con) {
118
     state = state_for_frame(con->frame);
119
     CIRCLEQ_REMOVE(&state_head, state, state);
120
     CIRCLEQ_REMOVE(&old_state_head, state, old_state);
121
+    TAILQ_REMOVE(&client_head, state, client_order);
122
+    client_list_changed = true;
123
     FREE(state->name);
124
     free(state);
125
 
126
@@ -876,6 +888,8 @@ void x_push_changes(Con *con) {
127
         btt_stack_num = cnt;
128
     }
129
 
130
+    DLOG("Getting window stacking order\n");
131
+
132
     xcb_window_t *walk = btt_stack;
133
 
134
     /* X11 correctly represents the stack if we push it from bottom to top */
135
@@ -906,6 +920,33 @@ void x_push_changes(Con *con) {
136
     if (stacking_changed)
137
         ewmh_update_client_list_stacking(btt_stack, btt_stack_num);
138
 
139
+    /* If the client list changed since the last call update the
140
+     * _NET_CLIENT_LIST property. */
141
+    if (client_list_changed) {
142
+	xcb_window_t *client_list;
143
+
144
+	DLOG("Client list changed (%i clients)\n", cnt);
145
+
146
+	if (cnt) {
147
+	    client_list = smalloc(cnt * sizeof(xcb_window_t));
148
+	    walk = client_list;
149
+
150
+	    TAILQ_FOREACH(state, &client_head, client_order) {
151
+		assert((client_list - walk) < cnt);
152
+		if (state->con && state->con->window) 
153
+		    *walk++ = state->con->window->id;
154
+	    }
155
+
156
+	    ewmh_update_client_list(client_list, cnt);
157
+
158
+	    free(client_list);
159
+	} else {
160
+	    ewmh_update_client_list(NULL, 0);
161
+	}
162
+
163
+	client_list_changed = false;
164
+    }
165
+
166
     DLOG("PUSHING CHANGES\n");
167
     x_push_node(con);
168