Path: blob/master/thirdparty/sdl/joystick/windows/SDL_windows_gaming_input.c
21892 views
/*1Simple DirectMedia Layer2Copyright (C) 1997-2025 Sam Lantinga <[email protected]>34This software is provided 'as-is', without any express or implied5warranty. In no event will the authors be held liable for any damages6arising from the use of this software.78Permission is granted to anyone to use this software for any purpose,9including commercial applications, and to alter it and redistribute it10freely, subject to the following restrictions:11121. The origin of this software must not be misrepresented; you must not13claim that you wrote the original software. If you use this software14in a product, an acknowledgment in the product documentation would be15appreciated but is not required.162. Altered source versions must be plainly marked as such, and must not be17misrepresented as being the original software.183. This notice may not be removed or altered from any source distribution.19*/20#include "SDL_internal.h"2122#ifdef SDL_JOYSTICK_WGI2324#include "../SDL_sysjoystick.h"25#include "../hidapi/SDL_hidapijoystick_c.h"26#include "SDL_rawinputjoystick_c.h"2728#include "../../core/windows/SDL_windows.h"29#define COBJMACROS30#include "windows.gaming.input.h"31#include <cfgmgr32.h>32#include <objidlbase.h>33#include <roapi.h>34#include <initguid.h>3536#ifdef ____FIReference_1_INT32_INTERFACE_DEFINED__37// MinGW-64 uses __FIReference_1_INT32 instead of Microsoft's __FIReference_1_int38#define __FIReference_1_int __FIReference_1_INT3239#define __FIReference_1_int_get_Value __FIReference_1_INT32_get_Value40#define __FIReference_1_int_Release __FIReference_1_INT32_Release41#endif4243struct joystick_hwdata44{45__x_ABI_CWindows_CGaming_CInput_CIRawGameController *controller;46__x_ABI_CWindows_CGaming_CInput_CIGameController *game_controller;47__x_ABI_CWindows_CGaming_CInput_CIGameControllerBatteryInfo *battery;48__x_ABI_CWindows_CGaming_CInput_CIGamepad *gamepad;49__x_ABI_CWindows_CGaming_CInput_CGamepadVibration vibration;50UINT64 timestamp;51};5253typedef struct WindowsGamingInputControllerState54{55SDL_JoystickID instance_id;56__x_ABI_CWindows_CGaming_CInput_CIRawGameController *controller;57char *name;58SDL_GUID guid;59SDL_JoystickType type;60int steam_virtual_gamepad_slot;61} WindowsGamingInputControllerState;6263typedef HRESULT(WINAPI *CoIncrementMTAUsage_t)(CO_MTA_USAGE_COOKIE *pCookie);64typedef HRESULT(WINAPI *RoGetActivationFactory_t)(HSTRING activatableClassId, REFIID iid, void **factory);65typedef HRESULT(WINAPI *WindowsCreateStringReference_t)(PCWSTR sourceString, UINT32 length, HSTRING_HEADER *hstringHeader, HSTRING *string);66typedef HRESULT(WINAPI *WindowsDeleteString_t)(HSTRING string);67typedef PCWSTR(WINAPI *WindowsGetStringRawBuffer_t)(HSTRING string, UINT32 *length);6869static struct70{71bool ro_initialized;72CoIncrementMTAUsage_t CoIncrementMTAUsage;73RoGetActivationFactory_t RoGetActivationFactory;74WindowsCreateStringReference_t WindowsCreateStringReference;75WindowsDeleteString_t WindowsDeleteString;76WindowsGetStringRawBuffer_t WindowsGetStringRawBuffer;77__x_ABI_CWindows_CGaming_CInput_CIRawGameControllerStatics *controller_statics;78__x_ABI_CWindows_CGaming_CInput_CIArcadeStickStatics *arcade_stick_statics;79__x_ABI_CWindows_CGaming_CInput_CIArcadeStickStatics2 *arcade_stick_statics2;80__x_ABI_CWindows_CGaming_CInput_CIFlightStickStatics *flight_stick_statics;81__x_ABI_CWindows_CGaming_CInput_CIGamepadStatics *gamepad_statics;82__x_ABI_CWindows_CGaming_CInput_CIGamepadStatics2 *gamepad_statics2;83__x_ABI_CWindows_CGaming_CInput_CIRacingWheelStatics *racing_wheel_statics;84__x_ABI_CWindows_CGaming_CInput_CIRacingWheelStatics2 *racing_wheel_statics2;85EventRegistrationToken controller_added_token;86EventRegistrationToken controller_removed_token;87int controller_count;88WindowsGamingInputControllerState *controllers;89} wgi;9091// WinRT headers in official Windows SDK contain only declarations, and we have to define these GUIDs ourselves.92// https://stackoverflow.com/a/55605485/179505093DEFINE_GUID(IID___FIEventHandler_1_Windows__CGaming__CInput__CRawGameController, 0x00621c22, 0x42e8, 0x529f, 0x92, 0x70, 0x83, 0x6b, 0x32, 0x93, 0x1d, 0x72);94DEFINE_GUID(IID___x_ABI_CWindows_CGaming_CInput_CIArcadeStickStatics, 0x5c37b8c8, 0x37b1, 0x4ad8, 0x94, 0x58, 0x20, 0x0f, 0x1a, 0x30, 0x01, 0x8e);95DEFINE_GUID(IID___x_ABI_CWindows_CGaming_CInput_CIArcadeStickStatics2, 0x52b5d744, 0xbb86, 0x445a, 0xb5, 0x9c, 0x59, 0x6f, 0x0e, 0x2a, 0x49, 0xdf);96DEFINE_GUID(IID___x_ABI_CWindows_CGaming_CInput_CIFlightStickStatics, 0x5514924a, 0xfecc, 0x435e, 0x83, 0xdc, 0x5c, 0xec, 0x8a, 0x18, 0xa5, 0x20);97DEFINE_GUID(IID___x_ABI_CWindows_CGaming_CInput_CIGameController, 0x1baf6522, 0x5f64, 0x42c5, 0x82, 0x67, 0xb9, 0xfe, 0x22, 0x15, 0xbf, 0xbd);98DEFINE_GUID(IID___x_ABI_CWindows_CGaming_CInput_CIGameControllerBatteryInfo, 0xdcecc681, 0x3963, 0x4da6, 0x95, 0x5d, 0x55, 0x3f, 0x3b, 0x6f, 0x61, 0x61);99DEFINE_GUID(IID___x_ABI_CWindows_CGaming_CInput_CIGamepadStatics, 0x8bbce529, 0xd49c, 0x39e9, 0x95, 0x60, 0xe4, 0x7d, 0xde, 0x96, 0xb7, 0xc8);100DEFINE_GUID(IID___x_ABI_CWindows_CGaming_CInput_CIGamepadStatics2, 0x42676dc5, 0x0856, 0x47c4, 0x92, 0x13, 0xb3, 0x95, 0x50, 0x4c, 0x3a, 0x3c);101DEFINE_GUID(IID___x_ABI_CWindows_CGaming_CInput_CIRacingWheelStatics, 0x3ac12cd5, 0x581b, 0x4936, 0x9f, 0x94, 0x69, 0xf1, 0xe6, 0x51, 0x4c, 0x7d);102DEFINE_GUID(IID___x_ABI_CWindows_CGaming_CInput_CIRacingWheelStatics2, 0xe666bcaa, 0xedfd, 0x4323, 0xa9, 0xf6, 0x3c, 0x38, 0x40, 0x48, 0xd1, 0xed);103DEFINE_GUID(IID___x_ABI_CWindows_CGaming_CInput_CIRawGameController, 0x7cad6d91, 0xa7e1, 0x4f71, 0x9a, 0x78, 0x33, 0xe9, 0xc5, 0xdf, 0xea, 0x62);104DEFINE_GUID(IID___x_ABI_CWindows_CGaming_CInput_CIRawGameController2, 0x43c0c035, 0xbb73, 0x4756, 0xa7, 0x87, 0x3e, 0xd6, 0xbe, 0xa6, 0x17, 0xbd);105DEFINE_GUID(IID___x_ABI_CWindows_CGaming_CInput_CIRawGameControllerStatics, 0xeb8d0792, 0xe95a, 0x4b19, 0xaf, 0xc7, 0x0a, 0x59, 0xf8, 0xbf, 0x75, 0x9e);106107extern bool SDL_XINPUT_Enabled(void);108109110static bool SDL_IsXInputDevice(Uint16 vendor, Uint16 product, const char *name)111{112#if defined(SDL_JOYSTICK_XINPUT) || defined(SDL_JOYSTICK_RAWINPUT)113PRAWINPUTDEVICELIST raw_devices = NULL;114UINT i, raw_device_count = 0;115LONG vidpid = MAKELONG(vendor, product);116117// XInput and RawInput backends will pick up XInput-compatible devices118if (!SDL_XINPUT_Enabled()119#ifdef SDL_JOYSTICK_RAWINPUT120&& !RAWINPUT_IsEnabled()121#endif122) {123return false;124}125126// Sometimes we'll get a Windows.Gaming.Input callback before the raw input device is even in the list,127// so try to do some checks up front to catch these cases.128if (SDL_IsJoystickXboxOne(vendor, product) ||129(name && SDL_strncmp(name, "Xbox ", 5) == 0)) {130return true;131}132133// Go through RAWINPUT (WinXP and later) to find HID devices.134if ((GetRawInputDeviceList(NULL, &raw_device_count, sizeof(RAWINPUTDEVICELIST)) == -1) || (!raw_device_count)) {135return false; // oh well.136}137138raw_devices = (PRAWINPUTDEVICELIST)SDL_malloc(sizeof(RAWINPUTDEVICELIST) * raw_device_count);139if (!raw_devices) {140return false;141}142143raw_device_count = GetRawInputDeviceList(raw_devices, &raw_device_count, sizeof(RAWINPUTDEVICELIST));144if (raw_device_count == (UINT)-1) {145SDL_free(raw_devices);146raw_devices = NULL;147return false; // oh well.148}149150for (i = 0; i < raw_device_count; i++) {151RID_DEVICE_INFO rdi;152char devName[MAX_PATH] = { 0 };153UINT rdiSize = sizeof(rdi);154UINT nameSize = SDL_arraysize(devName);155DEVINST devNode;156char devVidPidString[32];157int j;158159rdi.cbSize = sizeof(rdi);160161if ((raw_devices[i].dwType != RIM_TYPEHID) ||162(GetRawInputDeviceInfoA(raw_devices[i].hDevice, RIDI_DEVICEINFO, &rdi, &rdiSize) == ((UINT)-1)) ||163(GetRawInputDeviceInfoA(raw_devices[i].hDevice, RIDI_DEVICENAME, devName, &nameSize) == ((UINT)-1)) ||164(SDL_strstr(devName, "IG_") == NULL)) {165// Skip non-XInput devices166continue;167}168169// First check for a simple VID/PID match. This will work for Xbox 360 controllers.170if (MAKELONG(rdi.hid.dwVendorId, rdi.hid.dwProductId) == vidpid) {171SDL_free(raw_devices);172return true;173}174175/* For Xbox One controllers, Microsoft doesn't propagate the VID/PID down to the HID stack.176* We'll have to walk the device tree upwards searching for a match for our VID/PID. */177178// Make sure the device interface string is something we know how to parse179// Example: \\?\HID#VID_045E&PID_02FF&IG_00#9&2c203035&2&0000#{4d1e55b2-f16f-11cf-88cb-001111000030}180if ((SDL_strstr(devName, "\\\\?\\") != devName) || (SDL_strstr(devName, "#{") == NULL)) {181continue;182}183184// Unescape the backslashes in the string and terminate before the GUID portion185for (j = 0; devName[j] != '\0'; j++) {186if (devName[j] == '#') {187if (devName[j + 1] == '{') {188devName[j] = '\0';189break;190} else {191devName[j] = '\\';192}193}194}195196/* We'll be left with a string like this: \\?\HID\VID_045E&PID_02FF&IG_00\9&2c203035&2&0000197* Simply skip the \\?\ prefix and we'll have a properly formed device instance ID */198if (CM_Locate_DevNodeA(&devNode, &devName[4], CM_LOCATE_DEVNODE_NORMAL) != CR_SUCCESS) {199continue;200}201202(void)SDL_snprintf(devVidPidString, sizeof(devVidPidString), "VID_%04X&PID_%04X", vendor, product);203204while (CM_Get_Parent(&devNode, devNode, 0) == CR_SUCCESS) {205char deviceId[MAX_DEVICE_ID_LEN];206207if ((CM_Get_Device_IDA(devNode, deviceId, SDL_arraysize(deviceId), 0) == CR_SUCCESS) &&208(SDL_strstr(deviceId, devVidPidString) != NULL)) {209// The VID/PID matched a parent device210SDL_free(raw_devices);211return true;212}213}214}215216SDL_free(raw_devices);217#endif // SDL_JOYSTICK_XINPUT || SDL_JOYSTICK_RAWINPUT218219return false;220}221222static void WGI_LoadRawGameControllerStatics(void)223{224HRESULT hr;225HSTRING_HEADER class_name_header;226HSTRING class_name;227228hr = wgi.WindowsCreateStringReference(RuntimeClass_Windows_Gaming_Input_RawGameController, (UINT32)SDL_wcslen(RuntimeClass_Windows_Gaming_Input_RawGameController), &class_name_header, &class_name);229if (SUCCEEDED(hr)) {230hr = wgi.RoGetActivationFactory(class_name, &IID___x_ABI_CWindows_CGaming_CInput_CIRawGameControllerStatics, (void **)&wgi.controller_statics);231if (!SUCCEEDED(hr)) {232WIN_SetErrorFromHRESULT("Couldn't find Windows.Gaming.Input.IRawGameControllerStatics", hr);233}234}235}236237static void WGI_LoadOtherControllerStatics(void)238{239HRESULT hr;240HSTRING_HEADER class_name_header;241HSTRING class_name;242243if (!wgi.arcade_stick_statics) {244hr = wgi.WindowsCreateStringReference(RuntimeClass_Windows_Gaming_Input_ArcadeStick, (UINT32)SDL_wcslen(RuntimeClass_Windows_Gaming_Input_ArcadeStick), &class_name_header, &class_name);245if (SUCCEEDED(hr)) {246hr = wgi.RoGetActivationFactory(class_name, &IID___x_ABI_CWindows_CGaming_CInput_CIArcadeStickStatics, (void **)&wgi.arcade_stick_statics);247if (SUCCEEDED(hr)) {248__x_ABI_CWindows_CGaming_CInput_CIArcadeStickStatics_QueryInterface(wgi.arcade_stick_statics, &IID___x_ABI_CWindows_CGaming_CInput_CIArcadeStickStatics2, (void **)&wgi.arcade_stick_statics2);249} else {250WIN_SetErrorFromHRESULT("Couldn't find Windows.Gaming.Input.IArcadeStickStatics", hr);251}252}253}254255if (!wgi.flight_stick_statics) {256hr = wgi.WindowsCreateStringReference(RuntimeClass_Windows_Gaming_Input_FlightStick, (UINT32)SDL_wcslen(RuntimeClass_Windows_Gaming_Input_FlightStick), &class_name_header, &class_name);257if (SUCCEEDED(hr)) {258hr = wgi.RoGetActivationFactory(class_name, &IID___x_ABI_CWindows_CGaming_CInput_CIFlightStickStatics, (void **)&wgi.flight_stick_statics);259if (!SUCCEEDED(hr)) {260WIN_SetErrorFromHRESULT("Couldn't find Windows.Gaming.Input.IFlightStickStatics", hr);261}262}263}264265if (!wgi.gamepad_statics) {266hr = wgi.WindowsCreateStringReference(RuntimeClass_Windows_Gaming_Input_Gamepad, (UINT32)SDL_wcslen(RuntimeClass_Windows_Gaming_Input_Gamepad), &class_name_header, &class_name);267if (SUCCEEDED(hr)) {268hr = wgi.RoGetActivationFactory(class_name, &IID___x_ABI_CWindows_CGaming_CInput_CIGamepadStatics, (void **)&wgi.gamepad_statics);269if (SUCCEEDED(hr)) {270__x_ABI_CWindows_CGaming_CInput_CIGamepadStatics_QueryInterface(wgi.gamepad_statics, &IID___x_ABI_CWindows_CGaming_CInput_CIGamepadStatics2, (void **)&wgi.gamepad_statics2);271} else {272WIN_SetErrorFromHRESULT("Couldn't find Windows.Gaming.Input.IGamepadStatics", hr);273}274}275}276277if (!wgi.racing_wheel_statics) {278hr = wgi.WindowsCreateStringReference(RuntimeClass_Windows_Gaming_Input_RacingWheel, (UINT32)SDL_wcslen(RuntimeClass_Windows_Gaming_Input_RacingWheel), &class_name_header, &class_name);279if (SUCCEEDED(hr)) {280hr = wgi.RoGetActivationFactory(class_name, &IID___x_ABI_CWindows_CGaming_CInput_CIRacingWheelStatics, (void **)&wgi.racing_wheel_statics);281if (SUCCEEDED(hr)) {282__x_ABI_CWindows_CGaming_CInput_CIRacingWheelStatics_QueryInterface(wgi.racing_wheel_statics, &IID___x_ABI_CWindows_CGaming_CInput_CIRacingWheelStatics2, (void **)&wgi.racing_wheel_statics2);283} else {284WIN_SetErrorFromHRESULT("Couldn't find Windows.Gaming.Input.IRacingWheelStatics", hr);285}286}287}288}289290static SDL_JoystickType GetGameControllerType(__x_ABI_CWindows_CGaming_CInput_CIGameController *game_controller)291{292__x_ABI_CWindows_CGaming_CInput_CIArcadeStick *arcade_stick = NULL;293__x_ABI_CWindows_CGaming_CInput_CIFlightStick *flight_stick = NULL;294__x_ABI_CWindows_CGaming_CInput_CIGamepad *gamepad = NULL;295__x_ABI_CWindows_CGaming_CInput_CIRacingWheel *racing_wheel = NULL;296297/* Wait to initialize these interfaces until we need them.298* Initializing the gamepad interface will switch Bluetooth PS4 controllers into enhanced mode, breaking DirectInput299*/300WGI_LoadOtherControllerStatics();301302if (wgi.gamepad_statics2 && SUCCEEDED(__x_ABI_CWindows_CGaming_CInput_CIGamepadStatics2_FromGameController(wgi.gamepad_statics2, game_controller, &gamepad)) && gamepad) {303__x_ABI_CWindows_CGaming_CInput_CIGamepad_Release(gamepad);304return SDL_JOYSTICK_TYPE_GAMEPAD;305}306307if (wgi.arcade_stick_statics2 && SUCCEEDED(__x_ABI_CWindows_CGaming_CInput_CIArcadeStickStatics2_FromGameController(wgi.arcade_stick_statics2, game_controller, &arcade_stick)) && arcade_stick) {308__x_ABI_CWindows_CGaming_CInput_CIArcadeStick_Release(arcade_stick);309return SDL_JOYSTICK_TYPE_ARCADE_STICK;310}311312if (wgi.flight_stick_statics && SUCCEEDED(__x_ABI_CWindows_CGaming_CInput_CIFlightStickStatics_FromGameController(wgi.flight_stick_statics, game_controller, &flight_stick)) && flight_stick) {313__x_ABI_CWindows_CGaming_CInput_CIFlightStick_Release(flight_stick);314return SDL_JOYSTICK_TYPE_FLIGHT_STICK;315}316317if (wgi.racing_wheel_statics2 && SUCCEEDED(__x_ABI_CWindows_CGaming_CInput_CIRacingWheelStatics2_FromGameController(wgi.racing_wheel_statics2, game_controller, &racing_wheel)) && racing_wheel) {318__x_ABI_CWindows_CGaming_CInput_CIRacingWheel_Release(racing_wheel);319return SDL_JOYSTICK_TYPE_WHEEL;320}321322return SDL_JOYSTICK_TYPE_UNKNOWN;323}324325typedef struct RawGameControllerDelegate326{327__FIEventHandler_1_Windows__CGaming__CInput__CRawGameController iface;328SDL_AtomicInt refcount;329} RawGameControllerDelegate;330331static HRESULT STDMETHODCALLTYPE IEventHandler_CRawGameControllerVtbl_QueryInterface(__FIEventHandler_1_Windows__CGaming__CInput__CRawGameController *This, REFIID riid, void **ppvObject)332{333if (!ppvObject) {334return E_INVALIDARG;335}336337*ppvObject = NULL;338if (WIN_IsEqualIID(riid, &IID_IUnknown) || WIN_IsEqualIID(riid, &IID_IAgileObject) || WIN_IsEqualIID(riid, &IID___FIEventHandler_1_Windows__CGaming__CInput__CRawGameController)) {339*ppvObject = This;340__FIEventHandler_1_Windows__CGaming__CInput__CRawGameController_AddRef(This);341return S_OK;342} else if (WIN_IsEqualIID(riid, &IID_IMarshal)) {343// This seems complicated. Let's hope it doesn't happen.344return E_OUTOFMEMORY;345} else {346return E_NOINTERFACE;347}348}349350static ULONG STDMETHODCALLTYPE IEventHandler_CRawGameControllerVtbl_AddRef(__FIEventHandler_1_Windows__CGaming__CInput__CRawGameController *This)351{352RawGameControllerDelegate *self = (RawGameControllerDelegate *)This;353return SDL_AddAtomicInt(&self->refcount, 1) + 1UL;354}355356static ULONG STDMETHODCALLTYPE IEventHandler_CRawGameControllerVtbl_Release(__FIEventHandler_1_Windows__CGaming__CInput__CRawGameController *This)357{358RawGameControllerDelegate *self = (RawGameControllerDelegate *)This;359int rc = SDL_AddAtomicInt(&self->refcount, -1) - 1;360// Should never free the static delegate objects361SDL_assert(rc > 0);362return rc;363}364365static int GetSteamVirtualGamepadSlot(__x_ABI_CWindows_CGaming_CInput_CIRawGameController *controller, Uint16 vendor_id, Uint16 product_id)366{367int slot = -1;368369if (vendor_id == USB_VENDOR_VALVE &&370product_id == USB_PRODUCT_STEAM_VIRTUAL_GAMEPAD) {371__x_ABI_CWindows_CGaming_CInput_CIRawGameController2 *controller2 = NULL;372HRESULT hr = __x_ABI_CWindows_CGaming_CInput_CIRawGameController_QueryInterface(controller, &IID___x_ABI_CWindows_CGaming_CInput_CIRawGameController2, (void **)&controller2);373if (SUCCEEDED(hr)) {374HSTRING hString;375hr = __x_ABI_CWindows_CGaming_CInput_CIRawGameController2_get_NonRoamableId(controller2, &hString);376if (SUCCEEDED(hr)) {377PCWSTR string = wgi.WindowsGetStringRawBuffer(hString, NULL);378if (string) {379char *id = WIN_StringToUTF8W(string);380if (id) {381(void)SDL_sscanf(id, "{wgi/nrid/:steam-%*X&%*X&%*X#%d#%*u}", &slot);382SDL_free(id);383}384}385wgi.WindowsDeleteString(hString);386}387__x_ABI_CWindows_CGaming_CInput_CIRawGameController2_Release(controller2);388}389}390return slot;391}392393static HRESULT STDMETHODCALLTYPE IEventHandler_CRawGameControllerVtbl_InvokeAdded(__FIEventHandler_1_Windows__CGaming__CInput__CRawGameController *This, IInspectable *sender, __x_ABI_CWindows_CGaming_CInput_CIRawGameController *e)394{395HRESULT hr;396__x_ABI_CWindows_CGaming_CInput_CIRawGameController *controller = NULL;397398SDL_LockJoysticks();399400// We can get delayed calls to InvokeAdded() after WGI_JoystickQuit()401if (SDL_JoysticksQuitting() || !SDL_JoysticksInitialized()) {402SDL_UnlockJoysticks();403return S_OK;404}405406hr = __x_ABI_CWindows_CGaming_CInput_CIRawGameController_QueryInterface(e, &IID___x_ABI_CWindows_CGaming_CInput_CIRawGameController, (void **)&controller);407if (SUCCEEDED(hr)) {408char *name = NULL;409Uint16 bus = SDL_HARDWARE_BUS_USB;410Uint16 vendor = 0;411Uint16 product = 0;412Uint16 version = 0;413SDL_JoystickType type = SDL_JOYSTICK_TYPE_UNKNOWN;414__x_ABI_CWindows_CGaming_CInput_CIRawGameController2 *controller2 = NULL;415__x_ABI_CWindows_CGaming_CInput_CIGameController *game_controller = NULL;416bool ignore_joystick = false;417418__x_ABI_CWindows_CGaming_CInput_CIRawGameController_get_HardwareVendorId(controller, &vendor);419__x_ABI_CWindows_CGaming_CInput_CIRawGameController_get_HardwareProductId(controller, &product);420421hr = __x_ABI_CWindows_CGaming_CInput_CIRawGameController_QueryInterface(controller, &IID___x_ABI_CWindows_CGaming_CInput_CIGameController, (void **)&game_controller);422if (SUCCEEDED(hr)) {423boolean wireless = 0;424hr = __x_ABI_CWindows_CGaming_CInput_CIGameController_get_IsWireless(game_controller, &wireless);425if (SUCCEEDED(hr) && wireless) {426bus = SDL_HARDWARE_BUS_BLUETOOTH;427428// Fixup for Wireless Xbox 360 Controller429if (product == 0) {430vendor = USB_VENDOR_MICROSOFT;431product = USB_PRODUCT_XBOX360_XUSB_CONTROLLER;432}433}434435__x_ABI_CWindows_CGaming_CInput_CIGameController_Release(game_controller);436}437438hr = __x_ABI_CWindows_CGaming_CInput_CIRawGameController_QueryInterface(controller, &IID___x_ABI_CWindows_CGaming_CInput_CIRawGameController2, (void **)&controller2);439if (SUCCEEDED(hr)) {440HSTRING hString;441hr = __x_ABI_CWindows_CGaming_CInput_CIRawGameController2_get_DisplayName(controller2, &hString);442if (SUCCEEDED(hr)) {443PCWSTR string = wgi.WindowsGetStringRawBuffer(hString, NULL);444if (string) {445name = WIN_StringToUTF8W(string);446}447wgi.WindowsDeleteString(hString);448}449__x_ABI_CWindows_CGaming_CInput_CIRawGameController2_Release(controller2);450}451if (!name) {452name = SDL_strdup("");453}454455if (!ignore_joystick && SDL_ShouldIgnoreJoystick(vendor, product, version, name)) {456ignore_joystick = true;457}458459if (!ignore_joystick && SDL_JoystickHandledByAnotherDriver(&SDL_WGI_JoystickDriver, vendor, product, version, name)) {460ignore_joystick = true;461}462463if (!ignore_joystick && SDL_IsXInputDevice(vendor, product, name)) {464// This hasn't been detected by the RAWINPUT driver yet, but it will be picked up later.465ignore_joystick = true;466}467468if (!ignore_joystick) {469// New device, add it470WindowsGamingInputControllerState *controllers = SDL_realloc(wgi.controllers, sizeof(wgi.controllers[0]) * (wgi.controller_count + 1));471if (controllers) {472WindowsGamingInputControllerState *state = &controllers[wgi.controller_count];473SDL_JoystickID joystickID = SDL_GetNextObjectID();474475if (game_controller) {476type = GetGameControllerType(game_controller);477}478479SDL_zerop(state);480state->instance_id = joystickID;481state->controller = controller;482state->name = name;483state->guid = SDL_CreateJoystickGUID(bus, vendor, product, version, NULL, name, 'w', (Uint8)type);484state->type = type;485state->steam_virtual_gamepad_slot = GetSteamVirtualGamepadSlot(controller, vendor, product);486487__x_ABI_CWindows_CGaming_CInput_CIRawGameController_AddRef(controller);488489++wgi.controller_count;490wgi.controllers = controllers;491492SDL_PrivateJoystickAdded(joystickID);493} else {494SDL_free(name);495}496} else {497SDL_free(name);498}499500__x_ABI_CWindows_CGaming_CInput_CIRawGameController_Release(controller);501}502503SDL_UnlockJoysticks();504505return S_OK;506}507508static HRESULT STDMETHODCALLTYPE IEventHandler_CRawGameControllerVtbl_InvokeRemoved(__FIEventHandler_1_Windows__CGaming__CInput__CRawGameController *This, IInspectable *sender, __x_ABI_CWindows_CGaming_CInput_CIRawGameController *e)509{510HRESULT hr;511__x_ABI_CWindows_CGaming_CInput_CIRawGameController *controller = NULL;512513SDL_LockJoysticks();514515// Can we get delayed calls to InvokeRemoved() after WGI_JoystickQuit()?516if (!SDL_JoysticksInitialized()) {517SDL_UnlockJoysticks();518return S_OK;519}520521hr = __x_ABI_CWindows_CGaming_CInput_CIRawGameController_QueryInterface(e, &IID___x_ABI_CWindows_CGaming_CInput_CIRawGameController, (void **)&controller);522if (SUCCEEDED(hr)) {523int i;524525for (i = 0; i < wgi.controller_count; i++) {526if (wgi.controllers[i].controller == controller) {527WindowsGamingInputControllerState *state = &wgi.controllers[i];528SDL_JoystickID joystickID = state->instance_id;529530__x_ABI_CWindows_CGaming_CInput_CIRawGameController_Release(state->controller);531532SDL_free(state->name);533534--wgi.controller_count;535if (i < wgi.controller_count) {536SDL_memmove(&wgi.controllers[i], &wgi.controllers[i + 1], (wgi.controller_count - i) * sizeof(wgi.controllers[i]));537}538539SDL_PrivateJoystickRemoved(joystickID);540break;541}542}543544__x_ABI_CWindows_CGaming_CInput_CIRawGameController_Release(controller);545}546547SDL_UnlockJoysticks();548549return S_OK;550}551552#ifdef _MSC_VER553#pragma warning(push)554#pragma warning(disable : 4028) // formal parameter 3 different from declaration, when using older buggy WGI headers555#pragma warning(disable : 4113) // formal parameter 3 different from declaration (a more specific warning added in VS 2022), when using older buggy WGI headers556#endif557558static __FIEventHandler_1_Windows__CGaming__CInput__CRawGameControllerVtbl controller_added_vtbl = {559IEventHandler_CRawGameControllerVtbl_QueryInterface,560IEventHandler_CRawGameControllerVtbl_AddRef,561IEventHandler_CRawGameControllerVtbl_Release,562IEventHandler_CRawGameControllerVtbl_InvokeAdded563};564static RawGameControllerDelegate controller_added = {565{ &controller_added_vtbl },566{ 1 }567};568569static __FIEventHandler_1_Windows__CGaming__CInput__CRawGameControllerVtbl controller_removed_vtbl = {570IEventHandler_CRawGameControllerVtbl_QueryInterface,571IEventHandler_CRawGameControllerVtbl_AddRef,572IEventHandler_CRawGameControllerVtbl_Release,573IEventHandler_CRawGameControllerVtbl_InvokeRemoved574};575static RawGameControllerDelegate controller_removed = {576{ &controller_removed_vtbl },577{ 1 }578};579580#ifdef _MSC_VER581#pragma warning(pop)582#endif583584static bool WGI_JoystickInit(void)585{586HRESULT hr;587588if (!SDL_GetHintBoolean(SDL_HINT_JOYSTICK_WGI, false)) {589return true;590}591592if (FAILED(WIN_RoInitialize())) {593return SDL_SetError("RoInitialize() failed");594}595wgi.ro_initialized = true;596597#define RESOLVE(x) wgi.x = (x##_t)WIN_LoadComBaseFunction(#x); if (!wgi.x) return WIN_SetError("GetProcAddress failed for " #x);598RESOLVE(CoIncrementMTAUsage);599RESOLVE(RoGetActivationFactory);600RESOLVE(WindowsCreateStringReference);601RESOLVE(WindowsDeleteString);602RESOLVE(WindowsGetStringRawBuffer);603#undef RESOLVE604605{606/* There seems to be a bug in Windows where a dependency of WGI can be unloaded from memory prior to WGI itself.607* This results in Windows_Gaming_Input!GameController::~GameController() invoking an unloaded DLL and crashing.608* As a workaround, we will keep a reference to the MTA to prevent COM from unloading DLLs later.609* See https://github.com/libsdl-org/SDL/issues/5552 for more details.610*/611static CO_MTA_USAGE_COOKIE cookie = NULL;612if (!cookie) {613hr = wgi.CoIncrementMTAUsage(&cookie);614if (FAILED(hr)) {615return WIN_SetErrorFromHRESULT("CoIncrementMTAUsage() failed", hr);616}617}618}619620WGI_LoadRawGameControllerStatics();621622if (wgi.controller_statics) {623__FIVectorView_1_Windows__CGaming__CInput__CRawGameController *controllers;624625hr = __x_ABI_CWindows_CGaming_CInput_CIRawGameControllerStatics_add_RawGameControllerAdded(wgi.controller_statics, &controller_added.iface, &wgi.controller_added_token);626if (!SUCCEEDED(hr)) {627WIN_SetErrorFromHRESULT("Windows.Gaming.Input.IRawGameControllerStatics.add_RawGameControllerAdded failed", hr);628}629630hr = __x_ABI_CWindows_CGaming_CInput_CIRawGameControllerStatics_add_RawGameControllerRemoved(wgi.controller_statics, &controller_removed.iface, &wgi.controller_removed_token);631if (!SUCCEEDED(hr)) {632WIN_SetErrorFromHRESULT("Windows.Gaming.Input.IRawGameControllerStatics.add_RawGameControllerRemoved failed", hr);633}634635hr = __x_ABI_CWindows_CGaming_CInput_CIRawGameControllerStatics_get_RawGameControllers(wgi.controller_statics, &controllers);636if (SUCCEEDED(hr)) {637unsigned i, count = 0;638639hr = __FIVectorView_1_Windows__CGaming__CInput__CRawGameController_get_Size(controllers, &count);640if (SUCCEEDED(hr)) {641for (i = 0; i < count; ++i) {642__x_ABI_CWindows_CGaming_CInput_CIRawGameController *controller = NULL;643644hr = __FIVectorView_1_Windows__CGaming__CInput__CRawGameController_GetAt(controllers, i, &controller);645if (SUCCEEDED(hr) && controller) {646IEventHandler_CRawGameControllerVtbl_InvokeAdded(&controller_added.iface, NULL, controller);647__x_ABI_CWindows_CGaming_CInput_CIRawGameController_Release(controller);648}649}650}651652__FIVectorView_1_Windows__CGaming__CInput__CRawGameController_Release(controllers);653}654}655656return true;657}658659static int WGI_JoystickGetCount(void)660{661return wgi.controller_count;662}663664static void WGI_JoystickDetect(void)665{666}667668static bool WGI_JoystickIsDevicePresent(Uint16 vendor_id, Uint16 product_id, Uint16 version, const char *name)669{670// We don't override any other drivers671return false;672}673674static const char *WGI_JoystickGetDeviceName(int device_index)675{676return wgi.controllers[device_index].name;677}678679static const char *WGI_JoystickGetDevicePath(int device_index)680{681return NULL;682}683684static int WGI_JoystickGetDeviceSteamVirtualGamepadSlot(int device_index)685{686return wgi.controllers[device_index].steam_virtual_gamepad_slot;687}688689static int WGI_JoystickGetDevicePlayerIndex(int device_index)690{691return false;692}693694static void WGI_JoystickSetDevicePlayerIndex(int device_index, int player_index)695{696}697698static SDL_GUID WGI_JoystickGetDeviceGUID(int device_index)699{700return wgi.controllers[device_index].guid;701}702703static SDL_JoystickID WGI_JoystickGetDeviceInstanceID(int device_index)704{705return wgi.controllers[device_index].instance_id;706}707708static bool WGI_JoystickOpen(SDL_Joystick *joystick, int device_index)709{710WindowsGamingInputControllerState *state = &wgi.controllers[device_index];711struct joystick_hwdata *hwdata;712boolean wireless = false;713714hwdata = (struct joystick_hwdata *)SDL_calloc(1, sizeof(*hwdata));715if (!hwdata) {716return false;717}718joystick->hwdata = hwdata;719720hwdata->controller = state->controller;721__x_ABI_CWindows_CGaming_CInput_CIRawGameController_AddRef(hwdata->controller);722__x_ABI_CWindows_CGaming_CInput_CIRawGameController_QueryInterface(hwdata->controller, &IID___x_ABI_CWindows_CGaming_CInput_CIGameController, (void **)&hwdata->game_controller);723__x_ABI_CWindows_CGaming_CInput_CIRawGameController_QueryInterface(hwdata->controller, &IID___x_ABI_CWindows_CGaming_CInput_CIGameControllerBatteryInfo, (void **)&hwdata->battery);724725if (wgi.gamepad_statics2) {726__x_ABI_CWindows_CGaming_CInput_CIGamepadStatics2_FromGameController(wgi.gamepad_statics2, hwdata->game_controller, &hwdata->gamepad);727}728729if (hwdata->game_controller) {730__x_ABI_CWindows_CGaming_CInput_CIGameController_get_IsWireless(hwdata->game_controller, &wireless);731}732733// Initialize the joystick capabilities734if (wireless) {735joystick->connection_state = SDL_JOYSTICK_CONNECTION_WIRELESS;736} else {737joystick->connection_state = SDL_JOYSTICK_CONNECTION_WIRED;738}739__x_ABI_CWindows_CGaming_CInput_CIRawGameController_get_ButtonCount(hwdata->controller, &joystick->nbuttons);740__x_ABI_CWindows_CGaming_CInput_CIRawGameController_get_AxisCount(hwdata->controller, &joystick->naxes);741__x_ABI_CWindows_CGaming_CInput_CIRawGameController_get_SwitchCount(hwdata->controller, &joystick->nhats);742743if (hwdata->gamepad) {744// FIXME: Can WGI even tell us if trigger rumble is supported?745SDL_SetBooleanProperty(SDL_GetJoystickProperties(joystick), SDL_PROP_JOYSTICK_CAP_RUMBLE_BOOLEAN, true);746SDL_SetBooleanProperty(SDL_GetJoystickProperties(joystick), SDL_PROP_JOYSTICK_CAP_TRIGGER_RUMBLE_BOOLEAN, true);747}748return true;749}750751static bool WGI_JoystickRumble(SDL_Joystick *joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble)752{753struct joystick_hwdata *hwdata = joystick->hwdata;754755if (hwdata->gamepad) {756HRESULT hr;757758// Note: reusing partially filled vibration data struct759hwdata->vibration.LeftMotor = (DOUBLE)low_frequency_rumble / SDL_MAX_UINT16;760hwdata->vibration.RightMotor = (DOUBLE)high_frequency_rumble / SDL_MAX_UINT16;761hr = __x_ABI_CWindows_CGaming_CInput_CIGamepad_put_Vibration(hwdata->gamepad, hwdata->vibration);762if (SUCCEEDED(hr)) {763return true;764} else {765return WIN_SetErrorFromHRESULT("Windows.Gaming.Input.IGamepad.put_Vibration failed", hr);766}767} else {768return SDL_Unsupported();769}770}771772static bool WGI_JoystickRumbleTriggers(SDL_Joystick *joystick, Uint16 left_rumble, Uint16 right_rumble)773{774struct joystick_hwdata *hwdata = joystick->hwdata;775776if (hwdata->gamepad) {777HRESULT hr;778779// Note: reusing partially filled vibration data struct780hwdata->vibration.LeftTrigger = (DOUBLE)left_rumble / SDL_MAX_UINT16;781hwdata->vibration.RightTrigger = (DOUBLE)right_rumble / SDL_MAX_UINT16;782hr = __x_ABI_CWindows_CGaming_CInput_CIGamepad_put_Vibration(hwdata->gamepad, hwdata->vibration);783if (SUCCEEDED(hr)) {784return true;785} else {786return WIN_SetErrorFromHRESULT("Windows.Gaming.Input.IGamepad.put_Vibration failed", hr);787}788} else {789return SDL_Unsupported();790}791}792793static bool WGI_JoystickSetLED(SDL_Joystick *joystick, Uint8 red, Uint8 green, Uint8 blue)794{795return SDL_Unsupported();796}797798static bool WGI_JoystickSendEffect(SDL_Joystick *joystick, const void *data, int size)799{800return SDL_Unsupported();801}802803static bool WGI_JoystickSetSensorsEnabled(SDL_Joystick *joystick, bool enabled)804{805return SDL_Unsupported();806}807808static Uint8 ConvertHatValue(__x_ABI_CWindows_CGaming_CInput_CGameControllerSwitchPosition value)809{810switch (value) {811case GameControllerSwitchPosition_Up:812return SDL_HAT_UP;813case GameControllerSwitchPosition_UpRight:814return SDL_HAT_RIGHTUP;815case GameControllerSwitchPosition_Right:816return SDL_HAT_RIGHT;817case GameControllerSwitchPosition_DownRight:818return SDL_HAT_RIGHTDOWN;819case GameControllerSwitchPosition_Down:820return SDL_HAT_DOWN;821case GameControllerSwitchPosition_DownLeft:822return SDL_HAT_LEFTDOWN;823case GameControllerSwitchPosition_Left:824return SDL_HAT_LEFT;825case GameControllerSwitchPosition_UpLeft:826return SDL_HAT_LEFTUP;827default:828return SDL_HAT_CENTERED;829}830}831832static void WGI_JoystickUpdate(SDL_Joystick *joystick)833{834struct joystick_hwdata *hwdata = joystick->hwdata;835HRESULT hr;836UINT32 nbuttons = SDL_min(joystick->nbuttons, SDL_MAX_UINT8);837boolean *buttons = NULL;838UINT32 nhats = SDL_min(joystick->nhats, SDL_MAX_UINT8);839__x_ABI_CWindows_CGaming_CInput_CGameControllerSwitchPosition *hats = NULL;840UINT32 naxes = SDL_min(joystick->naxes, SDL_MAX_UINT8);841DOUBLE *axes = NULL;842UINT64 timestamp;843844if (nbuttons > 0) {845buttons = SDL_stack_alloc(boolean, nbuttons);846}847if (nhats > 0) {848hats = SDL_stack_alloc(__x_ABI_CWindows_CGaming_CInput_CGameControllerSwitchPosition, nhats);849}850if (naxes > 0) {851axes = SDL_stack_alloc(DOUBLE, naxes);852}853854hr = __x_ABI_CWindows_CGaming_CInput_CIRawGameController_GetCurrentReading(hwdata->controller, nbuttons, buttons, nhats, hats, naxes, axes, ×tamp);855if (SUCCEEDED(hr) && (!timestamp || timestamp != hwdata->timestamp)) {856UINT32 i;857bool all_zero = false;858859hwdata->timestamp = timestamp;860861// The axes are all zero when the application loses focus862if (naxes > 0) {863all_zero = true;864for (i = 0; i < naxes; ++i) {865if (axes[i] != 0.0f) {866all_zero = false;867break;868}869}870}871if (all_zero) {872SDL_PrivateJoystickForceRecentering(joystick);873} else {874// FIXME: What units are the timestamp we get from GetCurrentReading()?875timestamp = SDL_GetTicksNS();876for (i = 0; i < nbuttons; ++i) {877SDL_SendJoystickButton(timestamp, joystick, (Uint8)i, buttons[i]);878}879for (i = 0; i < nhats; ++i) {880SDL_SendJoystickHat(timestamp, joystick, (Uint8)i, ConvertHatValue(hats[i]));881}882for (i = 0; i < naxes; ++i) {883SDL_SendJoystickAxis(timestamp, joystick, (Uint8)i, (Sint16)((int)(axes[i] * 65535) - 32768));884}885}886}887888SDL_stack_free(buttons);889SDL_stack_free(hats);890SDL_stack_free(axes);891892if (hwdata->battery) {893__x_ABI_CWindows_CDevices_CPower_CIBatteryReport *report = NULL;894895hr = __x_ABI_CWindows_CGaming_CInput_CIGameControllerBatteryInfo_TryGetBatteryReport(hwdata->battery, &report);896if (SUCCEEDED(hr) && report) {897SDL_PowerState state = SDL_POWERSTATE_UNKNOWN;898int percent = 0;899__x_ABI_CWindows_CSystem_CPower_CBatteryStatus status;900int full_capacity = 0, curr_capacity = 0;901__FIReference_1_int *full_capacityP, *curr_capacityP;902903hr = __x_ABI_CWindows_CDevices_CPower_CIBatteryReport_get_Status(report, &status);904if (SUCCEEDED(hr)) {905switch (status) {906case BatteryStatus_NotPresent:907state = SDL_POWERSTATE_NO_BATTERY;908break;909case BatteryStatus_Discharging:910state = SDL_POWERSTATE_ON_BATTERY;911break;912case BatteryStatus_Idle:913state = SDL_POWERSTATE_CHARGED;914break;915case BatteryStatus_Charging:916state = SDL_POWERSTATE_CHARGING;917break;918default:919state = SDL_POWERSTATE_UNKNOWN;920break;921}922}923924hr = __x_ABI_CWindows_CDevices_CPower_CIBatteryReport_get_FullChargeCapacityInMilliwattHours(report, &full_capacityP);925if (SUCCEEDED(hr)) {926__FIReference_1_int_get_Value(full_capacityP, &full_capacity);927__FIReference_1_int_Release(full_capacityP);928}929930hr = __x_ABI_CWindows_CDevices_CPower_CIBatteryReport_get_RemainingCapacityInMilliwattHours(report, &curr_capacityP);931if (SUCCEEDED(hr)) {932__FIReference_1_int_get_Value(curr_capacityP, &curr_capacity);933__FIReference_1_int_Release(curr_capacityP);934}935936if (full_capacity > 0) {937percent = (int)SDL_roundf(((float)curr_capacity / full_capacity) * 100.0f);938}939940SDL_SendJoystickPowerInfo(joystick, state, percent);941942__x_ABI_CWindows_CDevices_CPower_CIBatteryReport_Release(report);943}944}945}946947static void WGI_JoystickClose(SDL_Joystick *joystick)948{949struct joystick_hwdata *hwdata = joystick->hwdata;950951if (hwdata) {952if (hwdata->controller) {953__x_ABI_CWindows_CGaming_CInput_CIRawGameController_Release(hwdata->controller);954}955if (hwdata->game_controller) {956__x_ABI_CWindows_CGaming_CInput_CIGameController_Release(hwdata->game_controller);957}958if (hwdata->battery) {959__x_ABI_CWindows_CGaming_CInput_CIGameControllerBatteryInfo_Release(hwdata->battery);960}961if (hwdata->gamepad) {962__x_ABI_CWindows_CGaming_CInput_CIGamepad_Release(hwdata->gamepad);963}964SDL_free(hwdata);965}966joystick->hwdata = NULL;967}968969static void WGI_JoystickQuit(void)970{971if (wgi.controller_statics) {972while (wgi.controller_count > 0) {973IEventHandler_CRawGameControllerVtbl_InvokeRemoved(&controller_removed.iface, NULL, wgi.controllers[wgi.controller_count - 1].controller);974}975if (wgi.controllers) {976SDL_free(wgi.controllers);977}978979if (wgi.arcade_stick_statics) {980__x_ABI_CWindows_CGaming_CInput_CIArcadeStickStatics_Release(wgi.arcade_stick_statics);981}982if (wgi.arcade_stick_statics2) {983__x_ABI_CWindows_CGaming_CInput_CIArcadeStickStatics2_Release(wgi.arcade_stick_statics2);984}985if (wgi.flight_stick_statics) {986__x_ABI_CWindows_CGaming_CInput_CIFlightStickStatics_Release(wgi.flight_stick_statics);987}988if (wgi.gamepad_statics) {989__x_ABI_CWindows_CGaming_CInput_CIGamepadStatics_Release(wgi.gamepad_statics);990}991if (wgi.gamepad_statics2) {992__x_ABI_CWindows_CGaming_CInput_CIGamepadStatics2_Release(wgi.gamepad_statics2);993}994if (wgi.racing_wheel_statics) {995__x_ABI_CWindows_CGaming_CInput_CIRacingWheelStatics_Release(wgi.racing_wheel_statics);996}997if (wgi.racing_wheel_statics2) {998__x_ABI_CWindows_CGaming_CInput_CIRacingWheelStatics2_Release(wgi.racing_wheel_statics2);999}10001001__x_ABI_CWindows_CGaming_CInput_CIRawGameControllerStatics_remove_RawGameControllerAdded(wgi.controller_statics, wgi.controller_added_token);1002__x_ABI_CWindows_CGaming_CInput_CIRawGameControllerStatics_remove_RawGameControllerRemoved(wgi.controller_statics, wgi.controller_removed_token);1003__x_ABI_CWindows_CGaming_CInput_CIRawGameControllerStatics_Release(wgi.controller_statics);1004}10051006if (wgi.ro_initialized) {1007WIN_RoUninitialize();1008}10091010SDL_zero(wgi);1011}10121013static bool WGI_JoystickGetGamepadMapping(int device_index, SDL_GamepadMapping *out)1014{1015return false;1016}10171018SDL_JoystickDriver SDL_WGI_JoystickDriver = {1019WGI_JoystickInit,1020WGI_JoystickGetCount,1021WGI_JoystickDetect,1022WGI_JoystickIsDevicePresent,1023WGI_JoystickGetDeviceName,1024WGI_JoystickGetDevicePath,1025WGI_JoystickGetDeviceSteamVirtualGamepadSlot,1026WGI_JoystickGetDevicePlayerIndex,1027WGI_JoystickSetDevicePlayerIndex,1028WGI_JoystickGetDeviceGUID,1029WGI_JoystickGetDeviceInstanceID,1030WGI_JoystickOpen,1031WGI_JoystickRumble,1032WGI_JoystickRumbleTriggers,1033WGI_JoystickSetLED,1034WGI_JoystickSendEffect,1035WGI_JoystickSetSensorsEnabled,1036WGI_JoystickUpdate,1037WGI_JoystickClose,1038WGI_JoystickQuit,1039WGI_JoystickGetGamepadMapping1040};10411042#endif // SDL_JOYSTICK_WGI104310441045