From 428f0dcd61685337d5d3c091fbe58b964334ddee Mon Sep 17 00:00:00 2001 From: expikr <77922942+expikr@users.noreply.github.com> Date: Fri, 21 Feb 2025 19:33:14 +0800 Subject: [PATCH] add SDL_SetRelativeMouseTransform --- include/SDL3/SDL_mouse.h | 43 +++++++++++++++++++++++++++ src/events/SDL_mouse.c | 26 +++++++++++----- src/events/SDL_mouse_c.h | 8 +++-- src/video/wayland/SDL_waylandevents.c | 3 +- 4 files changed, 70 insertions(+), 10 deletions(-) diff --git a/include/SDL3/SDL_mouse.h b/include/SDL3/SDL_mouse.h index 864135d2be..79ce61a507 100644 --- a/include/SDL3/SDL_mouse.h +++ b/include/SDL3/SDL_mouse.h @@ -147,6 +147,38 @@ typedef enum SDL_MouseWheelDirection */ typedef Uint32 SDL_MouseButtonFlags; +/** + * A callback used to transform mouse motion delta from raw values. + * + * This is called during SDL's handling of platform mouse events to + * scale the values of the resulting motion delta. + * + * \param userdata what was passed as `userdata` to SDL_SetRelativeMouseTransform(). + * \param timestamp the associated time at which this mouse motion event was received. + * \param window the associated window to which this mouse motion event was addressed. + * \param mouseID the associated mouse from which this mouse motion event was emitted. + * \param x pointer to a variable that will be treated as the resulting x-axis motion. + * \param y pointer to a variable that will be treated as the resulting y-axis motion. + * + * \threadsafety This callback is called by SDL's internal mouse input processing + * procedure, which may be a thread separate from the main event loop + * that is run at realtime priority. Stalling this thread with too much + * work in the callback can therefore potentially freeze the entire + * system. Care should be taken with proper synchronization practices + * when adding other side effects beyond mutation of the x and y values. + * + * \since This datatype is available since SDL 3.2.6. + * + * \sa SDL_SetRelativeMouseTransform + */ +typedef void (SDLCALL *SDL_MouseMotionTransformCallback)( + void *userdata, + Uint64 timestamp, + SDL_Window *window, + SDL_MouseID mouseID, + float *x, float *y +); + #define SDL_BUTTON_LEFT 1 #define SDL_BUTTON_MIDDLE 2 #define SDL_BUTTON_RIGHT 3 @@ -380,6 +412,17 @@ extern SDL_DECLSPEC void SDLCALL SDL_WarpMouseInWindow(SDL_Window *window, */ extern SDL_DECLSPEC bool SDLCALL SDL_WarpMouseGlobal(float x, float y); +/** + * Set a user-defined function by which to transform relative mouse inputs. + * This overrides the relative system scale and relative speed scale hints. + * + * \param callback a callback used to transform relative mouse motion, or NULL for default behavior. + * \param userdata a pointer that is passed to `callback`. + * + * \since This function is available since SDL 3.2.6. + */ +extern SDL_DECLSPEC void SDLCALL SDL_SetRelativeMouseTransform(SDL_MouseMotionTransformCallback callback, void *userdata); + /** * Set relative mouse mode for a window. * diff --git a/src/events/SDL_mouse.c b/src/events/SDL_mouse.c index 2ea5995dcc..7fcb097f34 100644 --- a/src/events/SDL_mouse.c +++ b/src/events/SDL_mouse.c @@ -705,14 +705,19 @@ static void SDL_PrivateSendMouseMotion(Uint64 timestamp, SDL_Window *window, SDL if (relative) { if (mouse->relative_mode) { - if (mouse->enable_relative_system_scale) { - if (mouse->ApplySystemScale) { - mouse->ApplySystemScale(mouse->system_scale_data, timestamp, window, mouseID, &x, &y); + if (mouse->InputTransform) { + void *data = mouse->input_transform_data; + mouse->InputTransform(data, timestamp, window, mouseID, &x, &y); + } else { + if (mouse->enable_relative_system_scale) { + if (mouse->ApplySystemScale) { + mouse->ApplySystemScale(mouse->system_scale_data, timestamp, window, mouseID, &x, &y); + } + } + if (mouse->enable_relative_speed_scale) { + x *= mouse->relative_speed_scale; + y *= mouse->relative_speed_scale; } - } - if (mouse->enable_relative_speed_scale) { - x *= mouse->relative_speed_scale; - y *= mouse->relative_speed_scale; } } else { if (mouse->enable_normal_speed_scale) { @@ -1115,6 +1120,13 @@ void SDL_QuitMouse(void) SDL_mice = NULL; } +void SDL_SetRelativeMouseTransform(SDL_MouseMotionTransformCallback transform, void *userdata) +{ + SDL_Mouse *mouse = SDL_GetMouse(); + mouse->InputTransform = transform; + mouse->input_transform_data = userdata; +} + SDL_MouseButtonFlags SDL_GetMouseState(float *x, float *y) { SDL_Mouse *mouse = SDL_GetMouse(); diff --git a/src/events/SDL_mouse_c.h b/src/events/SDL_mouse_c.h index 43cc5207da..ee3e814739 100644 --- a/src/events/SDL_mouse_c.h +++ b/src/events/SDL_mouse_c.h @@ -87,10 +87,14 @@ typedef struct // Get absolute mouse coordinates. (x) and (y) are never NULL and set to zero before call. SDL_MouseButtonFlags (*GetGlobalMouseState)(float *x, float *y); - // Platform-specific system mouse transform - void (*ApplySystemScale)(void *internal, Uint64 timestamp, SDL_Window *window, SDL_MouseID mouseID, float *x, float *y); + // Platform-specific system mouse transform applied in relative mode + SDL_MouseMotionTransformCallback ApplySystemScale; void *system_scale_data; + // User-defined mouse input transform applied in relative mode + SDL_MouseMotionTransformCallback InputTransform; + void *input_transform_data; + // Data common to all mice SDL_Window *focus; float x; diff --git a/src/video/wayland/SDL_waylandevents.c b/src/video/wayland/SDL_waylandevents.c index 83c414fb3a..4986419854 100644 --- a/src/video/wayland/SDL_waylandevents.c +++ b/src/video/wayland/SDL_waylandevents.c @@ -1012,6 +1012,7 @@ 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; + SDL_Mouse *mouse = SDL_GetMouse(); // Relative pointer event times are in microsecond granularity. const Uint64 timestamp = Wayland_GetEventTimestamp(SDL_US_TO_NS(((Uint64)time_hi << 32) | (Uint64)time_lo)); @@ -1019,7 +1020,7 @@ static void relative_pointer_handle_relative_motion(void *data, if (input->pointer_focus && d->relative_mouse_mode) { double dx; double dy; - if (!SDL_GetMouse()->enable_relative_system_scale) { + if (mouse->InputTransform || !mouse->enable_relative_system_scale) { dx = wl_fixed_to_double(dx_unaccel_w); dy = wl_fixed_to_double(dy_unaccel_w); } else {