From 0ce14a727968736e57fb3fabba3794b46903875f Mon Sep 17 00:00:00 2001 From: Dana Jansens Date: Fri, 11 Jan 2008 14:40:58 -0500 Subject: [PATCH] fix a focus race condition in two ways: 1. when focusing a window, ignore any enter events up until the serial of the X event causing the focus, not up until the last thing sent to the server. if we get 2 enters very quickly, then we don't want to ignore the second one just because we are focusing the first window. 2. there is a race if you check (focus_client != d->client) in the delay_focus_func, because the current focused window might change by the time this focus_client would take effect, so don't check that. --- openbox/event.c | 45 +++++++++++++++++++++++++++++++-------------- 1 file changed, 31 insertions(+), 14 deletions(-) diff --git a/openbox/event.c b/openbox/event.c index e3214a7a..03229117 100644 --- a/openbox/event.c +++ b/openbox/event.c @@ -91,6 +91,7 @@ static void event_handle_dockapp(ObDockApp *app, XEvent *e); static void event_handle_client(ObClient *c, XEvent *e); static void event_handle_user_input(ObClient *client, XEvent *e); static gboolean is_enter_focus_event_ignored(XEvent *e); +static void event_ignore_enter_range(gulong start, gulong end); static void focus_delay_dest(gpointer data); static gboolean focus_delay_cmp(gconstpointer d1, gconstpointer d2); @@ -99,6 +100,8 @@ static void focus_delay_client_dest(ObClient *client, gpointer data); Time event_curtime = CurrentTime; Time event_last_user_time = CurrentTime; +/*! The serial of the current X event */ +gulong event_curserial; static gboolean focus_left_screen = FALSE; /*! A list of ObSerialRanges which are to be ignored for mouse enter events */ @@ -243,6 +246,7 @@ static void event_set_curtime(XEvent *e) event_last_user_time = CurrentTime; event_curtime = t; + event_curserial = 0; } static void event_hack_mods(XEvent *e) @@ -479,6 +483,7 @@ static void event_process(const XEvent *ec, gpointer data) } event_set_curtime(e); + event_curserial = e->xany.serial; event_hack_mods(e); if (event_ignore(e, client)) { if (ed) @@ -1010,18 +1015,23 @@ static void event_handle_client(ObClient *client, XEvent *e) is_enter_focus_event_ignored(e)) { ob_debug_type(OB_DEBUG_FOCUS, - "%sNotify mode %d detail %d on %lx IGNORED\n", + "%sNotify mode %d detail %d serial %lu on %lx " + "IGNORED\n", (e->type == EnterNotify ? "Enter" : "Leave"), e->xcrossing.mode, - e->xcrossing.detail, client?client->window:0); + e->xcrossing.detail, + e->xcrossing.serial, + client?client->window:0); } else { ob_debug_type(OB_DEBUG_FOCUS, - "%sNotify mode %d detail %d on %lx, " + "%sNotify mode %d detail %d serial %lu on %lx, " "focusing window\n", (e->type == EnterNotify ? "Enter" : "Leave"), e->xcrossing.mode, - e->xcrossing.detail, (client?client->window:0)); + e->xcrossing.detail, + e->xcrossing.serial, + (client?client->window:0)); if (config_focus_follow) event_enter_client(client); } @@ -1864,10 +1874,8 @@ static gboolean focus_delay_func(gpointer data) if (menu_frame_visible || moveresize_in_progress) return FALSE; event_curtime = d->time; - if (focus_client != d->client) { - if (client_focus(d->client) && config_focus_raise) - stacking_raise(CLIENT_AS_WINDOW(d->client)); - } + if (client_focus(d->client) && config_focus_raise) + stacking_raise(CLIENT_AS_WINDOW(d->client)); event_curtime = old; return FALSE; /* no repeat */ } @@ -1878,35 +1886,44 @@ static void focus_delay_client_dest(ObClient *client, gpointer data) client, FALSE); } -void event_halt_focus_delay(void) +void event_halt_focus_delay(gulong serial) { - /* ignore all enter events up till now */ - event_end_ignore_all_enters(1); + /* ignore all enter events up till the event which caused this to occur */ + if (event_curserial) event_ignore_enter_range(1, event_curserial); ob_main_loop_timeout_remove(ob_main_loop, focus_delay_func); } gulong event_start_ignore_all_enters(void) { + /* increment the serial so we don't ignore events we weren't meant to */ XSync(ob_display, FALSE); return LastKnownRequestProcessed(ob_display); } -void event_end_ignore_all_enters(gulong start) +static void event_ignore_enter_range(gulong start, gulong end) { ObSerialRange *r; g_assert(start != 0); - XSync(ob_display, FALSE); + g_assert(end != 0); r = g_new(ObSerialRange, 1); r->start = start; - r->end = LastKnownRequestProcessed(ob_display); + r->end = end; ignore_serials = g_slist_prepend(ignore_serials, r); + ob_debug_type(OB_DEBUG_FOCUS, "ignoring enters from %lu until %lu\n", + r->start, r->end); + /* increment the serial so we don't ignore events we weren't meant to */ XSync(ob_display, FALSE); } +void event_end_ignore_all_enters(gulong start) +{ + event_ignore_enter_range(start, LastKnownRequestProcessed(ob_display)); +} + static gboolean is_enter_focus_event_ignored(XEvent *e) { GSList *it, *next; -- 2.39.2