[mutter] display: (Optionally) delay focus changes in focus-follows-mouse mode



commit 59bc5b7975f1f19ebacb520c1c2666c0828d1111
Author: Florian MÃllner <fmuellner gnome org>
Date:   Wed Aug 29 04:38:54 2012 +0200

    display: (Optionally) delay focus changes in focus-follows-mouse mode
    
    Moving focus immediately on crossing events as we currently do
    in focus-follows-mouse mode may trigger a lot of unwanted focus
    changes when moving over unrelated windows on the way to a target.
    Those accidental focus changes prevent features like GNOME Shell's
    application menu from working properly and are visually expensive
    since we now use a very distinct style for unfocused windows.
    Instead, delay the actual focus change until the pointer has stopped
    moving.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=678169

 src/core/display-private.h          |    3 +
 src/core/display.c                  |  192 ++++++++++++++++++++++++++++-------
 src/core/prefs.c                    |   17 +++
 src/meta/prefs.h                    |    2 +
 src/org.gnome.mutter.gschema.xml.in |   10 ++
 5 files changed, 186 insertions(+), 38 deletions(-)
---
diff --git a/src/core/display-private.h b/src/core/display-private.h
index 6db1b64..b03fffd 100644
--- a/src/core/display-private.h
+++ b/src/core/display-private.h
@@ -172,6 +172,9 @@ struct _MetaDisplay
   /* Pings which we're waiting for a reply from */
   GSList     *pending_pings;
 
+  /* Pending focus change */
+  guint       focus_timeout_id;
+
   /* Pending autoraise */
   guint       autoraise_timeout_id;
   MetaWindow* autoraise_window;
diff --git a/src/core/display.c b/src/core/display.c
index f62fad1..19c3437 100644
--- a/src/core/display.c
+++ b/src/core/display.c
@@ -50,6 +50,7 @@
 #include "workspace-private.h"
 #include "bell.h"
 #include <meta/compositor.h>
+#include <meta/compositor-mutter.h>
 #include <X11/Xatom.h>
 #include <X11/cursorfont.h>
 #include "mutter-enum-types.h"
@@ -122,6 +123,15 @@ typedef struct
   Window xwindow;
 } MetaAutoRaiseData;
 
+typedef struct
+{
+  MetaDisplay *display;
+  MetaWindow *window;
+  int pointer_x;
+  int pointer_y;
+} MetaFocusData;
+
+
 G_DEFINE_TYPE(MetaDisplay, meta_display, G_TYPE_OBJECT);
 
 /* Signals */
@@ -1039,6 +1049,10 @@ meta_display_close (MetaDisplay *display,
   
   meta_display_remove_autoraise_callback (display);
 
+  if (display->focus_timeout_id);
+    g_source_remove (display->focus_timeout_id);
+  display->focus_timeout_id = 0;
+
   if (display->grab_old_window_stacking)
     g_list_free (display->grab_old_window_stacking);
   
@@ -1569,6 +1583,103 @@ window_raise_with_delay_callback (void *data)
   return FALSE;
 }
 
+static void
+meta_display_mouse_mode_focus (MetaDisplay *display,
+                               MetaWindow  *window,
+                               guint32      timestamp) {
+  if (window->type != META_WINDOW_DESKTOP)
+    {
+      meta_topic (META_DEBUG_FOCUS,
+                  "Focusing %s at time %u.\n", window->desc, timestamp);
+
+      meta_window_focus (window, timestamp);
+
+      if (meta_prefs_get_auto_raise ())
+        meta_display_queue_autoraise_callback (display, window);
+      else
+        meta_topic (META_DEBUG_FOCUS, "Auto raise is disabled\n");
+    }
+  else
+    {
+      /* In mouse focus mode, we defocus when the mouse *enters*
+       * the DESKTOP window, instead of defocusing on LeaveNotify.
+       * This is because having the mouse enter override-redirect
+       * child windows unfortunately causes LeaveNotify events that
+       * we can't distinguish from the mouse actually leaving the
+       * toplevel window as we expect.  But, since we filter out
+       * EnterNotify events on override-redirect windows, this
+       * alternative mechanism works great.
+       */
+      if (meta_prefs_get_focus_mode() == G_DESKTOP_FOCUS_MODE_MOUSE &&
+          display->expected_focus_window != NULL)
+        {
+          meta_topic (META_DEBUG_FOCUS,
+                      "Unsetting focus from %s due to mouse entering "
+                      "the DESKTOP window\n",
+                      display->expected_focus_window->desc);
+          meta_display_focus_the_no_focus_window (display,
+                                                  window->screen,
+                                                  timestamp);
+        }
+    }
+}
+
+static gboolean
+window_focus_on_pointer_rest_callback (gpointer data) {
+  MetaFocusData *focus_data;
+  MetaDisplay *display;
+  MetaScreen *screen;
+  MetaWindow *window;
+  Window root, child;
+  int root_x, root_y, x, y;
+  guint32 timestamp;
+  guint mask;
+
+  focus_data = data;
+  display = focus_data->display;
+  screen = focus_data->window->screen;
+
+  if (meta_prefs_get_focus_mode () == G_DESKTOP_FOCUS_MODE_CLICK)
+    goto out;
+
+  meta_error_trap_push (display);
+  XQueryPointer (display->xdisplay,
+                 screen->xroot,
+                 &root, &child,
+                 &root_x, &root_y, &x, &y, &mask);
+  meta_error_trap_pop (display);
+
+  if (root_x != focus_data->pointer_x ||
+      root_y != focus_data->pointer_y)
+    {
+      focus_data->pointer_x = root_x;
+      focus_data->pointer_y = root_y;
+      return TRUE;
+    }
+
+  /* Explicitly check for the overlay window, as get_focus_window_at_point()
+   * may return windows that extend underneath the chrome (like
+   * override-redirect or DESKTOP windows)
+   */
+  if (child == meta_get_overlay_window (screen))
+    goto out;
+
+  window =
+      meta_stack_get_default_focus_window_at_point (screen->stack,
+                                                    screen->active_workspace,
+                                                    None, root_x, root_y);
+
+  if (window == NULL)
+    goto out;
+
+  timestamp = meta_display_get_current_time_roundtrip (display);
+  meta_display_mouse_mode_focus (display, window, timestamp);
+
+out:
+  display->focus_timeout_id = 0;
+  return FALSE;
+}
+
 void
 meta_display_queue_autoraise_callback (MetaDisplay *display,
                                        MetaWindow  *window)
