+
+/*
+ * Set or remove _NET_WM_STATE_STICKY on the window.
+ *
+ */
+void ewmh_update_sticky(xcb_window_t window, bool sticky) {
+ if (sticky) {
+ DLOG("Setting _NET_WM_STATE_STICKY for window = %d.\n", window);
+ xcb_add_property_atom(conn, window, A__NET_WM_STATE, A__NET_WM_STATE_STICKY);
+ } else {
+ DLOG("Removing _NET_WM_STATE_STICKY for window = %d.\n", window);
+ xcb_remove_property_atom(conn, window, A__NET_WM_STATE, A__NET_WM_STATE_STICKY);
+ }
+}
+
+/*
+ * Set or remove _NEW_WM_STATE_FOCUSED on the window.
+ *
+ */
+void ewmh_update_focused(xcb_window_t window, bool is_focused) {
+ if (is_focused) {
+ DLOG("Setting _NET_WM_STATE_FOCUSED for window = %d.\n", window);
+ xcb_add_property_atom(conn, window, A__NET_WM_STATE, A__NET_WM_STATE_FOCUSED);
+ } else {
+ DLOG("Removing _NET_WM_STATE_FOCUSED for window = %d.\n", window);
+ xcb_remove_property_atom(conn, window, A__NET_WM_STATE, A__NET_WM_STATE_FOCUSED);
+ }
+}
+
+/*
+ * Set up the EWMH hints on the root window.
+ *
+ */
+void ewmh_setup_hints(void) {
+ xcb_atom_t supported_atoms[] = {
+#define xmacro(atom) A_##atom,
+#include "atoms_NET_SUPPORTED.xmacro"
+#undef xmacro
+ };
+
+ /* Set up the window manager’s name. According to EWMH, section "Root Window
+ * Properties", to indicate that an EWMH-compliant window manager is
+ * present, a child window has to be created (and kept alive as long as the
+ * window manager is running) which has the _NET_SUPPORTING_WM_CHECK and
+ * _NET_WM_ATOMS. */
+ ewmh_window = xcb_generate_id(conn);
+ /* We create the window and put it at (-1, -1) so that it is off-screen. */
+ xcb_create_window(
+ conn,
+ XCB_COPY_FROM_PARENT, /* depth */
+ ewmh_window, /* window id */
+ root, /* parent */
+ -1, -1, 1, 1, /* dimensions (x, y, w, h) */
+ 0, /* border */
+ XCB_WINDOW_CLASS_INPUT_ONLY, /* window class */
+ XCB_COPY_FROM_PARENT, /* visual */
+ XCB_CW_OVERRIDE_REDIRECT,
+ (uint32_t[]){1});
+ xcb_change_property(conn, XCB_PROP_MODE_REPLACE, ewmh_window, A__NET_SUPPORTING_WM_CHECK, XCB_ATOM_WINDOW, 32, 1, &ewmh_window);
+ xcb_change_property(conn, XCB_PROP_MODE_REPLACE, ewmh_window, A__NET_WM_NAME, A_UTF8_STRING, 8, strlen("i3"), "i3");
+ xcb_change_property(conn, XCB_PROP_MODE_REPLACE, root, A__NET_SUPPORTING_WM_CHECK, XCB_ATOM_WINDOW, 32, 1, &ewmh_window);
+
+ /* I’m not entirely sure if we need to keep _NET_WM_NAME on root. */
+ xcb_change_property(conn, XCB_PROP_MODE_REPLACE, root, A__NET_WM_NAME, A_UTF8_STRING, 8, strlen("i3"), "i3");
+
+ xcb_change_property(conn, XCB_PROP_MODE_REPLACE, root, A__NET_SUPPORTED, XCB_ATOM_ATOM, 32, /* number of atoms */ sizeof(supported_atoms) / sizeof(xcb_atom_t), supported_atoms);
+
+ /* We need to map this window to be able to set the input focus to it if no other window is available to be focused. */
+ xcb_map_window(conn, ewmh_window);
+ xcb_configure_window(conn, ewmh_window, XCB_CONFIG_WINDOW_STACK_MODE, (uint32_t[]){XCB_STACK_MODE_BELOW});
+}
+
+/*
+ * Returns the workspace container as enumerated by the EWMH desktop model.
+ * Returns NULL if no workspace could be found for the index.
+ *
+ * This is the reverse of ewmh_get_workspace_index.
+ *
+ */
+Con *ewmh_get_workspace_by_index(uint32_t idx) {
+ if (idx == NET_WM_DESKTOP_NONE)
+ return NULL;
+
+ uint32_t current_index = 0;
+
+ Con *output;
+ TAILQ_FOREACH(output, &(croot->nodes_head), nodes) {
+ Con *workspace;
+ TAILQ_FOREACH(workspace, &(output_get_content(output)->nodes_head), nodes) {
+ if (con_is_internal(workspace))
+ continue;
+
+ if (current_index == idx)
+ return workspace;
+
+ ++current_index;
+ }
+ }
+
+ return NULL;
+}
+
+/*
+ * Returns the EWMH desktop index for the workspace the given container is on.
+ * Returns NET_WM_DESKTOP_NONE if the desktop index cannot be determined.
+ *
+ * This is the reverse of ewmh_get_workspace_by_index.
+ *
+ */
+uint32_t ewmh_get_workspace_index(Con *con) {
+ uint32_t index = 0;
+
+ Con *workspace = con_get_workspace(con);
+ Con *output;
+ TAILQ_FOREACH(output, &(croot->nodes_head), nodes) {
+ Con *current;
+ TAILQ_FOREACH(current, &(output_get_content(output)->nodes_head), nodes) {
+ if (con_is_internal(current))
+ continue;
+
+ if (current == workspace)
+ return index;
+
+ ++index;
+ }
+ }
+
+ return NET_WM_DESKTOP_NONE;
+}