Atlas - SDL_windows_gaming_input.c

Home / ext / SDL / src / joystick / windows Lines: 1 | Size: 43594 bytes [Download] [Show on GitHub] [Search similar files] [Raw] [Raw (proxy)]
[FILE BEGIN]
1/* 2 Simple DirectMedia Layer 3 Copyright (C) 1997-2025 Sam Lantinga <[email protected]> 4 5 This software is provided 'as-is', without any express or implied 6 warranty. In no event will the authors be held liable for any damages 7 arising from the use of this software. 8 9 Permission is granted to anyone to use this software for any purpose, 10 including commercial applications, and to alter it and redistribute it 11 freely, subject to the following restrictions: 12 13 1. The origin of this software must not be misrepresented; you must not 14 claim that you wrote the original software. If you use this software 15 in a product, an acknowledgment in the product documentation would be 16 appreciated but is not required. 17 2. Altered source versions must be plainly marked as such, and must not be 18 misrepresented as being the original software. 19 3. This notice may not be removed or altered from any source distribution. 20*/ 21#include "SDL_internal.h" 22 23#ifdef SDL_JOYSTICK_WGI 24 25#include "../SDL_sysjoystick.h" 26#include "../hidapi/SDL_hidapijoystick_c.h" 27#include "SDL_rawinputjoystick_c.h" 28 29#include "../../core/windows/SDL_windows.h" 30#define COBJMACROS 31#include "windows.gaming.input.h" 32#include <cfgmgr32.h> 33#include <objidlbase.h> 34#include <roapi.h> 35#include <initguid.h> 36 37#ifdef ____FIReference_1_INT32_INTERFACE_DEFINED__ 38// MinGW-64 uses __FIReference_1_INT32 instead of Microsoft's __FIReference_1_int 39#define __FIReference_1_int __FIReference_1_INT32 40#define __FIReference_1_int_get_Value __FIReference_1_INT32_get_Value 41#define __FIReference_1_int_Release __FIReference_1_INT32_Release 42#endif 43 44struct joystick_hwdata 45{ 46 __x_ABI_CWindows_CGaming_CInput_CIRawGameController *controller; 47 __x_ABI_CWindows_CGaming_CInput_CIGameController *game_controller; 48 __x_ABI_CWindows_CGaming_CInput_CIGameControllerBatteryInfo *battery; 49 __x_ABI_CWindows_CGaming_CInput_CIGamepad *gamepad; 50 __x_ABI_CWindows_CGaming_CInput_CGamepadVibration vibration; 51 UINT64 timestamp; 52}; 53 54typedef struct WindowsGamingInputControllerState 55{ 56 SDL_JoystickID instance_id; 57 __x_ABI_CWindows_CGaming_CInput_CIRawGameController *controller; 58 char *name; 59 SDL_GUID guid; 60 SDL_JoystickType type; 61 int steam_virtual_gamepad_slot; 62} WindowsGamingInputControllerState; 63 64typedef HRESULT(WINAPI *CoIncrementMTAUsage_t)(CO_MTA_USAGE_COOKIE *pCookie); 65typedef HRESULT(WINAPI *RoGetActivationFactory_t)(HSTRING activatableClassId, REFIID iid, void **factory); 66typedef HRESULT(WINAPI *WindowsCreateStringReference_t)(PCWSTR sourceString, UINT32 length, HSTRING_HEADER *hstringHeader, HSTRING *string); 67typedef HRESULT(WINAPI *WindowsDeleteString_t)(HSTRING string); 68typedef PCWSTR(WINAPI *WindowsGetStringRawBuffer_t)(HSTRING string, UINT32 *length); 69 70static struct 71{ 72 bool ro_initialized; 73 CoIncrementMTAUsage_t CoIncrementMTAUsage; 74 RoGetActivationFactory_t RoGetActivationFactory; 75 WindowsCreateStringReference_t WindowsCreateStringReference; 76 WindowsDeleteString_t WindowsDeleteString; 77 WindowsGetStringRawBuffer_t WindowsGetStringRawBuffer; 78 __x_ABI_CWindows_CGaming_CInput_CIRawGameControllerStatics *controller_statics; 79 __x_ABI_CWindows_CGaming_CInput_CIArcadeStickStatics *arcade_stick_statics; 80 __x_ABI_CWindows_CGaming_CInput_CIArcadeStickStatics2 *arcade_stick_statics2; 81 __x_ABI_CWindows_CGaming_CInput_CIFlightStickStatics *flight_stick_statics; 82 __x_ABI_CWindows_CGaming_CInput_CIGamepadStatics *gamepad_statics; 83 __x_ABI_CWindows_CGaming_CInput_CIGamepadStatics2 *gamepad_statics2; 84 __x_ABI_CWindows_CGaming_CInput_CIRacingWheelStatics *racing_wheel_statics; 85 __x_ABI_CWindows_CGaming_CInput_CIRacingWheelStatics2 *racing_wheel_statics2; 86 EventRegistrationToken controller_added_token; 87 EventRegistrationToken controller_removed_token; 88 int controller_count; 89 WindowsGamingInputControllerState *controllers; 90} wgi; 91 92// WinRT headers in official Windows SDK contain only declarations, and we have to define these GUIDs ourselves. 93// https://stackoverflow.com/a/55605485/1795050 94DEFINE_GUID(IID___FIEventHandler_1_Windows__CGaming__CInput__CRawGameController, 0x00621c22, 0x42e8, 0x529f, 0x92, 0x70, 0x83, 0x6b, 0x32, 0x93, 0x1d, 0x72); 95DEFINE_GUID(IID___x_ABI_CWindows_CGaming_CInput_CIArcadeStickStatics, 0x5c37b8c8, 0x37b1, 0x4ad8, 0x94, 0x58, 0x20, 0x0f, 0x1a, 0x30, 0x01, 0x8e); 96DEFINE_GUID(IID___x_ABI_CWindows_CGaming_CInput_CIArcadeStickStatics2, 0x52b5d744, 0xbb86, 0x445a, 0xb5, 0x9c, 0x59, 0x6f, 0x0e, 0x2a, 0x49, 0xdf); 97DEFINE_GUID(IID___x_ABI_CWindows_CGaming_CInput_CIFlightStickStatics, 0x5514924a, 0xfecc, 0x435e, 0x83, 0xdc, 0x5c, 0xec, 0x8a, 0x18, 0xa5, 0x20); 98DEFINE_GUID(IID___x_ABI_CWindows_CGaming_CInput_CIGameController, 0x1baf6522, 0x5f64, 0x42c5, 0x82, 0x67, 0xb9, 0xfe, 0x22, 0x15, 0xbf, 0xbd); 99DEFINE_GUID(IID___x_ABI_CWindows_CGaming_CInput_CIGameControllerBatteryInfo, 0xdcecc681, 0x3963, 0x4da6, 0x95, 0x5d, 0x55, 0x3f, 0x3b, 0x6f, 0x61, 0x61); 100DEFINE_GUID(IID___x_ABI_CWindows_CGaming_CInput_CIGamepadStatics, 0x8bbce529, 0xd49c, 0x39e9, 0x95, 0x60, 0xe4, 0x7d, 0xde, 0x96, 0xb7, 0xc8); 101DEFINE_GUID(IID___x_ABI_CWindows_CGaming_CInput_CIGamepadStatics2, 0x42676dc5, 0x0856, 0x47c4, 0x92, 0x13, 0xb3, 0x95, 0x50, 0x4c, 0x3a, 0x3c); 102DEFINE_GUID(IID___x_ABI_CWindows_CGaming_CInput_CIRacingWheelStatics, 0x3ac12cd5, 0x581b, 0x4936, 0x9f, 0x94, 0x69, 0xf1, 0xe6, 0x51, 0x4c, 0x7d); 103DEFINE_GUID(IID___x_ABI_CWindows_CGaming_CInput_CIRacingWheelStatics2, 0xe666bcaa, 0xedfd, 0x4323, 0xa9, 0xf6, 0x3c, 0x38, 0x40, 0x48, 0xd1, 0xed); 104DEFINE_GUID(IID___x_ABI_CWindows_CGaming_CInput_CIRawGameController, 0x7cad6d91, 0xa7e1, 0x4f71, 0x9a, 0x78, 0x33, 0xe9, 0xc5, 0xdf, 0xea, 0x62); 105DEFINE_GUID(IID___x_ABI_CWindows_CGaming_CInput_CIRawGameController2, 0x43c0c035, 0xbb73, 0x4756, 0xa7, 0x87, 0x3e, 0xd6, 0xbe, 0xa6, 0x17, 0xbd); 106DEFINE_GUID(IID___x_ABI_CWindows_CGaming_CInput_CIRawGameControllerStatics, 0xeb8d0792, 0xe95a, 0x4b19, 0xaf, 0xc7, 0x0a, 0x59, 0xf8, 0xbf, 0x75, 0x9e); 107 108extern bool SDL_XINPUT_Enabled(void); 109 110 111static bool SDL_IsXInputDevice(Uint16 vendor, Uint16 product, const char *name) 112{ 113#if defined(SDL_JOYSTICK_XINPUT) || defined(SDL_JOYSTICK_RAWINPUT) 114 PRAWINPUTDEVICELIST raw_devices = NULL; 115 UINT i, raw_device_count = 0; 116 LONG vidpid = MAKELONG(vendor, product); 117 118 // XInput and RawInput backends will pick up XInput-compatible devices 119 if (!SDL_XINPUT_Enabled() 120#ifdef SDL_JOYSTICK_RAWINPUT 121 && !RAWINPUT_IsEnabled() 122#endif 123 ) { 124 return false; 125 } 126 127 // Sometimes we'll get a Windows.Gaming.Input callback before the raw input device is even in the list, 128 // so try to do some checks up front to catch these cases. 129 if (SDL_IsJoystickXboxOne(vendor, product) || 130 (name && SDL_strncmp(name, "Xbox ", 5) == 0)) { 131 return true; 132 } 133 134 // Go through RAWINPUT (WinXP and later) to find HID devices. 135 if ((GetRawInputDeviceList(NULL, &raw_device_count, sizeof(RAWINPUTDEVICELIST)) == -1) || (!raw_device_count)) { 136 return false; // oh well. 137 } 138 139 raw_devices = (PRAWINPUTDEVICELIST)SDL_malloc(sizeof(RAWINPUTDEVICELIST) * raw_device_count); 140 if (!raw_devices) { 141 return false; 142 } 143 144 raw_device_count = GetRawInputDeviceList(raw_devices, &raw_device_count, sizeof(RAWINPUTDEVICELIST)); 145 if (raw_device_count == (UINT)-1) { 146 SDL_free(raw_devices); 147 raw_devices = NULL; 148 return false; // oh well. 149 } 150 151 for (i = 0; i < raw_device_count; i++) { 152 RID_DEVICE_INFO rdi; 153 char devName[MAX_PATH] = { 0 }; 154 UINT rdiSize = sizeof(rdi); 155 UINT nameSize = SDL_arraysize(devName); 156 DEVINST devNode; 157 char devVidPidString[32]; 158 int j; 159 160 rdi.cbSize = sizeof(rdi); 161 162 if ((raw_devices[i].dwType != RIM_TYPEHID) || 163 (GetRawInputDeviceInfoA(raw_devices[i].hDevice, RIDI_DEVICEINFO, &rdi, &rdiSize) == ((UINT)-1)) || 164 (GetRawInputDeviceInfoA(raw_devices[i].hDevice, RIDI_DEVICENAME, devName, &nameSize) == ((UINT)-1)) || 165 (SDL_strstr(devName, "IG_") == NULL)) { 166 // Skip non-XInput devices 167 continue; 168 } 169 170 // First check for a simple VID/PID match. This will work for Xbox 360 controllers. 171 if (MAKELONG(rdi.hid.dwVendorId, rdi.hid.dwProductId) == vidpid) { 172 SDL_free(raw_devices); 173 return true; 174 } 175 176 /* For Xbox One controllers, Microsoft doesn't propagate the VID/PID down to the HID stack. 177 * We'll have to walk the device tree upwards searching for a match for our VID/PID. */ 178 179 // Make sure the device interface string is something we know how to parse 180 // Example: \\?\HID#VID_045E&PID_02FF&IG_00#9&2c203035&2&0000#{4d1e55b2-f16f-11cf-88cb-001111000030} 181 if ((SDL_strstr(devName, "\\\\?\\") != devName) || (SDL_strstr(devName, "#{") == NULL)) { 182 continue; 183 } 184 185 // Unescape the backslashes in the string and terminate before the GUID portion 186 for (j = 0; devName[j] != '\0'; j++) { 187 if (devName[j] == '#') { 188 if (devName[j + 1] == '{') { 189 devName[j] = '\0'; 190 break; 191 } else { 192 devName[j] = '\\'; 193 } 194 } 195 } 196 197 /* We'll be left with a string like this: \\?\HID\VID_045E&PID_02FF&IG_00\9&2c203035&2&0000 198 * Simply skip the \\?\ prefix and we'll have a properly formed device instance ID */ 199 if (CM_Locate_DevNodeA(&devNode, &devName[4], CM_LOCATE_DEVNODE_NORMAL) != CR_SUCCESS) { 200 continue; 201 } 202 203 (void)SDL_snprintf(devVidPidString, sizeof(devVidPidString), "VID_%04X&PID_%04X", vendor, product); 204 205 while (CM_Get_Parent(&devNode, devNode, 0) == CR_SUCCESS) { 206 char deviceId[MAX_DEVICE_ID_LEN]; 207 208 if ((CM_Get_Device_IDA(devNode, deviceId, SDL_arraysize(deviceId), 0) == CR_SUCCESS) && 209 (SDL_strstr(deviceId, devVidPidString) != NULL)) { 210 // The VID/PID matched a parent device 211 SDL_free(raw_devices); 212 return true; 213 } 214 } 215 } 216 217 SDL_free(raw_devices); 218#endif // SDL_JOYSTICK_XINPUT || SDL_JOYSTICK_RAWINPUT 219 220 return false; 221} 222 223static void WGI_LoadRawGameControllerStatics(void) 224{ 225 HRESULT hr; 226 HSTRING_HEADER class_name_header; 227 HSTRING class_name; 228 229 hr = wgi.WindowsCreateStringReference(RuntimeClass_Windows_Gaming_Input_RawGameController, (UINT32)SDL_wcslen(RuntimeClass_Windows_Gaming_Input_RawGameController), &class_name_header, &class_name); 230 if (SUCCEEDED(hr)) { 231 hr = wgi.RoGetActivationFactory(class_name, &IID___x_ABI_CWindows_CGaming_CInput_CIRawGameControllerStatics, (void **)&wgi.controller_statics); 232 if (!SUCCEEDED(hr)) { 233 WIN_SetErrorFromHRESULT("Couldn't find Windows.Gaming.Input.IRawGameControllerStatics", hr); 234 } 235 } 236} 237 238static void WGI_LoadOtherControllerStatics(void) 239{ 240 HRESULT hr; 241 HSTRING_HEADER class_name_header; 242 HSTRING class_name; 243 244 if (!wgi.arcade_stick_statics) { 245 hr = wgi.WindowsCreateStringReference(RuntimeClass_Windows_Gaming_Input_ArcadeStick, (UINT32)SDL_wcslen(RuntimeClass_Windows_Gaming_Input_ArcadeStick), &class_name_header, &class_name); 246 if (SUCCEEDED(hr)) { 247 hr = wgi.RoGetActivationFactory(class_name, &IID___x_ABI_CWindows_CGaming_CInput_CIArcadeStickStatics, (void **)&wgi.arcade_stick_statics); 248 if (SUCCEEDED(hr)) { 249 __x_ABI_CWindows_CGaming_CInput_CIArcadeStickStatics_QueryInterface(wgi.arcade_stick_statics, &IID___x_ABI_CWindows_CGaming_CInput_CIArcadeStickStatics2, (void **)&wgi.arcade_stick_statics2); 250 } else { 251 WIN_SetErrorFromHRESULT("Couldn't find Windows.Gaming.Input.IArcadeStickStatics", hr); 252 } 253 } 254 } 255 256 if (!wgi.flight_stick_statics) { 257 hr = wgi.WindowsCreateStringReference(RuntimeClass_Windows_Gaming_Input_FlightStick, (UINT32)SDL_wcslen(RuntimeClass_Windows_Gaming_Input_FlightStick), &class_name_header, &class_name); 258 if (SUCCEEDED(hr)) { 259 hr = wgi.RoGetActivationFactory(class_name, &IID___x_ABI_CWindows_CGaming_CInput_CIFlightStickStatics, (void **)&wgi.flight_stick_statics); 260 if (!SUCCEEDED(hr)) { 261 WIN_SetErrorFromHRESULT("Couldn't find Windows.Gaming.Input.IFlightStickStatics", hr); 262 } 263 } 264 } 265 266 if (!wgi.gamepad_statics) { 267 hr = wgi.WindowsCreateStringReference(RuntimeClass_Windows_Gaming_Input_Gamepad, (UINT32)SDL_wcslen(RuntimeClass_Windows_Gaming_Input_Gamepad), &class_name_header, &class_name); 268 if (SUCCEEDED(hr)) { 269 hr = wgi.RoGetActivationFactory(class_name, &IID___x_ABI_CWindows_CGaming_CInput_CIGamepadStatics, (void **)&wgi.gamepad_statics); 270 if (SUCCEEDED(hr)) { 271 __x_ABI_CWindows_CGaming_CInput_CIGamepadStatics_QueryInterface(wgi.gamepad_statics, &IID___x_ABI_CWindows_CGaming_CInput_CIGamepadStatics2, (void **)&wgi.gamepad_statics2); 272 } else { 273 WIN_SetErrorFromHRESULT("Couldn't find Windows.Gaming.Input.IGamepadStatics", hr); 274 } 275 } 276 } 277 278 if (!wgi.racing_wheel_statics) { 279 hr = wgi.WindowsCreateStringReference(RuntimeClass_Windows_Gaming_Input_RacingWheel, (UINT32)SDL_wcslen(RuntimeClass_Windows_Gaming_Input_RacingWheel), &class_name_header, &class_name); 280 if (SUCCEEDED(hr)) { 281 hr = wgi.RoGetActivationFactory(class_name, &IID___x_ABI_CWindows_CGaming_CInput_CIRacingWheelStatics, (void **)&wgi.racing_wheel_statics); 282 if (SUCCEEDED(hr)) { 283 __x_ABI_CWindows_CGaming_CInput_CIRacingWheelStatics_QueryInterface(wgi.racing_wheel_statics, &IID___x_ABI_CWindows_CGaming_CInput_CIRacingWheelStatics2, (void **)&wgi.racing_wheel_statics2); 284 } else { 285 WIN_SetErrorFromHRESULT("Couldn't find Windows.Gaming.Input.IRacingWheelStatics", hr); 286 } 287 } 288 } 289} 290 291static SDL_JoystickType GetGameControllerType(__x_ABI_CWindows_CGaming_CInput_CIGameController *game_controller) 292{ 293 __x_ABI_CWindows_CGaming_CInput_CIArcadeStick *arcade_stick = NULL; 294 __x_ABI_CWindows_CGaming_CInput_CIFlightStick *flight_stick = NULL; 295 __x_ABI_CWindows_CGaming_CInput_CIGamepad *gamepad = NULL; 296 __x_ABI_CWindows_CGaming_CInput_CIRacingWheel *racing_wheel = NULL; 297 298 /* Wait to initialize these interfaces until we need them. 299 * Initializing the gamepad interface will switch Bluetooth PS4 controllers into enhanced mode, breaking DirectInput 300 */ 301 WGI_LoadOtherControllerStatics(); 302 303 if (wgi.gamepad_statics2 && SUCCEEDED(__x_ABI_CWindows_CGaming_CInput_CIGamepadStatics2_FromGameController(wgi.gamepad_statics2, game_controller, &gamepad)) && gamepad) { 304 __x_ABI_CWindows_CGaming_CInput_CIGamepad_Release(gamepad); 305 return SDL_JOYSTICK_TYPE_GAMEPAD; 306 } 307 308 if (wgi.arcade_stick_statics2 && SUCCEEDED(__x_ABI_CWindows_CGaming_CInput_CIArcadeStickStatics2_FromGameController(wgi.arcade_stick_statics2, game_controller, &arcade_stick)) && arcade_stick) { 309 __x_ABI_CWindows_CGaming_CInput_CIArcadeStick_Release(arcade_stick); 310 return SDL_JOYSTICK_TYPE_ARCADE_STICK; 311 } 312 313 if (wgi.flight_stick_statics && SUCCEEDED(__x_ABI_CWindows_CGaming_CInput_CIFlightStickStatics_FromGameController(wgi.flight_stick_statics, game_controller, &flight_stick)) && flight_stick) { 314 __x_ABI_CWindows_CGaming_CInput_CIFlightStick_Release(flight_stick); 315 return SDL_JOYSTICK_TYPE_FLIGHT_STICK; 316 } 317 318 if (wgi.racing_wheel_statics2 && SUCCEEDED(__x_ABI_CWindows_CGaming_CInput_CIRacingWheelStatics2_FromGameController(wgi.racing_wheel_statics2, game_controller, &racing_wheel)) && racing_wheel) { 319 __x_ABI_CWindows_CGaming_CInput_CIRacingWheel_Release(racing_wheel); 320 return SDL_JOYSTICK_TYPE_WHEEL; 321 } 322 323 return SDL_JOYSTICK_TYPE_UNKNOWN; 324} 325 326typedef struct RawGameControllerDelegate 327{ 328 __FIEventHandler_1_Windows__CGaming__CInput__CRawGameController iface; 329 SDL_AtomicInt refcount; 330} RawGameControllerDelegate; 331 332static HRESULT STDMETHODCALLTYPE IEventHandler_CRawGameControllerVtbl_QueryInterface(__FIEventHandler_1_Windows__CGaming__CInput__CRawGameController *This, REFIID riid, void **ppvObject) 333{ 334 if (!ppvObject) { 335 return E_INVALIDARG; 336 } 337 338 *ppvObject = NULL; 339 if (WIN_IsEqualIID(riid, &IID_IUnknown) || WIN_IsEqualIID(riid, &IID_IAgileObject) || WIN_IsEqualIID(riid, &IID___FIEventHandler_1_Windows__CGaming__CInput__CRawGameController)) { 340 *ppvObject = This; 341 __FIEventHandler_1_Windows__CGaming__CInput__CRawGameController_AddRef(This); 342 return S_OK; 343 } else if (WIN_IsEqualIID(riid, &IID_IMarshal)) { 344 // This seems complicated. Let's hope it doesn't happen. 345 return E_OUTOFMEMORY; 346 } else { 347 return E_NOINTERFACE; 348 } 349} 350 351static ULONG STDMETHODCALLTYPE IEventHandler_CRawGameControllerVtbl_AddRef(__FIEventHandler_1_Windows__CGaming__CInput__CRawGameController *This) 352{ 353 RawGameControllerDelegate *self = (RawGameControllerDelegate *)This; 354 return SDL_AddAtomicInt(&self->refcount, 1) + 1UL; 355} 356 357static ULONG STDMETHODCALLTYPE IEventHandler_CRawGameControllerVtbl_Release(__FIEventHandler_1_Windows__CGaming__CInput__CRawGameController *This) 358{ 359 RawGameControllerDelegate *self = (RawGameControllerDelegate *)This; 360 int rc = SDL_AddAtomicInt(&self->refcount, -1) - 1; 361 // Should never free the static delegate objects 362 SDL_assert(rc > 0); 363 return rc; 364} 365 366static int GetSteamVirtualGamepadSlot(__x_ABI_CWindows_CGaming_CInput_CIRawGameController *controller, Uint16 vendor_id, Uint16 product_id) 367{ 368 int slot = -1; 369 370 if (vendor_id == USB_VENDOR_VALVE && 371 product_id == USB_PRODUCT_STEAM_VIRTUAL_GAMEPAD) { 372 __x_ABI_CWindows_CGaming_CInput_CIRawGameController2 *controller2 = NULL; 373 HRESULT hr = __x_ABI_CWindows_CGaming_CInput_CIRawGameController_QueryInterface(controller, &IID___x_ABI_CWindows_CGaming_CInput_CIRawGameController2, (void **)&controller2); 374 if (SUCCEEDED(hr)) { 375 HSTRING hString; 376 hr = __x_ABI_CWindows_CGaming_CInput_CIRawGameController2_get_NonRoamableId(controller2, &hString); 377 if (SUCCEEDED(hr)) { 378 PCWSTR string = wgi.WindowsGetStringRawBuffer(hString, NULL); 379 if (string) { 380 char *id = WIN_StringToUTF8W(string); 381 if (id) { 382 (void)SDL_sscanf(id, "{wgi/nrid/:steam-%*X&%*X&%*X#%d#%*u}", &slot); 383 SDL_free(id); 384 } 385 } 386 wgi.WindowsDeleteString(hString); 387 } 388 __x_ABI_CWindows_CGaming_CInput_CIRawGameController2_Release(controller2); 389 } 390 } 391 return slot; 392} 393 394static HRESULT STDMETHODCALLTYPE IEventHandler_CRawGameControllerVtbl_InvokeAdded(__FIEventHandler_1_Windows__CGaming__CInput__CRawGameController *This, IInspectable *sender, __x_ABI_CWindows_CGaming_CInput_CIRawGameController *e) 395{ 396 HRESULT hr; 397 __x_ABI_CWindows_CGaming_CInput_CIRawGameController *controller = NULL; 398 399 SDL_LockJoysticks(); 400 401 // We can get delayed calls to InvokeAdded() after WGI_JoystickQuit() 402 if (SDL_JoysticksQuitting() || !SDL_JoysticksInitialized()) { 403 SDL_UnlockJoysticks(); 404 return S_OK; 405 } 406 407 hr = __x_ABI_CWindows_CGaming_CInput_CIRawGameController_QueryInterface(e, &IID___x_ABI_CWindows_CGaming_CInput_CIRawGameController, (void **)&controller); 408 if (SUCCEEDED(hr)) { 409 char *name = NULL; 410 Uint16 bus = SDL_HARDWARE_BUS_USB; 411 Uint16 vendor = 0; 412 Uint16 product = 0; 413 Uint16 version = 0; 414 SDL_JoystickType type = SDL_JOYSTICK_TYPE_UNKNOWN; 415 __x_ABI_CWindows_CGaming_CInput_CIRawGameController2 *controller2 = NULL; 416 __x_ABI_CWindows_CGaming_CInput_CIGameController *game_controller = NULL; 417 bool ignore_joystick = false; 418 419 __x_ABI_CWindows_CGaming_CInput_CIRawGameController_get_HardwareVendorId(controller, &vendor); 420 __x_ABI_CWindows_CGaming_CInput_CIRawGameController_get_HardwareProductId(controller, &product); 421 422 hr = __x_ABI_CWindows_CGaming_CInput_CIRawGameController_QueryInterface(controller, &IID___x_ABI_CWindows_CGaming_CInput_CIGameController, (void **)&game_controller); 423 if (SUCCEEDED(hr)) { 424 boolean wireless = 0; 425 hr = __x_ABI_CWindows_CGaming_CInput_CIGameController_get_IsWireless(game_controller, &wireless); 426 if (SUCCEEDED(hr) && wireless) { 427 bus = SDL_HARDWARE_BUS_BLUETOOTH; 428 429 // Fixup for Wireless Xbox 360 Controller 430 if (product == 0) { 431 vendor = USB_VENDOR_MICROSOFT; 432 product = USB_PRODUCT_XBOX360_XUSB_CONTROLLER; 433 } 434 } 435 436 __x_ABI_CWindows_CGaming_CInput_CIGameController_Release(game_controller); 437 } 438 439 hr = __x_ABI_CWindows_CGaming_CInput_CIRawGameController_QueryInterface(controller, &IID___x_ABI_CWindows_CGaming_CInput_CIRawGameController2, (void **)&controller2); 440 if (SUCCEEDED(hr)) { 441 HSTRING hString; 442 hr = __x_ABI_CWindows_CGaming_CInput_CIRawGameController2_get_DisplayName(controller2, &hString); 443 if (SUCCEEDED(hr)) { 444 PCWSTR string = wgi.WindowsGetStringRawBuffer(hString, NULL); 445 if (string) { 446 name = WIN_StringToUTF8W(string); 447 } 448 wgi.WindowsDeleteString(hString); 449 } 450 __x_ABI_CWindows_CGaming_CInput_CIRawGameController2_Release(controller2); 451 } 452 if (!name) { 453 name = SDL_strdup(""); 454 } 455 456 if (!ignore_joystick && SDL_ShouldIgnoreJoystick(vendor, product, version, name)) { 457 ignore_joystick = true; 458 } 459 460 if (!ignore_joystick && SDL_JoystickHandledByAnotherDriver(&SDL_WGI_JoystickDriver, vendor, product, version, name)) { 461 ignore_joystick = true; 462 } 463 464 if (!ignore_joystick && SDL_IsXInputDevice(vendor, product, name)) { 465 // This hasn't been detected by the RAWINPUT driver yet, but it will be picked up later. 466 ignore_joystick = true; 467 } 468 469 if (!ignore_joystick) { 470 // New device, add it 471 WindowsGamingInputControllerState *controllers = SDL_realloc(wgi.controllers, sizeof(wgi.controllers[0]) * (wgi.controller_count + 1)); 472 if (controllers) { 473 WindowsGamingInputControllerState *state = &controllers[wgi.controller_count]; 474 SDL_JoystickID joystickID = SDL_GetNextObjectID(); 475 476 if (game_controller) { 477 type = GetGameControllerType(game_controller); 478 } 479 480 SDL_zerop(state); 481 state->instance_id = joystickID; 482 state->controller = controller; 483 state->name = name; 484 state->guid = SDL_CreateJoystickGUID(bus, vendor, product, version, NULL, name, 'w', (Uint8)type); 485 state->type = type; 486 state->steam_virtual_gamepad_slot = GetSteamVirtualGamepadSlot(controller, vendor, product); 487 488 __x_ABI_CWindows_CGaming_CInput_CIRawGameController_AddRef(controller); 489 490 ++wgi.controller_count; 491 wgi.controllers = controllers; 492 493 SDL_PrivateJoystickAdded(joystickID); 494 } else { 495 SDL_free(name); 496 } 497 } else { 498 SDL_free(name); 499 } 500 501 __x_ABI_CWindows_CGaming_CInput_CIRawGameController_Release(controller); 502 } 503 504 SDL_UnlockJoysticks(); 505 506 return S_OK; 507} 508 509static HRESULT STDMETHODCALLTYPE IEventHandler_CRawGameControllerVtbl_InvokeRemoved(__FIEventHandler_1_Windows__CGaming__CInput__CRawGameController *This, IInspectable *sender, __x_ABI_CWindows_CGaming_CInput_CIRawGameController *e) 510{ 511 HRESULT hr; 512 __x_ABI_CWindows_CGaming_CInput_CIRawGameController *controller = NULL; 513 514 SDL_LockJoysticks(); 515 516 // Can we get delayed calls to InvokeRemoved() after WGI_JoystickQuit()? 517 if (!SDL_JoysticksInitialized()) { 518 SDL_UnlockJoysticks(); 519 return S_OK; 520 } 521 522 hr = __x_ABI_CWindows_CGaming_CInput_CIRawGameController_QueryInterface(e, &IID___x_ABI_CWindows_CGaming_CInput_CIRawGameController, (void **)&controller); 523 if (SUCCEEDED(hr)) { 524 int i; 525 526 for (i = 0; i < wgi.controller_count; i++) { 527 if (wgi.controllers[i].controller == controller) { 528 WindowsGamingInputControllerState *state = &wgi.controllers[i]; 529 SDL_JoystickID joystickID = state->instance_id; 530 531 __x_ABI_CWindows_CGaming_CInput_CIRawGameController_Release(state->controller); 532 533 SDL_free(state->name); 534 535 --wgi.controller_count; 536 if (i < wgi.controller_count) { 537 SDL_memmove(&wgi.controllers[i], &wgi.controllers[i + 1], (wgi.controller_count - i) * sizeof(wgi.controllers[i])); 538 } 539 540 SDL_PrivateJoystickRemoved(joystickID); 541 break; 542 } 543 } 544 545 __x_ABI_CWindows_CGaming_CInput_CIRawGameController_Release(controller); 546 } 547 548 SDL_UnlockJoysticks(); 549 550 return S_OK; 551} 552 553#ifdef _MSC_VER 554#pragma warning(push) 555#pragma warning(disable : 4028) // formal parameter 3 different from declaration, when using older buggy WGI headers 556#pragma warning(disable : 4113) // formal parameter 3 different from declaration (a more specific warning added in VS 2022), when using older buggy WGI headers 557#endif 558 559static __FIEventHandler_1_Windows__CGaming__CInput__CRawGameControllerVtbl controller_added_vtbl = { 560 IEventHandler_CRawGameControllerVtbl_QueryInterface, 561 IEventHandler_CRawGameControllerVtbl_AddRef, 562 IEventHandler_CRawGameControllerVtbl_Release, 563 IEventHandler_CRawGameControllerVtbl_InvokeAdded 564}; 565static RawGameControllerDelegate controller_added = { 566 { &controller_added_vtbl }, 567 { 1 } 568}; 569 570static __FIEventHandler_1_Windows__CGaming__CInput__CRawGameControllerVtbl controller_removed_vtbl = { 571 IEventHandler_CRawGameControllerVtbl_QueryInterface, 572 IEventHandler_CRawGameControllerVtbl_AddRef, 573 IEventHandler_CRawGameControllerVtbl_Release, 574 IEventHandler_CRawGameControllerVtbl_InvokeRemoved 575}; 576static RawGameControllerDelegate controller_removed = { 577 { &controller_removed_vtbl }, 578 { 1 } 579}; 580 581#ifdef _MSC_VER 582#pragma warning(pop) 583#endif 584 585static bool WGI_JoystickInit(void) 586{ 587 HRESULT hr; 588 589 if (!SDL_GetHintBoolean(SDL_HINT_JOYSTICK_WGI, false)) { 590 return true; 591 } 592 593 if (FAILED(WIN_RoInitialize())) { 594 return SDL_SetError("RoInitialize() failed"); 595 } 596 wgi.ro_initialized = true; 597 598#define RESOLVE(x) wgi.x = (x##_t)WIN_LoadComBaseFunction(#x); if (!wgi.x) return WIN_SetError("GetProcAddress failed for " #x); 599 RESOLVE(CoIncrementMTAUsage); 600 RESOLVE(RoGetActivationFactory); 601 RESOLVE(WindowsCreateStringReference); 602 RESOLVE(WindowsDeleteString); 603 RESOLVE(WindowsGetStringRawBuffer); 604#undef RESOLVE 605 606 { 607 /* There seems to be a bug in Windows where a dependency of WGI can be unloaded from memory prior to WGI itself. 608 * This results in Windows_Gaming_Input!GameController::~GameController() invoking an unloaded DLL and crashing. 609 * As a workaround, we will keep a reference to the MTA to prevent COM from unloading DLLs later. 610 * See https://github.com/libsdl-org/SDL/issues/5552 for more details. 611 */ 612 static CO_MTA_USAGE_COOKIE cookie = NULL; 613 if (!cookie) { 614 hr = wgi.CoIncrementMTAUsage(&cookie); 615 if (FAILED(hr)) { 616 return WIN_SetErrorFromHRESULT("CoIncrementMTAUsage() failed", hr); 617 } 618 } 619 } 620 621 WGI_LoadRawGameControllerStatics(); 622 623 if (wgi.controller_statics) { 624 __FIVectorView_1_Windows__CGaming__CInput__CRawGameController *controllers; 625 626 hr = __x_ABI_CWindows_CGaming_CInput_CIRawGameControllerStatics_add_RawGameControllerAdded(wgi.controller_statics, &controller_added.iface, &wgi.controller_added_token); 627 if (!SUCCEEDED(hr)) { 628 WIN_SetErrorFromHRESULT("Windows.Gaming.Input.IRawGameControllerStatics.add_RawGameControllerAdded failed", hr); 629 } 630 631 hr = __x_ABI_CWindows_CGaming_CInput_CIRawGameControllerStatics_add_RawGameControllerRemoved(wgi.controller_statics, &controller_removed.iface, &wgi.controller_removed_token); 632 if (!SUCCEEDED(hr)) { 633 WIN_SetErrorFromHRESULT("Windows.Gaming.Input.IRawGameControllerStatics.add_RawGameControllerRemoved failed", hr); 634 } 635 636 hr = __x_ABI_CWindows_CGaming_CInput_CIRawGameControllerStatics_get_RawGameControllers(wgi.controller_statics, &controllers); 637 if (SUCCEEDED(hr)) { 638 unsigned i, count = 0; 639 640 hr = __FIVectorView_1_Windows__CGaming__CInput__CRawGameController_get_Size(controllers, &count); 641 if (SUCCEEDED(hr)) { 642 for (i = 0; i < count; ++i) { 643 __x_ABI_CWindows_CGaming_CInput_CIRawGameController *controller = NULL; 644 645 hr = __FIVectorView_1_Windows__CGaming__CInput__CRawGameController_GetAt(controllers, i, &controller); 646 if (SUCCEEDED(hr) && controller) { 647 IEventHandler_CRawGameControllerVtbl_InvokeAdded(&controller_added.iface, NULL, controller); 648 __x_ABI_CWindows_CGaming_CInput_CIRawGameController_Release(controller); 649 } 650 } 651 } 652 653 __FIVectorView_1_Windows__CGaming__CInput__CRawGameController_Release(controllers); 654 } 655 } 656 657 return true; 658} 659 660static int WGI_JoystickGetCount(void) 661{ 662 return wgi.controller_count; 663} 664 665static void WGI_JoystickDetect(void) 666{ 667} 668 669static bool WGI_JoystickIsDevicePresent(Uint16 vendor_id, Uint16 product_id, Uint16 version, const char *name) 670{ 671 // We don't override any other drivers 672 return false; 673} 674 675static const char *WGI_JoystickGetDeviceName(int device_index) 676{ 677 return wgi.controllers[device_index].name; 678} 679 680static const char *WGI_JoystickGetDevicePath(int device_index) 681{ 682 return NULL; 683} 684 685static int WGI_JoystickGetDeviceSteamVirtualGamepadSlot(int device_index) 686{ 687 return wgi.controllers[device_index].steam_virtual_gamepad_slot; 688} 689 690static int WGI_JoystickGetDevicePlayerIndex(int device_index) 691{ 692 return false; 693} 694 695static void WGI_JoystickSetDevicePlayerIndex(int device_index, int player_index) 696{ 697} 698 699static SDL_GUID WGI_JoystickGetDeviceGUID(int device_index) 700{ 701 return wgi.controllers[device_index].guid; 702} 703 704static SDL_JoystickID WGI_JoystickGetDeviceInstanceID(int device_index) 705{ 706 return wgi.controllers[device_index].instance_id; 707} 708 709static bool WGI_JoystickOpen(SDL_Joystick *joystick, int device_index) 710{ 711 WindowsGamingInputControllerState *state = &wgi.controllers[device_index]; 712 struct joystick_hwdata *hwdata; 713 boolean wireless = false; 714 715 hwdata = (struct joystick_hwdata *)SDL_calloc(1, sizeof(*hwdata)); 716 if (!hwdata) { 717 return false; 718 } 719 joystick->hwdata = hwdata; 720 721 hwdata->controller = state->controller; 722 __x_ABI_CWindows_CGaming_CInput_CIRawGameController_AddRef(hwdata->controller); 723 __x_ABI_CWindows_CGaming_CInput_CIRawGameController_QueryInterface(hwdata->controller, &IID___x_ABI_CWindows_CGaming_CInput_CIGameController, (void **)&hwdata->game_controller); 724 __x_ABI_CWindows_CGaming_CInput_CIRawGameController_QueryInterface(hwdata->controller, &IID___x_ABI_CWindows_CGaming_CInput_CIGameControllerBatteryInfo, (void **)&hwdata->battery); 725 726 if (wgi.gamepad_statics2) { 727 __x_ABI_CWindows_CGaming_CInput_CIGamepadStatics2_FromGameController(wgi.gamepad_statics2, hwdata->game_controller, &hwdata->gamepad); 728 } 729 730 if (hwdata->game_controller) { 731 __x_ABI_CWindows_CGaming_CInput_CIGameController_get_IsWireless(hwdata->game_controller, &wireless); 732 } 733 734 // Initialize the joystick capabilities 735 if (wireless) { 736 joystick->connection_state = SDL_JOYSTICK_CONNECTION_WIRELESS; 737 } else { 738 joystick->connection_state = SDL_JOYSTICK_CONNECTION_WIRED; 739 } 740 __x_ABI_CWindows_CGaming_CInput_CIRawGameController_get_ButtonCount(hwdata->controller, &joystick->nbuttons); 741 __x_ABI_CWindows_CGaming_CInput_CIRawGameController_get_AxisCount(hwdata->controller, &joystick->naxes); 742 __x_ABI_CWindows_CGaming_CInput_CIRawGameController_get_SwitchCount(hwdata->controller, &joystick->nhats); 743 744 if (hwdata->gamepad) { 745 // FIXME: Can WGI even tell us if trigger rumble is supported? 746 SDL_SetBooleanProperty(SDL_GetJoystickProperties(joystick), SDL_PROP_JOYSTICK_CAP_RUMBLE_BOOLEAN, true); 747 SDL_SetBooleanProperty(SDL_GetJoystickProperties(joystick), SDL_PROP_JOYSTICK_CAP_TRIGGER_RUMBLE_BOOLEAN, true); 748 } 749 return true; 750} 751 752static bool WGI_JoystickRumble(SDL_Joystick *joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble) 753{ 754 struct joystick_hwdata *hwdata = joystick->hwdata; 755 756 if (hwdata->gamepad) { 757 HRESULT hr; 758 759 // Note: reusing partially filled vibration data struct 760 hwdata->vibration.LeftMotor = (DOUBLE)low_frequency_rumble / SDL_MAX_UINT16; 761 hwdata->vibration.RightMotor = (DOUBLE)high_frequency_rumble / SDL_MAX_UINT16; 762 hr = __x_ABI_CWindows_CGaming_CInput_CIGamepad_put_Vibration(hwdata->gamepad, hwdata->vibration); 763 if (SUCCEEDED(hr)) { 764 return true; 765 } else { 766 return WIN_SetErrorFromHRESULT("Windows.Gaming.Input.IGamepad.put_Vibration failed", hr); 767 } 768 } else { 769 return SDL_Unsupported(); 770 } 771} 772 773static bool WGI_JoystickRumbleTriggers(SDL_Joystick *joystick, Uint16 left_rumble, Uint16 right_rumble) 774{ 775 struct joystick_hwdata *hwdata = joystick->hwdata; 776 777 if (hwdata->gamepad) { 778 HRESULT hr; 779 780 // Note: reusing partially filled vibration data struct 781 hwdata->vibration.LeftTrigger = (DOUBLE)left_rumble / SDL_MAX_UINT16; 782 hwdata->vibration.RightTrigger = (DOUBLE)right_rumble / SDL_MAX_UINT16; 783 hr = __x_ABI_CWindows_CGaming_CInput_CIGamepad_put_Vibration(hwdata->gamepad, hwdata->vibration); 784 if (SUCCEEDED(hr)) { 785 return true; 786 } else { 787 return WIN_SetErrorFromHRESULT("Windows.Gaming.Input.IGamepad.put_Vibration failed", hr); 788 } 789 } else { 790 return SDL_Unsupported(); 791 } 792} 793 794static bool WGI_JoystickSetLED(SDL_Joystick *joystick, Uint8 red, Uint8 green, Uint8 blue) 795{ 796 return SDL_Unsupported(); 797} 798 799static bool WGI_JoystickSendEffect(SDL_Joystick *joystick, const void *data, int size) 800{ 801 return SDL_Unsupported(); 802} 803 804static bool WGI_JoystickSetSensorsEnabled(SDL_Joystick *joystick, bool enabled) 805{ 806 return SDL_Unsupported(); 807} 808 809static Uint8 ConvertHatValue(__x_ABI_CWindows_CGaming_CInput_CGameControllerSwitchPosition value) 810{ 811 switch (value) { 812 case GameControllerSwitchPosition_Up: 813 return SDL_HAT_UP; 814 case GameControllerSwitchPosition_UpRight: 815 return SDL_HAT_RIGHTUP; 816 case GameControllerSwitchPosition_Right: 817 return SDL_HAT_RIGHT; 818 case GameControllerSwitchPosition_DownRight: 819 return SDL_HAT_RIGHTDOWN; 820 case GameControllerSwitchPosition_Down: 821 return SDL_HAT_DOWN; 822 case GameControllerSwitchPosition_DownLeft: 823 return SDL_HAT_LEFTDOWN; 824 case GameControllerSwitchPosition_Left: 825 return SDL_HAT_LEFT; 826 case GameControllerSwitchPosition_UpLeft: 827 return SDL_HAT_LEFTUP; 828 default: 829 return SDL_HAT_CENTERED; 830 } 831} 832 833static void WGI_JoystickUpdate(SDL_Joystick *joystick) 834{ 835 struct joystick_hwdata *hwdata = joystick->hwdata; 836 HRESULT hr; 837 UINT32 nbuttons = SDL_min(joystick->nbuttons, SDL_MAX_UINT8); 838 boolean *buttons = NULL; 839 UINT32 nhats = SDL_min(joystick->nhats, SDL_MAX_UINT8); 840 __x_ABI_CWindows_CGaming_CInput_CGameControllerSwitchPosition *hats = NULL; 841 UINT32 naxes = SDL_min(joystick->naxes, SDL_MAX_UINT8); 842 DOUBLE *axes = NULL; 843 UINT64 timestamp; 844 845 if (nbuttons > 0) { 846 buttons = SDL_stack_alloc(boolean, nbuttons); 847 } 848 if (nhats > 0) { 849 hats = SDL_stack_alloc(__x_ABI_CWindows_CGaming_CInput_CGameControllerSwitchPosition, nhats); 850 } 851 if (naxes > 0) { 852 axes = SDL_stack_alloc(DOUBLE, naxes); 853 } 854 855 hr = __x_ABI_CWindows_CGaming_CInput_CIRawGameController_GetCurrentReading(hwdata->controller, nbuttons, buttons, nhats, hats, naxes, axes, &timestamp); 856 if (SUCCEEDED(hr) && (!timestamp || timestamp != hwdata->timestamp)) { 857 UINT32 i; 858 bool all_zero = false; 859 860 hwdata->timestamp = timestamp; 861 862 // The axes are all zero when the application loses focus 863 if (naxes > 0) { 864 all_zero = true; 865 for (i = 0; i < naxes; ++i) { 866 if (axes[i] != 0.0f) { 867 all_zero = false; 868 break; 869 } 870 } 871 } 872 if (all_zero) { 873 SDL_PrivateJoystickForceRecentering(joystick); 874 } else { 875 // FIXME: What units are the timestamp we get from GetCurrentReading()? 876 timestamp = SDL_GetTicksNS(); 877 for (i = 0; i < nbuttons; ++i) { 878 SDL_SendJoystickButton(timestamp, joystick, (Uint8)i, buttons[i]); 879 } 880 for (i = 0; i < nhats; ++i) { 881 SDL_SendJoystickHat(timestamp, joystick, (Uint8)i, ConvertHatValue(hats[i])); 882 } 883 for (i = 0; i < naxes; ++i) { 884 SDL_SendJoystickAxis(timestamp, joystick, (Uint8)i, (Sint16)((int)(axes[i] * 65535) - 32768)); 885 } 886 } 887 } 888 889 SDL_stack_free(buttons); 890 SDL_stack_free(hats); 891 SDL_stack_free(axes); 892 893 if (hwdata->battery) { 894 __x_ABI_CWindows_CDevices_CPower_CIBatteryReport *report = NULL; 895 896 hr = __x_ABI_CWindows_CGaming_CInput_CIGameControllerBatteryInfo_TryGetBatteryReport(hwdata->battery, &report); 897 if (SUCCEEDED(hr) && report) { 898 SDL_PowerState state = SDL_POWERSTATE_UNKNOWN; 899 int percent = 0; 900 __x_ABI_CWindows_CSystem_CPower_CBatteryStatus status; 901 int full_capacity = 0, curr_capacity = 0; 902 __FIReference_1_int *full_capacityP, *curr_capacityP; 903 904 hr = __x_ABI_CWindows_CDevices_CPower_CIBatteryReport_get_Status(report, &status); 905 if (SUCCEEDED(hr)) { 906 switch (status) { 907 case BatteryStatus_NotPresent: 908 state = SDL_POWERSTATE_NO_BATTERY; 909 break; 910 case BatteryStatus_Discharging: 911 state = SDL_POWERSTATE_ON_BATTERY; 912 break; 913 case BatteryStatus_Idle: 914 state = SDL_POWERSTATE_CHARGED; 915 break; 916 case BatteryStatus_Charging: 917 state = SDL_POWERSTATE_CHARGING; 918 break; 919 default: 920 state = SDL_POWERSTATE_UNKNOWN; 921 break; 922 } 923 } 924 925 hr = __x_ABI_CWindows_CDevices_CPower_CIBatteryReport_get_FullChargeCapacityInMilliwattHours(report, &full_capacityP); 926 if (SUCCEEDED(hr)) { 927 __FIReference_1_int_get_Value(full_capacityP, &full_capacity); 928 __FIReference_1_int_Release(full_capacityP); 929 } 930 931 hr = __x_ABI_CWindows_CDevices_CPower_CIBatteryReport_get_RemainingCapacityInMilliwattHours(report, &curr_capacityP); 932 if (SUCCEEDED(hr)) { 933 __FIReference_1_int_get_Value(curr_capacityP, &curr_capacity); 934 __FIReference_1_int_Release(curr_capacityP); 935 } 936 937 if (full_capacity > 0) { 938 percent = (int)SDL_roundf(((float)curr_capacity / full_capacity) * 100.0f); 939 } 940 941 SDL_SendJoystickPowerInfo(joystick, state, percent); 942 943 __x_ABI_CWindows_CDevices_CPower_CIBatteryReport_Release(report); 944 } 945 } 946} 947 948static void WGI_JoystickClose(SDL_Joystick *joystick) 949{ 950 struct joystick_hwdata *hwdata = joystick->hwdata; 951 952 if (hwdata) { 953 if (hwdata->controller) { 954 __x_ABI_CWindows_CGaming_CInput_CIRawGameController_Release(hwdata->controller); 955 } 956 if (hwdata->game_controller) { 957 __x_ABI_CWindows_CGaming_CInput_CIGameController_Release(hwdata->game_controller); 958 } 959 if (hwdata->battery) { 960 __x_ABI_CWindows_CGaming_CInput_CIGameControllerBatteryInfo_Release(hwdata->battery); 961 } 962 if (hwdata->gamepad) { 963 __x_ABI_CWindows_CGaming_CInput_CIGamepad_Release(hwdata->gamepad); 964 } 965 SDL_free(hwdata); 966 } 967 joystick->hwdata = NULL; 968} 969 970static void WGI_JoystickQuit(void) 971{ 972 if (wgi.controller_statics) { 973 while (wgi.controller_count > 0) { 974 IEventHandler_CRawGameControllerVtbl_InvokeRemoved(&controller_removed.iface, NULL, wgi.controllers[wgi.controller_count - 1].controller); 975 } 976 SDL_free(wgi.controllers); 977 978 if (wgi.arcade_stick_statics) { 979 __x_ABI_CWindows_CGaming_CInput_CIArcadeStickStatics_Release(wgi.arcade_stick_statics); 980 } 981 if (wgi.arcade_stick_statics2) { 982 __x_ABI_CWindows_CGaming_CInput_CIArcadeStickStatics2_Release(wgi.arcade_stick_statics2); 983 } 984 if (wgi.flight_stick_statics) { 985 __x_ABI_CWindows_CGaming_CInput_CIFlightStickStatics_Release(wgi.flight_stick_statics); 986 } 987 if (wgi.gamepad_statics) { 988 __x_ABI_CWindows_CGaming_CInput_CIGamepadStatics_Release(wgi.gamepad_statics); 989 } 990 if (wgi.gamepad_statics2) { 991 __x_ABI_CWindows_CGaming_CInput_CIGamepadStatics2_Release(wgi.gamepad_statics2); 992 } 993 if (wgi.racing_wheel_statics) { 994 __x_ABI_CWindows_CGaming_CInput_CIRacingWheelStatics_Release(wgi.racing_wheel_statics); 995 } 996 if (wgi.racing_wheel_statics2) { 997 __x_ABI_CWindows_CGaming_CInput_CIRacingWheelStatics2_Release(wgi.racing_wheel_statics2); 998 } 999 1000 __x_ABI_CWindows_CGaming_CInput_CIRawGameControllerStatics_remove_RawGameControllerAdded(wgi.controller_statics, wgi.controller_added_token); 1001 __x_ABI_CWindows_CGaming_CInput_CIRawGameControllerStatics_remove_RawGameControllerRemoved(wgi.controller_statics, wgi.controller_removed_token); 1002 __x_ABI_CWindows_CGaming_CInput_CIRawGameControllerStatics_Release(wgi.controller_statics); 1003 } 1004 1005 if (wgi.ro_initialized) { 1006 WIN_RoUninitialize(); 1007 } 1008 1009 SDL_zero(wgi); 1010} 1011 1012static bool WGI_JoystickGetGamepadMapping(int device_index, SDL_GamepadMapping *out) 1013{ 1014 return false; 1015} 1016 1017SDL_JoystickDriver SDL_WGI_JoystickDriver = { 1018 WGI_JoystickInit, 1019 WGI_JoystickGetCount, 1020 WGI_JoystickDetect, 1021 WGI_JoystickIsDevicePresent, 1022 WGI_JoystickGetDeviceName, 1023 WGI_JoystickGetDevicePath, 1024 WGI_JoystickGetDeviceSteamVirtualGamepadSlot, 1025 WGI_JoystickGetDevicePlayerIndex, 1026 WGI_JoystickSetDevicePlayerIndex, 1027 WGI_JoystickGetDeviceGUID, 1028 WGI_JoystickGetDeviceInstanceID, 1029 WGI_JoystickOpen, 1030 WGI_JoystickRumble, 1031 WGI_JoystickRumbleTriggers, 1032 WGI_JoystickSetLED, 1033 WGI_JoystickSendEffect, 1034 WGI_JoystickSetSensorsEnabled, 1035 WGI_JoystickUpdate, 1036 WGI_JoystickClose, 1037 WGI_JoystickQuit, 1038 WGI_JoystickGetGamepadMapping 1039}; 1040 1041#endif // SDL_JOYSTICK_WGI 1042
[FILE END]
(C) 2025 0x4248 (C) 2025 4248 Media and 4248 Systems, All part of 0x4248 See LICENCE files for more information. Not all files are by 0x4248 always check Licencing.