Compare commits

...

3 Commits

Author SHA1 Message Date
Jasper St. Pierre
48b74b86ec window: Separate pointer-focus operations and explicit-focus operations
Some clients, like on-screen keyboards, don't want their windows to be
activated on click, as that would steal focus from the window they're
trying to send events to. They do this by setting the Input Hint in
WM_HINTS to be FALSE, along with not specifying WM_TAKE_FOCUS in their
WM_PROTOCOLS.

However, in this case, when a window tries to get focus in this scenario,
we focus the frame so that a11y and key navigation works properly.
Both policies aren't acceptable -- we can't make OSK steal focus from
windows on click, and we can't make an OSK un-Alt-Tabbable-to.

To solve this, split meta_window_focus() into two different focus policies:

 * meta_window_focus_implicitly() should be called on pointer click or
   focus-follows-mouse or similar cases where we may not want to forcibly
   set focus for clients that don't want it.

 * meta_window_focus_explicitly() should be called by pagers, like
   the gnome-shell overview, or Alt-Tab. In this case, most of the existing
   clients are using meta_window_activate(), so simply adapting that so
   it calls meta_window_focus_explicitly() should be enough.

https://bugzilla.gnome.org/show_bug.cgi?id=715030
2013-11-25 15:08:22 -05:00
Jasper St. Pierre
91c2225ecd window: Reword a condition to make it easier to understand
https://bugzilla.gnome.org/show_bug.cgi?id=715030
2013-11-25 15:08:22 -05:00
Jasper St. Pierre
2aa24f2da8 Move autoraise callbacks into window.c
This matches what the wayland branch does, which allows us to share
code more easily.

https://bugzilla.gnome.org/show_bug.cgi?id=715030
2013-11-25 15:08:22 -05:00
8 changed files with 254 additions and 212 deletions

View File

@@ -303,7 +303,7 @@ meta_core_user_focus (Display *xdisplay,
{ {
MetaWindow *window = get_window (xdisplay, frame_xwindow); MetaWindow *window = get_window (xdisplay, frame_xwindow);
meta_window_focus (window, timestamp); meta_window_focus_implicitly (window, timestamp);
} }
void void
@@ -482,7 +482,7 @@ meta_core_show_window_menu (Display *xdisplay,
if (meta_prefs_get_raise_on_click ()) if (meta_prefs_get_raise_on_click ())
meta_window_raise (window); meta_window_raise (window);
meta_window_focus (window, timestamp); meta_window_focus_implicitly (window, timestamp);
meta_window_show_menu (window, root_x, root_y, button, timestamp); meta_window_show_menu (window, root_x, root_y, button, timestamp);
} }

View File

@@ -117,15 +117,6 @@ typedef struct
guint ping_timeout_id; guint ping_timeout_id;
} MetaPingData; } MetaPingData;
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 */
@@ -1561,21 +1552,6 @@ crossing_serial_is_ignored (MetaDisplay *display,
return FALSE; return FALSE;
} }
static void
reset_ignored_crossing_serials (MetaDisplay *display)
{
int i;
i = 0;
while (i < N_IGNORED_CROSSING_SERIALS)
{
display->ignored_crossing_serials[i] = 0;
++i;
}
display->ungrab_should_not_cause_focus_window = None;
}
static gboolean static gboolean
window_raise_with_delay_callback (void *data) window_raise_with_delay_callback (void *data)
{ {
@@ -1616,110 +1592,6 @@ 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->focus_window != NULL)
{
meta_topic (META_DEBUG_FOCUS,
"Unsetting focus from %s due to mouse entering "
"the DESKTOP window\n",
display->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;
double root_x, root_y, x, y;
guint32 timestamp;
XIButtonState buttons;
XIModifierState mods;
XIGroupState group;
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);
XIQueryPointer (display->xdisplay,
META_VIRTUAL_CORE_POINTER_ID,
screen->xroot,
&root, &child,
&root_x, &root_y, &x, &y,
&buttons, &mods, &group);
meta_error_trap_pop (display);
free (buttons.mask);
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)
@@ -1740,37 +1612,6 @@ 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,
@@ -2403,7 +2244,7 @@ event_callback (XEvent *event,
meta_topic (META_DEBUG_FOCUS, meta_topic (META_DEBUG_FOCUS,
"Focusing %s due to unmodified button %u press (display.c)\n", "Focusing %s due to unmodified button %u press (display.c)\n",
window->desc, device_event->detail); window->desc, device_event->detail);
meta_window_focus (window, device_event->time); meta_window_focus_implicitly (window, device_event->time);
} }
else else
/* However, do allow terminals to lose focus due to new /* However, do allow terminals to lose focus due to new
@@ -2552,36 +2393,10 @@ event_callback (XEvent *event,
enter_event->detail != XINotifyInferior && enter_event->detail != XINotifyInferior &&
meta_display_focus_sentinel_clear (display)) meta_display_focus_sentinel_clear (display))
{ {
switch (meta_prefs_get_focus_mode ()) meta_window_handle_enter (window,
{ enter_event->time,
case G_DESKTOP_FOCUS_MODE_SLOPPY:
case G_DESKTOP_FOCUS_MODE_MOUSE:
display->mouse_mode = TRUE;
if (window->type != META_WINDOW_DOCK)
{
meta_topic (META_DEBUG_FOCUS,
"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,
enter_event->time);
if (meta_prefs_get_focus_change_on_pointer_rest())
meta_display_queue_focus_callback (display, window,
enter_event->root_x, enter_event->root_x,
enter_event->root_y); enter_event->root_y);
else
meta_display_mouse_mode_focus (display, window,
enter_event->time);
/* stop ignoring stuff */
reset_ignored_crossing_serials (display);
}
break;
case G_DESKTOP_FOCUS_MODE_CLICK:
break;
}
if (window->type == META_WINDOW_DOCK) if (window->type == META_WINDOW_DOCK)
meta_window_raise (window); meta_window_raise (window);

