Skip to content

Commit

Permalink
Added WGI gamepad added/removed listeners for RAWINPUT
Browse files Browse the repository at this point in the history
This fixes WGI correlation on startup when the WGI gamepad list isn't populated yet

(cherry picked from commit f047e17)
(cherry picked from commit f8a0135)
  • Loading branch information
slouken committed Jul 8, 2023
1 parent 0351210 commit bb3997d
Show file tree
Hide file tree
Showing 2 changed files with 135 additions and 45 deletions.
100 changes: 95 additions & 5 deletions src/joystick/windows/SDL_rawinputjoystick.c
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@

#if SDL_JOYSTICK_RAWINPUT

#include "SDL_atomic.h"
#include "SDL_endian.h"
#include "SDL_events.h"
#include "SDL_hints.h"
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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 } };
Expand Down Expand Up @@ -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);
}
}
}
}
}
Expand Down Expand Up @@ -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;
Expand All @@ -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;
}
Expand Down Expand Up @@ -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) {
Expand Down
80 changes: 40 additions & 40 deletions src/joystick/windows/SDL_windows_gaming_input.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down

0 comments on commit bb3997d

Please sign in to comment.