SDL: Updated raw input events to match SDL style

From c44fa5bb07f7f859ded7bc7bcb037f9976ec147b Mon Sep 17 00:00:00 2001
From: Sam Lantinga <[EMAIL REDACTED]>
Date: Thu, 19 Dec 2024 16:18:05 -0800
Subject: [PATCH] Updated raw input events to match SDL style

Also added raw keyboard events, and implemented raw input events on iOS, OpenBSD console, Linux console, and X11
---
 include/SDL3/SDL_events.h                |  92 +++++++++-----
 src/core/linux/SDL_evdev.c               |  36 ++++--
 src/core/openbsd/SDL_wscons_kbd.c        |  40 ++++--
 src/core/openbsd/SDL_wscons_mouse.c      |  56 +++++----
 src/events/SDL_categories.c              |   6 +
 src/events/SDL_events.c                  |  53 ++++++--
 src/events/SDL_keyboard.c                |  16 +++
 src/events/SDL_keyboard_c.h              |   3 +
 src/events/SDL_mouse.c                   |  73 ++++++-----
 src/events/SDL_mouse_c.h                 |  15 ++-
 src/render/SDL_render.c                  |   2 +
 src/test/SDL_test_common.c               |  18 +++
 src/video/SDL_sysvideo.h                 |   3 -
 src/video/SDL_video.c                    |   7 --
 src/video/openvr/SDL_openvrvideo.c       |   6 +-
 src/video/uikit/SDL_uikitevents.m        |  27 ++++-
 src/video/wayland/SDL_waylandevents.c    | 101 ++++++++--------
 src/video/windows/SDL_windowsevents.c    | 148 ++++++++++++-----------
 src/video/windows/SDL_windowsgameinput.c |  14 +++
 src/video/windows/SDL_windowsrawinput.c  |  17 +--
 src/video/windows/SDL_windowsvideo.c     |   1 -
 src/video/x11/SDL_x11events.c            |  57 ++++++---
 src/video/x11/SDL_x11events.h            |   5 +-
 src/video/x11/SDL_x11xinput2.c           |  23 ++--
 test/testrelative.c                      |  10 +-
 25 files changed, 516 insertions(+), 313 deletions(-)

diff --git a/include/SDL3/SDL_events.h b/include/SDL3/SDL_events.h
index 4cc18cd641ea6..27da4b4eaf9c7 100644
--- a/include/SDL3/SDL_events.h
+++ b/include/SDL3/SDL_events.h
@@ -174,6 +174,8 @@ typedef enum SDL_EventType
     SDL_EVENT_KEYBOARD_ADDED,          /**< A new keyboard has been inserted into the system */
     SDL_EVENT_KEYBOARD_REMOVED,        /**< A keyboard has been removed */
     SDL_EVENT_TEXT_EDITING_CANDIDATES, /**< Keyboard text editing candidates */
+    SDL_EVENT_RAW_KEY_DOWN,            /**< Key pressed (raw key press) */
+    SDL_EVENT_RAW_KEY_UP,              /**< Key released (raw key release) */
 
     /* Mouse events */
     SDL_EVENT_MOUSE_MOTION    = 0x400, /**< Mouse moved */
@@ -182,9 +184,10 @@ typedef enum SDL_EventType
     SDL_EVENT_MOUSE_WHEEL,             /**< Mouse wheel motion */
     SDL_EVENT_MOUSE_ADDED,             /**< A new mouse has been inserted into the system */
     SDL_EVENT_MOUSE_REMOVED,           /**< A mouse has been removed */
-    SDL_EVENT_MOUSE_RAW_MOTION,        /**< Mouse moved (raw motion deltas) */
-    SDL_EVENT_MOUSE_RAW_BUTTON,        /**< Mouse click (raw button delta) */
-    SDL_EVENT_MOUSE_RAW_SCROLL,         /**< Mouse wheel (raw scroll deltas) */
+    SDL_EVENT_RAW_MOUSE_MOTION,        /**< Mouse moved (raw motion deltas) */
+    SDL_EVENT_RAW_MOUSE_BUTTON_DOWN,   /**< Mouse button pressed (raw button press) */
+    SDL_EVENT_RAW_MOUSE_BUTTON_UP,     /**< Mouse button released (raw button release) */
+    SDL_EVENT_RAW_MOUSE_WHEEL,         /**< Mouse wheel motion (raw wheel deltas) */
 
     /* Joystick events */
     SDL_EVENT_JOYSTICK_AXIS_MOTION  = 0x600, /**< Joystick axis motion */
