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
5 changed files with 186 additions and 38 deletions
|
@ -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;
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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;
|
||||
|
@ -304,6 +305,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,
|
||||
|
@ -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";
|
||||
|
||||
|
@ -2046,6 +2057,12 @@ meta_prefs_get_auto_raise_delay (void)
|
|||
return auto_raise_delay;
|
||||
}
|
||||
|
||||
gboolean
|
||||
meta_prefs_get_focus_change_on_pointer_rest ()
|
||||
{
|
||||
return focus_change_on_pointer_rest;
|
||||
}
|
||||
|
||||
gboolean
|
||||
meta_prefs_get_gnome_accessibility ()
|
||||
{
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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"/>
|
||||
|
|
Loading…
Reference in a new issue