+void ewmh_update_current_desktop(void) {
+ Con *focused_ws = con_get_workspace(focused);
+ Con *output;
+ uint32_t idx = 0;
+ /* We count to get the index of this workspace because named workspaces
+ * don’t have the ->num property */
+ TAILQ_FOREACH(output, &(croot->nodes_head), nodes) {
+ Con *ws;
+ TAILQ_FOREACH(ws, &(output_get_content(output)->nodes_head), nodes) {
+ if (STARTS_WITH(ws->name, "__"))
+ continue;
+
+ if (ws == focused_ws) {
+ xcb_change_property(conn, XCB_PROP_MODE_REPLACE, root,
+ A__NET_CURRENT_DESKTOP, XCB_ATOM_CARDINAL, 32, 1, &idx);
+ return;
+ }
+ ++idx;
+ }
+ }
+}
+
+/*
+ * Updates _NET_NUMBER_OF_DESKTOPS which we interpret as the number of
+ * noninternal workspaces.
+ */
+void ewmh_update_number_of_desktops(void) {
+ Con *output;
+ uint32_t idx = 0;
+
+ TAILQ_FOREACH(output, &(croot->nodes_head), nodes) {
+ Con *ws;
+ TAILQ_FOREACH(ws, &(output_get_content(output)->nodes_head), nodes) {
+ if (STARTS_WITH(ws->name, "__"))
+ continue;
+ ++idx;
+ }
+ }
+
+ xcb_change_property(conn, XCB_PROP_MODE_REPLACE, root,
+ A__NET_NUMBER_OF_DESKTOPS, XCB_ATOM_CARDINAL, 32, 1, &idx);
+}
+
+/*
+ * Updates _NET_DESKTOP_NAMES: "The names of all virtual desktops. This is a
+ * list of NULL-terminated strings in UTF-8 encoding"
+ */
+void ewmh_update_desktop_names(void) {
+ Con *output;
+ int msg_length = 0;
+
+ /* count the size of the property message to set */
+ TAILQ_FOREACH(output, &(croot->nodes_head), nodes) {
+ Con *ws;
+ TAILQ_FOREACH(ws, &(output_get_content(output)->nodes_head), nodes) {
+ if (STARTS_WITH(ws->name, "__"))
+ continue;
+ msg_length += strlen(ws->name) + 1;
+ }
+ }
+
+ char desktop_names[msg_length];
+ int current_position = 0;
+
+ /* fill the buffer with the names of the i3 workspaces */
+ TAILQ_FOREACH(output, &(croot->nodes_head), nodes) {
+ Con *ws;
+ TAILQ_FOREACH(ws, &(output_get_content(output)->nodes_head), nodes) {
+ if (STARTS_WITH(ws->name, "__"))
+ continue;
+
+ for (size_t i = 0; i < strlen(ws->name) + 1; i++) {
+ desktop_names[current_position++] = ws->name[i];
+ }
+ }
+ }
+
+ xcb_change_property(conn, XCB_PROP_MODE_REPLACE, root,
+ A__NET_DESKTOP_NAMES, A_UTF8_STRING, 8, msg_length, desktop_names);
+}
+
+/*
+ * Updates _NET_DESKTOP_VIEWPORT, which is an array of pairs of cardinals that
+ * define the top left corner of each desktop's viewport.
+ */
+void ewmh_update_desktop_viewport(void) {
+ Con *output;
+ int num_desktops = 0;
+ /* count number of desktops */
+ TAILQ_FOREACH(output, &(croot->nodes_head), nodes) {
+ Con *ws;
+ TAILQ_FOREACH(ws, &(output_get_content(output)->nodes_head), nodes) {
+ if (STARTS_WITH(ws->name, "__"))
+ continue;
+
+ num_desktops++;
+ }
+ }
+
+ uint32_t viewports[num_desktops * 2];
+
+ int current_position = 0;
+ /* fill the viewport buffer */
+ TAILQ_FOREACH(output, &(croot->nodes_head), nodes) {
+ Con *ws;
+ TAILQ_FOREACH(ws, &(output_get_content(output)->nodes_head), nodes) {
+ if (STARTS_WITH(ws->name, "__"))
+ continue;
+
+ viewports[current_position++] = output->rect.x;
+ viewports[current_position++] = output->rect.y;
+ }
+ }
+
+ xcb_change_property(conn, XCB_PROP_MODE_REPLACE, root,
+ A__NET_DESKTOP_VIEWPORT, XCB_ATOM_CARDINAL, 32, current_position, &viewports);