Blame SOURCES/For-mouse-and-sloppy-focus-return-to-mouse-mode-on.patch

8063f1
From a24b336303c50e74f7d5e8582049dfae0d70329d Mon Sep 17 00:00:00 2001
8063f1
From: "Owen W. Taylor" <otaylor@fishsoup.net>
8063f1
Date: Tue, 20 Oct 2009 15:13:45 -0400
8063f1
Subject: [PATCH] For mouse and sloppy focus, return to "mouse mode" on
8063f1
 motion
8063f1
8063f1
For mouse and sloppy focus, there are various cases where the focus
8063f1
window can be moved away from the focus window. Mostly these relate
8063f1
to "display->mouse_mode = FALSE", which we enter when the user
8063f1
starts keynav'ing, but it can also occur if a window is focus-denied
8063f1
mapped and mapped under the pointer.
8063f1
8063f1
Prior to this patch, there was no fast way for the user to start
8063f1
interacting with the window - if they just clicked on the window,
8063f1
the click would be passed through, and could disturb the windows
8063f1
contents, so the user had to either mouse out and then mouse back
8063f1
in, or go up and click on the titlebar.
8063f1
8063f1
With this patch, when we get into this state, we add a timeout
8063f1
and poll for pointer motion with XQueryPointer. If the user then
8063f1
moves the pointer (more than a single pixel to handle jitter),
8063f1
we focus the window under the pointer and return to mouse mode.
8063f1
8063f1
https://bugzilla.gnome.org/show_bug.cgi?id=599097
8063f1
---
8063f1
 src/core/display-private.h |   11 ++
8063f1
 src/core/display.c         |  239 +++++++++++++++++++++++++++++++++++--------
8063f1
 src/core/keybindings.c     |   10 +-
8063f1
 3 files changed, 210 insertions(+), 50 deletions(-)
8063f1
8063f1
diff --git a/src/core/display-private.h b/src/core/display-private.h
8063f1
index feee851..fee321c 100644
8063f1
--- a/src/core/display-private.h
8063f1
+++ b/src/core/display-private.h
8063f1
@@ -150,6 +150,14 @@ struct _MetaDisplay
8063f1
   guint       autoraise_timeout_id;
8063f1
   MetaWindow* autoraise_window;
8063f1
 
8063f1
+  /* When we ignore an enter due to !display->mouse_mode, a timeout
8063f1
+   * to check if the mouse is moved, in which case we should focus
8063f1
+   * the pointer window and return to mouse mode */
8063f1
+  guint       focus_on_motion_timeout_id;
8063f1
+  Window      focus_on_motion_start_root_window;
8063f1
+  int         focus_on_motion_start_x;
8063f1
+  int         focus_on_motion_start_y;
8063f1
+
8063f1
   /* Alt+click button grabs */
8063f1
   unsigned int window_grab_modifiers;
8063f1
   
8063f1
@@ -501,4 +509,7 @@ void meta_display_remove_autoraise_callback (MetaDisplay *display);
8063f1
 /* In above-tab-keycode.c */
8063f1
 guint meta_display_get_above_tab_keycode (MetaDisplay *display);
8063f1
 
8063f1
+void meta_display_disable_mouse_mode        (MetaDisplay *display);
8063f1
+void meta_display_enable_mouse_mode         (MetaDisplay *display);
8063f1
+
8063f1
 #endif
