summaryrefslogtreecommitdiff
path: root/contrib/SDL-3.2.8/src/joystick/windows/SDL_windows_gaming_input.c
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/SDL-3.2.8/src/joystick/windows/SDL_windows_gaming_input.c')
-rw-r--r--contrib/SDL-3.2.8/src/joystick/windows/SDL_windows_gaming_input.c1039
1 files changed, 1039 insertions, 0 deletions
diff --git a/contrib/SDL-3.2.8/src/joystick/windows/SDL_windows_gaming_input.c b/contrib/SDL-3.2.8/src/joystick/windows/SDL_windows_gaming_input.c
new file mode 100644
index 0000000..dbc5658
--- /dev/null
+++ b/contrib/SDL-3.2.8/src/joystick/windows/SDL_windows_gaming_input.c
@@ -0,0 +1,1039 @@
1/*
2 Simple DirectMedia Layer
3 Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
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 CoIncrementMTAUsage_t CoIncrementMTAUsage;
73 RoGetActivationFactory_t RoGetActivationFactory;
74 WindowsCreateStringReference_t WindowsCreateStringReference;
75 WindowsDeleteString_t WindowsDeleteString;
76 WindowsGetStringRawBuffer_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;
85 EventRegistrationToken controller_added_token;
86 EventRegistrationToken controller_removed_token;
87 int controller_count;
88 WindowsGamingInputControllerState *controllers;
89} wgi;
90
91// WinRT headers in official Windows SDK contain only declarations, and we have to define these GUIDs ourselves.
92// https://stackoverflow.com/a/55605485/1795050
93DEFINE_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);
106
107extern bool SDL_XINPUT_Enabled(void);
108
109
110static bool SDL_IsXInputDevice(Uint16 vendor, Uint16 product, const char *name)
111{
112#if defined(SDL_JOYSTICK_XINPUT) || defined(SDL_JOYSTICK_RAWINPUT)
113 PRAWINPUTDEVICELIST raw_devices = NULL;
114 UINT i, raw_device_count = 0;
115 LONG vidpid = MAKELONG(vendor, product);
116
117 // XInput and RawInput backends will pick up XInput-compatible devices
118 if (!SDL_XINPUT_Enabled()
119#ifdef SDL_JOYSTICK_RAWINPUT
120 && !RAWINPUT_IsEnabled()
121#endif
122 ) {
123 return false;
124 }
125
126 // 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.
128 if (SDL_IsJoystickXboxOne(vendor, product) ||
129 (name && SDL_strncmp(name, "Xbox ", 5) == 0)) {
130 return true;
131 }
132
133 // Go through RAWINPUT (WinXP and later) to find HID devices.
134 if ((GetRawInputDeviceList(NULL, &raw_device_count, sizeof(RAWINPUTDEVICELIST)) == -1) || (!raw_device_count)) {
135 return false; // oh well.
136 }
137
138 raw_devices = (PRAWINPUTDEVICELIST)SDL_malloc(sizeof(RAWINPUTDEVICELIST) * raw_device_count);
139 if (!raw_devices) {
140 return false;
141 }
142
143 raw_device_count = GetRawInputDeviceList(raw_devices, &raw_device_count, sizeof(RAWINPUTDEVICELIST));
144 if (raw_device_count == (UINT)-1) {
145 SDL_free(raw_devices);
146 raw_devices = NULL;
147 return false; // oh well.
148 }
149
150 for (i = 0; i < raw_device_count; i++) {
151 RID_DEVICE_INFO rdi;
152 char devName[MAX_PATH] = { 0 };
153 UINT rdiSize = sizeof(rdi);
154 UINT nameSize = SDL_arraysize(devName);
155 DEVINST devNode;
156 char devVidPidString[32];
157 int j;
158
159 rdi.cbSize = sizeof(rdi);
160
161 if ((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 devices
166 continue;
167 }
168
169 // First check for a simple VID/PID match. This will work for Xbox 360 controllers.
170 if (MAKELONG(rdi.hid.dwVendorId, rdi.hid.dwProductId) == vidpid) {
171 SDL_free(raw_devices);
172 return true;
173 }
174
175 /* 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. */
177
178 // Make sure the device interface string is something we know how to parse
179 // Example: \\?\HID#VID_045E&PID_02FF&IG_00#9&2c203035&2&0000#{4d1e55b2-f16f-11cf-88cb-001111000030}
180 if ((SDL_strstr(devName, "\\\\?\\") != devName) || (SDL_strstr(devName, "#{") == NULL)) {
181 continue;
182 }
183
184 // Unescape the backslashes in the string and terminate before the GUID portion
185 for (j = 0; devName[j] != '\0'; j++) {
186 if (devName[j] == '#') {
187 if (devName[j + 1] == '{') {
188 devName[j] = '\0';
189 break;
190 } else {
191 devName[j] = '\\';
192 }
193 }
194 }
195
196 /* We'll be left with a string like this: \\?\HID\VID_045E&PID_02FF&IG_00\9&2c203035&2&0000
197 * Simply skip the \\?\ prefix and we'll have a properly formed device instance ID */
198 if (CM_Locate_DevNodeA(&devNode, &devName[4], CM_LOCATE_DEVNODE_NORMAL) != CR_SUCCESS) {
199 continue;
200 }
201
202 (void)SDL_snprintf(devVidPidString, sizeof(devVidPidString), "VID_%04X&PID_%04X", vendor, product);
203
204 while (CM_Get_Parent(&devNode, devNode, 0) == CR_SUCCESS) {
205 char deviceId[MAX_DEVICE_ID_LEN];
206
207 if ((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 device
210 SDL_free(raw_devices);
211 return true;
212 }
213 }
214 }
215
216 SDL_free(raw_devices);
217#endif // SDL_JOYSTICK_XINPUT || SDL_JOYSTICK_RAWINPUT
218
219 return false;
220}
221
222static void WGI_LoadRawGameControllerStatics(void)
223{
224 HRESULT hr;
225 HSTRING_HEADER class_name_header;
226 HSTRING class_name;
227
228 hr = wgi.WindowsCreateStringReference(RuntimeClass_Windows_Gaming_Input_RawGameController, (UINT32)SDL_wcslen(RuntimeClass_Windows_Gaming_Input_RawGameController), &class_name_header, &class_name);
229 if (SUCCEEDED(hr)) {
230 hr = wgi.RoGetActivationFactory(class_name, &IID___x_ABI_CWindows_CGaming_CInput_CIRawGameControllerStatics, (void **)&wgi.controller_statics);
231 if (!SUCCEEDED(hr)) {
232 WIN_SetErrorFromHRESULT("Couldn't find Windows.Gaming.Input.IRawGameControllerStatics", hr);
233 }
234 }
235}
236
237static void WGI_LoadOtherControllerStatics(void)
238{
239 HRESULT hr;
240 HSTRING_HEADER class_name_header;
241 HSTRING class_name;
242
243 if (!wgi.arcade_stick_statics) {
244 hr = wgi.WindowsCreateStringReference(RuntimeClass_Windows_Gaming_Input_ArcadeStick, (UINT32)SDL_wcslen(RuntimeClass_Windows_Gaming_Input_ArcadeStick), &class_name_header, &class_name);
245 if (SUCCEEDED(hr)) {
246 hr = wgi.RoGetActivationFactory(class_name, &IID___x_ABI_CWindows_CGaming_CInput_CIArcadeStickStatics, (void **)&wgi.arcade_stick_statics);
247 if (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 {
250 WIN_SetErrorFromHRESULT("Couldn't find Windows.Gaming.Input.IArcadeStickStatics", hr);
251 }
252 }
253 }
254
255 if (!wgi.flight_stick_statics) {
256 hr = wgi.WindowsCreateStringReference(RuntimeClass_Windows_Gaming_Input_FlightStick, (UINT32)SDL_wcslen(RuntimeClass_Windows_Gaming_Input_FlightStick), &class_name_header, &class_name);
257 if (SUCCEEDED(hr)) {
258 hr = wgi.RoGetActivationFactory(class_name, &IID___x_ABI_CWindows_CGaming_CInput_CIFlightStickStatics, (void **)&wgi.flight_stick_statics);
259 if (!SUCCEEDED(hr)) {
260 WIN_SetErrorFromHRESULT("Couldn't find Windows.Gaming.Input.IFlightStickStatics", hr);
261 }
262 }
263 }
264
265 if (!wgi.gamepad_statics) {
266 hr = wgi.WindowsCreateStringReference(RuntimeClass_Windows_Gaming_Input_Gamepad, (UINT32)SDL_wcslen(RuntimeClass_Windows_Gaming_Input_Gamepad), &class_name_header, &class_name);
267 if (SUCCEEDED(hr)) {
268 hr = wgi.RoGetActivationFactory(class_name, &IID___x_ABI_CWindows_CGaming_CInput_CIGamepadStatics, (void **)&wgi.gamepad_statics);
269 if (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 {
272 WIN_SetErrorFromHRESULT("Couldn't find Windows.Gaming.Input.IGamepadStatics", hr);
273 }
274 }
275 }
276
277 if (!wgi.racing_wheel_statics) {
278 hr = wgi.WindowsCreateStringReference(RuntimeClass_Windows_Gaming_Input_RacingWheel, (UINT32)SDL_wcslen(RuntimeClass_Windows_Gaming_Input_RacingWheel), &class_name_header, &class_name);
279 if (SUCCEEDED(hr)) {
280 hr = wgi.RoGetActivationFactory(class_name, &IID___x_ABI_CWindows_CGaming_CInput_CIRacingWheelStatics, (void **)&wgi.racing_wheel_statics);
281 if (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 {
284 WIN_SetErrorFromHRESULT("Couldn't find Windows.Gaming.Input.IRacingWheelStatics", hr);
285 }
286 }
287 }
288}
289
290static 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;
296
297 /* Wait to initialize these interfaces until we need them.
298 * Initializing the gamepad interface will switch Bluetooth PS4 controllers into enhanced mode, breaking DirectInput
299 */
300 WGI_LoadOtherControllerStatics();
301
302 if (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);
304 return SDL_JOYSTICK_TYPE_GAMEPAD;
305 }
306
307 if (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);
309 return SDL_JOYSTICK_TYPE_ARCADE_STICK;
310 }
311
312 if (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);
314 return SDL_JOYSTICK_TYPE_FLIGHT_STICK;
315 }
316
317 if (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);
319 return SDL_JOYSTICK_TYPE_WHEEL;
320 }
321
322 return SDL_JOYSTICK_TYPE_UNKNOWN;
323}
324
325typedef struct RawGameControllerDelegate
326{
327 __FIEventHandler_1_Windows__CGaming__CInput__CRawGameController iface;
328 SDL_AtomicInt refcount;
329} RawGameControllerDelegate;
330
331static HRESULT STDMETHODCALLTYPE IEventHandler_CRawGameControllerVtbl_QueryInterface(__FIEventHandler_1_Windows__CGaming__CInput__CRawGameController *This, REFIID riid, void **ppvObject)
332{
333 if (!ppvObject) {
334 return E_INVALIDARG;
335 }
336
337 *ppvObject = NULL;
338 if (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);
341 return S_OK;
342 } else if (WIN_IsEqualIID(riid, &IID_IMarshal)) {
343 // This seems complicated. Let's hope it doesn't happen.
344 return E_OUTOFMEMORY;
345 } else {
346 return E_NOINTERFACE;
347 }
348}
349
350static ULONG STDMETHODCALLTYPE IEventHandler_CRawGameControllerVtbl_AddRef(__FIEventHandler_1_Windows__CGaming__CInput__CRawGameController *This)
351{
352 RawGameControllerDelegate *self = (RawGameControllerDelegate *)This;
353 return SDL_AddAtomicInt(&self->refcount, 1) + 1UL;
354}
355
356static ULONG STDMETHODCALLTYPE IEventHandler_CRawGameControllerVtbl_Release(__FIEventHandler_1_Windows__CGaming__CInput__CRawGameController *This)
357{
358 RawGameControllerDelegate *self = (RawGameControllerDelegate *)This;
359 int rc = SDL_AddAtomicInt(&self->refcount, -1) - 1;
360 // Should never free the static delegate objects
361 SDL_assert(rc > 0);
362 return rc;
363}
364
365static int GetSteamVirtualGamepadSlot(__x_ABI_CWindows_CGaming_CInput_CIRawGameController *controller, Uint16 vendor_id, Uint16 product_id)
366{
367 int slot = -1;
368
369 if (vendor_id == USB_VENDOR_VALVE &&
370 product_id == USB_PRODUCT_STEAM_VIRTUAL_GAMEPAD) {
371 __x_ABI_CWindows_CGaming_CInput_CIRawGameController2 *controller2 = NULL;
372 HRESULT hr = __x_ABI_CWindows_CGaming_CInput_CIRawGameController_QueryInterface(controller, &IID___x_ABI_CWindows_CGaming_CInput_CIRawGameController2, (void **)&controller2);
373 if (SUCCEEDED(hr)) {
374 HSTRING hString;
375 hr = __x_ABI_CWindows_CGaming_CInput_CIRawGameController2_get_NonRoamableId(controller2, &hString);
376 if (SUCCEEDED(hr)) {
377 PCWSTR string = wgi.WindowsGetStringRawBuffer(hString, NULL);
378 if (string) {
379 char *id = WIN_StringToUTF8W(string);
380 if (id) {
381 (void)SDL_sscanf(id, "{wgi/nrid/:steam-%*X&%*X&%*X#%d#%*u}", &slot);
382 SDL_free(id);
383 }
384 }
385 wgi.WindowsDeleteString(hString);
386 }
387 __x_ABI_CWindows_CGaming_CInput_CIRawGameController2_Release(controller2);
388 }
389 }
390 return slot;
391}
392
393static HRESULT STDMETHODCALLTYPE IEventHandler_CRawGameControllerVtbl_InvokeAdded(__FIEventHandler_1_Windows__CGaming__CInput__CRawGameController *This, IInspectable *sender, __x_ABI_CWindows_CGaming_CInput_CIRawGameController *e)
394{
395 HRESULT hr;
396 __x_ABI_CWindows_CGaming_CInput_CIRawGameController *controller = NULL;
397
398 SDL_LockJoysticks();
399
400 // We can get delayed calls to InvokeAdded() after WGI_JoystickQuit()
401 if (SDL_JoysticksQuitting() || !SDL_JoysticksInitialized()) {
402 SDL_UnlockJoysticks();
403 return S_OK;
404 }
405
406 hr = __x_ABI_CWindows_CGaming_CInput_CIRawGameController_QueryInterface(e, &IID___x_ABI_CWindows_CGaming_CInput_CIRawGameController, (void **)&controller);
407 if (SUCCEEDED(hr)) {
408 char *name = NULL;
409 Uint16 bus = SDL_HARDWARE_BUS_USB;
410 Uint16 vendor = 0;
411 Uint16 product = 0;
412 Uint16 version = 0;
413 SDL_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;
416 bool ignore_joystick = false;
417
418 __x_ABI_CWindows_CGaming_CInput_CIRawGameController_get_HardwareVendorId(controller, &vendor);
419 __x_ABI_CWindows_CGaming_CInput_CIRawGameController_get_HardwareProductId(controller, &product);
420
421 hr = __x_ABI_CWindows_CGaming_CInput_CIRawGameController_QueryInterface(controller, &IID___x_ABI_CWindows_CGaming_CInput_CIGameController, (void **)&game_controller);
422 if (SUCCEEDED(hr)) {
423 boolean wireless = 0;
424 hr = __x_ABI_CWindows_CGaming_CInput_CIGameController_get_IsWireless(game_controller, &wireless);
425 if (SUCCEEDED(hr) && wireless) {
426 bus = SDL_HARDWARE_BUS_BLUETOOTH;
427
428 // Fixup for Wireless Xbox 360 Controller
429 if (product == 0) {
430 vendor = USB_VENDOR_MICROSOFT;
431 product = USB_PRODUCT_XBOX360_XUSB_CONTROLLER;
432 }
433 }
434
435 __x_ABI_CWindows_CGaming_CInput_CIGameController_Release(game_controller);
436 }
437
438 hr = __x_ABI_CWindows_CGaming_CInput_CIRawGameController_QueryInterface(controller, &IID___x_ABI_CWindows_CGaming_CInput_CIRawGameController2, (void **)&controller2);
439 if (SUCCEEDED(hr)) {
440 HSTRING hString;
441 hr = __x_ABI_CWindows_CGaming_CInput_CIRawGameController2_get_DisplayName(controller2, &hString);
442 if (SUCCEEDED(hr)) {
443 PCWSTR string = wgi.WindowsGetStringRawBuffer(hString, NULL);
444 if (string) {
445 name = WIN_StringToUTF8W(string);
446 }
447 wgi.WindowsDeleteString(hString);
448 }
449 __x_ABI_CWindows_CGaming_CInput_CIRawGameController2_Release(controller2);
450 }
451 if (!name) {
452 name = SDL_strdup("");
453 }
454
455 if (!ignore_joystick && SDL_ShouldIgnoreJoystick(vendor, product, version, name)) {
456 ignore_joystick = true;
457 }
458
459 if (!ignore_joystick && SDL_JoystickHandledByAnotherDriver(&SDL_WGI_JoystickDriver, vendor, product, version, name)) {
460 ignore_joystick = true;
461 }
462
463 if (!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.
465 ignore_joystick = true;
466 }
467
468 if (!ignore_joystick) {
469 // New device, add it
470 WindowsGamingInputControllerState *controllers = SDL_realloc(wgi.controllers, sizeof(wgi.controllers[0]) * (wgi.controller_count + 1));
471 if (controllers) {
472 WindowsGamingInputControllerState *state = &controllers[wgi.controller_count];
473 SDL_JoystickID joystickID = SDL_GetNextObjectID();
474
475 if (game_controller) {
476 type = GetGameControllerType(game_controller);
477 }
478
479 SDL_zerop(state);
480 state->instance_id = joystickID;
481 state->controller = controller;
482 state->name = name;
483 state->guid = SDL_CreateJoystickGUID(bus, vendor, product, version, NULL, name, 'w', (Uint8)type);
484 state->type = type;
485 state->steam_virtual_gamepad_slot = GetSteamVirtualGamepadSlot(controller, vendor, product);
486
487 __x_ABI_CWindows_CGaming_CInput_CIRawGameController_AddRef(controller);
488
489 ++wgi.controller_count;
490 wgi.controllers = controllers;
491
492 SDL_PrivateJoystickAdded(joystickID);
493 } else {
494 SDL_free(name);
495 }
496 } else {
497 SDL_free(name);
498 }
499
500 __x_ABI_CWindows_CGaming_CInput_CIRawGameController_Release(controller);
501 }
502
503 SDL_UnlockJoysticks();
504
505 return S_OK;
506}
507
508static HRESULT STDMETHODCALLTYPE IEventHandler_CRawGameControllerVtbl_InvokeRemoved(__FIEventHandler_1_Windows__CGaming__CInput__CRawGameController *This, IInspectable *sender, __x_ABI_CWindows_CGaming_CInput_CIRawGameController *e)
509{
510 HRESULT hr;
511 __x_ABI_CWindows_CGaming_CInput_CIRawGameController *controller = NULL;
512
513 SDL_LockJoysticks();
514
515 // Can we get delayed calls to InvokeRemoved() after WGI_JoystickQuit()?
516 if (!SDL_JoysticksInitialized()) {
517 SDL_UnlockJoysticks();
518 return S_OK;
519 }
520
521 hr = __x_ABI_CWindows_CGaming_CInput_CIRawGameController_QueryInterface(e, &IID___x_ABI_CWindows_CGaming_CInput_CIRawGameController, (void **)&controller);
522 if (SUCCEEDED(hr)) {
523 int i;
524
525 for (i = 0; i < wgi.controller_count; i++) {
526 if (wgi.controllers[i].controller == controller) {
527 WindowsGamingInputControllerState *state = &wgi.controllers[i];
528 SDL_JoystickID joystickID = state->instance_id;
529
530 __x_ABI_CWindows_CGaming_CInput_CIRawGameController_Release(state->controller);
531
532 SDL_free(state->name);
533
534 --wgi.controller_count;
535 if (i < wgi.controller_count) {
536 SDL_memmove(&wgi.controllers[i], &wgi.controllers[i + 1], (wgi.controller_count - i) * sizeof(wgi.controllers[i]));
537 }
538
539 SDL_PrivateJoystickRemoved(joystickID);
540 break;
541 }
542 }
543
544 __x_ABI_CWindows_CGaming_CInput_CIRawGameController_Release(controller);
545 }
546
547 SDL_UnlockJoysticks();
548
549 return S_OK;
550}
551
552#ifdef _MSC_VER
553#pragma warning(push)
554#pragma warning(disable : 4028) // formal parameter 3 different from declaration, when using older buggy WGI headers
555#pragma warning(disable : 4113) // formal parameter 3 different from declaration (a more specific warning added in VS 2022), when using older buggy WGI headers
556#endif
557
558static __FIEventHandler_1_Windows__CGaming__CInput__CRawGameControllerVtbl controller_added_vtbl = {
559 IEventHandler_CRawGameControllerVtbl_QueryInterface,
560 IEventHandler_CRawGameControllerVtbl_AddRef,
561 IEventHandler_CRawGameControllerVtbl_Release,
562 IEventHandler_CRawGameControllerVtbl_InvokeAdded
563};
564static RawGameControllerDelegate controller_added = {
565 { &controller_added_vtbl },
566 { 1 }
567};
568
569static __FIEventHandler_1_Windows__CGaming__CInput__CRawGameControllerVtbl controller_removed_vtbl = {
570 IEventHandler_CRawGameControllerVtbl_QueryInterface,
571 IEventHandler_CRawGameControllerVtbl_AddRef,
572 IEventHandler_CRawGameControllerVtbl_Release,
573 IEventHandler_CRawGameControllerVtbl_InvokeRemoved
574};
575static RawGameControllerDelegate controller_removed = {
576 { &controller_removed_vtbl },
577 { 1 }
578};
579
580#ifdef _MSC_VER
581#pragma warning(pop)
582#endif
583
584static bool WGI_JoystickInit(void)
585{
586 HRESULT hr;
587
588 if (!SDL_GetHintBoolean(SDL_HINT_JOYSTICK_WGI, true)) {
589 return true;
590 }
591
592 if (FAILED(WIN_RoInitialize())) {
593 return SDL_SetError("RoInitialize() failed");
594 }
595
596#define RESOLVE(x) wgi.x = (x##_t)WIN_LoadComBaseFunction(#x); if (!wgi.x) return WIN_SetError("GetProcAddress failed for " #x);
597 RESOLVE(CoIncrementMTAUsage);
598 RESOLVE(RoGetActivationFactory);
599 RESOLVE(WindowsCreateStringReference);
600 RESOLVE(WindowsDeleteString);
601 RESOLVE(WindowsGetStringRawBuffer);
602#undef RESOLVE
603
604 {
605 /* There seems to be a bug in Windows where a dependency of WGI can be unloaded from memory prior to WGI itself.
606 * This results in Windows_Gaming_Input!GameController::~GameController() invoking an unloaded DLL and crashing.
607 * As a workaround, we will keep a reference to the MTA to prevent COM from unloading DLLs later.
608 * See https://github.com/libsdl-org/SDL/issues/5552 for more details.
609 */
610 static CO_MTA_USAGE_COOKIE cookie = NULL;
611 if (!cookie) {
612 hr = wgi.CoIncrementMTAUsage(&cookie);
613 if (FAILED(hr)) {
614 return WIN_SetErrorFromHRESULT("CoIncrementMTAUsage() failed", hr);
615 }
616 }
617 }
618
619 WGI_LoadRawGameControllerStatics();
620
621 if (wgi.controller_statics) {
622 __FIVectorView_1_Windows__CGaming__CInput__CRawGameController *controllers;
623
624 hr = __x_ABI_CWindows_CGaming_CInput_CIRawGameControllerStatics_add_RawGameControllerAdded(wgi.controller_statics, &controller_added.iface, &wgi.controller_added_token);
625 if (!SUCCEEDED(hr)) {
626 WIN_SetErrorFromHRESULT("Windows.Gaming.Input.IRawGameControllerStatics.add_RawGameControllerAdded failed", hr);
627 }
628
629 hr = __x_ABI_CWindows_CGaming_CInput_CIRawGameControllerStatics_add_RawGameControllerRemoved(wgi.controller_statics, &controller_removed.iface, &wgi.controller_removed_token);
630 if (!SUCCEEDED(hr)) {
631 WIN_SetErrorFromHRESULT("Windows.Gaming.Input.IRawGameControllerStatics.add_RawGameControllerRemoved failed", hr);
632 }
633
634 hr = __x_ABI_CWindows_CGaming_CInput_CIRawGameControllerStatics_get_RawGameControllers(wgi.controller_statics, &controllers);
635 if (SUCCEEDED(hr)) {
636 unsigned i, count = 0;
637
638 hr = __FIVectorView_1_Windows__CGaming__CInput__CRawGameController_get_Size(controllers, &count);
639 if (SUCCEEDED(hr)) {
640 for (i = 0; i < count; ++i) {
641 __x_ABI_CWindows_CGaming_CInput_CIRawGameController *controller = NULL;
642
643 hr = __FIVectorView_1_Windows__CGaming__CInput__CRawGameController_GetAt(controllers, i, &controller);
644 if (SUCCEEDED(hr) && controller) {
645 IEventHandler_CRawGameControllerVtbl_InvokeAdded(&controller_added.iface, NULL, controller);
646 __x_ABI_CWindows_CGaming_CInput_CIRawGameController_Release(controller);
647 }
648 }
649 }
650
651 __FIVectorView_1_Windows__CGaming__CInput__CRawGameController_Release(controllers);
652 }
653 }
654
655 return true;
656}
657
658static int WGI_JoystickGetCount(void)
659{
660 return wgi.controller_count;
661}
662
663static void WGI_JoystickDetect(void)
664{
665}
666
667static bool WGI_JoystickIsDevicePresent(Uint16 vendor_id, Uint16 product_id, Uint16 version, const char *name)
668{
669 // We don't override any other drivers
670 return false;
671}
672
673static const char *WGI_JoystickGetDeviceName(int device_index)
674{
675 return wgi.controllers[device_index].name;
676}
677
678static const char *WGI_JoystickGetDevicePath(int device_index)
679{
680 return NULL;
681}
682
683static int WGI_JoystickGetDeviceSteamVirtualGamepadSlot(int device_index)
684{
685 return wgi.controllers[device_index].steam_virtual_gamepad_slot;
686}
687
688static int WGI_JoystickGetDevicePlayerIndex(int device_index)
689{
690 return false;
691}
692
693static void WGI_JoystickSetDevicePlayerIndex(int device_index, int player_index)
694{
695}
696
697static SDL_GUID WGI_JoystickGetDeviceGUID(int device_index)
698{
699 return wgi.controllers[device_index].guid;
700}
701
702static SDL_JoystickID WGI_JoystickGetDeviceInstanceID(int device_index)
703{
704 return wgi.controllers[device_index].instance_id;
705}
706
707static bool WGI_JoystickOpen(SDL_Joystick *joystick, int device_index)
708{
709 WindowsGamingInputControllerState *state = &wgi.controllers[device_index];
710 struct joystick_hwdata *hwdata;
711 boolean wireless = false;
712
713 hwdata = (struct joystick_hwdata *)SDL_calloc(1, sizeof(*hwdata));
714 if (!hwdata) {
715 return false;
716 }
717 joystick->hwdata = hwdata;
718
719 hwdata->controller = state->controller;
720 __x_ABI_CWindows_CGaming_CInput_CIRawGameController_AddRef(hwdata->controller);
721 __x_ABI_CWindows_CGaming_CInput_CIRawGameController_QueryInterface(hwdata->controller, &IID___x_ABI_CWindows_CGaming_CInput_CIGameController, (void **)&hwdata->game_controller);
722 __x_ABI_CWindows_CGaming_CInput_CIRawGameController_QueryInterface(hwdata->controller, &IID___x_ABI_CWindows_CGaming_CInput_CIGameControllerBatteryInfo, (void **)&hwdata->battery);
723
724 if (wgi.gamepad_statics2) {
725 __x_ABI_CWindows_CGaming_CInput_CIGamepadStatics2_FromGameController(wgi.gamepad_statics2, hwdata->game_controller, &hwdata->gamepad);
726 }
727
728 if (hwdata->game_controller) {
729 __x_ABI_CWindows_CGaming_CInput_CIGameController_get_IsWireless(hwdata->game_controller, &wireless);
730 }
731
732 // Initialize the joystick capabilities
733 if (wireless) {
734 joystick->connection_state = SDL_JOYSTICK_CONNECTION_WIRELESS;
735 } else {
736 joystick->connection_state = SDL_JOYSTICK_CONNECTION_WIRED;
737 }
738 __x_ABI_CWindows_CGaming_CInput_CIRawGameController_get_ButtonCount(hwdata->controller, &joystick->nbuttons);
739 __x_ABI_CWindows_CGaming_CInput_CIRawGameController_get_AxisCount(hwdata->controller, &joystick->naxes);
740 __x_ABI_CWindows_CGaming_CInput_CIRawGameController_get_SwitchCount(hwdata->controller, &joystick->nhats);
741
742 if (hwdata->gamepad) {
743 // FIXME: Can WGI even tell us if trigger rumble is supported?
744 SDL_SetBooleanProperty(SDL_GetJoystickProperties(joystick), SDL_PROP_JOYSTICK_CAP_RUMBLE_BOOLEAN, true);
745 SDL_SetBooleanProperty(SDL_GetJoystickProperties(joystick), SDL_PROP_JOYSTICK_CAP_TRIGGER_RUMBLE_BOOLEAN, true);
746 }
747 return true;
748}
749
750static bool WGI_JoystickRumble(SDL_Joystick *joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble)
751{
752 struct joystick_hwdata *hwdata = joystick->hwdata;
753
754 if (hwdata->gamepad) {
755 HRESULT hr;
756
757 // Note: reusing partially filled vibration data struct
758 hwdata->vibration.LeftMotor = (DOUBLE)low_frequency_rumble / SDL_MAX_UINT16;
759 hwdata->vibration.RightMotor = (DOUBLE)high_frequency_rumble / SDL_MAX_UINT16;
760 hr = __x_ABI_CWindows_CGaming_CInput_CIGamepad_put_Vibration(hwdata->gamepad, hwdata->vibration);
761 if (SUCCEEDED(hr)) {
762 return true;
763 } else {
764 return WIN_SetErrorFromHRESULT("Windows.Gaming.Input.IGamepad.put_Vibration failed", hr);
765 }
766 } else {
767 return SDL_Unsupported();
768 }
769}
770
771static bool WGI_JoystickRumbleTriggers(SDL_Joystick *joystick, Uint16 left_rumble, Uint16 right_rumble)
772{
773 struct joystick_hwdata *hwdata = joystick->hwdata;
774
775 if (hwdata->gamepad) {
776 HRESULT hr;
777
778 // Note: reusing partially filled vibration data struct
779 hwdata->vibration.LeftTrigger = (DOUBLE)left_rumble / SDL_MAX_UINT16;
780 hwdata->vibration.RightTrigger = (DOUBLE)right_rumble / SDL_MAX_UINT16;
781 hr = __x_ABI_CWindows_CGaming_CInput_CIGamepad_put_Vibration(hwdata->gamepad, hwdata->vibration);
782 if (SUCCEEDED(hr)) {
783 return true;
784 } else {
785 return WIN_SetErrorFromHRESULT("Windows.Gaming.Input.IGamepad.put_Vibration failed", hr);
786 }
787 } else {
788 return SDL_Unsupported();
789 }
790}
791
792static bool WGI_JoystickSetLED(SDL_Joystick *joystick, Uint8 red, Uint8 green, Uint8 blue)
793{
794 return SDL_Unsupported();
795}
796
797static bool WGI_JoystickSendEffect(SDL_Joystick *joystick, const void *data, int size)
798{
799 return SDL_Unsupported();
800}
801
802static bool WGI_JoystickSetSensorsEnabled(SDL_Joystick *joystick, bool enabled)
803{
804 return SDL_Unsupported();
805}
806
807static Uint8 ConvertHatValue(__x_ABI_CWindows_CGaming_CInput_CGameControllerSwitchPosition value)
808{
809 switch (value) {
810 case GameControllerSwitchPosition_Up:
811 return SDL_HAT_UP;
812 case GameControllerSwitchPosition_UpRight:
813 return SDL_HAT_RIGHTUP;
814 case GameControllerSwitchPosition_Right:
815 return SDL_HAT_RIGHT;
816 case GameControllerSwitchPosition_DownRight:
817 return SDL_HAT_RIGHTDOWN;
818 case GameControllerSwitchPosition_Down:
819 return SDL_HAT_DOWN;
820 case GameControllerSwitchPosition_DownLeft:
821 return SDL_HAT_LEFTDOWN;
822 case GameControllerSwitchPosition_Left:
823 return SDL_HAT_LEFT;
824 case GameControllerSwitchPosition_UpLeft:
825 return SDL_HAT_LEFTUP;
826 default:
827 return SDL_HAT_CENTERED;
828 }
829}
830
831static void WGI_JoystickUpdate(SDL_Joystick *joystick)
832{
833 struct joystick_hwdata *hwdata = joystick->hwdata;
834 HRESULT hr;
835 UINT32 nbuttons = SDL_min(joystick->nbuttons, SDL_MAX_UINT8);
836 boolean *buttons = NULL;
837 UINT32 nhats = SDL_min(joystick->nhats, SDL_MAX_UINT8);
838 __x_ABI_CWindows_CGaming_CInput_CGameControllerSwitchPosition *hats = NULL;
839 UINT32 naxes = SDL_min(joystick->naxes, SDL_MAX_UINT8);
840 DOUBLE *axes = NULL;
841 UINT64 timestamp;
842
843 if (nbuttons > 0) {
844 buttons = SDL_stack_alloc(boolean, nbuttons);
845 }
846 if (nhats > 0) {
847 hats = SDL_stack_alloc(__x_ABI_CWindows_CGaming_CInput_CGameControllerSwitchPosition, nhats);
848 }
849 if (naxes > 0) {
850 axes = SDL_stack_alloc(DOUBLE, naxes);
851 }
852
853 hr = __x_ABI_CWindows_CGaming_CInput_CIRawGameController_GetCurrentReading(hwdata->controller, nbuttons, buttons, nhats, hats, naxes, axes, &timestamp);
854 if (SUCCEEDED(hr) && (!timestamp || timestamp != hwdata->timestamp)) {
855 UINT32 i;
856 bool all_zero = false;
857
858 hwdata->timestamp = timestamp;
859
860 // The axes are all zero when the application loses focus
861 if (naxes > 0) {
862 all_zero = true;
863 for (i = 0; i < naxes; ++i) {
864 if (axes[i] != 0.0f) {
865 all_zero = false;
866 break;
867 }
868 }
869 }
870 if (all_zero) {
871 SDL_PrivateJoystickForceRecentering(joystick);
872 } else {
873 // FIXME: What units are the timestamp we get from GetCurrentReading()?
874 timestamp = SDL_GetTicksNS();
875 for (i = 0; i < nbuttons; ++i) {
876 SDL_SendJoystickButton(timestamp, joystick, (Uint8)i, buttons[i]);
877 }
878 for (i = 0; i < nhats; ++i) {
879 SDL_SendJoystickHat(timestamp, joystick, (Uint8)i, ConvertHatValue(hats[i]));
880 }
881 for (i = 0; i < naxes; ++i) {
882 SDL_SendJoystickAxis(timestamp, joystick, (Uint8)i, (Sint16)((int)(axes[i] * 65535) - 32768));
883 }
884 }
885 }
886
887 SDL_stack_free(buttons);
888 SDL_stack_free(hats);
889 SDL_stack_free(axes);
890
891 if (hwdata->battery) {
892 __x_ABI_CWindows_CDevices_CPower_CIBatteryReport *report = NULL;
893
894 hr = __x_ABI_CWindows_CGaming_CInput_CIGameControllerBatteryInfo_TryGetBatteryReport(hwdata->battery, &report);
895 if (SUCCEEDED(hr) && report) {
896 SDL_PowerState state = SDL_POWERSTATE_UNKNOWN;
897 int percent = 0;
898 __x_ABI_CWindows_CSystem_CPower_CBatteryStatus status;
899 int full_capacity = 0, curr_capacity = 0;
900 __FIReference_1_int *full_capacityP, *curr_capacityP;
901
902 hr = __x_ABI_CWindows_CDevices_CPower_CIBatteryReport_get_Status(report, &status);
903 if (SUCCEEDED(hr)) {
904 switch (status) {
905 case BatteryStatus_NotPresent:
906 state = SDL_POWERSTATE_NO_BATTERY;
907 break;
908 case BatteryStatus_Discharging:
909 state = SDL_POWERSTATE_ON_BATTERY;
910 break;
911 case BatteryStatus_Idle:
912 state = SDL_POWERSTATE_CHARGED;
913 break;
914 case BatteryStatus_Charging:
915 state = SDL_POWERSTATE_CHARGING;
916 break;
917 default:
918 state = SDL_POWERSTATE_UNKNOWN;
919 break;
920 }
921 }
922
923 hr = __x_ABI_CWindows_CDevices_CPower_CIBatteryReport_get_FullChargeCapacityInMilliwattHours(report, &full_capacityP);
924 if (SUCCEEDED(hr)) {
925 __FIReference_1_int_get_Value(full_capacityP, &full_capacity);
926 __FIReference_1_int_Release(full_capacityP);
927 }
928
929 hr = __x_ABI_CWindows_CDevices_CPower_CIBatteryReport_get_RemainingCapacityInMilliwattHours(report, &curr_capacityP);
930 if (SUCCEEDED(hr)) {
931 __FIReference_1_int_get_Value(curr_capacityP, &curr_capacity);
932 __FIReference_1_int_Release(curr_capacityP);
933 }
934
935 if (full_capacity > 0) {
936 percent = (int)SDL_roundf(((float)curr_capacity / full_capacity) * 100.0f);
937 }
938
939 SDL_SendJoystickPowerInfo(joystick, state, percent);
940
941 __x_ABI_CWindows_CDevices_CPower_CIBatteryReport_Release(report);
942 }
943 }
944}
945
946static void WGI_JoystickClose(SDL_Joystick *joystick)
947{
948 struct joystick_hwdata *hwdata = joystick->hwdata;
949
950 if (hwdata) {
951 if (hwdata->controller) {
952 __x_ABI_CWindows_CGaming_CInput_CIRawGameController_Release(hwdata->controller);
953 }
954 if (hwdata->game_controller) {
955 __x_ABI_CWindows_CGaming_CInput_CIGameController_Release(hwdata->game_controller);
956 }
957 if (hwdata->battery) {
958 __x_ABI_CWindows_CGaming_CInput_CIGameControllerBatteryInfo_Release(hwdata->battery);
959 }
960 if (hwdata->gamepad) {
961 __x_ABI_CWindows_CGaming_CInput_CIGamepad_Release(hwdata->gamepad);
962 }
963 SDL_free(hwdata);
964 }
965 joystick->hwdata = NULL;
966}
967
968static void WGI_JoystickQuit(void)
969{
970 if (wgi.controller_statics) {
971 while (wgi.controller_count > 0) {
972 IEventHandler_CRawGameControllerVtbl_InvokeRemoved(&controller_removed.iface, NULL, wgi.controllers[wgi.controller_count - 1].controller);
973 }
974 if (wgi.controllers) {
975 SDL_free(wgi.controllers);
976 }
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 WIN_RoUninitialize();
1006
1007 SDL_zero(wgi);
1008}
1009
1010static bool WGI_JoystickGetGamepadMapping(int device_index, SDL_GamepadMapping *out)
1011{
1012 return false;
1013}
1014
1015SDL_JoystickDriver SDL_WGI_JoystickDriver = {
1016 WGI_JoystickInit,
1017 WGI_JoystickGetCount,
1018 WGI_JoystickDetect,
1019 WGI_JoystickIsDevicePresent,
1020 WGI_JoystickGetDeviceName,
1021 WGI_JoystickGetDevicePath,
1022 WGI_JoystickGetDeviceSteamVirtualGamepadSlot,
1023 WGI_JoystickGetDevicePlayerIndex,
1024 WGI_JoystickSetDevicePlayerIndex,
1025 WGI_JoystickGetDeviceGUID,
1026 WGI_JoystickGetDeviceInstanceID,
1027 WGI_JoystickOpen,
1028 WGI_JoystickRumble,
1029 WGI_JoystickRumbleTriggers,
1030 WGI_JoystickSetLED,
1031 WGI_JoystickSendEffect,
1032 WGI_JoystickSetSensorsEnabled,
1033 WGI_JoystickUpdate,
1034 WGI_JoystickClose,
1035 WGI_JoystickQuit,
1036 WGI_JoystickGetGamepadMapping
1037};
1038
1039#endif // SDL_JOYSTICK_WGI