@@ -361,10 +364,26 @@ typedef struct SDL_KeyboardEvent
     SDL_Keycode key;        /**< SDL virtual key code */
     SDL_Keymod mod;         /**< current key modifiers */
     Uint16 raw;             /**< The platform dependent scancode for this event */
-    bool down;          /**< true if the key is pressed */
-    bool repeat;        /**< true if this is a key repeat */
+    bool down;              /**< true if the key is pressed */
+    bool repeat;            /**< true if this is a key repeat */
 } SDL_KeyboardEvent;
 
+/**
+ * Raw keyboard button event structure (event.raw_key.*)
+ *
+ * \since This struct is available since SDL 3.1.8.
+ */
+typedef struct SDL_RawKeyboardEvent
+{
+    SDL_EventType type;     /**< SDL_EVENT_RAW_KEY_DOWN or SDL_EVENT_RAW_KEY_UP */
+    Uint32 reserved;
+    Uint64 timestamp;       /**< In nanoseconds, populated using SDL_GetTicksNS() */
+    SDL_KeyboardID which;   /**< The keyboard instance id */
+    SDL_Scancode scancode;  /**< SDL physical key code */
+    Uint16 raw;             /**< The platform dependent scancode for this event */
+    bool down;              /**< true if the key is pressed */
+} SDL_RawKeyboardEvent;
+
 /**
  * Keyboard text editing event structure (event.edit.*)
  *
@@ -458,21 +477,21 @@ typedef struct SDL_MouseMotionEvent
 } SDL_MouseMotionEvent;
 
 /**
- * Mouse raw motion and wheel event structure (event.maxis.*)
+ * Raw mouse motion event structure (event.raw_motion.*)
  *
- * \since This struct is available since SDL 3.0.0.
+ * \since This struct is available since SDL 3.1.8.
  */
-typedef struct SDL_MouseRawAxisEvent
+typedef struct SDL_RawMouseMotionEvent
 {
-    SDL_EventType type; /**< SDL_EVENT_MOUSE_RAW_MOTION or SDL_EVENT_MOUSE_RAW_SCROLL */
+    SDL_EventType type; /**< SDL_EVENT_RAW_MOUSE_MOTION */
     Uint32 reserved;
     Uint64 timestamp;   /**< In nanoseconds, populated using SDL_GetTicksNS() */
-    SDL_MouseID which;  /**< The mouse instance id, SDL_TOUCH_MOUSEID, or SDL_PEN_MOUSEID */
-    int dx;             /**< The axis delta value in the X direction */
-    int dy;             /**< The axis delta value in the Y direction */
-    float ux;           /**< The denominator unit in the X direction */
-    float uy;           /**< The denominator unit in the Y direction */
-} SDL_MouseRawAxisEvent;
+    SDL_MouseID which;  /**< The mouse instance id */
+    Sint32 dx;          /**< X axis delta */
+    Sint32 dy;          /**< Y axis delta */
+    float scale_x;      /**< X value scale to approximate desktop acceleration */
+    float scale_y;      /**< Y value scale to approximate desktop acceleration */
+} SDL_RawMouseMotionEvent;
 
 /**
  * Mouse button event structure (event.button.*)
@@ -485,9 +504,9 @@ typedef struct SDL_MouseButtonEvent
     Uint32 reserved;
     Uint64 timestamp;   /**< In nanoseconds, populated using SDL_GetTicksNS() */
     SDL_WindowID windowID; /**< The window with mouse focus, if any */
-    SDL_MouseID which;  /**< The mouse instance id, SDL_TOUCH_MOUSEID */
+    SDL_MouseID which;  /**< The mouse instance id or SDL_TOUCH_MOUSEID */
     Uint8 button;       /**< The mouse button index */
-    bool down;      /**< true if the button is pressed */
+    bool down;          /**< true if the button is pressed */
     Uint8 clicks;       /**< 1 for single-click, 2 for double-click, etc. */
     Uint8 padding;
     float x;            /**< X coordinate, relative to window */
@@ -495,19 +514,19 @@ typedef struct SDL_MouseButtonEvent
 } SDL_MouseButtonEvent;
 
 /**
- * Mouse raw button event structure (event.mbutton.*)
+ * Raw mouse button event structure (event.raw_button.*)
  *
- * \since This struct is available since SDL 3.0.0.
+ * \since This struct is available since SDL 3.1.8.
  */