8063f1
diff --git a/src/core/display.c b/src/core/display.c
8063f1
index 2e959b6..5bcf025 100644
8063f1
--- a/src/core/display.c
8063f1
+++ b/src/core/display.c
8063f1
@@ -165,6 +165,9 @@ static void    sanity_check_timestamps   (MetaDisplay *display,
8063f1
 
8063f1
 MetaGroup*     get_focussed_group (MetaDisplay *display);
8063f1
 
8063f1
+static void start_focus_on_motion (MetaDisplay *display);
8063f1
+static void stop_focus_on_motion  (MetaDisplay *display);
8063f1
+
8063f1
 /**
8063f1
  * Destructor for MetaPingData structs. Will destroy the
8063f1
  * event source for the struct as well.
8063f1
@@ -876,6 +879,7 @@ meta_display_close (MetaDisplay *display,
8063f1
   meta_prefs_remove_listener (prefs_changed_callback, display);
8063f1
   
8063f1
   meta_display_remove_autoraise_callback (display);
8063f1
+  stop_focus_on_motion (display);
8063f1
 
8063f1
   if (display->grab_old_window_stacking)
8063f1
     g_list_free (display->grab_old_window_stacking);
8063f1
@@ -1778,67 +1782,86 @@ event_callback (XEvent   *event,
8063f1
       if (window && !serial_is_ignored (display, event->xany.serial) &&
8063f1
                event->xcrossing.mode != NotifyGrab && 
8063f1
                event->xcrossing.mode != NotifyUngrab &&
8063f1
-               event->xcrossing.detail != NotifyInferior &&
8063f1
-               meta_display_focus_sentinel_clear (display))
8063f1
+               event->xcrossing.detail != NotifyInferior)
8063f1
         {
8063f1
           switch (meta_prefs_get_focus_mode ())
8063f1
             {
8063f1
             case G_DESKTOP_FOCUS_MODE_SLOPPY:
8063f1
             case G_DESKTOP_FOCUS_MODE_MOUSE:
8063f1
-              display->mouse_mode = TRUE;
8063f1
-              if (window->type != META_WINDOW_DOCK &&
8063f1
-                  window->type != META_WINDOW_DESKTOP)
8063f1
+              if (!meta_display_focus_sentinel_clear (display))
8063f1
                 {
8063f1
-                  meta_topic (META_DEBUG_FOCUS,
8063f1
-                              "Focusing %s due to enter notify with serial %lu "
8063f1
-                              "at time %lu, and setting display->mouse_mode to "
8063f1
-                              "TRUE.\n",
8063f1
-                              window->desc, 
8063f1
-                              event->xany.serial,
8063f1
-                              event->xcrossing.time);
8063f1
-
8063f1
-                  meta_window_focus (window, event->xcrossing.time);
8063f1
-
8063f1
-                  /* stop ignoring stuff */
8063f1
-                  reset_ignores (display);
8063f1
-                  
8063f1
-                  if (meta_prefs_get_auto_raise ()) 
8063f1
+                  /* There was an enter event that we want to ignore because
8063f1
+                   * we're in "keynav mode" or because we are mapping
8063f1
+                   * a focus-denied window; the next time the mouse is moved
8063f1
+                   * we want to focus the window so the user doesn't have
8063f1
+                   * to click (possibly messing up window contents) or
8063f1
+                   * enter/leave to get focus to the window.
8063f1
+                   *
8063f1
+                   * (This check will also trigger for visual bell flashes
8063f1
+                   * but it doesn't really do any harm to check for motion
8063f1
+                   * in that case, since the next motion will just result in
8063f1
+                   * the current window being focused.)
8063f1
+                   */
8063f1
+                  start_focus_on_motion (display);
8063f1
+                }
8063f1
+              else
8063f1
+                {
8063f1
+                  meta_display_enable_mouse_mode (display);
8063f1
+                  if (window->type != META_WINDOW_DOCK &&
8063f1
+                      window->type != META_WINDOW_DESKTOP)
8063f1
                     {
8063f1
-                      meta_display_queue_autoraise_callback (display, window);
8063f1
+                      meta_topic (META_DEBUG_FOCUS,
8063f1
+                                  "Focusing %s due to enter notify with serial %lu "
8063f1
+                                  "at time %lu, and setting display->mouse_mode to "
8063f1
+                                  "TRUE.\n",
8063f1
+                                  window->desc, 
8063f1
+                                  event->xany.serial,
8063f1
+                                  event->xcrossing.time);
8063f1
+
8063f1
+                      meta_window_focus (window, event->xcrossing.time);
8063f1
+
8063f1
+                      /* stop ignoring stuff */
8063f1
+                      reset_ignores (display);
8063f1
+
8063f1
+                      if (meta_prefs_get_auto_raise ()) 
8063f1
+                        {
8063f1
+                          meta_display_queue_autoraise_callback (display, window);
8063f1
+                        }
8063f1
+                      else
8063f1
+                        {
8063f1
+                          meta_topic (META_DEBUG_FOCUS,
8063f1
+                                      "Auto raise is disabled\n");		      
8063f1
+                        }
8063f1
                     }
8063f1
-                  else
8063f1
+                  /* In mouse focus mode, we defocus when the mouse *enters*
8063f1
+                   * the DESKTOP window, instead of defocusing on LeaveNotify.
8063f1
+                   * This is because having the mouse enter override-redirect
8063f1
+                   * child windows unfortunately causes LeaveNotify events that
8063f1
+                   * we can't distinguish from the mouse actually leaving the
8063f1
+                   * toplevel window as we expect.  But, since we filter out
8063f1
+                   * EnterNotify events on override-redirect windows, this
8063f1
+                   * alternative mechanism works great.
8063f1
+                   */
8063f1
+                  if (window->type == META_WINDOW_DESKTOP &&
8063f1
+                      meta_prefs_get_focus_mode() == G_DESKTOP_FOCUS_MODE_MOUSE &&
8063f1
+                      display->expected_focus_window != NULL)
8063f1
                     {
8063f1
                       meta_topic (META_DEBUG_FOCUS,
8063f1
-                                  "Auto raise is disabled\n");		      
8063f1
+                                  "Unsetting focus from %s due to mouse entering "
8063f1
+                                  "the DESKTOP window\n",
8063f1
+                                  display->expected_focus_window->desc);
8063f1
+                      meta_display_focus_the_no_focus_window (display, 
8063f1
+                                                              window->screen,
8063f1
+                                                              event->xcrossing.time);
8063f1
                     }
