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
9905 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
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
93
DEFINE_GUID(IID___FIEventHandler_1_Windows__CGaming__CInput__CRawGameController, 0x00621c22, 0x42e8, 0x529f, 0x92, 0x70, 0x83, 0x6b, 0x32, 0x93, 0x1d, 0x72);
94
DEFINE_GUID(IID___x_ABI_CWindows_CGaming_CInput_CIArcadeStickStatics, 0x5c37b8c8, 0x37b1, 0x4ad8, 0x94, 0x58, 0x20, 0x0f, 0x1a, 0x30, 0x01, 0x8e);
95
DEFINE_GUID(IID___x_ABI_CWindows_CGaming_CInput_CIArcadeStickStatics2, 0x52b5d744, 0xbb86, 0x445a, 0xb5, 0x9c, 0x59, 0x6f, 0x0e, 0x2a, 0x49, 0xdf);
96
DEFINE_GUID(IID___x_ABI_CWindows_CGaming_CInput_CIFlightStickStatics, 0x5514924a, 0xfecc, 0x435e, 0x83, 0xdc, 0x5c, 0xec, 0x8a, 0x18, 0xa5, 0x20);
97
DEFINE_GUID(IID___x_ABI_CWindows_CGaming_CInput_CIGameController, 0x1baf6522, 0x5f64, 0x42c5, 0x82, 0x67, 0xb9, 0xfe, 0x22, 0x15, 0xbf, 0xbd);
98
DEFINE_GUID(IID___x_ABI_CWindows_CGaming_CInput_CIGameControllerBatteryInfo, 0xdcecc681, 0x3963, 0x4da6, 0x95, 0x5d, 0x55, 0x3f, 0x3b, 0x6f, 0x61, 0x61);
99
DEFINE_GUID(IID___x_ABI_CWindows_CGaming_CInput_CIGamepadStatics, 0x8bbce529, 0xd49c, 0x39e9, 0x95, 0x60, 0xe4, 0x7d, 0xde, 0x96, 0xb7, 0xc8);
100
DEFINE_GUID(IID___x_ABI_CWindows_CGaming_CInput_CIGamepadStatics2, 0x42676dc5, 0x0856, 0x47c4, 0x92, 0x13, 0xb3, 0x95, 0x50, 0x4c, 0x3a, 0x3c);
101
DEFINE_GUID(IID___x_ABI_CWindows_CGaming_CInput_CIRacingWheelStatics, 0x3ac12cd5, 0x581b, 0x4936, 0x9f, 0x94, 0x69, 0xf1, 0xe6, 0x51, 0x4c, 0x7d);
102
DEFINE_GUID(IID___x_ABI_CWindows_CGaming_CInput_CIRacingWheelStatics2, 0xe666bcaa, 0xedfd, 0x4323, 0xa9, 0xf6, 0x3c, 0x38, 0x40, 0x48, 0xd1, 0xed);
103
DEFINE_GUID(IID___x_ABI_CWindows_CGaming_CInput_CIRawGameController, 0x7cad6d91, 0xa7e1, 0x4f71, 0x9a, 0x78, 0x33, 0xe9, 0xc5, 0xdf, 0xea, 0x62);
104
DEFINE_GUID(IID___x_ABI_CWindows_CGaming_CInput_CIRawGameController2, 0x43c0c035, 0xbb73, 0x4756, 0xa7, 0x87, 0x3e, 0xd6, 0xbe, 0xa6, 0x17, 0xbd);
105
DEFINE_GUID(IID___x_ABI_CWindows_CGaming_CInput_CIRawGameControllerStatics, 0xeb8d0792, 0xe95a, 0x4b19, 0xaf, 0xc7, 0x0a, 0x59, 0xf8, 0xbf, 0x75, 0x9e);
106
107
extern bool SDL_XINPUT_Enabled(void);
108
109
110
static 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
222
static 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
237
static 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
290
static 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
325
typedef struct RawGameControllerDelegate
326
{
327
__FIEventHandler_1_Windows__CGaming__CInput__CRawGameController iface;
328
SDL_AtomicInt refcount;
329
} RawGameControllerDelegate;
330
331
static 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
350
static 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
356
static 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
365
static 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
393
static 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
508
static 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
558
static __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
};
564
static RawGameControllerDelegate controller_added = {
565
{ &controller_added_vtbl },
566
{ 1 }
567
};
568
569
static __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
};
575
static RawGameControllerDelegate controller_removed = {
576
{ &controller_removed_vtbl },
577
{ 1 }
578
};
579
580
#ifdef _MSC_VER
581
#pragma warning(pop)
582
#endif
583
584
static 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
658
static int WGI_JoystickGetCount(void)
659
{
660
return wgi.controller_count;
661
}
662
663
static void WGI_JoystickDetect(void)
664
{
665
}
666
667
static 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
673
static const char *WGI_JoystickGetDeviceName(int device_index)
674
{
675
return wgi.controllers[device_index].name;
676
}
677
678
static const char *WGI_JoystickGetDevicePath(int device_index)
679
{
680
return NULL;
681
}
682
683
static int WGI_JoystickGetDeviceSteamVirtualGamepadSlot(int device_index)
684
{
685
return wgi.controllers[device_index].steam_virtual_gamepad_slot;
686
}
687
688
static int WGI_JoystickGetDevicePlayerIndex(int device_index)
689
{
690
return false;
691
}
692
693
static void WGI_JoystickSetDevicePlayerIndex(int device_index, int player_index)
694
{
695
}
696
697
static SDL_GUID WGI_JoystickGetDeviceGUID(int device_index)
698
{
699
return wgi.controllers[device_index].guid;
700
}
701
702
static SDL_JoystickID WGI_JoystickGetDeviceInstanceID(int device_index)
703
{
704
return wgi.controllers[device_index].instance_id;
705
}
706
707
static 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
750
static 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
771
static 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
792
static bool WGI_JoystickSetLED(SDL_Joystick *joystick, Uint8 red, Uint8 green, Uint8 blue)
793
{
794
return SDL_Unsupported();
795
}
796
797
static bool WGI_JoystickSendEffect(SDL_Joystick *joystick, const void *data, int size)
798
{
799
return SDL_Unsupported();
800
}
801
802
static bool WGI_JoystickSetSensorsEnabled(SDL_Joystick *joystick, bool enabled)
803
{
804
return SDL_Unsupported();
805
}
806
807
static 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
831
static 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
946
static 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
968
static 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
1010
static bool WGI_JoystickGetGamepadMapping(int device_index, SDL_GamepadMapping *out)
1011
{
1012
return false;
1013
}
1014
1015
SDL_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
1040
1041