Browse Source

Added SDL_HINT_WINDOWS_RAW_KEYBOARD to control whether raw keyboard is enabled on Windows

Sam Lantinga 1 year ago
parent
commit
78c7834f90

+ 2 - 0
VisualC-GDK/SDL/SDL.vcxproj

@@ -575,6 +575,7 @@
     <ClInclude Include="..\..\src\video\windows\SDL_windowsmouse.h" />
     <ClInclude Include="..\..\src\video\windows\SDL_windowsopengl.h" />
     <ClInclude Include="..\..\src\video\windows\SDL_windowsopengles.h" />
+    <ClInclude Include="..\..\src\video\windows\SDL_windowsrawinput.h" />
     <ClInclude Include="..\..\src\video\windows\SDL_windowsshape.h" />
     <ClInclude Include="..\..\src\video\windows\SDL_windowsvideo.h" />
     <ClInclude Include="..\..\src\video\windows\SDL_windowsvulkan.h" />
@@ -835,6 +836,7 @@
     <ClCompile Include="..\..\src\video\windows\SDL_windowsmouse.c" />
     <ClCompile Include="..\..\src\video\windows\SDL_windowsopengl.c" />
     <ClCompile Include="..\..\src\video\windows\SDL_windowsopengles.c" />
+    <ClCompile Include="..\..\src\video\windows\SDL_windowsrawinput.c" />
     <ClCompile Include="..\..\src\video\windows\SDL_windowsshape.c" />
     <ClCompile Include="..\..\src\video\windows\SDL_windowsvideo.c" />
     <ClCompile Include="..\..\src\video\windows\SDL_windowsvulkan.c" />

+ 2 - 0
VisualC-GDK/SDL/SDL.vcxproj.filters

@@ -211,6 +211,7 @@
     <ClCompile Include="..\..\src\video\windows\SDL_windowsmouse.c" />
     <ClCompile Include="..\..\src\video\windows\SDL_windowsopengl.c" />
     <ClCompile Include="..\..\src\video\windows\SDL_windowsopengles.c" />
+    <ClCompile Include="..\..\src\video\windows\SDL_windowsrawinput.c" />
     <ClCompile Include="..\..\src\video\windows\SDL_windowsshape.c" />
     <ClCompile Include="..\..\src\video\windows\SDL_windowsvideo.c" />
     <ClCompile Include="..\..\src\video\windows\SDL_windowsvulkan.c" />
@@ -440,6 +441,7 @@
     <ClInclude Include="..\..\src\video\windows\SDL_windowsmouse.h" />
     <ClInclude Include="..\..\src\video\windows\SDL_windowsopengl.h" />
     <ClInclude Include="..\..\src\video\windows\SDL_windowsopengles.h" />
+    <ClInclude Include="..\..\src\video\windows\SDL_windowsrawinput.h" />
     <ClInclude Include="..\..\src\video\windows\SDL_windowsshape.h" />
     <ClInclude Include="..\..\src\video\windows\SDL_windowsvideo.h" />
     <ClInclude Include="..\..\src\video\windows\SDL_windowsvulkan.h" />

+ 2 - 0
VisualC/SDL/SDL.vcxproj

@@ -473,6 +473,7 @@
     <ClInclude Include="..\..\src\video\windows\SDL_windowsmouse.h" />
     <ClInclude Include="..\..\src\video\windows\SDL_windowsopengl.h" />
     <ClInclude Include="..\..\src\video\windows\SDL_windowsopengles.h" />
+    <ClInclude Include="..\..\src\video\windows\SDL_windowsrawinput.h" />
     <ClInclude Include="..\..\src\video\windows\SDL_windowsshape.h" />
     <ClInclude Include="..\..\src\video\windows\SDL_windowsvideo.h" />
     <ClInclude Include="..\..\src\video\windows\SDL_windowsvulkan.h" />
@@ -689,6 +690,7 @@
     <ClCompile Include="..\..\src\video\windows\SDL_windowsmouse.c" />
     <ClCompile Include="..\..\src\video\windows\SDL_windowsopengl.c" />
     <ClCompile Include="..\..\src\video\windows\SDL_windowsopengles.c" />
