From 3114d6821dd9679f7702b90732f2648482d8cc06 Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Wed, 5 Aug 2009 00:39:55 +0200 Subject: [PATCH] Add support for WM_CLIENT_LEADER, put floating windows mapping to (0x0) to center of leader/workspace --- include/data.h | 4 ++++ include/handlers.h | 9 +++++++++ include/i3.h | 2 +- include/xcb.h | 3 ++- src/handlers.c | 28 ++++++++++++++++++++++++++++ src/mainx.c | 5 +++++ src/manage.c | 38 ++++++++++++++++++++++++++++++++++---- 7 files changed, 83 insertions(+), 6 deletions(-) diff --git a/include/data.h b/include/data.h index 4ced21f8..99e79ab2 100644 --- a/include/data.h +++ b/include/data.h @@ -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; diff --git a/include/handlers.h b/include/handlers.h index 74e29e4b..83b70894 100644 --- a/include/handlers.h +++ b/include/handlers.h @@ -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 diff --git a/include/i3.h b/include/i3.h index 18844e09..46caa415 100644 --- a/include/i3.h +++ b/include/i3.h @@ -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; diff --git a/include/xcb.h b/include/xcb.h index 6ae49107..d01f6da1 100644 --- a/include/xcb.h +++ b/include/xcb.h @@ -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; diff --git a/src/handlers.c b/src/handlers.c index ee47141a..9a402629 100644 --- a/src/handlers.c +++ b/src/handlers.c @@ -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; +} diff --git a/src/mainx.c b/src/mainx.c index 03cb9d1b..27e026b5 100644 --- a/src/mainx.c +++ b/src/mainx.c @@ -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"); diff --git a/src/manage.c b/src/manage.c index 12d96c8c..ba1e6cb1 100644 --- a/src/manage.c +++ b/src/manage.c @@ -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); -- 2.39.2