diff --git a/src/core/events.c b/src/core/events.c index 1ae1e933e..23763f9e5 100644 --- a/src/core/events.c +++ b/src/core/events.c @@ -31,6 +31,7 @@ #include "core/display-private.h" #include "core/keybindings-private.h" #include "core/window-private.h" +#include "core/zoom-grab.h" #include "meta/meta-backend.h" #ifdef HAVE_NATIVE_BACKEND @@ -291,6 +292,10 @@ meta_display_handle_event (MetaDisplay *display, meta_display_a11y_zoom (display, FALSE); } + /* Hold an active pointer grab so subsequent scroll-valuator motion + * events in this gesture are captured and not delivered to clients. */ + meta_zoom_pointer_grab_ensure (display, clutter_event_get_time (event)); + bypass_wayland = bypass_clutter = TRUE; goto out; } diff --git a/src/core/keybindings-private.h b/src/core/keybindings-private.h index 0e3586869..6e964ef4d 100644 --- a/src/core/keybindings-private.h +++ b/src/core/keybindings-private.h @@ -126,6 +126,12 @@ typedef struct /* Alt+click button grabs */ ClutterModifierType window_grab_modifiers; ClutterModifierType mouse_zoom_modifiers; + + /* Active pointer grab held during a11y mouse-wheel zoom, so that + * scroll-valuator motion events are not delivered to client windows + * (passive button-4/5 grabs cannot intercept XI_Motion scroll events). */ + gboolean zoom_pointer_grab_active; + guint zoom_pointer_grab_timeout_id; } MetaKeyBindingManager; void meta_display_init_keys (MetaDisplay *display); diff --git a/src/core/keybindings.c b/src/core/keybindings.c index 2c610d5f1..d91729399 100644 --- a/src/core/keybindings.c +++ b/src/core/keybindings.c @@ -37,6 +37,7 @@ #include "core/edge-resistance.h" #include "core/frame.h" #include "core/keybindings-private.h" +#include "core/zoom-grab.h" #include "core/meta-accel-parse.h" #include "core/meta-workspace-manager-private.h" #include "core/workspace-private.h" @@ -1500,6 +1501,8 @@ meta_display_shutdown_keys (MetaDisplay *display) meta_prefs_remove_listener (prefs_changed_callback, display); + meta_zoom_pointer_grab_release_all (display); + g_hash_table_destroy (keys->key_bindings_index); g_hash_table_destroy (keys->key_bindings); diff --git a/src/core/zoom-grab.c b/src/core/zoom-grab.c new file mode 100644 index 000000000..1dcc7b60e --- /dev/null +++ b/src/core/zoom-grab.c @@ -0,0 +1,83 @@ +/* + * Zoom pointer grab. + * + * Cinnamon's magnifier (Accessibility > Zoom) can be driven by holding a + * configurable modifier and turning the mouse wheel. On X11, scroll-valuator + * motion events are delivered directly to the client window under the pointer + * by the X server, so the magnified application keeps scrolling while the user + * is zooming. The passive button-4/5 grab in keybindings.c cannot intercept + * XI_Motion scroll events, so we hold a short-lived active pointer grab for + * the duration of the zoom gesture; an active grab captures all pointer + * events, including scroll-valuator motion. The grab is released shortly after + * the last zoom-scroll event so normal pointer interaction resumes promptly. + */ + +#include "config.h" +#include "core/zoom-grab.h" +#include "core/keybindings-private.h" +#include "backends/meta-backend-private.h" +#include "meta/meta-backend.h" + +#define ZOOM_GRAB_IDLE_MS 350 + +static gboolean +zoom_pointer_grab_release (gpointer user_data) +{ + MetaDisplay *display = META_DISPLAY (user_data); + MetaKeyBindingManager *keys = &display->key_binding_manager; + + if (keys->zoom_pointer_grab_active) + { + meta_backend_ungrab_device (meta_get_backend (), + META_VIRTUAL_CORE_POINTER_ID, + CLUTTER_CURRENT_TIME); + keys->zoom_pointer_grab_active = FALSE; + } + + keys->zoom_pointer_grab_timeout_id = 0; + return G_SOURCE_REMOVE; +} + +void +meta_zoom_pointer_grab_ensure (MetaDisplay *display, + uint32_t timestamp) +{ + MetaKeyBindingManager *keys = &display->key_binding_manager; + + if (meta_is_wayland_compositor ()) + return; + + if (!keys->zoom_pointer_grab_active) + { + keys->zoom_pointer_grab_active = + meta_backend_grab_device (meta_get_backend (), + META_VIRTUAL_CORE_POINTER_ID, + timestamp); + } + + if (keys->zoom_pointer_grab_timeout_id) + g_source_remove (keys->zoom_pointer_grab_timeout_id); + keys->zoom_pointer_grab_timeout_id = + g_timeout_add_full (G_PRIORITY_DEFAULT, ZOOM_GRAB_IDLE_MS, + zoom_pointer_grab_release, display, NULL); +} + +void +meta_zoom_pointer_grab_release_all (MetaDisplay *display) +{ + MetaKeyBindingManager *keys = &display->key_binding_manager; + + if (keys->zoom_pointer_grab_timeout_id) + { + g_source_remove (keys->zoom_pointer_grab_timeout_id); + keys->zoom_pointer_grab_timeout_id = 0; + } + + if (keys->zoom_pointer_grab_active) + { + meta_backend_ungrab_device (meta_get_backend (), + META_VIRTUAL_CORE_POINTER_ID, + CLUTTER_CURRENT_TIME); + keys->zoom_pointer_grab_active = FALSE; + } +} diff --git a/src/core/zoom-grab.h b/src/core/zoom-grab.h new file mode 100644 index 000000000..f28e64b85 --- /dev/null +++ b/src/core/zoom-grab.h @@ -0,0 +1,25 @@ +/* + * Zoom pointer grab. + * + * Cinnamon's magnifier (Accessibility > Zoom) can be driven by holding a + * configurable modifier and turning the mouse wheel. On X11, scroll-valuator + * motion events are delivered directly to the client window under the pointer + * by the X server, so the magnified application keeps scrolling while the user + * is zooming. The passive button-4/5 grab in keybindings.c cannot intercept + * XI_Motion scroll events, so we hold a short-lived active pointer grab for + * the duration of the zoom gesture; an active grab captures all pointer + * events, including scroll-valuator motion. The grab is released shortly after + * the last zoom-scroll event so normal pointer interaction resumes promptly. + */ + +#ifndef META_ZOOM_GRAB_H +#define META_ZOOM_GRAB_H + +#include "core/display-private.h" + +void meta_zoom_pointer_grab_ensure (MetaDisplay *display, + uint32_t timestamp); + +void meta_zoom_pointer_grab_release_all (MetaDisplay *display); + +#endif /* META_ZOOM_GRAB_H */ diff --git a/src/meson.build b/src/meson.build index 7508bfaa6..d2175a1eb 100644 --- a/src/meson.build +++ b/src/meson.build @@ -343,6 +343,8 @@ muffin_sources = [ 'core/meta-accel-parse.c', 'core/meta-accel-parse.h', 'core/meta-anonymous-file.c', + 'core/zoom-grab.c', + 'core/zoom-grab.h', 'core/meta-anonymous-file.h', 'core/meta-border.c', 'core/meta-border.h',