wayland: Add mouse pointer warp support

The pointer confinement protocol does allow attempted warping the pointer via a hint, provided that the pointer is locked at the time of the request, and the requested coordinates fall within the bounds of the window.

Toggle the pointer locked state and request the pointer warp when the required protocol is available. This is similar to what XWayland does internally.
This commit is contained in:
Frank Praznik committed 2024-05-03 12:33:19 -04:00
1 parent 22016228ca
commit 3a6d9c59f4
6 files changed
+108 -52

No files matched your search

+35 -28
View File
@@ -3197,32 +3197,47 @@ static const struct zwp_locked_pointer_v1_listener locked_pointer_listener = {
locked_pointer_unlocked,
};
static void lock_pointer_to_window(SDL_Window *window,
struct SDL_WaylandInput *input)
int Wayland_input_lock_pointer(struct SDL_WaylandInput *input, SDL_Window *window)
{
SDL_WindowData *w = window->driverdata;
SDL_VideoData *d = input->display;
struct zwp_locked_pointer_v1 *locked_pointer;
if (!d->pointer_constraints || !input->pointer) {
return;
return -1;
}
if (!w->locked_pointer) {
if (w->confined_pointer) {
/* If the pointer is already confined to the surface, the lock will fail with a protocol error. */
Wayland_input_unconfine_pointer(input, window);
}
w->locked_pointer = zwp_pointer_constraints_v1_lock_pointer(d->pointer_constraints,
w->surface,
input->pointer,
NULL,
ZWP_POINTER_CONSTRAINTS_V1_LIFETIME_PERSISTENT);
zwp_locked_pointer_v1_add_listener(w->locked_pointer,
&locked_pointer_listener,
window);
}
return 0;
}
int Wayland_input_unlock_pointer(struct SDL_WaylandInput *input, SDL_Window *window)
{
SDL_WindowData *w = window->driverdata;
if (w->locked_pointer) {
return;
zwp_locked_pointer_v1_destroy(w->locked_pointer);
w->locked_pointer = NULL;
}
locked_pointer =
zwp_pointer_constraints_v1_lock_pointer(d->pointer_constraints,
w->surface,
input->pointer,
NULL,
ZWP_POINTER_CONSTRAINTS_V1_LIFETIME_PERSISTENT);
zwp_locked_pointer_v1_add_listener(locked_pointer,
&locked_pointer_listener,
window);
/* Restore existing pointer confinement. */
Wayland_input_confine_pointer(input, window);
w->locked_pointer = locked_pointer;
return 0;
}
static void pointer_confine_destroy(SDL_Window *window)
@@ -3234,7 +3249,7 @@ static void pointer_confine_destroy(SDL_Window *window)
}
}
int Wayland_input_lock_pointer(struct SDL_WaylandInput *input)
int Wayland_input_enable_relative_pointer(struct SDL_WaylandInput *input)
{
SDL_VideoDevice *vd = SDL_GetVideoDevice();
SDL_VideoData *d = input->display;
@@ -3261,10 +3276,7 @@ int Wayland_input_lock_pointer(struct SDL_WaylandInput *input)
}
if (!input->relative_pointer) {
relative_pointer =
zwp_relative_pointer_manager_v1_get_relative_pointer(
d->relative_pointer_manager,
input->pointer);
relative_pointer = zwp_relative_pointer_manager_v1_get_relative_pointer(d->relative_pointer_manager, input->pointer);
zwp_relative_pointer_v1_add_listener(relative_pointer,
&relative_pointer_listener,
input);
@@ -3272,7 +3284,7 @@ int Wayland_input_lock_pointer(struct SDL_WaylandInput *input)
}
for (window = vd->windows; window; window = window->next) {
lock_pointer_to_window(window, input);
Wayland_input_lock_pointer(input, window);
}
d->relative_mouse_mode = 1;
@@ -3280,19 +3292,14 @@ int Wayland_input_lock_pointer(struct SDL_WaylandInput *input)
return 0;
}
int Wayland_input_unlock_pointer(struct SDL_WaylandInput *input)
int Wayland_input_disable_relative_pointer(struct SDL_WaylandInput *input)
{
SDL_VideoDevice *vd = SDL_GetVideoDevice();
SDL_VideoData *d = input->display;
SDL_Window *window;
SDL_WindowData *w;
for (window = vd->windows; window; window = window->next) {
w = window->driverdata;
if (w->locked_pointer) {
zwp_locked_pointer_v1_destroy(w->locked_pointer);
}
w->locked_pointer = NULL;
Wayland_input_unlock_pointer(input, window);
}
if (input->relative_pointer) {
+5 -2
View File
@@ -200,8 +200,11 @@ extern void Wayland_create_text_input(SDL_VideoData *d);
extern void Wayland_input_initialize_seat(SDL_VideoData *d);
extern void Wayland_display_destroy_input(SDL_VideoData *d);
extern int Wayland_input_lock_pointer(struct SDL_WaylandInput *input);
extern int Wayland_input_unlock_pointer(struct SDL_WaylandInput *input);
extern int Wayland_input_enable_relative_pointer(struct SDL_WaylandInput *input);
extern int Wayland_input_disable_relative_pointer(struct SDL_WaylandInput *input);
extern int Wayland_input_lock_pointer(struct SDL_WaylandInput *input, SDL_Window *window);
extern int Wayland_input_unlock_pointer(struct SDL_WaylandInput *input, SDL_Window *window);
extern int Wayland_input_confine_pointer(struct SDL_WaylandInput *input, SDL_Window *window);
extern int Wayland_input_unconfine_pointer(struct SDL_WaylandInput *input, SDL_Window *window);
+53 -10
View File
@@ -36,6 +36,7 @@
#include "SDL_waylandshmbuffer.h"
#include "cursor-shape-v1-client-protocol.h"
#include "pointer-constraints-unstable-v1-client-protocol.h"
#include "../../SDL_hints_c.h"
@@ -556,7 +557,7 @@ static int Wayland_ShowCursor(SDL_Cursor *cursor)
input->cursor_visible = SDL_TRUE;
if (input->relative_mode_override) {
Wayland_input_unlock_pointer(input);
Wayland_input_disable_relative_pointer(input);
input->relative_mode_override = SDL_FALSE;
}
@@ -572,21 +573,62 @@ static int Wayland_WarpMouse(SDL_Window *window, float x, float y)
{
SDL_VideoDevice *vd = SDL_GetVideoDevice();
SDL_VideoData *d = vd->driverdata;
SDL_WindowData *wind = window->driverdata;
struct SDL_WaylandInput *input = d->input;
if (input->cursor_visible == SDL_TRUE) {
return SDL_Unsupported();
if (input->cursor_visible || (input->warp_emulation_prohibited && !d->relative_mouse_mode)) {
if (d->pointer_constraints) {
const SDL_bool toggle_lock = !wind->locked_pointer;
/* 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) {
Wayland_input_lock_pointer(input, window);
}
if (wind->locked_pointer) {
zwp_locked_pointer_v1_set_cursor_position_hint(wind->locked_pointer, wl_fixed_from_double(x), wl_fixed_from_double(y));
wl_surface_commit(wind->surface);
}
if (toggle_lock) {
Wayland_input_unlock_pointer(input, window);
}
/* 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, SDL_GLOBAL_MOUSE_ID, SDL_FALSE, x, y);
} else {
return SDL_SetError("wayland: mouse warp failed; compositor lacks support for the required zwp_pointer_confinement_v1 protocol");
}
} else if (input->warp_emulation_prohibited) {
return SDL_Unsupported();
} else {
if (!d->relative_mouse_mode) {
Wayland_input_lock_pointer(input);
input->relative_mode_override = SDL_TRUE;
}
} else if (!d->relative_mouse_mode) {
Wayland_input_lock_pointer(input, window);
input->relative_mode_override = SDL_TRUE;
}
return 0;
}
static int Wayland_WarpMouseGlobal(float x, float y)
{
SDL_VideoDevice *vd = SDL_GetVideoDevice();
SDL_VideoData *d = vd->driverdata;
struct SDL_WaylandInput *input = d->input;
SDL_WindowData *wind = input->pointer_focus;
/* If the client wants the coordinates warped to within the focused window, just convert the coordinates to relative. */
if (wind) {
SDL_Window *window = wind->sdlwindow;
return Wayland_WarpMouse(window, x - (float)window->x, y - (float)window->y);
}
return SDL_SetError("wayland: can't warp the mouse when a window does not have focus");
}
static int Wayland_SetRelativeMouseMode(SDL_bool enabled)
{
SDL_VideoDevice *vd = SDL_GetVideoDevice();
@@ -603,9 +645,9 @@ static int Wayland_SetRelativeMouseMode(SDL_bool enabled)
* mouse warp emulation by default.
*/
data->input->warp_emulation_prohibited = SDL_TRUE;
return Wayland_input_lock_pointer(data->input);
return Wayland_input_enable_relative_pointer(data->input);
} else {
return Wayland_input_unlock_pointer(data->input);
return Wayland_input_disable_relative_pointer(data->input);
}
}
@@ -713,6 +755,7 @@ void Wayland_InitMouse(void)
mouse->ShowCursor = Wayland_ShowCursor;
mouse->FreeCursor = Wayland_FreeCursor;
mouse->WarpMouse = Wayland_WarpMouse;
mouse->WarpMouseGlobal = Wayland_WarpMouseGlobal;
mouse->SetRelativeMouseMode = Wayland_SetRelativeMouseMode;
mouse->GetGlobalMouseState = Wayland_GetGlobalMouseState;
+1 -1
View File
@@ -2350,7 +2350,7 @@ int Wayland_CreateWindow(SDL_VideoDevice *_this, SDL_Window *window, SDL_Propert
#endif
if (c->relative_mouse_mode) {
Wayland_input_lock_pointer(c->input);
Wayland_input_enable_relative_pointer(c->input);
}
/* We may need to create an idle inhibitor for this new window */