8063f1
                 }
8063f1
-              /* In mouse focus mode, we defocus when the mouse *enters*
8063f1
-               * the DESKTOP window, instead of defocusing on LeaveNotify.
8063f1
-               * This is because having the mouse enter override-redirect
8063f1
-               * child windows unfortunately causes LeaveNotify events that
8063f1
-               * we can't distinguish from the mouse actually leaving the
8063f1
-               * toplevel window as we expect.  But, since we filter out
8063f1
-               * EnterNotify events on override-redirect windows, this
8063f1
-               * alternative mechanism works great.
8063f1
-               */
8063f1
-              if (window->type == META_WINDOW_DESKTOP &&
8063f1
-                  meta_prefs_get_focus_mode() == G_DESKTOP_FOCUS_MODE_MOUSE &&
8063f1
-                  display->expected_focus_window != NULL)
8063f1
-                {
8063f1
-                  meta_topic (META_DEBUG_FOCUS,
8063f1
-                              "Unsetting focus from %s due to mouse entering "
8063f1
-                              "the DESKTOP window\n",
8063f1
-                              display->expected_focus_window->desc);
8063f1
-                  meta_display_focus_the_no_focus_window (display, 
8063f1
-                                                          window->screen,
8063f1
-                                                          event->xcrossing.time);
8063f1
-                }
8063f1
               break;
8063f1
             case G_DESKTOP_FOCUS_MODE_CLICK:
8063f1
               break;
8063f1
             }
8063f1
-          
8063f1
-          if (window->type == META_WINDOW_DOCK)
8063f1
+
8063f1
+          if (window->type == META_WINDOW_DOCK &&
8063f1
+              meta_display_focus_sentinel_clear (display))
8063f1
             meta_window_raise (window);
8063f1
         }
8063f1
       break;
8063f1
@@ -5095,6 +5118,132 @@ meta_display_remove_autoraise_callback (MetaDisplay *display)
8063f1
     }
8063f1
 }
8063f1
 