@@ -1596,6 +1707,37 @@ meta_display_queue_autoraise_callback (MetaDisplay *display,
   display->autoraise_window = window;
 }
 
+/* The interval, in milliseconds, we use in focus-follows-mouse
+ * mode to check whether the pointer has stopped moving after a
+ * crossing event.
+ */
+#define FOCUS_TIMEOUT_DELAY 25
+
+static void
+meta_display_queue_focus_callback (MetaDisplay *display,
+                                   MetaWindow  *window,
+                                   int          pointer_x,
+                                   int          pointer_y)
+{
+  MetaFocusData *focus_data;
+
+  focus_data = g_new (MetaFocusData, 1);
+  focus_data->display = display;
+  focus_data->window = window;
+  focus_data->pointer_x = pointer_x;
+  focus_data->pointer_y = pointer_y;
+
+  if (display->focus_timeout_id != 0)
+    g_source_remove (display->focus_timeout_id);
+
+  display->focus_timeout_id =
+    g_timeout_add_full (G_PRIORITY_DEFAULT,
+                        FOCUS_TIMEOUT_DELAY,
+                        window_focus_on_pointer_rest_callback,
+                        focus_data,
+                        g_free);
+}
+
 #if 0
 static void
 handle_net_restack_window (MetaDisplay* display,
@@ -2084,52 +2226,26 @@ event_callback (XEvent   *event,
             case G_DESKTOP_FOCUS_MODE_SLOPPY:
             case G_DESKTOP_FOCUS_MODE_MOUSE:
               display->mouse_mode = TRUE;
-              if (window->type != META_WINDOW_DOCK &&
-                  window->type != META_WINDOW_DESKTOP)
+              if (window->type != META_WINDOW_DOCK)
                 {
                   meta_topic (META_DEBUG_FOCUS,
-                              "Focusing %s due to enter notify with serial %lu "
-                              "at time %lu, and setting display->mouse_mode to "
-                              "TRUE.\n",
-                              window->desc, 
+                              "Queuing a focus change for %s due to "
+                              "enter notify with serial %lu at time %lu, "
+                              "and setting display->mouse_mode to TRUE.\n",
+                              window->desc,
                               event->xany.serial,
                               event->xcrossing.time);
 
-                  meta_window_focus (window, event->xcrossing.time);
+                  if (meta_prefs_get_focus_change_on_pointer_rest())
+                    meta_display_queue_focus_callback (display, window,
+                                                       event->xcrossing.x_root,
+                                                       event->xcrossing.y_root);
+                  else
+                    meta_display_mouse_mode_focus (display, window,
+                                                   event->xcrossing.time);
 
                   /* stop ignoring stuff */
                   reset_ignored_crossing_serials (display);
-                  
-                  if (meta_prefs_get_auto_raise ()) 
-                    {
-                      meta_display_queue_autoraise_callback (display, window);
-                    }
-                  else
-                    {
-                      meta_topic (META_DEBUG_FOCUS,
-                                  "Auto raise is disabled\n");		      
-                    }
-                }
-              /* In mouse focus mode, we defocus when the mouse *enters*
-               * the DESKTOP window, instead of defocusing on LeaveNotify.
-               * This is because having the mouse enter override-redirect
-               * child windows unfortunately causes LeaveNotify events that
-               * we can't distinguish from the mouse actually leaving the
-               * toplevel window as we expect.  But, since we filter out
-               * EnterNotify events on override-redirect windows, this
-               * alternative mechanism works great.
-               */
-              if (window->type == META_WINDOW_DESKTOP &&
-                  meta_prefs_get_focus_mode() == G_DESKTOP_FOCUS_MODE_MOUSE &&
-                  display->expected_focus_window != NULL)
-                {
-                  meta_topic (META_DEBUG_FOCUS,
-                              "Unsetting focus from %s due to mouse entering "
-                              "the DESKTOP window\n",
-                              display->expected_focus_window->desc);
-                  meta_display_focus_the_no_focus_window (display, 
-                                                          window->screen,
-                                                          event->xcrossing.time);
                 }
               break;
             case G_DESKTOP_FOCUS_MODE_CLICK:
diff --git a/src/core/prefs.c b/src/core/prefs.c
index f67b283..802d620 100644
--- a/src/core/prefs.c
+++ b/src/core/prefs.c
@@ -87,6 +87,7 @@ static gboolean application_based = FALSE;
 static gboolean disable_workarounds = FALSE;
 static gboolean auto_raise = FALSE;
 static gboolean auto_raise_delay = 500;
+static gboolean focus_change_on_pointer_rest = FALSE;
 static gboolean bell_is_visible = FALSE;
 static gboolean bell_is_audible = TRUE;
 static gboolean gnome_accessibility = FALSE;
@@ -305,6 +306,13 @@ static MetaBoolPreference preferences_bool[] =
       &auto_raise,
     },
     {
+      { "focus-change-on-pointer-rest",
+        SCHEMA_MUTTER,
+        META_PREF_FOCUS_CHANGE_ON_POINTER_REST,
+      },
+      &focus_change_on_pointer_rest
+    },
+    {
       { "visual-bell",
         SCHEMA_GENERAL,
         META_PREF_VISUAL_BELL,
@@ -1608,6 +1616,9 @@ meta_preference_to_string (MetaPreference pref)
     case META_PREF_AUTO_RAISE_DELAY:
       return "AUTO_RAISE_DELAY";
 
+    case META_PREF_FOCUS_CHANGE_ON_POINTER_REST:
+      return "FOCUS_CHANGE_ON_POINTER_REST";
+
     case META_PREF_BUTTON_LAYOUT:
       return "BUTTON_LAYOUT";
 
@@ -2047,6 +2058,12 @@ meta_prefs_get_auto_raise_delay (void)
 }
 
 gboolean
+meta_prefs_get_focus_change_on_pointer_rest ()
+{
+  return focus_change_on_pointer_rest;
+}
+
+gboolean
 meta_prefs_get_gnome_accessibility ()
 {
   return gnome_accessibility;
diff --git a/src/meta/prefs.h b/src/meta/prefs.h
index bbbe768..ff6984c 100644
--- a/src/meta/prefs.h
+++ b/src/meta/prefs.h
@@ -45,6 +45,7 @@ typedef enum
   META_PREF_ACTION_RIGHT_CLICK_TITLEBAR,
   META_PREF_AUTO_RAISE,
   META_PREF_AUTO_RAISE_DELAY,
+  META_PREF_FOCUS_CHANGE_ON_POINTER_REST,
   META_PREF_THEME,
   META_PREF_TITLEBAR_FONT,
   META_PREF_NUM_WORKSPACES,
@@ -100,6 +101,7 @@ gboolean                    meta_prefs_get_application_based  (void);
 gboolean                    meta_prefs_get_disable_workarounds (void);
 gboolean                    meta_prefs_get_auto_raise         (void);
 int                         meta_prefs_get_auto_raise_delay   (void);
+gboolean                    meta_prefs_get_focus_change_on_pointer_rest (void);
 gboolean                    meta_prefs_get_gnome_accessibility (void);
 gboolean                    meta_prefs_get_gnome_animations   (void);
 gboolean                    meta_prefs_get_edge_tiling        (void);
diff --git a/src/org.gnome.mutter.gschema.xml.in b/src/org.gnome.mutter.gschema.xml.in
index e23ad81..a2b9eec 100644
--- a/src/org.gnome.mutter.gschema.xml.in
+++ b/src/org.gnome.mutter.gschema.xml.in
@@ -63,6 +63,16 @@
       </_description>
     </key>
 
+    <key name="focus-change-on-pointer-rest" type="b">
+      <default>false</default>
+      <_summary>Delay focus changes until the pointer stops moving</_summary>
+      <_description>
+        If set to true, and the focus mode is either "sloppy" or "mouse"
+        then the focus will not be changed immediately when entering a
+        window, but only after the pointer stops moving.
+      </_description>
+    </key>
+
     <key name="draggable-border-width" type="i">
       <default>10</default>
       <range min="0" max="64"/>



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