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