Skip to content

Commit ec12971

Browse files
ZamundaaaKontrabant
authored andcommitted
wayland: Implement the pointer warp protocol
The pointer warp protocol allows us to warp the pointer to a different position on the surface, without any hacks like locking and unlocking the pointer.
1 parent 558a89f commit ec12971

File tree

4 files changed

+120
-31
lines changed

4 files changed

+120
-31
lines changed

src/video/wayland/SDL_waylandmouse.c

Lines changed: 39 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
#include "cursor-shape-v1-client-protocol.h"
3939
#include "pointer-constraints-unstable-v1-client-protocol.h"
4040
#include "viewporter-client-protocol.h"
41+
#include "pointer-warp-v1-client-protocol.h"
4142

4243
#include "../../SDL_hints_c.h"
4344

@@ -832,43 +833,50 @@ void Wayland_SeatWarpMouse(SDL_WaylandSeat *seat, SDL_WindowData *window, float
832833
SDL_VideoData *d = vd->internal;
833834

834835
if (seat->pointer.wl_pointer) {
835-
bool toggle_lock = !seat->pointer.locked_pointer;
836-
bool update_grabs = false;
837-
838-
/* The pointer confinement protocol allows setting a hint to warp the pointer,
839-
* but only when the pointer is locked.
840-
*
841-
* Lock the pointer, set the position hint, unlock, and hope for the best.
842-
*/
843-
if (toggle_lock) {
844-
if (seat->pointer.confined_pointer) {
845-
zwp_confined_pointer_v1_destroy(seat->pointer.confined_pointer);
846-
seat->pointer.confined_pointer = NULL;
847-
update_grabs = true;
836+
if (d->wp_pointer_warp_v1) {
837+
// It's a protocol error to warp the pointer outside of the surface, so clamp the position.
838+
const wl_fixed_t f_x = wl_fixed_from_double(SDL_clamp(x / window->pointer_scale.x, 0, window->current.logical_width));
839+
const wl_fixed_t f_y = wl_fixed_from_double(SDL_clamp(y / window->pointer_scale.y, 0, window->current.logical_height));
840+
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);
841+
} else {
842+
bool toggle_lock = !seat->pointer.locked_pointer;
843+
bool update_grabs = false;
844+
845+
/* The pointer confinement protocol allows setting a hint to warp the pointer,
846+
* but only when the pointer is locked.
847+
*
848+
* Lock the pointer, set the position hint, unlock, and hope for the best.
849+
*/
850+
if (toggle_lock) {
851+
if (seat->pointer.confined_pointer) {
852+
zwp_confined_pointer_v1_destroy(seat->pointer.confined_pointer);
853+
seat->pointer.confined_pointer = NULL;
854+
update_grabs = true;
855+
}
856+
seat->pointer.locked_pointer = zwp_pointer_constraints_v1_lock_pointer(d->pointer_constraints, window->surface,
857+
seat->pointer.wl_pointer, NULL,
858+
ZWP_POINTER_CONSTRAINTS_V1_LIFETIME_ONESHOT);
848859
}
849-
seat->pointer.locked_pointer = zwp_pointer_constraints_v1_lock_pointer(d->pointer_constraints, window->surface,
850-
seat->pointer.wl_pointer, NULL,
851-
ZWP_POINTER_CONSTRAINTS_V1_LIFETIME_ONESHOT);
852-
}
853860

854-
const wl_fixed_t f_x = wl_fixed_from_double(x / window->pointer_scale.x);
855-
const wl_fixed_t f_y = wl_fixed_from_double(y / window->pointer_scale.y);
856-
zwp_locked_pointer_v1_set_cursor_position_hint(seat->pointer.locked_pointer, f_x, f_y);
857-
wl_surface_commit(window->surface);
861+
const wl_fixed_t f_x = wl_fixed_from_double(x / window->pointer_scale.x);
862+
const wl_fixed_t f_y = wl_fixed_from_double(y / window->pointer_scale.y);
863+
zwp_locked_pointer_v1_set_cursor_position_hint(seat->pointer.locked_pointer, f_x, f_y);
864+
wl_surface_commit(window->surface);
858865

859-
if (toggle_lock) {
860-
zwp_locked_pointer_v1_destroy(seat->pointer.locked_pointer);
861-
seat->pointer.locked_pointer = NULL;
866+
if (toggle_lock) {
867+
zwp_locked_pointer_v1_destroy(seat->pointer.locked_pointer);
868+
seat->pointer.locked_pointer = NULL;
862869

863-
if (update_grabs) {
864-
Wayland_SeatUpdatePointerGrab(seat);
870+
if (update_grabs) {
871+
Wayland_SeatUpdatePointerGrab(seat);
872+
}
865873
}
866-
}
867874

868-
/* NOTE: There is a pending warp event under discussion that should replace this when available.
869-
* https://gitlab.freedesktop.org/wayland/wayland/-/merge_requests/340
870-
*/
871-
SDL_SendMouseMotion(0, window->sdlwindow, seat->pointer.sdl_id, false, x, y);
875+
/* NOTE: There is a pending warp event under discussion that should replace this when available.
876+
* https://gitlab.freedesktop.org/wayland/wayland/-/merge_requests/340
877+
*/
878+
SDL_SendMouseMotion(0, window->sdlwindow, seat->pointer.sdl_id, false, x, y);
879+
}
872880
}
873881
}
874882

src/video/wayland/SDL_waylandvideo.c

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@
6666
#include "xdg-shell-client-protocol.h"
6767
#include "xdg-toplevel-icon-v1-client-protocol.h"
6868
#include "color-management-v1-client-protocol.h"
69+
#include "pointer-warp-v1-client-protocol.h"
6970

7071
#ifdef HAVE_LIBDECOR_H
7172
#include <libdecor.h>
@@ -1311,6 +1312,8 @@ static void display_handle_global(void *data, struct wl_registry *registry, uint
13111312
} else if (SDL_strcmp(interface, "wp_color_manager_v1") == 0) {
13121313
d->wp_color_manager_v1 = wl_registry_bind(d->registry, id, &wp_color_manager_v1_interface, 1);
13131314
Wayland_InitColorManager(d);
1315+
} else if (SDL_strcmp(interface, "wp_pointer_warp_v1") == 0) {
1316+
d->wp_pointer_warp_v1 = wl_registry_bind(d->registry, id, &wp_pointer_warp_v1_interface, 1);
13141317
}
13151318
}
13161319

