From 23ba3e527fdb454d3aca2552f2e252a8ca814609 Mon Sep 17 00:00:00 2001 From: "Jasper St. Pierre" Date: Wed, 16 Oct 2013 00:06:10 -0400 Subject: [PATCH] wayland: Support alternative focus modes like focus-follows-mouse Use the existing code for MetaWindow focus-follows-mouse to support this. --- src/core/display.c | 193 +---------------------------- src/core/window-private.h | 5 + src/core/window.c | 182 +++++++++++++++++++++++++++ src/wayland/meta-wayland-pointer.c | 5 + 4 files changed, 196 insertions(+), 189 deletions(-) diff --git a/src/core/display.c b/src/core/display.c index c7e86b228..4de870ad4 100644 --- a/src/core/display.c +++ b/src/core/display.c @@ -117,15 +117,6 @@ typedef struct guint ping_timeout_id; } MetaPingData; -typedef struct -{ - MetaDisplay *display; - MetaWindow *window; - int pointer_x; - int pointer_y; -} MetaFocusData; - - G_DEFINE_TYPE(MetaDisplay, meta_display, G_TYPE_OBJECT); /* Signals */ @@ -1596,21 +1587,6 @@ crossing_serial_is_ignored (MetaDisplay *display, 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 window_raise_with_delay_callback (void *data) { @@ -1651,110 +1627,6 @@ 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->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 meta_display_queue_autoraise_callback (MetaDisplay *display, MetaWindow *window) @@ -1775,37 +1647,6 @@ 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, @@ -2502,36 +2343,10 @@ handle_input_xevent (MetaDisplay *display, enter_event->detail != XINotifyInferior && meta_display_focus_sentinel_clear (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) - { - 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, - 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_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; - } + meta_window_handle_enter (window, + enter_event->time, + enter_event->root_x, + enter_event->root_y); if (window->type == META_WINDOW_DOCK) meta_window_raise (window); diff --git a/src/core/window-private.h b/src/core/window-private.h index 8c125cdac..1a428030c 100644 --- a/src/core/window-private.h +++ b/src/core/window-private.h @@ -734,4 +734,9 @@ void meta_window_set_gtk_dbus_properties (MetaWindow *window, void meta_window_set_transient_for (MetaWindow *window, MetaWindow *parent); +void meta_window_handle_enter (MetaWindow *window, + guint32 timestamp, + guint root_x, + guint root_y); + #endif diff --git a/src/core/window.c b/src/core/window.c index 09a8f833c..bef47bec8 100644 --- a/src/core/window.c +++ b/src/core/window.c @@ -11667,3 +11667,185 @@ meta_window_set_transient_for (MetaWindow *window, if (meta_window_appears_focused (window) && window->transient_for != None) meta_window_propagate_focus_appearance (window, TRUE); } + +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 (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; + } +} diff --git a/src/wayland/meta-wayland-pointer.c b/src/wayland/meta-wayland-pointer.c index 1f6962a5f..2a44ac06a 100644 --- a/src/wayland/meta-wayland-pointer.c +++ b/src/wayland/meta-wayland-pointer.c @@ -353,6 +353,11 @@ meta_wayland_pointer_set_focus (MetaWaylandPointer *pointer, } meta_wayland_pointer_get_relative_coordinates (pointer, surface, &sx, &sy); + meta_window_handle_enter (surface->window, + /* XXX -- can we reliably get a timestamp for setting focus? */ + clutter_get_current_event_time (), + wl_fixed_to_int (pointer->x), + wl_fixed_to_int (pointer->y)); wl_pointer_send_enter (resource, serial, surface->resource, sx, sy); wl_resource_add_destroy_listener (resource, &pointer->focus_listener); pointer->focus_serial = serial;