summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorOwen W. Taylor <otaylor@fishsoup.net>2013-12-16 16:01:04 -0500
committerOwen W. Taylor <otaylor@fishsoup.net>2013-12-18 09:41:49 -0500
commitf3196e356b66420b5d1bbf6ad00c6c152c975ddd (patch)
treef59835ba44fb52e6f88260f30c9b3fc7ac22f1df
parent491b17af194b051dfd6837fcbd17c4e436b63e5b (diff)
Fix problems with focus tracking
When a client spontaneously focuses their window, perhaps in response to WM_TAKE_FOCUS we'll get a FocusOut/FocusIn pair with same serial. Updating display->focus_serial in response to FocusOut then was causing us to ignore FocusIn and think that the focus was not on any window. We need to distinguish this spontaneous case from the case where we set the focus ourselves - when we set the focus ourselves, we're careful to combine the SetFocus with a property change so that we know definitively what focus events we have already accounted for. https://bugzilla.gnome.org/show_bug.cgi?id=720558
-rw-r--r--src/core/display-private.h8
-rw-r--r--src/core/display.c25
2 files changed, 27 insertions, 6 deletions
diff --git a/src/core/display-private.h b/src/core/display-private.h
index 86284fe1..3d2e08d5 100644
--- a/src/core/display-private.h
+++ b/src/core/display-private.h
@@ -138,6 +138,14 @@ struct _MetaDisplay
*/
guint allow_terminal_deactivation : 1;
+ /* If true, server->focus_serial refers to us changing the focus; in
+ * this case, we can ignore focus events that have exactly focus_serial,
+ * since we take care to make another request immediately afterwards.
+ * But if focus is being changed by another client, we have to accept
+ * multiple events with the same serial.
+ */
+ guint focused_by_us : 1;
+
guint static_gravity_works : 1;
/*< private-ish >*/
diff --git a/src/core/display.c b/src/core/display.c
index c44a42ac..5fd3e3d4 100644
--- a/src/core/display.c
+++ b/src/core/display.c
@@ -1890,9 +1890,11 @@ static void
update_focus_window (MetaDisplay *display,
MetaWindow *window,
Window xwindow,
- gulong serial)
+ gulong serial,
+ gboolean focused_by_us)
{
display->focus_serial = serial;
+ display->focused_by_us = focused_by_us;
if (display->focus_xwindow == xwindow)
return;
@@ -2003,7 +2005,8 @@ request_xserver_input_focus_change (MetaDisplay *display,
update_focus_window (display,
meta_window,
xwindow,
- serial);
+ serial,
+ TRUE);
meta_error_trap_pop (display);
@@ -2117,12 +2120,20 @@ handle_window_focus_event (MetaDisplay *display,
else
g_return_if_reached ();
- if (display->server_focus_serial > display->focus_serial)
+ /* If display->focused_by_us, then the focus_serial will be used only
+ * for a focus change we made and have already accounted for.
+ * (See request_xserver_input_focus_change().) Otherwise, we can get
+ * multiple focus events with the same serial.
+ */
+ if (display->server_focus_serial > display->focus_serial ||
+ (!display->focused_by_us &&
+ display->server_focus_serial == display->focus_serial))
{
update_focus_window (display,
focus_window,
focus_window ? focus_window->xwindow : None,
- display->server_focus_serial);
+ display->server_focus_serial,
+ FALSE);
}
}
@@ -2179,7 +2190,8 @@ event_callback (XEvent *event,
display->current_time = event_get_time (display, event);
display->monitor_cache_invalidated = TRUE;
- if (event->xany.serial > display->focus_serial &&
+ if (display->focused_by_us &&
+ event->xany.serial > display->focus_serial &&
display->focus_window &&
display->focus_window->xwindow != display->server_focus_window)
{
@@ -2188,7 +2200,8 @@ event_callback (XEvent *event,
update_focus_window (display,
meta_display_lookup_x_window (display, display->server_focus_window),
display->server_focus_window,
- display->server_focus_serial);
+ display->server_focus_serial,
+ FALSE);
}
screen = meta_display_screen_for_root (display, event->xany.window);