8063f1
+#define FOCUS_ON_MOTION_CHECK_INTERVAL 200 /* 0.2 seconds */
8063f1
+#define FOCUS_ON_MOTION_THRESHOLD 2        /* Must move 2 pixels */
8063f1
+
8063f1
+static gboolean
8063f1
+check_focus_on_motion (gpointer data)
8063f1
+{
8063f1
+  MetaDisplay *display = data;
8063f1
+  Window root, child;
8063f1
+  int root_x, root_y;
8063f1
+  int window_x, window_y;
8063f1
+  guint mask;
8063f1
+
8063f1
+  XQueryPointer (display->xdisplay,
8063f1
+                 DefaultRootWindow (display->xdisplay),
8063f1
+                 &root, &child,
8063f1
+                 &root_x, &root_y,
8063f1
+                 &window_x, &window_y,
8063f1
+                 &mask);
8063f1
+
8063f1
+  if (root != display->focus_on_motion_start_root_window ||
8063f1
+      MAX (ABS (root_x - display->focus_on_motion_start_x),
8063f1
+           ABS (root_y - display->focus_on_motion_start_y)) >= FOCUS_ON_MOTION_THRESHOLD)
8063f1
+    {
8063f1
+      MetaScreen *screen;
8063f1
+
8063f1
+      meta_topic (META_DEBUG_FOCUS,
8063f1
+                  "Returning to mouse mode on mouse motion\n");
8063f1
+
8063f1
+      meta_display_enable_mouse_mode (display);
8063f1
+
8063f1
+      screen = meta_display_screen_for_root (display, root);
8063f1
+      if (screen != NULL)
8063f1
+        {
8063f1
+          MetaWindow *window = meta_screen_get_mouse_window (screen, NULL);
8063f1
+          guint32 timestamp = meta_display_get_current_time_roundtrip (display);
8063f1
+
8063f1
+          if (window &&
8063f1
+              window->type != META_WINDOW_DOCK &&
8063f1
+              window->type != META_WINDOW_DESKTOP)
8063f1
+            {
8063f1
+              meta_topic (META_DEBUG_FOCUS,
8063f1
+                          "Focusing mouse window %s\n", window->desc);
8063f1
+
8063f1
+              meta_window_focus (window, timestamp);
8063f1
+
8063f1
+              if (display->autoraise_window != window &&
8063f1
+                  meta_prefs_get_auto_raise ())
8063f1
+                {
8063f1
+                  meta_display_queue_autoraise_callback (display, window);
8063f1
+                }
8063f1
+            }
8063f1
+          else if (meta_prefs_get_focus_mode () == G_DESKTOP_FOCUS_MODE_MOUSE)
8063f1
+            {
8063f1
+              meta_topic (META_DEBUG_FOCUS,
8063f1
+                          "Setting focus to no_focus_windowm, since no mouse window.\n");
8063f1
+              meta_display_focus_the_no_focus_window (display, screen, timestamp);
8063f1
+            }
8063f1
+
8063f1
+          /* for G_DESKTOP_FOCUS_MODE_SLOPPY, if the pointer isn't over a window, we just
8063f1
+           * leave the last window focused */
8063f1
+        }
8063f1
+    }
8063f1
+
8063f1
+  return TRUE;
8063f1
+}
8063f1
+
8063f1
+static void
8063f1
+start_focus_on_motion (MetaDisplay *display)
8063f1
+{
8063f1
+  if (!display->focus_on_motion_timeout_id)
8063f1
+    {
8063f1
+      Window child;
8063f1
+      guint mask;
8063f1
+      int window_x, window_y;
8063f1
+
8063f1
+      XQueryPointer (display->xdisplay,
8063f1
+                     DefaultRootWindow (display->xdisplay),
8063f1
+                     &display->focus_on_motion_start_root_window,
8063f1
+                     &child,
8063f1
+                     &display->focus_on_motion_start_x,
8063f1
+                     &display->focus_on_motion_start_y,
8063f1
+                     &window_x, &window_y,
8063f1
+                     &mask);
8063f1
+
8063f1
+      display->focus_on_motion_timeout_id =
8063f1
+        g_timeout_add (FOCUS_ON_MOTION_CHECK_INTERVAL,
8063f1
+                       check_focus_on_motion,
8063f1
+                       display);
8063f1
+    }
8063f1
+}
8063f1
+
8063f1
+static void
8063f1
+stop_focus_on_motion (MetaDisplay *display)
8063f1
+{
8063f1
+  if (display->focus_on_motion_timeout_id)
8063f1
+    {
8063f1
+      g_source_remove (display->focus_on_motion_timeout_id);
8063f1
+      display->focus_on_motion_timeout_id = 0;
8063f1
+    }
8063f1
+}
8063f1
+
8063f1
+void
8063f1
+meta_display_disable_mouse_mode (MetaDisplay *display)
8063f1
+{
8063f1
+  display->mouse_mode = FALSE;
8063f1
+
8063f1
+  /* mouse_mode disabled means that we are now allowing the
8063f1
+   * mouse window to be different from the focus window;
8063f1
+   * that discrepancy might not come until we ignore some
8063f1
+   * enter event, but in a case like tabbing away from the
8063f1
+   * mouse window, it occurs immediately, so we need to
8063f1
+   * start checking for motion events to see if we should
8063f1
+   * focus the mouse window and return to mouse mode.
8063f1
+   */
8063f1
+  if (meta_prefs_get_focus_mode () != G_DESKTOP_FOCUS_MODE_CLICK)
8063f1
+    start_focus_on_motion (display);
8063f1
+}
8063f1
+
8063f1
+void
8063f1
+meta_display_enable_mouse_mode (MetaDisplay *display)
8063f1
+{
8063f1
+  display->mouse_mode = TRUE;
8063f1
+
8063f1
+  stop_focus_on_motion (display);
8063f1
+}
8063f1
+
8063f1
 #ifdef HAVE_COMPOSITE_EXTENSIONS
