/
SDL_hidapi_xbox360.c
1317 lines (1182 loc) · 56.8 KB
1
2
/*
Simple DirectMedia Layer
3
Copyright (C) 1997-2020 Sam Lantinga <slouken@libsdl.org>
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "../../SDL_internal.h"
#ifdef SDL_JOYSTICK_HIDAPI
#include "SDL_hints.h"
#include "SDL_events.h"
#include "SDL_timer.h"
#include "SDL_joystick.h"
#include "SDL_gamecontroller.h"
#include "../SDL_sysjoystick.h"
#include "SDL_hidapijoystick_c.h"
32
#include "SDL_hidapi_rumble.h"
33
34
35
36
#ifdef SDL_JOYSTICK_HIDAPI_XBOX360
37
#ifdef __WIN32__
38
39
40
41
42
43
#define SDL_JOYSTICK_HIDAPI_WINDOWS_XINPUT
/* This requires the Windows 10 SDK to build */
/*#define SDL_JOYSTICK_HIDAPI_WINDOWS_GAMING_INPUT*/
#endif
#ifdef SDL_JOYSTICK_HIDAPI_WINDOWS_XINPUT
44
45
46
#include "../../core/windows/SDL_xinput.h"
#endif
47
48
#ifdef SDL_JOYSTICK_HIDAPI_WINDOWS_GAMING_INPUT
#include "../../core/windows/SDL_windows.h"
49
50
typedef struct WindowsGamingInputGamepadState WindowsGamingInputGamepadState;
#define GamepadButtons_GUIDE 0x40000000
51
52
53
54
#define COBJMACROS
#include "windows.gaming.input.h"
#endif
55
56
57
#if defined(SDL_JOYSTICK_HIDAPI_WINDOWS_XINPUT) || defined(SDL_JOYSTICK_HIDAPI_WINDOWS_GAMING_INPUT)
#define SDL_JOYSTICK_HIDAPI_WINDOWS_MATCHING
#endif
58
59
60
typedef struct {
Uint8 last_state[USB_PACKET_LENGTH];
61
62
63
64
#ifdef SDL_JOYSTICK_HIDAPI_WINDOWS_MATCHING
Uint32 match_state; /* Low 16 bits for button states, high 16 for 4 4bit axes */
Uint32 last_state_packet;
#endif
65
#ifdef SDL_JOYSTICK_HIDAPI_WINDOWS_XINPUT
66
SDL_bool xinput_enabled;
67
68
69
70
SDL_bool xinput_correlated;
Uint8 xinput_correlation_id;
Uint8 xinput_correlation_count;
Uint8 xinput_uncorrelate_count;
71
72
Uint8 xinput_slot;
#endif
73
#ifdef SDL_JOYSTICK_HIDAPI_WINDOWS_GAMING_INPUT
74
75
76
77
78
SDL_bool wgi_correlated;
Uint8 wgi_correlation_id;
Uint8 wgi_correlation_count;
Uint8 wgi_uncorrelate_count;
WindowsGamingInputGamepadState *wgi_slot;
79
#endif
80
81
} SDL_DriverXbox360_Context;
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
#ifdef SDL_JOYSTICK_HIDAPI_WINDOWS_MATCHING
static struct {
Uint32 last_state_packet;
SDL_Joystick *joystick;
SDL_Joystick *last_joystick;
} guide_button_candidate;
typedef struct WindowsMatchState {
SHORT match_axes[4];
#ifdef SDL_JOYSTICK_HIDAPI_WINDOWS_XINPUT
WORD xinput_buttons;
#endif
#ifdef SDL_JOYSTICK_HIDAPI_WINDOWS_GAMING_INPUT
Uint32 wgi_buttons;
#endif
SDL_bool any_data;
} WindowsMatchState;
static void HIDAPI_DriverXbox360_FillMatchState(WindowsMatchState *state, Uint32 match_state)
{
int ii;
state->any_data = SDL_FALSE;
/* SHORT state->match_axes[4] = {
(match_state & 0x000F0000) >> 4,
(match_state & 0x00F00000) >> 8,
(match_state & 0x0F000000) >> 12,
(match_state & 0xF0000000) >> 16,
}; */
for (ii = 0; ii < 4; ii++) {
state->match_axes[ii] = (match_state & (0x000F0000 << (ii * 4))) >> (4 + ii * 4);
if ((Uint32)(state->match_axes[ii] + 0x1000) > 0x2000) { /* match_state bit is not 0xF, 0x1, or 0x2 */
state->any_data = SDL_TRUE;
}
}
#ifdef SDL_JOYSTICK_HIDAPI_WINDOWS_XINPUT
/* Match axes by checking if the distance between the high 4 bits of axis and the 4 bits from match_state is 1 or less */
#define XInputAxesMatch(gamepad) (\
(Uint32)(gamepad.sThumbLX - state->match_axes[0] + 0x1000) <= 0x2fff && \
(Uint32)(~gamepad.sThumbLY - state->match_axes[1] + 0x1000) <= 0x2fff && \
(Uint32)(gamepad.sThumbRX - state->match_axes[2] + 0x1000) <= 0x2fff && \
(Uint32)(~gamepad.sThumbRY - state->match_axes[3] + 0x1000) <= 0x2fff)
/* Explicit
#define XInputAxesMatch(gamepad) (\
SDL_abs((Sint8)((gamepad.sThumbLX & 0xF000) >> 8) - ((match_state & 0x000F0000) >> 12)) <= 0x10 && \
SDL_abs((Sint8)((~gamepad.sThumbLY & 0xF000) >> 8) - ((match_state & 0x00F00000) >> 16)) <= 0x10 && \
SDL_abs((Sint8)((gamepad.sThumbRX & 0xF000) >> 8) - ((match_state & 0x0F000000) >> 20)) <= 0x10 && \
SDL_abs((Sint8)((~gamepad.sThumbRY & 0xF000) >> 8) - ((match_state & 0xF0000000) >> 24)) <= 0x10) */
state->xinput_buttons =
/* Bitwise map .RLDUWVQTS.KYXBA -> YXBA..WVQTKSRLDU */
match_state << 12 | (match_state & 0x0780) >> 1 | (match_state & 0x0010) << 1 | (match_state & 0x0040) >> 2 | (match_state & 0x7800) >> 11;
/* Explicit
((match_state & (1<<SDL_CONTROLLER_BUTTON_A)) ? XINPUT_GAMEPAD_A : 0) |
((match_state & (1<<SDL_CONTROLLER_BUTTON_B)) ? XINPUT_GAMEPAD_B : 0) |
((match_state & (1<<SDL_CONTROLLER_BUTTON_X)) ? XINPUT_GAMEPAD_X : 0) |
((match_state & (1<<SDL_CONTROLLER_BUTTON_Y)) ? XINPUT_GAMEPAD_Y : 0) |
((match_state & (1<<SDL_CONTROLLER_BUTTON_BACK)) ? XINPUT_GAMEPAD_BACK : 0) |
((match_state & (1<<SDL_CONTROLLER_BUTTON_START)) ? XINPUT_GAMEPAD_START : 0) |
((match_state & (1<<SDL_CONTROLLER_BUTTON_LEFTSTICK)) ? XINPUT_GAMEPAD_LEFT_THUMB : 0) |
((match_state & (1<<SDL_CONTROLLER_BUTTON_RIGHTSTICK)) ? XINPUT_GAMEPAD_RIGHT_THUMB: 0) |
((match_state & (1<<SDL_CONTROLLER_BUTTON_LEFTSHOULDER)) ? XINPUT_GAMEPAD_LEFT_SHOULDER : 0) |
((match_state & (1<<SDL_CONTROLLER_BUTTON_RIGHTSHOULDER)) ? XINPUT_GAMEPAD_RIGHT_SHOULDER : 0) |
((match_state & (1<<SDL_CONTROLLER_BUTTON_DPAD_UP)) ? XINPUT_GAMEPAD_DPAD_UP : 0) |
((match_state & (1<<SDL_CONTROLLER_BUTTON_DPAD_DOWN)) ? XINPUT_GAMEPAD_DPAD_DOWN : 0) |
((match_state & (1<<SDL_CONTROLLER_BUTTON_DPAD_LEFT)) ? XINPUT_GAMEPAD_DPAD_LEFT : 0) |
((match_state & (1<<SDL_CONTROLLER_BUTTON_DPAD_RIGHT)) ? XINPUT_GAMEPAD_DPAD_RIGHT : 0);
*/
if (state->xinput_buttons)
state->any_data = SDL_TRUE;
#endif
#ifdef SDL_JOYSTICK_HIDAPI_WINDOWS_GAMING_INPUT
/* Match axes by checking if the distance between the high 4 bits of axis and the 4 bits from match_state is 1 or less */
#define WindowsGamingInputAxesMatch(gamepad) (\
(Uint16)(((Sint16)(gamepad.LeftThumbstickX * SDL_MAX_SINT16) & 0xF000) - state->match_axes[0] + 0x1000) <= 0x2fff && \
(Uint16)((~(Sint16)(gamepad.LeftThumbstickY * SDL_MAX_SINT16) & 0xF000) - state->match_axes[1] + 0x1000) <= 0x2fff && \
(Uint16)(((Sint16)(gamepad.RightThumbstickX * SDL_MAX_SINT16) & 0xF000) - state->match_axes[2] + 0x1000) <= 0x2fff && \
(Uint16)((~(Sint16)(gamepad.RightThumbstickY * SDL_MAX_SINT16) & 0xF000) - state->match_axes[3] + 0x1000) <= 0x2fff)
state->wgi_buttons =
/* Bitwise map .RLD UWVQ TS.K YXBA -> ..QT WVRL DUYX BAKS */
/* RStick/LStick (QT) RShould/LShould (WV) DPad R/L/D/U YXBA bac(K) (S)tart */
(match_state & 0x0180) << 5 | (match_state & 0x0600) << 1 | (match_state & 0x7800) >> 5 | (match_state & 0x000F) << 2 | (match_state & 0x0010) >> 3 | (match_state & 0x0040) >> 6;
/* Explicit
((match_state & (1<<SDL_CONTROLLER_BUTTON_A)) ? GamepadButtons_A : 0) |
((match_state & (1<<SDL_CONTROLLER_BUTTON_B)) ? GamepadButtons_B : 0) |
((match_state & (1<<SDL_CONTROLLER_BUTTON_X)) ? GamepadButtons_X : 0) |
((match_state & (1<<SDL_CONTROLLER_BUTTON_Y)) ? GamepadButtons_Y : 0) |
((match_state & (1<<SDL_CONTROLLER_BUTTON_BACK)) ? GamepadButtons_View : 0) |
((match_state & (1<<SDL_CONTROLLER_BUTTON_START)) ? GamepadButtons_Menu : 0) |
((match_state & (1<<SDL_CONTROLLER_BUTTON_LEFTSTICK)) ? GamepadButtons_LeftThumbstick : 0) |
((match_state & (1<<SDL_CONTROLLER_BUTTON_RIGHTSTICK)) ? GamepadButtons_RightThumbstick: 0) |
((match_state & (1<<SDL_CONTROLLER_BUTTON_LEFTSHOULDER)) ? GamepadButtons_LeftShoulder: 0) |
((match_state & (1<<SDL_CONTROLLER_BUTTON_RIGHTSHOULDER)) ? GamepadButtons_RightShoulder: 0) |
((match_state & (1<<SDL_CONTROLLER_BUTTON_DPAD_UP)) ? GamepadButtons_DPadUp : 0) |
((match_state & (1<<SDL_CONTROLLER_BUTTON_DPAD_DOWN)) ? GamepadButtons_DPadDown : 0) |
((match_state & (1<<SDL_CONTROLLER_BUTTON_DPAD_LEFT)) ? GamepadButtons_DPadLeft : 0) |
((match_state & (1<<SDL_CONTROLLER_BUTTON_DPAD_RIGHT)) ? GamepadButtons_DPadRight : 0); */
if (state->wgi_buttons)
state->any_data = SDL_TRUE;
#endif
}
#endif
193
194
#ifdef SDL_JOYSTICK_HIDAPI_WINDOWS_XINPUT
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
static struct {
XINPUT_STATE_EX state;
SDL_bool connected; /* Currently has an active XInput device */
SDL_bool used; /* Is currently mapped to an SDL device */
Uint8 correlation_id;
} xinput_state[XUSER_MAX_COUNT];
static SDL_bool xinput_device_change = SDL_TRUE;
static SDL_bool xinput_state_dirty = SDL_TRUE;
static void
HIDAPI_DriverXbox360_UpdateXInput()
{
DWORD user_index;
if (xinput_device_change) {
for (user_index = 0; user_index < XUSER_MAX_COUNT; user_index++) {
XINPUT_CAPABILITIES capabilities;
xinput_state[user_index].connected = XINPUTGETCAPABILITIES(user_index, XINPUT_FLAG_GAMEPAD, &capabilities) == ERROR_SUCCESS;
}
xinput_device_change = SDL_FALSE;
xinput_state_dirty = SDL_TRUE;
}
if (xinput_state_dirty) {
xinput_state_dirty = SDL_FALSE;
for (user_index = 0; user_index < SDL_arraysize(xinput_state); ++user_index) {
if (xinput_state[user_index].connected) {
220
if (XINPUTGETSTATE(user_index, &xinput_state[user_index].state) != ERROR_SUCCESS) {
221
222
223
224
225
226
xinput_state[user_index].connected = SDL_FALSE;
}
}
}
}
}
227
228
229
230
231
static void
HIDAPI_DriverXbox360_MarkXInputSlotUsed(Uint8 xinput_slot)
{
if (xinput_slot != XUSER_INDEX_ANY) {
232
xinput_state[xinput_slot].used = SDL_TRUE;
233
234
235
236
237
238
239
}
}
static void
HIDAPI_DriverXbox360_MarkXInputSlotFree(Uint8 xinput_slot)
{
if (xinput_slot != XUSER_INDEX_ANY) {
240
xinput_state[xinput_slot].used = SDL_FALSE;
241
242
243
244
245
}
}
static SDL_bool
HIDAPI_DriverXbox360_MissingXInputSlot()
{
246
247
248
249
250
251
252
int ii;
for (ii = 0; ii < SDL_arraysize(xinput_state); ii++) {
if (xinput_state[ii].connected && !xinput_state[ii].used) {
return SDL_TRUE;
}
}
return SDL_FALSE;
253
254
}
255
256
static SDL_bool
HIDAPI_DriverXbox360_XInputSlotMatches(const WindowsMatchState *state, Uint8 slot_idx)
257
{
258
259
260
261
262
if (xinput_state[slot_idx].connected) {
WORD xinput_buttons = xinput_state[slot_idx].state.Gamepad.wButtons;
if ((xinput_buttons & ~XINPUT_GAMEPAD_GUIDE) == state->xinput_buttons && XInputAxesMatch(xinput_state[slot_idx].state.Gamepad)) {
return SDL_TRUE;
}
263
}
264
265
266
267
268
269
270
271
272
return SDL_FALSE;
}
static SDL_bool
HIDAPI_DriverXbox360_GuessXInputSlot(const WindowsMatchState *state, Uint8 *correlation_id, Uint8 *slot_idx)
{
int user_index;
int match_count;
273
274
275
*slot_idx = 0;
276
277
match_count = 0;
for (user_index = 0; user_index < XUSER_MAX_COUNT; ++user_index) {
278
279
280
281
282
if (!xinput_state[user_index].used && HIDAPI_DriverXbox360_XInputSlotMatches(state, user_index)) {
++match_count;
*slot_idx = (Uint8)user_index;
/* Incrementing correlation_id for any match, as negative evidence for others being correlated */
*correlation_id = ++xinput_state[user_index].correlation_id;
283
284
}
}
285
286
287
288
289
/* Only return a match if we match exactly one, and we have some non-zero data (buttons or axes) that matched.
Note that we're still invalidating *other* potential correlations if we have more than one match or we have no
data. */
if (match_count == 1 && state->any_data) {
return SDL_TRUE;
290
}
291
return SDL_FALSE;
292
293
}
294
295
296
297
#endif /* SDL_JOYSTICK_HIDAPI_WINDOWS_XINPUT */
#ifdef SDL_JOYSTICK_HIDAPI_WINDOWS_GAMING_INPUT
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
typedef struct WindowsGamingInputGamepadState {
__x_ABI_CWindows_CGaming_CInput_CIGamepad *gamepad;
struct __x_ABI_CWindows_CGaming_CInput_CGamepadReading state;
SDL_DriverXbox360_Context *correlated_context;
SDL_bool used; /* Is currently mapped to an SDL device */
SDL_bool connected; /* Just used during update to track disconnected */
Uint8 correlation_id;
struct __x_ABI_CWindows_CGaming_CInput_CGamepadVibration vibration;
} WindowsGamingInputGamepadState;
static struct {
WindowsGamingInputGamepadState **per_gamepad;
int per_gamepad_count;
SDL_bool initialized;
SDL_bool dirty;
SDL_bool need_device_list_update;
int ref_count;
__x_ABI_CWindows_CGaming_CInput_CIGamepadStatics *gamepad_statics;
} wgi_state;
318
static void
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
HIDAPI_DriverXbox360_MarkWindowsGamingInputSlotUsed(WindowsGamingInputGamepadState *wgi_slot, SDL_DriverXbox360_Context *ctx)
{
wgi_slot->used = SDL_TRUE;
wgi_slot->correlated_context = ctx;
}
static void
HIDAPI_DriverXbox360_MarkWindowsGamingInputSlotFree(WindowsGamingInputGamepadState *wgi_slot)
{
wgi_slot->used = SDL_FALSE;
wgi_slot->correlated_context = NULL;
}
static SDL_bool
HIDAPI_DriverXbox360_MissingWindowsGamingInputSlot()
{
int ii;
for (ii = 0; ii < wgi_state.per_gamepad_count; ii++) {
if (!wgi_state.per_gamepad[ii]->used) {
return SDL_TRUE;
}
}
return SDL_FALSE;
}
static void
HIDAPI_DriverXbox360_UpdateWindowsGamingInput()
346
{
347
348
349
350
351
int ii;
if (!wgi_state.gamepad_statics)
return;
if (!wgi_state.dirty)
352
return;
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
wgi_state.dirty = SDL_FALSE;
if (wgi_state.need_device_list_update) {
wgi_state.need_device_list_update = SDL_FALSE;
for (ii = 0; ii < wgi_state.per_gamepad_count; ii++) {
wgi_state.per_gamepad[ii]->connected = SDL_FALSE;
}
HRESULT hr;
__FIVectorView_1_Windows__CGaming__CInput__CGamepad *gamepads;
hr = __x_ABI_CWindows_CGaming_CInput_CIGamepadStatics_get_Gamepads(wgi_state.gamepad_statics, &gamepads);
if (SUCCEEDED(hr)) {
unsigned int num_gamepads;
hr = __FIVectorView_1_Windows__CGaming__CInput__CGamepad_get_Size(gamepads, &num_gamepads);
if (SUCCEEDED(hr)) {
unsigned int i;
for (i = 0; i < num_gamepads; ++i) {
__x_ABI_CWindows_CGaming_CInput_CIGamepad *gamepad;
hr = __FIVectorView_1_Windows__CGaming__CInput__CGamepad_GetAt(gamepads, i, &gamepad);
if (SUCCEEDED(hr)) {
SDL_bool found = SDL_FALSE;
int jj;
for (jj = 0; jj < wgi_state.per_gamepad_count ; jj++) {
if (wgi_state.per_gamepad[jj]->gamepad == gamepad) {
found = SDL_TRUE;
wgi_state.per_gamepad[jj]->connected = SDL_TRUE;
break;
}
}
if (!found) {
/* New device, add it */
wgi_state.per_gamepad_count++;
wgi_state.per_gamepad = SDL_realloc(wgi_state.per_gamepad, sizeof(wgi_state.per_gamepad[0]) * wgi_state.per_gamepad_count);
if (!wgi_state.per_gamepad) {
SDL_OutOfMemory();
return;
}
WindowsGamingInputGamepadState *gamepad_state = SDL_calloc(1, sizeof(*gamepad_state));
if (!gamepad_state) {
SDL_OutOfMemory();
return;
}
wgi_state.per_gamepad[wgi_state.per_gamepad_count - 1] = gamepad_state;
gamepad_state->gamepad = gamepad;
gamepad_state->connected = SDL_TRUE;
} else {
/* Already tracked */
__x_ABI_CWindows_CGaming_CInput_CIGamepad_Release(gamepad);
}
}
}
for (ii = wgi_state.per_gamepad_count - 1; ii >= 0; ii--) {
WindowsGamingInputGamepadState *gamepad_state = wgi_state.per_gamepad[ii];
if (!gamepad_state->connected) {
/* Device missing, must be disconnected */
if (gamepad_state->correlated_context) {
gamepad_state->correlated_context->wgi_correlated = SDL_FALSE;
gamepad_state->correlated_context->wgi_slot = NULL;
}
__x_ABI_CWindows_CGaming_CInput_CIGamepad_Release(gamepad_state->gamepad);
SDL_free(gamepad_state);
wgi_state.per_gamepad[ii] = wgi_state.per_gamepad[wgi_state.per_gamepad_count - 1];
--wgi_state.per_gamepad_count;
}
}
}
__FIVectorView_1_Windows__CGaming__CInput__CGamepad_Release(gamepads);
}
} /* need_device_list_update */
for (ii = 0; ii < wgi_state.per_gamepad_count; ii++) {
HRESULT hr = __x_ABI_CWindows_CGaming_CInput_CIGamepad_GetCurrentReading(wgi_state.per_gamepad[ii]->gamepad, &wgi_state.per_gamepad[ii]->state);
if (!SUCCEEDED(hr)) {
wgi_state.per_gamepad[ii]->connected = SDL_FALSE; /* Not used by anything, currently */
}
430
}
431
432
433
434
435
436
437
438
439
440
441
442
443
}
static void
HIDAPI_DriverXbox360_InitWindowsGamingInput(SDL_DriverXbox360_Context *ctx)
{
wgi_state.need_device_list_update = SDL_TRUE;
wgi_state.ref_count++;
if (!wgi_state.initialized) {
/* I think this takes care of RoInitialize() in a way that is compatible with the rest of SDL */
if (FAILED(WIN_CoInitialize())) {
return;
}
wgi_state.initialized = SDL_TRUE;
wgi_state.dirty = SDL_TRUE;
444
445
446
447
448
static const IID SDL_IID_IGamepadStatics = { 0x8BBCE529, 0xD49C, 0x39E9, { 0x95, 0x60, 0xE4, 0x7D, 0xDE, 0x96, 0xB7, 0xC8 } };
HRESULT hr;
HMODULE hModule = LoadLibraryA("combase.dll");
if (hModule != NULL) {
449
450
451
typedef HRESULT (WINAPI *WindowsCreateString_t)(PCNZWCH sourceString, UINT32 length, HSTRING* string);
typedef HRESULT (WINAPI *WindowsDeleteString_t)(HSTRING string);
typedef HRESULT (WINAPI *RoGetActivationFactory_t)(HSTRING activatableClassId, REFIID iid, void** factory);
452
453
WindowsCreateString_t WindowsCreateStringFunc = (WindowsCreateString_t)GetProcAddress(hModule, "WindowsCreateString");
454
WindowsDeleteString_t WindowsDeleteStringFunc = (WindowsDeleteString_t)GetProcAddress(hModule, "WindowsDeleteString");
455
RoGetActivationFactory_t RoGetActivationFactoryFunc = (RoGetActivationFactory_t)GetProcAddress(hModule, "RoGetActivationFactory");
456
if (WindowsCreateStringFunc && WindowsDeleteStringFunc && RoGetActivationFactoryFunc) {
457
458
459
460
461
LPTSTR pNamespace = L"Windows.Gaming.Input.Gamepad";
HSTRING hNamespaceString;
hr = WindowsCreateStringFunc(pNamespace, SDL_wcslen(pNamespace), &hNamespaceString);
if (SUCCEEDED(hr)) {
462
RoGetActivationFactoryFunc(hNamespaceString, &SDL_IID_IGamepadStatics, &wgi_state.gamepad_statics);
463
WindowsDeleteStringFunc(hNamespaceString);
464
465
466
467
468
469
470
}
}
FreeLibrary(hModule);
}
}
}
471
472
static SDL_bool
HIDAPI_DriverXbox360_WindowsGamingInputSlotMatches(const WindowsMatchState *state, WindowsGamingInputGamepadState *slot)
473
{
474
475
476
477
478
479
480
481
482
483
484
Uint32 wgi_buttons = slot->state.Buttons;
if ((wgi_buttons & 0x3FFF) == state->wgi_buttons && WindowsGamingInputAxesMatch(slot->state)) {
return SDL_TRUE;
}
return SDL_FALSE;
}
static SDL_bool
HIDAPI_DriverXbox360_GuessWindowsGamingInputSlot(const WindowsMatchState *state, Uint8 *correlation_id, WindowsGamingInputGamepadState **slot)
{
int match_count;
485
486
487
488
489
490
491
492
493
match_count = 0;
for (int user_index = 0; user_index < wgi_state.per_gamepad_count; ++user_index) {
WindowsGamingInputGamepadState *gamepad_state = wgi_state.per_gamepad[user_index];
if (HIDAPI_DriverXbox360_WindowsGamingInputSlotMatches(state, gamepad_state)) {
++match_count;
*slot = gamepad_state;
/* Incrementing correlation_id for any match, as negative evidence for others being correlated */
*correlation_id = ++gamepad_state->correlation_id;
494
}
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
}
/* Only return a match if we match exactly one, and we have some non-zero data (buttons or axes) that matched.
Note that we're still invalidating *other* potential correlations if we have more than one match or we have no
data. */
if (match_count == 1 && state->any_data) {
return SDL_TRUE;
}
return SDL_FALSE;
}
static void
HIDAPI_DriverXbox360_QuitWindowsGamingInput(SDL_DriverXbox360_Context *ctx)
{
wgi_state.need_device_list_update = SDL_TRUE;
--wgi_state.ref_count;
if (!wgi_state.ref_count && wgi_state.initialized) {
for (int ii = 0; ii < wgi_state.per_gamepad_count; ii++) {
__x_ABI_CWindows_CGaming_CInput_CIGamepad_Release(wgi_state.per_gamepad[ii]->gamepad);
513
}
514
515
516
if (wgi_state.per_gamepad) {
SDL_free(wgi_state.per_gamepad);
wgi_state.per_gamepad = NULL;
517
}
518
519
520
521
wgi_state.per_gamepad_count = 0;
if (wgi_state.gamepad_statics) {
__x_ABI_CWindows_CGaming_CInput_CIGamepadStatics_Release(wgi_state.gamepad_statics);
wgi_state.gamepad_statics = NULL;
522
}
523
524
WIN_CoUninitialize();
wgi_state.initialized = SDL_FALSE;
525
526
527
}
}
528
529
#endif /* SDL_JOYSTICK_HIDAPI_WINDOWS_GAMING_INPUT */
530
static void
531
HIDAPI_DriverXbox360_PostUpdate(void)
532
{
533
534
#ifdef SDL_JOYSTICK_HIDAPI_WINDOWS_MATCHING
SDL_bool unmapped_guide_pressed = SDL_FALSE;
535
536
537
538
539
540
541
542
543
#ifdef SDL_JOYSTICK_HIDAPI_WINDOWS_GAMING_INPUT
if (!wgi_state.dirty) {
int ii;
for (ii = 0; ii < wgi_state.per_gamepad_count; ii++) {
WindowsGamingInputGamepadState *gamepad_state = wgi_state.per_gamepad[ii];
if (!gamepad_state->used && (gamepad_state->state.Buttons & GamepadButtons_GUIDE)) {
unmapped_guide_pressed = SDL_TRUE;
break;
544
545
546
}
}
}
547
548
wgi_state.dirty = SDL_TRUE;
#endif
549
550
551
552
553
554
555
556
557
558
559
#ifdef SDL_JOYSTICK_HIDAPI_WINDOWS_XINPUT
if (!xinput_state_dirty) {
int ii;
for (ii = 0; ii < SDL_arraysize(xinput_state); ii++) {
if (xinput_state[ii].connected && !xinput_state[ii].used && (xinput_state[ii].state.Gamepad.wButtons & XINPUT_GAMEPAD_GUIDE)) {
unmapped_guide_pressed = SDL_TRUE;
break;
}
}
560
}
561
562
xinput_state_dirty = SDL_TRUE;
#endif
563
564
565
566
567
568
569
570
571
if (unmapped_guide_pressed) {
if (guide_button_candidate.joystick && !guide_button_candidate.last_joystick) {
SDL_PrivateJoystickButton(guide_button_candidate.joystick, SDL_CONTROLLER_BUTTON_GUIDE, SDL_PRESSED);
guide_button_candidate.last_joystick = guide_button_candidate.joystick;
}
} else if (guide_button_candidate.last_joystick) {
SDL_PrivateJoystickButton(guide_button_candidate.last_joystick, SDL_CONTROLLER_BUTTON_GUIDE, SDL_RELEASED);
guide_button_candidate.last_joystick = NULL;
572
}
573
574
guide_button_candidate.joystick = NULL;
#endif
575
576
}
577
#if defined(__MACOSX__)
578
579
580
581
582
583
584
585
586
587
588
589
590
static SDL_bool
IsBluetoothXboxOneController(Uint16 vendor_id, Uint16 product_id)
{
/* Check to see if it's the Xbox One S or Xbox One Elite Series 2 in Bluetooth mode */
if (vendor_id == USB_VENDOR_MICROSOFT) {
if (product_id == USB_PRODUCT_XBOX_ONE_S_REV1_BLUETOOTH ||
product_id == USB_PRODUCT_XBOX_ONE_S_REV2_BLUETOOTH ||
product_id == USB_PRODUCT_XBOX_ONE_ELITE_SERIES_2_BLUETOOTH) {
return SDL_TRUE;
}
}
return SDL_FALSE;
}
591
#endif
592
593
static SDL_bool
594
HIDAPI_DriverXbox360_IsSupportedDevice(const char *name, SDL_GameControllerType type, Uint16 vendor_id, Uint16 product_id, Uint16 version, int interface_number, int interface_class, int interface_subclass, int interface_protocol)
595
{
596
const int XB360W_IFACE_PROTOCOL = 129; /* Wireless */
597
598
if (vendor_id == USB_VENDOR_NVIDIA) {
599
/* This is the NVIDIA Shield controller which doesn't talk Xbox controller protocol */
600
601
return SDL_FALSE;
}
602
603
604
605
if ((vendor_id == USB_VENDOR_MICROSOFT && (product_id == 0x0291 || product_id == 0x0719)) ||
(type == SDL_CONTROLLER_TYPE_XBOX360 && interface_protocol == XB360W_IFACE_PROTOCOL)) {
/* This is the wireless dongle, which talks a different protocol */
return SDL_FALSE;
606
}
607
608
609
610
if (interface_number > 0) {
/* This is the chatpad or other input interface, not the Xbox 360 interface */
return SDL_FALSE;
}
611
#if defined(__MACOSX__) || defined(__WIN32__)
612
if (vendor_id == USB_VENDOR_MICROSOFT && product_id == 0x028e && version == 1) {
613
614
615
/* This is the Steam Virtual Gamepad, which isn't supported by this driver */
return SDL_FALSE;
}
616
617
618
619
620
621
622
623
624
625
626
#if defined(__MACOSX__)
/* Wired Xbox One controllers are handled by this driver, interfacing with
the 360Controller driver available from:
https://github.com/360Controller/360Controller/releases
Bluetooth Xbox One controllers are handled by the SDL Xbox One driver
*/
if (IsBluetoothXboxOneController(vendor_id, product_id)) {
return SDL_FALSE;
}
#endif
627
return (type == SDL_CONTROLLER_TYPE_XBOX360 || type == SDL_CONTROLLER_TYPE_XBOXONE);
628
#else
629
return (type == SDL_CONTROLLER_TYPE_XBOX360);
630
631
632
633
634
635
#endif
}
static const char *
HIDAPI_DriverXbox360_GetDeviceName(Uint16 vendor_id, Uint16 product_id)
{
636
return NULL;
637
638
639
640
}
static SDL_bool SetSlotLED(hid_device *dev, Uint8 slot)
{
641
642
Uint8 mode = 0x02 + slot;
const Uint8 led_packet[] = { 0x01, 0x03, mode };
643
644
if (hid_write(dev, led_packet, sizeof(led_packet)) != sizeof(led_packet)) {
645
646
647
return SDL_FALSE;
}
return SDL_TRUE;
648
649
650
}
static SDL_bool
651
652
HIDAPI_DriverXbox360_InitDevice(SDL_HIDAPI_Device *device)
{
653
return HIDAPI_JoystickConnected(device, NULL, SDL_FALSE);
654
655
}
656
657
658
659
660
661
662
663
664
665
666
667
668
669
static int
HIDAPI_DriverXbox360_GetDevicePlayerIndex(SDL_HIDAPI_Device *device, SDL_JoystickID instance_id)
{
return -1;
}
static void
HIDAPI_DriverXbox360_SetDevicePlayerIndex(SDL_HIDAPI_Device *device, SDL_JoystickID instance_id, int player_index)
{
if (device->dev) {
SetSlotLED(device->dev, (player_index % 4));
}
}
670
671
static SDL_bool
HIDAPI_DriverXbox360_OpenJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick)
672
673
{
SDL_DriverXbox360_Context *ctx;
674
int player_index;
675
676
677
678
679
680
ctx = (SDL_DriverXbox360_Context *)SDL_calloc(1, sizeof(*ctx));
if (!ctx) {
SDL_OutOfMemory();
return SDL_FALSE;
}
681
682
683
684
685
686
687
688
if (device->path) { /* else opened for RAWINPUT driver */
device->dev = hid_open_path(device->path, 0);
if (!device->dev) {
SDL_SetError("Couldn't open %s", device->path);
SDL_free(ctx);
return SDL_FALSE;
}
689
690
691
}
device->context = ctx;
692
#ifdef SDL_JOYSTICK_HIDAPI_WINDOWS_XINPUT
693
694
695
xinput_device_change = SDL_TRUE;
ctx->xinput_enabled = SDL_GetHintBoolean(SDL_HINT_JOYSTICK_HIDAPI_CORRELATE_XINPUT, SDL_TRUE);
if (ctx->xinput_enabled && (WIN_LoadXInputDLL() < 0 || !XINPUTGETSTATE)) {
696
697
698
ctx->xinput_enabled = SDL_FALSE;
}
ctx->xinput_slot = XUSER_INDEX_ANY;
699
700
701
#endif
#ifdef SDL_JOYSTICK_HIDAPI_WINDOWS_GAMING_INPUT
HIDAPI_DriverXbox360_InitWindowsGamingInput(ctx);
702
#endif
703
704
/* Set the controller LED */
705
player_index = SDL_JoystickGetPlayerIndex(joystick);
706
if (player_index >= 0 && device->dev) {
707
708
SetSlotLED(device->dev, (player_index % 4));
}
709
710
711
712
713
714
715
716
717
718
/* Initialize the joystick capabilities */
joystick->nbuttons = SDL_CONTROLLER_BUTTON_MAX;
joystick->naxes = SDL_CONTROLLER_AXIS_MAX;
joystick->epowerlevel = SDL_JOYSTICK_POWER_WIRED;
return SDL_TRUE;
}
static int
719
HIDAPI_DriverXbox360_RumbleJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble)
720
{
721
#if defined(SDL_JOYSTICK_HIDAPI_WINDOWS_GAMING_INPUT) || defined(SDL_JOYSTICK_HIDAPI_WINDOWS_XINPUT)
722
SDL_DriverXbox360_Context *ctx = (SDL_DriverXbox360_Context *)device->context;
723
#endif
724
725
#ifdef __WIN32__
726
727
728
SDL_bool rumbled = SDL_FALSE;
#ifdef SDL_JOYSTICK_HIDAPI_WINDOWS_GAMING_INPUT
729
730
if (!rumbled && ctx->wgi_correlated) {
WindowsGamingInputGamepadState *gamepad_state = ctx->wgi_slot;
731
HRESULT hr;
732
733
734
gamepad_state->vibration.LeftMotor = (DOUBLE)low_frequency_rumble / SDL_MAX_UINT16;
gamepad_state->vibration.RightMotor = (DOUBLE)high_frequency_rumble / SDL_MAX_UINT16;
hr = __x_ABI_CWindows_CGaming_CInput_CIGamepad_put_Vibration(gamepad_state->gamepad, gamepad_state->vibration);
735
736
737
738
739
740
741
if (SUCCEEDED(hr)) {
rumbled = SDL_TRUE;
}
}
#endif
#ifdef SDL_JOYSTICK_HIDAPI_WINDOWS_XINPUT
742
if (!rumbled && ctx->xinput_correlated) {
743
744
745
746
747
748
749
750
XINPUT_VIBRATION XVibration;
if (!XINPUTSETSTATE) {
return SDL_Unsupported();
}
XVibration.wLeftMotorSpeed = low_frequency_rumble;
XVibration.wRightMotorSpeed = high_frequency_rumble;
751
752
753
if (XINPUTSETSTATE(ctx->xinput_slot, &XVibration) == ERROR_SUCCESS) {
rumbled = SDL_TRUE;
} else {
754
755
756
return SDL_SetError("XInputSetState() failed");
}
}
757
758
759
760
#endif /* SDL_JOYSTICK_HIDAPI_WINDOWS_XINPUT */
#else /* !__WIN32__ */
761
#ifdef __MACOSX__
762
763
764
765
766
if (IsBluetoothXboxOneController(device->vendor_id, device->product_id)) {
Uint8 rumble_packet[] = { 0x03, 0x0F, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00 };
rumble_packet[4] = (low_frequency_rumble >> 8);
rumble_packet[5] = (high_frequency_rumble >> 8);
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
if (SDL_HIDAPI_SendRumble(device, rumble_packet, sizeof(rumble_packet)) != sizeof(rumble_packet)) {
return SDL_SetError("Couldn't send rumble packet");
}
} else {
/* On Mac OS X the 360Controller driver uses this short report,
and we need to prefix it with a magic token so hidapi passes it through untouched
*/
Uint8 rumble_packet[] = { 'M', 'A', 'G', 'I', 'C', '0', 0x00, 0x04, 0x00, 0x00 };
rumble_packet[6+2] = (low_frequency_rumble >> 8);
rumble_packet[6+3] = (high_frequency_rumble >> 8);
if (SDL_HIDAPI_SendRumble(device, rumble_packet, sizeof(rumble_packet)) != sizeof(rumble_packet)) {
return SDL_SetError("Couldn't send rumble packet");
}
}
784
785
786
787
788
789
#else
Uint8 rumble_packet[] = { 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
rumble_packet[3] = (low_frequency_rumble >> 8);
rumble_packet[4] = (high_frequency_rumble >> 8);
790
if (SDL_HIDAPI_SendRumble(device, rumble_packet, sizeof(rumble_packet)) != sizeof(rumble_packet)) {
791
792
return SDL_SetError("Couldn't send rumble packet");
}
793
#endif
794
#endif /* __WIN32__ */
795
796
797
798
return 0;
}
799
#ifdef __WIN32__
800
801
802
/* This is the packet format for Xbox 360 and Xbox One controllers on Windows,
however with this interface there is no rumble support, no guide button,
and the left and right triggers are tied together as a single axis.
803
804
We use XInput and Windows.Gaming.Input to make up for these shortcomings.
805
806
*/
static void
807
HIDAPI_DriverXbox360_HandleStatePacket(SDL_Joystick *joystick, hid_device *dev, SDL_DriverXbox360_Context *ctx, Uint8 *data, int size)
808
{
809
810
811
812
813
814
815
#ifdef SDL_JOYSTICK_HIDAPI_WINDOWS_MATCHING
Uint32 match_state = ctx->match_state;
/* Update match_state with button bit, then fall through */
# define SDL_PrivateJoystickButton(joystick, button, state) if (state) match_state |= 1 << (button); else match_state &=~(1<<(button)); SDL_PrivateJoystickButton(joystick, button, state)
/* Grab high 4 bits of value, then fall through */
# define SDL_PrivateJoystickAxis(joystick, axis, value) if (axis < 4) match_state = (match_state & ~(0xF << (4 * axis + 16))) | ((value) & 0xF000) << (4 * axis + 4); SDL_PrivateJoystickAxis(joystick, axis, value)
#endif
816
Sint16 axis;
817
SDL_bool has_trigger_data = SDL_FALSE;
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
if (ctx->last_state[10] != data[10]) {
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_A, (data[10] & 0x01) ? SDL_PRESSED : SDL_RELEASED);
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_B, (data[10] & 0x02) ? SDL_PRESSED : SDL_RELEASED);
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_X, (data[10] & 0x04) ? SDL_PRESSED : SDL_RELEASED);
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_Y, (data[10] & 0x08) ? SDL_PRESSED : SDL_RELEASED);
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_LEFTSHOULDER, (data[10] & 0x10) ? SDL_PRESSED : SDL_RELEASED);
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER, (data[10] & 0x20) ? SDL_PRESSED : SDL_RELEASED);
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_BACK, (data[10] & 0x40) ? SDL_PRESSED : SDL_RELEASED);
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_START, (data[10] & 0x80) ? SDL_PRESSED : SDL_RELEASED);
}
if (ctx->last_state[11] != data[11]) {
SDL_bool dpad_up = SDL_FALSE;
SDL_bool dpad_down = SDL_FALSE;
SDL_bool dpad_left = SDL_FALSE;
SDL_bool dpad_right = SDL_FALSE;
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_LEFTSTICK, (data[11] & 0x01) ? SDL_PRESSED : SDL_RELEASED);
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_RIGHTSTICK, (data[11] & 0x02) ? SDL_PRESSED : SDL_RELEASED);
switch (data[11] & 0x3C) {
case 4:
dpad_up = SDL_TRUE;
break;
case 8:
dpad_up = SDL_TRUE;
dpad_right = SDL_TRUE;
break;
case 12:
dpad_right = SDL_TRUE;
break;
case 16:
dpad_right = SDL_TRUE;
dpad_down = SDL_TRUE;
break;
case 20:
dpad_down = SDL_TRUE;
break;
case 24:
dpad_left = SDL_TRUE;
dpad_down = SDL_TRUE;
break;
case 28:
dpad_left = SDL_TRUE;
break;
case 32:
dpad_up = SDL_TRUE;
dpad_left = SDL_TRUE;
break;
default:
break;
}
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_DOWN, dpad_down);
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_UP, dpad_up);
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_RIGHT, dpad_right);
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_LEFT, dpad_left);
}
axis = (int)*(Uint16*)(&data[0]) - 0x8000;
SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_LEFTX, axis);
axis = (int)*(Uint16*)(&data[2]) - 0x8000;
SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_LEFTY, axis);
axis = (int)*(Uint16*)(&data[4]) - 0x8000;
SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_RIGHTX, axis);
axis = (int)*(Uint16*)(&data[6]) - 0x8000;
SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_RIGHTY, axis);
886
887
888
#ifdef SDL_JOYSTICK_HIDAPI_WINDOWS_MATCHING
#undef SDL_PrivateJoystickAxis
#endif
889
890
891
892
893
#ifdef SDL_JOYSTICK_HIDAPI_WINDOWS_XINPUT
/* Prefer XInput over WindowsGamingInput, it continues to provide data in the background */
if (!has_trigger_data && ctx->xinput_enabled && ctx->xinput_correlated) {
has_trigger_data = SDL_TRUE;
894
}
895
#endif /* SDL_JOYSTICK_HIDAPI_WINDOWS_XINPUT */
896
897
898
899
#ifdef SDL_JOYSTICK_HIDAPI_WINDOWS_GAMING_INPUT
if (!has_trigger_data && ctx->wgi_correlated) {
has_trigger_data = SDL_TRUE;
900
901
902
903
}
#endif /* SDL_JOYSTICK_HIDAPI_WINDOWS_GAMING_INPUT */
if (!has_trigger_data) {
904
905
906
axis = (data[9] * 257) - 32768;
if (data[9] < 0x80) {
axis = -axis * 2 - 32769;
907
SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERLEFT, SDL_MIN_SINT16);
908
909
910
911
SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERRIGHT, axis);
} else if (data[9] > 0x80) {
axis = axis * 2 - 32767;
SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERLEFT, axis);
912
SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERRIGHT, SDL_MIN_SINT16);
913
914
915
916
} else {
SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERLEFT, SDL_MIN_SINT16);
SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERRIGHT, SDL_MIN_SINT16);
}
917
918
}
919
920
921
922
923
#ifdef SDL_JOYSTICK_HIDAPI_WINDOWS_MATCHING
ctx->match_state = match_state;
ctx->last_state_packet = SDL_GetTicks();
#undef SDL_PrivateJoystickButton
#endif
924
925
SDL_memcpy(ctx->last_state, data, SDL_min(size, sizeof(ctx->last_state)));
}
926
927
928
929
930
931
932
933
934
935
#ifdef SDL_JOYSTICK_RAWINPUT
static void
HIDAPI_DriverXbox360_HandleStatePacketFromRAWINPUT(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, Uint8 *data, int size)
{
SDL_DriverXbox360_Context *ctx = (SDL_DriverXbox360_Context *)device->context;
HIDAPI_DriverXbox360_HandleStatePacket(joystick, NULL, ctx, data, size);
}
#endif
936
#else
937
938
939
940
941
942
static void
HIDAPI_DriverXbox360_HandleStatePacket(SDL_Joystick *joystick, hid_device *dev, SDL_DriverXbox360_Context *ctx, Uint8 *data, int size)
{
Sint16 axis;
#ifdef __MACOSX__
943
const SDL_bool invert_y_axes = SDL_FALSE;
944
#else
945
const SDL_bool invert_y_axes = SDL_TRUE;
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
#endif
if (ctx->last_state[2] != data[2]) {
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_UP, (data[2] & 0x01) ? SDL_PRESSED : SDL_RELEASED);
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_DOWN, (data[2] & 0x02) ? SDL_PRESSED : SDL_RELEASED);
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_LEFT, (data[2] & 0x04) ? SDL_PRESSED : SDL_RELEASED);
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_RIGHT, (data[2] & 0x08) ? SDL_PRESSED : SDL_RELEASED);
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_START, (data[2] & 0x10) ? SDL_PRESSED : SDL_RELEASED);
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_BACK, (data[2] & 0x20) ? SDL_PRESSED : SDL_RELEASED);
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_LEFTSTICK, (data[2] & 0x40) ? SDL_PRESSED : SDL_RELEASED);
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_RIGHTSTICK, (data[2] & 0x80) ? SDL_PRESSED : SDL_RELEASED);
}
if (ctx->last_state[3] != data[3]) {
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_LEFTSHOULDER, (data[3] & 0x01) ? SDL_PRESSED : SDL_RELEASED);
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER, (data[3] & 0x02) ? SDL_PRESSED : SDL_RELEASED);
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_GUIDE, (data[3] & 0x04) ? SDL_PRESSED : SDL_RELEASED);
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_A, (data[3] & 0x10) ? SDL_PRESSED : SDL_RELEASED);
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_B, (data[3] & 0x20) ? SDL_PRESSED : SDL_RELEASED);
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_X, (data[3] & 0x40) ? SDL_PRESSED : SDL_RELEASED);
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_Y, (data[3] & 0x80) ? SDL_PRESSED : SDL_RELEASED);
}
axis = ((int)data[4] * 257) - 32768;
SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERLEFT, axis);
axis = ((int)data[5] * 257) - 32768;
SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERRIGHT, axis);
axis = *(Sint16*)(&data[6]);
SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_LEFTX, axis);
axis = *(Sint16*)(&data[8]);
976
977
978
if (invert_y_axes) {
axis = ~axis;
}
979
980
981
982
SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_LEFTY, axis);
axis = *(Sint16*)(&data[10]);
SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_RIGHTX, axis);
axis = *(Sint16*)(&data[12]);
983
984
985
if (invert_y_axes) {
axis = ~axis;
}
986
987
988
989
SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_RIGHTY, axis);
SDL_memcpy(ctx->last_state, data, SDL_min(size, sizeof(ctx->last_state)));
}
990
#endif /* __WIN32__ */
991
992
993
994
static void
HIDAPI_DriverXbox360_UpdateOtherAPIs(SDL_HIDAPI_Device *device, SDL_Joystick *joystick)
{
995
#if defined(SDL_JOYSTICK_HIDAPI_WINDOWS_MATCHING) || defined(SDL_JOYSTICK_HIDAPI_WINDOWS_XINPUT) || defined(SDL_JOYSTICK_HIDAPI_WINDOWS_GAMING_INPUT)
996
997
998
999
1000
SDL_DriverXbox360_Context *ctx = (SDL_DriverXbox360_Context *)device->context;
SDL_bool has_trigger_data = SDL_FALSE;
SDL_bool correlated = SDL_FALSE;
#ifdef SDL_JOYSTICK_HIDAPI_WINDOWS_MATCHING
WindowsMatchState match_state_xinput;