summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFlorian Müllner <fmuellner@gnome.org>2012-08-29 04:38:54 +0200
committerFlorian Müllner <fmuellner@gnome.org>2012-10-11 07:49:21 +0200
commit59bc5b7975f1f19ebacb520c1c2666c0828d1111 (patch)
treee79e1339ff719ef6f8debc4c8b1e2349be64894a
parent99cbe762d7a23cd5f4cf44f0b7faf5aacb6a6b6d (diff)
downloadmutter-59bc5b7975f1f19ebacb520c1c2666c0828d1111.tar.gz
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
-rw-r--r--src/core/display-private.h3
-rw-r--r--src/core/display.c192
-rw-r--r--src/core/prefs.c17
-rw-r--r--src/meta/prefs.h2
-rw-r--r--src/org.gnome.mutter.gschema.xml.in10
5 files changed, 186 insertions, 38 deletions
diff --git a/src/core/display-private.h b/src/core/display-private.h
index 6db1b6461..b03fffd69 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 f62fad1d0..19c343700 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 f67b2831b..802d620ea 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 bbbe76868..ff6984cc2 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 e23ad8175..a2b9eec0b 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"/>