8063f1
 void
8063f1
 meta_display_get_compositor_version (MetaDisplay *display,
8063f1
diff --git a/src/core/keybindings.c b/src/core/keybindings.c
8063f1
index 08d861e..3c0ef95 100644
8063f1
--- a/src/core/keybindings.c
8063f1
+++ b/src/core/keybindings.c
8063f1
@@ -2082,7 +2082,7 @@ process_tab_grab (MetaDisplay *display,
8063f1
           meta_topic (META_DEBUG_FOCUS, "Activating %s due to tab popup "
8063f1
                       "selection and turning mouse_mode off\n",
8063f1
                       target_window->desc);
8063f1
-          display->mouse_mode = FALSE;
8063f1
+          meta_display_disable_mouse_mode (display);
8063f1
           meta_window_activate (target_window, event->xkey.time);
8063f1
 
8063f1
           meta_topic (META_DEBUG_KEYBINDINGS,
8063f1
@@ -2686,7 +2686,7 @@ handle_panel (MetaDisplay    *display,
8063f1
   meta_topic (META_DEBUG_KEYBINDINGS,
8063f1
               "Sending panel message with timestamp %lu, and turning mouse_mode "
8063f1
               "off due to keybinding press\n", event->xkey.time);
8063f1
-  display->mouse_mode = FALSE;
8063f1
+  meta_display_disable_mouse_mode (display);
8063f1
 
8063f1
   meta_error_trap_push (display);
8063f1
 
8063f1
@@ -2809,7 +2809,7 @@ do_choose_window (MetaDisplay    *display,
8063f1
                       "Activating %s and turning off mouse_mode due to "
8063f1
                       "switch/cycle windows with no modifiers\n",
8063f1
                       initial_selection->desc);
8063f1
-          display->mouse_mode = FALSE;
8063f1
+          meta_display_disable_mouse_mode (display);
8063f1
           meta_window_activate (initial_selection, event->xkey.time);
8063f1
         }
8063f1
       else if (meta_display_begin_grab_op (display,
8063f1
@@ -2838,7 +2838,7 @@ do_choose_window (MetaDisplay    *display,
8063f1
                           "modifier was released prior to grab\n",
8063f1
                           initial_selection->desc);
8063f1
               meta_display_end_grab_op (display, event->xkey.time);
8063f1
-              display->mouse_mode = FALSE;
8063f1
+              meta_display_disable_mouse_mode (display);
8063f1
               meta_window_activate (initial_selection, event->xkey.time);
8063f1
             }
8063f1
           else
8063f1
@@ -3079,7 +3079,7 @@ handle_move_to_workspace  (MetaDisplay    *display,
8063f1
           meta_topic (META_DEBUG_FOCUS,
8063f1
                       "Resetting mouse_mode to FALSE due to "
8063f1
                       "handle_move_to_workspace() call with flip set.\n");
8063f1
-          workspace->screen->display->mouse_mode = FALSE;
8063f1
+          meta_display_disable_mouse_mode (display);
8063f1
           meta_workspace_activate_with_focus (workspace,
8063f1
                                               window,
8063f1
                                               event->xkey.time);
8063f1
-- 
8063f1
1.7.9
8063f1