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.
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,73 +1720,114 @@ 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;