SDL: Add hint for blocking win key when using raw keyboard (#13066)

From b4eaf9d96f59a4155673143282b0419bd06899a6 Mon Sep 17 00:00:00 2001
From: expikr <[EMAIL REDACTED]>
Date: Mon, 27 Oct 2025 00:27:33 +0800
Subject: [PATCH] Add hint for blocking win key when using raw keyboard 
 (#13066)

Co-authored-by: Susko3 <Susko3@protonmail.com>
---
 include/SDL3/SDL_hints.h                | 22 ++++++++++++++
 src/video/windows/SDL_windowsrawinput.c | 40 +++++++++++++++++++++++++
 src/video/windows/SDL_windowsrawinput.h |  2 +-
 src/video/windows/SDL_windowsvideo.c    |  9 ++++++
 src/video/windows/SDL_windowsvideo.h    |  1 +
 5 files changed, 73 insertions(+), 1 deletion(-)

diff --git a/include/SDL3/SDL_hints.h b/include/SDL3/SDL_hints.h
index b5565b5496101..a53ae96828c85 100644
--- a/include/SDL3/SDL_hints.h
+++ b/include/SDL3/SDL_hints.h
@@ -4384,6 +4384,28 @@ extern "C" {
  */
 #define SDL_HINT_WINDOWS_RAW_KEYBOARD "SDL_WINDOWS_RAW_KEYBOARD"
 
+/**
+ * A variable controlling whether or not the RIDEV_NOHOTKEYS flag is set when 
+ * enabling Windows raw keyboard events. 
+ *
+ * This blocks any hotkeys that have been registered by applications from 
+ * having any effect beyond generating raw WM_INPUT events. 
+ *
+ * This flag does not affect system-hotkeys like ALT-TAB or CTRL-ALT-DEL,
+ * but does affect the Windows Logo key since it is a userland hotkey registered
+ * by explorer.exe.
+ *
+ * The variable can be set to the following values:
+ *
+ * - "0": Hotkeys are not excluded. (default)
+ * - "1": Hotkeys are excluded.
+ *
+ * This hint can be set anytime.
+ *
+ * \since This hint is available since SDL 3.4.0.
+ */
+#define SDL_HINT_WINDOWS_RAW_KEYBOARD_EXCLUDE_HOTKEYS "SDL_WINDOWS_RAW_KEYBOARD_EXCLUDE_HOTKEYS"
+
 /**
  * A variable controlling whether SDL uses Kernel Semaphores on Windows.
  *
diff --git a/src/video/windows/SDL_windowsrawinput.c b/src/video/windows/SDL_windowsrawinput.c
index 5f32b373c5327..46da4a158b7ce 100644
--- a/src/video/windows/SDL_windowsrawinput.c
+++ b/src/video/windows/SDL_windowsrawinput.c
@@ -34,6 +34,7 @@
 
 #define ENABLE_RAW_MOUSE_INPUT      0x01
 #define ENABLE_RAW_KEYBOARD_INPUT   0x02
+#define RAW_KEYBOARD_FLAG_NOHOTKEYS 0x04
 
 typedef struct
 {
@@ -81,6 +82,9 @@ static DWORD WINAPI WIN_RawInputThread(LPVOID param)
         devices[count].usUsagePage = USB_USAGEPAGE_GENERIC_DESKTOP;
         devices[count].usUsage = USB_USAGE_GENERIC_KEYBOARD;
         devices[count].dwFlags = 0;
+        if (data->flags & RAW_KEYBOARD_FLAG_NOHOTKEYS) {
+            devices[count].dwFlags |= RIDEV_NOHOTKEYS;
+        }
         devices[count].hwndTarget = window;
         ++count;
     }
@@ -203,6 +207,9 @@ static bool WIN_UpdateRawInputEnabled(SDL_VideoDevice *_this)
     }
     if (data->raw_keyboard_enabled) {
         flags |= ENABLE_RAW_KEYBOARD_INPUT;
+        if (data->raw_keyboard_flag_nohotkeys) {
+            flags |= RAW_KEYBOARD_FLAG_NOHOTKEYS;
+        }
     }
     if (flags != data->raw_input_enabled) {
         if (WIN_SetRawInputEnabled(_this, flags)) {
@@ -250,6 +257,34 @@ bool WIN_SetRawKeyboardEnabled(SDL_VideoDevice *_this, bool enabled)
     return true;
 }
 
+typedef enum WIN_RawKeyboardFlag {
+    NOHOTKEYS
+} WIN_RawKeyboardFlag;
+
+static bool WIN_SetRawKeyboardFlag(SDL_VideoDevice *_this, WIN_RawKeyboardFlag flag, bool enabled)
+{
+    SDL_VideoData *data = _this->internal;
+
+    switch(flag) {
+        case NOHOTKEYS:
+            data->raw_keyboard_flag_nohotkeys = enabled;
+            break;
+        default:
+            return false;
+    }
+
+    if (data->gameinput_context) {
+        return true;
+    }
+
+    return WIN_UpdateRawInputEnabled(_this);
+}
+
+bool WIN_SetRawKeyboardFlag_NoHotkeys(SDL_VideoDevice *_this, bool enabled)
+{
+    return WIN_SetRawKeyboardFlag(_this, NOHOTKEYS, enabled);
+}
+
 #else
 
 bool WIN_SetRawMouseEnabled(SDL_VideoDevice *_this, bool enabled)
@@ -262,6 +297,11 @@ bool WIN_SetRawKeyboardEnabled(SDL_VideoDevice *_this, bool enabled)
     return SDL_Unsupported();
 }
 
+bool WIN_SetRawKeyboardFlag_NoHotkeys(SDL_VideoDevice *_this, bool enabled)
+{
+    return SDL_Unsupported();
+}
+
 #endif // !SDL_PLATFORM_XBOXONE && !SDL_PLATFORM_XBOXSERIES
 
 #endif // SDL_VIDEO_DRIVER_WINDOWS
diff --git a/src/video/windows/SDL_windowsrawinput.h b/src/video/windows/SDL_windowsrawinput.h
index 25ae7054ebe86..dc2e28fe33867 100644
--- a/src/video/windows/SDL_windowsrawinput.h
+++ b/src/video/windows/SDL_windowsrawinput.h
@@ -25,6 +25,6 @@
 
 extern bool WIN_SetRawMouseEnabled(SDL_VideoDevice *_this, bool enabled);
 extern bool WIN_SetRawKeyboardEnabled(SDL_VideoDevice *_this, bool enabled);
-extern bool WIN_RefreshRawInputEnabled(SDL_VideoDevice *_this);
+extern bool WIN_SetRawKeyboardFlag_NoHotkeys(SDL_VideoDevice *_this, bool enabled);
 
 #endif // SDL_windowsrawinput_h_
diff --git a/src/video/windows/SDL_windowsvideo.c b/src/video/windows/SDL_windowsvideo.c
index e840b0507e6db..6b459138f7fa2 100644
--- a/src/video/windows/SDL_windowsvideo.c
+++ b/src/video/windows/SDL_windowsvideo.c
@@ -149,6 +149,13 @@ static void SDLCALL UpdateWindowsRawKeyboard(void *userdata, const char *name, c
     WIN_SetRawKeyboardEnabled(_this, enabled);
 }
 
+static void SDLCALL UpdateWindowsRawKeyboardNoHotkeys(void *userdata, const char *name, const char *oldValue, const char *newValue)
+{
+    SDL_VideoDevice *_this = (SDL_VideoDevice *)userdata;
+    bool enabled = SDL_GetStringBoolean(newValue, false);
+    WIN_SetRawKeyboardFlag_NoHotkeys(_this, enabled);
+}
+
 static void SDLCALL UpdateWindowsEnableMessageLoop(void *userdata, const char *name, const char *oldValue, const char *newValue)
 {
     g_WindowsEnableMessageLoop = SDL_GetStringBoolean(newValue, true);
@@ -632,6 +639,7 @@ static bool WIN_VideoInit(SDL_VideoDevice *_this)
 #endif
 
     SDL_AddHintCallback(SDL_HINT_WINDOWS_RAW_KEYBOARD, UpdateWindowsRawKeyboard, _this);
+    SDL_AddHintCallback(SDL_HINT_WINDOWS_RAW_KEYBOARD_EXCLUDE_HOTKEYS, UpdateWindowsRawKeyboardNoHotkeys, _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);
@@ -649,6 +657,7 @@ void WIN_VideoQuit(SDL_VideoDevice *_this)
     SDL_VideoData *data = _this->internal;
 
     SDL_RemoveHintCallback(SDL_HINT_WINDOWS_RAW_KEYBOARD, UpdateWindowsRawKeyboard, _this);
+    SDL_RemoveHintCallback(SDL_HINT_WINDOWS_RAW_KEYBOARD_EXCLUDE_HOTKEYS, UpdateWindowsRawKeyboardNoHotkeys, _this);
     SDL_RemoveHintCallback(SDL_HINT_WINDOWS_ENABLE_MESSAGELOOP, UpdateWindowsEnableMessageLoop, NULL);
     SDL_RemoveHintCallback(SDL_HINT_WINDOWS_ENABLE_MENU_MNEMONICS, UpdateWindowsEnableMenuMnemonics, NULL);
     SDL_RemoveHintCallback(SDL_HINT_WINDOW_FRAME_USABLE_WHILE_CURSOR_HIDDEN, UpdateWindowFrameUsableWhileCursorHidden, NULL);
diff --git a/src/video/windows/SDL_windowsvideo.h b/src/video/windows/SDL_windowsvideo.h
index b192fb664c2b0..4ed1a42b58065 100644
--- a/src/video/windows/SDL_windowsvideo.h
+++ b/src/video/windows/SDL_windowsvideo.h
@@ -590,6 +590,7 @@ struct SDL_VideoData
     SDL_Point last_raw_mouse_position;
     bool raw_mouse_enabled;
     bool raw_keyboard_enabled;
+    bool raw_keyboard_flag_nohotkeys;
     bool pending_E1_key_sequence;
     Uint32 raw_input_enabled;