-typedef struct SDL_MouseRawButtonEvent
+typedef struct SDL_RawMouseButtonEvent
 {
-    SDL_EventType type; /**< SDL_EVENT_MOUSE_RAW_BUTTON */
+    SDL_EventType type; /**< SDL_EVENT_RAW_MOUSE_BUTTON_DOWN or SDL_EVENT_RAW_MOUSE_BUTTON_UP */
     Uint32 reserved;
     Uint64 timestamp;   /**< In nanoseconds, populated using SDL_GetTicksNS() */
-    SDL_MouseID which;  /**< The mouse instance id, SDL_TOUCH_MOUSEID, or SDL_PEN_MOUSEID */
+    SDL_MouseID which;  /**< The mouse instance id */
     Uint8 button;       /**< The mouse button index */
-    Uint8 state;        /**< SDL_PRESSED or SDL_RELEASED */
-} SDL_MouseRawButtonEvent;
+    bool down;          /**< true if the button is pressed */
+} SDL_RawMouseButtonEvent;
 
 /**
  * Mouse wheel event structure (event.wheel.*)
@@ -520,7 +539,7 @@ typedef struct SDL_MouseWheelEvent
     Uint32 reserved;
     Uint64 timestamp;   /**< In nanoseconds, populated using SDL_GetTicksNS() */
     SDL_WindowID windowID; /**< The window with mouse focus, if any */
-    SDL_MouseID which;  /**< The mouse instance id, SDL_TOUCH_MOUSEID */
+    SDL_MouseID which;  /**< The mouse instance id */
     float x;            /**< The amount scrolled horizontally, positive to the right and negative to the left */
     float y;            /**< The amount scrolled vertically, positive away from the user and negative toward the user */
     SDL_MouseWheelDirection direction; /**< Set to one of the SDL_MOUSEWHEEL_* defines. When FLIPPED the values in X and Y will be opposite. Multiply by -1 to change them back */
@@ -528,6 +547,23 @@ typedef struct SDL_MouseWheelEvent
     float mouse_y;      /**< Y coordinate, relative to window */
 } SDL_MouseWheelEvent;
 
+/**
+ * Raw mouse wheel event structure (event.raw_wheel.*)
+ *
+ * \since This struct is available since SDL 3.1.3.
+ */
+typedef struct SDL_RawMouseWheelEvent
+{
+    SDL_EventType type; /**< SDL_EVENT_RAW_MOUSE_WHEEL */
+    Uint32 reserved;
+    Uint64 timestamp;   /**< In nanoseconds, populated using SDL_GetTicksNS() */
+    SDL_MouseID which;  /**< The mouse instance id */
+    Sint32 dx;          /**< X axis delta, positive to the right and negative to the left */
+    Sint32 dy;          /**< Y axis delta, positive away from the user and negative toward the user */
+    float scale_x;      /**< X value scale to convert to logical scroll units */
+    float scale_y;      /**< Y value scale to convert to logical scroll units */
+} SDL_RawMouseWheelEvent;
+
 /**
  * Joystick axis motion event structure (event.jaxis.*)
  *
@@ -1025,15 +1061,17 @@ typedef union SDL_Event
     SDL_WindowEvent window;                 /**< Window event data */
     SDL_KeyboardDeviceEvent kdevice;        /**< Keyboard device change event data */
     SDL_KeyboardEvent key;                  /**< Keyboard event data */
+    SDL_RawKeyboardEvent raw_key;           /**< Raw keyboard event data */
     SDL_TextEditingEvent edit;              /**< Text editing event data */
     SDL_TextEditingCandidatesEvent edit_candidates; /**< Text editing candidates event data */
     SDL_TextInputEvent text;                /**< Text input event data */
     SDL_MouseDeviceEvent mdevice;           /**< Mouse device change event data */
     SDL_MouseMotionEvent motion;            /**< Mouse motion event data */
+    SDL_RawMouseMotionEvent raw_motion;     /**< Raw mouse motion event data */
     SDL_MouseButtonEvent button;            /**< Mouse button event data */
+    SDL_RawMouseButtonEvent raw_button;     /**< Raw mouse button event data */
     SDL_MouseWheelEvent wheel;              /**< Mouse wheel event data */
-    SDL_MouseRawAxisEvent maxis;            /**< Mouse raw axis event data (motion or wheel deltas) */
-    SDL_MouseRawButtonEvent mbutton;        /**< Mouse raw button event data */
+    SDL_RawMouseWheelEvent raw_wheel;       /**< Raw mouse wheel event data */
     SDL_JoyDeviceEvent jdevice;             /**< Joystick device change event data */
     SDL_JoyAxisEvent jaxis;                 /**< Joystick axis event data */
     SDL_JoyBallEvent jball;                 /**< Joystick ball event data */
