mirror of
https://github.com/brl/mutter.git
synced 2024-11-28 11:00:54 -05:00
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
This commit is contained in:
parent
99cbe762d7
commit
59bc5b7975
@ -172,6 +172,9 @@ struct _MetaDisplay
|
|||||||
/* Pings which we're waiting for a reply from */
|
/* Pings which we're waiting for a reply from */
|
||||||
GSList *pending_pings;
|
GSList *pending_pings;
|
||||||
|
|
||||||
|
/* Pending focus change */
|
||||||
|
guint focus_timeout_id;
|
||||||
|
|
||||||
/* Pending autoraise */
|
/* Pending autoraise */
|
||||||
guint autoraise_timeout_id;
|
guint autoraise_timeout_id;
|
||||||
MetaWindow* autoraise_window;
|
MetaWindow* autoraise_window;
|
||||||
|
@ -50,6 +50,7 @@
|
|||||||
#include "workspace-private.h"
|
#include "workspace-private.h"
|
||||||
#include "bell.h"
|
#include "bell.h"
|
||||||
#include <meta/compositor.h>
|
#include <meta/compositor.h>
|
||||||
|
#include <meta/compositor-mutter.h>
|
||||||
#include <X11/Xatom.h>
|
#include <X11/Xatom.h>
|
||||||
#include <X11/cursorfont.h>
|
#include <X11/cursorfont.h>
|
||||||
#include "mutter-enum-types.h"
|
#include "mutter-enum-types.h"
|
||||||
@ -122,6 +123,15 @@ typedef struct
|
|||||||
Window xwindow;
|
Window xwindow;
|
||||||
} MetaAutoRaiseData;
|
} MetaAutoRaiseData;
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
MetaDisplay *display;
|
||||||
|
MetaWindow *window;
|
||||||
|
int pointer_x;
|
||||||
|
int pointer_y;
|
||||||
|
} MetaFocusData;
|
||||||
|
|
||||||
|
|
||||||
G_DEFINE_TYPE(MetaDisplay, meta_display, G_TYPE_OBJECT);
|
G_DEFINE_TYPE(MetaDisplay, meta_display, G_TYPE_OBJECT);
|
||||||
|
|
||||||
/* Signals */
|
/* Signals */
|
||||||
@ -1039,6 +1049,10 @@ meta_display_close (MetaDisplay *display,
|
|||||||
|
|
||||||
meta_display_remove_autoraise_callback (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)
|
if (display->grab_old_window_stacking)
|
||||||
g_list_free (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;
|
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
|
void
|
||||||
meta_display_queue_autoraise_callback (MetaDisplay *display,
|
meta_display_queue_autoraise_callback (MetaDisplay *display,
|
||||||
MetaWindow *window)
|
MetaWindow *window)
|
||||||
@ -1596,6 +1707,37 @@ meta_display_queue_autoraise_callback (MetaDisplay *display,
|
|||||||
display->autoraise_window = window;
|
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
|
#if 0
|
||||||
static void
|
static void
|
||||||
handle_net_restack_window (MetaDisplay* display,
|
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_SLOPPY:
|
||||||
case G_DESKTOP_FOCUS_MODE_MOUSE:
|
case G_DESKTOP_FOCUS_MODE_MOUSE:
|
||||||
display->mouse_mode = TRUE;
|
display->mouse_mode = TRUE;
|
||||||
if (window->type != META_WINDOW_DOCK &&
|
if (window->type != META_WINDOW_DOCK)
|
||||||
window->type != META_WINDOW_DESKTOP)
|
|
||||||
{
|
{
|
||||||
meta_topic (META_DEBUG_FOCUS,
|
meta_topic (META_DEBUG_FOCUS,
|
||||||
"Focusing %s due to enter notify with serial %lu "
|
"Queuing a focus change for %s due to "
|
||||||
"at time %lu, and setting display->mouse_mode to "
|
"enter notify with serial %lu at time %lu, "
|
||||||
"TRUE.\n",
|
"and setting display->mouse_mode to TRUE.\n",
|
||||||
window->desc,
|
window->desc,
|
||||||
event->xany.serial,
|
event->xany.serial,
|
||||||
event->xcrossing.time);
|
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 */
|
/* stop ignoring stuff */
|
||||||
reset_ignored_crossing_serials (display);
|
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;
|
break;
|
||||||
case G_DESKTOP_FOCUS_MODE_CLICK:
|
case G_DESKTOP_FOCUS_MODE_CLICK:
|
||||||
|
@ -87,6 +87,7 @@ static gboolean application_based = FALSE;
|
|||||||
static gboolean disable_workarounds = FALSE;
|
static gboolean disable_workarounds = FALSE;
|
||||||
static gboolean auto_raise = FALSE;
|
static gboolean auto_raise = FALSE;
|
||||||
static gboolean auto_raise_delay = 500;
|
static gboolean auto_raise_delay = 500;
|
||||||
|
static gboolean focus_change_on_pointer_rest = FALSE;
|
||||||
static gboolean bell_is_visible = FALSE;
|
static gboolean bell_is_visible = FALSE;
|
||||||
static gboolean bell_is_audible = TRUE;
|
static gboolean bell_is_audible = TRUE;
|
||||||
static gboolean gnome_accessibility = FALSE;
|
static gboolean gnome_accessibility = FALSE;
|
||||||
@ -304,6 +305,13 @@ static MetaBoolPreference preferences_bool[] =
|
|||||||
},
|
},
|
||||||
&auto_raise,
|
&auto_raise,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
{ "focus-change-on-pointer-rest",
|
||||||
|
SCHEMA_MUTTER,
|
||||||
|
META_PREF_FOCUS_CHANGE_ON_POINTER_REST,
|
||||||
|
},
|
||||||
|
&focus_change_on_pointer_rest
|
||||||
|
},
|
||||||
{
|
{
|
||||||
{ "visual-bell",
|
{ "visual-bell",
|
||||||
SCHEMA_GENERAL,
|
SCHEMA_GENERAL,
|
||||||
@ -1608,6 +1616,9 @@ meta_preference_to_string (MetaPreference pref)
|
|||||||
case META_PREF_AUTO_RAISE_DELAY:
|
case META_PREF_AUTO_RAISE_DELAY:
|
||||||
return "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:
|
case META_PREF_BUTTON_LAYOUT:
|
||||||
return "BUTTON_LAYOUT";
|
return "BUTTON_LAYOUT";
|
||||||
|
|
||||||
@ -2046,6 +2057,12 @@ meta_prefs_get_auto_raise_delay (void)
|
|||||||
return auto_raise_delay;
|
return auto_raise_delay;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
meta_prefs_get_focus_change_on_pointer_rest ()
|
||||||
|
{
|
||||||
|
return focus_change_on_pointer_rest;
|
||||||
|
}
|
||||||
|
|
||||||
gboolean
|
gboolean
|
||||||
meta_prefs_get_gnome_accessibility ()
|
meta_prefs_get_gnome_accessibility ()
|
||||||
{
|
{
|
||||||
|
@ -45,6 +45,7 @@ typedef enum
|
|||||||
META_PREF_ACTION_RIGHT_CLICK_TITLEBAR,
|
META_PREF_ACTION_RIGHT_CLICK_TITLEBAR,
|
||||||
META_PREF_AUTO_RAISE,
|
META_PREF_AUTO_RAISE,
|
||||||
META_PREF_AUTO_RAISE_DELAY,
|
META_PREF_AUTO_RAISE_DELAY,
|
||||||
|
META_PREF_FOCUS_CHANGE_ON_POINTER_REST,
|
||||||
META_PREF_THEME,
|
META_PREF_THEME,
|
||||||
META_PREF_TITLEBAR_FONT,
|
META_PREF_TITLEBAR_FONT,
|
||||||
META_PREF_NUM_WORKSPACES,
|
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_disable_workarounds (void);
|
||||||
gboolean meta_prefs_get_auto_raise (void);
|
gboolean meta_prefs_get_auto_raise (void);
|
||||||
int meta_prefs_get_auto_raise_delay (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_accessibility (void);
|
||||||
gboolean meta_prefs_get_gnome_animations (void);
|
gboolean meta_prefs_get_gnome_animations (void);
|
||||||
gboolean meta_prefs_get_edge_tiling (void);
|
gboolean meta_prefs_get_edge_tiling (void);
|
||||||
|
@ -63,6 +63,16 @@
|
|||||||
</_description>
|
</_description>
|
||||||
</key>
|
</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">
|
<key name="draggable-border-width" type="i">
|
||||||
<default>10</default>
|
<default>10</default>
|
||||||
<range min="0" max="64"/>
|
<range min="0" max="64"/>
|
||||||
|
Loading…
Reference in New Issue
Block a user