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