Skip to content

Commit

Permalink
Moved raw input event processing from the main thread to the joystick…
Browse files Browse the repository at this point in the history
… thread

This allows fast joystick event delivery regardless of what the main thread is doing.
  • Loading branch information
slouken committed Nov 27, 2020
1 parent 4fbefbe commit a0c5bfa
Show file tree
Hide file tree
Showing 6 changed files with 127 additions and 129 deletions.
2 changes: 2 additions & 0 deletions include/SDL_config_windows.h
Expand Up @@ -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
Expand Down
4 changes: 2 additions & 2 deletions src/joystick/hidapi/SDL_hidapijoystick.c
Expand Up @@ -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 */
Expand All @@ -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;
Expand Down
210 changes: 100 additions & 110 deletions src/joystick/windows/SDL_rawinputjoystick.c
Expand Up @@ -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"
Expand Down Expand Up @@ -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);

Expand Down Expand Up @@ -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;
Expand All @@ -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;
}

Expand Down Expand Up @@ -930,8 +883,6 @@ RAWINPUT_PostUpdate(void)
guide_button_candidate.joystick = NULL;

#endif /* SDL_JOYSTICK_RAWINPUT_MATCHING */

pump_device_events = SDL_TRUE;
}

SDL_bool
Expand All @@ -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;
Expand Down Expand Up @@ -983,8 +931,6 @@ RAWINPUT_IsDevicePresent(Uint16 vendor_id, Uint16 product_id, Uint16 version, co
static void
RAWINPUT_JoystickDetect(void)
{
RAWINPUT_GetPendingDeviceChanges();

RAWINPUT_PostUpdate();
}

Expand Down Expand Up @@ -1727,8 +1673,6 @@ RAWINPUT_UpdateOtherAPIs(SDL_Joystick *joystick)
static void
RAWINPUT_JoystickUpdate(SDL_Joystick *joystick)
{
RAWINPUT_GetPendingDeviceInput();

RAWINPUT_UpdateOtherAPIs(joystick);
}

Expand Down Expand Up @@ -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);
}
Expand All @@ -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
Expand Down
6 changes: 5 additions & 1 deletion src/joystick/windows/SDL_rawinputjoystick_c.h
Expand Up @@ -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: */

0 comments on commit a0c5bfa

Please sign in to comment.