From 2d14ced024416e2074b57290bf7dade7d08899e5 Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Wed, 18 Jan 2012 19:16:57 +0000 Subject: [PATCH] Bugfix: Respect WM_HINTS.input for WM_TAKE_FOCUS clients This fixes problems with Qt apps (like Quassel) and apparently Eclipse since the last commit. --- include/data.h | 6 +++++- include/window.h | 6 ++++++ src/handlers.c | 29 ++++++++++------------------- src/manage.c | 4 +++- src/window.c | 27 ++++++++++++++++++++++++++- src/x.c | 11 ++++++++--- 6 files changed, 58 insertions(+), 25 deletions(-) diff --git a/include/data.h b/include/data.h index 740278ae..10fc16d2 100644 --- a/include/data.h +++ b/include/data.h @@ -2,7 +2,7 @@ * vim:ts=4:sw=4:expandtab * * i3 - an improved dynamic tiling window manager - * © 2009-2011 Michael Stapelberg and contributors (see also: LICENSE) + * © 2009-2012 Michael Stapelberg and contributors (see also: LICENSE) * * include/data.h: This file defines all data structures used by i3 * @@ -293,6 +293,10 @@ struct Window { /** Whether the application needs to receive WM_TAKE_FOCUS */ bool needs_take_focus; + /** Whether this window accepts focus. We store this inverted so that the + * default will be 'accepts focus'. */ + bool doesnt_accept_focus; + /** Whether the window says it is a dock window */ enum { W_NODOCK = 0, W_DOCK_TOP = 1, W_DOCK_BOTTOM = 2 } dock; diff --git a/include/window.h b/include/window.h index 61ec8614..60198b87 100644 --- a/include/window.h +++ b/include/window.h @@ -57,4 +57,10 @@ void window_update_strut_partial(i3Window *win, xcb_get_property_reply_t *prop); */ void window_update_role(i3Window *win, xcb_get_property_reply_t *prop, bool before_mgmt); +/** + * Updates the WM_HINTS (we only care about the input focus handling part). + * + */ +void window_update_hints(i3Window *win, xcb_get_property_reply_t *prop); + #endif diff --git a/src/handlers.c b/src/handlers.c index 69c1831d..d835d367 100644 --- a/src/handlers.c +++ b/src/handlers.c @@ -855,18 +855,16 @@ static bool handle_hints(void *data, xcb_connection_t *conn, uint8_t state, xcb_ xcb_icccm_wm_hints_t hints; - if (reply != NULL) { - if (!xcb_icccm_get_wm_hints_from_reply(&hints, reply)) + if (reply == NULL) + if (!(reply = xcb_get_property_reply(conn, xcb_icccm_get_wm_hints(conn, window), NULL))) return false; - } else { - if (!xcb_icccm_get_wm_hints_reply(conn, xcb_icccm_get_wm_hints_unchecked(conn, con->window->id), &hints, NULL)) - return false; - } + + if (!xcb_icccm_get_wm_hints_from_reply(&hints, reply)) + return false; if (!con->urgent && focused == con) { DLOG("Ignoring urgency flag for current client\n"); - FREE(reply); - return true; + goto end; } /* Update the flag on the client directly */ @@ -882,17 +880,10 @@ static bool handle_hints(void *data, xcb_connection_t *conn, uint8_t state, xcb_ tree_render(); -#if 0 - /* If the workspace this client is on is not visible, we need to redraw - * the workspace bar */ - if (!workspace_is_visible(client->workspace)) { - Output *output = client->workspace->output; - render_workspace(conn, output, output->current_workspace); - xcb_flush(conn); - } -#endif - - FREE(reply); +end: + if (con->window) + window_update_hints(con->window, reply); + else free(reply); return true; } diff --git a/src/manage.c b/src/manage.c index ee1b3d6c..a87807b4 100644 --- a/src/manage.c +++ b/src/manage.c @@ -80,7 +80,7 @@ void manage_window(xcb_window_t window, xcb_get_window_attributes_cookie_t cooki xcb_get_property_cookie_t wm_type_cookie, strut_cookie, state_cookie, utf8_title_cookie, title_cookie, class_cookie, leader_cookie, transient_cookie, - role_cookie, startup_id_cookie; + role_cookie, startup_id_cookie, wm_hints_cookie; geomc = xcb_get_geometry(conn, d); @@ -142,6 +142,7 @@ void manage_window(xcb_window_t window, xcb_get_window_attributes_cookie_t cooki class_cookie = GET_PROPERTY(XCB_ATOM_WM_CLASS, 128); role_cookie = GET_PROPERTY(A_WM_WINDOW_ROLE, 128); startup_id_cookie = GET_PROPERTY(A__NET_STARTUP_ID, 512); + wm_hints_cookie = xcb_icccm_get_wm_hints(conn, window); /* TODO: also get wm_normal_hints here. implement after we got rid of xcb-event */ DLOG("Managing window 0x%08x\n", window); @@ -169,6 +170,7 @@ void manage_window(xcb_window_t window, xcb_get_window_attributes_cookie_t cooki window_update_transient_for(cwindow, xcb_get_property_reply(conn, transient_cookie, NULL)); window_update_strut_partial(cwindow, xcb_get_property_reply(conn, strut_cookie, NULL)); window_update_role(cwindow, xcb_get_property_reply(conn, role_cookie, NULL), true); + window_update_hints(cwindow, xcb_get_property_reply(conn, wm_hints_cookie, NULL)); xcb_get_property_reply_t *startup_id_reply; startup_id_reply = xcb_get_property_reply(conn, startup_id_cookie, NULL); diff --git a/src/window.c b/src/window.c index 30957a4b..e630e776 100644 --- a/src/window.c +++ b/src/window.c @@ -2,7 +2,7 @@ * vim:ts=4:sw=4:expandtab * * i3 - an improved dynamic tiling window manager - * © 2009-2011 Michael Stapelberg and contributors (see also: LICENSE) + * © 2009-2012 Michael Stapelberg and contributors (see also: LICENSE) * * window.c: Updates window attributes (X11 hints/properties). * @@ -251,3 +251,28 @@ void window_update_role(i3Window *win, xcb_get_property_reply_t *prop, bool befo free(prop); } + +/* + * Updates the WM_HINTS (we only care about the input focus handling part). + * + */ +void window_update_hints(i3Window *win, xcb_get_property_reply_t *prop) { + if (prop == NULL || xcb_get_property_value_length(prop) == 0) { + DLOG("WM_HINTS not set.\n"); + FREE(prop); + return; + } + + xcb_icccm_wm_hints_t hints; + + if (!xcb_icccm_get_wm_hints_from_reply(&hints, prop)) { + DLOG("Could not get WM_HINTS\n"); + free(prop); + return; + } + + win->doesnt_accept_focus = !hints.input; + LOG("WM_HINTS.input changed to \"%d\"\n", hints.input); + + free(prop); +} diff --git a/src/x.c b/src/x.c index 42bb744d..557a49d9 100644 --- a/src/x.c +++ b/src/x.c @@ -2,7 +2,7 @@ * vim:ts=4:sw=4:expandtab * * i3 - an improved dynamic tiling window manager - * © 2009-2011 Michael Stapelberg and contributors (see also: LICENSE) + * © 2009-2012 Michael Stapelberg and contributors (see also: LICENSE) * * x.c: Interface to X11, transfers our in-memory state to X11 (see also * render.c). Basically a big state machine. @@ -849,12 +849,17 @@ void x_push_changes(Con *con) { /* Invalidate focused_id to correctly focus new windows with the same ID */ focused_id = XCB_NONE; } else { + bool set_focus = true; if (focused->window != NULL && focused->window->needs_take_focus) { - DLOG("Updating focus by sending WM_TAKE_FOCUS to window 0x%08x only (focused: %p / %s)\n", + DLOG("Updating focus by sending WM_TAKE_FOCUS to window 0x%08x (focused: %p / %s)\n", to_focus, focused, focused->name); send_take_focus(to_focus); - } else { + set_focus = !focused->window->doesnt_accept_focus; + DLOG("set_focus = %d\n", set_focus); + } + + if (set_focus) { DLOG("Updating focus (focused: %p / %s)\n", focused, focused->name); /* We remove XCB_EVENT_MASK_FOCUS_CHANGE from the event mask to get * no focus change events for our own focus changes. We only want