diff --git a/src/video/windows/SDL_windowsevents.c b/src/video/windows/SDL_windowsevents.c index c0a1e4d41e..63bc28a76e 100644 --- a/src/video/windows/SDL_windowsevents.c +++ b/src/video/windows/SDL_windowsevents.c @@ -607,34 +607,14 @@ static void WIN_HandleRawMouseInput(Uint64 timestamp, SDL_WindowData *data, RAWM WIN_CheckRawMouseButtons(timestamp, rawmouse->usButtonFlags, data, mouseID); } -/* The layout of memory for data returned from GetRawInputBuffer(), documented here: - * https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getrawinputbuffer - */ -typedef struct -{ - union - { - RAWINPUTHEADER header; - BYTE padding[24]; - } hdr; - - union - { - RAWMOUSE mouse; - RAWKEYBOARD keyboard; - RAWHID hid; - } data; - -} ALIGNED_RAWINPUT; - -static void WIN_PollRawMouseInput() +void WIN_PollRawMouseInput(void) { SDL_Mouse *mouse = SDL_GetMouse(); SDL_Window *window; SDL_WindowData *data; UINT size, count, i, total = 0; RAWINPUT *input; - Uint64 now, timestamp, increment; + Uint64 now; /* We only use raw mouse input in relative mode */ if (!mouse->relative_mode || mouse->relative_mode_warp) { @@ -649,6 +629,14 @@ static void WIN_PollRawMouseInput() data = window->driverdata; if (data->rawinput_size == 0) { + BOOL isWow64; + + data->rawinput_offset = sizeof(RAWINPUTHEADER); + if (IsWow64Process(GetCurrentProcess(), &isWow64) && isWow64) { + /* We're going to get 64-bit data, so use the 64-bit RAWINPUTHEADER size */ + data->rawinput_offset += 8; + } + if (GetRawInputBuffer(NULL, &data->rawinput_size, sizeof(RAWINPUTHEADER)) == (UINT)-1) { return; } @@ -679,13 +667,21 @@ static void WIN_PollRawMouseInput() now = SDL_GetTicksNS(); if (total > 0) { - /* We'll spread these events over the time since the last poll */ - timestamp = data->last_rawinput_poll; - increment = (now - timestamp) / total; + Uint64 timestamp, increment; + Uint64 delta = (now - data->last_rawinput_poll); + if (total > 1 && delta <= SDL_MS_TO_NS(100)) { + /* We'll spread these events over the time since the last poll */ + timestamp = data->last_rawinput_poll; + increment = delta / total; + } else { + /* Do we want to track the update rate per device? */ + timestamp = now; + increment = 0; + } for (i = 0, input = data->rawinput; i < total; ++i, input = NEXTRAWINPUTBLOCK(input)) { timestamp += increment; if (input->header.dwType == RIM_TYPEMOUSE) { - RAWMOUSE *rawmouse = &(((ALIGNED_RAWINPUT *)input)->data.mouse); + RAWMOUSE *rawmouse = (RAWMOUSE *)((BYTE *)input + data->rawinput_offset); WIN_HandleRawMouseInput(timestamp, window->driverdata, rawmouse); } } @@ -1763,8 +1759,6 @@ void WIN_PumpEvents(SDL_VideoDevice *_this) SDL_Window *focusWindow; #endif - WIN_PollRawMouseInput(); - if (g_WindowsEnableMessageLoop) { SDL_processing_messages = SDL_TRUE; diff --git a/src/video/windows/SDL_windowsevents.h b/src/video/windows/SDL_windowsevents.h index 414019ba9b..77f1dd464a 100644 --- a/src/video/windows/SDL_windowsevents.h +++ b/src/video/windows/SDL_windowsevents.h @@ -30,6 +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_PumpEvents(SDL_VideoDevice *_this); extern void WIN_SendWakeupEvent(SDL_VideoDevice *_this, SDL_Window *window); extern int WIN_WaitEventTimeout(SDL_VideoDevice *_this, Sint64 timeoutNS); diff --git a/src/video/windows/SDL_windowsmouse.c b/src/video/windows/SDL_windowsmouse.c index 4ff7950406..a078e039d2 100644 --- a/src/video/windows/SDL_windowsmouse.c +++ b/src/video/windows/SDL_windowsmouse.c @@ -23,9 +23,11 @@ #if defined(SDL_VIDEO_DRIVER_WINDOWS) && !defined(__XBOXONE__) && !defined(__XBOXSERIES__) #include "SDL_windowsvideo.h" +#include "SDL_windowsevents.h" -#include "../../events/SDL_mouse_c.h" #include "../SDL_video_c.h" +#include "../../events/SDL_mouse_c.h" +#include "../../joystick/usb_ids.h" DWORD SDL_last_warp_time = 0; HCURSOR SDL_cursor = NULL; @@ -33,9 +35,84 @@ static SDL_Cursor *SDL_blank_cursor = NULL; static int rawInputEnableCount = 0; +typedef struct +{ + HANDLE ready_event; + HANDLE done_event; + HANDLE thread; +} RawMouseThreadData; + +static RawMouseThreadData thread_data = { + INVALID_HANDLE_VALUE, + INVALID_HANDLE_VALUE, + INVALID_HANDLE_VALUE +}; + +static DWORD WINAPI WIN_RawMouseThread(LPVOID param) +{ + RAWINPUTDEVICE rawMouse; + HWND window; + + window = CreateWindowEx(0, TEXT("Message"), NULL, 0, 0, 0, 0, 0, HWND_MESSAGE, NULL, NULL, NULL); + if (!window) { + return 0; + } + + rawMouse.usUsagePage = USB_USAGEPAGE_GENERIC_DESKTOP; + rawMouse.usUsage = USB_USAGE_GENERIC_MOUSE; + rawMouse.dwFlags = 0; + rawMouse.hwndTarget = window; + + if (!RegisterRawInputDevices(&rawMouse, 1, sizeof(rawMouse))) { + DestroyWindow(window); + return 0; + } + + /* Tell the parent we're ready to go! */ + SetEvent(thread_data.ready_event); + + for ( ; ; ) { + if (MsgWaitForMultipleObjects(1, &thread_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(); + } + + rawMouse.dwFlags |= RIDEV_REMOVE; + RegisterRawInputDevices(&rawMouse, 1, sizeof(rawMouse)); + + DestroyWindow(window); + + return 0; +} + +static void CleanupRawMouseThreadData(void) +{ + 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 (thread_data.ready_event != INVALID_HANDLE_VALUE) { + CloseHandle(thread_data.ready_event); + thread_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; + } +} + static int ToggleRawInput(SDL_bool enabled) { - RAWINPUTDEVICE rawMouse = { 0x01, 0x02, 0, NULL }; /* Mouse: UsagePage = 1, Usage = 2 */ + int result = -1; if (enabled) { rawInputEnableCount++; @@ -52,23 +129,48 @@ static int ToggleRawInput(SDL_bool enabled) } } - if (!enabled) { - rawMouse.dwFlags |= RIDEV_REMOVE; - } + if (enabled) { + HANDLE handles[2]; - /* (Un)register raw input for mice */ - if (RegisterRawInputDevices(&rawMouse, 1, sizeof(RAWINPUTDEVICE)) == FALSE) { - /* Reset the enable count, otherwise subsequent enable calls will - believe raw input is enabled */ - rawInputEnableCount = 0; - - /* Only return an error when registering. If we unregister and fail, - then it's probably that we unregistered twice. That's OK. */ - if (enabled) { - return SDL_Unsupported(); + 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_RawMouseThread, &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 { + CleanupRawMouseThreadData(); + result = 0; } - return 0; + +done: + if (enabled && result < 0) { + CleanupRawMouseThreadData(); + + /* Reset rawInputEnableCount so we can try again */ + rawInputEnableCount = 0; + } + return result; } static SDL_Cursor *WIN_CreateDefaultCursor() diff --git a/src/video/windows/SDL_windowswindow.h b/src/video/windows/SDL_windowswindow.h index c5ff4ce8c7..835ba622db 100644 --- a/src/video/windows/SDL_windowswindow.h +++ b/src/video/windows/SDL_windowswindow.h @@ -67,6 +67,7 @@ struct SDL_WindowData SDL_bool in_window_deactivation; RECT cursor_clipped_rect; RAWINPUT *rawinput; + UINT rawinput_offset; UINT rawinput_size; UINT rawinput_count; Uint64 last_rawinput_poll;