]> git.sur5r.net Git - i3/i3/commitdiff
Add support for WM_CLIENT_LEADER, put floating windows mapping to (0x0) to center...
authorMichael Stapelberg <michael@stapelberg.de>
Tue, 4 Aug 2009 22:39:55 +0000 (00:39 +0200)
committerMichael Stapelberg <michael@stapelberg.de>
Tue, 4 Aug 2009 22:39:55 +0000 (00:39 +0200)
include/data.h
include/handlers.h
include/i3.h
include/xcb.h
src/handlers.c
src/mainx.c
src/manage.c

index 4ced21f800518165fa108a0028d6db09afb997cf..99e79ab2b7adcc908ea8dec76445138c9a15c1a2 100644 (file)
@@ -341,6 +341,10 @@ struct Client {
         /** Holds the WM_CLASS, useful for matching the client in commands */
         char *window_class;
 
+        /** Holds the xcb_window_t (just an ID) for the leader window (logical
+         * parent for toolwindows and similar floating windows) */
+        xcb_window_t leader;
+
         /** fullscreen is pretty obvious */
         bool fullscreen;
 
index 74e29e4b73760ccd369dcb2488e2910581aea552..83b70894b5df483ed60e44b78d63b76b36cdd51d 100644 (file)
@@ -154,4 +154,13 @@ int handle_transient_for(void *data, xcb_connection_t *conn, uint8_t state,
                          xcb_window_t window, xcb_atom_t name,
                          xcb_get_property_reply_t *reply);
 
+/**
+ * Handles changes of the WM_CLIENT_LEADER atom which specifies if this is a
+ * toolwindow (or similar) and to which window it belongs (logical parent).
+ *
+ */
+int handle_clientleader_change(void *data, xcb_connection_t *conn,
+                               uint8_t state, xcb_window_t window,
+                               xcb_atom_t name, xcb_get_property_reply_t *prop);
+
 #endif
index 18844e090a652f9e4e11db8fd30f8d65d3debd6a..46caa415133f098b3fb49afc2ddbef300abdb984 100644 (file)
@@ -20,7 +20,7 @@
 #ifndef _I3_H
 #define _I3_H
 
-#define NUM_ATOMS 17
+#define NUM_ATOMS 18
 
 extern xcb_connection_t *global_conn;
 extern char **start_argv;
index 6ae49107badbc373252f71a4292e4a9183835321..d01f6da170823f02ac908e38e293dc6cad62b244 100644 (file)
@@ -60,7 +60,8 @@ enum { _NET_SUPPORTED = 0,
         WM_PROTOCOLS,
         WM_DELETE_WINDOW,
         UTF8_STRING,
-        WM_STATE
+        WM_STATE,
+        WM_CLIENT_LEADER
 };
 
 extern unsigned int xcb_numlock_mask;
index ee47141acd97806dbba41bad299085e589f5fbbc..9a402629f2dd175883f651f9c69e0a538536317b 100644 (file)
@@ -1064,3 +1064,31 @@ int handle_transient_for(void *data, xcb_connection_t *conn, uint8_t state, xcb_
 
         return 1;
 }
+
+/*
+ * Handles changes of the WM_CLIENT_LEADER atom which specifies if this is a
+ * toolwindow (or similar) and to which window it belongs (logical parent).
+ *
+ */
+int handle_clientleader_change(void *data, xcb_connection_t *conn, uint8_t state, xcb_window_t window,
+                        xcb_atom_t name, xcb_get_property_reply_t *prop) {
+        LOG("client leader changed\n");
+        if (prop == NULL) {
+                prop = xcb_get_property_reply(conn, xcb_get_property_unchecked(conn,
+                                        false, window, WM_CLIENT_LEADER, WINDOW, 0, 32), NULL);
+        }
+
+        Client *client = table_get(&by_child, window);
+        if (client == NULL)
+                return 1;
+
+        xcb_window_t *leader = xcb_get_property_value(prop);
+        if (leader == NULL)
+                return 1;
+
+        LOG("changed to %08x\n", *leader);
+
+        client->leader = *leader;
+
+        return 1;
+}
index 03cb9d1b647d53034ec3e9abb0c63c6c679d70fb..27e026b57ec3881b2d3bcba92d06a617993fe40f 100644 (file)
@@ -178,6 +178,7 @@ int main(int argc, char *argv[], char *env[]) {
         REQUEST_ATOM(WM_DELETE_WINDOW);
         REQUEST_ATOM(UTF8_STRING);
         REQUEST_ATOM(WM_STATE);
+        REQUEST_ATOM(WM_CLIENT_LEADER);
 
         /* TODO: this has to be more beautiful somewhen */
         int major, minor, error;
@@ -308,6 +309,7 @@ int main(int argc, char *argv[], char *env[]) {
         GET_ATOM(WM_DELETE_WINDOW);
         GET_ATOM(UTF8_STRING);
         GET_ATOM(WM_STATE);
+        GET_ATOM(WM_CLIENT_LEADER);
 
         xcb_property_set_handler(&prophs, atoms[_NET_WM_WINDOW_TYPE], UINT_MAX, handle_window_type, NULL);
         /* TODO: In order to comply with EWMH, we have to watch _NET_WM_STRUT_PARTIAL */
@@ -324,6 +326,9 @@ int main(int argc, char *argv[], char *env[]) {
         /* Watch WM_CLASS (= class of the window) */
         xcb_property_set_handler(&prophs, WM_CLASS, 128, handle_windowclass_change, NULL);
 
+        /* Watch WM_CLIENT_LEADER (= logical parent window for toolbars etc.) */
+        xcb_property_set_handler(&prophs, atoms[WM_CLIENT_LEADER], UINT_MAX, handle_clientleader_change, NULL);
+
         /* Set up the atoms we support */
         check_error(conn, xcb_change_property_checked(conn, XCB_PROP_MODE_REPLACE, root, atoms[_NET_SUPPORTED],
                        ATOM, 32, 7, atoms), "Could not set _NET_SUPPORTED");
index 12d96c8c683e63758486d51b0dfc1bd278fdd520..ba1e6cb1a79f299fe00fc35feba38515e8577150 100644 (file)
@@ -111,6 +111,7 @@ void manage_window(xcb_property_handlers_t *prophs, xcb_connection_t *conn,
         xcb_property_changed(prophs, XCB_PROPERTY_NEW_VALUE, window, WM_NAME);
         xcb_property_changed(prophs, XCB_PROPERTY_NEW_VALUE, window, WM_NORMAL_HINTS);
         xcb_property_changed(prophs, XCB_PROPERTY_NEW_VALUE, window, WM_TRANSIENT_FOR);
+        xcb_property_changed(prophs, XCB_PROPERTY_NEW_VALUE, window, atoms[WM_CLIENT_LEADER]);
         xcb_property_changed(prophs, XCB_PROPERTY_NEW_VALUE, window, atoms[_NET_WM_NAME]);
 
         free(geom);
@@ -131,7 +132,8 @@ void reparent_window(xcb_connection_t *conn, xcb_window_t child,
                      int16_t x, int16_t y, uint16_t width, uint16_t height) {
 
         xcb_get_property_cookie_t wm_type_cookie, strut_cookie, state_cookie,
-                                  utf8_title_cookie, title_cookie, class_cookie;
+                                  utf8_title_cookie, title_cookie,
+                                  class_cookie, leader_cookie;
         uint32_t mask = 0;
         uint32_t values[3];
         uint16_t original_height = height;
@@ -147,8 +149,9 @@ void reparent_window(xcb_connection_t *conn, xcb_window_t child,
         strut_cookie = xcb_get_any_property_unchecked(conn, false, child, atoms[_NET_WM_STRUT_PARTIAL], UINT32_MAX);
         state_cookie = xcb_get_any_property_unchecked(conn, false, child, atoms[_NET_WM_STATE], UINT32_MAX);
         utf8_title_cookie = xcb_get_any_property_unchecked(conn, false, child, atoms[_NET_WM_NAME], 128);
+        leader_cookie = xcb_get_any_property_unchecked(conn, false, child, atoms[WM_CLIENT_LEADER], UINT32_MAX);
         title_cookie = xcb_get_any_property_unchecked(conn, false, child, WM_NAME, 128);
-        class_cookie  = xcb_get_any_property_unchecked(conn, false, child, WM_CLASS, 128);
+        class_cookie = xcb_get_any_property_unchecked(conn, false, child, WM_CLASS, 128);
 
         Client *new = table_get(&by_child, child);
 
@@ -253,6 +256,7 @@ void reparent_window(xcb_connection_t *conn, xcb_window_t child,
                                 new->container = NULL;
                                 SLIST_INSERT_HEAD(&(c_ws->screen->dock_clients), new, dock_clients);
                                 /* If it’s a dock we can’t make it float, so we break */
+                                new->floating = FLOATING_AUTO_OFF;
                                 break;
                         } else if (atom[i] == atoms[_NET_WM_WINDOW_TYPE_DIALOG] ||
                                    atom[i] == atoms[_NET_WM_WINDOW_TYPE_UTILITY] ||
@@ -264,6 +268,12 @@ void reparent_window(xcb_connection_t *conn, xcb_window_t child,
                         }
         }
 
+        /* All clients which have a leader should be floating */
+        if (!new->dock && !client_is_floating(new) && new->leader != 0) {
+                LOG("Client has WM_CLIENT_LEADER hint set, setting floating\n");
+                new->floating = FLOATING_AUTO_ON;
+        }
+
         if (new->workspace->auto_float) {
                 new->floating = FLOATING_AUTO_ON;
                 LOG("workspace is in autofloat mode, setting floating\n");
@@ -304,6 +314,9 @@ void reparent_window(xcb_connection_t *conn, xcb_window_t child,
                 preply = xcb_get_property_reply(conn, class_cookie, NULL);
                 handle_windowclass_change(NULL, conn, 0, new->child, WM_CLASS, preply);
 
+                preply = xcb_get_property_reply(conn, leader_cookie, NULL);
+                handle_clientleader_change(NULL, conn, 0, new->child, atoms[WM_CLIENT_LEADER], preply);
+
                 LOG("DEBUG: should have all infos now\n");
                 struct Assignment *assign;
                 TAILQ_FOREACH(assign, &assignments, assignments) {
@@ -375,10 +388,27 @@ void reparent_window(xcb_connection_t *conn, xcb_window_t child,
 
                 new->container = NULL;
 
-                new->floating_rect.x = new->rect.x = x;
-                new->floating_rect.y = new->rect.y = y;
                 new->rect.width = new->floating_rect.width + 2 + 2;
                 new->rect.height = new->floating_rect.height + (font->height + 2 + 2) + 2;
+
+                /* Some clients (like GIMP’s color picker window) get mapped
+                 * to (0, 0), so we push them to a reasonable position
+                 * (centered over their leader) */
+                if (new->leader != 0 && x == 0 && y == 0) {
+                        LOG("Floating client wants to (0x0), moving it over its leader instead\n");
+                        Client *leader = table_get(&by_child, new->leader);
+                        if (leader == NULL) {
+                                LOG("leader is NULL, centering it over current workspace\n");
+
+                                x = c_ws->rect.x + (c_ws->rect.width / 2) - (new->rect.width / 2);
+                                y = c_ws->rect.y + (c_ws->rect.height / 2) - (new->rect.height / 2);
+                        } else {
+                                x = leader->rect.x + (leader->rect.width / 2) - (new->rect.width / 2);
+                                y = leader->rect.y + (leader->rect.height / 2) - (new->rect.height / 2);
+                        }
+                }
+                new->floating_rect.x = new->rect.x = x;
+                new->floating_rect.y = new->rect.y = y;
                 LOG("copying floating_rect from tiling (%d, %d) size (%d, %d)\n",
                                 new->floating_rect.x, new->floating_rect.y,
                                 new->floating_rect.width, new->floating_rect.height);