windows: enable raw keyboard input when raw mouse input is enabled

This commit is contained in:
Sam Lantinga 2024-03-25 09:49:35 -07:00
parent 35d335e61f
commit 012fc1e32b
4 changed files with 96 additions and 58 deletions

View file

@ -512,9 +512,13 @@ WIN_KeyboardHookProc(int nCode, WPARAM wParam, LPARAM lParam)
} }
if (wParam == WM_KEYDOWN || wParam == WM_SYSKEYDOWN) { 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 { } 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 /* 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 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) static void WIN_HandleRawMouseInput(Uint64 timestamp, SDL_WindowData *data, HANDLE hDevice, RAWMOUSE *rawmouse)
{ {
SDL_Mouse *mouse = SDL_GetMouse();
SDL_MouseID mouseID; 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) { if (GetMouseMessageSource(rawmouse->ulExtraInformation) == SDL_MOUSE_EVENT_SOURCE_TOUCH) {
return; return;
} }
@ -619,17 +629,34 @@ static void WIN_HandleRawMouseInput(Uint64 timestamp, SDL_WindowData *data, HAND
WIN_CheckRawMouseButtons(timestamp, hDevice, rawmouse->usButtonFlags, data, mouseID); 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_Window *window;
SDL_WindowData *data; SDL_WindowData *data;
UINT size, count, i, total = 0; UINT size, count, i, total = 0;
RAWINPUT *input; RAWINPUT *input;
Uint64 now; Uint64 now;
/* We only use raw mouse input in relative mode */ if (_this->driverdata->raw_input_enable_count == 0) {
if (!mouse->relative_mode || mouse->relative_mode_warp) {
return; return;
} }
@ -695,6 +722,9 @@ void WIN_PollRawMouseInput(void)
if (input->header.dwType == RIM_TYPEMOUSE) { if (input->header.dwType == RIM_TYPEMOUSE) {
RAWMOUSE *rawmouse = (RAWMOUSE *)((BYTE *)input + data->rawinput_offset); RAWMOUSE *rawmouse = (RAWMOUSE *)((BYTE *)input + data->rawinput_offset);
WIN_HandleRawMouseInput(timestamp, window->driverdata, input->header.hDevice, rawmouse); 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 */ #if 0 /* We handle raw input all at once instead of using a syscall for each mouse event */
case WM_INPUT: case WM_INPUT:
{ {
SDL_Mouse *mouse = SDL_GetMouse();
HRAWINPUT hRawInput = (HRAWINPUT)lParam; HRAWINPUT hRawInput = (HRAWINPUT)lParam;
RAWINPUT inp; RAWINPUT inp;
UINT size = sizeof(inp); UINT size = sizeof(inp);
/* We only use raw mouse input in relative mode */ if (data->raw_input_enable_count == 0) {
if (!mouse->relative_mode || mouse->relative_mode_warp) {
break; 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)); GetRawInputData(hRawInput, RID_INPUT, &inp, &size, sizeof(RAWINPUTHEADER));
/* Mouse data (ignoring synthetic mouse events generated for touchscreens) */
if (inp.header.dwType == RIM_TYPEMOUSE) { if (inp.header.dwType == RIM_TYPEMOUSE) {
WIN_HandleRawMouseInput(WIN_GetEventTimestamp(), data, inp.header.hDevice, &inp.data.mouse); 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; } break;
#endif #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); 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); SDL_Scancode code = WindowsScanCodeToSDLScanCode(lParam, wParam);
const Uint8 *keyboardState = SDL_GetKeyboardState(NULL); 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 && if (code == SDL_SCANCODE_PRINTSCREEN &&
keyboardState[code] == SDL_RELEASED) { keyboardState[code] == SDL_RELEASED) {
SDL_SendKeyboardKey(WIN_GetEventTimestamp(), SDL_GLOBAL_KEYBOARD_ID, SDL_PRESSED, code); SDL_SendKeyboardKey(WIN_GetEventTimestamp(), SDL_GLOBAL_KEYBOARD_ID, SDL_PRESSED, code);

View file

@ -30,7 +30,7 @@ extern HINSTANCE SDL_Instance;
extern LRESULT CALLBACK WIN_KeyboardHookProc(int nCode, WPARAM wParam, LPARAM lParam); extern LRESULT CALLBACK WIN_KeyboardHookProc(int nCode, WPARAM wParam, LPARAM lParam);
extern LRESULT CALLBACK WIN_WindowProc(HWND hwnd, UINT msg, WPARAM wParam, extern LRESULT CALLBACK WIN_WindowProc(HWND hwnd, UINT msg, WPARAM wParam,
LPARAM lParam); 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_CheckKeyboardAndMouseHotplug(SDL_VideoDevice *_this, SDL_bool initial_check);
extern void WIN_PumpEvents(SDL_VideoDevice *_this); extern void WIN_PumpEvents(SDL_VideoDevice *_this);
extern void WIN_SendWakeupEvent(SDL_VideoDevice *_this, SDL_Window *window); extern void WIN_SendWakeupEvent(SDL_VideoDevice *_this, SDL_Window *window);

View file

@ -33,24 +33,24 @@ DWORD SDL_last_warp_time = 0;
HCURSOR SDL_cursor = NULL; HCURSOR SDL_cursor = NULL;
static SDL_Cursor *SDL_blank_cursor = NULL; static SDL_Cursor *SDL_blank_cursor = NULL;
static int rawInputEnableCount = 0;
typedef struct typedef struct
{ {
HANDLE ready_event; HANDLE ready_event;
HANDLE done_event; HANDLE done_event;
HANDLE thread; HANDLE thread;
} RawMouseThreadData; } RawInputThreadData;
static RawMouseThreadData thread_data = { static RawInputThreadData thread_data = {
INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE,
INVALID_HANDLE_VALUE, 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; HWND window;
window = CreateWindowEx(0, TEXT("Message"), NULL, 0, 0, 0, 0, 0, HWND_MESSAGE, NULL, NULL, NULL); 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; return 0;
} }
rawMouse.usUsagePage = USB_USAGEPAGE_GENERIC_DESKTOP; devices[0].usUsagePage = USB_USAGEPAGE_GENERIC_DESKTOP;
rawMouse.usUsage = USB_USAGE_GENERIC_MOUSE; devices[0].usUsage = USB_USAGE_GENERIC_MOUSE;
rawMouse.dwFlags = 0; devices[0].dwFlags = 0;
rawMouse.hwndTarget = window; devices[0].hwndTarget = window;
if (!RegisterRawInputDevices(&rawMouse, 1, sizeof(rawMouse))) { 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); DestroyWindow(window);
return 0; 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); SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL);
/* Tell the parent we're ready to go! */ /* Tell the parent we're ready to go! */
SetEvent(thread_data.ready_event); SetEvent(data->ready_event);
for ( ; ; ) { 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; break;
} }
/* Clear the queue status so MsgWaitForMultipleObjects() will wait again */ /* Clear the queue status so MsgWaitForMultipleObjects() will wait again */
(void)GetQueueStatus(QS_RAWINPUT); (void)GetQueueStatus(QS_RAWINPUT);
WIN_PollRawMouseInput(); WIN_PollRawInput(_this);
} }
rawMouse.dwFlags |= RIDEV_REMOVE; devices[0].dwFlags |= RIDEV_REMOVE;
RegisterRawInputDevices(&rawMouse, 1, sizeof(rawMouse)); devices[1].dwFlags |= RIDEV_REMOVE;
RegisterRawInputDevices(devices, SDL_arraysize(devices), sizeof(devices[0]));
DestroyWindow(window); DestroyWindow(window);
return 0; return 0;
} }
static void CleanupRawMouseThreadData(void) static void CleanupRawInputThreadData(RawInputThreadData *data)
{ {
if (thread_data.thread != INVALID_HANDLE_VALUE) { if (data->thread != INVALID_HANDLE_VALUE) {
SetEvent(thread_data.done_event); SetEvent(data->done_event);
WaitForSingleObject(thread_data.thread, 500); WaitForSingleObject(data->thread, 500);
CloseHandle(thread_data.thread); CloseHandle(data->thread);
thread_data.thread = INVALID_HANDLE_VALUE; data->thread = INVALID_HANDLE_VALUE;
} }
if (thread_data.ready_event != INVALID_HANDLE_VALUE) { if (data->ready_event != INVALID_HANDLE_VALUE) {
CloseHandle(thread_data.ready_event); CloseHandle(data->ready_event);
thread_data.ready_event = INVALID_HANDLE_VALUE; data->ready_event = INVALID_HANDLE_VALUE;
} }
if (thread_data.done_event != INVALID_HANDLE_VALUE) { if (data->done_event != INVALID_HANDLE_VALUE) {
CloseHandle(thread_data.done_event); CloseHandle(data->done_event);
thread_data.done_event = INVALID_HANDLE_VALUE; 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; int result = -1;
if (enabled) { if (enabled) {
rawInputEnableCount++; ++data->raw_input_enable_count;
if (rawInputEnableCount > 1) { if (data->raw_input_enable_count > 1) {
return 0; /* already done. */ return 0; /* already done. */
} }
} else { } else {
if (rawInputEnableCount == 0) { if (data->raw_input_enable_count == 0) {
return 0; /* already done. */ return 0; /* already done. */
} }
rawInputEnableCount--; --data->raw_input_enable_count;
if (rawInputEnableCount > 0) { if (data->raw_input_enable_count > 0) {
return 0; /* not time to disable yet */ return 0; /* not time to disable yet */
} }
} }
@ -147,7 +154,7 @@ static int ToggleRawInput(SDL_bool enabled)
goto done; 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) { if (thread_data.thread == INVALID_HANDLE_VALUE) {
WIN_SetError("CreateThread"); WIN_SetError("CreateThread");
goto done; goto done;
@ -162,16 +169,16 @@ static int ToggleRawInput(SDL_bool enabled)
} }
result = 0; result = 0;
} else { } else {
CleanupRawMouseThreadData(); CleanupRawInputThreadData(&thread_data);
result = 0; result = 0;
} }
done: done:
if (enabled && result < 0) { if (enabled && result < 0) {
CleanupRawMouseThreadData(); CleanupRawInputThreadData(&thread_data);
/* Reset rawInputEnableCount so we can try again */ /* Reset so we can try again */
rawInputEnableCount = 0; data->raw_input_enable_count = 0;
} }
return result; return result;
} }
@ -509,7 +516,7 @@ static int WIN_WarpMouseGlobal(float x, float y)
static int WIN_SetRelativeMouseMode(SDL_bool enabled) static int WIN_SetRelativeMouseMode(SDL_bool enabled)
{ {
return ToggleRawInput(enabled); return ToggleRawInput(SDL_GetVideoDevice(), enabled);
} }
static int WIN_CaptureMouse(SDL_Window *window) static int WIN_CaptureMouse(SDL_Window *window)
@ -574,9 +581,10 @@ void WIN_InitMouse(SDL_VideoDevice *_this)
void WIN_QuitMouse(SDL_VideoDevice *_this) void WIN_QuitMouse(SDL_VideoDevice *_this)
{ {
if (rawInputEnableCount) { /* force RAWINPUT off here. */ SDL_VideoData *data = _this->driverdata;
rawInputEnableCount = 1; if (data->raw_input_enable_count) { /* force RAWINPUT off here. */
ToggleRawInput(SDL_FALSE); data->raw_input_enable_count = 1;
ToggleRawInput(_this, SDL_FALSE);
} }
if (SDL_blank_cursor) { if (SDL_blank_cursor) {

View file

@ -406,6 +406,8 @@ struct SDL_VideoData
SDL_bool cleared; SDL_bool cleared;
int raw_input_enable_count;
#ifndef SDL_DISABLE_WINDOWS_IME #ifndef SDL_DISABLE_WINDOWS_IME
SDL_bool ime_com_initialized; SDL_bool ime_com_initialized;
struct ITfThreadMgr *ime_threadmgr; struct ITfThreadMgr *ime_threadmgr;