Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
godotengine
GitHub Repository: godotengine/godot
Path: blob/master/thirdparty/sdl/joystick/windows/SDL_windows_gaming_input.c
21892 views
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
44
struct 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
54
typedef 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
64
typedef HRESULT(WINAPI *CoIncrementMTAUsage_t)(CO_MTA_USAGE_COOKIE *pCookie);
65
typedef HRESULT(WINAPI *RoGetActivationFactory_t)(HSTRING activatableClassId, REFIID iid, void **factory);
66
typedef HRESULT(WINAPI *WindowsCreateStringReference_t)(PCWSTR sourceString, UINT32 length, HSTRING_HEADER *hstringHeader, HSTRING *string);
67
typedef HRESULT(WINAPI *WindowsDeleteString_t)(HSTRING string);
68
typedef PCWSTR(WINAPI *WindowsGetStringRawBuffer_t)(HSTRING string, UINT32 *length);
69
70
static 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
94
DEFINE_GUID(IID___FIEventHandler_1_Windows__CGaming__CInput__CRawGameController, 0x00621c22, 0x42e8, 0x529f, 0x92, 0x70, 0x83, 0x6b, 0x32, 0x93, 0x1d, 0x72);
95
DEFINE_GUID(IID___x_ABI_CWindows_CGaming_CInput_CIArcadeStickStatics, 0x5c37b8c8, 0x37b1, 0x4ad8, 0x94, 0x58, 0x20, 0x0f, 0x1a, 0x30, 0x01, 0x8e);
96
DEFINE_GUID(IID___x_ABI_CWindows_CGaming_CInput_CIArcadeStickStatics2, 0x52b5d744, 0xbb86, 0x445a, 0xb5, 0x9c, 0x59, 0x6f, 0x0e, 0x2a, 0x49, 0xdf);
97
DEFINE_GUID(IID___x_ABI_CWindows_CGaming_CInput_CIFlightStickStatics, 0x5514924a, 0xfecc, 0x435e, 0x83, 0xdc, 0x5c, 0xec, 0x8a, 0x18, 0xa5, 0x20);
98
DEFINE_GUID(IID___x_ABI_CWindows_CGaming_CInput_CIGameController, 0x1baf6522, 0x5f64, 0x42c5, 0x82, 0x67, 0xb9, 0xfe, 0x22, 0x15, 0xbf, 0xbd);
99
DEFINE_GUID(IID___x_ABI_CWindows_CGaming_CInput_CIGameControllerBatteryInfo, 0xdcecc681, 0x3963, 0x4da6, 0x95, 0x5d, 0x55, 0x3f, 0x3b, 0x6f, 0x61, 0x61);
100
DEFINE_GUID(IID___x_ABI_CWindows_CGaming_CInput_CIGamepadStatics, 0x8bbce529, 0xd49c, 0x39e9, 0x95, 0x60, 0xe4, 0x7d, 0xde, 0x96, 0xb7, 0xc8);
101
DEFINE_GUID(IID___x_ABI_CWindows_CGaming_CInput_CIGamepadStatics2, 0x42676dc5, 0x0856, 0x47c4, 0x92, 0x13, 0xb3, 0x95, 0x50, 0x4c, 0x3a, 0x3c);
102
DEFINE_GUID(IID___x_ABI_CWindows_CGaming_CInput_CIRacingWheelStatics, 0x3ac12cd5, 0x581b, 0x4936, 0x9f, 0x94, 0x69, 0xf1, 0xe6, 0x51, 0x4c, 0x7d);
103
DEFINE_GUID(IID___x_ABI_CWindows_CGaming_CInput_CIRacingWheelStatics2, 0xe666bcaa, 0xedfd, 0x4323, 0xa9, 0xf6, 0x3c, 0x38, 0x40, 0x48, 0xd1, 0xed);
104
DEFINE_GUID(IID___x_ABI_CWindows_CGaming_CInput_CIRawGameController, 0x7cad6d91, 0xa7e1, 0x4f71, 0x9a, 0x78, 0x33, 0xe9, 0xc5, 0xdf, 0xea, 0x62);
105
DEFINE_GUID(IID___x_ABI_CWindows_CGaming_CInput_CIRawGameController2, 0x43c0c035, 0xbb73, 0x4756, 0xa7, 0x87, 0x3e, 0xd6, 0xbe, 0xa6, 0x17, 0xbd);
106
DEFINE_GUID(IID___x_ABI_CWindows_CGaming_CInput_CIRawGameControllerStatics, 0xeb8d0792, 0xe95a, 0x4b19, 0xaf, 0xc7, 0x0a, 0x59, 0xf8, 0xbf, 0x75, 0x9e);
107
108
extern bool SDL_XINPUT_Enabled(void);
109
110
111
static 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
223
static 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
238
static 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
291
static 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
326
typedef struct RawGameControllerDelegate
327
{
328
__FIEventHandler_1_Windows__CGaming__CInput__CRawGameController iface;
329
SDL_AtomicInt refcount;
330
} RawGameControllerDelegate;
331
332
static 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
351
static 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
357
static 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
366
static 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
394
static 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
509
static 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
559
static __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
};
565
static RawGameControllerDelegate controller_added = {
566
{ &controller_added_vtbl },
567
{ 1 }
568
};
569
570
static __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
};
576
static RawGameControllerDelegate controller_removed = {
577
{ &controller_removed_vtbl },
578
{ 1 }
579
};
580
581
#ifdef _MSC_VER
582
#pragma warning(pop)
583
#endif
584
585
static 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
660
static int WGI_JoystickGetCount(void)
661
{
662
return wgi.controller_count;
663
}
664
665
static void WGI_JoystickDetect(void)
666
{
667
}
668
669
static 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
675
static const char *WGI_JoystickGetDeviceName(int device_index)
676
{
677
return wgi.controllers[device_index].name;
678
}
679
680
static const char *WGI_JoystickGetDevicePath(int device_index)
681
{
682
return NULL;
683
}
684
685
static int WGI_JoystickGetDeviceSteamVirtualGamepadSlot(int device_index)
686
{
687
return wgi.controllers[device_index].steam_virtual_gamepad_slot;
688
}
689
690
static int WGI_JoystickGetDevicePlayerIndex(int device_index)
691
{
692
return false;
693
}
694
695
static void WGI_JoystickSetDevicePlayerIndex(int device_index, int player_index)
696
{
697
}
698
699
static SDL_GUID WGI_JoystickGetDeviceGUID(int device_index)
700
{
701
return wgi.controllers[device_index].guid;
702
}
703
704
static SDL_JoystickID WGI_JoystickGetDeviceInstanceID(int device_index)
705
{
706
return wgi.controllers[device_index].instance_id;
707
}
708
709
static 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
752
static 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
773
static 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
794
static bool WGI_JoystickSetLED(SDL_Joystick *joystick, Uint8 red, Uint8 green, Uint8 blue)
795
{
796
return SDL_Unsupported();
797
}
798
799
static bool WGI_JoystickSendEffect(SDL_Joystick *joystick, const void *data, int size)
800
{
801
return SDL_Unsupported();
802
}
803
804
static bool WGI_JoystickSetSensorsEnabled(SDL_Joystick *joystick, bool enabled)
805
{
806
return SDL_Unsupported();
807
}
808
809
static 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
833
static 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
948
static 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
970
static 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
if (wgi.controllers) {
977
SDL_free(wgi.controllers);
978
}
979
980
if (wgi.arcade_stick_statics) {
981
__x_ABI_CWindows_CGaming_CInput_CIArcadeStickStatics_Release(wgi.arcade_stick_statics);
982
}
983
if (wgi.arcade_stick_statics2) {
984
__x_ABI_CWindows_CGaming_CInput_CIArcadeStickStatics2_Release(wgi.arcade_stick_statics2);
985
}
986
if (wgi.flight_stick_statics) {
987
__x_ABI_CWindows_CGaming_CInput_CIFlightStickStatics_Release(wgi.flight_stick_statics);
988
}
989
if (wgi.gamepad_statics) {
990
__x_ABI_CWindows_CGaming_CInput_CIGamepadStatics_Release(wgi.gamepad_statics);
991
}
992
if (wgi.gamepad_statics2) {
993
__x_ABI_CWindows_CGaming_CInput_CIGamepadStatics2_Release(wgi.gamepad_statics2);
994
}
995
if (wgi.racing_wheel_statics) {
996
__x_ABI_CWindows_CGaming_CInput_CIRacingWheelStatics_Release(wgi.racing_wheel_statics);
997
}
998
if (wgi.racing_wheel_statics2) {
999
__x_ABI_CWindows_CGaming_CInput_CIRacingWheelStatics2_Release(wgi.racing_wheel_statics2);
1000
}
1001
1002
__x_ABI_CWindows_CGaming_CInput_CIRawGameControllerStatics_remove_RawGameControllerAdded(wgi.controller_statics, wgi.controller_added_token);
1003
__x_ABI_CWindows_CGaming_CInput_CIRawGameControllerStatics_remove_RawGameControllerRemoved(wgi.controller_statics, wgi.controller_removed_token);
1004
__x_ABI_CWindows_CGaming_CInput_CIRawGameControllerStatics_Release(wgi.controller_statics);
1005
}
1006
1007
if (wgi.ro_initialized) {
1008
WIN_RoUninitialize();
1009
}
1010
1011
SDL_zero(wgi);
1012
}
1013
1014
static bool WGI_JoystickGetGamepadMapping(int device_index, SDL_GamepadMapping *out)
1015
{
1016
return false;
1017
}
1018
1019
SDL_JoystickDriver SDL_WGI_JoystickDriver = {
1020
WGI_JoystickInit,
1021
WGI_JoystickGetCount,
1022
WGI_JoystickDetect,
1023
WGI_JoystickIsDevicePresent,
1024
WGI_JoystickGetDeviceName,
1025
WGI_JoystickGetDevicePath,
1026
WGI_JoystickGetDeviceSteamVirtualGamepadSlot,
1027
WGI_JoystickGetDevicePlayerIndex,
1028
WGI_JoystickSetDevicePlayerIndex,
1029
WGI_JoystickGetDeviceGUID,
1030
WGI_JoystickGetDeviceInstanceID,
1031
WGI_JoystickOpen,
1032
WGI_JoystickRumble,
1033
WGI_JoystickRumbleTriggers,
1034
WGI_JoystickSetLED,
1035
WGI_JoystickSendEffect,
1036
WGI_JoystickSetSensorsEnabled,
1037
WGI_JoystickUpdate,
1038
WGI_JoystickClose,
1039
WGI_JoystickQuit,
1040
WGI_JoystickGetGamepadMapping
1041
};
1042
1043
#endif // SDL_JOYSTICK_WGI
1044
1045