diff --git a/awesome.c b/awesome.c index 77f14e19..276cae1b 100644 --- a/awesome.c +++ b/awesome.c @@ -39,6 +39,7 @@ #include "event.h" #include "property.h" #include "screen.h" +#include "titlebar.h" #include "common/version.h" #include "common/atoms.h" #include "common/xcursor.h" @@ -79,7 +80,10 @@ awesome_atexit(void) /* remap all clients since some WM won't handle them otherwise */ for(c = globalconf.clients; c; c = c->next) + { client_unban(c); + titlebar_client_detach(c); + } xcb_flush(globalconf.connection); diff --git a/client.c b/client.c index d10ef264..d60cdae9 100644 --- a/client.c +++ b/client.c @@ -158,8 +158,13 @@ client_getbywin(xcb_window_t w) static void client_unfocus(client_t *c) { + xcb_window_t root_win = xutil_screen_get(globalconf.connection, c->phys_screen)->root; globalconf.screens[c->phys_screen].client_focus = NULL; + /* Set focus on root window, so no events leak to the current window. */ + xcb_set_input_focus(globalconf.connection, XCB_INPUT_FOCUS_POINTER_ROOT, + root_win, XCB_CURRENT_TIME); + /* Call hook */ if(globalconf.hooks.unfocus != LUA_REFNIL) { @@ -170,21 +175,45 @@ client_unfocus(client_t *c) ewmh_update_net_active_window(c->phys_screen); } -/** Ban client and unmap it. +/** Ban client and move it out of the viewport. * \param c The client. */ void client_ban(client_t *c) { - if(globalconf.screen_focus->client_focus == c) + if(!c->isbanned) + { + /* Move all clients out of the physical viewport into negative coordinate space. */ + /* They will all be put on top of each other. */ + uint32_t request[2] = { - (c->geometry.width + 2 * c->border), + - (c->geometry.height + 2 * c->border) }; + + xcb_configure_window(globalconf.connection, c->win, + XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y, + request); + + /* Do it manually because client geometry remains unchanged. */ + if (c->titlebar) + { + simple_window_t *sw = &c->titlebar->sw; + + if (sw->window) + { + request[0] = - (sw->geometry.width); + request[1] = - (sw->geometry.height); + /* Move the titlebar to the same place as the window. */ + xcb_configure_window(globalconf.connection, sw->window, + XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y, + request); + } + } + + c->isbanned = true; + } + + /* Wait until the last moment to take away the focus from the window. */ + if (globalconf.screens[c->phys_screen].client_focus == c) client_unfocus(c); - xcb_unmap_window(globalconf.connection, c->win); - if(c->ishidden) - window_state_set(c->win, XCB_WM_STATE_ICONIC); - else - window_state_set(c->win, XCB_WM_STATE_WITHDRAWN); - if(c->titlebar) - xcb_unmap_window(globalconf.connection, c->titlebar->sw.window); } /** Give focus to client, or to first client if client is NULL. @@ -496,6 +525,29 @@ client_manage(xcb_window_t w, xcb_get_geometry_reply_t *wgeom, int phys_screen, ewmh_update_net_client_list(c->phys_screen); + /* Always stay in NORMAL_STATE. Even though iconified seems more + * appropriate sometimes. The only possible loss is that clients not using + * visibility events may continue to proces data (when banned). + * Without any exposes or other events the cost should be fairly limited though. + * + * Some clients may expect the window to be unmapped when STATE_ICONIFIED. + * Two conflicting parts of the ICCCM v2.0 (section 4.1.4): + * + * "Normal -> Iconic - The client should send a ClientMessage event as described later in this section." + * (note no explicit mention of unmapping, while Normal->Widthdrawn does mention that) + * + * "Once a client's window has left the Withdrawn state, the window will be mapped + * if it is in the Normal state and the window will be unmapped if it is in the Iconic state." + * + * At this stage it's just safer to keep it in normal state and avoid confusion. + */ + window_state_set(c->win, XCB_WM_STATE_NORMAL); + + /* Move window outside the viewport before mapping it. */ + /* This also sets the state to iconified. */ + client_ban(c); + xcb_map_window(globalconf.connection, c->win); + /* Call hook to notify list change */ if(globalconf.hooks.clients != LUA_REFNIL) luaA_dofunction(globalconf.L, globalconf.hooks.clients, 0, 0); @@ -620,6 +672,15 @@ client_resize(client_t *c, area_t geometry, bool hints) titlebar_update_geometry_floating(c); + /* The idea is to give a client a resize even when banned. */ + /* We just have to move the (x,y) to keep it out of the viewport. */ + /* This at least doesn't break expectations about events. */ + if (c->isbanned) + { + geometry.x = values[0] = - (geometry.width + 2 * c->border); + geometry.y = values[1] = - (geometry.height + 2 * c->border); + } + xcb_configure_window(globalconf.connection, c->win, XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y | XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT, @@ -813,20 +874,38 @@ client_saveprops_tags(client_t *c) xcb_change_property(globalconf.connection, XCB_PROP_MODE_REPLACE, c->win, _AWESOME_TAGS, STRING, 8, i, prop); } -/** Unban a client. +/** Unban a client and move it back into the viewport. * \param c The client. */ void client_unban(client_t *c) { - xcb_map_window(globalconf.connection, c->win); - window_state_set(c->win, XCB_WM_STATE_NORMAL); - if(c->titlebar) + if(c->isbanned) { - if(c->isfullscreen || !c->titlebar->isvisible) - xcb_unmap_window(globalconf.connection, c->titlebar->sw.window); - else - xcb_map_window(globalconf.connection, c->titlebar->sw.window); + /* Move the client back where it belongs. */ + uint32_t request[2] = { c->geometry.x, c->geometry.y }; + + xcb_configure_window(globalconf.connection, c->win, + XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y, + request); + window_configure(c->win, c->geometry, c->border); + + /* Do this manually because the system doesn't know we moved the toolbar. + * Note that !isvisible titlebars are unmapped and for fullscreen it'll + * end up offscreen anyway. */ + if(c->titlebar) + { + simple_window_t *sw = &c->titlebar->sw; + /* All resizing is done, so only move now. */ + request[0] = sw->geometry.x; + request[1] = sw->geometry.y; + + xcb_configure_window(globalconf.connection, sw->window, + XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y, + request); + } + + c->isbanned = false; } } diff --git a/client.h b/client.h index e4956755..fe4cece3 100644 --- a/client.h +++ b/client.h @@ -127,11 +127,23 @@ client_isfloating(client_t *c) * \return true if the client is visible, false otherwise. */ static inline bool -client_isvisible(client_t *c, int screen) +client_isvisible_exclude_banned(client_t *c, int screen) { return (!c->ishidden && !c->isminimized && client_maybevisible(c, screen)); } +/** Returns true if a client is tagged + * with one of the tags of the specified screen and is not hidden or banned. + * \param c The client to check. + * \param screen Virtual screen number. + * \return true if the client is visible, false otherwise. + */ +static inline bool +client_isvisible(client_t *c, int screen) +{ + return (client_isvisible_exclude_banned(c, screen) && !c->isbanned); +} + /** Check if a client has strut information. * \param c A client. * \return A boolean value, true if the client has strut information. diff --git a/layout.c b/layout.c index e7ac8848..56565c59 100644 --- a/layout.c +++ b/layout.c @@ -40,7 +40,7 @@ arrange(int screen) for(c = globalconf.clients; c; c = c->next) { - if(client_isvisible(c, screen)) + if(client_isvisible_exclude_banned(c, screen)) client_unban(c); /* we don't touch other screens windows */ else if(c->screen == screen) diff --git a/structs.h b/structs.h index 1568cee4..c8549deb 100644 --- a/structs.h +++ b/structs.h @@ -181,6 +181,10 @@ struct client_t bool ismodal; /** True if the client is on top */ bool isontop; + /** True if a client is banned to a position outside the viewport. + * Note that the geometry remains unchanged and that the window is still mapped. + */ + bool isbanned; /** true if the client must be skipped from task bar client list */ bool skiptb; /** True if the client cannot have focus */ diff --git a/titlebar.c b/titlebar.c index 15253a51..9fd70bf3 100644 --- a/titlebar.c +++ b/titlebar.c @@ -200,7 +200,6 @@ titlebar_client_attach(client_t *c, wibox_t *t) break; } - titlebar_geometry_compute(c, c->geometry, &wingeom); simplewindow_init(&t->sw, c->phys_screen, @@ -210,6 +209,9 @@ titlebar_client_attach(client_t *c, wibox_t *t) t->need_update = true; + /* This may seem useless, but it's the cleanest way to avoid seeing titlebars for banned clients. */ + titlebar_update_geometry_floating(c); + if(t->isvisible) xcb_map_window(globalconf.connection, t->sw.window); @@ -218,6 +220,29 @@ titlebar_client_attach(client_t *c, wibox_t *t) } } +/** Map or unmap a titlebar wibox. + * \param t The wibox/titlebar. + * \param visible The new state of the titlebar. + */ +void +titlebar_set_visible(wibox_t *t, bool visible) +{ + if (visible != t->isvisible) + { + /* The price of (un)mapping something small like a titlebar is pretty cheap. + * It would complicate matters if this rare case was treated like clients. + * Clients are moved out of the viewport when banned. + */ + if ((t->isvisible = visible)) + xcb_map_window(globalconf.connection, t->sw.window); + else + xcb_unmap_window(globalconf.connection, t->sw.window); + + globalconf.screens[t->screen].need_arrange = true; + client_stack(); + } +} + /** Titlebar newindex. * \param L The Lua VM state. * \param titlebar The wibox titlebar. diff --git a/titlebar.h b/titlebar.h index 8294e3f8..441134ee 100644 --- a/titlebar.h +++ b/titlebar.h @@ -30,6 +30,7 @@ void titlebar_geometry_compute(client_t *, area_t, area_t *); void titlebar_init(client_t *); void titlebar_client_detach(client_t *); void titlebar_client_attach(client_t *, wibox_t *); +void titlebar_set_visible(wibox_t *t, bool visible); int luaA_titlebar_newindex(lua_State *, wibox_t *, awesome_token_t); @@ -130,7 +131,22 @@ titlebar_update_geometry_tiled(client_t *c, area_t geometry) return; titlebar_geometry_compute(c, geometry, &geom); - wibox_moveresize(c->titlebar, geom); + /* Can't actually move titlebar right now, but we will resize it. */ + if(c->isbanned) + { + area_t moved_geom = geom; + + /* Make sure it stays outside the viewport. */ + moved_geom.x = - geom.width; + moved_geom.y = - geom.height; + + wibox_moveresize(c->titlebar, moved_geom); + + /* Store the real geometry. */ + c->titlebar->sw.geometry = geom; + } + else + wibox_moveresize(c->titlebar, geom); } /** Update the titlebar geometry for a floating client. diff --git a/wibox.c b/wibox.c index 54f3790c..f2321d64 100644 --- a/wibox.c +++ b/wibox.c @@ -1004,8 +1004,7 @@ luaA_wibox_newindex(lua_State *L) wibox_setvisible(*wibox, b); break; case WIBOX_TYPE_TITLEBAR: - (*wibox)->isvisible = b; - globalconf.screens[(*wibox)->screen].need_arrange = true; + titlebar_set_visible(*wibox, b); break; } break;