Path: blob/master/thirdparty/sdl/joystick/windows/SDL_windows_gaming_input.c
9905 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{71CoIncrementMTAUsage_t CoIncrementMTAUsage;72RoGetActivationFactory_t RoGetActivationFactory;73WindowsCreateStringReference_t WindowsCreateStringReference;74WindowsDeleteString_t WindowsDeleteString;75WindowsGetStringRawBuffer_t WindowsGetStringRawBuffer;76__x_ABI_CWindows_CGaming_CInput_CIRawGameControllerStatics *controller_statics;77__x_ABI_CWindows_CGaming_CInput_CIArcadeStickStatics *arcade_stick_statics;78__x_ABI_CWindows_CGaming_CInput_CIArcadeStickStatics2 *arcade_stick_statics2;79__x_ABI_CWindows_CGaming_CInput_CIFlightStickStatics *flight_stick_statics;80__x_ABI_CWindows_CGaming_CInput_CIGamepadStatics *gamepad_statics;81__x_ABI_CWindows_CGaming_CInput_CIGamepadStatics2 *gamepad_statics2;82__x_ABI_CWindows_CGaming_CInput_CIRacingWheelStatics *racing_wheel_statics;83__x_ABI_CWindows_CGaming_CInput_CIRacingWheelStatics2 *racing_wheel_statics2;84EventRegistrationToken controller_added_token;85EventRegistrationToken controller_removed_token;86int controller_count;87WindowsGamingInputControllerState *controllers;88} wgi;8990// WinRT headers in official Windows SDK contain only declarations, and we have to define these GUIDs ourselves.91// https://stackoverflow.com/a/55605485/179505092DEFINE_GUID(IID___FIEventHandler_1_Windows__CGaming__CInput__CRawGameController, 0x00621c22, 0x42e8, 0x529f, 0x92, 0x70, 0x83, 0x6b, 0x32, 0x93, 0x1d, 0x72);93DEFINE_GUID(IID___x_ABI_CWindows_CGaming_CInput_CIArcadeStickStatics, 0x5c37b8c8, 0x37b1, 0x4ad8, 0x94, 0x58, 0x20, 0x0f, 0x1a, 0x30, 0x01, 0x8e);94DEFINE_GUID(IID___x_ABI_CWindows_CGaming_CInput_CIArcadeStickStatics2, 0x52b5d744, 0xbb86, 0x445a, 0xb5, 0x9c, 0x59, 0x6f, 0x0e, 0x2a, 0x49, 0xdf);95DEFINE_GUID(IID___x_ABI_CWindows_CGaming_CInput_CIFlightStickStatics, 0x5514924a, 0xfecc, 0x435e, 0x83, 0xdc, 0x5c, 0xec, 0x8a, 0x18, 0xa5, 0x20);96DEFINE_GUID(IID___x_ABI_CWindows_CGaming_CInput_CIGameController, 0x1baf6522, 0x5f64, 0x42c5, 0x82, 0x67, 0xb9, 0xfe, 0x22, 0x15, 0xbf, 0xbd);97DEFINE_GUID(IID___x_ABI_CWindows_CGaming_CInput_CIGameControllerBatteryInfo, 0xdcecc681, 0x3963, 0x4da6, 0x95, 0x5d, 0x55, 0x3f, 0x3b, 0x6f, 0x61, 0x61);98DEFINE_GUID(IID___x_ABI_CWindows_CGaming_CInput_CIGamepadStatics, 0x8bbce529, 0xd49c, 0x39e9, 0x95, 0x60, 0xe4, 0x7d, 0xde, 0x96, 0xb7, 0xc8);99DEFINE_GUID(IID___x_ABI_CWindows_CGaming_CInput_CIGamepadStatics2, 0x42676dc5, 0x0856, 0x47c4, 0x92, 0x13, 0xb3, 0x95, 0x50, 0x4c, 0x3a, 0x3c);100DEFINE_GUID(IID___x_ABI_CWindows_CGaming_CInput_CIRacingWheelStatics, 0x3ac12cd5, 0x581b, 0x4936, 0x9f, 0x94, 0x69, 0xf1, 0xe6, 0x51, 0x4c, 0x7d);101DEFINE_GUID(IID___x_ABI_CWindows_CGaming_CInput_CIRacingWheelStatics2, 0xe666bcaa, 0xedfd, 0x4323, 0xa9, 0xf6, 0x3c, 0x38, 0x40, 0x48, 0xd1, 0xed);102DEFINE_GUID(IID___x_ABI_CWindows_CGaming_CInput_CIRawGameController, 0x7cad6d91, 0xa7e1, 0x4f71, 0x9a, 0x78, 0x33, 0xe9, 0xc5, 0xdf, 0xea, 0x62);103DEFINE_GUID(IID___x_ABI_CWindows_CGaming_CInput_CIRawGameController2, 0x43c0c035, 0xbb73, 0x4756, 0xa7, 0x87, 0x3e, 0xd6, 0xbe, 0xa6, 0x17, 0xbd);104DEFINE_GUID(IID___x_ABI_CWindows_CGaming_CInput_CIRawGameControllerStatics, 0xeb8d0792, 0xe95a, 0x4b19, 0xaf, 0xc7, 0x0a, 0x59, 0xf8, 0xbf, 0x75, 0x9e);105106extern bool SDL_XINPUT_Enabled(void);107108109static bool SDL_IsXInputDevice(Uint16 vendor, Uint16 product, const char *name)110{111#if defined(SDL_JOYSTICK_XINPUT) || defined(SDL_JOYSTICK_RAWINPUT)112PRAWINPUTDEVICELIST raw_devices = NULL;113UINT i, raw_device_count = 0;114LONG vidpid = MAKELONG(vendor, product);115116// XInput and RawInput backends will pick up XInput-compatible devices117if (!SDL_XINPUT_Enabled()118#ifdef SDL_JOYSTICK_RAWINPUT119&& !RAWINPUT_IsEnabled()120#endif121) {122return false;123}124125// Sometimes we'll get a Windows.Gaming.Input callback before the raw input device is even in the list,126// so try to do some checks up front to catch these cases.127if (SDL_IsJoystickXboxOne(vendor, product) ||128(name && SDL_strncmp(name, "Xbox ", 5) == 0)) {129return true;130}131132// Go through RAWINPUT (WinXP and later) to find HID devices.133if ((GetRawInputDeviceList(NULL, &raw_device_count, sizeof(RAWINPUTDEVICELIST)) == -1) || (!raw_device_count)) {134return false; // oh well.135}136137raw_devices = (PRAWINPUTDEVICELIST)SDL_malloc(sizeof(RAWINPUTDEVICELIST) * raw_device_count);138if (!raw_devices) {139return false;140}141142raw_device_count = GetRawInputDeviceList(raw_devices, &raw_device_count, sizeof(RAWINPUTDEVICELIST));143if (raw_device_count == (UINT)-1) {144SDL_free(raw_devices);145raw_devices = NULL;146return false; // oh well.147}148149for (i = 0; i < raw_device_count; i++) {150RID_DEVICE_INFO rdi;151char devName[MAX_PATH] = { 0 };152UINT rdiSize = sizeof(rdi);153UINT nameSize = SDL_arraysize(devName);154DEVINST devNode;155char devVidPidString[32];156int j;157158rdi.cbSize = sizeof(rdi);159160if ((raw_devices[i].dwType != RIM_TYPEHID) ||161(GetRawInputDeviceInfoA(raw_devices[i].hDevice, RIDI_DEVICEINFO, &rdi, &rdiSize) == ((UINT)-1)) ||162(GetRawInputDeviceInfoA(raw_devices[i].hDevice, RIDI_DEVICENAME, devName, &nameSize) == ((UINT)-1)) ||163(SDL_strstr(devName, "IG_") == NULL)) {164// Skip non-XInput devices165continue;166}167168// First check for a simple VID/PID match. This will work for Xbox 360 controllers.169if (MAKELONG(rdi.hid.dwVendorId, rdi.hid.dwProductId) == vidpid) {170SDL_free(raw_devices);171return true;172}173174/* For Xbox One controllers, Microsoft doesn't propagate the VID/PID down to the HID stack.175* We'll have to walk the device tree upwards searching for a match for our VID/PID. */176177// Make sure the device interface string is something we know how to parse178// Example: \\?\HID#VID_045E&PID_02FF&IG_00#9&2c203035&2&0000#{4d1e55b2-f16f-11cf-88cb-001111000030}179if ((SDL_strstr(devName, "\\\\?\\") != devName) || (SDL_strstr(devName, "#{") == NULL)) {180continue;181}182183// Unescape the backslashes in the string and terminate before the GUID portion184for (j = 0; devName[j] != '\0'; j++) {185if (devName[j] == '#') {186if (devName[j + 1] == '{') {187devName[j] = '\0';188break;189} else {190devName[j] = '\\';191}192}193}194195/* We'll be left with a string like this: \\?\HID\VID_045E&PID_02FF&IG_00\9&2c203035&2&0000196* Simply skip the \\?\ prefix and we'll have a properly formed device instance ID */197if (CM_Locate_DevNodeA(&devNode, &devName[4], CM_LOCATE_DEVNODE_NORMAL) != CR_SUCCESS) {198continue;199}200201(void)SDL_snprintf(devVidPidString, sizeof(devVidPidString), "VID_%04X&PID_%04X", vendor, product);202203while (CM_Get_Parent(&devNode, devNode, 0) == CR_SUCCESS) {204char deviceId[MAX_DEVICE_ID_LEN];205206if ((CM_Get_Device_IDA(devNode, deviceId, SDL_arraysize(deviceId), 0) == CR_SUCCESS) &&207(SDL_strstr(deviceId, devVidPidString) != NULL)) {208// The VID/PID matched a parent device209SDL_free(raw_devices);210return true;211}212}213}214215SDL_free(raw_devices);216#endif // SDL_JOYSTICK_XINPUT || SDL_JOYSTICK_RAWINPUT217218return false;219}220221static void WGI_LoadRawGameControllerStatics(void)222{223HRESULT hr;224HSTRING_HEADER class_name_header;225HSTRING class_name;226227hr = wgi.WindowsCreateStringReference(RuntimeClass_Windows_Gaming_Input_RawGameController, (UINT32)SDL_wcslen(RuntimeClass_Windows_Gaming_Input_RawGameController), &class_name_header, &class_name);228if (SUCCEEDED(hr)) {229hr = wgi.RoGetActivationFactory(class_name, &IID___x_ABI_CWindows_CGaming_CInput_CIRawGameControllerStatics, (void **)&wgi.controller_statics);230if (!SUCCEEDED(hr)) {231WIN_SetErrorFromHRESULT("Couldn't find Windows.Gaming.Input.IRawGameControllerStatics", hr);232}233}234}235236static void WGI_LoadOtherControllerStatics(void)237{238HRESULT hr;239HSTRING_HEADER class_name_header;240HSTRING class_name;241242if (!wgi.arcade_stick_statics) {243hr = wgi.WindowsCreateStringReference(RuntimeClass_Windows_Gaming_Input_ArcadeStick, (UINT32)SDL_wcslen(RuntimeClass_Windows_Gaming_Input_ArcadeStick), &class_name_header, &class_name);244if (SUCCEEDED(hr)) {245hr = wgi.RoGetActivationFactory(class_name, &IID___x_ABI_CWindows_CGaming_CInput_CIArcadeStickStatics, (void **)&wgi.arcade_stick_statics);246if (SUCCEEDED(hr)) {247__x_ABI_CWindows_CGaming_CInput_CIArcadeStickStatics_QueryInterface(wgi.arcade_stick_statics, &IID___x_ABI_CWindows_CGaming_CInput_CIArcadeStickStatics2, (void **)&wgi.arcade_stick_statics2);248} else {249WIN_SetErrorFromHRESULT("Couldn't find Windows.Gaming.Input.IArcadeStickStatics", hr);250}251}252}253254if (!wgi.flight_stick_statics) {255hr = wgi.WindowsCreateStringReference(RuntimeClass_Windows_Gaming_Input_FlightStick, (UINT32)SDL_wcslen(RuntimeClass_Windows_Gaming_Input_FlightStick), &class_name_header, &class_name);256if (SUCCEEDED(hr)) {257hr = wgi.RoGetActivationFactory(class_name, &IID___x_ABI_CWindows_CGaming_CInput_CIFlightStickStatics, (void **)&wgi.flight_stick_statics);258if (!SUCCEEDED(hr)) {259WIN_SetErrorFromHRESULT("Couldn't find Windows.Gaming.Input.IFlightStickStatics", hr);260}261}262}263264if (!wgi.gamepad_statics) {265hr = wgi.WindowsCreateStringReference(RuntimeClass_Windows_Gaming_Input_Gamepad, (UINT32)SDL_wcslen(RuntimeClass_Windows_Gaming_Input_Gamepad), &class_name_header, &class_name);266if (SUCCEEDED(hr)) {267hr = wgi.RoGetActivationFactory(class_name, &IID___x_ABI_CWindows_CGaming_CInput_CIGamepadStatics, (void **)&wgi.gamepad_statics);268if (SUCCEEDED(hr)) {269__x_ABI_CWindows_CGaming_CInput_CIGamepadStatics_QueryInterface(wgi.gamepad_statics, &IID___x_ABI_CWindows_CGaming_CInput_CIGamepadStatics2, (void **)&wgi.gamepad_statics2);270} else {271WIN_SetErrorFromHRESULT("Couldn't find Windows.Gaming.Input.IGamepadStatics", hr);272}273}274}275276if (!wgi.racing_wheel_statics) {277hr = wgi.WindowsCreateStringReference(RuntimeClass_Windows_Gaming_Input_RacingWheel, (UINT32)SDL_wcslen(RuntimeClass_Windows_Gaming_Input_RacingWheel), &class_name_header, &class_name);278if (SUCCEEDED(hr)) {279hr = wgi.RoGetActivationFactory(class_name, &IID___x_ABI_CWindows_CGaming_CInput_CIRacingWheelStatics, (void **)&wgi.racing_wheel_statics);280if (SUCCEEDED(hr)) {281__x_ABI_CWindows_CGaming_CInput_CIRacingWheelStatics_QueryInterface(wgi.racing_wheel_statics, &IID___x_ABI_CWindows_CGaming_CInput_CIRacingWheelStatics2, (void **)&wgi.racing_wheel_statics2);282} else {283WIN_SetErrorFromHRESULT("Couldn't find Windows.Gaming.Input.IRacingWheelStatics", hr);284}285}286}287}288289static SDL_JoystickType GetGameControllerType(__x_ABI_CWindows_CGaming_CInput_CIGameController *game_controller)290{291__x_ABI_CWindows_CGaming_CInput_CIArcadeStick *arcade_stick = NULL;292__x_ABI_CWindows_CGaming_CInput_CIFlightStick *flight_stick = NULL;293__x_ABI_CWindows_CGaming_CInput_CIGamepad *gamepad = NULL;294__x_ABI_CWindows_CGaming_CInput_CIRacingWheel *racing_wheel = NULL;295296/* Wait to initialize these interfaces until we need them.297* Initializing the gamepad interface will switch Bluetooth PS4 controllers into enhanced mode, breaking DirectInput298*/299WGI_LoadOtherControllerStatics();300301if (wgi.gamepad_statics2 && SUCCEEDED(__x_ABI_CWindows_CGaming_CInput_CIGamepadStatics2_FromGameController(wgi.gamepad_statics2, game_controller, &gamepad)) && gamepad) {302__x_ABI_CWindows_CGaming_CInput_CIGamepad_Release(gamepad);303return SDL_JOYSTICK_TYPE_GAMEPAD;304}305306if (wgi.arcade_stick_statics2 && SUCCEEDED(__x_ABI_CWindows_CGaming_CInput_CIArcadeStickStatics2_FromGameController(wgi.arcade_stick_statics2, game_controller, &arcade_stick)) && arcade_stick) {307__x_ABI_CWindows_CGaming_CInput_CIArcadeStick_Release(arcade_stick);308return SDL_JOYSTICK_TYPE_ARCADE_STICK;309}310311if (wgi.flight_stick_statics && SUCCEEDED(__x_ABI_CWindows_CGaming_CInput_CIFlightStickStatics_FromGameController(wgi.flight_stick_statics, game_controller, &flight_stick)) && flight_stick) {312__x_ABI_CWindows_CGaming_CInput_CIFlightStick_Release(flight_stick);313return SDL_JOYSTICK_TYPE_FLIGHT_STICK;314}315316if (wgi.racing_wheel_statics2 && SUCCEEDED(__x_ABI_CWindows_CGaming_CInput_CIRacingWheelStatics2_FromGameController(wgi.racing_wheel_statics2, game_controller, &racing_wheel)) && racing_wheel) {317__x_ABI_CWindows_CGaming_CInput_CIRacingWheel_Release(racing_wheel);318return SDL_JOYSTICK_TYPE_WHEEL;319}320321return SDL_JOYSTICK_TYPE_UNKNOWN;322}323324typedef struct RawGameControllerDelegate325{326__FIEventHandler_1_Windows__CGaming__CInput__CRawGameController iface;327SDL_AtomicInt refcount;328} RawGameControllerDelegate;329330static HRESULT STDMETHODCALLTYPE IEventHandler_CRawGameControllerVtbl_QueryInterface(__FIEventHandler_1_Windows__CGaming__CInput__CRawGameController *This, REFIID riid, void **ppvObject)331{332if (!ppvObject) {333return E_INVALIDARG;334}335336*ppvObject = NULL;337if (WIN_IsEqualIID(riid, &IID_IUnknown) || WIN_IsEqualIID(riid, &IID_IAgileObject) || WIN_IsEqualIID(riid, &IID___FIEventHandler_1_Windows__CGaming__CInput__CRawGameController)) {338*ppvObject = This;339__FIEventHandler_1_Windows__CGaming__CInput__CRawGameController_AddRef(This);340return S_OK;341} else if (WIN_IsEqualIID(riid, &IID_IMarshal)) {342// This seems complicated. Let's hope it doesn't happen.343return E_OUTOFMEMORY;344} else {345return E_NOINTERFACE;346}347}348349static ULONG STDMETHODCALLTYPE IEventHandler_CRawGameControllerVtbl_AddRef(__FIEventHandler_1_Windows__CGaming__CInput__CRawGameController *This)350{351RawGameControllerDelegate *self = (RawGameControllerDelegate *)This;352return SDL_AddAtomicInt(&self->refcount, 1) + 1UL;353}354355static ULONG STDMETHODCALLTYPE IEventHandler_CRawGameControllerVtbl_Release(__FIEventHandler_1_Windows__CGaming__CInput__CRawGameController *This)356{357RawGameControllerDelegate *self = (RawGameControllerDelegate *)This;358int rc = SDL_AddAtomicInt(&self->refcount, -1) - 1;359// Should never free the static delegate objects360SDL_assert(rc > 0);361return rc;362}363364static int GetSteamVirtualGamepadSlot(__x_ABI_CWindows_CGaming_CInput_CIRawGameController *controller, Uint16 vendor_id, Uint16 product_id)365{366int slot = -1;367368if (vendor_id == USB_VENDOR_VALVE &&369product_id == USB_PRODUCT_STEAM_VIRTUAL_GAMEPAD) {370__x_ABI_CWindows_CGaming_CInput_CIRawGameController2 *controller2 = NULL;371HRESULT hr = __x_ABI_CWindows_CGaming_CInput_CIRawGameController_QueryInterface(controller, &IID___x_ABI_CWindows_CGaming_CInput_CIRawGameController2, (void **)&controller2);372if (SUCCEEDED(hr)) {373HSTRING hString;374hr = __x_ABI_CWindows_CGaming_CInput_CIRawGameController2_get_NonRoamableId(controller2, &hString);375if (SUCCEEDED(hr)) {376PCWSTR string = wgi.WindowsGetStringRawBuffer(hString, NULL);377if (string) {378char *id = WIN_StringToUTF8W(string);379if (id) {380(void)SDL_sscanf(id, "{wgi/nrid/:steam-%*X&%*X&%*X#%d#%*u}", &slot);381SDL_free(id);382}383}384wgi.WindowsDeleteString(hString);385}386__x_ABI_CWindows_CGaming_CInput_CIRawGameController2_Release(controller2);387}388}389return slot;390}391392static HRESULT STDMETHODCALLTYPE IEventHandler_CRawGameControllerVtbl_InvokeAdded(__FIEventHandler_1_Windows__CGaming__CInput__CRawGameController *This, IInspectable *sender, __x_ABI_CWindows_CGaming_CInput_CIRawGameController *e)393{394HRESULT hr;395__x_ABI_CWindows_CGaming_CInput_CIRawGameController *controller = NULL;396397SDL_LockJoysticks();398399// We can get delayed calls to InvokeAdded() after WGI_JoystickQuit()400if (SDL_JoysticksQuitting() || !SDL_JoysticksInitialized()) {401SDL_UnlockJoysticks();402return S_OK;403}404405hr = __x_ABI_CWindows_CGaming_CInput_CIRawGameController_QueryInterface(e, &IID___x_ABI_CWindows_CGaming_CInput_CIRawGameController, (void **)&controller);406if (SUCCEEDED(hr)) {407char *name = NULL;408Uint16 bus = SDL_HARDWARE_BUS_USB;409Uint16 vendor = 0;410Uint16 product = 0;411Uint16 version = 0;412SDL_JoystickType type = SDL_JOYSTICK_TYPE_UNKNOWN;413__x_ABI_CWindows_CGaming_CInput_CIRawGameController2 *controller2 = NULL;414__x_ABI_CWindows_CGaming_CInput_CIGameController *game_controller = NULL;415bool ignore_joystick = false;416417__x_ABI_CWindows_CGaming_CInput_CIRawGameController_get_HardwareVendorId(controller, &vendor);418__x_ABI_CWindows_CGaming_CInput_CIRawGameController_get_HardwareProductId(controller, &product);419420hr = __x_ABI_CWindows_CGaming_CInput_CIRawGameController_QueryInterface(controller, &IID___x_ABI_CWindows_CGaming_CInput_CIGameController, (void **)&game_controller);421if (SUCCEEDED(hr)) {422boolean wireless = 0;423hr = __x_ABI_CWindows_CGaming_CInput_CIGameController_get_IsWireless(game_controller, &wireless);424if (SUCCEEDED(hr) && wireless) {425bus = SDL_HARDWARE_BUS_BLUETOOTH;426427// Fixup for Wireless Xbox 360 Controller428if (product == 0) {429vendor = USB_VENDOR_MICROSOFT;430product = USB_PRODUCT_XBOX360_XUSB_CONTROLLER;431}432}433434__x_ABI_CWindows_CGaming_CInput_CIGameController_Release(game_controller);435}436437hr = __x_ABI_CWindows_CGaming_CInput_CIRawGameController_QueryInterface(controller, &IID___x_ABI_CWindows_CGaming_CInput_CIRawGameController2, (void **)&controller2);438if (SUCCEEDED(hr)) {439HSTRING hString;440hr = __x_ABI_CWindows_CGaming_CInput_CIRawGameController2_get_DisplayName(controller2, &hString);441if (SUCCEEDED(hr)) {442PCWSTR string = wgi.WindowsGetStringRawBuffer(hString, NULL);443if (string) {444name = WIN_StringToUTF8W(string);445}446wgi.WindowsDeleteString(hString);447}448__x_ABI_CWindows_CGaming_CInput_CIRawGameController2_Release(controller2);449}450if (!name) {451name = SDL_strdup("");452}453454if (!ignore_joystick && SDL_ShouldIgnoreJoystick(vendor, product, version, name)) {455ignore_joystick = true;456}457458if (!ignore_joystick && SDL_JoystickHandledByAnotherDriver(&SDL_WGI_JoystickDriver, vendor, product, version, name)) {459ignore_joystick = true;460}461462if (!ignore_joystick && SDL_IsXInputDevice(vendor, product, name)) {463// This hasn't been detected by the RAWINPUT driver yet, but it will be picked up later.464ignore_joystick = true;465}466467if (!ignore_joystick) {468// New device, add it469WindowsGamingInputControllerState *controllers = SDL_realloc(wgi.controllers, sizeof(wgi.controllers[0]) * (wgi.controller_count + 1));470if (controllers) {471WindowsGamingInputControllerState *state = &controllers[wgi.controller_count];472SDL_JoystickID joystickID = SDL_GetNextObjectID();473474if (game_controller) {475type = GetGameControllerType(game_controller);476}477478SDL_zerop(state);479state->instance_id = joystickID;480state->controller = controller;481state->name = name;482state->guid = SDL_CreateJoystickGUID(bus, vendor, product, version, NULL, name, 'w', (Uint8)type);483state->type = type;484state->steam_virtual_gamepad_slot = GetSteamVirtualGamepadSlot(controller, vendor, product);485486__x_ABI_CWindows_CGaming_CInput_CIRawGameController_AddRef(controller);487488++wgi.controller_count;489wgi.controllers = controllers;490491SDL_PrivateJoystickAdded(joystickID);492} else {493SDL_free(name);494}495} else {496SDL_free(name);497}498499__x_ABI_CWindows_CGaming_CInput_CIRawGameController_Release(controller);500}501502SDL_UnlockJoysticks();503504return S_OK;505}506507static HRESULT STDMETHODCALLTYPE IEventHandler_CRawGameControllerVtbl_InvokeRemoved(__FIEventHandler_1_Windows__CGaming__CInput__CRawGameController *This, IInspectable *sender, __x_ABI_CWindows_CGaming_CInput_CIRawGameController *e)508{509HRESULT hr;510__x_ABI_CWindows_CGaming_CInput_CIRawGameController *controller = NULL;511512SDL_LockJoysticks();513514// Can we get delayed calls to InvokeRemoved() after WGI_JoystickQuit()?515if (!SDL_JoysticksInitialized()) {516SDL_UnlockJoysticks();517return S_OK;518}519520hr = __x_ABI_CWindows_CGaming_CInput_CIRawGameController_QueryInterface(e, &IID___x_ABI_CWindows_CGaming_CInput_CIRawGameController, (void **)&controller);521if (SUCCEEDED(hr)) {522int i;523524for (i = 0; i < wgi.controller_count; i++) {525if (wgi.controllers[i].controller == controller) {526WindowsGamingInputControllerState *state = &wgi.controllers[i];527SDL_JoystickID joystickID = state->instance_id;528529__x_ABI_CWindows_CGaming_CInput_CIRawGameController_Release(state->controller);530531SDL_free(state->name);532533--wgi.controller_count;534if (i < wgi.controller_count) {535SDL_memmove(&wgi.controllers[i], &wgi.controllers[i + 1], (wgi.controller_count - i) * sizeof(wgi.controllers[i]));536}537538SDL_PrivateJoystickRemoved(joystickID);539break;540}541}542543__x_ABI_CWindows_CGaming_CInput_CIRawGameController_Release(controller);544}545546SDL_UnlockJoysticks();547548return S_OK;549}550551#ifdef _MSC_VER552#pragma warning(push)553#pragma warning(disable : 4028) // formal parameter 3 different from declaration, when using older buggy WGI headers554#pragma warning(disable : 4113) // formal parameter 3 different from declaration (a more specific warning added in VS 2022), when using older buggy WGI headers555#endif556557static __FIEventHandler_1_Windows__CGaming__CInput__CRawGameControllerVtbl controller_added_vtbl = {558IEventHandler_CRawGameControllerVtbl_QueryInterface,559IEventHandler_CRawGameControllerVtbl_AddRef,560IEventHandler_CRawGameControllerVtbl_Release,561IEventHandler_CRawGameControllerVtbl_InvokeAdded562};563static RawGameControllerDelegate controller_added = {564{ &controller_added_vtbl },565{ 1 }566};567568static __FIEventHandler_1_Windows__CGaming__CInput__CRawGameControllerVtbl controller_removed_vtbl = {569IEventHandler_CRawGameControllerVtbl_QueryInterface,570IEventHandler_CRawGameControllerVtbl_AddRef,571IEventHandler_CRawGameControllerVtbl_Release,572IEventHandler_CRawGameControllerVtbl_InvokeRemoved573};574static RawGameControllerDelegate controller_removed = {575{ &controller_removed_vtbl },576{ 1 }577};578579#ifdef _MSC_VER580#pragma warning(pop)581#endif582583static bool WGI_JoystickInit(void)584{585HRESULT hr;586587if (!SDL_GetHintBoolean(SDL_HINT_JOYSTICK_WGI, true)) {588return true;589}590591if (FAILED(WIN_RoInitialize())) {592return SDL_SetError("RoInitialize() failed");593}594595#define RESOLVE(x) wgi.x = (x##_t)WIN_LoadComBaseFunction(#x); if (!wgi.x) return WIN_SetError("GetProcAddress failed for " #x);596RESOLVE(CoIncrementMTAUsage);597RESOLVE(RoGetActivationFactory);598RESOLVE(WindowsCreateStringReference);599RESOLVE(WindowsDeleteString);600RESOLVE(WindowsGetStringRawBuffer);601#undef RESOLVE602603{604/* There seems to be a bug in Windows where a dependency of WGI can be unloaded from memory prior to WGI itself.605* This results in Windows_Gaming_Input!GameController::~GameController() invoking an unloaded DLL and crashing.606* As a workaround, we will keep a reference to the MTA to prevent COM from unloading DLLs later.607* See https://github.com/libsdl-org/SDL/issues/5552 for more details.608*/609static CO_MTA_USAGE_COOKIE cookie = NULL;610if (!cookie) {611hr = wgi.CoIncrementMTAUsage(&cookie);612if (FAILED(hr)) {613return WIN_SetErrorFromHRESULT("CoIncrementMTAUsage() failed", hr);614}615}616}617618WGI_LoadRawGameControllerStatics();619620if (wgi.controller_statics) {621__FIVectorView_1_Windows__CGaming__CInput__CRawGameController *controllers;622623hr = __x_ABI_CWindows_CGaming_CInput_CIRawGameControllerStatics_add_RawGameControllerAdded(wgi.controller_statics, &controller_added.iface, &wgi.controller_added_token);624if (!SUCCEEDED(hr)) {625WIN_SetErrorFromHRESULT("Windows.Gaming.Input.IRawGameControllerStatics.add_RawGameControllerAdded failed", hr);626}627628hr = __x_ABI_CWindows_CGaming_CInput_CIRawGameControllerStatics_add_RawGameControllerRemoved(wgi.controller_statics, &controller_removed.iface, &wgi.controller_removed_token);629if (!SUCCEEDED(hr)) {630WIN_SetErrorFromHRESULT("Windows.Gaming.Input.IRawGameControllerStatics.add_RawGameControllerRemoved failed", hr);631}632633hr = __x_ABI_CWindows_CGaming_CInput_CIRawGameControllerStatics_get_RawGameControllers(wgi.controller_statics, &controllers);634if (SUCCEEDED(hr)) {635unsigned i, count = 0;636637hr = __FIVectorView_1_Windows__CGaming__CInput__CRawGameController_get_Size(controllers, &count);638if (SUCCEEDED(hr)) {639for (i = 0; i < count; ++i) {640__x_ABI_CWindows_CGaming_CInput_CIRawGameController *controller = NULL;641642hr = __FIVectorView_1_Windows__CGaming__CInput__CRawGameController_GetAt(controllers, i, &controller);643if (SUCCEEDED(hr) && controller) {644IEventHandler_CRawGameControllerVtbl_InvokeAdded(&controller_added.iface, NULL, controller);645__x_ABI_CWindows_CGaming_CInput_CIRawGameController_Release(controller);646}647}648}649650__FIVectorView_1_Windows__CGaming__CInput__CRawGameController_Release(controllers);651}652}653654return true;655}656657static int WGI_JoystickGetCount(void)658{659return wgi.controller_count;660}661662static void WGI_JoystickDetect(void)663{664}665666static bool WGI_JoystickIsDevicePresent(Uint16 vendor_id, Uint16 product_id, Uint16 version, const char *name)667{668// We don't override any other drivers669return false;670}671672static const char *WGI_JoystickGetDeviceName(int device_index)673{674return wgi.controllers[device_index].name;675}676677static const char *WGI_JoystickGetDevicePath(int device_index)678{679return NULL;680}681682static int WGI_JoystickGetDeviceSteamVirtualGamepadSlot(int device_index)683{684return wgi.controllers[device_index].steam_virtual_gamepad_slot;685}686687static int WGI_JoystickGetDevicePlayerIndex(int device_index)688{689return false;690}691692static void WGI_JoystickSetDevicePlayerIndex(int device_index, int player_index)693{694}695696static SDL_GUID WGI_JoystickGetDeviceGUID(int device_index)697{698return wgi.controllers[device_index].guid;699}700701static SDL_JoystickID WGI_JoystickGetDeviceInstanceID(int device_index)702{703return wgi.controllers[device_index].instance_id;704}705706static bool WGI_JoystickOpen(SDL_Joystick *joystick, int device_index)707{708WindowsGamingInputControllerState *state = &wgi.controllers[device_index];709struct joystick_hwdata *hwdata;710boolean wireless = false;711712hwdata = (struct joystick_hwdata *)SDL_calloc(1, sizeof(*hwdata));713if (!hwdata) {714return false;715}716joystick->hwdata = hwdata;717718hwdata->controller = state->controller;719__x_ABI_CWindows_CGaming_CInput_CIRawGameController_AddRef(hwdata->controller);720__x_ABI_CWindows_CGaming_CInput_CIRawGameController_QueryInterface(hwdata->controller, &IID___x_ABI_CWindows_CGaming_CInput_CIGameController, (void **)&hwdata->game_controller);721__x_ABI_CWindows_CGaming_CInput_CIRawGameController_QueryInterface(hwdata->controller, &IID___x_ABI_CWindows_CGaming_CInput_CIGameControllerBatteryInfo, (void **)&hwdata->battery);722723if (wgi.gamepad_statics2) {724__x_ABI_CWindows_CGaming_CInput_CIGamepadStatics2_FromGameController(wgi.gamepad_statics2, hwdata->game_controller, &hwdata->gamepad);725}726727if (hwdata->game_controller) {728__x_ABI_CWindows_CGaming_CInput_CIGameController_get_IsWireless(hwdata->game_controller, &wireless);729}730731// Initialize the joystick capabilities732if (wireless) {733joystick->connection_state = SDL_JOYSTICK_CONNECTION_WIRELESS;734} else {735joystick->connection_state = SDL_JOYSTICK_CONNECTION_WIRED;736}737__x_ABI_CWindows_CGaming_CInput_CIRawGameController_get_ButtonCount(hwdata->controller, &joystick->nbuttons);738__x_ABI_CWindows_CGaming_CInput_CIRawGameController_get_AxisCount(hwdata->controller, &joystick->naxes);739__x_ABI_CWindows_CGaming_CInput_CIRawGameController_get_SwitchCount(hwdata->controller, &joystick->nhats);740741if (hwdata->gamepad) {742// FIXME: Can WGI even tell us if trigger rumble is supported?743SDL_SetBooleanProperty(SDL_GetJoystickProperties(joystick), SDL_PROP_JOYSTICK_CAP_RUMBLE_BOOLEAN, true);744SDL_SetBooleanProperty(SDL_GetJoystickProperties(joystick), SDL_PROP_JOYSTICK_CAP_TRIGGER_RUMBLE_BOOLEAN, true);745}746return true;747}748749static bool WGI_JoystickRumble(SDL_Joystick *joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble)750{751struct joystick_hwdata *hwdata = joystick->hwdata;752753if (hwdata->gamepad) {754HRESULT hr;755756// Note: reusing partially filled vibration data struct757hwdata->vibration.LeftMotor = (DOUBLE)low_frequency_rumble / SDL_MAX_UINT16;758hwdata->vibration.RightMotor = (DOUBLE)high_frequency_rumble / SDL_MAX_UINT16;759hr = __x_ABI_CWindows_CGaming_CInput_CIGamepad_put_Vibration(hwdata->gamepad, hwdata->vibration);760if (SUCCEEDED(hr)) {761return true;762} else {763return WIN_SetErrorFromHRESULT("Windows.Gaming.Input.IGamepad.put_Vibration failed", hr);764}765} else {766return SDL_Unsupported();767}768}769770static bool WGI_JoystickRumbleTriggers(SDL_Joystick *joystick, Uint16 left_rumble, Uint16 right_rumble)771{772struct joystick_hwdata *hwdata = joystick->hwdata;773774if (hwdata->gamepad) {775HRESULT hr;776777// Note: reusing partially filled vibration data struct778hwdata->vibration.LeftTrigger = (DOUBLE)left_rumble / SDL_MAX_UINT16;779hwdata->vibration.RightTrigger = (DOUBLE)right_rumble / SDL_MAX_UINT16;780hr = __x_ABI_CWindows_CGaming_CInput_CIGamepad_put_Vibration(hwdata->gamepad, hwdata->vibration);781if (SUCCEEDED(hr)) {782return true;783} else {784return WIN_SetErrorFromHRESULT("Windows.Gaming.Input.IGamepad.put_Vibration failed", hr);785}786} else {787return SDL_Unsupported();788}789}790791static bool WGI_JoystickSetLED(SDL_Joystick *joystick, Uint8 red, Uint8 green, Uint8 blue)792{793return SDL_Unsupported();794}795796static bool WGI_JoystickSendEffect(SDL_Joystick *joystick, const void *data, int size)797{798return SDL_Unsupported();799}800801static bool WGI_JoystickSetSensorsEnabled(SDL_Joystick *joystick, bool enabled)802{803return SDL_Unsupported();804}805806static Uint8 ConvertHatValue(__x_ABI_CWindows_CGaming_CInput_CGameControllerSwitchPosition value)807{808switch (value) {809case GameControllerSwitchPosition_Up:810return SDL_HAT_UP;811case GameControllerSwitchPosition_UpRight:812return SDL_HAT_RIGHTUP;813case GameControllerSwitchPosition_Right:814return SDL_HAT_RIGHT;815case GameControllerSwitchPosition_DownRight:816return SDL_HAT_RIGHTDOWN;817case GameControllerSwitchPosition_Down:818return SDL_HAT_DOWN;819case GameControllerSwitchPosition_DownLeft:820return SDL_HAT_LEFTDOWN;821case GameControllerSwitchPosition_Left:822return SDL_HAT_LEFT;823case GameControllerSwitchPosition_UpLeft:824return SDL_HAT_LEFTUP;825default:826return SDL_HAT_CENTERED;827}828}829830static void WGI_JoystickUpdate(SDL_Joystick *joystick)831{832struct joystick_hwdata *hwdata = joystick->hwdata;833HRESULT hr;834UINT32 nbuttons = SDL_min(joystick->nbuttons, SDL_MAX_UINT8);835boolean *buttons = NULL;836UINT32 nhats = SDL_min(joystick->nhats, SDL_MAX_UINT8);837__x_ABI_CWindows_CGaming_CInput_CGameControllerSwitchPosition *hats = NULL;838UINT32 naxes = SDL_min(joystick->naxes, SDL_MAX_UINT8);839DOUBLE *axes = NULL;840UINT64 timestamp;841842if (nbuttons > 0) {843buttons = SDL_stack_alloc(boolean, nbuttons);844}845if (nhats > 0) {846hats = SDL_stack_alloc(__x_ABI_CWindows_CGaming_CInput_CGameControllerSwitchPosition, nhats);847}848if (naxes > 0) {849axes = SDL_stack_alloc(DOUBLE, naxes);850}851852hr = __x_ABI_CWindows_CGaming_CInput_CIRawGameController_GetCurrentReading(hwdata->controller, nbuttons, buttons, nhats, hats, naxes, axes, ×tamp);853if (SUCCEEDED(hr) && (!timestamp || timestamp != hwdata->timestamp)) {854UINT32 i;855bool all_zero = false;856857hwdata->timestamp = timestamp;858859// The axes are all zero when the application loses focus860if (naxes > 0) {861all_zero = true;862for (i = 0; i < naxes; ++i) {863if (axes[i] != 0.0f) {864all_zero = false;865break;866}867}868}869if (all_zero) {870SDL_PrivateJoystickForceRecentering(joystick);871} else {872// FIXME: What units are the timestamp we get from GetCurrentReading()?873timestamp = SDL_GetTicksNS();874for (i = 0; i < nbuttons; ++i) {875SDL_SendJoystickButton(timestamp, joystick, (Uint8)i, buttons[i]);876}877for (i = 0; i < nhats; ++i) {878SDL_SendJoystickHat(timestamp, joystick, (Uint8)i, ConvertHatValue(hats[i]));879}880for (i = 0; i < naxes; ++i) {881SDL_SendJoystickAxis(timestamp, joystick, (Uint8)i, (Sint16)((int)(axes[i] * 65535) - 32768));882}883}884}885886SDL_stack_free(buttons);887SDL_stack_free(hats);888SDL_stack_free(axes);889890if (hwdata->battery) {891__x_ABI_CWindows_CDevices_CPower_CIBatteryReport *report = NULL;892893hr = __x_ABI_CWindows_CGaming_CInput_CIGameControllerBatteryInfo_TryGetBatteryReport(hwdata->battery, &report);894if (SUCCEEDED(hr) && report) {895SDL_PowerState state = SDL_POWERSTATE_UNKNOWN;896int percent = 0;897__x_ABI_CWindows_CSystem_CPower_CBatteryStatus status;898int full_capacity = 0, curr_capacity = 0;899__FIReference_1_int *full_capacityP, *curr_capacityP;900901hr = __x_ABI_CWindows_CDevices_CPower_CIBatteryReport_get_Status(report, &status);902if (SUCCEEDED(hr)) {903switch (status) {904case BatteryStatus_NotPresent:905state = SDL_POWERSTATE_NO_BATTERY;906break;907case BatteryStatus_Discharging:908state = SDL_POWERSTATE_ON_BATTERY;909break;910case BatteryStatus_Idle:911state = SDL_POWERSTATE_CHARGED;912break;913case BatteryStatus_Charging:914state = SDL_POWERSTATE_CHARGING;915break;916default:917state = SDL_POWERSTATE_UNKNOWN;918break;919}920}921922hr = __x_ABI_CWindows_CDevices_CPower_CIBatteryReport_get_FullChargeCapacityInMilliwattHours(report, &full_capacityP);923if (SUCCEEDED(hr)) {924__FIReference_1_int_get_Value(full_capacityP, &full_capacity);925__FIReference_1_int_Release(full_capacityP);926}927928hr = __x_ABI_CWindows_CDevices_CPower_CIBatteryReport_get_RemainingCapacityInMilliwattHours(report, &curr_capacityP);929if (SUCCEEDED(hr)) {930__FIReference_1_int_get_Value(curr_capacityP, &curr_capacity);931__FIReference_1_int_Release(curr_capacityP);932}933934if (full_capacity > 0) {935percent = (int)SDL_roundf(((float)curr_capacity / full_capacity) * 100.0f);936}937938SDL_SendJoystickPowerInfo(joystick, state, percent);939940__x_ABI_CWindows_CDevices_CPower_CIBatteryReport_Release(report);941}942}943}944945static void WGI_JoystickClose(SDL_Joystick *joystick)946{947struct joystick_hwdata *hwdata = joystick->hwdata;948949if (hwdata) {950if (hwdata->controller) {951__x_ABI_CWindows_CGaming_CInput_CIRawGameController_Release(hwdata->controller);952}953if (hwdata->game_controller) {954__x_ABI_CWindows_CGaming_CInput_CIGameController_Release(hwdata->game_controller);955}956if (hwdata->battery) {957__x_ABI_CWindows_CGaming_CInput_CIGameControllerBatteryInfo_Release(hwdata->battery);958}959if (hwdata->gamepad) {960__x_ABI_CWindows_CGaming_CInput_CIGamepad_Release(hwdata->gamepad);961}962SDL_free(hwdata);963}964joystick->hwdata = NULL;965}966967static void WGI_JoystickQuit(void)968{969if (wgi.controller_statics) {970while (wgi.controller_count > 0) {971IEventHandler_CRawGameControllerVtbl_InvokeRemoved(&controller_removed.iface, NULL, wgi.controllers[wgi.controller_count - 1].controller);972}973if (wgi.controllers) {974SDL_free(wgi.controllers);975}976977if (wgi.arcade_stick_statics) {978__x_ABI_CWindows_CGaming_CInput_CIArcadeStickStatics_Release(wgi.arcade_stick_statics);979}980if (wgi.arcade_stick_statics2) {981__x_ABI_CWindows_CGaming_CInput_CIArcadeStickStatics2_Release(wgi.arcade_stick_statics2);982}983if (wgi.flight_stick_statics) {984__x_ABI_CWindows_CGaming_CInput_CIFlightStickStatics_Release(wgi.flight_stick_statics);985}986if (wgi.gamepad_statics) {987__x_ABI_CWindows_CGaming_CInput_CIGamepadStatics_Release(wgi.gamepad_statics);988}989if (wgi.gamepad_statics2) {990__x_ABI_CWindows_CGaming_CInput_CIGamepadStatics2_Release(wgi.gamepad_statics2);991}992if (wgi.racing_wheel_statics) {993__x_ABI_CWindows_CGaming_CInput_CIRacingWheelStatics_Release(wgi.racing_wheel_statics);994}995if (wgi.racing_wheel_statics2) {996__x_ABI_CWindows_CGaming_CInput_CIRacingWheelStatics2_Release(wgi.racing_wheel_statics2);997}998999__x_ABI_CWindows_CGaming_CInput_CIRawGameControllerStatics_remove_RawGameControllerAdded(wgi.controller_statics, wgi.controller_added_token);1000__x_ABI_CWindows_CGaming_CInput_CIRawGameControllerStatics_remove_RawGameControllerRemoved(wgi.controller_statics, wgi.controller_removed_token);1001__x_ABI_CWindows_CGaming_CInput_CIRawGameControllerStatics_Release(wgi.controller_statics);1002}10031004WIN_RoUninitialize();10051006SDL_zero(wgi);1007}10081009static bool WGI_JoystickGetGamepadMapping(int device_index, SDL_GamepadMapping *out)1010{1011return false;1012}10131014SDL_JoystickDriver SDL_WGI_JoystickDriver = {1015WGI_JoystickInit,1016WGI_JoystickGetCount,1017WGI_JoystickDetect,1018WGI_JoystickIsDevicePresent,1019WGI_JoystickGetDeviceName,1020WGI_JoystickGetDevicePath,1021WGI_JoystickGetDeviceSteamVirtualGamepadSlot,1022WGI_JoystickGetDevicePlayerIndex,1023WGI_JoystickSetDevicePlayerIndex,1024WGI_JoystickGetDeviceGUID,1025WGI_JoystickGetDeviceInstanceID,1026WGI_JoystickOpen,1027WGI_JoystickRumble,1028WGI_JoystickRumbleTriggers,1029WGI_JoystickSetLED,1030WGI_JoystickSendEffect,1031WGI_JoystickSetSensorsEnabled,1032WGI_JoystickUpdate,1033WGI_JoystickClose,1034WGI_JoystickQuit,1035WGI_JoystickGetGamepadMapping1036};10371038#endif // SDL_JOYSTICK_WGI103910401041