View File

@@ -1578,7 +1578,7 @@ meta_window_grab_all_keys (MetaWindow *window,
meta_topic (META_DEBUG_FOCUS, meta_topic (META_DEBUG_FOCUS,
"Focusing %s because we're grabbing all its keys\n", "Focusing %s because we're grabbing all its keys\n",
window->desc); window->desc);
meta_window_focus (window, timestamp); meta_window_focus_implicitly (window, timestamp);
grabwindow = window->frame ? window->frame->xwindow : window->xwindow; grabwindow = window->frame ? window->frame->xwindow : window->xwindow;

View File

@@ -2984,7 +2984,7 @@ meta_screen_show_desktop (MetaScreen *screen,
if (w->screen == screen && if (w->screen == screen &&
w->type == META_WINDOW_DESKTOP) w->type == META_WINDOW_DESKTOP)
{ {
meta_window_focus (w, timestamp); meta_window_focus_explicitly (w, timestamp);
break; break;
} }

View File

@@ -696,4 +696,12 @@ void meta_window_set_shape_region (MetaWindow *window,
cairo_region_t *region); cairo_region_t *region);
void meta_window_update_shape_region_x11 (MetaWindow *window); void meta_window_update_shape_region_x11 (MetaWindow *window);
void meta_window_handle_enter (MetaWindow *window,
guint32 timestamp,
guint root_x,
guint root_y);
void meta_window_focus_implicitly (MetaWindow *window,
guint32 timestamp);
#endif #endif

View File

@@ -49,6 +49,7 @@
#include "constraints.h" #include "constraints.h"
#include "mutter-enum-types.h" #include "mutter-enum-types.h"
#include "core.h" #include "core.h"
#include <meta/compositor-mutter.h>
#include <X11/Xatom.h> #include <X11/Xatom.h>
#include <X11/Xlibint.h> /* For display->resource_mask */ #include <X11/Xlibint.h> /* For display->resource_mask */
@@ -3182,7 +3183,7 @@ meta_window_show (MetaWindow *window)
timestamp = meta_display_get_current_time_roundtrip (window->display); timestamp = meta_display_get_current_time_roundtrip (window->display);
meta_window_focus (window, timestamp); meta_window_focus_explicitly (window, timestamp);
} }
else else
{ {
@@ -4224,7 +4225,7 @@ meta_window_shade (MetaWindow *window,
meta_topic (META_DEBUG_FOCUS, meta_topic (META_DEBUG_FOCUS,
"Re-focusing window %s after shading it\n", "Re-focusing window %s after shading it\n",
window->desc); window->desc);
meta_window_focus (window, timestamp); meta_window_focus_explicitly (window, timestamp);
set_net_wm_state (window); set_net_wm_state (window);
} }
@@ -4248,7 +4249,7 @@ meta_window_unshade (MetaWindow *window,
meta_topic (META_DEBUG_FOCUS, meta_topic (META_DEBUG_FOCUS,
"Focusing window %s after unshading it\n", "Focusing window %s after unshading it\n",
window->desc); window->desc);
meta_window_focus (window, timestamp); meta_window_focus_explicitly (window, timestamp);
set_net_wm_state (window); set_net_wm_state (window);
} }
@@ -4347,7 +4348,7 @@ window_activate (MetaWindow *window,
meta_topic (META_DEBUG_FOCUS, meta_topic (META_DEBUG_FOCUS,
"Focusing window %s due to activation\n", "Focusing window %s due to activation\n",
window->desc); window->desc);
meta_window_focus (window, timestamp); meta_window_focus_explicitly (window, timestamp);
meta_window_check_alive (window, timestamp); meta_window_check_alive (window, timestamp);
} }
@@ -5971,17 +5972,18 @@ get_modal_transient (MetaWindow *window)
} }
/* XXX META_EFFECT_FOCUS */ /* XXX META_EFFECT_FOCUS */
void static void
meta_window_focus (MetaWindow *window, meta_window_focus_internal (MetaWindow *window,
guint32 timestamp) guint32 timestamp,
gboolean explicit_focus)
{ {
MetaWindow *modal_transient; MetaWindow *modal_transient;
g_return_if_fail (!window->override_redirect); g_return_if_fail (!window->override_redirect);
meta_topic (META_DEBUG_FOCUS, meta_topic (META_DEBUG_FOCUS,
"Setting input focus to window %s, input: %d take_focus: %d\n", "%s setting input focus to window %s, input: %d take_focus: %d\n",
window->desc, window->input, window->take_focus); explicit_focus ? "Explicitly" : "Implicitly", window->desc, window->input, window->take_focus);
if (window->display->grab_window && if (window->display->grab_window &&
window->display->grab_window->all_keys_grabbed) window->display->grab_window->all_keys_grabbed)
@@ -6016,15 +6018,16 @@ meta_window_focus (MetaWindow *window,
return; return;
} }
/* For output-only or shaded windows, focus the frame. /* For output-only or shaded windows, focus the frame if it was
* This seems to result in the client window getting key events * an explicit focus event. This seems to result in the client
* though, so I don't know if it's icccm-compliant. * window getting key events though, so I don't know if it's
* icccm-compliant.
* *
* Still, we have to do this or keynav breaks for these windows. * Still, we have to do this or keynav breaks for these windows.
*/ */
if (window->frame && if (window->frame &&
(window->shaded || (window->shaded ||
!(window->input || window->take_focus))) (explicit_focus && !window->input && !window->take_focus)))
{ {
if (window->frame) if (window->frame)
{ {
@@ -6067,6 +6070,40 @@ meta_window_focus (MetaWindow *window,
/* meta_effect_run_focus(window, NULL, NULL); */ /* meta_effect_run_focus(window, NULL, NULL); */
} }
/**
* meta_window_focus_explicitly:
* @window: A #MetaWindow
* @timestamp: The timestamp to focus the window with
*
* Explicitly grab the window's focus. This should be used in cases
* where the user wants to focus the window with Alt-Tab, pagers,
* or other means like that, as opposed to simply clicking on the
* window or other focus-modes like focus-follows-mouse.
*/
void
meta_window_focus_explicitly (MetaWindow *window,
guint32 timestamp)
{
meta_window_focus_internal (window, timestamp, TRUE);
}
/**
* meta_window_focus_implicitly:
* @window: A #MetaWindow
* @timestamp: The timestamp to focus the window with
*
* Implicitly focus the window. This should be done if the user
* simply clicks on the window. Most of the time, this should be
* handled by mutter internally, and you should never have to
* call this.
*/
void
meta_window_focus_implicitly (MetaWindow *window,
guint32 timestamp)
{
meta_window_focus_internal (window, timestamp, FALSE);
}
static void static void
meta_window_change_workspace_without_transients (MetaWindow *window, meta_window_change_workspace_without_transients (MetaWindow *window,
MetaWorkspace *workspace) MetaWorkspace *workspace)
@@ -11432,3 +11469,185 @@ meta_window_can_close (MetaWindow *window)
{ {
return window->has_close_func; return window->has_close_func;
} }
static void
reset_ignored_crossing_serials (MetaDisplay *display)
{
int i;
i = 0;
while (i < N_IGNORED_CROSSING_SERIALS)
{
display->ignored_crossing_serials[i] = 0;
++i;
}
display->ungrab_should_not_cause_focus_window = None;
}
typedef struct
{
MetaWindow *window;
int pointer_x;
int pointer_y;
} MetaFocusData;
static void
mouse_mode_focus (MetaWindow *window,
guint32 timestamp)
{
MetaDisplay *display = window->display;
if (window->type != META_WINDOW_DESKTOP)
{
meta_topic (META_DEBUG_FOCUS,
"Focusing %s at time %u.\n", window->desc, timestamp);
meta_window_focus_implicitly (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->focus_window != NULL)
{
meta_topic (META_DEBUG_FOCUS,
"Unsetting focus from %s due to mouse entering "
"the DESKTOP window\n",
display->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 = data;
MetaWindow *window = focus_data->window;
MetaDisplay *display = window->display;
MetaScreen *screen = window->screen;
Window root, child;
double root_x, root_y, x, y;
guint32 timestamp;
XIButtonState buttons;
XIModifierState mods;
XIGroupState group;
if (meta_prefs_get_focus_mode () == G_DESKTOP_FOCUS_MODE_CLICK)
goto out;
meta_error_trap_push (display);
XIQueryPointer (display->xdisplay,
META_VIRTUAL_CORE_POINTER_ID,
screen->xroot,
&root, &child,
&root_x, &root_y, &x, &y,
&buttons, &mods, &group);
meta_error_trap_pop (display);
free (buttons.mask);
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);
mouse_mode_focus (window, timestamp);
out:
display->focus_timeout_id = 0;
return FALSE;
}
/* 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
queue_focus_callback (MetaDisplay *display,
MetaWindow *window,
int pointer_x,
int pointer_y)
{
MetaFocusData *focus_data;
focus_data = g_new (MetaFocusData, 1);
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);
}
void
meta_window_handle_enter (MetaWindow *window,
guint32 timestamp,
guint root_x,
guint root_y)
{
MetaDisplay *display = window->display;
switch (meta_prefs_get_focus_mode ())
{
case G_DESKTOP_FOCUS_MODE_SLOPPY:
case G_DESKTOP_FOCUS_MODE_MOUSE:
display->mouse_mode = TRUE;
if (window->type != META_WINDOW_DOCK)
{
if (meta_prefs_get_focus_change_on_pointer_rest())
queue_focus_callback (display, window, root_x, root_y);
else
mouse_mode_focus (window, timestamp);
/* stop ignoring stuff */
reset_ignored_crossing_serials (display);
}
break;
case G_DESKTOP_FOCUS_MODE_CLICK:
break;
}
}

View File

@@ -1256,7 +1256,7 @@ meta_workspace_focus_default_window (MetaWorkspace *workspace,
{ {
meta_topic (META_DEBUG_FOCUS, meta_topic (META_DEBUG_FOCUS,
"Focusing mouse window %s\n", window->desc); "Focusing mouse window %s\n", window->desc);
meta_window_focus (window, timestamp); meta_window_focus_explicitly (window, timestamp);
} }
if (workspace->screen->display->autoraise_window != window && if (workspace->screen->display->autoraise_window != window &&
@@ -1319,7 +1319,7 @@ focus_ancestor_or_top_window (MetaWorkspace *workspace,
"Focusing %s, ancestor of %s\n", "Focusing %s, ancestor of %s\n",
ancestor->desc, not_this_one->desc); ancestor->desc, not_this_one->desc);
meta_window_focus (ancestor, timestamp); meta_window_focus_explicitly (ancestor, timestamp);
/* Also raise the window if in click-to-focus */ /* Also raise the window if in click-to-focus */
if (meta_prefs_get_focus_mode () == G_DESKTOP_FOCUS_MODE_CLICK) if (meta_prefs_get_focus_mode () == G_DESKTOP_FOCUS_MODE_CLICK)
@@ -1338,7 +1338,7 @@ focus_ancestor_or_top_window (MetaWorkspace *workspace,
meta_topic (META_DEBUG_FOCUS, meta_topic (META_DEBUG_FOCUS,
"Focusing workspace MRU window %s\n", window->desc); "Focusing workspace MRU window %s\n", window->desc);
meta_window_focus (window, timestamp); meta_window_focus_explicitly (window, timestamp);
/* Also raise the window if in click-to-focus */ /* Also raise the window if in click-to-focus */
if (meta_prefs_get_focus_mode () == G_DESKTOP_FOCUS_MODE_CLICK) if (meta_prefs_get_focus_mode () == G_DESKTOP_FOCUS_MODE_CLICK)

View File

@@ -227,7 +227,7 @@ void meta_window_stick (MetaWindow *window);
void meta_window_unstick (MetaWindow *window); void meta_window_unstick (MetaWindow *window);
void meta_window_kill (MetaWindow *window); void meta_window_kill (MetaWindow *window);
void meta_window_focus (MetaWindow *window, void meta_window_focus_explicitly (MetaWindow *window,
guint32 timestamp); guint32 timestamp);
void meta_window_check_alive (MetaWindow *window, void meta_window_check_alive (MetaWindow *window,