[metacity] fix problems with focus tracking



commit 19c5732a24e89f405365efcfd749c55b4c9c215f
Author: Owen W. Taylor <otaylor fishsoup net>
Date:   Mon Dec 16 16:01:04 2013 -0500

    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

 src/core/display-private.h |  8 ++++++++
 src/core/display.c         | 25 +++++++++++++++++++------
 2 files changed, 27 insertions(+), 6 deletions(-)
---
diff --git a/src/core/display-private.h b/src/core/display-private.h
index b7246ec9..8a2ee3ed 100644
--- a/src/core/display-private.h
+++ b/src/core/display-private.h
@@ -127,6 +127,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;
+
   /*< private-ish >*/
   guint error_trap_synced_at_last_pop : 1;
   MetaScreen *screen;
diff --git a/src/core/display.c b/src/core/display.c
index f4338da7..b048f5f4 100644
--- a/src/core/display.c
+++ b/src/core/display.c
@@ -1415,9 +1415,11 @@ button_press_event_new (XEvent *xevent,
 static void
 update_focus_window (MetaDisplay *display,
                      MetaWindow  *window,
-                     gulong       serial)
+                     gulong       serial,
+                     gboolean     focused_by_us)
 {
   display->focus_serial = serial;
+  display->focused_by_us = focused_by_us;
 
   if (window == display->focus_window)
     return;
@@ -1527,7 +1529,8 @@ request_xserver_input_focus_change (MetaDisplay *display,
 
   update_focus_window (display,
                        meta_window,
-                       serial);
+                       serial,
+                       TRUE);
 
   meta_error_trap_pop (display);
 
@@ -1637,10 +1640,18 @@ 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,
-                           display->server_focus_serial);
+                           display->server_focus_serial,
+                           FALSE);
     }
 }
 
@@ -1685,7 +1696,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)
     {
@@ -1693,7 +1705,8 @@ event_callback (XEvent   *event,
                   display->focus_window->desc);
       update_focus_window (display,
                            meta_display_lookup_x_window (display, display->server_focus_window),
-                           display->server_focus_serial);
+                           display->server_focus_serial,
+                           FALSE);
     }
 
   modified = event_get_modified_window (display, event);


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]