@@ -1620,6 +1623,11 @@ static void Wayland_VideoCleanup(SDL_VideoDevice *_this)
16201623
data->wp_color_manager_v1 = NULL;
16211624
}
16221625

1626+
if (data->wp_pointer_warp_v1) {
1627+
wp_pointer_warp_v1_destroy(data->wp_pointer_warp_v1);
1628+
data->wp_pointer_warp_v1 = NULL;
1629+
}
1630+
16231631
if (data->compositor) {
16241632
wl_compositor_destroy(data->compositor);
16251633
data->compositor = NULL;

src/video/wayland/SDL_waylandvideo.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ struct SDL_VideoData
6565
} shell;
6666
struct zwp_relative_pointer_manager_v1 *relative_pointer_manager;
6767
struct zwp_pointer_constraints_v1 *pointer_constraints;
68+
struct wp_pointer_warp_v1 *wp_pointer_warp_v1;
6869
struct wp_cursor_shape_manager_v1 *cursor_shape_manager;
6970
struct wl_data_device_manager *data_device_manager;
7071
struct zwp_primary_selection_device_manager_v1 *primary_selection_device_manager;

wayland-protocols/pointer-warp-v1.xml

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<protocol name="pointer_warp_v1">
3+
<copyright>
4+
Copyright © 2024 Neal Gompa
5+
Copyright © 2024 Xaver Hugl
6+
Copyright © 2024 Matthias Klumpp
7+
Copyright © 2024 Vlad Zahorodnii
8+
9+
Permission is hereby granted, free of charge, to any person obtaining a
10+
copy of this software and associated documentation files (the "Software"),
11+
to deal in the Software without restriction, including without limitation
12+
the rights to use, copy, modify, merge, publish, distribute, sublicense,
13+
and/or sell copies of the Software, and to permit persons to whom the
14+
Software is furnished to do so, subject to the following conditions:
15+
The above copyright notice and this permission notice (including the next
16+
paragraph) shall be included in all copies or substantial portions of the
17+
Software.
18+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
21+
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
23+
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
24+
DEALINGS IN THE SOFTWARE.
25+
</copyright>
26+
27+
<interface name="wp_pointer_warp_v1" version="1">
28+
<description summary="reposition the pointer to a location on a surface">
29+
This global interface allows applications to request the pointer to be
30+
moved to a position relative to a wl_surface.
31+
32+
Note that if the desired behavior is to constrain the pointer to an area
33+
or lock it to a position, this protocol does not provide a reliable way
34+
to do that. The pointer constraint and pointer lock protocols should be
35+
used for those use cases instead.
36+
37+
Warning! The protocol described in this file is currently in the testing
38+
phase. Backward compatible changes may be added together with the
39+
corresponding interface version bump. Backward incompatible changes can
40+
only be done by creating a new major version of the extension.
41+
</description>
42+
43+
<request name="destroy" type="destructor">
44+
<description summary="destroy the warp manager">
45+
Destroy the pointer warp manager.
46+
</description>
47+
</request>
48+
49+
<request name="warp_pointer">
50+
<description summary="reposition the pointer">
51+
Request the compositor to move the pointer to a surface-local position.
52+
Whether or not the compositor honors the request is implementation defined,
53+
but it should
54+
- honor it if the surface has pointer focus, including
55+
when it has an implicit pointer grab
56+
- reject it if the enter serial is incorrect
57+
- reject it if the requested position is outside of the surface
58+
59+
Note that the enter serial is valid for any surface of the client,
60+
and does not have to be from the surface the pointer is warped to.
61+
62+
</description>
63+
<arg name="surface" type="object" interface="wl_surface"
64+
summary="surface to position the pointer on"/>
65+
<arg name="pointer" type="object" interface="wl_pointer"
66+
summary="the pointer that should be repositioned"/>
67+
<arg name="x" type="fixed"/>
68+
<arg name="y" type="fixed"/>
69+
<arg name="serial" type="uint" summary="serial number of the enter event"/>
70+
</request>
71+
</interface>
72+
</protocol>

0 commit comments

Comments
 (0)