diff --git a/client.c b/client.c index a195d9fe..9703069a 100644 --- a/client.c +++ b/client.c @@ -185,36 +185,50 @@ client_getbywin(xcb_window_t w) return c; } -/** Call unfocus hook. - * \param c Client being unfocused +/** Sets focus on window - using xcb_set_input_focus or WM_TAKE_FOCUS + * \param w Window that should get focus + * \param set_input_focus Should we call xcb_set_input_focus */ static void -client_unfocus_hook(client_t *c) +window_setfocus(xcb_window_t w, bool set_input_focus) { + bool takefocus = window_hasproto(w, WM_TAKE_FOCUS); + if(set_input_focus) + xcb_set_input_focus(globalconf.connection, XCB_INPUT_FOCUS_PARENT, + w, XCB_CURRENT_TIME); + if(takefocus) + window_takefocus(w); +} + +/** Record that a client lost focus. + * \param c Client being unfocused + */ +void +client_unfocus_update(client_t *c) +{ + globalconf.screens[c->phys_screen].client_focus = NULL; + ewmh_update_net_active_window(c->phys_screen); + /* Call hook */ if(globalconf.hooks.unfocus != LUA_REFNIL) { luaA_client_userdata_new(globalconf.L, c); luaA_dofunction(globalconf.L, globalconf.hooks.unfocus, 1, 0); } + } /** Unfocus a client. * \param c The client. */ -static void +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); - - client_unfocus_hook(c); - - ewmh_update_net_active_window(c->phys_screen); + window_setfocus(root_win, true); } /** Ban client and move it out of the viewport. @@ -239,19 +253,41 @@ client_ban(client_t *c) /* All the wiboxes (may) need to be repositioned. */ if(client_hasstrut(c)) wibox_update_positions(); - } - /* 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); + /* 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); + } } -/** Call focus hook. +/** Record that a client got focus. * \param c Client being focused. */ -static void -client_focus_hook(client_t *c) +void +client_focus_update(client_t *c) { + if(!client_maybevisible(c, c->screen)) + return; + + /* stop hiding client */ + c->ishidden = false; + client_setminimized(c, false); + + /* unban the client before focusing for consistency */ + client_unban(c); + + globalconf.screen_focus = &globalconf.screens[c->phys_screen]; + globalconf.screen_focus->client_focus = c; + + /* Some layouts use focused client differently, so call them back. + * And anyway, we have maybe unhidden */ + client_need_arrange(c); + + /* according to EWMH, we have to remove the urgent state from a client */ + client_seturgent(c, false); + + ewmh_update_net_active_window(c->phys_screen); + /* execute hook */ if(globalconf.hooks.focus != LUA_REFNIL) { @@ -260,81 +296,24 @@ client_focus_hook(client_t *c) } } + /** Give focus to client, or to first client if client is NULL. * \param c The client or NULL. - * \param sendmessage true, if we should send message. */ void -client_focus(client_t *c, bool sendmessage) +client_focus(client_t *c) { - /* Handle c == NULL case */ - if(!c) - { - if(sendmessage) - c = globalconf.clients; - - if(!c) - return; - } + /* We have to set focus on first client */ + if(!c && !(c = globalconf.clients)) + return; if(!client_maybevisible(c, c->screen)) return; - /* Does client window support WM_TAKE_FOCUS protocol ? */ - bool takefocus = window_hasproto(c->win, WM_TAKE_FOCUS); - - /* Disallow setting focus on client with No Input Model */ - if(sendmessage && !takefocus && c->nofocus) - return; - - /* Save current focused client */ - client_t *focused_before = globalconf.screen_focus->client_focus; - - if(c == focused_before) - return; - - /* stop hiding c */ - c->ishidden = false; - client_setminimized(c, false); - - /* unban the client before focusing or it will fail */ - client_unban(c); - globalconf.screen_focus = &globalconf.screens[c->phys_screen]; globalconf.screen_focus->client_focus = c; - if(sendmessage) - { - if(!c->nofocus) - /* Input models: Passive, Locally Active */ - xcb_set_input_focus(globalconf.connection, XCB_INPUT_FOCUS_POINTER_ROOT, - c->win, XCB_CURRENT_TIME); - - if(takefocus) - /* Input models: Local Active, Globally Active */ - window_takefocus(c->win); - } - - /* Some layouts use focused client differently, so call them back. - * And anyway, we have maybe unhidden */ - client_need_arrange(c); - - /* unfocus current selected client - * We don't really need to unfocus here, - * because client already received FocusOut event. - * What we need to do is call unfocus hook, to - * inform lua script, about this event. - */ - if(focused_before) - client_unfocus_hook(focused_before); - - client_focus_hook(c); - - /* according to EWMH, we have to remove the urgent state from a client */ - client_seturgent(c, false); - - ewmh_update_net_active_window(c->phys_screen); - + window_setfocus(c->win, !c->nofocus); } /** Stack a window below. @@ -1548,7 +1527,7 @@ luaA_client_redraw(lua_State *L) if(globalconf.screen_focus->client_focus == *c) { client_unfocus(*c); - client_focus(*c, true); + client_focus(*c); } return 0; @@ -2293,7 +2272,7 @@ luaA_client_module_newindex(lua_State *L) { case A_TK_FOCUS: c = luaA_checkudata(L, 3, "client"); - client_focus(*c, true); + client_focus(*c); break; default: break; diff --git a/client.h b/client.h index b1d10778..d2cd3e7a 100644 --- a/client.h +++ b/client.h @@ -75,7 +75,10 @@ void client_setmaxvert(client_t *, bool); void client_setminimized(client_t *, bool); void client_setborder(client_t *, int); void client_seturgent(client_t *, bool); -void client_focus(client_t *, bool); +void client_focus(client_t *); +void client_focus_update(client_t *); +void client_unfocus(client_t *); +void client_unfocus_update(client_t *); int luaA_client_newindex(lua_State *); diff --git a/event.c b/event.c index 3e970dfc..d3bd4d13 100644 --- a/event.c +++ b/event.c @@ -538,17 +538,64 @@ event_handle_focusin(void *data __attribute__ ((unused)), xcb_connection_t *connection, xcb_focus_in_event_t *ev) { - /* filter focus-in events */ - if (ev->detail != XCB_NOTIFY_DETAIL_NONLINEAR) - return 0; - client_t *c; - if((c = client_getbytitlebarwin(ev->event)) - || (c = client_getbywin(ev->event))) - client_focus(c, false); + /* Events that we are interested in: */ + switch(ev->detail) + { + /* These are events that jump between windows of a toplevel client. + * + * NotifyVirtual event is handled in case where NotifyAncestor or + * NotifyInferior event is not generated on window that is managed by + * awesome ( client_* returns NULL ) + * + * Can someone explain exactly why they are needed ? + */ + case XCB_NOTIFY_DETAIL_VIRTUAL: + case XCB_NOTIFY_DETAIL_ANCESTOR: + case XCB_NOTIFY_DETAIL_INFERIOR: - return 0; + /* These are events that jump between clients. + * Virtual events ensure we always get an event on our top-level window. + */ + case XCB_NOTIFY_DETAIL_NONLINEAR_VIRTUAL: + case XCB_NOTIFY_DETAIL_NONLINEAR: + if((c = client_getbytitlebarwin(ev->event)) + || (c = client_getbywin(ev->event))) + client_focus_update(c); + /* all other events are ignored */ + default: + return 0; + } +} + +/** The focus out event handler. + * \param data currently unused. + * \param connection The connection to the X server. + * \param ev The event. + */ +static int +event_handle_focusout(void *data __attribute__ ((unused)), + xcb_connection_t *connection, + xcb_focus_out_event_t *ev) +{ + client_t *c; + + /* Events that we are interested in: */ + switch(ev->detail) + { + /* These are events that jump between clients. + * Virtual events ensure we always get an event on our top-level window. + */ + case XCB_NOTIFY_DETAIL_NONLINEAR_VIRTUAL: + case XCB_NOTIFY_DETAIL_NONLINEAR: + if((c = client_getbytitlebarwin(ev->event)) + || (c = client_getbywin(ev->event))) + client_unfocus_update(c); + /* all other events are ignored */ + default: + return 0; + } } /** The expose event handler. @@ -868,6 +915,7 @@ void a_xcb_set_event_handlers(void) xcb_event_set_enter_notify_handler(&globalconf.evenths, event_handle_enternotify, NULL); xcb_event_set_leave_notify_handler(&globalconf.evenths, event_handle_leavenotify, NULL); xcb_event_set_focus_in_handler(&globalconf.evenths, event_handle_focusin, NULL); + xcb_event_set_focus_out_handler(&globalconf.evenths, event_handle_focusout, NULL); xcb_event_set_motion_notify_handler(&globalconf.evenths, event_handle_motionnotify, NULL); xcb_event_set_expose_handler(&globalconf.evenths, event_handle_expose, NULL); xcb_event_set_key_press_handler(&globalconf.evenths, event_handle_key, NULL); diff --git a/ewmh.c b/ewmh.c index 382bdf27..43d1d283 100644 --- a/ewmh.c +++ b/ewmh.c @@ -429,7 +429,7 @@ ewmh_process_client_message(xcb_client_message_event_t *ev) else if(ev->type == _NET_ACTIVE_WINDOW) { if((c = client_getbywin(ev->window))) - client_focus(c, true); + client_focus(c); } return 0;