SDL: wayland: Add support for the MOUSE_RAW_* events

From 8c849ecc6cdc1d924668b8987f98260cf2243160 Mon Sep 17 00:00:00 2001
From: Frank Praznik <[EMAIL REDACTED]>
Date: Thu, 19 Dec 2024 13:15:39 -0500
Subject: [PATCH] wayland: Add support for the MOUSE_RAW_* events

Mouse button numbers are passed through directly, and axis values are passed through as the raw, 32-bit, wl_fixed_t 24.8 fixed-point values, which need to be divided by the denominator to convert to int/float.
---
 src/video/wayland/SDL_waylandevents.c   | 311 +++++++++++++-----------
 src/video/wayland/SDL_waylandevents_c.h |   4 +-
 2 files changed, 173 insertions(+), 142 deletions(-)

diff --git a/src/video/wayland/SDL_waylandevents.c b/src/video/wayland/SDL_waylandevents.c
index 9d8b4baf52422..518c85bb0bbc2 100644
--- a/src/video/wayland/SDL_waylandevents.c
+++ b/src/video/wayland/SDL_waylandevents.c
@@ -696,11 +696,17 @@ static void pointer_handle_button_common(struct SDL_WaylandInput *input, uint32_
 {
     SDL_WindowData *window = input->pointer_focus;
     enum wl_pointer_button_state state = state_w;
-    uint32_t sdl_button;
-    bool ignore_click = false;
+    Uint64 timestamp = Wayland_GetPointerTimestamp(input, time);
+
+    if (SDL_EventEnabled(SDL_EVENT_MOUSE_RAW_BUTTON)) {
+        SDL_SendRawMouseButton(timestamp, input->pointer_id, !!state, button);
+    }
 
     if (window) {
         SDL_VideoData *viddata = window->waylandData;
+        Uint32 sdl_button;
+        bool ignore_click = false;
+
         switch (button) {
         case BTN_LEFT:
             sdl_button = SDL_BUTTON_LEFT;
@@ -760,7 +766,7 @@ static void pointer_handle_button_common(struct SDL_WaylandInput *input, uint32_
 
         if (!ignore_click) {
             SDL_SendMouseButton(Wayland_GetPointerTimestamp(input, time), window->sdlwindow, input->pointer_id,
-                                sdl_button, (state != 0));
+                                sdl_button, !!state);
         }
     }
 }
@@ -777,10 +783,23 @@ static void pointer_handle_axis_common_v1(struct SDL_WaylandInput *input,
                                           uint32_t time, uint32_t axis, wl_fixed_t value)
 {
     SDL_WindowData *window = input->pointer_focus;
-    enum wl_pointer_axis a = axis;
-    float x, y;
+    const Uint64 timestamp = Wayland_GetPointerTimestamp(input, time);
+    const enum wl_pointer_axis a = axis;
+
+    if (SDL_EventEnabled(SDL_EVENT_MOUSE_RAW_SCROLL)) {
+        // wl_fixed_t is a 24.8 signed fixed-point number which needs to be converted by dividing by 256
+        const float denom = (float)(WAYLAND_WHEEL_AXIS_UNIT * 256);
+
+        if (a == WL_POINTER_AXIS_VERTICAL_SCROLL) {
+            SDL_SendRawMouseAxis(timestamp, input->pointer_id, 0, (int)value, 0.f, denom, SDL_EVENT_MOUSE_RAW_SCROLL);
+        } else {
+            SDL_SendRawMouseAxis(timestamp, input->pointer_id, (int)value, 0, denom, 0.f, SDL_EVENT_MOUSE_RAW_SCROLL);
+        }
+    }
 
     if (input->pointer_focus) {
+        float x, y;
+
         switch (a) {
         case WL_POINTER_AXIS_VERTICAL_SCROLL:
             x = 0;
@@ -797,14 +816,25 @@ static void pointer_handle_axis_common_v1(struct SDL_WaylandInput *input,
         x /= WAYLAND_WHEEL_AXIS_UNIT;
         y /= WAYLAND_WHEEL_AXIS_UNIT;
 
-        SDL_SendMouseWheel(Wayland_GetPointerTimestamp(input, time), window->sdlwindow, input->pointer_id, x, y, SDL_MOUSEWHEEL_NORMAL);
+        SDL_SendMouseWheel(timestamp, window->sdlwindow, input->pointer_id, x, y, SDL_MOUSEWHEEL_NORMAL);
     }
 }
 
 static void pointer_handle_axis_common(struct SDL_WaylandInput *input, enum SDL_WaylandAxisEvent type,
                                        uint32_t axis, wl_fixed_t value)
 {
-    enum wl_pointer_axis a = axis;
+    const enum wl_pointer_axis a = axis;
+
+    if (SDL_EventEnabled(SDL_EVENT_MOUSE_RAW_SCROLL)) {
+        // wl_fixed_t is a 24.8 signed fixed-point number which needs to be converted by dividing by 256
+        const float denom = (float)((type == AXIS_EVENT_VALUE120 ? 120 : WAYLAND_WHEEL_AXIS_UNIT) * 256);
+
+        if (a == WL_POINTER_AXIS_VERTICAL_SCROLL) {
+            SDL_SendRawMouseAxis(input->pointer_curr_axis_info.timestamp_ns, input->pointer_id, 0, (int)value, 0.f, denom, SDL_EVENT_MOUSE_RAW_SCROLL);
+        } else {
+            SDL_SendRawMouseAxis(input->pointer_curr_axis_info.timestamp_ns, input->pointer_id, (int)value, 0, denom, 0.f, SDL_EVENT_MOUSE_RAW_SCROLL);
+        }
+    }
 
     if (input->pointer_focus) {
         switch (a) {
@@ -992,6 +1022,128 @@ static const struct wl_pointer_listener pointer_listener = {
     pointer_handle_axis_relative_direction // Version 9
 };
 
+static void relative_pointer_handle_relative_motion(void *data,
+                                                    struct zwp_relative_pointer_v1 *pointer,
+                                                    uint32_t time_hi,
+                                                    uint32_t time_lo,
+                                                    wl_fixed_t dx_w,
+                                                    wl_fixed_t dy_w,
+                                                    wl_fixed_t dx_unaccel_w,
+                                                    wl_fixed_t dy_unaccel_w)
+{
+    struct SDL_WaylandInput *input = data;
+    SDL_VideoData *d = input->display;
+    SDL_WindowData *window = input->pointer_focus;
+
+    // Relative pointer event times are in microsecond granularity.
+    const Uint64 timestamp = Wayland_GetEventTimestamp(SDL_US_TO_NS(((Uint64)time_hi << 32) | (Uint64)time_lo));
+
+    if (SDL_EventEnabled(SDL_EVENT_MOUSE_RAW_MOTION)) {
+        // wl_fixed_t is a 24.8 signed fixed-point number which needs to be converted by dividing by 256
+        SDL_SendRawMouseAxis(timestamp, input->pointer_id, (int)dx_unaccel_w, (int)dy_unaccel_w, 256.0f, 256.0f, SDL_EVENT_MOUSE_RAW_MOTION);
+    }
+
+    if (input->pointer_focus && d->relative_mouse_mode) {
+        double dx;
+        double dy;
+        if (!SDL_GetMouse()->enable_relative_system_scale) {
+            dx = wl_fixed_to_double(dx_unaccel_w);
+            dy = wl_fixed_to_double(dy_unaccel_w);
+        } else {
+            dx = wl_fixed_to_double(dx_w);
+            dy = wl_fixed_to_double(dy_w);
+        }
+
+        SDL_SendMouseMotion(timestamp, window->sdlwindow, input->pointer_id, true, (float)dx, (float)dy);
+    }
+}
+
+static const struct zwp_relative_pointer_v1_listener relative_pointer_listener = {
+    relative_pointer_handle_relative_motion,
+};
+
+static void locked_pointer_locked(void *data,
+                                  struct zwp_locked_pointer_v1 *locked_pointer)
+{
+}
+
+static void locked_pointer_unlocked(void *data,
+                                    struct zwp_locked_pointer_v1 *locked_pointer)
+{
+}
+
+static const struct zwp_locked_pointer_v1_listener locked_pointer_listener = {
+    locked_pointer_locked,
+    locked_pointer_unlocked,
+};
+
+bool Wayland_input_lock_pointer(struct SDL_WaylandInput *input, SDL_Window *window)
+{
+    SDL_WindowData *w = window->internal;
+    SDL_VideoData *d = input->display;
+
+    if (!d->pointer_constraints || !input->pointer) {
+        return false;
+    }
+
+    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 true;
+}
+
+bool Wayland_input_unlock_pointer(struct SDL_WaylandInput *input, SDL_Window *window)
+{
+    SDL_WindowData *w = window->internal;
+
+    if (w->locked_pointer) {
+        zwp_locked_pointer_v1_destroy(w->locked_pointer);
+        w->locked_pointer = NULL;
+    }
+
+    // Restore existing pointer confinement.
+    Wayland_input_confine_pointer(input, window);
+
+    return true;
+}
+
+static void pointer_confine_destroy(SDL_Window *window)
+{
+    SDL_WindowData *w = window->internal;
+    if (w->confined_pointer) {
+        zwp_confined_pointer_v1_destroy(w->confined_pointer);
+        w->confined_pointer = NULL;
+    }
+}
+
+static void confined_pointer_confined(void *data,
+                                      struct zwp_confined_pointer_v1 *confined_pointer)
+{
+}
+
+static void confined_pointer_unconfined(void *data,
+                                        struct zwp_confined_pointer_v1 *confined_pointer)
+{
+}
+
+static const struct zwp_confined_pointer_v1_listener confined_pointer_listener = {
+    confined_pointer_confined,
+    confined_pointer_unconfined,
+};
+
 static void touch_handler_down(void *data, struct wl_touch *touch, uint32_t serial,
                                uint32_t timestamp, struct wl_surface *surface,
                                int id, wl_fixed_t fx, wl_fixed_t fy)
@@ -1807,6 +1959,13 @@ static void seat_handle_capabilities(void *data, struct wl_seat *seat,
         wl_pointer_set_user_data(input->pointer, input);
         wl_pointer_add_listener(input->pointer, &pointer_listener, input);
 
+        if (input->display->relative_pointer_manager) {
+            input->relative_pointer = zwp_relative_pointer_manager_v1_get_relative_pointer(input->display->relative_pointer_manager, input->pointer);
+            zwp_relative_pointer_v1_add_listener(input->relative_pointer,
+                                                 &relative_pointer_listener,
+                                                 input);
+        }
+
         input->pointer_id = SDL_GetNextObjectID();
         SDL_AddMouse(input->pointer_id, WAYLAND_DEFAULT_POINTER_NAME, true);
     } else if (!(caps & WL_SEAT_CAPABILITY_POINTER) && input->pointer) {
@@ -2970,6 +3129,10 @@ void Wayland_display_destroy_input(SDL_VideoData *d)
         }
     }
 
+    if (input->relative_pointer) {
+        zwp_relative_pointer_v1_destroy(input->relative_pointer);
+    }
+
     if (input->cursor_shape) {
         wp_cursor_shape_device_v1_destroy(input->cursor_shape);
     }
@@ -3030,114 +3193,11 @@ void Wayland_display_destroy_input(SDL_VideoData *d)
     d->input = NULL;
 }
 
-static void relative_pointer_handle_relative_motion(void *data,
-                                                    struct zwp_relative_pointer_v1 *pointer,
-                                                    uint32_t time_hi,
-                                                    uint32_t time_lo,
-                                                    wl_fixed_t dx_w,
-                                                    wl_fixed_t dy_w,
-                                                    wl_fixed_t dx_unaccel_w,
-                                                    wl_fixed_t dy_unaccel_w)
-{
-    struct SDL_WaylandInput *input = data;
-    SDL_VideoData *d = input->display;
-    SDL_WindowData *window = input->pointer_focus;
-    double dx;
-    double dy;
-
-    // Relative pointer event times are in microsecond granularity.
-    const Uint64 timestamp = SDL_US_TO_NS(((Uint64)time_hi << 32) | (Uint64)time_lo);
-
-    if (!SDL_GetMouse()->enable_relative_system_scale) {
-        dx = wl_fixed_to_double(dx_unaccel_w);
-        dy = wl_fixed_to_double(dy_unaccel_w);
-    } else {
-        dx = wl_fixed_to_double(dx_w);
-        dy = wl_fixed_to_double(dy_w);
-    }
-
-    if (input->pointer_focus && d->relative_mouse_mode) {
-        SDL_SendMouseMotion(Wayland_GetEventTimestamp(timestamp), window->sdlwindow, input->pointer_id, true, (float)dx, (float)dy);
-    }
-}
-
-static const struct zwp_relative_pointer_v1_listener relative_pointer_listener = {
-    relative_pointer_handle_relative_motion,
-};
-
-static void locked_pointer_locked(void *data,
-                                  struct zwp_locked_pointer_v1 *locked_pointer)
-{
-}
-
-static void locked_pointer_unlocked(void *data,
-                                    struct zwp_locked_pointer_v1 *locked_pointer)
-{
-}
-
-static const struct zwp_locked_pointer_v1_listener locked_pointer_listener = {
-    locked_pointer_locked,
-    locked_pointer_unlocked,
-};
-
-bool Wayland_input_lock_pointer(struct SDL_WaylandInput *input, SDL_Window *window)
-{
-    SDL_WindowData *w = window->internal;
-    SDL_VideoData *d = input->display;
-
-    if (!d->pointer_constraints || !input->pointer) {
-        return false;
-    }
-
-    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 true;
-}
-
-bool Wayland_input_unlock_pointer(struct SDL_WaylandInput *input, SDL_Window *window)
-{
-    SDL_WindowData *w = window->internal;
-
-    if (w->locked_pointer) {
-        zwp_locked_pointer_v1_destroy(w->locked_pointer);
-        w->locked_pointer = NULL;
-    }
-
-    // Restore existing pointer confinement.
-    Wayland_input_confine_pointer(input, window);
-
-    return true;
-}
-
-static void pointer_confine_destroy(SDL_Window *window)
-{
-    SDL_WindowData *w = window->internal;
-    if (w->confined_pointer) {
-        zwp_confined_pointer_v1_destroy(w->confined_pointer);
-        w->confined_pointer = NULL;
-    }
-}
-
 bool Wayland_input_enable_relative_pointer(struct SDL_WaylandInput *input)
 {
     SDL_VideoDevice *vd = SDL_GetVideoDevice();
     SDL_VideoData *d = input->display;
     SDL_Window *window;
-    struct zwp_relative_pointer_v1 *relative_pointer;
 
     if (!d->relative_pointer_manager) {
         return false;
@@ -3158,14 +3218,6 @@ bool Wayland_input_enable_relative_pointer(struct SDL_WaylandInput *input)
         pointer_confine_destroy(window);
     }
 
-    if (!input->relative_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);
-        input->relative_pointer = relative_pointer;
-    }
-
     for (window = vd->windows; window; window = window->next) {
         Wayland_input_lock_pointer(input, window);
     }
@@ -3185,11 +3237,6 @@ bool Wayland_input_disable_relative_pointer(struct SDL_WaylandInput *input)
         Wayland_input_unlock_pointer(input, window);
     }
 
-    if (input->relative_pointer) {
-        zwp_relative_pointer_v1_destroy(input->relative_pointer);
-        input->relative_pointer = NULL;
-    }
-
     d->relative_mouse_mode = 0;
 
     for (window = vd->windows; window; window = window->next) {
@@ -3199,26 +3246,10 @@ bool Wayland_input_disable_relative_pointer(struct SDL_WaylandInput *input)
     return true;
 }
 
-static void confined_pointer_confined(void *data,
-                                      struct zwp_confined_pointer_v1 *confined_pointer)
-{
-}
-
-static void confined_pointer_unconfined(void *data,
-                                        struct zwp_confined_pointer_v1 *confined_pointer)
-{
-}
-
-static const struct zwp_confined_pointer_v1_listener confined_pointer_listener = {
-    confined_pointer_confined,
-    confined_pointer_unconfined,
-};
-
 bool Wayland_input_confine_pointer(struct SDL_WaylandInput *input, SDL_Window *window)
 {
     SDL_WindowData *w = window->internal;
     SDL_VideoData *d = input->display;
-    struct zwp_confined_pointer_v1 *confined_pointer;
     struct wl_region *confine_rect;
 
     if (!d->pointer_constraints) {
@@ -3264,7 +3295,7 @@ bool Wayland_input_confine_pointer(struct SDL_WaylandInput *input, SDL_Window *w
                       scaled_mouse_rect.h);
     }
 
-    confined_pointer =
+    struct zwp_confined_pointer_v1 *confined_pointer =
         zwp_pointer_constraints_v1_confine_pointer(d->pointer_constraints,
                                                    w->surface,
                                                    input->pointer,
diff --git a/src/video/wayland/SDL_waylandevents_c.h b/src/video/wayland/SDL_waylandevents_c.h
index c5741a5e20535..b67e7e3918a6d 100644
--- a/src/video/wayland/SDL_waylandevents_c.h
+++ b/src/video/wayland/SDL_waylandevents_c.h
@@ -81,8 +81,8 @@ struct SDL_WaylandInput
     SDL_WindowData *pointer_focus;
     SDL_WindowData *keyboard_focus;
     SDL_CursorData *current_cursor;
-    Uint32 keyboard_id;
-    Uint32 pointer_id;
+    SDL_KeyboardID keyboard_id;
+    SDL_MouseID pointer_id;
     uint32_t pointer_enter_serial;
 
     // High-resolution event timestamps