]> git.sur5r.net Git - i3/i3/commitdiff
Correctly handle _NET_WM_WINDOW_TYPE == _NET_WM_WINDOW_TYPE_DOCK (for dzen2 -dock)
authorMichael Stapelberg <michael+git@stapelberg.de>
Sun, 22 Feb 2009 23:18:13 +0000 (00:18 +0100)
committerMichael Stapelberg <michael+git@stapelberg.de>
Sun, 22 Feb 2009 23:18:13 +0000 (00:18 +0100)
include/data.h
include/handlers.h
include/i3.h
include/xcb.h
src/handlers.c
src/layout.c
src/mainx.c
src/table.c

index c1e10613ec43105acb227f4624f2cbfe9dca141d..d5f9f27a449e2cbbbe0e7d9880ea0f308d323cd8 100644 (file)
@@ -63,6 +63,9 @@ struct Workspace {
 
         Client *fullscreen_client;
 
+        /* Contains all clients with _NET_WM_WINDOW_TYPE == _NET_WM_WINDOW_TYPE_DOCK */
+        SLIST_HEAD(dock_clients_head, Client) dock_clients;
+
         /* Backpointer to the screen this workspace is on */
         i3Screen *screen;
 
@@ -125,6 +128,10 @@ struct Client {
         /* x, y, width, height */
         Rect rect;
 
+        /* Height which was determined by reading the _NET_WM_STRUT_PARTIAL top/bottom of the screen
+           reservation */
+        int desired_height;
+
         /* Name */
         char *name;
         int name_len;
@@ -132,6 +139,12 @@ struct Client {
         /* fullscreen is pretty obvious */
         bool fullscreen;
 
+        enum { TITLEBAR_TOP = 0, TITLEBAR_LEFT, TITLEBAR_RIGHT, TITLEBAR_BOTTOM, TITLEBAR_OFF } titlebar_position;
+
+        /* If a client is set as a dock, it is placed at the very bottom of the screen and its
+           requested size is used */
+        bool dock;
+
         /* After leaving fullscreen mode, a client needs to be reconfigured (configuration =
            setting X, Y, width and height). By setting the force_reconfigure flag, render_layout()
            will reconfigure the client. */
@@ -148,6 +161,7 @@ struct Client {
 
         /* The following entry provides the necessary list pointers to use Client with LIST_* macros */
         CIRCLEQ_ENTRY(Client) clients;
+        SLIST_ENTRY(Client) dock_clients;
 };
 
 /*
index bd0c9d78786e8c97b8eea38895c4363d5a045063..0116d42b23e614847dc83c16d5d5feab82055a09 100644 (file)
@@ -20,5 +20,6 @@ int handle_windowname_change(void *data, xcb_connection_t *conn, uint8_t state,
                                 xcb_window_t window, xcb_atom_t atom, xcb_get_property_reply_t *prop);
 int handle_expose_event(void *data, xcb_connection_t *conn, xcb_expose_event_t *event);
 int handle_client_message(void *data, xcb_connection_t *conn, xcb_client_message_event_t *event);
+int window_type_handler(void *data, xcb_connection_t *conn, uint8_t state, xcb_window_t window, xcb_atom_t atom, xcb_get_property_reply_t *property);
 
 #endif
index 466871135443eb76d0d2278f8e80fc16c9d0b09c..53ebb69cbe4a84cfed39e2c1e372ea1de59a54ce 100644 (file)
@@ -23,6 +23,6 @@ extern TAILQ_HEAD(bindings_head, Binding) bindings;
 extern xcb_event_handlers_t evenths;
 extern char *pattern;
 extern int num_screens;
-extern xcb_atom_t atoms[6];
+extern xcb_atom_t atoms[9];
 
 #endif
index d234ac204c9ea091695d0ee1b638244091646e6a..bc625629137c88b96597487d2e44d08bc3f5de62 100644 (file)
 #define _NET_WM_STATE_TOGGLE 2
 
 enum { _NET_SUPPORTED = 0,
-        _NET_SUPPORTING_WM_CHECK = 1,
-        _NET_WM_NAME = 2,
-        _NET_WM_STATE_FULLSCREEN = 3,
-        _NET_WM_STATE = 4,
-        UTF8_STRING = 5
+        _NET_SUPPORTING_WM_CHECK,
+        _NET_WM_NAME,
+        _NET_WM_STATE_FULLSCREEN,
+        _NET_WM_STATE,
+        _NET_WM_WINDOW_TYPE,
+        _NET_WM_WINDOW_TYPE_DOCK,
+        _NET_WM_DESKTOP,
+        _NET_WM_STRUT_PARTIAL,
+        UTF8_STRING
 };
 
 uint32_t get_colorpixel(xcb_connection_t *conn, xcb_window_t window, char *hex);
index f1a247ee32e8a8069053226aed0282230b8cf854..6620587921c70aad4917cd067d2de47e117546c4 100644 (file)
@@ -377,3 +377,10 @@ int handle_client_message(void *data, xcb_connection_t *conn, xcb_client_message
 
         return 1;
 }
+
+int window_type_handler(void *data, xcb_connection_t *conn, uint8_t state, xcb_window_t window,
+                        xcb_atom_t atom, xcb_get_property_reply_t *property) {
+        /* TODO: Implement this one. To do this, implement a little test program which sleep(1)s
+         before changing this property. */
+        printf("_NET_WM_WINDOW_TYPE changed, this is not yet implemented.\n");
+}
index 48fcaf3691fbe8cf81bd2be89c7ae7281e0a091d..bf867ccac3d16f6f4886add2b0df2c6b515ae823 100644 (file)
@@ -128,18 +128,73 @@ void decorate_window(xcb_connection_t *conn, Client *client) {
         free(label);
 }
 
+/*
+ * Pushes the client’s x and y coordinates to X11
+ *
+ */
+static void reposition_client(xcb_connection_t *connection, Client *client) {
+        printf("frame needs to be pushed to %dx%d\n", client->rect.x, client->rect.y);
+        /* Note: We can use a pointer to client->x like an array of uint32_ts
+           because it is followed by client->y by definition */
+        xcb_configure_window(connection, client->frame,
+                        XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y, &(client->rect.x));
+}
+
+/*
+ * Pushes the client’s width/height to X11 and resizes the child window
+ *
+ */
+static void resize_client(xcb_connection_t *connection, Client *client) {
+        i3Font *font = load_font(connection, pattern);
+
+        printf("resizing client to %d x %d\n", client->rect.width, client->rect.height);
+        xcb_configure_window(connection, client->frame,
+                        XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT,
+                        &(client->rect.width));
+
+        /* Adjust the position of the child inside its frame.
+         * The coordinates of the child are relative to its frame, we
+         * add a border of 2 pixel to each value */
+        uint32_t mask = XCB_CONFIG_WINDOW_X |
+                        XCB_CONFIG_WINDOW_Y |
+                        XCB_CONFIG_WINDOW_WIDTH |
+                        XCB_CONFIG_WINDOW_HEIGHT;
+        Rect rect;
+        if (client->titlebar_position == TITLEBAR_OFF) {
+                rect.x = 0;
+                rect.y = 0;
+                rect.width = client->rect.width;
+                rect.height = client->rect.height;
+        } else {
+                rect.x = 2;
+                rect.y = font->height + 2 + 2;
+                rect.width = client->rect.width - (2 + 2);
+                rect.height = client->rect.height - ((font->height + 2 + 2) + 2);
+        }
+
+        printf("child will be at %dx%d with size %dx%d\n", rect.x, rect.y, rect.width, rect.height);
+
+        xcb_configure_window(connection, client->child, mask, &(rect.x));
+}
+
 static void render_container(xcb_connection_t *connection, Container *container) {
         Client *client;
-        i3Font *font = load_font(connection, pattern);
 
         if (container->mode == MODE_DEFAULT) {
                 int num_clients = 0;
                 CIRCLEQ_FOREACH(client, &(container->clients), clients)
-                        num_clients++;
+                        if (!client->dock)
+                                num_clients++;
                 printf("got %d clients in this default container.\n", num_clients);
 
                 int current_client = 0;
                 CIRCLEQ_FOREACH(client, &(container->clients), clients) {
+                        /* TODO: currently, clients are assigned to the current container.
+                           Therefore, we need to skip them here. Does anything harmful happen
+                           if clients *do not* have a container. Is this the more desired
+                           situation? Let’s find out… */
+                        if (client->dock)
+                                continue;
                         /* TODO: at the moment, every column/row is screen / num_cols. This
                          * needs to be changed to "percentage of the screen" by
                          * default and adjustable by the user if necessary.
@@ -151,38 +206,14 @@ static void render_container(xcb_connection_t *connection, Container *container)
                             (client->rect.x != (client->rect.x = container->x)) |
                             (client->rect.y != (client->rect.y = container->y +
                                           (container->height / num_clients) * current_client))) {
-                                printf("frame needs to be pushed to %dx%d\n", client->rect.x, client->rect.y);
-                                /* Note: We can use a pointer to client->x like an array of uint32_ts
-                                   because it is followed by client->y by definition */
-                                xcb_configure_window(connection, client->frame,
-                                                XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y, &(client->rect.x));
+                                reposition_client(connection, client);
                         }
 
                         /* TODO: vertical default layout */
                         if (client->force_reconfigure |
                             (client->rect.width != (client->rect.width = container->width)) |
                             (client->rect.height != (client->rect.height = container->height / num_clients))) {
-                                printf("resizing client to %d x %d\n", client->rect.width, client->rect.height);
-                                xcb_configure_window(connection, client->frame,
-                                                XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT,
-                                                &(client->rect.width));
-
-                                /* Adjust the position of the child inside its frame.
-                                 * The coordinates of the child are relative to its frame, we
-                                 * add a border of 2 pixel to each value */
-                                uint32_t mask = XCB_CONFIG_WINDOW_X |
-                                                XCB_CONFIG_WINDOW_Y |
-                                                XCB_CONFIG_WINDOW_WIDTH |
-                                                XCB_CONFIG_WINDOW_HEIGHT;
-                                uint32_t values[4] = {2,                                                   /* x */
-                                                      font->height + 2 + 2,                                /* y */
-                                                      client->rect.width - (2 + 2),                        /* width */
-                                                      client->rect.height - ((font->height + 2 + 2) + 2)}; /* height */
-
-                                printf("child will be at %dx%d with size %dx%d\n",
-                                                values[0], values[1], values[2], values[3]);
-
-                                xcb_configure_window(connection, client->child, mask, values);
+                                resize_client(connection, client);
                         }
 
                         if (client->force_reconfigure)
@@ -195,6 +226,24 @@ static void render_container(xcb_connection_t *connection, Container *container)
         }
 }
 
+static void render_bars(xcb_connection_t *connection, Workspace *r_ws, int width, int height) {
+        Client *client;
+        SLIST_FOREACH(client, &(r_ws->dock_clients), dock_clients) {
+                if (client->force_reconfigure |
+                    (client->rect.x != (client->rect.x = 0)) |
+                    (client->rect.y != (client->rect.y = height)))
+                        reposition_client(connection, client);
+
+                if (client->force_reconfigure |
+                    (client->rect.width != (client->rect.width = width)) |
+                    (client->rect.height != (client->rect.height = client->desired_height)))
+                        resize_client(connection, client);
+
+                client->force_reconfigure = false;
+                height += client->desired_height;
+        }
+}
+
 void render_layout(xcb_connection_t *connection) {
         i3Screen *screen;
 
@@ -206,11 +255,19 @@ void render_layout(xcb_connection_t *connection) {
                 if (r_ws->fullscreen_client != NULL)
                         /* This is easy: A client has entered fullscreen mode, so we don’t render at all */
                         continue;
+
                 int width = r_ws->rect.width;
                 int height = r_ws->rect.height;
                 int x = r_ws->rect.x;
                 int y = r_ws->rect.y;
 
+                Client *client;
+                SLIST_FOREACH(client, &(r_ws->dock_clients), dock_clients) {
+                        printf("got dock client: %p\n", client);
+                        printf("it wants to be this height: %d\n", client->desired_height);
+                        height -= client->desired_height;
+                }
+
                 printf("got %d rows and %d cols\n", r_ws->rows, r_ws->cols);
                 printf("each of them therefore is %d px width and %d px height\n",
                                 width / r_ws->cols, height / r_ws->rows);
@@ -240,6 +297,8 @@ void render_layout(xcb_connection_t *connection) {
 
                                 x += container->width;
                         }
+
+                render_bars(connection, r_ws, width, height);
         }
 
         xcb_flush(connection);
index 229e84e4b3a57e58cf64b32d8851e884e9a992e3..ca714eaddf5ee2d609a7ae09734590fc56bb3866 100644 (file)
@@ -50,7 +50,7 @@ TAILQ_HEAD(bindings_head, Binding) bindings = TAILQ_HEAD_INITIALIZER(bindings);
 xcb_event_handlers_t evenths;
 
 xcb_window_t root_win;
-xcb_atom_t atoms[6];
+xcb_atom_t atoms[9];
 
 
 char *pattern = "-misc-fixed-medium-r-normal--13-120-75-75-C-70-iso8859-1";
@@ -122,6 +122,12 @@ void reparent_window(xcb_connection_t *conn, xcb_window_t child,
                 xcb_visualid_t visual, xcb_window_t root, uint8_t depth,
                 int16_t x, int16_t y, uint16_t width, uint16_t height) {
 
+        xcb_get_property_cookie_t wm_type_cookie, strut_cookie;
+
+        /* Place requests for propertys ASAP */
+        wm_type_cookie = xcb_get_any_property_unchecked(conn, false, child, atoms[_NET_WM_WINDOW_TYPE], UINT32_MAX);
+        strut_cookie = xcb_get_any_property_unchecked(conn, false, child, atoms[_NET_WM_STRUT_PARTIAL], UINT32_MAX);
+
         Client *new = table_get(byChild, child);
         if (new == NULL) {
                 /* TODO: When does this happen for existing clients? Is that a bug? */
@@ -206,6 +212,32 @@ void reparent_window(xcb_connection_t *conn, xcb_window_t child,
         /* Focus the new window */
         xcb_set_input_focus(conn, XCB_INPUT_FOCUS_POINTER_ROOT, new->child, XCB_CURRENT_TIME);
 
+        /* Get _NET_WM_WINDOW_TYPE (to see if it’s a dock) */
+        xcb_atom_t *atom;
+        xcb_get_property_reply_t *preply = xcb_get_property_reply(conn, wm_type_cookie, NULL);
+        if (preply != NULL && preply->value_len > 0 && (atom = xcb_get_property_value(preply))) {
+                for (int i = 0; i < xcb_get_property_value_length(preply); i++)
+                        if (atom[i] == atoms[_NET_WM_WINDOW_TYPE_DOCK]) {
+                                printf("Window is a dock.\n");
+                                new->dock = true;
+                                new->titlebar_position = TITLEBAR_OFF;
+                                new->force_reconfigure = true;
+                                SLIST_INSERT_HEAD(&(new->container->workspace->dock_clients), new, dock_clients);
+                        }
+        }
+
+        /* Get _NET_WM_STRUT_PARTIAL to determine the client’s requested height */
+        uint32_t *strut;
+        preply = xcb_get_property_reply(conn, strut_cookie, NULL);
+        if (preply != NULL && preply->value_len > 0 && (strut = xcb_get_property_value(preply))) {
+                /* We only use a subset of the provided values, namely the reserved space at the top/bottom
+                   of the screen. This is because the only possibility for bars is at to be at the top/bottom
+                   with maximum horizontal size.
+                   TODO: bars at the top */
+                new->desired_height = strut[3];
+                printf("the client wants to be %d pixels height\n", new->desired_height);
+        }
+
         render_layout(conn);
 }
 
@@ -328,10 +360,17 @@ int main(int argc, char *argv[], char *env[]) {
         GET_ATOM(_NET_WM_STATE_FULLSCREEN);
         GET_ATOM(_NET_SUPPORTING_WM_CHECK);
         GET_ATOM(_NET_WM_NAME);
-        GET_ATOM(UTF8_STRING);
         GET_ATOM(_NET_WM_STATE);
+        GET_ATOM(_NET_WM_WINDOW_TYPE);
+        GET_ATOM(_NET_WM_DESKTOP);
+        GET_ATOM(_NET_WM_WINDOW_TYPE_DOCK);
+        GET_ATOM(_NET_WM_STRUT_PARTIAL);
+        GET_ATOM(UTF8_STRING);
+
+        xcb_property_set_handler(&prophs, atoms[_NET_WM_WINDOW_TYPE], UINT_MAX, window_type_handler, NULL);
+        /* TODO: In order to comply with EWMH, we have to watch _NET_WM_STRUT_PARTIAL */
 
-        check_error(c, xcb_change_property_checked(c, XCB_PROP_MODE_REPLACE, root, atoms[_NET_SUPPORTED], ATOM, 32, 5, atoms), "Could not set _NET_SUPPORTED");
+        check_error(c, xcb_change_property_checked(c, XCB_PROP_MODE_REPLACE, root, atoms[_NET_SUPPORTED], ATOM, 32, 7, atoms), "Could not set _NET_SUPPORTED");
 
         xcb_change_property(c, XCB_PROP_MODE_REPLACE, root, atoms[_NET_SUPPORTING_WM_CHECK], WINDOW, 32, 1, &root);
 
index c0f75e167a5bd0dbb92c250a7e46708ee922b637..6d8d1ca9b956ea2c8591fdd450117cb1e5586d2a 100644 (file)
@@ -39,6 +39,7 @@ void init_table() {
 
         for (int i = 0; i < 10; i++) {
                 workspaces[i].screen = NULL;
+                SLIST_INIT(&(workspaces[i].dock_clients));
                 expand_table_cols(&(workspaces[i]));
                 expand_table_rows(&(workspaces[i]));
         }