diff --git a/src/core/linux/SDL_evdev.c b/src/core/linux/SDL_evdev.c
index e655eea6ef0f1..fce82ebc3cd70 100644
--- a/src/core/linux/SDL_evdev.c
+++ b/src/core/linux/SDL_evdev.c
@@ -346,8 +346,10 @@ void SDL_EVDEV_Poll(void)
                 switch (event->type) {
                 case EV_KEY:
                     if (event->code >= BTN_MOUSE && event->code < BTN_MOUSE + SDL_arraysize(EVDEV_MouseButtons)) {
+                        Uint64 timestamp = SDL_EVDEV_GetEventTimestamp(event);
                         mouse_button = event->code - BTN_MOUSE;
-                        SDL_SendMouseButton(SDL_EVDEV_GetEventTimestamp(event), mouse->focus, (SDL_MouseID)item->fd, EVDEV_MouseButtons[mouse_button], (event->value != 0));
+                        SDL_SendRawMouseButton(timestamp, (SDL_MouseID)item->fd, EVDEV_MouseButtons[mouse_button], (event->value != 0));
+                        SDL_SendMouseButton(timestamp, mouse->focus, (SDL_MouseID)item->fd, EVDEV_MouseButtons[mouse_button], (event->value != 0));
                         break;
                     }
 
@@ -367,13 +369,18 @@ void SDL_EVDEV_Poll(void)
                     }
 
                     // Probably keyboard
