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.
This commit is contained in:
Sam Lantinga 2020-11-27 13:08:40 -08:00
parent 4fbefbe20d
commit a0c5bfa3bd
6 changed files with 125 additions and 127 deletions

View File

@ -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

View File

@ -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;

View File

@ -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;
result = 0;
break;
case WM_INPUT:
{
Uint8 data[sizeof(RAWINPUTHEADER) + sizeof(RAWHID) + USB_PACKET_LENGTH];
UINT buffer_size = SDL_arraysize(data);
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);
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

View File

@ -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: */

View File

@ -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;
}

View File

@ -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;