i3
src/ewmh.c
Go to the documentation of this file.
00001 /*
00002  * vim:ts=4:sw=4:expandtab
00003  *
00004  * i3 - an improved dynamic tiling window manager
00005  * © 2009-2011 Michael Stapelberg and contributors (see also: LICENSE)
00006  *
00007  * ewmh.c: Get/set certain EWMH properties easily.
00008  *
00009  */
00010 #include "all.h"
00011 
00012 /*
00013  * Updates _NET_CURRENT_DESKTOP with the current desktop number.
00014  *
00015  * EWMH: The index of the current desktop. This is always an integer between 0
00016  * and _NET_NUMBER_OF_DESKTOPS - 1.
00017  *
00018  */
00019 void ewmh_update_current_desktop() {
00020     Con *focused_ws = con_get_workspace(focused);
00021     Con *output;
00022     uint32_t idx = 0;
00023     /* We count to get the index of this workspace because named workspaces
00024      * don’t have the ->num property */
00025     TAILQ_FOREACH(output, &(croot->nodes_head), nodes) {
00026         Con *ws;
00027         TAILQ_FOREACH(ws, &(output_get_content(output)->nodes_head), nodes) {
00028             if (ws == focused_ws) {
00029                 xcb_change_property(conn, XCB_PROP_MODE_REPLACE, root,
00030                         A__NET_CURRENT_DESKTOP, XCB_ATOM_CARDINAL, 32, 1, &idx);
00031                 return;
00032             }
00033             ++idx;
00034         }
00035     }
00036 }
00037 
00038 /*
00039  * Updates _NET_ACTIVE_WINDOW with the currently focused window.
00040  *
00041  * EWMH: The window ID of the currently active window or None if no window has
00042  * the focus.
00043  *
00044  */
00045 void ewmh_update_active_window(xcb_window_t window) {
00046     xcb_change_property(conn, XCB_PROP_MODE_REPLACE, root,
00047             A__NET_ACTIVE_WINDOW, XCB_ATOM_WINDOW, 32, 1, &window);
00048 }
00049 
00050 /*
00051  * Updates the workarea for each desktop.
00052  *
00053  * This function is not called at the moment due to:
00054  * http://bugs.i3wm.org/539
00055  * http://bugs.i3wm.org/301
00056  *
00057  * EWMH: Contains a geometry for each desktop. These geometries specify an area
00058  * that is completely contained within the viewport. Work area SHOULD be used by
00059  * desktop applications to place desktop icons appropriately.
00060  *
00061  */
00062 void ewmh_update_workarea() {
00063     int num_workspaces = 0, count = 0;
00064     Rect last_rect = {0, 0, 0, 0};
00065     Con *output;
00066 
00067     TAILQ_FOREACH(output, &(croot->nodes_head), nodes) {
00068         Con *ws;
00069         TAILQ_FOREACH(ws, &(output_get_content(output)->nodes_head), nodes) {
00070             /* Check if we need to initialize last_rect. The case that the
00071              * first workspace is all-zero may happen when the user
00072              * assigned workspace 2 for his first screen, for example. Thus
00073              * we need an initialized last_rect in the very first run of
00074              * the following loop. */
00075             if (last_rect.width == 0 && last_rect.height == 0 &&
00076                     ws->rect.width != 0 && ws->rect.height != 0) {
00077                 memcpy(&last_rect, &(ws->rect), sizeof(Rect));
00078             }
00079             num_workspaces++;
00080         }
00081     }
00082 
00083     DLOG("Got %d workspaces\n", num_workspaces);
00084     uint8_t *workarea = smalloc(sizeof(Rect) * num_workspaces);
00085     TAILQ_FOREACH(output, &(croot->nodes_head), nodes) {
00086         Con *ws;
00087         TAILQ_FOREACH(ws, &(output_get_content(output)->nodes_head), nodes) {
00088             DLOG("storing %d: %dx%d with %d x %d\n", count, ws->rect.x,
00089                  ws->rect.y, ws->rect.width, ws->rect.height);
00090             /* If a workspace is not yet initialized and thus its
00091              * dimensions are zero, we will instead put the dimensions
00092              * of the last workspace in the list. For example firefox
00093              * intersects all workspaces and does not cope so well with
00094              * an all-zero workspace. */
00095             if (ws->rect.width == 0 || ws->rect.height == 0) {
00096                 DLOG("re-using last_rect (%dx%d, %d, %d)\n",
00097                      last_rect.x, last_rect.y, last_rect.width,
00098                      last_rect.height);
00099                 memcpy(workarea + (sizeof(Rect) * count++), &last_rect, sizeof(Rect));
00100                 continue;
00101             }
00102             memcpy(workarea + (sizeof(Rect) * count++), &(ws->rect), sizeof(Rect));
00103             memcpy(&last_rect, &(ws->rect), sizeof(Rect));
00104         }
00105     }
00106     xcb_change_property(conn, XCB_PROP_MODE_REPLACE, root,
00107             A__NET_WORKAREA, XCB_ATOM_CARDINAL, 32,
00108             num_workspaces * (sizeof(Rect) / sizeof(uint32_t)),
00109             workarea);
00110     free(workarea);
00111     xcb_flush(conn);
00112 }
00113 
00114 /*
00115  * Updates the _NET_CLIENT_LIST_STACKING hint.
00116  *
00117  */
00118 void ewmh_update_client_list_stacking(xcb_window_t *stack, int num_windows) {
00119     xcb_change_property(
00120         conn,
00121         XCB_PROP_MODE_REPLACE,
00122         root,
00123         A__NET_CLIENT_LIST_STACKING,
00124         XCB_ATOM_WINDOW,
00125         32,
00126         num_windows,
00127         stack);
00128 }
00129 
00130 /*
00131  * Set up the EWMH hints on the root window.
00132  *
00133  */
00134 void ewmh_setup_hints() {
00135     xcb_atom_t supported_atoms[] = {
00136 #define xmacro(atom) A_ ## atom,
00137 #include "atoms.xmacro"
00138 #undef xmacro
00139     };
00140 
00141     /* Set up the window manager’s name. According to EWMH, section "Root Window
00142      * Properties", to indicate that an EWMH-compliant window manager is
00143      * present, a child window has to be created (and kept alive as long as the
00144      * window manager is running) which has the _NET_SUPPORTING_WM_CHECK and
00145      * _NET_WM_ATOMS. */
00146     xcb_window_t child_window = xcb_generate_id(conn);
00147     xcb_create_window(
00148         conn,
00149         XCB_COPY_FROM_PARENT, /* depth */
00150         child_window, /* window id */
00151         root, /* parent */
00152         0, 0, 1, 1, /* dimensions (x, y, w, h) */
00153         0, /* border */
00154         XCB_WINDOW_CLASS_INPUT_ONLY, /* window class */
00155         XCB_COPY_FROM_PARENT, /* visual */
00156         0,
00157         NULL);
00158     xcb_change_property(conn, XCB_PROP_MODE_REPLACE, child_window, A__NET_SUPPORTING_WM_CHECK, XCB_ATOM_WINDOW, 32, 1, &child_window);
00159     xcb_change_property(conn, XCB_PROP_MODE_REPLACE, child_window, A__NET_WM_NAME, A_UTF8_STRING, 8, strlen("i3"), "i3");
00160     xcb_change_property(conn, XCB_PROP_MODE_REPLACE, root, A__NET_SUPPORTING_WM_CHECK, XCB_ATOM_WINDOW, 32, 1, &child_window);
00161 
00162     /* I’m not entirely sure if we need to keep _NET_WM_NAME on root. */
00163     xcb_change_property(conn, XCB_PROP_MODE_REPLACE, root, A__NET_WM_NAME, A_UTF8_STRING, 8, strlen("i3"), "i3");
00164 
00165     xcb_change_property(conn, XCB_PROP_MODE_REPLACE, root, A__NET_SUPPORTED, XCB_ATOM_ATOM, 32, 16, supported_atoms);
00166 }