From 428f0dcd61685337d5d3c091fbe58b964334ddee Mon Sep 17 00:00:00 2001
From: expikr <[EMAIL REDACTED]>
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 864135d2be09a..79ce61a507c13 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 2ea5995dccd56..7fcb097f342f3 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 43cc5207da806..ee3e814739c1d 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 83c414fb3af08..49864198544be 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 {