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. Adds the _NET_CLIENT_LIST Atom to the _NET_SUPPORTED root property to claim support of this feature.
To apply this patch, use:
curl http://cr.i3wm.org/patch/396/raw.patch | git am
b/include/atoms.xmacro
24 |
@@ -12,6 +12,7 @@ xmacro(_NET_WM_WINDOW_TYPE_TOOLBAR) |
25 |
xmacro(_NET_WM_WINDOW_TYPE_SPLASH) |
26 |
xmacro(_NET_WM_DESKTOP) |
27 |
xmacro(_NET_WM_STRUT_PARTIAL) |
28 |
+xmacro(_NET_CLIENT_LIST) |
29 |
xmacro(_NET_CLIENT_LIST_STACKING) |
30 |
xmacro(_NET_CURRENT_DESKTOP) |
31 |
xmacro(_NET_ACTIVE_WINDOW) |
b/include/ewmh.h
36 |
@@ -28,6 +28,11 @@ void ewmh_update_current_desktop(void); |
37 |
void ewmh_update_active_window(xcb_window_t window); |
38 |
|
39 |
/** |
40 |
+ * Updates the _NET_CLIENT_LIST hint. Used for window listers. |
41 |
+ */ |
42 |
+void ewmh_update_client_list(xcb_window_t *list, int num_windows); |
43 |
+ |
44 |
+/** |
45 |
* Updates the _NET_CLIENT_LIST_STACKING hint. Necessary to move tabs in |
46 |
* Chromium correctly. |
47 |
* |
b/src/ewmh.c
52 |
@@ -69,6 +69,22 @@ void ewmh_update_workarea(void) { |
53 |
} |
54 |
|
55 |
/* |
56 |
+ * Updates the _NET_CLIENT_LIST hint. |
57 |
+ * |
58 |
+ */ |
59 |
+void ewmh_update_client_list(xcb_window_t *list, int num_windows) { |
60 |
+ xcb_change_property( |
61 |
+ conn, |
62 |
+ XCB_PROP_MODE_REPLACE, |
63 |
+ root, |
64 |
+ A__NET_CLIENT_LIST, |
65 |
+ XCB_ATOM_WINDOW, |
66 |
+ 32, |
67 |
+ num_windows, |
68 |
+ list); |
69 |
+} |
70 |
+ |
71 |
+/* |
72 |
* Updates the _NET_CLIENT_LIST_STACKING hint. |
73 |
* |
74 |
*/ |
75 |
@@ -119,5 +135,5 @@ void ewmh_setup_hints(void) { |
76 |
/* I’m not entirely sure if we need to keep _NET_WM_NAME on root. */ |
77 |
xcb_change_property(conn, XCB_PROP_MODE_REPLACE, root, A__NET_WM_NAME, A_UTF8_STRING, 8, strlen("i3"), "i3"); |
78 |
|
79 |
- xcb_change_property(conn, XCB_PROP_MODE_REPLACE, root, A__NET_SUPPORTED, XCB_ATOM_ATOM, 32, 18, supported_atoms); |
80 |
+ xcb_change_property(conn, XCB_PROP_MODE_REPLACE, root, A__NET_SUPPORTED, XCB_ATOM_ATOM, 32, 19, supported_atoms); |
81 |
} |
b/src/x.c
86 |
@@ -15,11 +15,6 @@ |
87 |
/* Stores the X11 window ID of the currently focused window */ |
88 |
xcb_window_t focused_id = XCB_NONE; |
89 |
|
90 |
-/* The bottom-to-top window stack of all windows which are managed by i3. |
91 |
- * Used for x_get_window_stack(). */ |
92 |
-static xcb_window_t *btt_stack; |
93 |
-static int btt_stack_num; |
94 |
- |
95 |
/* Stores coordinates to warp mouse pointer to if set */ |
96 |
static Rect *warp_to; |
97 |
|
98 |
@@ -55,6 +50,7 @@ typedef struct con_state { |
99 |
|
100 |
CIRCLEQ_ENTRY(con_state) state; |
101 |
CIRCLEQ_ENTRY(con_state) old_state; |
102 |
+ TAILQ_ENTRY(con_state) initial_mapping_order; |
103 |
} con_state; |
104 |
|
105 |
CIRCLEQ_HEAD(state_head, con_state) state_head = |
106 |
@@ -63,6 +59,9 @@ CIRCLEQ_HEAD(state_head, con_state) state_head = |
107 |
CIRCLEQ_HEAD(old_state_head, con_state) old_state_head = |
108 |
CIRCLEQ_HEAD_INITIALIZER(old_state_head); |
109 |
|
110 |
+TAILQ_HEAD(client_head, con_state) client_head = |
111 |
+ TAILQ_HEAD_INITIALIZER(client_head); |
112 |
+ |
113 |
/* |
114 |
* Returns the container state for the given frame. This function always |
115 |
* returns a container state (otherwise, there is a bug in the code and the |
116 |
@@ -146,8 +145,10 @@ void x_con_init(Con *con, uint16_t depth) { |
117 |
state->id = con->frame; |
118 |
state->mapped = false; |
119 |
state->initial = true; |
120 |
+ DLOG("Adding window 0x%08x to lists\n", state->id); |
121 |
CIRCLEQ_INSERT_HEAD(&state_head, state, state); |
122 |
CIRCLEQ_INSERT_HEAD(&old_state_head, state, old_state); |
123 |
+ TAILQ_INSERT_TAIL(&client_head, state, initial_mapping_order); |
124 |
DLOG("adding new state for window id 0x%08x\n", state->id); |
125 |
} |
126 |
|
127 |
@@ -228,6 +229,7 @@ void x_con_kill(Con *con) { |
128 |
state = state_for_frame(con->frame); |
129 |
CIRCLEQ_REMOVE(&state_head, state, state); |
130 |
CIRCLEQ_REMOVE(&old_state_head, state, old_state); |
131 |
+ TAILQ_REMOVE(&client_head, state, initial_mapping_order); |
132 |
FREE(state->name); |
133 |
free(state); |
134 |
|
135 |
@@ -863,6 +865,9 @@ void x_push_changes(Con *con) { |
136 |
//DLOG("Done, EnterNotify disabled\n"); |
137 |
bool order_changed = false; |
138 |
bool stacking_changed = false; |
139 |
+ |
140 |
+ /* Flags clients created or destroyed. */ |
141 |
+ bool client_list_changed = false; |
142 |
|
143 |
/* count first, necessary to (re)allocate memory for the bottom-to-top |
144 |
* stack afterwards */ |
145 |
@@ -871,12 +876,20 @@ void x_push_changes(Con *con) { |
146 |
if (state->con && state->con->window) |
147 |
cnt++; |
148 |
|
149 |
- if (cnt != btt_stack_num) { |
150 |
- btt_stack = srealloc(btt_stack, sizeof(xcb_window_t) * cnt); |
151 |
- btt_stack_num = cnt; |
152 |
+ /* The bottom-to-top window stack of all windows which are managed by i3. |
153 |
+ * Used for x_get_window_stack(). */ |
154 |
+ static xcb_window_t *client_list_windows = NULL; |
155 |
+ static int client_list_count = 0; |
156 |
+ |
157 |
+ if (cnt != client_list_count) { |
158 |
+ client_list_windows = srealloc(client_list_windows, sizeof(xcb_window_t) * cnt); |
159 |
+ client_list_count = cnt; |
160 |
+ client_list_changed = true; |
161 |
} |
162 |
|
163 |
- xcb_window_t *walk = btt_stack; |
164 |
+ DLOG("Getting window stacking order\n"); |
165 |
+ |
166 |
+ xcb_window_t *walk = client_list_windows; |
167 |
|
168 |
/* X11 correctly represents the stack if we push it from bottom to top */ |
169 |
CIRCLEQ_FOREACH_REVERSE(state, &state_head, state) { |
170 |
@@ -904,7 +917,27 @@ void x_push_changes(Con *con) { |
171 |
/* If we re-stacked something (or a new window appeared), we need to update |
172 |
* the _NET_CLIENT_LIST_STACKING hint */ |
173 |
if (stacking_changed) |
174 |
- ewmh_update_client_list_stacking(btt_stack, btt_stack_num); |
175 |
+ ewmh_update_client_list_stacking(client_list_windows, client_list_count); |
176 |
+ |
177 |
+ /* If the client list changed since the last call update the |
178 |
+ * _NET_CLIENT_LIST property. */ |
179 |
+ if (client_list_changed) { |
180 |
+ DLOG("Client list changed (%i clients)\n", cnt); |
181 |
+ |
182 |
+ if (cnt) { |
183 |
+ walk = client_list_windows; |
184 |
+ |
185 |
+ TAILQ_FOREACH(state, &client_head, initial_mapping_order) { |
186 |
+ assert((client_list_windows - walk) < cnt); |
187 |
+ if (state->con && state->con->window) |
188 |
+ *walk++ = state->con->window->id; |
189 |
+ } |
190 |
+ |
191 |
+ ewmh_update_client_list(client_list_windows, client_list_count); |
192 |
+ } else { |
193 |
+ ewmh_update_client_list(NULL, 0); |
194 |
+ } |
195 |
+ } |
196 |
|
197 |
DLOG("PUSHING CHANGES\n"); |
198 |
x_push_node(con); |