From 012fc1e32b4a8b7fb79ab769566bb34c3f6502e6 Mon Sep 17 00:00:00 2001
From: Sam Lantinga <[EMAIL REDACTED]>
Date: Mon, 25 Mar 2024 09:49:35 -0700
Subject: [PATCH] windows: enable raw keyboard input when raw mouse input is
enabled
---
src/video/windows/SDL_windowsevents.c | 54 +++++++++++----
src/video/windows/SDL_windowsevents.h | 2 +-
src/video/windows/SDL_windowsmouse.c | 96 +++++++++++++++------------
src/video/windows/SDL_windowsvideo.h | 2 +
4 files changed, 96 insertions(+), 58 deletions(-)
diff --git a/src/video/windows/SDL_windowsevents.c b/src/video/windows/SDL_windowsevents.c
index 9409c3fbf46a7..96dfcb07ca4cb 100644
--- a/src/video/windows/SDL_windowsevents.c
+++ b/src/video/windows/SDL_windowsevents.c
@@ -512,9 +512,13 @@ WIN_KeyboardHookProc(int nCode, WPARAM wParam, LPARAM lParam)
}
if (wParam == WM_KEYDOWN || wParam == WM_SYSKEYDOWN) {
- SDL_SendKeyboardKey(0, SDL_GLOBAL_KEYBOARD_ID, SDL_PRESSED, scanCode);
+ if (data->raw_input_enable_count == 0) {
+ SDL_SendKeyboardKey(0, SDL_GLOBAL_KEYBOARD_ID, SDL_PRESSED, scanCode);
+ }
} else {
- SDL_SendKeyboardKey(0, SDL_GLOBAL_KEYBOARD_ID, SDL_RELEASED, scanCode);
+ if (data->raw_input_enable_count == 0) {
+ SDL_SendKeyboardKey(0, SDL_GLOBAL_KEYBOARD_ID, SDL_RELEASED, scanCode);
+ }
/* If the key was down prior to our hook being installed, allow the
key up message to pass normally the first time. This ensures other
@@ -532,8 +536,14 @@ 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) {
+ return;
+ }
+
if (GetMouseMessageSource(rawmouse->ulExtraInformation) == SDL_MOUSE_EVENT_SOURCE_TOUCH) {
return;
}
@@ -619,17 +629,34 @@ static void WIN_HandleRawMouseInput(Uint64 timestamp, SDL_WindowData *data, HAND
WIN_CheckRawMouseButtons(timestamp, hDevice, rawmouse->usButtonFlags, data, mouseID);
}
-void WIN_PollRawMouseInput(void)
+static void WIN_HandleRawKeyboardInput(Uint64 timestamp, SDL_WindowData *data, HANDLE hDevice, RAWKEYBOARD *rawkeyboard)
+{
+ SDL_KeyboardID keyboardID = (SDL_KeyboardID)(uintptr_t)hDevice;
+
+ Uint8 state = (rawkeyboard->Flags & RI_KEY_BREAK) ? SDL_RELEASED : SDL_PRESSED;
+ Uint16 scanCode = rawkeyboard->MakeCode;
+ if (rawkeyboard->Flags & RI_KEY_E0) {
+ scanCode |= (0xE0 << 8);
+ } else if (rawkeyboard->Flags & RI_KEY_E1) {
+ scanCode |= (0xE1 << 8);
+ }
+
+ // Pack scan code into one byte to make the index
+ Uint8 index = LOBYTE(scanCode) | (HIBYTE(scanCode) ? 0x80 : 0x00);
+ SDL_Scancode code = windows_scancode_table[index];
+
+ SDL_SendKeyboardKey(timestamp, keyboardID, state, code);
+}
+
+void WIN_PollRawInput(SDL_VideoDevice *_this)
{
- SDL_Mouse *mouse = SDL_GetMouse();
SDL_Window *window;
SDL_WindowData *data;
UINT size, count, i, total = 0;
RAWINPUT *input;
Uint64 now;
- /* We only use raw mouse input in relative mode */
- if (!mouse->relative_mode || mouse->relative_mode_warp) {
+ if (_this->driverdata->raw_input_enable_count == 0) {
return;
}
@@ -695,6 +722,9 @@ void WIN_PollRawMouseInput(void)
if (input->header.dwType == RIM_TYPEMOUSE) {
RAWMOUSE *rawmouse = (RAWMOUSE *)((BYTE *)input + data->rawinput_offset);
WIN_HandleRawMouseInput(timestamp, window->driverdata, input->header.hDevice, rawmouse);
+ } else if (input->header.dwType == RIM_TYPEKEYBOARD) {
+ RAWKEYBOARD *rawkeyboard = (RAWKEYBOARD *)((BYTE *)input + data->rawinput_offset);
+ WIN_HandleRawKeyboardInput(timestamp, window->driverdata, input->header.hDevice, rawkeyboard);
}
}
}
@@ -1024,13 +1054,11 @@ LRESULT CALLBACK WIN_WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lPara
#if 0 /* We handle raw input all at once instead of using a syscall for each mouse event */
case WM_INPUT:
{
- SDL_Mouse *mouse = SDL_GetMouse();
HRAWINPUT hRawInput = (HRAWINPUT)lParam;
RAWINPUT inp;
UINT size = sizeof(inp);
- /* We only use raw mouse input in relative mode */
- if (!mouse->relative_mode || mouse->relative_mode_warp) {
+ if (data->raw_input_enable_count == 0) {
break;
}
@@ -1040,10 +1068,10 @@ LRESULT CALLBACK WIN_WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lPara
}
GetRawInputData(hRawInput, RID_INPUT, &inp, &size, sizeof(RAWINPUTHEADER));
-
- /* Mouse data (ignoring synthetic mouse events generated for touchscreens) */
if (inp.header.dwType == RIM_TYPEMOUSE) {
WIN_HandleRawMouseInput(WIN_GetEventTimestamp(), data, inp.header.hDevice, &inp.data.mouse);
+ } else if (inp.header.dwType == RIM_TYPEKEYBOARD) {
+ WIN_HandleRawKeyboardInput(WIN_GetEventTimestamp(), data, inp.header.hDevice, &inp.data.keyboard);
}
} break;
#endif
@@ -1107,7 +1135,7 @@ LRESULT CALLBACK WIN_WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lPara
}
}
- if (code != SDL_SCANCODE_UNKNOWN) {
+ if (data->videodata->raw_input_enable_count == 0 && code != SDL_SCANCODE_UNKNOWN) {
SDL_SendKeyboardKey(WIN_GetEventTimestamp(), SDL_GLOBAL_KEYBOARD_ID, SDL_PRESSED, code);
}
}
@@ -1121,7 +1149,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 (code != SDL_SCANCODE_UNKNOWN) {
+ if (data->videodata->raw_input_enable_count == 0 && code != SDL_SCANCODE_UNKNOWN) {
if (code == SDL_SCANCODE_PRINTSCREEN &&
keyboardState[code] == SDL_RELEASED) {
SDL_SendKeyboardKey(WIN_GetEventTimestamp(), SDL_GLOBAL_KEYBOARD_ID, SDL_PRESSED, code);
diff --git a/src/video/windows/SDL_windowsevents.h b/src/video/windows/SDL_windowsevents.h
index 1b01ba4bdd6d4..5acd69b12c221 100644
--- a/src/video/windows/SDL_windowsevents.h
+++ b/src/video/windows/SDL_windowsevents.h
@@ -30,7 +30,7 @@ extern HINSTANCE SDL_Instance;
extern LRESULT CALLBACK WIN_KeyboardHookProc(int nCode, WPARAM wParam, LPARAM lParam);
extern LRESULT CALLBACK WIN_WindowProc(HWND hwnd, UINT msg, WPARAM wParam,
LPARAM lParam);
-extern void WIN_PollRawMouseInput(void);
+extern void WIN_PollRawInput(SDL_VideoDevice *_this);
extern void WIN_CheckKeyboardAndMouseHotplug(SDL_VideoDevice *_this, SDL_bool initial_check);
extern void WIN_PumpEvents(SDL_VideoDevice *_this);
extern void WIN_SendWakeupEvent(SDL_VideoDevice *_this, SDL_Window *window);
diff --git a/src/video/windows/SDL_windowsmouse.c b/src/video/windows/SDL_windowsmouse.c
index 5485f0213211a..74ddae62472e3 100644
--- a/src/video/windows/SDL_windowsmouse.c
+++ b/src/video/windows/SDL_windowsmouse.c
@@ -33,24 +33,24 @@ DWORD SDL_last_warp_time = 0;
HCURSOR SDL_cursor = NULL;
static SDL_Cursor *SDL_blank_cursor = NULL;
-static int rawInputEnableCount = 0;
-
typedef struct
{
HANDLE ready_event;
HANDLE done_event;
HANDLE thread;
-} RawMouseThreadData;
+} RawInputThreadData;
-static RawMouseThreadData thread_data = {
+static RawInputThreadData thread_data = {
INVALID_HANDLE_VALUE,
INVALID_HANDLE_VALUE,
INVALID_HANDLE_VALUE
};
-static DWORD WINAPI WIN_RawMouseThread(LPVOID param)
+static DWORD WINAPI WIN_RawInputThread(LPVOID param)
{
- RAWINPUTDEVICE rawMouse;
+ 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);
@@ -58,76 +58,83 @@ static DWORD WINAPI WIN_RawMouseThread(LPVOID param)
return 0;
}
- rawMouse.usUsagePage = USB_USAGEPAGE_GENERIC_DESKTOP;
- rawMouse.usUsage = USB_USAGE_GENERIC_MOUSE;
- rawMouse.dwFlags = 0;
- rawMouse.hwndTarget = window;
+ 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(&rawMouse, 1, sizeof(rawMouse))) {
+ if (!RegisterRawInputDevices(devices, SDL_arraysize(devices), sizeof(devices[0]))) {
DestroyWindow(window);
return 0;
}
- /* Make sure we get mouse events as soon as possible */
+ /* Make sure we get events as soon as possible */
SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL);
/* Tell the parent we're ready to go! */
- SetEvent(thread_data.ready_event);
+ SetEvent(data->ready_event);
for ( ; ; ) {
- if (MsgWaitForMultipleObjects(1, &thread_data.done_event, 0, INFINITE, QS_RAWINPUT) != WAIT_OBJECT_0 + 1) {
+ 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_PollRawMouseInput();
+ WIN_PollRawInput(_this);
}
- rawMouse.dwFlags |= RIDEV_REMOVE;
- RegisterRawInputDevices(&rawMouse, 1, sizeof(rawMouse));
+ devices[0].dwFlags |= RIDEV_REMOVE;
+ devices[1].dwFlags |= RIDEV_REMOVE;
+ RegisterRawInputDevices(devices, SDL_arraysize(devices), sizeof(devices[0]));
DestroyWindow(window);
return 0;
}
-static void CleanupRawMouseThreadData(void)
+static void CleanupRawInputThreadData(RawInputThreadData *data)
{
- if (thread_data.thread != INVALID_HANDLE_VALUE) {
- SetEvent(thread_data.done_event);
- WaitForSingleObject(thread_data.thread, 500);
- CloseHandle(thread_data.thread);
- thread_data.thread = INVALID_HANDLE_VALUE;
+ if (data->thread != INVALID_HANDLE_VALUE) {
+ SetEvent(data->done_event);
+ WaitForSingleObject(data->thread, 500);
+ CloseHandle(data->thread);
+ data->thread = INVALID_HANDLE_VALUE;
}
- if (thread_data.ready_event != INVALID_HANDLE_VALUE) {
- CloseHandle(thread_data.ready_event);
- thread_data.ready_event = INVALID_HANDLE_VALUE;
+ if (data->ready_event != INVALID_HANDLE_VALUE) {
+ CloseHandle(data->ready_event);
+ data->ready_event = INVALID_HANDLE_VALUE;
}
- if (thread_data.done_event != INVALID_HANDLE_VALUE) {
- CloseHandle(thread_data.done_event);
- thread_data.done_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_bool enabled)
+static int ToggleRawInput(SDL_VideoDevice *_this, SDL_bool enabled)
{
+ SDL_VideoData *data = _this->driverdata;
int result = -1;
if (enabled) {
- rawInputEnableCount++;
- if (rawInputEnableCount > 1) {
+ ++data->raw_input_enable_count;
+ if (data->raw_input_enable_count > 1) {
return 0; /* already done. */
}
} else {
- if (rawInputEnableCount == 0) {
+ if (data->raw_input_enable_count == 0) {
return 0; /* already done. */
}
- rawInputEnableCount--;
- if (rawInputEnableCount > 0) {
+ --data->raw_input_enable_count;
+ if (data->raw_input_enable_count > 0) {
return 0; /* not time to disable yet */
}
}
@@ -147,7 +154,7 @@ static int ToggleRawInput(SDL_bool enabled)
goto done;
}
- thread_data.thread = CreateThread(NULL, 0, WIN_RawMouseThread, &thread_data, 0, NULL);
+ thread_data.thread = CreateThread(NULL, 0, WIN_RawInputThread, &thread_data, 0, NULL);
if (thread_data.thread == INVALID_HANDLE_VALUE) {
WIN_SetError("CreateThread");
goto done;
@@ -162,16 +169,16 @@ static int ToggleRawInput(SDL_bool enabled)
}
result = 0;
} else {
- CleanupRawMouseThreadData();
+ CleanupRawInputThreadData(&thread_data);
result = 0;
}
done:
if (enabled && result < 0) {
- CleanupRawMouseThreadData();
+ CleanupRawInputThreadData(&thread_data);
- /* Reset rawInputEnableCount so we can try again */
- rawInputEnableCount = 0;
+ /* Reset so we can try again */
+ data->raw_input_enable_count = 0;
}
return result;
}
@@ -509,7 +516,7 @@ static int WIN_WarpMouseGlobal(float x, float y)
static int WIN_SetRelativeMouseMode(SDL_bool enabled)
{
- return ToggleRawInput(enabled);
+ return ToggleRawInput(SDL_GetVideoDevice(), enabled);
}
static int WIN_CaptureMouse(SDL_Window *window)
@@ -574,9 +581,10 @@ void WIN_InitMouse(SDL_VideoDevice *_this)
void WIN_QuitMouse(SDL_VideoDevice *_this)
{
- if (rawInputEnableCount) { /* force RAWINPUT off here. */
- rawInputEnableCount = 1;
- ToggleRawInput(SDL_FALSE);
+ 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) {
diff --git a/src/video/windows/SDL_windowsvideo.h b/src/video/windows/SDL_windowsvideo.h
index 4b3d3c27acbfa..9dbcc01006f96 100644
--- a/src/video/windows/SDL_windowsvideo.h
+++ b/src/video/windows/SDL_windowsvideo.h
@@ -406,6 +406,8 @@ struct SDL_VideoData
SDL_bool cleared;
+ int raw_input_enable_count;
+
#ifndef SDL_DISABLE_WINDOWS_IME
SDL_bool ime_com_initialized;
struct ITfThreadMgr *ime_threadmgr;