-                    scancode = SDL_EVDEV_translate_keycode(event->code);
-                    if (event->value == 0) {
-                        SDL_SendKeyboardKey(SDL_EVDEV_GetEventTimestamp(event), (SDL_KeyboardID)item->fd, event->code, scancode, false);
-                    } else if (event->value == 1 || event->value == 2 /* key repeated */) {
-                        SDL_SendKeyboardKey(SDL_EVDEV_GetEventTimestamp(event), (SDL_KeyboardID)item->fd, event->code, scancode, true);
+                    {
+                        Uint64 timestamp = SDL_EVDEV_GetEventTimestamp(event);
+                        scancode = SDL_EVDEV_translate_keycode(event->code);
+                        if (event->value == 0) {
+                            SDL_SendRawKeyboardKey(timestamp, (SDL_KeyboardID)item->fd, event->code, scancode, false);
+                            SDL_SendKeyboardKey(timestamp, (SDL_KeyboardID)item->fd, event->code, scancode, false);
+                        } else if (event->value == 1 || event->value == 2 /* key repeated */) {
+                            SDL_SendRawKeyboardKey(timestamp, (SDL_KeyboardID)item->fd, event->code, scancode, true);
+                            SDL_SendKeyboardKey(timestamp, (SDL_KeyboardID)item->fd, event->code, scancode, true);
+                        }
+                        SDL_EVDEV_kbd_keycode(_this->kbd, event->code, event->value);
                     }
-                    SDL_EVDEV_kbd_keycode(_this->kbd, event->code, event->value);
                     break;
                 case EV_ABS:
                     switch (event->code) {
@@ -485,7 +492,9 @@ void SDL_EVDEV_Poll(void)
                         // Send mouse axis changes together to ensure consistency and reduce event processing overhead
                         if (item->relative_mouse) {
                             if (item->mouse_x != 0 || item->mouse_y != 0) {
-                                SDL_SendMouseMotion(SDL_EVDEV_GetEventTimestamp(event), mouse->focus, (SDL_MouseID)item->fd, item->relative_mouse, (float)item->mouse_x, (float)item->mouse_y);
+                                Uint64 timestamp = SDL_EVDEV_GetEventTimestamp(event);
+                                SDL_SendRawMouseMotion(timestamp, (SDL_MouseID)item->fd, item->mouse_x, item->mouse_y, 1.0f, 1.0f);
+                                SDL_SendMouseMotion(timestamp, mouse->focus, (SDL_MouseID)item->fd, item->relative_mouse, (float)item->mouse_x, (float)item->mouse_y);
                                 item->mouse_x = item->mouse_y = 0;
                             }
                         } else if (item->range_x > 0 && item->range_y > 0) {
@@ -508,10 +517,15 @@ void SDL_EVDEV_Poll(void)
                         }
 
                         if (item->mouse_wheel != 0 || item->mouse_hwheel != 0) {
-                            SDL_SendMouseWheel(SDL_EVDEV_GetEventTimestamp(event),
+                            Uint64 timestamp = SDL_EVDEV_GetEventTimestamp(event);
+                            const float scale = (item->high_res_hwheel ? 1.0f / 120.0f : 1.0f);
+                            SDL_SendRawMouseWheel(timestamp,
+                                                  (SDL_MouseID)item->fd,
+                                                  item->mouse_hwheel, item->mouse_wheel, scale, scale);
+                            SDL_SendMouseWheel(timestamp,
                                                mouse->focus, (SDL_MouseID)item->fd,
-                                               item->mouse_hwheel / (item->high_res_hwheel ? 120.0f : 1.0f),
-                                               item->mouse_wheel / (item->high_res_wheel ? 120.0f : 1.0f),
+                                               item->mouse_hwheel * scale,
+                                               item->mouse_wheel * scale,
                                                SDL_MOUSEWHEEL_NORMAL);
                             item->mouse_wheel = item->mouse_hwheel = 0;
                         }
diff --git a/src/core/openbsd/SDL_wscons_kbd.c b/src/core/openbsd/SDL_wscons_kbd.c
index 79566ef5b0bd7..b2ce943d21320 100644
--- a/src/core/openbsd/SDL_wscons_kbd.c
+++ b/src/core/openbsd/SDL_wscons_kbd.c
@@ -550,7 +550,7 @@ static void Translate_to_text(SDL_WSCONS_input_data *input, keysym_t ksym)
     }
 }
 
-static void Translate_to_keycode(SDL_WSCONS_input_data *input, int type, keysym_t ksym)
+static void Translate_to_keycode(SDL_WSCONS_input_data *input, int type, keysym_t ksym, Uint64 timestamp)
 {
     struct wscons_keymap keyDesc = input->keymap.map[ksym];
     keysym_t *group = &keyDesc.group1[KS_GROUP(keyDesc.group1[0]) == KS_GROUP_Keypad && IS_NUMLOCK_ON ? !IS_SHIFT_HELD : 0];
@@ -560,22 +560,32 @@ static void Translate_to_keycode(SDL_WSCONS_input_data *input, int type, keysym_
     switch (keyDesc.command) {
     case KS_Cmd_ScrollBack:
     {
-        SDL_SendKeyboardKey(0, input->keyboardID, 0, SDL_SCANCODE_PAGEUP, (type == WSCONS_EVENT_KEY_DOWN));
+        SDL_SendKeyboardKey(timestamp, input->keyboardID, 0, SDL_SCANCODE_PAGEUP, (type == WSCONS_EVENT_KEY_DOWN));
         return;
     }
     case KS_Cmd_ScrollFwd:
     {
-        SDL_SendKeyboardKey(0, input->keyboardID, 0, SDL_SCANCODE_PAGEDOWN, (type == WSCONS_EVENT_KEY_DOWN));
+        SDL_SendKeyboardKey(timestamp, input->keyboardID, 0, SDL_SCANCODE_PAGEDOWN, (type == WSCONS_EVENT_KEY_DOWN));
         return;
     }
+    default:
+        break;
     }
-    for (i = 0; i < sizeof(conversion_table) / sizeof(struct wscons_keycode_to_SDL); i++) {
+    for (i = 0; i < SDL_arraysize(conversion_table); i++) {
         if (conversion_table[i].sourcekey == group[0]) {
-            SDL_SendKeyboardKey(0, input->keyboardID, group[0], conversion_table[i].targetKey, (type == WSCONS_EVENT_KEY_DOWN));
+            SDL_SendRawKeyboardKey(timestamp, input->keyboardID, group[0], conversion_table[i].targetKey, (type == WSCONS_EVENT_KEY_DOWN));
+            SDL_SendKeyboardKey(timestamp, input->keyboardID, group[0], conversion_table[i].targetKey, (type == WSCONS_EVENT_KEY_DOWN));
             return;
         }
     }
-    SDL_SendKeyboardKey(0, input->keyboardID, group[0], SDL_SCANCODE_UNKNOWN, (type == WSCONS_EVENT_KEY_DOWN));
+    SDL_SendRawKeyboardKey(timestamp, input->keyboardID, group[0], SDL_SCANCODE_UNKNOWN, (type == WSCONS_EVENT_KEY_DOWN));
+    SDL_SendKeyboardKey(timestamp, input->keyboardID, group[0], SDL_SCANCODE_UNKNOWN, (type == WSCONS_EVENT_KEY_DOWN));
+}
+
+static Uint64 GetEventTimestamp(struct timespec *time)
+{
+    // FIXME: Get the event time in the SDL tick time base
+    return SDL_GetTicksNS();
 }
 
 static void updateKeyboard(SDL_WSCONS_input_data *input)
@@ -592,6 +602,7 @@ static void updateKeyboard(SDL_WSCONS_input_data *input)
     if ((n = read(input->fd, events, sizeof(events))) > 0) {
         n /= sizeof(struct wscons_event);
         for (i = 0; i < n; i++) {
+            Uint64 timestamp = GetEventTimestamp(&events[i].time);
             type = events[i].type;
             switch (type) {
             case WSCONS_EVENT_KEY_DOWN:
@@ -809,15 +820,20 @@ static void updateKeyboard(SDL_WSCONS_input_data *input)
             } break;
             case WSCONS_EVENT_ALL_KEYS_UP:
                 for (i = 0; i < SDL_SCANCODE_COUNT; i++) {
-                    SDL_SendKeyboardKey(0, input->keyboardID, 0, (SDL_Scancode)i, false);
+                    SDL_SendRawKeyboardKey(timestamp, input->keyboardID, 0, (SDL_Scancode)i, false);
+                    SDL_SendKeyboardKey(timestamp, input->keyboardID, 0, (SDL_Scancode)i, false);
                 }
                 break;
+            default:
+                break;
             }
 
-            if (input->type == WSKBD_TYPE_USB && events[i].value <= 0xE7)
-                SDL_SendKeyboardKey(0, input->keyboardID, 0, (SDL_Scancode)events[i].value, (type == WSCONS_EVENT_KEY_DOWN));
-            else
-                Translate_to_keycode(input, type, events[i].value);
+            if (input->type == WSKBD_TYPE_USB && events[i].value <= 0xE7) {
+                SDL_SendRawKeyboardKey(timestamp, input->keyboardID, 0, (SDL_Scancode)events[i].value, (type == WSCONS_EVENT_KEY_DOWN));
+                SDL_SendKeyboardKey(timestamp, input->keyboardID, 0, (SDL_Scancode)events[i].value, (type == WSCONS_EVENT_KEY_DOWN));
+            } else {
+                Translate_to_keycode(input, type, events[i].value, timestamp);
+            }
 
             if (type == WSCONS_EVENT_KEY_UP) {
                 continue;
@@ -910,7 +926,7 @@ static void updateKeyboard(SDL_WSCONS_input_data *input)
                 }
                 if (IS_ALT_HELD) {
                     if (input->encoding & KB_METAESC) {
-                        Translate_to_keycode(input, WSCONS_EVENT_KEY_DOWN, KS_Escape);
+                        Translate_to_keycode(input, WSCONS_EVENT_KEY_DOWN, KS_Escape, 0);
                         Translate_to_text(input, result);
                         continue;
                     } else {
diff --git a/src/core/openbsd/SDL_wscons_mouse.c b/src/core/openbsd/SDL_wscons_mouse.c
index 5ba7a5007bb12..57e4f02fa72b0 100644
--- a/src/core/openbsd/SDL_wscons_mouse.c
+++ b/src/core/openbsd/SDL_wscons_mouse.c
@@ -63,6 +63,12 @@ SDL_WSCONS_mouse_input_data *SDL_WSCONS_Init_Mouse(void)
     return input;
 }
 
+static Uint64 GetEventTimestamp(struct timespec *time)
+{
+    // FIXME: Get the event time in the SDL tick time base
+    return SDL_GetTicksNS();
+}
+
 void updateMouse(SDL_WSCONS_mouse_input_data *input)
 {
     struct wscons_event events[64];
@@ -73,56 +79,48 @@ void updateMouse(SDL_WSCONS_mouse_input_data *input)
         int i;
         n /= sizeof(struct wscons_event);
         for (i = 0; i < n; i++) {
+            Uint64 timestamp = GetEventTimestamp(&events[i].time);
             int type = events[i].type;
             switch (type) {
             case WSCONS_EVENT_MOUSE_DOWN:
-            {
-                switch (events[i].value) {
-                case 0: // Left Mouse Button.
-                    SDL_SendMouseButton(0, mouse->focus, input->mouseID, SDL_BUTTON_LEFT, true);
-                    break;
-                case 1: // Middle Mouse Button.
-                    SDL_SendMouseButton(0, mouse->focus, input->mouseID, SDL_BUTTON_MIDDLE, true);
-                    break;
-                case 2: // Right Mouse Button.
-                    SDL_SendMouseButton(0, mouse->focus, input->mouseID, SDL_BUTTON_RIGHT, true);
-                    break;
-                }
-            } break;
             case WSCONS_EVENT_MOUSE_UP:
             {
-                switch (events[i].value) {
-                case 0: // Left Mouse Button.
-                    SDL_SendMouseButton(0, mouse->focus, input->mouseID, SDL_BUTTON_LEFT, false);
-                    break;
-                case 1: // Middle Mouse Button.
-                    SDL_SendMouseButton(0, mouse->focus, input->mouseID, SDL_BUTTON_MIDDLE, false);
-                    break;
-                case 2: // Right Mouse Button.
-                    SDL_SendMouseButton(0, mouse->focus, input->mouseID, SDL_BUTTON_RIGHT, false);
-                    break;
-                }
-            } break;
+                Uint8 button = SDL_BUTTON_LEFT + events[i].value;
+                bool down = (type == WSCONS_EVENT_MOUSE_DOWN);
+                SDL_SendRawMouseButton(timestamp, input->mouseID, button, down);
+                SDL_SendMouseButton(timestamp, mouse->focus, input->mouseID, button, down);
+                break;
+            }
             case WSCONS_EVENT_MOUSE_DELTA_X:
             {
-                SDL_SendMouseMotion(0, mouse->focus, input->mouseID, 1, (float)events[i].value, 0.0f);
+                const float scale = 1.0f;
+                SDL_SendRawMouseMotion(timestamp, input->mouseID, events[i].value, 0, scale, scale);
+                SDL_SendMouseMotion(timestamp, mouse->focus, input->mouseID, true, (float)events[i].value, 0.0f);
                 break;
             }
             case WSCONS_EVENT_MOUSE_DELTA_Y:
             {
-                SDL_SendMouseMotion(0, mouse->focus, input->mouseID, 1, 0.0f, -(float)events[i].value);
+                const float scale = 1.0f;
+                SDL_SendRawMouseMotion(timestamp, input->mouseID, 0, -events[i].value, scale, scale);
+                SDL_SendMouseMotion(timestamp, mouse->focus, input->mouseID, true, 0.0f, -(float)events[i].value);
                 break;
             }
             case WSCONS_EVENT_MOUSE_DELTA_W:
             {
-                SDL_SendMouseWheel(0, mouse->focus, input->mouseID, events[i].value, 0, SDL_MOUSEWHEEL_NORMAL);
+                const float scale = 1.0f;
+                SDL_SendRawMouseWheel(timestamp, input->mouseID, events[i].value, 0, scale, scale);
+                SDL_SendMouseWheel(timestamp, mouse->focus, input->mouseID, events[i].value, 0, SDL_MOUSEWHEEL_NORMAL);
                 break;
             }
             case WSCONS_EVENT_MOUSE_DELTA_Z:
             {
-                SDL_SendMouseWheel(0, mouse->focus, input->mouseID, 0, -events[i].value, SDL_MOUSEWHEEL_NORMAL);
+                const float scale = 1.0f;
+                SDL_SendRawMouseWheel(timestamp, input->mouseID, 0, -events[i].value, scale, scale);
+                SDL_SendMouseWheel(timestamp, mouse->focus, input->mouseID, 0, -events[i].value, SDL_MOUSEWHEEL_NORMAL);
                 break;
             }
+            default:
+                break;
             }
         }
     }
diff --git a/src/events/SDL_categories.c b/src/events/SDL_categories.c
index 5f0853476f3ac..283c5e0680e9b 100644
--- a/src/events/SDL_categories.c
+++ b/src/events/SDL_categories.c
@@ -62,6 +62,8 @@ SDL_EventCategory SDL_GetEventCategory(Uint32 type)
 
     case SDL_EVENT_KEY_DOWN:
     case SDL_EVENT_KEY_UP:
+    case SDL_EVENT_RAW_KEY_DOWN:
+    case SDL_EVENT_RAW_KEY_UP:
         return SDL_EVENTCATEGORY_KEY;
 
     case SDL_EVENT_TEXT_EDITING:
@@ -78,13 +80,17 @@ SDL_EventCategory SDL_GetEventCategory(Uint32 type)
         return SDL_EVENTCATEGORY_EDIT_CANDIDATES;
 
     case SDL_EVENT_MOUSE_MOTION:
+    case SDL_EVENT_RAW_MOUSE_MOTION:
         return SDL_EVENTCATEGORY_MOTION;
 
     case SDL_EVENT_MOUSE_BUTTON_DOWN:
     case SDL_EVENT_MOUSE_BUTTON_UP:
+    case SDL_EVENT_RAW_MOUSE_BUTTON_DOWN:
+    case SDL_EVENT_RAW_MOUSE_BUTTON_UP:
         return SDL_EVENTCATEGORY_BUTTON;
 
     case SDL_EVENT_MOUSE_WHEEL:
+    case SDL_EVENT_RAW_MOUSE_WHEEL:
         return SDL_EVENTCATEGORY_WHEEL;
 
     case SDL_EVENT_MOUSE_ADDED:
diff --git a/src/events/SDL_events.c b/src/events/SDL_events.c
index c68dbc126cbd7..33d7999e3255c 100644
--- a/src/events/SDL_events.c
+++ b/src/events/SDL_events.c
@@ -390,7 +390,7 @@ static void SDL_LogEvent(const SDL_Event *event)
     // sensor/mouse/pen/finger motion are spammy, ignore these if they aren't demanded.
     if ((SDL_EventLoggingVerbosity < 2) &&
         ((event->type == SDL_EVENT_MOUSE_MOTION) ||
-         (event->type == SDL_EVENT_MOUSE_RAW_MOTION) ||
+         (event->type == SDL_EVENT_RAW_MOUSE_MOTION) ||
          (event->type == SDL_EVENT_FINGER_MOTION) ||
          (event->type == SDL_EVENT_PEN_AXIS) ||
          (event->type == SDL_EVENT_PEN_MOTION) ||
@@ -535,6 +535,19 @@ static void SDL_LogEvent(const SDL_Event *event)
         break;
 #undef PRINT_KEY_EVENT
 
+#define PRINT_RAW_KEY_EVENT(event)                                                                              \
+    (void)SDL_snprintf(details, sizeof(details), " (timestamp=%u which=%u state=%s scancode=%u)",               \
+                       (uint)event->raw_key.timestamp, (uint)event->raw_key.which,                              \
+                       event->raw_key.down ? "pressed" : "released",                                            \
+                       (uint)event->raw_key.scancode);
+        SDL_EVENT_CASE(SDL_EVENT_RAW_KEY_DOWN)
+        PRINT_RAW_KEY_EVENT(event);
+        break;
+        SDL_EVENT_CASE(SDL_EVENT_RAW_KEY_UP)
+        PRINT_RAW_KEY_EVENT(event);
+        break;
+#undef PRINT_RAW_KEY_EVENT
+
         SDL_EVENT_CASE(SDL_EVENT_TEXT_EDITING)
         (void)SDL_snprintf(details, sizeof(details), " (timestamp=%u windowid=%u text='%s' start=%d length=%d)",
                            (uint)event->edit.timestamp, (uint)event->edit.windowID,
@@ -568,11 +581,18 @@ static void SDL_LogEvent(const SDL_Event *event)
                            event->motion.xrel, event->motion.yrel);
         break;
 
+        SDL_EVENT_CASE(SDL_EVENT_RAW_MOUSE_MOTION)
+        (void)SDL_snprintf(details, sizeof(details), " (timestamp=%u which=%u dx=%d dy=%d)",
+                           (uint)event->raw_motion.timestamp,
+                           (uint)event->raw_motion.which,
+                           (int)event->raw_motion.dx, (int)event->raw_motion.dy);
+        break;
+
 #define PRINT_MBUTTON_EVENT(event)                                                                                              \
     (void)SDL_snprintf(details, sizeof(details), " (timestamp=%u windowid=%u which=%u button=%u state=%s clicks=%u x=%g y=%g)", \
                        (uint)event->button.timestamp, (uint)event->button.windowID,                                             \
                        (uint)event->button.which, (uint)event->button.button,                                                   \
-                       event->button.down ? "pressed" : "released",                                             \
+                       event->button.down ? "pressed" : "released",                                                             \
                        (uint)event->button.clicks, event->button.x, event->button.y)
         SDL_EVENT_CASE(SDL_EVENT_MOUSE_BUTTON_DOWN)
         PRINT_MBUTTON_EVENT(event);
@@ -582,6 +602,19 @@ static void SDL_LogEvent(const SDL_Event *event)
         break;
 #undef PRINT_MBUTTON_EVENT
 
+#define PRINT_RAW_MBUTTON_EVENT(event)                                                                                          \
+    (void)SDL_snprintf(details, sizeof(details), " (timestamp=%u which=%u button=%u state=%s)",                                 \
+                       (uint)event->raw_button.timestamp,                                                                       \
+                       (uint)event->raw_button.which, (uint)event->raw_button.button,                                           \
+                       event->raw_button.down ? "pressed" : "released");
+        SDL_EVENT_CASE(SDL_EVENT_RAW_MOUSE_BUTTON_DOWN)
+        PRINT_RAW_MBUTTON_EVENT(event);
+        break;
+        SDL_EVENT_CASE(SDL_EVENT_RAW_MOUSE_BUTTON_UP)
+        PRINT_RAW_MBUTTON_EVENT(event);
+        break;
+#undef PRINT_RAW_MBUTTON_EVENT
+
         SDL_EVENT_CASE(SDL_EVENT_MOUSE_WHEEL)
         (void)SDL_snprintf(details, sizeof(details), " (timestamp=%u windowid=%u which=%u x=%g y=%g direction=%s)",
                            (uint)event->wheel.timestamp, (uint)event->wheel.windowID,
@@ -589,6 +622,13 @@ static void SDL_LogEvent(const SDL_Event *event)
                            event->wheel.direction == SDL_MOUSEWHEEL_NORMAL ? "normal" 

(Patch may be truncated, please check the link at the top of this post.)