From adb75d642cabcd8b42b817de711b4ab03e35b634 Mon Sep 17 00:00:00 2001
From: Sam Lantinga <[EMAIL REDACTED]>
Date: Fri, 23 Jul 2021 23:45:18 -0700
Subject: [PATCH] Fixed hotplug detection not working on UWP or when SDL isn't
pumping Windows events
This fixes bugs:
https://github.com/libsdl-org/SDL/issues/4321
https://github.com/libsdl-org/SDL/issues/4147
Thanks to DJm00n for the suggestion!
---
src/joystick/windows/SDL_windowsjoystick.c | 151 +++++++++++++++++----
1 file changed, 124 insertions(+), 27 deletions(-)
diff --git a/src/joystick/windows/SDL_windowsjoystick.c b/src/joystick/windows/SDL_windowsjoystick.c
index 29dd4e95b..65d02808e 100644
--- a/src/joystick/windows/SDL_windowsjoystick.c
+++ b/src/joystick/windows/SDL_windowsjoystick.c
@@ -59,6 +59,77 @@
#define DEVICE_NOTIFY_WINDOW_HANDLE 0x00000000
#endif
+/* CM_Register_Notification definitions */
+
+#define CR_SUCCESS (0x00000000)
+
+DECLARE_HANDLE(HCMNOTIFICATION);
+typedef HCMNOTIFICATION* PHCMNOTIFICATION;
+
+typedef enum _CM_NOTIFY_FILTER_TYPE {
+ CM_NOTIFY_FILTER_TYPE_DEVICEINTERFACE = 0,
+ CM_NOTIFY_FILTER_TYPE_DEVICEHANDLE,
+ CM_NOTIFY_FILTER_TYPE_DEVICEINSTANCE,
+ CM_NOTIFY_FILTER_TYPE_MAX
+} CM_NOTIFY_FILTER_TYPE, * PCM_NOTIFY_FILTER_TYPE;
+
+typedef struct _CM_NOTIFY_FILTER {
+ DWORD cbSize;
+ DWORD Flags;
+ CM_NOTIFY_FILTER_TYPE FilterType;
+ DWORD Reserved;
+ union {
+ struct {
+ GUID ClassGuid;
+ } DeviceInterface;
+ struct {
+ HANDLE hTarget;
+ } DeviceHandle;
+ struct {
+ WCHAR InstanceId[200];
+ } DeviceInstance;
+ } u;
+} CM_NOTIFY_FILTER, * PCM_NOTIFY_FILTER;
+
+typedef enum _CM_NOTIFY_ACTION {
+ CM_NOTIFY_ACTION_DEVICEINTERFACEARRIVAL = 0,
+ CM_NOTIFY_ACTION_DEVICEINTERFACEREMOVAL,
+ CM_NOTIFY_ACTION_DEVICEQUERYREMOVE,
+ CM_NOTIFY_ACTION_DEVICEQUERYREMOVEFAILED,
+ CM_NOTIFY_ACTION_DEVICEREMOVEPENDING,
+ CM_NOTIFY_ACTION_DEVICEREMOVECOMPLETE,
+ CM_NOTIFY_ACTION_DEVICECUSTOMEVENT,
+ CM_NOTIFY_ACTION_DEVICEINSTANCEENUMERATED,
+ CM_NOTIFY_ACTION_DEVICEINSTANCESTARTED,
+ CM_NOTIFY_ACTION_DEVICEINSTANCEREMOVED,
+ CM_NOTIFY_ACTION_MAX
+} CM_NOTIFY_ACTION, * PCM_NOTIFY_ACTION;
+
+typedef struct _CM_NOTIFY_EVENT_DATA {
+ CM_NOTIFY_FILTER_TYPE FilterType;
+ DWORD Reserved;
+ union {
+ struct {
+ GUID ClassGuid;
+ WCHAR SymbolicLink[ANYSIZE_ARRAY];
+ } DeviceInterface;
+ struct {
+ GUID EventGuid;
+ LONG NameOffset;
+ DWORD DataSize;
+ BYTE Data[ANYSIZE_ARRAY];
+ } DeviceHandle;
+ struct {
+ WCHAR InstanceId[ANYSIZE_ARRAY];
+ } DeviceInstance;
+ } u;
+} CM_NOTIFY_EVENT_DATA, * PCM_NOTIFY_EVENT_DATA;
+
+typedef DWORD (CALLBACK *PCM_NOTIFY_CALLBACK)(HCMNOTIFICATION hNotify, PVOID Context, CM_NOTIFY_ACTION Action, PCM_NOTIFY_EVENT_DATA EventData, DWORD EventDataSize);
+
+typedef DWORD (WINAPI *CM_Register_NotificationFunc)(PCM_NOTIFY_FILTER pFilter, PVOID pContext, PCM_NOTIFY_CALLBACK pCallback, PHCMNOTIFICATION pNotifyContext);
+typedef DWORD (WINAPI *CM_Unregister_NotificationFunc)(HCMNOTIFICATION NotifyContext);
+
/* local variables */
static SDL_bool s_bJoystickThread = SDL_FALSE;
static SDL_bool s_bWindowsDeviceChanged = SDL_FALSE;
@@ -66,34 +137,65 @@ static SDL_cond *s_condJoystickThread = NULL;
static SDL_mutex *s_mutexJoyStickEnum = NULL;
static SDL_Thread *s_joystickThread = NULL;
static SDL_bool s_bJoystickThreadQuit = SDL_FALSE;
+static GUID GUID_DEVINTERFACE_HID = { 0x4D1E55B2L, 0xF16F, 0x11CF, { 0x88, 0xCB, 0x00, 0x11, 0x11, 0x00, 0x00, 0x30 } };
JoyStick_DeviceData *SYS_Joystick; /* array to hold joystick ID values */
-#ifdef __WINRT__
-typedef struct
-{
- int unused;
-} SDL_DeviceNotificationData;
+static HMODULE cfgmgr32_lib_handle;
+static CM_Register_NotificationFunc CM_Register_Notification;
+static CM_Unregister_NotificationFunc CM_Unregister_Notification;
+static HCMNOTIFICATION s_DeviceNotificationFuncHandle;
-static void
-SDL_CleanupDeviceNotification(SDL_DeviceNotificationData *data)
+static DWORD CALLBACK
+SDL_DeviceNotificationFunc(HCMNOTIFICATION hNotify, PVOID context, CM_NOTIFY_ACTION action, PCM_NOTIFY_EVENT_DATA eventData, DWORD event_data_size)
{
+ if (action == CM_NOTIFY_ACTION_DEVICEINTERFACEARRIVAL ||
+ action == CM_NOTIFY_ACTION_DEVICEINTERFACEREMOVAL) {
+ s_bWindowsDeviceChanged = SDL_TRUE;
+ }
+ return ERROR_SUCCESS;
}
-static int
-SDL_CreateDeviceNotification(SDL_DeviceNotificationData *data)
+static void
+SDL_CleanupDeviceNotificationFunc(void)
{
- return 0;
+ if (cfgmgr32_lib_handle) {
+ if (s_DeviceNotificationFuncHandle) {
+ CM_Unregister_Notification(s_DeviceNotificationFuncHandle);
+ s_DeviceNotificationFuncHandle = NULL;
+ }
+
+ FreeLibrary(cfgmgr32_lib_handle);
+ cfgmgr32_lib_handle = NULL;
+ }
}
static SDL_bool
-SDL_WaitForDeviceNotification(SDL_DeviceNotificationData *data, SDL_mutex *mutex)
-{
+SDL_CreateDeviceNotificationFunc(void)
+{
+ cfgmgr32_lib_handle = LoadLibraryA("cfgmgr32.dll");
+ if (cfgmgr32_lib_handle) {
+ CM_Register_Notification = (CM_Register_NotificationFunc)GetProcAddress(cfgmgr32_lib_handle, "CM_Register_Notification");
+ CM_Unregister_Notification = (CM_Unregister_NotificationFunc)GetProcAddress(cfgmgr32_lib_handle, "CM_Unregister_Notification");
+ if (CM_Register_Notification && CM_Unregister_Notification) {
+ CM_NOTIFY_FILTER notify_filter;
+
+ SDL_zero(notify_filter);
+ notify_filter.cbSize = sizeof(notify_filter);
+ notify_filter.FilterType = CM_NOTIFY_FILTER_TYPE_DEVICEINTERFACE;
+ notify_filter.u.DeviceInterface.ClassGuid = GUID_DEVINTERFACE_HID;
+ if (CM_Register_Notification(¬ify_filter, NULL, SDL_DeviceNotificationFunc, &s_DeviceNotificationFuncHandle) == CR_SUCCESS) {
+ return SDL_TRUE;
+ }
+ }
+ }
+
+ SDL_CleanupDeviceNotificationFunc();
return SDL_FALSE;
}
-#else /* !__WINRT__ */
+#ifndef __WINRT__
typedef struct
{
@@ -164,7 +266,6 @@ static int
SDL_CreateDeviceNotification(SDL_DeviceNotificationData *data)
{
DEV_BROADCAST_DEVICEINTERFACE dbh;
- GUID GUID_DEVINTERFACE_HID = { 0x4D1E55B2L, 0xF16F, 0x11CF, { 0x88, 0xCB, 0x00, 0x11, 0x11, 0x00, 0x00, 0x30 } };
SDL_zerop(data);
@@ -228,8 +329,6 @@ SDL_WaitForDeviceNotification(SDL_DeviceNotificationData *data, SDL_mutex *mutex
return (lastret != -1) ? SDL_TRUE : SDL_FALSE;
}
-#endif /* __WINRT__ */
-
static SDL_DeviceNotificationData s_notification_data;
/* Function/thread to scan the system for joysticks. */
@@ -310,9 +409,7 @@ SDL_StopJoystickThread(void)
s_bJoystickThreadQuit = SDL_TRUE;
SDL_CondBroadcast(s_condJoystickThread); /* signal the joystick thread to quit */
SDL_UnlockMutex(s_mutexJoyStickEnum);
-#ifndef __WINRT__
PostThreadMessage(SDL_GetThreadID(s_joystickThread), WM_QUIT, 0, 0);
-#endif
SDL_WaitThread(s_joystickThread, NULL); /* wait for it to bugger off */
SDL_DestroyCond(s_condJoystickThread);
@@ -324,6 +421,8 @@ SDL_StopJoystickThread(void)
s_joystickThread = NULL;
}
+#endif /* !__WINRT__ */
+
void WINDOWS_AddJoystickDevice(JoyStick_DeviceData *device)
{
device->send_add_event = SDL_TRUE;
@@ -356,15 +455,9 @@ WINDOWS_JoystickInit(void)
WINDOWS_JoystickDetect();
-#ifdef __WINRT__
- /* FIXME: WinRT silently does not support device notifications.
- * Revisit this if UWP ever adds support in a future release.
- */
- s_bJoystickThread = SDL_TRUE;
- if (SDL_StartJoystickThread() < 0) {
- return -1;
- }
-#else
+ SDL_CreateDeviceNotificationFunc();
+
+#ifndef __WINRT__
s_bJoystickThread = SDL_GetHintBoolean(SDL_HINT_JOYSTICK_THREAD, SDL_FALSE);
if (s_bJoystickThread) {
if (SDL_StartJoystickThread() < 0) {
@@ -632,11 +725,15 @@ WINDOWS_JoystickQuit(void)
}
SYS_Joystick = NULL;
+#ifndef __WINRT__
if (s_bJoystickThread) {
SDL_StopJoystickThread();
} else {
SDL_CleanupDeviceNotification(&s_notification_data);
}
+#endif
+
+ SDL_CleanupDeviceNotificationFunc();
SDL_DINPUT_JoystickQuit();
SDL_XINPUT_JoystickQuit();