From 235e4870af091ea7e3814ee2dbdb8e2ec627aaf0 Mon Sep 17 00:00:00 2001 From: Frank Praznik Date: Thu, 9 Oct 2025 09:48:22 -0400 Subject: [PATCH] wayland: Special-case relative warp mode to deliver accelerated relative motion The Wayland backend lacks pointer warp functionality, so special-case the relative warp mode hint to deliver accelerated relative motion deltas, which is ultimately what the client wants by enabling this hint. --- src/events/SDL_mouse.c | 9 +++++++++ src/video/wayland/SDL_waylandevents.c | 23 ++++++++++++++--------- src/video/wayland/SDL_waylandmouse.c | 3 +++ src/video/wayland/SDL_waylandvideo.h | 1 + 4 files changed, 27 insertions(+), 9 deletions(-) diff --git a/src/events/SDL_mouse.c b/src/events/SDL_mouse.c index a9a89f63d8..3eec659ede 100644 --- a/src/events/SDL_mouse.c +++ b/src/events/SDL_mouse.c @@ -1121,6 +1121,15 @@ int SDL_WarpMouseGlobal(int x, int y) static SDL_bool ShouldUseRelativeModeWarp(SDL_Mouse *mouse) { +#ifdef SDL_VIDEO_DRIVER_WAYLAND + SDL_VideoDevice *vid = SDL_GetVideoDevice(); + + /* Wayland can't warp the mouse, but uses this hint internally to deliver accelerated motion */ + if (SDL_strcmp(vid->name, "wayland") == 0) { + return SDL_FALSE; + } +#endif + if (!mouse->WarpMouse) { /* Need this functionality for relative mode warp implementation */ return SDL_FALSE; diff --git a/src/video/wayland/SDL_waylandevents.c b/src/video/wayland/SDL_waylandevents.c index 496bb5e8fe..189798a91e 100644 --- a/src/video/wayland/SDL_waylandevents.c +++ b/src/video/wayland/SDL_waylandevents.c @@ -2681,23 +2681,28 @@ static void relative_pointer_handle_relative_motion(void *data, struct SDL_WaylandInput *input = data; SDL_VideoData *d = input->display; SDL_WindowData *window = input->pointer_focus; - double dx_unaccel; - double dy_unaccel; double dx; double dy; + double dx_mod; + double dy_mod; - dx_unaccel = wl_fixed_to_double(dx_unaccel_w); - dy_unaccel = wl_fixed_to_double(dy_unaccel_w); + if (!d->relative_mode_accelerated) { + dx = wl_fixed_to_double(dx_unaccel_w); + dy = wl_fixed_to_double(dy_unaccel_w); + } else { + dx = wl_fixed_to_double(dx_w) * (window ? window->pointer_scale_x : 1.0); + dy = wl_fixed_to_double(dy_w) * (window ? window->pointer_scale_y : 1.0); + } /* Add left over fraction from last event. */ - dx_unaccel += input->dx_frac; - dy_unaccel += input->dy_frac; + dx += input->dx_frac; + dy += input->dy_frac; - input->dx_frac = modf(dx_unaccel, &dx); - input->dy_frac = modf(dy_unaccel, &dy); + input->dx_frac = modf(dx, &dx_mod); + input->dy_frac = modf(dy, &dy_mod); if (input->pointer_focus && d->relative_mouse_mode) { - SDL_SendMouseMotion(window->sdlwindow, 0, 1, (int)dx, (int)dy); + SDL_SendMouseMotion(window->sdlwindow, 0, 1, (int)dx_mod, (int)dy_mod); } } diff --git a/src/video/wayland/SDL_waylandmouse.c b/src/video/wayland/SDL_waylandmouse.c index bf1bf9b8d3..89fb301d18 100644 --- a/src/video/wayland/SDL_waylandmouse.c +++ b/src/video/wayland/SDL_waylandmouse.c @@ -537,6 +537,9 @@ static int Wayland_SetRelativeMouseMode(SDL_bool enabled) SDL_VideoData *data = (SDL_VideoData *)vd->driverdata; if (enabled) { + /* Clients use relative warp mode to get accelerated motion deltas, which Wayland delivers internally. */ + data->relative_mode_accelerated = SDL_GetHintBoolean(SDL_HINT_MOUSE_RELATIVE_MODE_WARP, SDL_FALSE); + /* Disable mouse warp emulation if it's enabled. */ if (data->input->relative_mode_override) { data->input->relative_mode_override = SDL_FALSE; diff --git a/src/video/wayland/SDL_waylandvideo.h b/src/video/wayland/SDL_waylandvideo.h index 13e9b35ee8..c8464f397a 100644 --- a/src/video/wayland/SDL_waylandvideo.h +++ b/src/video/wayland/SDL_waylandvideo.h @@ -101,6 +101,7 @@ typedef struct char *classname; int relative_mouse_mode; + int relative_mode_accelerated; SDL_bool egl_transparency_enabled; } SDL_VideoData;