From a0c5bfa3bd32dc8edc7d5a2a1240fc525b436c8d Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Fri, 27 Nov 2020 13:08:40 -0800 Subject: [PATCH] Moved raw input event processing from the main thread to the joystick thread This allows fast joystick event delivery regardless of what the main thread is doing. --- include/SDL_config_windows.h | 2 + src/joystick/hidapi/SDL_hidapijoystick.c | 4 +- src/joystick/windows/SDL_rawinputjoystick.c | 210 +++++++++--------- src/joystick/windows/SDL_rawinputjoystick_c.h | 6 +- src/joystick/windows/SDL_windowsjoystick.c | 19 +- src/video/windows/SDL_windowswindow.c | 15 +- 6 files changed, 127 insertions(+), 129 deletions(-) diff --git a/include/SDL_config_windows.h b/include/SDL_config_windows.h index fcc33e3ea3632..18a3638295e9d 100644 --- a/include/SDL_config_windows.h +++ b/include/SDL_config_windows.h @@ -215,7 +215,9 @@ typedef unsigned int uintptr_t; /* Enable various input drivers */ #define SDL_JOYSTICK_DINPUT 1 #define SDL_JOYSTICK_HIDAPI 1 +#ifndef __WINRT__ #define SDL_JOYSTICK_RAWINPUT 1 +#endif #define SDL_JOYSTICK_VIRTUAL 1 #ifdef SDL_WINDOWS10_SDK #define SDL_JOYSTICK_WGI 1 diff --git a/src/joystick/hidapi/SDL_hidapijoystick.c b/src/joystick/hidapi/SDL_hidapijoystick.c index 9aaf4c31a5bf8..53442b5d4ac06 100644 --- a/src/joystick/hidapi/SDL_hidapijoystick.c +++ b/src/joystick/hidapi/SDL_hidapijoystick.c @@ -192,7 +192,7 @@ HIDAPI_InitializeDiscovery() #if defined(__WIN32__) SDL_HIDAPI_discovery.m_nThreadID = SDL_ThreadID(); - SDL_memset(&SDL_HIDAPI_discovery.m_wndClass, 0x0, sizeof(SDL_HIDAPI_discovery.m_wndClass)); + SDL_zero(SDL_HIDAPI_discovery.m_wndClass); SDL_HIDAPI_discovery.m_wndClass.hInstance = GetModuleHandle(NULL); SDL_HIDAPI_discovery.m_wndClass.lpszClassName = "SDL_HIDAPI_DEVICE_DETECTION"; SDL_HIDAPI_discovery.m_wndClass.lpfnWndProc = ControllerWndProc; /* This function is called by windows */ @@ -203,8 +203,8 @@ HIDAPI_InitializeDiscovery() { DEV_BROADCAST_DEVICEINTERFACE_A devBroadcast; - SDL_memset( &devBroadcast, 0x0, sizeof( devBroadcast ) ); + SDL_zero(devBroadcast); devBroadcast.dbcc_size = sizeof( devBroadcast ); devBroadcast.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE; devBroadcast.dbcc_classguid = GUID_DEVINTERFACE_USB_DEVICE; diff --git a/src/joystick/windows/SDL_rawinputjoystick.c b/src/joystick/windows/SDL_rawinputjoystick.c index 2f978266e8ba3..b9d63b78022db 100644 --- a/src/joystick/windows/SDL_rawinputjoystick.c +++ b/src/joystick/windows/SDL_rawinputjoystick.c @@ -37,6 +37,7 @@ #include "SDL_endian.h" #include "SDL_events.h" #include "SDL_hints.h" +#include "SDL_mutex.h" #include "SDL_timer.h" #include "../usb_ids.h" #include "../SDL_sysjoystick.h" @@ -84,12 +85,10 @@ typedef struct WindowsGamingInputGamepadState WindowsGamingInputGamepadState; #define GIDC_REMOVAL 2 #endif -/* external variables referenced. */ -extern HWND SDL_HelperWindow; - static SDL_bool SDL_RAWINPUT_inited = SDL_FALSE; static int SDL_RAWINPUT_numjoysticks = 0; +static SDL_mutex *SDL_RAWINPUT_mutex = NULL; static void RAWINPUT_JoystickClose(SDL_Joystick *joystick); @@ -625,40 +624,10 @@ RAWINPUT_QuitWindowsGamingInput(RAWINPUT_DeviceContext *ctx) #endif /* SDL_JOYSTICK_RAWINPUT_WGI */ -/* Most of the time the raw input messages will get dispatched in the main event loop, - * but sometimes we want to get any pending device change messages immediately. - */ -static void -RAWINPUT_GetPendingDeviceChanges(void) -{ - MSG msg; - while (PeekMessage(&msg, SDL_HelperWindow, WM_INPUT_DEVICE_CHANGE, WM_INPUT_DEVICE_CHANGE + 1, PM_REMOVE)) { - TranslateMessage(&msg); - DispatchMessage(&msg); - } -} - -static SDL_bool pump_device_events; -static void -RAWINPUT_GetPendingDeviceInput(void) -{ - if (pump_device_events) { - MSG msg; - while (PeekMessage(&msg, SDL_HelperWindow, WM_INPUT, WM_INPUT + 1, PM_REMOVE)) { - TranslateMessage(&msg); - DispatchMessage(&msg); - } - pump_device_events = SDL_FALSE; - } -} - static int RAWINPUT_JoystickInit(void) { - int ii; - RAWINPUTDEVICE rid[SDL_arraysize(subscribed_devices)]; SDL_assert(!SDL_RAWINPUT_inited); - SDL_assert(SDL_HelperWindow); if (!SDL_GetHintBoolean(SDL_HINT_JOYSTICK_RAWINPUT, SDL_TRUE)) { return -1; @@ -668,25 +637,9 @@ RAWINPUT_JoystickInit(void) return -1; } - for (ii = 0; ii < SDL_arraysize(subscribed_devices); ii++) { - rid[ii].usUsagePage = USB_USAGEPAGE_GENERIC_DESKTOP; - rid[ii].usUsage = subscribed_devices[ii]; - rid[ii].dwFlags = RIDEV_DEVNOTIFY | RIDEV_INPUTSINK; /* Receive messages when in background, including device add/remove */ - rid[ii].hwndTarget = SDL_HelperWindow; - } - - if (!RegisterRawInputDevices(rid, SDL_arraysize(rid), sizeof(RAWINPUTDEVICE))) { - SDL_SetError("Couldn't initialize RAWINPUT"); - WIN_UnloadHIDDLL(); - return -1; - } - + SDL_RAWINPUT_mutex = SDL_CreateMutex(); SDL_RAWINPUT_inited = SDL_TRUE; - /* Get initial controller connect messages */ - RAWINPUT_GetPendingDeviceChanges(); - pump_device_events = SDL_TRUE; - return 0; } @@ -930,8 +883,6 @@ RAWINPUT_PostUpdate(void) guide_button_candidate.joystick = NULL; #endif /* SDL_JOYSTICK_RAWINPUT_MATCHING */ - - pump_device_events = SDL_TRUE; } SDL_bool @@ -945,9 +896,6 @@ RAWINPUT_IsDevicePresent(Uint16 vendor_id, Uint16 product_id, Uint16 version, co { SDL_RAWINPUT_Device *device; - /* Make sure the device list is completely up to date when we check for device presence */ - RAWINPUT_GetPendingDeviceChanges(); - /* If we're being asked about a device, that means another API just detected one, so rescan */ #ifdef SDL_JOYSTICK_RAWINPUT_XINPUT xinput_device_change = SDL_TRUE; @@ -983,8 +931,6 @@ RAWINPUT_IsDevicePresent(Uint16 vendor_id, Uint16 product_id, Uint16 version, co static void RAWINPUT_JoystickDetect(void) { - RAWINPUT_GetPendingDeviceChanges(); - RAWINPUT_PostUpdate(); } @@ -1727,8 +1673,6 @@ RAWINPUT_UpdateOtherAPIs(SDL_Joystick *joystick) static void RAWINPUT_JoystickUpdate(SDL_Joystick *joystick) { - RAWINPUT_GetPendingDeviceInput(); - RAWINPUT_UpdateOtherAPIs(joystick); } @@ -1776,74 +1720,115 @@ RAWINPUT_JoystickClose(SDL_Joystick *joystick) } } -LRESULT RAWINPUT_WindowProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) +SDL_bool +RAWINPUT_RegisterNotifications(HWND hWnd) { - if (!SDL_RAWINPUT_inited) - return -1; + RAWINPUTDEVICE rid[SDL_arraysize(subscribed_devices)]; + int i; - switch (msg) - { - case WM_INPUT_DEVICE_CHANGE: - { - HANDLE hDevice = (HANDLE)lParam; - switch (wParam) { - case GIDC_ARRIVAL: - RAWINPUT_AddDevice(hDevice); - break; - case GIDC_REMOVAL: { - SDL_RAWINPUT_Device *device; - device = RAWINPUT_DeviceFromHandle(hDevice); - if (device) { - RAWINPUT_DelDevice(device, SDL_TRUE); + for (i = 0; i < SDL_arraysize(subscribed_devices); i++) { + rid[i].usUsagePage = USB_USAGEPAGE_GENERIC_DESKTOP; + rid[i].usUsage = subscribed_devices[i]; + rid[i].dwFlags = RIDEV_DEVNOTIFY | RIDEV_INPUTSINK; /* Receive messages when in background, including device add/remove */ + rid[i].hwndTarget = hWnd; + } + + if (!RegisterRawInputDevices(rid, SDL_arraysize(rid), sizeof(RAWINPUTDEVICE))) { + SDL_SetError("Couldn't register for raw input events"); + return SDL_FALSE; + } + return SDL_TRUE; +} + +void +RAWINPUT_UnregisterNotifications() +{ + int i; + RAWINPUTDEVICE rid[SDL_arraysize(subscribed_devices)]; + + for (i = 0; i < SDL_arraysize(subscribed_devices); i++) { + rid[i].usUsagePage = USB_USAGEPAGE_GENERIC_DESKTOP; + rid[i].usUsage = subscribed_devices[i]; + rid[i].dwFlags = RIDEV_REMOVE; + rid[i].hwndTarget = NULL; + } + + if (!RegisterRawInputDevices(rid, SDL_arraysize(rid), sizeof(RAWINPUTDEVICE))) { + SDL_SetError("Couldn't unregister for raw input events"); + return; + } +} + +LRESULT CALLBACK +RAWINPUT_WindowProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + LRESULT result = -1; + + SDL_LockMutex(SDL_RAWINPUT_mutex); + + if (SDL_RAWINPUT_inited) { + switch (msg) { + case WM_INPUT_DEVICE_CHANGE: + { + HANDLE hDevice = (HANDLE)lParam; + switch (wParam) { + case GIDC_ARRIVAL: + RAWINPUT_AddDevice(hDevice); + break; + case GIDC_REMOVAL: + { + SDL_RAWINPUT_Device *device; + device = RAWINPUT_DeviceFromHandle(hDevice); + if (device) { + RAWINPUT_DelDevice(device, SDL_TRUE); + } + break; + } + default: + break; } - } break; - default: - return 0; } - } - return 0; - - case WM_INPUT: - { - Uint8 data[sizeof(RAWINPUTHEADER) + sizeof(RAWHID) + USB_PACKET_LENGTH]; - UINT buffer_size = SDL_arraysize(data); - - if ((int)GetRawInputData((HRAWINPUT)lParam, RID_INPUT, data, &buffer_size, sizeof(RAWINPUTHEADER)) > 0) { - PRAWINPUT raw_input = (PRAWINPUT)data; - SDL_RAWINPUT_Device *device = RAWINPUT_DeviceFromHandle(raw_input->header.hDevice); - if (device) { - SDL_Joystick *joystick = device->joystick; - if (joystick) { - RAWINPUT_HandleStatePacket(joystick, raw_input->data.hid.bRawData, raw_input->data.hid.dwSizeHid); + result = 0; + break; + + case WM_INPUT: + { + Uint8 data[sizeof(RAWINPUTHEADER) + sizeof(RAWHID) + USB_PACKET_LENGTH]; + UINT buffer_size = SDL_arraysize(data); + + if ((int)GetRawInputData((HRAWINPUT)lParam, RID_INPUT, data, &buffer_size, sizeof(RAWINPUTHEADER)) > 0) { + PRAWINPUT raw_input = (PRAWINPUT)data; + SDL_RAWINPUT_Device *device = RAWINPUT_DeviceFromHandle(raw_input->header.hDevice); + if (device) { + SDL_Joystick *joystick = device->joystick; + if (joystick) { + RAWINPUT_HandleStatePacket(joystick, raw_input->data.hid.bRawData, raw_input->data.hid.dwSizeHid); + } } } } + result = 0; + break; } - return 0; } - return -1; + + SDL_UnlockMutex(SDL_RAWINPUT_mutex); + + if (result >= 0) { + return result; + } + return CallWindowProc(DefWindowProc, hWnd, msg, wParam, lParam); } static void RAWINPUT_JoystickQuit(void) { - int ii; - RAWINPUTDEVICE rid[SDL_arraysize(subscribed_devices)]; - - if (!SDL_RAWINPUT_inited) + if (!SDL_RAWINPUT_inited) { return; - - for (ii = 0; ii < SDL_arraysize(subscribed_devices); ii++) { - rid[ii].usUsagePage = USB_USAGEPAGE_GENERIC_DESKTOP; - rid[ii].usUsage = subscribed_devices[ii]; - rid[ii].dwFlags = RIDEV_REMOVE; - rid[ii].hwndTarget = NULL; } - if (!RegisterRawInputDevices(rid, SDL_arraysize(rid), sizeof(RAWINPUTDEVICE))) { - SDL_Log("Couldn't un-register RAWINPUT"); - } - + SDL_LockMutex(SDL_RAWINPUT_mutex); + while (SDL_RAWINPUT_devices) { RAWINPUT_DelDevice(SDL_RAWINPUT_devices, SDL_FALSE); } @@ -1853,6 +1838,11 @@ RAWINPUT_JoystickQuit(void) SDL_RAWINPUT_numjoysticks = 0; SDL_RAWINPUT_inited = SDL_FALSE; + + SDL_UnlockMutex(SDL_RAWINPUT_mutex); + SDL_DestroyMutex(SDL_RAWINPUT_mutex); + SDL_RAWINPUT_mutex = NULL; + } static SDL_bool diff --git a/src/joystick/windows/SDL_rawinputjoystick_c.h b/src/joystick/windows/SDL_rawinputjoystick_c.h index 4543798bad92e..14f77fb354039 100644 --- a/src/joystick/windows/SDL_rawinputjoystick_c.h +++ b/src/joystick/windows/SDL_rawinputjoystick_c.h @@ -27,8 +27,12 @@ extern SDL_bool RAWINPUT_IsEnabled(); /* Return true if a RawInput device is present and supported as a joystick */ extern SDL_bool RAWINPUT_IsDevicePresent(Uint16 vendor_id, Uint16 product_id, Uint16 version, const char *name); +/* Registers for input events */ +extern SDL_bool RAWINPUT_RegisterNotifications(HWND hWnd); +extern void RAWINPUT_UnregisterNotifications(); + /* Returns 0 if message was handled */ -extern LRESULT RAWINPUT_WindowProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); +extern LRESULT CALLBACK RAWINPUT_WindowProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); /* vi: set ts=4 sw=4 expandtab: */ diff --git a/src/joystick/windows/SDL_windowsjoystick.c b/src/joystick/windows/SDL_windowsjoystick.c index fb18faa98fd51..cbabba9e4775d 100644 --- a/src/joystick/windows/SDL_windowsjoystick.c +++ b/src/joystick/windows/SDL_windowsjoystick.c @@ -49,6 +49,7 @@ #include "SDL_windowsjoystick_c.h" #include "SDL_dinputjoystick_c.h" #include "SDL_xinputjoystick_c.h" +#include "SDL_rawinputjoystick_c.h" #include "../../haptic/windows/SDL_dinputhaptic_c.h" /* For haptic hot plugging */ #include "../../haptic/windows/SDL_xinputhaptic_c.h" /* For haptic hot plugging */ @@ -109,9 +110,9 @@ typedef struct /* windowproc for our joystick detect thread message only window, to detect any USB device addition/removal */ static LRESULT CALLBACK -SDL_PrivateJoystickDetectProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) +SDL_PrivateJoystickDetectProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { - switch (message) { + switch (msg) { case WM_DEVICECHANGE: switch (wParam) { case DBT_DEVICEARRIVAL: @@ -130,12 +131,20 @@ SDL_PrivateJoystickDetectProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lPa return 0; } - return DefWindowProc (hwnd, message, wParam, lParam); +#if SDL_JOYSTICK_RAWINPUT + return CallWindowProc(RAWINPUT_WindowProc, hwnd, msg, wParam, lParam); +#else + return CallWindowProc(DefWindowProc, hwnd, msg, wParam, lParam); +#endif } static void SDL_CleanupDeviceNotification(SDL_DeviceNotificationData *data) { +#if SDL_JOYSTICK_RAWINPUT + RAWINPUT_UnregisterNotifications(); +#endif + if (data->hNotify) UnregisterDeviceNotification(data->hNotify); @@ -188,6 +197,10 @@ SDL_CreateDeviceNotification(SDL_DeviceNotificationData *data) SDL_CleanupDeviceNotification(data); return -1; } + +#if SDL_JOYSTICK_RAWINPUT + RAWINPUT_RegisterNotifications(data->messageWindow); +#endif return 0; } diff --git a/src/video/windows/SDL_windowswindow.c b/src/video/windows/SDL_windowswindow.c index 825680e44b7de..ae3a9f60ceb77 100644 --- a/src/video/windows/SDL_windowswindow.c +++ b/src/video/windows/SDL_windowswindow.c @@ -30,7 +30,6 @@ #include "../../events/SDL_keyboard_c.h" #include "../../events/SDL_mouse_c.h" -#include "../../joystick/windows/SDL_rawinputjoystick_c.h" #include "SDL_windowsvideo.h" #include "SDL_windowswindow.h" #include "SDL_hints.h" @@ -811,18 +810,8 @@ WIN_GetWindowWMInfo(_THIS, SDL_Window * window, SDL_SysWMinfo * info) } } -static LRESULT CALLBACK SDL_HelperWindowProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) -{ -#if SDL_JOYSTICK_RAWINPUT - if (RAWINPUT_WindowProc(hWnd, msg, wParam, lParam) == 0) { - return 0; - } -#endif - return DefWindowProc(hWnd, msg, wParam, lParam); -} - /* - * Creates a HelperWindow used for DirectInput and RawInput events. + * Creates a HelperWindow used for DirectInput. */ int SDL_HelperWindowCreate(void) @@ -837,7 +826,7 @@ SDL_HelperWindowCreate(void) /* Create the class. */ SDL_zero(wce); - wce.lpfnWndProc = SDL_GetHintBoolean(SDL_HINT_JOYSTICK_RAWINPUT, SDL_TRUE) ? SDL_HelperWindowProc : DefWindowProc; + wce.lpfnWndProc = DefWindowProc; wce.lpszClassName = (LPCWSTR) SDL_HelperWindowClassName; wce.hInstance = hInstance;