diff --git a/src/video/wayland/SDL_waylandmouse.c b/src/video/wayland/SDL_waylandmouse.c index 39b3fcb8c7..82a1fdfe07 100644 --- a/src/video/wayland/SDL_waylandmouse.c +++ b/src/video/wayland/SDL_waylandmouse.c @@ -38,6 +38,7 @@ #include "cursor-shape-v1-client-protocol.h" #include "pointer-constraints-unstable-v1-client-protocol.h" #include "viewporter-client-protocol.h" +#include "pointer-warp-v1-client-protocol.h" #include "../../SDL_hints_c.h" @@ -832,43 +833,50 @@ void Wayland_SeatWarpMouse(SDL_WaylandSeat *seat, SDL_WindowData *window, float SDL_VideoData *d = vd->internal; if (seat->pointer.wl_pointer) { - bool toggle_lock = !seat->pointer.locked_pointer; - bool update_grabs = false; + if (d->wp_pointer_warp_v1) { + // It's a protocol error to warp the pointer outside of the surface, so clamp the position. + const wl_fixed_t f_x = wl_fixed_from_double(SDL_clamp(x / window->pointer_scale.x, 0, window->current.logical_width)); + const wl_fixed_t f_y = wl_fixed_from_double(SDL_clamp(y / window->pointer_scale.y, 0, window->current.logical_height)); + wp_pointer_warp_v1_warp_pointer(d->wp_pointer_warp_v1, window->surface, seat->pointer.wl_pointer, f_x, f_y, seat->pointer.enter_serial); + } else { + bool toggle_lock = !seat->pointer.locked_pointer; + bool update_grabs = false; - /* The pointer confinement protocol allows setting a hint to warp the pointer, - * but only when the pointer is locked. - * - * Lock the pointer, set the position hint, unlock, and hope for the best. - */ - if (toggle_lock) { - if (seat->pointer.confined_pointer) { - zwp_confined_pointer_v1_destroy(seat->pointer.confined_pointer); - seat->pointer.confined_pointer = NULL; - update_grabs = true; + /* The pointer confinement protocol allows setting a hint to warp the pointer, + * but only when the pointer is locked. + * + * Lock the pointer, set the position hint, unlock, and hope for the best. + */ + if (toggle_lock) { + if (seat->pointer.confined_pointer) { + zwp_confined_pointer_v1_destroy(seat->pointer.confined_pointer); + seat->pointer.confined_pointer = NULL; + update_grabs = true; + } + seat->pointer.locked_pointer = zwp_pointer_constraints_v1_lock_pointer(d->pointer_constraints, window->surface, + seat->pointer.wl_pointer, NULL, + ZWP_POINTER_CONSTRAINTS_V1_LIFETIME_ONESHOT); } - seat->pointer.locked_pointer = zwp_pointer_constraints_v1_lock_pointer(d->pointer_constraints, window->surface, - seat->pointer.wl_pointer, NULL, - ZWP_POINTER_CONSTRAINTS_V1_LIFETIME_ONESHOT); - } - const wl_fixed_t f_x = wl_fixed_from_double(x / window->pointer_scale.x); - const wl_fixed_t f_y = wl_fixed_from_double(y / window->pointer_scale.y); - zwp_locked_pointer_v1_set_cursor_position_hint(seat->pointer.locked_pointer, f_x, f_y); - wl_surface_commit(window->surface); + const wl_fixed_t f_x = wl_fixed_from_double(x / window->pointer_scale.x); + const wl_fixed_t f_y = wl_fixed_from_double(y / window->pointer_scale.y); + zwp_locked_pointer_v1_set_cursor_position_hint(seat->pointer.locked_pointer, f_x, f_y); + wl_surface_commit(window->surface); - if (toggle_lock) { - zwp_locked_pointer_v1_destroy(seat->pointer.locked_pointer); - seat->pointer.locked_pointer = NULL; + if (toggle_lock) { + zwp_locked_pointer_v1_destroy(seat->pointer.locked_pointer); + seat->pointer.locked_pointer = NULL; - if (update_grabs) { - Wayland_SeatUpdatePointerGrab(seat); + if (update_grabs) { + Wayland_SeatUpdatePointerGrab(seat); + } } - } - /* NOTE: There is a pending warp event under discussion that should replace this when available. - * https://gitlab.freedesktop.org/wayland/wayland/-/merge_requests/340 - */ - SDL_SendMouseMotion(0, window->sdlwindow, seat->pointer.sdl_id, false, x, y); + /* NOTE: There is a pending warp event under discussion that should replace this when available. + * https://gitlab.freedesktop.org/wayland/wayland/-/merge_requests/340 + */ + SDL_SendMouseMotion(0, window->sdlwindow, seat->pointer.sdl_id, false, x, y); + } } } diff --git a/src/video/wayland/SDL_waylandvideo.c b/src/video/wayland/SDL_waylandvideo.c index 8514a903d6..a1b5276648 100644 --- a/src/video/wayland/SDL_waylandvideo.c +++ b/src/video/wayland/SDL_waylandvideo.c @@ -66,6 +66,7 @@ #include "xdg-shell-client-protocol.h" #include "xdg-toplevel-icon-v1-client-protocol.h" #include "color-management-v1-client-protocol.h" +#include "pointer-warp-v1-client-protocol.h" #ifdef HAVE_LIBDECOR_H #include @@ -1311,6 +1312,8 @@ static void display_handle_global(void *data, struct wl_registry *registry, uint } else if (SDL_strcmp(interface, "wp_color_manager_v1") == 0) { d->wp_color_manager_v1 = wl_registry_bind(d->registry, id, &wp_color_manager_v1_interface, 1); Wayland_InitColorManager(d); + } else if (SDL_strcmp(interface, "wp_pointer_warp_v1") == 0) { + d->wp_pointer_warp_v1 = wl_registry_bind(d->registry, id, &wp_pointer_warp_v1_interface, 1); } } @@ -1620,6 +1623,11 @@ static void Wayland_VideoCleanup(SDL_VideoDevice *_this) data->wp_color_manager_v1 = NULL; } + if (data->wp_pointer_warp_v1) { + wp_pointer_warp_v1_destroy(data->wp_pointer_warp_v1); + data->wp_pointer_warp_v1 = NULL; + } + if (data->compositor) { wl_compositor_destroy(data->compositor); data->compositor = NULL; diff --git a/src/video/wayland/SDL_waylandvideo.h b/src/video/wayland/SDL_waylandvideo.h index e702a8e6a3..e964731137 100644 --- a/src/video/wayland/SDL_waylandvideo.h +++ b/src/video/wayland/SDL_waylandvideo.h @@ -65,6 +65,7 @@ struct SDL_VideoData } shell; struct zwp_relative_pointer_manager_v1 *relative_pointer_manager; struct zwp_pointer_constraints_v1 *pointer_constraints; + struct wp_pointer_warp_v1 *wp_pointer_warp_v1; struct wp_cursor_shape_manager_v1 *cursor_shape_manager; struct wl_data_device_manager *data_device_manager; struct zwp_primary_selection_device_manager_v1 *primary_selection_device_manager; diff --git a/wayland-protocols/pointer-warp-v1.xml b/wayland-protocols/pointer-warp-v1.xml new file mode 100644 index 0000000000..158dad83c5 --- /dev/null +++ b/wayland-protocols/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. + + + + + + + + + +