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); |