+    <ClCompile Include="..\..\src\video\windows\SDL_windowsrawinput.c" />
     <ClCompile Include="..\..\src\video\windows\SDL_windowsshape.c" />
     <ClCompile Include="..\..\src\video\windows\SDL_windowsvideo.c" />
     <ClCompile Include="..\..\src\video\windows\SDL_windowsvulkan.c" />

+ 6 - 0
VisualC/SDL/SDL.vcxproj.filters

@@ -681,6 +681,9 @@
     <ClInclude Include="..\..\src\video\windows\SDL_windowsopengl.h">
       <Filter>video\windows</Filter>
     </ClInclude>
+    <ClInclude Include="..\..\src\video\windows\SDL_windowsrawinput.h">
+      <Filter>video\windows</Filter>
+    </ClInclude>
     <ClInclude Include="..\..\src\video\windows\SDL_windowsshape.h">
       <Filter>video\windows</Filter>
     </ClInclude>
@@ -1294,6 +1297,9 @@
     <ClCompile Include="..\..\src\video\windows\SDL_windowsopengles.c">
       <Filter>video\windows</Filter>
     </ClCompile>
+    <ClCompile Include="..\..\src\video\windows\SDL_windowsrawinput.c">
+      <Filter>video\windows</Filter>
+    </ClCompile>
     <ClCompile Include="..\..\src\video\windows\SDL_windowsshape.c">
       <Filter>video\windows</Filter>
     </ClCompile>

+ 22 - 11
include/SDL3/SDL_hints.h

