From f8a0135edfb07ebe7201b764fd0aba42d600b99c Mon Sep 17 00:00:00 2001
From: Sam Lantinga <[EMAIL REDACTED]>
Date: Sat, 8 Jul 2023 10:24:00 -0700
Subject: [PATCH] Added WGI gamepad added/removed listeners for RAWINPUT
This fixes WGI correlation on startup when the WGI gamepad list isn't populated yet
(cherry picked from commit f047e178b610c6888212c3096c10eb3f64f31a15)
---
src/joystick/windows/SDL_rawinputjoystick.c | 100 +++++++++++++++++-
.../windows/SDL_windows_gaming_input.c | 80 +++++++-------
2 files changed, 135 insertions(+), 45 deletions(-)
diff --git a/src/joystick/windows/SDL_rawinputjoystick.c b/src/joystick/windows/SDL_rawinputjoystick.c
index b6f8a5edb5fc..22a9b4efb5c3 100644
--- a/src/joystick/windows/SDL_rawinputjoystick.c
+++ b/src/joystick/windows/SDL_rawinputjoystick.c
@@ -33,6 +33,7 @@
#if SDL_JOYSTICK_RAWINPUT
+#include "SDL_atomic.h"
#include "SDL_endian.h"
#include "SDL_events.h"
#include "SDL_hints.h"
@@ -445,8 +446,86 @@ static struct
SDL_bool need_device_list_update;
int ref_count;
__x_ABI_CWindows_CGaming_CInput_CIGamepadStatics *gamepad_statics;
+ EventRegistrationToken gamepad_added_token;
+ EventRegistrationToken gamepad_removed_token;
} wgi_state;
+typedef struct GamepadDelegate
+{
+ __FIEventHandler_1_Windows__CGaming__CInput__CGamepad iface;
+ SDL_atomic_t refcount;
+} GamepadDelegate;
+
+static const IID IID_IEventHandler_Gamepad = { 0x8a7639ee, 0x624a, 0x501a, { 0xbb, 0x53, 0x56, 0x2d, 0x1e, 0xc1, 0x1b, 0x52 } };
+
+static HRESULT STDMETHODCALLTYPE IEventHandler_CGamepadVtbl_QueryInterface(__FIEventHandler_1_Windows__CGaming__CInput__CGamepad *This, REFIID riid, void **ppvObject)
+{
+ if (ppvObject == NULL) {
+ return E_INVALIDARG;
+ }
+
+ *ppvObject = NULL;
+ if (WIN_IsEqualIID(riid, &IID_IUnknown) || WIN_IsEqualIID(riid, &IID_IAgileObject) || WIN_IsEqualIID(riid, &IID_IEventHandler_Gamepad)) {
+ *ppvObject = This;
+ __FIEventHandler_1_Windows__CGaming__CInput__CGamepad_AddRef(This);
+ return S_OK;
+ } else if (WIN_IsEqualIID(riid, &IID_IMarshal)) {
+ /* This seems complicated. Let's hope it doesn't happen. */
+ return E_OUTOFMEMORY;
+ } else {
+ return E_NOINTERFACE;
+ }
+}
+
+static ULONG STDMETHODCALLTYPE IEventHandler_CGamepadVtbl_AddRef(__FIEventHandler_1_Windows__CGaming__CInput__CGamepad *This)
+{
+ GamepadDelegate *self = (GamepadDelegate *)This;
+ return SDL_AtomicAdd(&self->refcount, 1) + 1UL;
+}
+
+static ULONG STDMETHODCALLTYPE IEventHandler_CGamepadVtbl_Release(__FIEventHandler_1_Windows__CGaming__CInput__CGamepad *This)
+{
+ GamepadDelegate *self = (GamepadDelegate *)This;
+ int rc = SDL_AtomicAdd(&self->refcount, -1) - 1;
+ /* Should never free the static delegate objects */
+ SDL_assert(rc > 0);
+ return rc;
+}
+
+static HRESULT STDMETHODCALLTYPE IEventHandler_CGamepadVtbl_InvokeAdded(__FIEventHandler_1_Windows__CGaming__CInput__CGamepad *This, IInspectable *sender, __x_ABI_CWindows_CGaming_CInput_CIGamepad *e)
+{
+ wgi_state.need_device_list_update = SDL_TRUE;
+ return S_OK;
+}
+
+static HRESULT STDMETHODCALLTYPE IEventHandler_CGamepadVtbl_InvokeRemoved(__FIEventHandler_1_Windows__CGaming__CInput__CGamepad *This, IInspectable *sender, __x_ABI_CWindows_CGaming_CInput_CIGamepad *e)
+{
+ wgi_state.need_device_list_update = SDL_TRUE;
+ return S_OK;
+}
+
+static __FIEventHandler_1_Windows__CGaming__CInput__CGamepadVtbl gamepad_added_vtbl = {
+ IEventHandler_CGamepadVtbl_QueryInterface,
+ IEventHandler_CGamepadVtbl_AddRef,
+ IEventHandler_CGamepadVtbl_Release,
+ IEventHandler_CGamepadVtbl_InvokeAdded
+};
+static GamepadDelegate gamepad_added = {
+ { &gamepad_added_vtbl },
+ { 1 }
+};
+
+static __FIEventHandler_1_Windows__CGaming__CInput__CGamepadVtbl gamepad_removed_vtbl = {
+ IEventHandler_CGamepadVtbl_QueryInterface,
+ IEventHandler_CGamepadVtbl_AddRef,
+ IEventHandler_CGamepadVtbl_Release,
+ IEventHandler_CGamepadVtbl_InvokeRemoved
+};
+static GamepadDelegate gamepad_removed = {
+ { &gamepad_removed_vtbl },
+ { 1 }
+};
+
static void RAWINPUT_MarkWindowsGamingInputSlotUsed(WindowsGamingInputGamepadState *wgi_slot, RAWINPUT_DeviceContext *ctx)
{
wgi_slot->used = SDL_TRUE;
@@ -568,7 +647,6 @@ static void RAWINPUT_InitWindowsGamingInput(RAWINPUT_DeviceContext *ctx)
return;
}
- wgi_state.need_device_list_update = SDL_TRUE;
wgi_state.ref_count++;
if (!wgi_state.initialized) {
static const IID SDL_IID_IGamepadStatics = { 0x8BBCE529, 0xD49C, 0x39E9, { 0x95, 0x60, 0xE4, 0x7D, 0xDE, 0x96, 0xB7, 0xC8 } };
@@ -600,6 +678,20 @@ static void RAWINPUT_InitWindowsGamingInput(RAWINPUT_DeviceContext *ctx)
if (SUCCEEDED(hr)) {
RoGetActivationFactoryFunc(hNamespaceString, &SDL_IID_IGamepadStatics, (void **)&wgi_state.gamepad_statics);
}
+
+ if (wgi_state.gamepad_statics) {
+ wgi_state.need_device_list_update = SDL_TRUE;
+
+ hr = __x_ABI_CWindows_CGaming_CInput_CIGamepadStatics_add_GamepadAdded(wgi_state.gamepad_statics, &gamepad_added.iface, &wgi_state.gamepad_added_token);
+ if (!SUCCEEDED(hr)) {
+ SDL_SetError("add_GamepadAdded() failed: 0x%lx\n", hr);
+ }
+
+ hr = __x_ABI_CWindows_CGaming_CInput_CIGamepadStatics_add_GamepadRemoved(wgi_state.gamepad_statics, &gamepad_removed.iface, &wgi_state.gamepad_removed_token);
+ if (!SUCCEEDED(hr)) {
+ SDL_SetError("add_GamepadRemoved() failed: 0x%lx\n", hr);
+ }
+ }
}
}
}
@@ -647,7 +739,6 @@ static SDL_bool RAWINPUT_GuessWindowsGamingInputSlot(const WindowsMatchState *st
static void RAWINPUT_QuitWindowsGamingInput(RAWINPUT_DeviceContext *ctx)
{
- wgi_state.need_device_list_update = SDL_TRUE;
--wgi_state.ref_count;
if (!wgi_state.ref_count && wgi_state.initialized) {
int ii;
@@ -660,6 +751,8 @@ static void RAWINPUT_QuitWindowsGamingInput(RAWINPUT_DeviceContext *ctx)
}
wgi_state.per_gamepad_count = 0;
if (wgi_state.gamepad_statics) {
+ __x_ABI_CWindows_CGaming_CInput_CIGamepadStatics_remove_GamepadAdded(wgi_state.gamepad_statics, wgi_state.gamepad_added_token);
+ __x_ABI_CWindows_CGaming_CInput_CIGamepadStatics_remove_GamepadRemoved(wgi_state.gamepad_statics, wgi_state.gamepad_removed_token);
__x_ABI_CWindows_CGaming_CInput_CIGamepadStatics_Release(wgi_state.gamepad_statics);
wgi_state.gamepad_statics = NULL;
}
@@ -921,9 +1014,6 @@ SDL_bool RAWINPUT_IsDevicePresent(Uint16 vendor_id, Uint16 product_id, Uint16 ve
#ifdef SDL_JOYSTICK_RAWINPUT_XINPUT
xinput_device_change = SDL_TRUE;
#endif
-#ifdef SDL_JOYSTICK_RAWINPUT_WGI
- wgi_state.need_device_list_update = SDL_TRUE;
-#endif
device = SDL_RAWINPUT_devices;
while (device) {
diff --git a/src/joystick/windows/SDL_windows_gaming_input.c b/src/joystick/windows/SDL_windows_gaming_input.c
index 4144fa4fadfe..6f01b4e1bc4b 100644
--- a/src/joystick/windows/SDL_windows_gaming_input.c
+++ b/src/joystick/windows/SDL_windows_gaming_input.c
@@ -214,46 +214,6 @@ static SDL_bool SDL_IsXInputDevice(Uint16 vendor, Uint16 product)
return SDL_FALSE;
}
-typedef struct RawGameControllerDelegate
-{
- __FIEventHandler_1_Windows__CGaming__CInput__CRawGameController iface;
- SDL_atomic_t refcount;
-} RawGameControllerDelegate;
-
-static HRESULT STDMETHODCALLTYPE IEventHandler_CRawGameControllerVtbl_QueryInterface(__FIEventHandler_1_Windows__CGaming__CInput__CRawGameController *This, REFIID riid, void **ppvObject)
-{
- if (ppvObject == NULL) {
- return E_INVALIDARG;
- }
-
- *ppvObject = NULL;
- if (WIN_IsEqualIID(riid, &IID_IUnknown) || WIN_IsEqualIID(riid, &IID_IAgileObject) || WIN_IsEqualIID(riid, &IID_IEventHandler_RawGameController)) {
- *ppvObject = This;
- __x_ABI_CWindows_CGaming_CInput_CIRawGameControllerStatics_AddRef(This);
- return S_OK;
- } else if (WIN_IsEqualIID(riid, &IID_IMarshal)) {
- /* This seems complicated. Let's hope it doesn't happen. */
- return E_OUTOFMEMORY;
- } else {
- return E_NOINTERFACE;
- }
-}
-
-static ULONG STDMETHODCALLTYPE IEventHandler_CRawGameControllerVtbl_AddRef(__FIEventHandler_1_Windows__CGaming__CInput__CRawGameController *This)
-{
- RawGameControllerDelegate *self = (RawGameControllerDelegate *)This;
- return SDL_AtomicAdd(&self->refcount, 1) + 1UL;
-}
-
-static ULONG STDMETHODCALLTYPE IEventHandler_CRawGameControllerVtbl_Release(__FIEventHandler_1_Windows__CGaming__CInput__CRawGameController *This)
-{
- RawGameControllerDelegate *self = (RawGameControllerDelegate *)This;
- int rc = SDL_AtomicAdd(&self->refcount, -1) - 1;
- /* Should never free the static delegate objects */
- SDL_assert(rc > 0);
- return rc;
-}
-
static void WGI_LoadRawGameControllerStatics()
{
WindowsCreateStringReference_t WindowsCreateStringReferenceFunc = NULL;
@@ -384,6 +344,46 @@ static SDL_JoystickType GetGameControllerType(__x_ABI_CWindows_CGaming_CInput_CI
return SDL_JOYSTICK_TYPE_UNKNOWN;
}
+typedef struct RawGameControllerDelegate
+{
+ __FIEventHandler_1_Windows__CGaming__CInput__CRawGameController iface;
+ SDL_atomic_t refcount;
+} RawGameControllerDelegate;
+
+static HRESULT STDMETHODCALLTYPE IEventHandler_CRawGameControllerVtbl_QueryInterface(__FIEventHandler_1_Windows__CGaming__CInput__CRawGameController *This, REFIID riid, void **ppvObject)
+{
+ if (ppvObject == NULL) {
+ return E_INVALIDARG;
+ }
+
+ *ppvObject = NULL;
+ if (WIN_IsEqualIID(riid, &IID_IUnknown) || WIN_IsEqualIID(riid, &IID_IAgileObject) || WIN_IsEqualIID(riid, &IID_IEventHandler_RawGameController)) {
+ *ppvObject = This;
+ __FIEventHandler_1_Windows__CGaming__CInput__CRawGameController_AddRef(This);
+ return S_OK;
+ } else if (WIN_IsEqualIID(riid, &IID_IMarshal)) {
+ /* This seems complicated. Let's hope it doesn't happen. */
+ return E_OUTOFMEMORY;
+ } else {
+ return E_NOINTERFACE;
+ }
+}
+
+static ULONG STDMETHODCALLTYPE IEventHandler_CRawGameControllerVtbl_AddRef(__FIEventHandler_1_Windows__CGaming__CInput__CRawGameController *This)
+{
+ RawGameControllerDelegate *self = (RawGameControllerDelegate *)This;
+ return SDL_AtomicAdd(&self->refcount, 1) + 1UL;
+}
+
+static ULONG STDMETHODCALLTYPE IEventHandler_CRawGameControllerVtbl_Release(__FIEventHandler_1_Windows__CGaming__CInput__CRawGameController *This)
+{
+ RawGameControllerDelegate *self = (RawGameControllerDelegate *)This;
+ int rc = SDL_AtomicAdd(&self->refcount, -1) - 1;
+ /* Should never free the static delegate objects */
+ SDL_assert(rc > 0);
+ return rc;
+}
+
static HRESULT STDMETHODCALLTYPE IEventHandler_CRawGameControllerVtbl_InvokeAdded(__FIEventHandler_1_Windows__CGaming__CInput__CRawGameController *This, IInspectable *sender, __x_ABI_CWindows_CGaming_CInput_CIRawGameController *e)
{
HRESULT hr;