From 9b51e9510e2906d56667fcc9d49238c80ba79bdb Mon Sep 17 00:00:00 2001 From: Aleksey Samoilov Date: Mon, 9 Feb 2026 03:00:33 +0400 Subject: [PATCH] [Wayland] Add support for pointer-warp-v1 protocol Only allow this while an implicit grab is held, so that it is not assumed to be a generic warping mechanism. --- src/meson.build | 3 + src/wayland/meta-wayland-pointer-warp.c | 135 +++++++++++++++++++++++ src/wayland/meta-wayland-pointer-warp.h | 33 ++++++ src/wayland/meta-wayland-pointer.c | 26 +++++ src/wayland/meta-wayland-pointer.h | 8 ++ src/wayland/meta-wayland-seat.c | 2 + src/wayland/meta-wayland-seat.h | 2 + src/wayland/meta-wayland-versions.h | 1 + src/wayland/protocol/pointer-warp-v1.xml | 72 ++++++++++++ 9 files changed, 282 insertions(+) create mode 100644 src/wayland/meta-wayland-pointer-warp.c create mode 100644 src/wayland/meta-wayland-pointer-warp.h create mode 100644 src/wayland/protocol/pointer-warp-v1.xml diff --git a/src/meson.build b/src/meson.build index e40d74c37..da2a9f96d 100644 --- a/src/meson.build +++ b/src/meson.build @@ -546,6 +546,8 @@ if have_wayland 'wayland/meta-wayland-pointer-gestures.h', 'wayland/meta-wayland-pointer-gesture-swipe.c', 'wayland/meta-wayland-pointer-gesture-swipe.h', + 'wayland/meta-wayland-pointer-warp.c', + 'wayland/meta-wayland-pointer-warp.h', 'wayland/meta-wayland-pointer.h', 'wayland/meta-wayland-popup.c', 'wayland/meta-wayland-popup.h', @@ -813,6 +815,7 @@ if have_wayland ['linux-dmabuf', 'unstable', 'v1', ], ['pointer-constraints', 'unstable', 'v1', ], ['pointer-gestures', 'unstable', 'v1', ], + ['pointer-warp-v1', 'private',], ['primary-selection', 'unstable', 'v1', ], ['relative-pointer', 'unstable', 'v1', ], ['tablet', 'unstable', 'v2', ], diff --git a/src/wayland/meta-wayland-pointer-warp.c b/src/wayland/meta-wayland-pointer-warp.c new file mode 100644 index 000000000..fca7c3a9f --- /dev/null +++ b/src/wayland/meta-wayland-pointer-warp.c @@ -0,0 +1,135 @@ +/* + * Wayland Support + * + * Copyright (C) 2025 Red Hat Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see . + * + * Author: Carlos Garnacho + */ + +#include "config.h" + +#include "meta-wayland-pointer-warp.h" + +#include "meta-wayland-private.h" +#include "meta-wayland-seat.h" +#include "meta-wayland-surface.h" + +#include "pointer-warp-v1-server-protocol.h" + +struct _MetaWaylandPointerWarp +{ + MetaWaylandSeat *seat; + struct wl_list resource_list; +}; + +static void +pointer_warp_destroy (struct wl_client *client, + struct wl_resource *resource) +{ + wl_resource_destroy (resource); +} + +static void +pointer_warp_perform (struct wl_client *client, + struct wl_resource *resource, + struct wl_resource *surface_resource, + struct wl_resource *pointer_resource, + wl_fixed_t x, + wl_fixed_t y, + uint32_t serial) +{ + ClutterSeat *seat; + MetaWaylandSurface *surface = wl_resource_get_user_data (surface_resource); + MetaWaylandPointer *pointer = wl_resource_get_user_data (pointer_resource); + MetaSurfaceActor *surface_actor = meta_wayland_surface_get_actor (surface); + graphene_point3d_t coords = + GRAPHENE_POINT3D_INIT ((float) wl_fixed_to_double (x), + (float) wl_fixed_to_double (y), + 0.0); + + /* Not focused and implicitly grabbed */ + if (!meta_wayland_pointer_get_grab_info (pointer, surface, serial, TRUE, + NULL, NULL, NULL)) + return; + + /* Outside of actor */ + if (!surface_actor || + x < 0 || x > clutter_actor_get_width (CLUTTER_ACTOR (surface_actor)) || + y < 0 || y > clutter_actor_get_height (CLUTTER_ACTOR (surface_actor))) + return; + + clutter_actor_apply_transform_to_point (CLUTTER_ACTOR (surface_actor), + &coords, &coords); + + seat = clutter_backend_get_default_seat (clutter_get_default_backend ()); + + clutter_seat_warp_pointer (seat, (int) coords.x, (int) coords.y); +} + +static struct wp_pointer_warp_v1_interface pointer_warp_interface = { + pointer_warp_destroy, + pointer_warp_perform, +}; + +static void +unbind_resource (struct wl_resource *resource) +{ + wl_list_remove (wl_resource_get_link (resource)); +} + +static void +bind_pointer_warp (struct wl_client *client, + void *data, + uint32_t version, + uint32_t id) +{ + MetaWaylandPointerWarp *pointer_warp = data; + struct wl_resource *resource; + + resource = wl_resource_create (client, &wp_pointer_warp_v1_interface, + MIN (version, META_WP_POINTER_WARP_VERSION), + id); + wl_resource_set_implementation (resource, &pointer_warp_interface, + pointer_warp, unbind_resource); + wl_resource_set_user_data (resource, pointer_warp); + wl_list_insert (&pointer_warp->resource_list, + wl_resource_get_link (resource)); +} + +MetaWaylandPointerWarp * +meta_wayland_pointer_warp_new (MetaWaylandSeat *seat) +{ + MetaWaylandCompositor *compositor = meta_wayland_compositor_get_default (); + MetaWaylandPointerWarp *pointer_warp; + + pointer_warp = g_new0 (MetaWaylandPointerWarp, 1); + pointer_warp->seat = seat; + wl_list_init (&pointer_warp->resource_list); + + wl_global_create (compositor->wayland_display, + &wp_pointer_warp_v1_interface, + META_WP_POINTER_WARP_VERSION, + pointer_warp, bind_pointer_warp); + + return pointer_warp; +} + +void +meta_wayland_pointer_warp_destroy (MetaWaylandPointerWarp *pointer_warp) +{ + wl_list_remove (&pointer_warp->resource_list); + g_free (pointer_warp); +} diff --git a/src/wayland/meta-wayland-pointer-warp.h b/src/wayland/meta-wayland-pointer-warp.h new file mode 100644 index 000000000..658638b34 --- /dev/null +++ b/src/wayland/meta-wayland-pointer-warp.h @@ -0,0 +1,33 @@ +/* + * Wayland Support + * + * Copyright (C) 2025 Red Hat Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see . + * + * Author: Carlos Garnacho + */ + +#pragma once + +#include +#include + +#include "wayland/meta-wayland-types.h" + +typedef struct _MetaWaylandPointerWarp MetaWaylandPointerWarp; + +MetaWaylandPointerWarp * meta_wayland_pointer_warp_new (MetaWaylandSeat *seat); + +void meta_wayland_pointer_warp_destroy (MetaWaylandPointerWarp *pointer_warp); diff --git a/src/wayland/meta-wayland-pointer.c b/src/wayland/meta-wayland-pointer.c index 9aef940cb..40b78992a 100644 --- a/src/wayland/meta-wayland-pointer.c +++ b/src/wayland/meta-wayland-pointer.c @@ -1289,6 +1289,32 @@ meta_wayland_pointer_can_grab_surface (MetaWaylandPointer *pointer, pointer_can_grab_surface (pointer, surface)); } +gboolean +meta_wayland_pointer_get_grab_info (MetaWaylandPointer *pointer, + MetaWaylandSurface *surface, + uint32_t serial, + gboolean require_pressed, + ClutterInputDevice **device_out, + float *x, + float *y) +{ + if ((!require_pressed || pointer->button_count > 0) && + meta_wayland_pointer_can_grab_surface (pointer, surface, serial)) + { + if (device_out) + *device_out = pointer->device; + + if (x) + *x = pointer->grab_x; + if (y) + *y = pointer->grab_y; + + return TRUE; + } + + return FALSE; +} + gboolean meta_wayland_pointer_can_popup (MetaWaylandPointer *pointer, uint32_t serial) { diff --git a/src/wayland/meta-wayland-pointer.h b/src/wayland/meta-wayland-pointer.h index 1b0cdecd1..7375aad0a 100644 --- a/src/wayland/meta-wayland-pointer.h +++ b/src/wayland/meta-wayland-pointer.h @@ -143,6 +143,14 @@ gboolean meta_wayland_pointer_can_grab_surface (MetaWaylandPointer *pointer, MetaWaylandSurface *surface, uint32_t serial); +gboolean meta_wayland_pointer_get_grab_info (MetaWaylandPointer *pointer, + MetaWaylandSurface *surface, + uint32_t serial, + gboolean require_pressed, + ClutterInputDevice **device_out, + float *x, + float *y); + gboolean meta_wayland_pointer_can_popup (MetaWaylandPointer *pointer, uint32_t serial); diff --git a/src/wayland/meta-wayland-seat.c b/src/wayland/meta-wayland-seat.c index 782c798d0..2d05f0478 100644 --- a/src/wayland/meta-wayland-seat.c +++ b/src/wayland/meta-wayland-seat.c @@ -236,6 +236,7 @@ meta_wayland_seat_new (MetaWaylandCompositor *compositor, NULL); seat->text_input = meta_wayland_text_input_new (seat); + seat->pointer_warp = meta_wayland_pointer_warp_new (seat); meta_wayland_data_device_init (&seat->data_device); meta_wayland_data_device_primary_init (&seat->primary_data_device); @@ -274,6 +275,7 @@ meta_wayland_seat_free (MetaWaylandSeat *seat) g_object_unref (seat->keyboard); g_object_unref (seat->touch); meta_wayland_text_input_destroy (seat->text_input); + meta_wayland_pointer_warp_destroy (seat->pointer_warp); g_free (seat); } diff --git a/src/wayland/meta-wayland-seat.h b/src/wayland/meta-wayland-seat.h index 2e20a201c..2c545f100 100644 --- a/src/wayland/meta-wayland-seat.h +++ b/src/wayland/meta-wayland-seat.h @@ -30,6 +30,7 @@ #include "wayland/meta-wayland-input-device.h" #include "wayland/meta-wayland-keyboard.h" #include "wayland/meta-wayland-pointer.h" +#include "wayland/meta-wayland-pointer-warp.h" #include "wayland/meta-wayland-tablet-tool.h" #include "wayland/meta-wayland-text-input.h" #include "wayland/meta-wayland-touch.h" @@ -48,6 +49,7 @@ struct _MetaWaylandSeat MetaWaylandDataDevicePrimary primary_data_device; MetaWaylandTextInput *text_input; + MetaWaylandPointerWarp *pointer_warp; guint capabilities; }; diff --git a/src/wayland/meta-wayland-versions.h b/src/wayland/meta-wayland-versions.h index 666501229..bb0d9ca6e 100644 --- a/src/wayland/meta-wayland-versions.h +++ b/src/wayland/meta-wayland-versions.h @@ -60,5 +60,6 @@ #define META_ZWP_PRIMARY_SELECTION_V1_VERSION 1 #define META_XDG_TOPLEVEL_TAG_V1_VERSION 1 #define META_WP_CURSOR_SHAPE_VERSION 2 +#define META_WP_POINTER_WARP_VERSION 1 #endif diff --git a/src/wayland/protocol/pointer-warp-v1.xml b/src/wayland/protocol/pointer-warp-v1.xml new file mode 100644 index 000000000..158dad83c --- /dev/null +++ b/src/wayland/protocol/pointer-warp-v1.xml @@ -0,0 +1,72 @@ + + + + Copyright © 2024 Neal Gompa + Copyright © 2024 Xaver Hugl + Copyright © 2024 Matthias Klumpp + Copyright © 2024 Vlad Zahorodnii + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + The above copyright notice and this permission notice (including the next + paragraph) shall be included in all copies or substantial portions of the + Software. + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. + + + + + This global interface allows applications to request the pointer to be + moved to a position relative to a wl_surface. + + Note that if the desired behavior is to constrain the pointer to an area + or lock it to a position, this protocol does not provide a reliable way + to do that. The pointer constraint and pointer lock protocols should be + used for those use cases instead. + + Warning! The protocol described in this file is currently in the testing + phase. Backward compatible changes may be added together with the + corresponding interface version bump. Backward incompatible changes can + only be done by creating a new major version of the extension. + + + + + Destroy the pointer warp manager. + + + + + + Request the compositor to move the pointer to a surface-local position. + Whether or not the compositor honors the request is implementation defined, + but it should + - honor it if the surface has pointer focus, including + when it has an implicit pointer grab + - reject it if the enter serial is incorrect + - reject it if the requested position is outside of the surface + + Note that the enter serial is valid for any surface of the client, + and does not have to be from the surface the pointer is warped to. + + + + + + + + + +