Focus events handlers
- We are tracking focus, using FocusIn/FocusOut events handlers, so user should never be confused about which client has focus - window_setfocus function generates focus change requests to the X server - client_focus uses window_setfocus to set input focus - revert_to when setting input focus set to Parent, compliant with ICCCM convention ([1]) - DEBUG flag for those who want to debug focus handlers Most of the changes, are compliant with X11 handbook ([0]), but some where obtained experimentally. Kudos to Maarten Maathuis who helped a lot with this. [0] http://cgit.freedesktop.org/xorg/doc/xorg-docs/plain/hardcopy/X11/xlib.PS.gz [1] http://tronche.com/gui/x/icccm/sec-4.html#s-4.2.7 Signed-off-by: Maarten Maathuis <madman2003@gmail.com> Signed-off-by: Mariusz Ceier <mceier@gmail.com> Signed-off-by: Julien Danjou <julien@danjou.info>
This commit is contained in:
parent
4d6bf949a8
commit
904502552f
143
client.c
143
client.c
|
@ -185,36 +185,50 @@ client_getbywin(xcb_window_t w)
|
||||||
return c;
|
return c;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Call unfocus hook.
|
/** Sets focus on window - using xcb_set_input_focus or WM_TAKE_FOCUS
|
||||||
* \param c Client being unfocused
|
* \param w Window that should get focus
|
||||||
|
* \param set_input_focus Should we call xcb_set_input_focus
|
||||||
*/
|
*/
|
||||||
static void
|
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 */
|
/* Call hook */
|
||||||
if(globalconf.hooks.unfocus != LUA_REFNIL)
|
if(globalconf.hooks.unfocus != LUA_REFNIL)
|
||||||
{
|
{
|
||||||
luaA_client_userdata_new(globalconf.L, c);
|
luaA_client_userdata_new(globalconf.L, c);
|
||||||
luaA_dofunction(globalconf.L, globalconf.hooks.unfocus, 1, 0);
|
luaA_dofunction(globalconf.L, globalconf.hooks.unfocus, 1, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Unfocus a client.
|
/** Unfocus a client.
|
||||||
* \param c The client.
|
* \param c The client.
|
||||||
*/
|
*/
|
||||||
static void
|
void
|
||||||
client_unfocus(client_t *c)
|
client_unfocus(client_t *c)
|
||||||
{
|
{
|
||||||
xcb_window_t root_win = xutil_screen_get(globalconf.connection, c->phys_screen)->root;
|
xcb_window_t root_win = xutil_screen_get(globalconf.connection, c->phys_screen)->root;
|
||||||
globalconf.screens[c->phys_screen].client_focus = NULL;
|
globalconf.screens[c->phys_screen].client_focus = NULL;
|
||||||
|
|
||||||
/* Set focus on root window, so no events leak to the current window. */
|
/* Set focus on root window, so no events leak to the current window. */
|
||||||
xcb_set_input_focus(globalconf.connection, XCB_INPUT_FOCUS_POINTER_ROOT,
|
window_setfocus(root_win, true);
|
||||||
root_win, XCB_CURRENT_TIME);
|
|
||||||
|
|
||||||
client_unfocus_hook(c);
|
|
||||||
|
|
||||||
ewmh_update_net_active_window(c->phys_screen);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Ban client and move it out of the viewport.
|
/** 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. */
|
/* All the wiboxes (may) need to be repositioned. */
|
||||||
if(client_hasstrut(c))
|
if(client_hasstrut(c))
|
||||||
wibox_update_positions();
|
wibox_update_positions();
|
||||||
}
|
|
||||||
|
|
||||||
/* Wait until the last moment to take away the focus from the window. */
|
/* Wait until the last moment to take away the focus from the window. */
|
||||||
if(globalconf.screens[c->phys_screen].client_focus == c)
|
if(globalconf.screens[c->phys_screen].client_focus == c)
|
||||||
client_unfocus(c);
|
client_unfocus(c);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Call focus hook.
|
/** Record that a client got focus.
|
||||||
* \param c Client being focused.
|
* \param c Client being focused.
|
||||||
*/
|
*/
|
||||||
static void
|
void
|
||||||
client_focus_hook(client_t *c)
|
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 */
|
/* execute hook */
|
||||||
if(globalconf.hooks.focus != LUA_REFNIL)
|
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.
|
/** Give focus to client, or to first client if client is NULL.
|
||||||
* \param c The client or NULL.
|
* \param c The client or NULL.
|
||||||
* \param sendmessage true, if we should send message.
|
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
client_focus(client_t *c, bool sendmessage)
|
client_focus(client_t *c)
|
||||||
{
|
{
|
||||||
/* Handle c == NULL case */
|
/* We have to set focus on first client */
|
||||||
if(!c)
|
if(!c && !(c = globalconf.clients))
|
||||||
{
|
return;
|
||||||
if(sendmessage)
|
|
||||||
c = globalconf.clients;
|
|
||||||
|
|
||||||
if(!c)
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(!client_maybevisible(c, c->screen))
|
if(!client_maybevisible(c, c->screen))
|
||||||
return;
|
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 = &globalconf.screens[c->phys_screen];
|
||||||
globalconf.screen_focus->client_focus = c;
|
globalconf.screen_focus->client_focus = c;
|
||||||
|
|
||||||
if(sendmessage)
|
window_setfocus(c->win, !c->nofocus);
|
||||||
{
|
|
||||||
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);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Stack a window below.
|
/** Stack a window below.
|
||||||
|
@ -1548,7 +1527,7 @@ luaA_client_redraw(lua_State *L)
|
||||||
if(globalconf.screen_focus->client_focus == *c)
|
if(globalconf.screen_focus->client_focus == *c)
|
||||||
{
|
{
|
||||||
client_unfocus(*c);
|
client_unfocus(*c);
|
||||||
client_focus(*c, true);
|
client_focus(*c);
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -2293,7 +2272,7 @@ luaA_client_module_newindex(lua_State *L)
|
||||||
{
|
{
|
||||||
case A_TK_FOCUS:
|
case A_TK_FOCUS:
|
||||||
c = luaA_checkudata(L, 3, "client");
|
c = luaA_checkudata(L, 3, "client");
|
||||||
client_focus(*c, true);
|
client_focus(*c);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
|
|
5
client.h
5
client.h
|
@ -75,7 +75,10 @@ void client_setmaxvert(client_t *, bool);
|
||||||
void client_setminimized(client_t *, bool);
|
void client_setminimized(client_t *, bool);
|
||||||
void client_setborder(client_t *, int);
|
void client_setborder(client_t *, int);
|
||||||
void client_seturgent(client_t *, bool);
|
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 *);
|
int luaA_client_newindex(lua_State *);
|
||||||
|
|
||||||
|
|
64
event.c
64
event.c
|
@ -538,17 +538,64 @@ event_handle_focusin(void *data __attribute__ ((unused)),
|
||||||
xcb_connection_t *connection,
|
xcb_connection_t *connection,
|
||||||
xcb_focus_in_event_t *ev)
|
xcb_focus_in_event_t *ev)
|
||||||
{
|
{
|
||||||
/* filter focus-in events */
|
|
||||||
if (ev->detail != XCB_NOTIFY_DETAIL_NONLINEAR)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
client_t *c;
|
client_t *c;
|
||||||
|
|
||||||
if((c = client_getbytitlebarwin(ev->event))
|
/* Events that we are interested in: */
|
||||||
|| (c = client_getbywin(ev->event)))
|
switch(ev->detail)
|
||||||
client_focus(c, false);
|
{
|
||||||
|
/* 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.
|
/** 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_enter_notify_handler(&globalconf.evenths, event_handle_enternotify, NULL);
|
||||||
xcb_event_set_leave_notify_handler(&globalconf.evenths, event_handle_leavenotify, 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_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_motion_notify_handler(&globalconf.evenths, event_handle_motionnotify, NULL);
|
||||||
xcb_event_set_expose_handler(&globalconf.evenths, event_handle_expose, NULL);
|
xcb_event_set_expose_handler(&globalconf.evenths, event_handle_expose, NULL);
|
||||||
xcb_event_set_key_press_handler(&globalconf.evenths, event_handle_key, NULL);
|
xcb_event_set_key_press_handler(&globalconf.evenths, event_handle_key, NULL);
|
||||||
|
|
2
ewmh.c
2
ewmh.c
|
@ -429,7 +429,7 @@ ewmh_process_client_message(xcb_client_message_event_t *ev)
|
||||||
else if(ev->type == _NET_ACTIVE_WINDOW)
|
else if(ev->type == _NET_ACTIVE_WINDOW)
|
||||||
{
|
{
|
||||||
if((c = client_getbywin(ev->window)))
|
if((c = client_getbywin(ev->window)))
|
||||||
client_focus(c, true);
|
client_focus(c);
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
Loading…
Reference in New Issue