@@ -2380,6 +2380,17 @@ extern "C" {
  */
 #define SDL_HINT_WINDOW_FRAME_USABLE_WHILE_CURSOR_HIDDEN    "SDL_WINDOW_FRAME_USABLE_WHILE_CURSOR_HIDDEN"
 
+/**
+ * A variable controlling whether SDL generates window-close events for Alt+F4 on Windows.
+ *
+ * The variable can be set to the following values:
+ *   "0"       - SDL will only do normal key handling for Alt+F4.
+ *   "1"       - SDL will generate a window-close event when it sees Alt+F4. (default)
+ *
+ * This hint can be set anytime.
+ */
+#define SDL_HINT_WINDOWS_CLOSE_ON_ALT_F4 "SDL_WINDOWS_CLOSE_ON_ALT_F4"
+
 /**
  * A variable controlling whether menus can be opened with their keyboard shortcut (Alt+mnemonic).
  *
@@ -2408,6 +2419,17 @@ extern "C" {
  */
 #define SDL_HINT_WINDOWS_ENABLE_MESSAGELOOP "SDL_WINDOWS_ENABLE_MESSAGELOOP"
 
+/**
+ * A variable controlling whether raw keyboard events are used on Windows
+ *
+ * The variable can be set to the following values:
+ *   "0"       - The Windows message loop is used for keyboard events.
+ *   "1"       - Low latency raw keyboard events are used. (default)
+ *
+ * This hint can be set anytime.
+ */
+#define SDL_HINT_WINDOWS_RAW_KEYBOARD   "SDL_WINDOWS_RAW_KEYBOARD"
+
 /**
  * A variable controlling whether SDL uses Critical Sections for mutexes on Windows.
  *
@@ -2442,17 +2464,6 @@ extern "C" {
 #define SDL_HINT_WINDOWS_INTRESOURCE_ICON       "SDL_WINDOWS_INTRESOURCE_ICON"
 #define SDL_HINT_WINDOWS_INTRESOURCE_ICON_SMALL "SDL_WINDOWS_INTRESOURCE_ICON_SMALL"
 
-/**
- * A variable controlling whether SDL generates window-close events for Alt+F4 on Windows.
- *
- * The variable can be set to the following values:
- *   "0"       - SDL will only do normal key handling for Alt+F4.
- *   "1"       - SDL will generate a window-close event when it sees Alt+F4. (default)
- *
- * This hint can be set anytime.
- */
-#define SDL_HINT_WINDOWS_CLOSE_ON_ALT_F4 "SDL_WINDOWS_CLOSE_ON_ALT_F4"
-
 /**
  * A variable controlling whether SDL uses the D3D9Ex API introduced in Windows Vista, instead of normal D3D9.
  *

+ 10 - 16
src/video/windows/SDL_windowsevents.c

@@ -512,11 +512,11 @@ WIN_KeyboardHookProc(int nCode, WPARAM wParam, LPARAM lParam)
     }
 
     if (wParam == WM_KEYDOWN || wParam == WM_SYSKEYDOWN) {
-        if (data->raw_input_enable_count == 0) {
+        if (!data->raw_keyboard_enabled) {
             SDL_SendKeyboardKey(0, SDL_GLOBAL_KEYBOARD_ID, SDL_PRESSED, scanCode);
         }
     } else {
-        if (data->raw_input_enable_count == 0) {
+        if (!data->raw_keyboard_enabled) {
             SDL_SendKeyboardKey(0, SDL_GLOBAL_KEYBOARD_ID, SDL_RELEASED, scanCode);
         }
 
@@ -536,11 +536,9 @@ WIN_KeyboardHookProc(int nCode, WPARAM wParam, LPARAM lParam)
 
 static void WIN_HandleRawMouseInput(Uint64 timestamp, SDL_WindowData *data, HANDLE hDevice, RAWMOUSE *rawmouse)
 {
-    SDL_Mouse *mouse = SDL_GetMouse();
     SDL_MouseID mouseID;
 
-    /* We only use raw mouse input in relative mode */
-    if (!mouse->relative_mode || mouse->relative_mode_warp) {
+    if (!data->videodata->raw_mouse_enabled) {
         return;
     }
 
@@ -633,6 +631,10 @@ static void WIN_HandleRawKeyboardInput(Uint64 timestamp, SDL_WindowData *data, H
 {
     SDL_KeyboardID keyboardID = (SDL_KeyboardID)(uintptr_t)hDevice;
 
+    if (!data->videodata->raw_keyboard_enabled) {
+        return;
+    }
+
     Uint8 state = (rawkeyboard->Flags & RI_KEY_BREAK) ? SDL_RELEASED : SDL_PRESSED;
     Uint16 scanCode = rawkeyboard->MakeCode;
     if (rawkeyboard->Flags & RI_KEY_E0) {
@@ -656,10 +658,6 @@ void WIN_PollRawInput(SDL_VideoDevice *_this)
     RAWINPUT *input;
     Uint64 now;
 
-    if (_this->driverdata->raw_input_enable_count == 0) {
-        return;
-    }
-
     /* Relative mouse motion is delivered to the window with keyboard focus */
     window = SDL_GetKeyboardFocus();
     if (!window) {
@@ -905,8 +903,8 @@ void WIN_CheckKeyboardAndMouseHotplug(SDL_VideoDevice *_this, SDL_bool initial_c
     }
 
     SDL_free(old_keyboards);
-    SDL_free(old_mice);
     SDL_free(new_keyboards);
+    SDL_free(old_mice);
     SDL_free(new_mice);
 
     SetupDiDestroyDeviceInfoList(devinfo);
@@ -1058,10 +1056,6 @@ LRESULT CALLBACK WIN_WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lPara
         RAWINPUT inp;
         UINT size = sizeof(inp);
 
-        if (data->raw_input_enable_count == 0) {
-            break;
-        }
-
         /* Relative mouse motion is delivered to the window with keyboard focus */
         if (data->window != SDL_GetKeyboardFocus()) {
             break;
@@ -1135,7 +1129,7 @@ LRESULT CALLBACK WIN_WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lPara
             }
         }
 
-        if (data->videodata->raw_input_enable_count == 0 && code != SDL_SCANCODE_UNKNOWN) {
+        if (!data->videodata->raw_keyboard_enabled && code != SDL_SCANCODE_UNKNOWN) {
             SDL_SendKeyboardKey(WIN_GetEventTimestamp(), SDL_GLOBAL_KEYBOARD_ID, SDL_PRESSED, code);
         }
     }
@@ -1149,7 +1143,7 @@ LRESULT CALLBACK WIN_WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lPara
         SDL_Scancode code = WindowsScanCodeToSDLScanCode(lParam, wParam);
         const Uint8 *keyboardState = SDL_GetKeyboardState(NULL);
 
-        if (data->videodata->raw_input_enable_count == 0 && code != SDL_SCANCODE_UNKNOWN) {
+        if (!data->videodata->raw_keyboard_enabled && code != SDL_SCANCODE_UNKNOWN) {
             if (code == SDL_SCANCODE_PRINTSCREEN &&
                 keyboardState[code] == SDL_RELEASED) {
                 SDL_SendKeyboardKey(WIN_GetEventTimestamp(), SDL_GLOBAL_KEYBOARD_ID, SDL_PRESSED, code);

+ 2 - 157
src/video/windows/SDL_windowsmouse.c

@@ -24,6 +24,7 @@
 
 #include "SDL_windowsvideo.h"
 #include "SDL_windowsevents.h"
+#include "SDL_windowsrawinput.h"
 
 #include "../SDL_video_c.h"
 #include "../../events/SDL_mouse_c.h"
@@ -33,156 +34,6 @@ DWORD SDL_last_warp_time = 0;
 HCURSOR SDL_cursor = NULL;
 static SDL_Cursor *SDL_blank_cursor = NULL;
 
-typedef struct
-{
-    HANDLE ready_event;
-    HANDLE done_event;
-    HANDLE thread;
-} RawInputThreadData;
-
-static RawInputThreadData thread_data = {
-    INVALID_HANDLE_VALUE,
-    INVALID_HANDLE_VALUE,
-    INVALID_HANDLE_VALUE
-};
-
-static DWORD WINAPI WIN_RawInputThread(LPVOID param)
-{
-    SDL_VideoDevice *_this = SDL_GetVideoDevice();
-    RawInputThreadData *data = (RawInputThreadData *)param;
-    RAWINPUTDEVICE devices[2];
-    HWND window;
-
-    window = CreateWindowEx(0, TEXT("Message"), NULL, 0, 0, 0, 0, 0, HWND_MESSAGE, NULL, NULL, NULL);
-    if (!window) {
-        return 0;
-    }
-
-    devices[0].usUsagePage = USB_USAGEPAGE_GENERIC_DESKTOP;
-    devices[0].usUsage = USB_USAGE_GENERIC_MOUSE;
-    devices[0].dwFlags = 0;
-    devices[0].hwndTarget = window;
-
-    devices[1].usUsagePage = USB_USAGEPAGE_GENERIC_DESKTOP;
-    devices[1].usUsage = USB_USAGE_GENERIC_KEYBOARD;
-    devices[1].dwFlags = 0;
-    devices[1].hwndTarget = window;
-
-    if (!RegisterRawInputDevices(devices, SDL_arraysize(devices), sizeof(devices[0]))) {
-        DestroyWindow(window);
-        return 0;
-    }
-
-    /* Make sure we get events as soon as possible */
-    SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL);
-
-    /* Tell the parent we're ready to go! */
-    SetEvent(data->ready_event);
-
-    for ( ; ; ) {
-        if (MsgWaitForMultipleObjects(1, &data->done_event, 0, INFINITE, QS_RAWINPUT) != WAIT_OBJECT_0 + 1) {
-            break;
-        }
-
-        /* Clear the queue status so MsgWaitForMultipleObjects() will wait again */
-        (void)GetQueueStatus(QS_RAWINPUT);
-
-        WIN_PollRawInput(_this);
-    }
-
-    devices[0].dwFlags |= RIDEV_REMOVE;
-    devices[1].dwFlags |= RIDEV_REMOVE;
-    RegisterRawInputDevices(devices, SDL_arraysize(devices), sizeof(devices[0]));
-
-    DestroyWindow(window);
-
-    return 0;
-}
-
-static void CleanupRawInputThreadData(RawInputThreadData *data)
-{
-    if (data->thread != INVALID_HANDLE_VALUE) {
-        SetEvent(data->done_event);
-        WaitForSingleObject(data->thread, 500);
-        CloseHandle(data->thread);
-        data->thread = INVALID_HANDLE_VALUE;
-    }
-
-    if (data->ready_event != INVALID_HANDLE_VALUE) {
-        CloseHandle(data->ready_event);
-        data->ready_event = INVALID_HANDLE_VALUE;
-    }
-
-    if (data->done_event != INVALID_HANDLE_VALUE) {
-        CloseHandle(data->done_event);
-        data->done_event = INVALID_HANDLE_VALUE;
-    }
-}
-
-static int ToggleRawInput(SDL_VideoDevice *_this, SDL_bool enabled)
-{
-    SDL_VideoData *data = _this->driverdata;
-    int result = -1;
-
-    if (enabled) {
-        ++data->raw_input_enable_count;
-        if (data->raw_input_enable_count > 1) {
-            return 0; /* already done. */
-        }
-    } else {
-        if (data->raw_input_enable_count == 0) {
-            return 0; /* already done. */
-        }
-        --data->raw_input_enable_count;
-        if (data->raw_input_enable_count > 0) {
-            return 0; /* not time to disable yet */
-        }
-    }
-
-    if (enabled) {
-        HANDLE handles[2];
-
-        thread_data.ready_event = CreateEvent(NULL, FALSE, FALSE, NULL);
-        if (thread_data.ready_event == INVALID_HANDLE_VALUE) {
-            WIN_SetError("CreateEvent");
-            goto done;
-        }
-
-        thread_data.done_event = CreateEvent(NULL, FALSE, FALSE, NULL);
-        if (thread_data.done_event == INVALID_HANDLE_VALUE) {
-            WIN_SetError("CreateEvent");
-            goto done;
-        }
-
-        thread_data.thread = CreateThread(NULL, 0, WIN_RawInputThread, &thread_data, 0, NULL);
-        if (thread_data.thread == INVALID_HANDLE_VALUE) {
-            WIN_SetError("CreateThread");
-            goto done;
-        }
-
-        /* Wait for the thread to signal ready or exit */
-        handles[0] = thread_data.ready_event;
-        handles[1] = thread_data.thread;
-        if (WaitForMultipleObjects(2, handles, FALSE, INFINITE) != WAIT_OBJECT_0) {
-            SDL_SetError("Couldn't set up raw input handling");
-            goto done;
-        }
-        result = 0;
-    } else {
-        CleanupRawInputThreadData(&thread_data);
-        result = 0;
-    }
-
-done:
-    if (enabled && result < 0) {
-        CleanupRawInputThreadData(&thread_data);
-
-        /* Reset so we can try again */
-        data->raw_input_enable_count = 0;
-    }
-    return result;
-}
-
 static SDL_Cursor *WIN_CreateDefaultCursor()
 {
     SDL_Cursor *cursor = (SDL_Cursor *)SDL_calloc(1, sizeof(*cursor));
@@ -516,7 +367,7 @@ static int WIN_WarpMouseGlobal(float x, float y)
 
 static int WIN_SetRelativeMouseMode(SDL_bool enabled)
 {
-    return ToggleRawInput(SDL_GetVideoDevice(), enabled);
+    return WIN_SetRawMouseEnabled(SDL_GetVideoDevice(), enabled);
 }
 
 static int WIN_CaptureMouse(SDL_Window *window)
@@ -581,12 +432,6 @@ void WIN_InitMouse(SDL_VideoDevice *_this)
 
 void WIN_QuitMouse(SDL_VideoDevice *_this)
 {
-    SDL_VideoData *data = _this->driverdata;
-    if (data->raw_input_enable_count) { /* force RAWINPUT off here. */
-        data->raw_input_enable_count = 1;
-        ToggleRawInput(_this, SDL_FALSE);
-    }
-
     if (SDL_blank_cursor) {
         WIN_FreeCursor(SDL_blank_cursor);
         SDL_blank_cursor = NULL;

+ 205 - 0
src/video/windows/SDL_windowsrawinput.c

@@ -0,0 +1,205 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2024 Sam Lantinga <slouken@libsdl.org>
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+*/
+#include "SDL_internal.h"
+
+#if defined(SDL_VIDEO_DRIVER_WINDOWS)
+
+#if !defined(SDL_PLATFORM_XBOXONE) && !defined(SDL_PLATFORM_XBOXSERIES)
+
+#include "SDL_windowsvideo.h"
+#include "SDL_windowsevents.h"
+
+#include "../../joystick/usb_ids.h"
+
+typedef struct
+{
+    HANDLE ready_event;
+    HANDLE done_event;
+    HANDLE thread;
+} RawInputThreadData;
+
+static RawInputThreadData thread_data = {
+    INVALID_HANDLE_VALUE,
+    INVALID_HANDLE_VALUE,
+    INVALID_HANDLE_VALUE
+};
+
+static DWORD WINAPI WIN_RawInputThread(LPVOID param)
+{
+    SDL_VideoDevice *_this = SDL_GetVideoDevice();
+    RawInputThreadData *data = (RawInputThreadData *)param;
+    RAWINPUTDEVICE devices[2];
+    HWND window;
+
+    window = CreateWindowEx(0, TEXT("Message"), NULL, 0, 0, 0, 0, 0, HWND_MESSAGE, NULL, NULL, NULL);
+    if (!window) {
+        return 0;
+    }
+
+    devices[0].usUsagePage = USB_USAGEPAGE_GENERIC_DESKTOP;
+    devices[0].usUsage = USB_USAGE_GENERIC_MOUSE;
+    devices[0].dwFlags = 0;
+    devices[0].hwndTarget = window;
+
+    devices[1].usUsagePage = USB_USAGEPAGE_GENERIC_DESKTOP;
+    devices[1].usUsage = USB_USAGE_GENERIC_KEYBOARD;
+    devices[1].dwFlags = 0;
+    devices[1].hwndTarget = window;
+
+    if (!RegisterRawInputDevices(devices, SDL_arraysize(devices), sizeof(devices[0]))) {
+        DestroyWindow(window);
+        return 0;
+    }
+
+    /* Make sure we get events as soon as possible */
+    SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL);
+
+    /* Tell the parent we're ready to go! */
+    SetEvent(data->ready_event);
+
+    for ( ; ; ) {
+        if (MsgWaitForMultipleObjects(1, &data->done_event, 0, INFINITE, QS_RAWINPUT) != WAIT_OBJECT_0 + 1) {
+            break;
+        }
+
+        /* Clear the queue status so MsgWaitForMultipleObjects() will wait again */
+        (void)GetQueueStatus(QS_RAWINPUT);
+
+        WIN_PollRawInput(_this);
+    }
+
+    devices[0].dwFlags |= RIDEV_REMOVE;
+    devices[1].dwFlags |= RIDEV_REMOVE;
+    RegisterRawInputDevices(devices, SDL_arraysize(devices), sizeof(devices[0]));
+
+    DestroyWindow(window);
+
+    return 0;
+}
+
+static void CleanupRawInputThreadData(RawInputThreadData *data)
+{
+    if (data->thread != INVALID_HANDLE_VALUE) {
+        SetEvent(data->done_event);
+        WaitForSingleObject(data->thread, 500);
+        CloseHandle(data->thread);
+        data->thread = INVALID_HANDLE_VALUE;
+    }
+
+    if (data->ready_event != INVALID_HANDLE_VALUE) {
+        CloseHandle(data->ready_event);
+        data->ready_event = INVALID_HANDLE_VALUE;
+    }
+
+    if (data->done_event != INVALID_HANDLE_VALUE) {
+        CloseHandle(data->done_event);
+        data->done_event = INVALID_HANDLE_VALUE;
+    }
+}
+
+static int WIN_SetRawInputEnabled(SDL_VideoDevice *_this, SDL_bool enabled)
+{
+    int result = -1;
+
+    if (enabled) {
+        HANDLE handles[2];
+
+        thread_data.ready_event = CreateEvent(NULL, FALSE, FALSE, NULL);
+        if (thread_data.ready_event == INVALID_HANDLE_VALUE) {
+            WIN_SetError("CreateEvent");
+            goto done;
+        }
+
+        thread_data.done_event = CreateEvent(NULL, FALSE, FALSE, NULL);
+        if (thread_data.done_event == INVALID_HANDLE_VALUE) {
+            WIN_SetError("CreateEvent");
+            goto done;
+        }
+
+        thread_data.thread = CreateThread(NULL, 0, WIN_RawInputThread, &thread_data, 0, NULL);
+        if (thread_data.thread == INVALID_HANDLE_VALUE) {
+            WIN_SetError("CreateThread");
+            goto done;
+        }
+
+        /* Wait for the thread to signal ready or exit */
+        handles[0] = thread_data.ready_event;
+        handles[1] = thread_data.thread;
+        if (WaitForMultipleObjects(2, handles, FALSE, INFINITE) != WAIT_OBJECT_0) {
+            SDL_SetError("Couldn't set up raw input handling");
+            goto done;
+        }
+        result = 0;
+    } else {
+        CleanupRawInputThreadData(&thread_data);
+        result = 0;
+    }
+
+done:
+    if (enabled && result < 0) {
+        CleanupRawInputThreadData(&thread_data);
+    }
+    return result;
+}
+
+static int WIN_UpdateRawInputEnabled(SDL_VideoDevice *_this)
+{
+    SDL_VideoData *data = _this->driverdata;
+    SDL_bool enabled = (data->raw_mouse_enabled || data->raw_keyboard_enabled);
+    if (enabled != data->raw_input_enabled) {
+        if (WIN_SetRawInputEnabled(_this, enabled) == 0) {
+            data->raw_input_enabled = enabled;
+        } else {
+            return -1;
+        }
+    }
+    return 0;
+}
+
+int WIN_SetRawMouseEnabled(SDL_VideoDevice *_this, SDL_bool enabled)
+{
+    SDL_VideoData *data = _this->driverdata;
+    data->raw_mouse_enabled = enabled;
+    return WIN_UpdateRawInputEnabled(_this);
+}
+
+int WIN_SetRawKeyboardEnabled(SDL_VideoDevice *_this, SDL_bool enabled)
+{
+    SDL_VideoData *data = _this->driverdata;
+    data->raw_keyboard_enabled = enabled;
+    return WIN_UpdateRawInputEnabled(_this);
+}
+
+#else
+
+int WIN_SetRawMouseEnabled(SDL_VideoDevice *_this, SDL_bool enabled)
+{
+    return SDL_Unsupported();
+}
+
+int WIN_SetRawKeyboardEnabled(SDL_VideoDevice *_this, SDL_bool enabled)
+{
+    return SDL_Unsupported();
+}
+
+#endif /* !SDL_PLATFORM_XBOXONE && !SDL_PLATFORM_XBOXSERIES */
+
+#endif /* SDL_VIDEO_DRIVER_WINDOWS */

+ 29 - 0
src/video/windows/SDL_windowsrawinput.h

@@ -0,0 +1,29 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2024 Sam Lantinga <slouken@libsdl.org>
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+*/
+#include "SDL_internal.h"
+
+#ifndef SDL_windowsrawinput_h_
+#define SDL_windowsrawinput_h_
+
+extern int WIN_SetRawMouseEnabled(SDL_VideoDevice *_this, SDL_bool enabled);
+extern int WIN_SetRawKeyboardEnabled(SDL_VideoDevice *_this, SDL_bool enabled);
+
+#endif /* SDL_windowsrawinput_h_ */

+ 18 - 1
src/video/windows/SDL_windowsvideo.c

@@ -32,8 +32,9 @@
 
 #include "SDL_windowsvideo.h"
 #include "SDL_windowsframebuffer.h"
-#include "SDL_windowsvulkan.h"
 #include "SDL_windowsmessagebox.h"
+#include "SDL_windowsrawinput.h"
+#include "SDL_windowsvulkan.h"
 
 #ifdef SDL_GDK_TEXTINPUT
 #include "../gdk/SDL_gdktextinput.h"
@@ -50,6 +51,13 @@ SDL_bool g_WindowsEnableMessageLoop = SDL_TRUE;
 SDL_bool g_WindowsEnableMenuMnemonics = SDL_FALSE;
 SDL_bool g_WindowFrameUsableWhileCursorHidden = SDL_TRUE;
 
+static void SDLCALL UpdateWindowsRawKeyboard(void *userdata, const char *name, const char *oldValue, const char *newValue)
+{
+    SDL_VideoDevice *_this = (SDL_VideoDevice *)userdata;
+    SDL_bool enabled = SDL_GetStringBoolean(newValue, SDL_TRUE);
+    WIN_SetRawKeyboardEnabled(_this, enabled);
+}
+
 static void SDLCALL UpdateWindowsEnableMessageLoop(void *userdata, const char *name, const char *oldValue, const char *newValue)
 {
     g_WindowsEnableMessageLoop = SDL_GetStringBoolean(newValue, SDL_TRUE);
@@ -458,6 +466,7 @@ int WIN_VideoInit(SDL_VideoDevice *_this)
     WIN_CheckKeyboardAndMouseHotplug(_this, SDL_TRUE);
 #endif
 
+    SDL_AddHintCallback(SDL_HINT_WINDOWS_RAW_KEYBOARD, UpdateWindowsRawKeyboard, _this);
     SDL_AddHintCallback(SDL_HINT_WINDOWS_ENABLE_MESSAGELOOP, UpdateWindowsEnableMessageLoop, NULL);
     SDL_AddHintCallback(SDL_HINT_WINDOWS_ENABLE_MENU_MNEMONICS, UpdateWindowsEnableMenuMnemonics, NULL);
     SDL_AddHintCallback(SDL_HINT_WINDOW_FRAME_USABLE_WHILE_CURSOR_HIDDEN, UpdateWindowFrameUsableWhileCursorHidden, NULL);
@@ -477,6 +486,14 @@ void WIN_VideoQuit(SDL_VideoDevice *_this)
     WIN_QuitKeyboard(_this);
     WIN_QuitMouse(_this);
 #endif
+
+    SDL_DelHintCallback(SDL_HINT_WINDOWS_RAW_KEYBOARD, UpdateWindowsRawKeyboard, _this);
+    SDL_DelHintCallback(SDL_HINT_WINDOWS_ENABLE_MESSAGELOOP, UpdateWindowsEnableMessageLoop, NULL);
+    SDL_DelHintCallback(SDL_HINT_WINDOWS_ENABLE_MENU_MNEMONICS, UpdateWindowsEnableMenuMnemonics, NULL);
+    SDL_DelHintCallback(SDL_HINT_WINDOW_FRAME_USABLE_WHILE_CURSOR_HIDDEN, UpdateWindowFrameUsableWhileCursorHidden, NULL);
+
+    WIN_SetRawMouseEnabled(_this, SDL_FALSE);
+    WIN_SetRawKeyboardEnabled(_this, SDL_FALSE);
 }
 
 #if !defined(SDL_PLATFORM_XBOXONE) && !defined(SDL_PLATFORM_XBOXSERIES)

+ 3 - 1
src/video/windows/SDL_windowsvideo.h

@@ -406,7 +406,9 @@ struct SDL_VideoData
 
     SDL_bool cleared;
 
-    int raw_input_enable_count;
+    SDL_bool raw_mouse_enabled;
+    SDL_bool raw_keyboard_enabled;
+    SDL_bool raw_input_enabled;
 
 #ifndef SDL_DISABLE_WINDOWS_IME
     SDL_bool ime_com_initialized;