1 /*
2 Simple DirectMedia Layer
3 Copyright (C) 1997-2020 Sam Lantinga <slouken@libsdl.org>
4
5 This software is provided 'as-is', without any express or implied
6 warranty. In no event will the authors be held liable for any damages
7 arising from the use of this software.
8
9 Permission is granted to anyone to use this software for any purpose,
10 including commercial applications, and to alter it and redistribute it
11 freely, subject to the following restrictions:
12
13 1. The origin of this software must not be misrepresented; you must not
14 claim that you wrote the original software. If you use this software
15 in a product, an acknowledgment in the product documentation would be
16 appreciated but is not required.
17 2. Altered source versions must be plainly marked as such, and must not be
18 misrepresented as being the original software.
19 3. This notice may not be removed or altered from any source distribution.
20 */
21 #include "../../SDL_internal.h"
22
23 #ifdef SDL_JOYSTICK_HIDAPI
24
25 #include "SDL_hints.h"
26 #include "SDL_events.h"
27 #include "SDL_timer.h"
28 #include "SDL_joystick.h"
29 #include "SDL_gamecontroller.h"
30 #include "../SDL_sysjoystick.h"
31 #include "SDL_hidapijoystick_c.h"
32 #include "SDL_hidapi_rumble.h"
33
34
35 #ifdef SDL_JOYSTICK_HIDAPI_XBOX360
36
37 #ifdef __WIN32__
38 #define SDL_JOYSTICK_HIDAPI_WINDOWS_XINPUT
39 /* This requires the Windows 10 SDK to build */
40 /*#define SDL_JOYSTICK_HIDAPI_WINDOWS_GAMING_INPUT*/
41 #endif
42
43 #ifdef SDL_JOYSTICK_HIDAPI_WINDOWS_XINPUT
44 #include "../../core/windows/SDL_xinput.h"
45 #endif
46
47 #ifdef SDL_JOYSTICK_HIDAPI_WINDOWS_GAMING_INPUT
48 #include "../../core/windows/SDL_windows.h"
49 typedef struct WindowsGamingInputGamepadState WindowsGamingInputGamepadState;
50 #define GamepadButtons_GUIDE 0x40000000
51 #define COBJMACROS
52 #include "windows.gaming.input.h"
53 #endif
54
55 #if defined(SDL_JOYSTICK_HIDAPI_WINDOWS_XINPUT) || defined(SDL_JOYSTICK_HIDAPI_WINDOWS_GAMING_INPUT)
56 #define SDL_JOYSTICK_HIDAPI_WINDOWS_MATCHING
57 #endif
58
59 typedef struct {
60 Uint8 last_state[USB_PACKET_LENGTH];
61 #ifdef SDL_JOYSTICK_HIDAPI_WINDOWS_MATCHING
62 Uint32 match_state; /* Low 16 bits for button states, high 16 for 4 4bit axes */
63 Uint32 last_state_packet;
64 #endif
65 #ifdef SDL_JOYSTICK_HIDAPI_WINDOWS_XINPUT
66 SDL_bool xinput_enabled;
67 SDL_bool xinput_correlated;
68 Uint8 xinput_correlation_id;
69 Uint8 xinput_correlation_count;
70 Uint8 xinput_uncorrelate_count;
71 Uint8 xinput_slot;
72 #endif
73 #ifdef SDL_JOYSTICK_HIDAPI_WINDOWS_GAMING_INPUT
74 SDL_bool wgi_correlated;
75 Uint8 wgi_correlation_id;
76 Uint8 wgi_correlation_count;
77 Uint8 wgi_uncorrelate_count;
78 WindowsGamingInputGamepadState *wgi_slot;
79 #endif
80 } SDL_DriverXbox360_Context;
81
82 #ifdef SDL_JOYSTICK_HIDAPI_WINDOWS_MATCHING
83 static struct {
84 Uint32 last_state_packet;
85 SDL_Joystick *joystick;
86 SDL_Joystick *last_joystick;
87 } guide_button_candidate;
88
89 typedef struct WindowsMatchState {
90 SHORT match_axes[4];
91 #ifdef SDL_JOYSTICK_HIDAPI_WINDOWS_XINPUT
92 WORD xinput_buttons;
93 #endif
94 #ifdef SDL_JOYSTICK_HIDAPI_WINDOWS_GAMING_INPUT
95 Uint32 wgi_buttons;
96 #endif
97 SDL_bool any_data;
98 } WindowsMatchState;
99
HIDAPI_DriverXbox360_FillMatchState(WindowsMatchState * state,Uint32 match_state)100 static void HIDAPI_DriverXbox360_FillMatchState(WindowsMatchState *state, Uint32 match_state)
101 {
102 int ii;
103 state->any_data = SDL_FALSE;
104 /* SHORT state->match_axes[4] = {
105 (match_state & 0x000F0000) >> 4,
106 (match_state & 0x00F00000) >> 8,
107 (match_state & 0x0F000000) >> 12,
108 (match_state & 0xF0000000) >> 16,
109 }; */
110 for (ii = 0; ii < 4; ii++) {
111 state->match_axes[ii] = (match_state & (0x000F0000 << (ii * 4))) >> (4 + ii * 4);
112 if ((Uint32)(state->match_axes[ii] + 0x1000) > 0x2000) { /* match_state bit is not 0xF, 0x1, or 0x2 */
113 state->any_data = SDL_TRUE;
114 }
115 }
116
117 #ifdef SDL_JOYSTICK_HIDAPI_WINDOWS_XINPUT
118 /* 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 */
119 #define XInputAxesMatch(gamepad) (\
120 (Uint32)(gamepad.sThumbLX - state->match_axes[0] + 0x1000) <= 0x2fff && \
121 (Uint32)(~gamepad.sThumbLY - state->match_axes[1] + 0x1000) <= 0x2fff && \
122 (Uint32)(gamepad.sThumbRX - state->match_axes[2] + 0x1000) <= 0x2fff && \
123 (Uint32)(~gamepad.sThumbRY - state->match_axes[3] + 0x1000) <= 0x2fff)
124 /* Explicit
125 #define XInputAxesMatch(gamepad) (\
126 SDL_abs((Sint8)((gamepad.sThumbLX & 0xF000) >> 8) - ((match_state & 0x000F0000) >> 12)) <= 0x10 && \
127 SDL_abs((Sint8)((~gamepad.sThumbLY & 0xF000) >> 8) - ((match_state & 0x00F00000) >> 16)) <= 0x10 && \
128 SDL_abs((Sint8)((gamepad.sThumbRX & 0xF000) >> 8) - ((match_state & 0x0F000000) >> 20)) <= 0x10 && \
129 SDL_abs((Sint8)((~gamepad.sThumbRY & 0xF000) >> 8) - ((match_state & 0xF0000000) >> 24)) <= 0x10) */
130
131
132 state->xinput_buttons =
133 /* Bitwise map .RLDUWVQTS.KYXBA -> YXBA..WVQTKSRLDU */
134 match_state << 12 | (match_state & 0x0780) >> 1 | (match_state & 0x0010) << 1 | (match_state & 0x0040) >> 2 | (match_state & 0x7800) >> 11;
135 /* Explicit
136 ((match_state & (1<<SDL_CONTROLLER_BUTTON_A)) ? XINPUT_GAMEPAD_A : 0) |
137 ((match_state & (1<<SDL_CONTROLLER_BUTTON_B)) ? XINPUT_GAMEPAD_B : 0) |
138 ((match_state & (1<<SDL_CONTROLLER_BUTTON_X)) ? XINPUT_GAMEPAD_X : 0) |
139 ((match_state & (1<<SDL_CONTROLLER_BUTTON_Y)) ? XINPUT_GAMEPAD_Y : 0) |
140 ((match_state & (1<<SDL_CONTROLLER_BUTTON_BACK)) ? XINPUT_GAMEPAD_BACK : 0) |
141 ((match_state & (1<<SDL_CONTROLLER_BUTTON_START)) ? XINPUT_GAMEPAD_START : 0) |
142 ((match_state & (1<<SDL_CONTROLLER_BUTTON_LEFTSTICK)) ? XINPUT_GAMEPAD_LEFT_THUMB : 0) |
143 ((match_state & (1<<SDL_CONTROLLER_BUTTON_RIGHTSTICK)) ? XINPUT_GAMEPAD_RIGHT_THUMB: 0) |
144 ((match_state & (1<<SDL_CONTROLLER_BUTTON_LEFTSHOULDER)) ? XINPUT_GAMEPAD_LEFT_SHOULDER : 0) |
145 ((match_state & (1<<SDL_CONTROLLER_BUTTON_RIGHTSHOULDER)) ? XINPUT_GAMEPAD_RIGHT_SHOULDER : 0) |
146 ((match_state & (1<<SDL_CONTROLLER_BUTTON_DPAD_UP)) ? XINPUT_GAMEPAD_DPAD_UP : 0) |
147 ((match_state & (1<<SDL_CONTROLLER_BUTTON_DPAD_DOWN)) ? XINPUT_GAMEPAD_DPAD_DOWN : 0) |
148 ((match_state & (1<<SDL_CONTROLLER_BUTTON_DPAD_LEFT)) ? XINPUT_GAMEPAD_DPAD_LEFT : 0) |
149 ((match_state & (1<<SDL_CONTROLLER_BUTTON_DPAD_RIGHT)) ? XINPUT_GAMEPAD_DPAD_RIGHT : 0);
150 */
151
152 if (state->xinput_buttons)
153 state->any_data = SDL_TRUE;
154 #endif
155
156 #ifdef SDL_JOYSTICK_HIDAPI_WINDOWS_GAMING_INPUT
157 /* 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 */
158 #define WindowsGamingInputAxesMatch(gamepad) (\
159 (Uint16)(((Sint16)(gamepad.LeftThumbstickX * SDL_MAX_SINT16) & 0xF000) - state->match_axes[0] + 0x1000) <= 0x2fff && \
160 (Uint16)((~(Sint16)(gamepad.LeftThumbstickY * SDL_MAX_SINT16) & 0xF000) - state->match_axes[1] + 0x1000) <= 0x2fff && \
161 (Uint16)(((Sint16)(gamepad.RightThumbstickX * SDL_MAX_SINT16) & 0xF000) - state->match_axes[2] + 0x1000) <= 0x2fff && \
162 (Uint16)((~(Sint16)(gamepad.RightThumbstickY * SDL_MAX_SINT16) & 0xF000) - state->match_axes[3] + 0x1000) <= 0x2fff)
163
164
165 state->wgi_buttons =
166 /* Bitwise map .RLD UWVQ TS.K YXBA -> ..QT WVRL DUYX BAKS */
167 /* RStick/LStick (QT) RShould/LShould (WV) DPad R/L/D/U YXBA bac(K) (S)tart */
168 (match_state & 0x0180) << 5 | (match_state & 0x0600) << 1 | (match_state & 0x7800) >> 5 | (match_state & 0x000F) << 2 | (match_state & 0x0010) >> 3 | (match_state & 0x0040) >> 6;
169 /* Explicit
170 ((match_state & (1<<SDL_CONTROLLER_BUTTON_A)) ? GamepadButtons_A : 0) |
171 ((match_state & (1<<SDL_CONTROLLER_BUTTON_B)) ? GamepadButtons_B : 0) |
172 ((match_state & (1<<SDL_CONTROLLER_BUTTON_X)) ? GamepadButtons_X : 0) |
173 ((match_state & (1<<SDL_CONTROLLER_BUTTON_Y)) ? GamepadButtons_Y : 0) |
174 ((match_state & (1<<SDL_CONTROLLER_BUTTON_BACK)) ? GamepadButtons_View : 0) |
175 ((match_state & (1<<SDL_CONTROLLER_BUTTON_START)) ? GamepadButtons_Menu : 0) |
176 ((match_state & (1<<SDL_CONTROLLER_BUTTON_LEFTSTICK)) ? GamepadButtons_LeftThumbstick : 0) |
177 ((match_state & (1<<SDL_CONTROLLER_BUTTON_RIGHTSTICK)) ? GamepadButtons_RightThumbstick: 0) |
178 ((match_state & (1<<SDL_CONTROLLER_BUTTON_LEFTSHOULDER)) ? GamepadButtons_LeftShoulder: 0) |
179 ((match_state & (1<<SDL_CONTROLLER_BUTTON_RIGHTSHOULDER)) ? GamepadButtons_RightShoulder: 0) |
180 ((match_state & (1<<SDL_CONTROLLER_BUTTON_DPAD_UP)) ? GamepadButtons_DPadUp : 0) |
181 ((match_state & (1<<SDL_CONTROLLER_BUTTON_DPAD_DOWN)) ? GamepadButtons_DPadDown : 0) |
182 ((match_state & (1<<SDL_CONTROLLER_BUTTON_DPAD_LEFT)) ? GamepadButtons_DPadLeft : 0) |
183 ((match_state & (1<<SDL_CONTROLLER_BUTTON_DPAD_RIGHT)) ? GamepadButtons_DPadRight : 0); */
184
185 if (state->wgi_buttons)
186 state->any_data = SDL_TRUE;
187 #endif
188
189 }
190
191
192 #endif
193
194 #ifdef SDL_JOYSTICK_HIDAPI_WINDOWS_XINPUT
195 static struct {
196 XINPUT_STATE_EX state;
197 SDL_bool connected; /* Currently has an active XInput device */
198 SDL_bool used; /* Is currently mapped to an SDL device */
199 Uint8 correlation_id;
200 } xinput_state[XUSER_MAX_COUNT];
201 static SDL_bool xinput_device_change = SDL_TRUE;
202 static SDL_bool xinput_state_dirty = SDL_TRUE;
203
204 static void
HIDAPI_DriverXbox360_UpdateXInput()205 HIDAPI_DriverXbox360_UpdateXInput()
206 {
207 DWORD user_index;
208 if (xinput_device_change) {
209 for (user_index = 0; user_index < XUSER_MAX_COUNT; user_index++) {
210 XINPUT_CAPABILITIES capabilities;
211 xinput_state[user_index].connected = XINPUTGETCAPABILITIES(user_index, XINPUT_FLAG_GAMEPAD, &capabilities) == ERROR_SUCCESS;
212 }
213 xinput_device_change = SDL_FALSE;
214 xinput_state_dirty = SDL_TRUE;
215 }
216 if (xinput_state_dirty) {
217 xinput_state_dirty = SDL_FALSE;
218 for (user_index = 0; user_index < SDL_arraysize(xinput_state); ++user_index) {
219 if (xinput_state[user_index].connected) {
220 if (XINPUTGETSTATE(user_index, &xinput_state[user_index].state) != ERROR_SUCCESS) {
221 xinput_state[user_index].connected = SDL_FALSE;
222 }
223 }
224 }
225 }
226 }
227
228 static void
HIDAPI_DriverXbox360_MarkXInputSlotUsed(Uint8 xinput_slot)229 HIDAPI_DriverXbox360_MarkXInputSlotUsed(Uint8 xinput_slot)
230 {
231 if (xinput_slot != XUSER_INDEX_ANY) {
232 xinput_state[xinput_slot].used = SDL_TRUE;
233 }
234 }
235
236 static void
HIDAPI_DriverXbox360_MarkXInputSlotFree(Uint8 xinput_slot)237 HIDAPI_DriverXbox360_MarkXInputSlotFree(Uint8 xinput_slot)
238 {
239 if (xinput_slot != XUSER_INDEX_ANY) {
240 xinput_state[xinput_slot].used = SDL_FALSE;
241 }
242 }
243 static SDL_bool
HIDAPI_DriverXbox360_MissingXInputSlot()244 HIDAPI_DriverXbox360_MissingXInputSlot()
245 {
246 int ii;
247 for (ii = 0; ii < SDL_arraysize(xinput_state); ii++) {
248 if (xinput_state[ii].connected && !xinput_state[ii].used) {
249 return SDL_TRUE;
250 }
251 }
252 return SDL_FALSE;
253 }
254
255 static SDL_bool
HIDAPI_DriverXbox360_XInputSlotMatches(const WindowsMatchState * state,Uint8 slot_idx)256 HIDAPI_DriverXbox360_XInputSlotMatches(const WindowsMatchState *state, Uint8 slot_idx)
257 {
258 if (xinput_state[slot_idx].connected) {
259 WORD xinput_buttons = xinput_state[slot_idx].state.Gamepad.wButtons;
260 if ((xinput_buttons & ~XINPUT_GAMEPAD_GUIDE) == state->xinput_buttons && XInputAxesMatch(xinput_state[slot_idx].state.Gamepad)) {
261 return SDL_TRUE;
262 }
263 }
264 return SDL_FALSE;
265 }
266
267
268 static SDL_bool
HIDAPI_DriverXbox360_GuessXInputSlot(const WindowsMatchState * state,Uint8 * correlation_id,Uint8 * slot_idx)269 HIDAPI_DriverXbox360_GuessXInputSlot(const WindowsMatchState *state, Uint8 *correlation_id, Uint8 *slot_idx)
270 {
271 int user_index;
272 int match_count;
273
274 *slot_idx = 0;
275
276 match_count = 0;
277 for (user_index = 0; user_index < XUSER_MAX_COUNT; ++user_index) {
278 if (!xinput_state[user_index].used && HIDAPI_DriverXbox360_XInputSlotMatches(state, user_index)) {
279 ++match_count;
280 *slot_idx = (Uint8)user_index;
281 /* Incrementing correlation_id for any match, as negative evidence for others being correlated */
282 *correlation_id = ++xinput_state[user_index].correlation_id;
283 }
284 }
285 /* Only return a match if we match exactly one, and we have some non-zero data (buttons or axes) that matched.
286 Note that we're still invalidating *other* potential correlations if we have more than one match or we have no
287 data. */
288 if (match_count == 1 && state->any_data) {
289 return SDL_TRUE;
290 }
291 return SDL_FALSE;
292 }
293
294 #endif /* SDL_JOYSTICK_HIDAPI_WINDOWS_XINPUT */
295
296 #ifdef SDL_JOYSTICK_HIDAPI_WINDOWS_GAMING_INPUT
297
298 typedef struct WindowsGamingInputGamepadState {
299 __x_ABI_CWindows_CGaming_CInput_CIGamepad *gamepad;
300 struct __x_ABI_CWindows_CGaming_CInput_CGamepadReading state;
301 SDL_DriverXbox360_Context *correlated_context;
302 SDL_bool used; /* Is currently mapped to an SDL device */
303 SDL_bool connected; /* Just used during update to track disconnected */
304 Uint8 correlation_id;
305 struct __x_ABI_CWindows_CGaming_CInput_CGamepadVibration vibration;
306 } WindowsGamingInputGamepadState;
307
308 static struct {
309 WindowsGamingInputGamepadState **per_gamepad;
310 int per_gamepad_count;
311 SDL_bool initialized;
312 SDL_bool dirty;
313 SDL_bool need_device_list_update;
314 int ref_count;
315 __x_ABI_CWindows_CGaming_CInput_CIGamepadStatics *gamepad_statics;
316 } wgi_state;
317
318 static void
HIDAPI_DriverXbox360_MarkWindowsGamingInputSlotUsed(WindowsGamingInputGamepadState * wgi_slot,SDL_DriverXbox360_Context * ctx)319 HIDAPI_DriverXbox360_MarkWindowsGamingInputSlotUsed(WindowsGamingInputGamepadState *wgi_slot, SDL_DriverXbox360_Context *ctx)
320 {
321 wgi_slot->used = SDL_TRUE;
322 wgi_slot->correlated_context = ctx;
323 }
324
325 static void
HIDAPI_DriverXbox360_MarkWindowsGamingInputSlotFree(WindowsGamingInputGamepadState * wgi_slot)326 HIDAPI_DriverXbox360_MarkWindowsGamingInputSlotFree(WindowsGamingInputGamepadState *wgi_slot)
327 {
328 wgi_slot->used = SDL_FALSE;
329 wgi_slot->correlated_context = NULL;
330 }
331
332 static SDL_bool
HIDAPI_DriverXbox360_MissingWindowsGamingInputSlot()333 HIDAPI_DriverXbox360_MissingWindowsGamingInputSlot()
334 {
335 int ii;
336 for (ii = 0; ii < wgi_state.per_gamepad_count; ii++) {
337 if (!wgi_state.per_gamepad[ii]->used) {
338 return SDL_TRUE;
339 }
340 }
341 return SDL_FALSE;
342 }
343
344 static void
HIDAPI_DriverXbox360_UpdateWindowsGamingInput()345 HIDAPI_DriverXbox360_UpdateWindowsGamingInput()
346 {
347 int ii;
348 if (!wgi_state.gamepad_statics)
349 return;
350
351 if (!wgi_state.dirty)
352 return;
353 wgi_state.dirty = SDL_FALSE;
354
355 if (wgi_state.need_device_list_update) {
356 wgi_state.need_device_list_update = SDL_FALSE;
357 for (ii = 0; ii < wgi_state.per_gamepad_count; ii++) {
358 wgi_state.per_gamepad[ii]->connected = SDL_FALSE;
359 }
360 HRESULT hr;
361 __FIVectorView_1_Windows__CGaming__CInput__CGamepad *gamepads;
362
363 hr = __x_ABI_CWindows_CGaming_CInput_CIGamepadStatics_get_Gamepads(wgi_state.gamepad_statics, &gamepads);
364 if (SUCCEEDED(hr)) {
365 unsigned int num_gamepads;
366
367 hr = __FIVectorView_1_Windows__CGaming__CInput__CGamepad_get_Size(gamepads, &num_gamepads);
368 if (SUCCEEDED(hr)) {
369 unsigned int i;
370 for (i = 0; i < num_gamepads; ++i) {
371 __x_ABI_CWindows_CGaming_CInput_CIGamepad *gamepad;
372
373 hr = __FIVectorView_1_Windows__CGaming__CInput__CGamepad_GetAt(gamepads, i, &gamepad);
374 if (SUCCEEDED(hr)) {
375 SDL_bool found = SDL_FALSE;
376 int jj;
377 for (jj = 0; jj < wgi_state.per_gamepad_count ; jj++) {
378 if (wgi_state.per_gamepad[jj]->gamepad == gamepad) {
379 found = SDL_TRUE;
380 wgi_state.per_gamepad[jj]->connected = SDL_TRUE;
381 break;
382 }
383 }
384 if (!found) {
385 /* New device, add it */
386 wgi_state.per_gamepad_count++;
387 wgi_state.per_gamepad = SDL_realloc(wgi_state.per_gamepad, sizeof(wgi_state.per_gamepad[0]) * wgi_state.per_gamepad_count);
388 if (!wgi_state.per_gamepad) {
389 SDL_OutOfMemory();
390 return;
391 }
392 WindowsGamingInputGamepadState *gamepad_state = SDL_calloc(1, sizeof(*gamepad_state));
393 if (!gamepad_state) {
394 SDL_OutOfMemory();
395 return;
396 }
397 wgi_state.per_gamepad[wgi_state.per_gamepad_count - 1] = gamepad_state;
398 gamepad_state->gamepad = gamepad;
399 gamepad_state->connected = SDL_TRUE;
400 } else {
401 /* Already tracked */
402 __x_ABI_CWindows_CGaming_CInput_CIGamepad_Release(gamepad);
403 }
404 }
405 }
406 for (ii = wgi_state.per_gamepad_count - 1; ii >= 0; ii--) {
407 WindowsGamingInputGamepadState *gamepad_state = wgi_state.per_gamepad[ii];
408 if (!gamepad_state->connected) {
409 /* Device missing, must be disconnected */
410 if (gamepad_state->correlated_context) {
411 gamepad_state->correlated_context->wgi_correlated = SDL_FALSE;
412 gamepad_state->correlated_context->wgi_slot = NULL;
413 }
414 __x_ABI_CWindows_CGaming_CInput_CIGamepad_Release(gamepad_state->gamepad);
415 SDL_free(gamepad_state);
416 wgi_state.per_gamepad[ii] = wgi_state.per_gamepad[wgi_state.per_gamepad_count - 1];
417 --wgi_state.per_gamepad_count;
418 }
419 }
420 }
421 __FIVectorView_1_Windows__CGaming__CInput__CGamepad_Release(gamepads);
422 }
423 } /* need_device_list_update */
424
425 for (ii = 0; ii < wgi_state.per_gamepad_count; ii++) {
426 HRESULT hr = __x_ABI_CWindows_CGaming_CInput_CIGamepad_GetCurrentReading(wgi_state.per_gamepad[ii]->gamepad, &wgi_state.per_gamepad[ii]->state);
427 if (!SUCCEEDED(hr)) {
428 wgi_state.per_gamepad[ii]->connected = SDL_FALSE; /* Not used by anything, currently */
429 }
430 }
431 }
432 static void
HIDAPI_DriverXbox360_InitWindowsGamingInput(SDL_DriverXbox360_Context * ctx)433 HIDAPI_DriverXbox360_InitWindowsGamingInput(SDL_DriverXbox360_Context *ctx)
434 {
435 wgi_state.need_device_list_update = SDL_TRUE;
436 wgi_state.ref_count++;
437 if (!wgi_state.initialized) {
438 /* I think this takes care of RoInitialize() in a way that is compatible with the rest of SDL */
439 if (FAILED(WIN_CoInitialize())) {
440 return;
441 }
442 wgi_state.initialized = SDL_TRUE;
443 wgi_state.dirty = SDL_TRUE;
444
445 static const IID SDL_IID_IGamepadStatics = { 0x8BBCE529, 0xD49C, 0x39E9, { 0x95, 0x60, 0xE4, 0x7D, 0xDE, 0x96, 0xB7, 0xC8 } };
446 HRESULT hr;
447 HMODULE hModule = LoadLibraryA("combase.dll");
448 if (hModule != NULL) {
449 typedef HRESULT (WINAPI *WindowsCreateString_t)(PCNZWCH sourceString, UINT32 length, HSTRING* string);
450 typedef HRESULT (WINAPI *WindowsDeleteString_t)(HSTRING string);
451 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 LPTSTR pNamespace = L"Windows.Gaming.Input.Gamepad";
458 HSTRING hNamespaceString;
459
460 hr = WindowsCreateStringFunc(pNamespace, SDL_wcslen(pNamespace), &hNamespaceString);
461 if (SUCCEEDED(hr)) {
462 RoGetActivationFactoryFunc(hNamespaceString, &SDL_IID_IGamepadStatics, &wgi_state.gamepad_statics);
463 WindowsDeleteStringFunc(hNamespaceString);
464 }
465 }
466 FreeLibrary(hModule);
467 }
468 }
469 }
470
471 static SDL_bool
HIDAPI_DriverXbox360_WindowsGamingInputSlotMatches(const WindowsMatchState * state,WindowsGamingInputGamepadState * slot)472 HIDAPI_DriverXbox360_WindowsGamingInputSlotMatches(const WindowsMatchState *state, WindowsGamingInputGamepadState *slot)
473 {
474 Uint32 wgi_buttons = slot->state.Buttons;
475 if ((wgi_buttons & 0x3FFF) == state->wgi_buttons && WindowsGamingInputAxesMatch(slot->state)) {
476 return SDL_TRUE;
477 }
478 return SDL_FALSE;
479 }
480
481 static SDL_bool
HIDAPI_DriverXbox360_GuessWindowsGamingInputSlot(const WindowsMatchState * state,Uint8 * correlation_id,WindowsGamingInputGamepadState ** slot)482 HIDAPI_DriverXbox360_GuessWindowsGamingInputSlot(const WindowsMatchState *state, Uint8 *correlation_id, WindowsGamingInputGamepadState **slot)
483 {
484 int match_count;
485
486 match_count = 0;
487 for (int user_index = 0; user_index < wgi_state.per_gamepad_count; ++user_index) {
488 WindowsGamingInputGamepadState *gamepad_state = wgi_state.per_gamepad[user_index];
489 if (HIDAPI_DriverXbox360_WindowsGamingInputSlotMatches(state, gamepad_state)) {
490 ++match_count;
491 *slot = gamepad_state;
492 /* Incrementing correlation_id for any match, as negative evidence for others being correlated */
493 *correlation_id = ++gamepad_state->correlation_id;
494 }
495 }
496 /* Only return a match if we match exactly one, and we have some non-zero data (buttons or axes) that matched.
497 Note that we're still invalidating *other* potential correlations if we have more than one match or we have no
498 data. */
499 if (match_count == 1 && state->any_data) {
500 return SDL_TRUE;
501 }
502 return SDL_FALSE;
503 }
504
505 static void
HIDAPI_DriverXbox360_QuitWindowsGamingInput(SDL_DriverXbox360_Context * ctx)506 HIDAPI_DriverXbox360_QuitWindowsGamingInput(SDL_DriverXbox360_Context *ctx)
507 {
508 wgi_state.need_device_list_update = SDL_TRUE;
509 --wgi_state.ref_count;
510 if (!wgi_state.ref_count && wgi_state.initialized) {
511 for (int ii = 0; ii < wgi_state.per_gamepad_count; ii++) {
512 __x_ABI_CWindows_CGaming_CInput_CIGamepad_Release(wgi_state.per_gamepad[ii]->gamepad);
513 }
514 if (wgi_state.per_gamepad) {
515 SDL_free(wgi_state.per_gamepad);
516 wgi_state.per_gamepad = NULL;
517 }
518 wgi_state.per_gamepad_count = 0;
519 if (wgi_state.gamepad_statics) {
520 __x_ABI_CWindows_CGaming_CInput_CIGamepadStatics_Release(wgi_state.gamepad_statics);
521 wgi_state.gamepad_statics = NULL;
522 }
523 WIN_CoUninitialize();
524 wgi_state.initialized = SDL_FALSE;
525 }
526 }
527
528 #endif /* SDL_JOYSTICK_HIDAPI_WINDOWS_GAMING_INPUT */
529
530 static void
HIDAPI_DriverXbox360_PostUpdate(void)531 HIDAPI_DriverXbox360_PostUpdate(void)
532 {
533 #ifdef SDL_JOYSTICK_HIDAPI_WINDOWS_MATCHING
534 SDL_bool unmapped_guide_pressed = SDL_FALSE;
535
536 #ifdef SDL_JOYSTICK_HIDAPI_WINDOWS_GAMING_INPUT
537 if (!wgi_state.dirty) {
538 int ii;
539 for (ii = 0; ii < wgi_state.per_gamepad_count; ii++) {
540 WindowsGamingInputGamepadState *gamepad_state = wgi_state.per_gamepad[ii];
541 if (!gamepad_state->used && (gamepad_state->state.Buttons & GamepadButtons_GUIDE)) {
542 unmapped_guide_pressed = SDL_TRUE;
543 break;
544 }
545 }
546 }
547 wgi_state.dirty = SDL_TRUE;
548 #endif
549
550
551 #ifdef SDL_JOYSTICK_HIDAPI_WINDOWS_XINPUT
552 if (!xinput_state_dirty) {
553 int ii;
554 for (ii = 0; ii < SDL_arraysize(xinput_state); ii++) {
555 if (xinput_state[ii].connected && !xinput_state[ii].used && (xinput_state[ii].state.Gamepad.wButtons & XINPUT_GAMEPAD_GUIDE)) {
556 unmapped_guide_pressed = SDL_TRUE;
557 break;
558 }
559 }
560 }
561 xinput_state_dirty = SDL_TRUE;
562 #endif
563
564 if (unmapped_guide_pressed) {
565 if (guide_button_candidate.joystick && !guide_button_candidate.last_joystick) {
566 SDL_PrivateJoystickButton(guide_button_candidate.joystick, SDL_CONTROLLER_BUTTON_GUIDE, SDL_PRESSED);
567 guide_button_candidate.last_joystick = guide_button_candidate.joystick;
568 }
569 } else if (guide_button_candidate.last_joystick) {
570 SDL_PrivateJoystickButton(guide_button_candidate.last_joystick, SDL_CONTROLLER_BUTTON_GUIDE, SDL_RELEASED);
571 guide_button_candidate.last_joystick = NULL;
572 }
573 guide_button_candidate.joystick = NULL;
574 #endif
575 }
576
577 #if defined(__MACOSX__)
578 static SDL_bool
IsBluetoothXboxOneController(Uint16 vendor_id,Uint16 product_id)579 IsBluetoothXboxOneController(Uint16 vendor_id, Uint16 product_id)
580 {
581 /* Check to see if it's the Xbox One S or Xbox One Elite Series 2 in Bluetooth mode */
582 if (vendor_id == USB_VENDOR_MICROSOFT) {
583 if (product_id == USB_PRODUCT_XBOX_ONE_S_REV1_BLUETOOTH ||
584 product_id == USB_PRODUCT_XBOX_ONE_S_REV2_BLUETOOTH ||
585 product_id == USB_PRODUCT_XBOX_ONE_ELITE_SERIES_2_BLUETOOTH) {
586 return SDL_TRUE;
587 }
588 }
589 return SDL_FALSE;
590 }
591 #endif
592
593 static SDL_bool
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)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 return SDL_FALSE;
601 }
602 if ((vendor_id == USB_VENDOR_MICROSOFT && (product_id == 0x0291 || product_id == 0x0719)) ||
603 (type == SDL_CONTROLLER_TYPE_XBOX360 && interface_protocol == XB360W_IFACE_PROTOCOL)) {
604 /* This is the wireless dongle, which talks a different protocol */
605 return SDL_FALSE;
606 }
607 if (interface_number > 0) {
608 /* This is the chatpad or other input interface, not the Xbox 360 interface */
609 return SDL_FALSE;
610 }
611 #if defined(__MACOSX__) || defined(__WIN32__)
612 if (vendor_id == USB_VENDOR_MICROSOFT && product_id == 0x028e && version == 1) {
613 /* This is the Steam Virtual Gamepad, which isn't supported by this driver */
614 return SDL_FALSE;
615 }
616 #if defined(__MACOSX__)
617 /* Wired Xbox One controllers are handled by this driver, interfacing with
618 the 360Controller driver available from:
619 https://github.com/360Controller/360Controller/releases
620
621 Bluetooth Xbox One controllers are handled by the SDL Xbox One driver
622 */
623 if (IsBluetoothXboxOneController(vendor_id, product_id)) {
624 return SDL_FALSE;
625 }
626 #endif
627 return (type == SDL_CONTROLLER_TYPE_XBOX360 || type == SDL_CONTROLLER_TYPE_XBOXONE);
628 #else
629 return (type == SDL_CONTROLLER_TYPE_XBOX360);
630 #endif
631 }
632
633 static const char *
HIDAPI_DriverXbox360_GetDeviceName(Uint16 vendor_id,Uint16 product_id)634 HIDAPI_DriverXbox360_GetDeviceName(Uint16 vendor_id, Uint16 product_id)
635 {
636 return NULL;
637 }
638
SetSlotLED(hid_device * dev,Uint8 slot)639 static SDL_bool SetSlotLED(hid_device *dev, Uint8 slot)
640 {
641 Uint8 mode = 0x02 + slot;
642 const Uint8 led_packet[] = { 0x01, 0x03, mode };
643
644 if (hid_write(dev, led_packet, sizeof(led_packet)) != sizeof(led_packet)) {
645 return SDL_FALSE;
646 }
647 return SDL_TRUE;
648 }
649
650 static SDL_bool
HIDAPI_DriverXbox360_InitDevice(SDL_HIDAPI_Device * device)651 HIDAPI_DriverXbox360_InitDevice(SDL_HIDAPI_Device *device)
652 {
653 return HIDAPI_JoystickConnected(device, NULL, SDL_FALSE);
654 }
655
656 static int
HIDAPI_DriverXbox360_GetDevicePlayerIndex(SDL_HIDAPI_Device * device,SDL_JoystickID instance_id)657 HIDAPI_DriverXbox360_GetDevicePlayerIndex(SDL_HIDAPI_Device *device, SDL_JoystickID instance_id)
658 {
659 return -1;
660 }
661
662 static void
HIDAPI_DriverXbox360_SetDevicePlayerIndex(SDL_HIDAPI_Device * device,SDL_JoystickID instance_id,int player_index)663 HIDAPI_DriverXbox360_SetDevicePlayerIndex(SDL_HIDAPI_Device *device, SDL_JoystickID instance_id, int player_index)
664 {
665 if (device->dev) {
666 SetSlotLED(device->dev, (player_index % 4));
667 }
668 }
669
670 static SDL_bool
HIDAPI_DriverXbox360_OpenJoystick(SDL_HIDAPI_Device * device,SDL_Joystick * joystick)671 HIDAPI_DriverXbox360_OpenJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick)
672 {
673 SDL_DriverXbox360_Context *ctx;
674 int player_index;
675
676 ctx = (SDL_DriverXbox360_Context *)SDL_calloc(1, sizeof(*ctx));
677 if (!ctx) {
678 SDL_OutOfMemory();
679 return SDL_FALSE;
680 }
681
682 if (device->path) { /* else opened for RAWINPUT driver */
683 device->dev = hid_open_path(device->path, 0);
684 if (!device->dev) {
685 SDL_SetError("Couldn't open %s", device->path);
686 SDL_free(ctx);
687 return SDL_FALSE;
688 }
689 }
690 device->context = ctx;
691
692 #ifdef SDL_JOYSTICK_HIDAPI_WINDOWS_XINPUT
693 xinput_device_change = SDL_TRUE;
694 ctx->xinput_enabled = SDL_GetHintBoolean(SDL_HINT_JOYSTICK_HIDAPI_CORRELATE_XINPUT, SDL_TRUE);
695 if (ctx->xinput_enabled && (WIN_LoadXInputDLL() < 0 || !XINPUTGETSTATE)) {
696 ctx->xinput_enabled = SDL_FALSE;
697 }
698 ctx->xinput_slot = XUSER_INDEX_ANY;
699 #endif
700 #ifdef SDL_JOYSTICK_HIDAPI_WINDOWS_GAMING_INPUT
701 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 SetSlotLED(device->dev, (player_index % 4));
708 }
709
710 /* Initialize the joystick capabilities */
711 joystick->nbuttons = SDL_CONTROLLER_BUTTON_MAX;
712 joystick->naxes = SDL_CONTROLLER_AXIS_MAX;
713 joystick->epowerlevel = SDL_JOYSTICK_POWER_WIRED;
714
715 return SDL_TRUE;
716 }
717
718 static int
HIDAPI_DriverXbox360_RumbleJoystick(SDL_HIDAPI_Device * device,SDL_Joystick * joystick,Uint16 low_frequency_rumble,Uint16 high_frequency_rumble)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 SDL_bool rumbled = SDL_FALSE;
727
728 #ifdef SDL_JOYSTICK_HIDAPI_WINDOWS_GAMING_INPUT
729 if (!rumbled && ctx->wgi_correlated) {
730 WindowsGamingInputGamepadState *gamepad_state = ctx->wgi_slot;
731 HRESULT hr;
732 gamepad_state->vibration.LeftMotor = (DOUBLE)low_frequency_rumble / SDL_MAX_UINT16;
733 gamepad_state->vibration.RightMotor = (DOUBLE)high_frequency_rumble / SDL_MAX_UINT16;
734 hr = __x_ABI_CWindows_CGaming_CInput_CIGamepad_put_Vibration(gamepad_state->gamepad, gamepad_state->vibration);
735 if (SUCCEEDED(hr)) {
736 rumbled = SDL_TRUE;
737 }
738 }
739 #endif
740
741 #ifdef SDL_JOYSTICK_HIDAPI_WINDOWS_XINPUT
742 if (!rumbled && ctx->xinput_correlated) {
743 XINPUT_VIBRATION XVibration;
744
745 if (!XINPUTSETSTATE) {
746 return SDL_Unsupported();
747 }
748
749 XVibration.wLeftMotorSpeed = low_frequency_rumble;
750 XVibration.wRightMotorSpeed = high_frequency_rumble;
751 if (XINPUTSETSTATE(ctx->xinput_slot, &XVibration) == ERROR_SUCCESS) {
752 rumbled = SDL_TRUE;
753 } else {
754 return SDL_SetError("XInputSetState() failed");
755 }
756 }
757 #endif /* SDL_JOYSTICK_HIDAPI_WINDOWS_XINPUT */
758
759 #else /* !__WIN32__ */
760
761 #ifdef __MACOSX__
762 if (IsBluetoothXboxOneController(device->vendor_id, device->product_id)) {
763 Uint8 rumble_packet[] = { 0x03, 0x0F, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00 };
764
765 rumble_packet[4] = (low_frequency_rumble >> 8);
766 rumble_packet[5] = (high_frequency_rumble >> 8);
767
768 if (SDL_HIDAPI_SendRumble(device, rumble_packet, sizeof(rumble_packet)) != sizeof(rumble_packet)) {
769 return SDL_SetError("Couldn't send rumble packet");
770 }
771 } else {
772 /* On Mac OS X the 360Controller driver uses this short report,
773 and we need to prefix it with a magic token so hidapi passes it through untouched
774 */
775 Uint8 rumble_packet[] = { 'M', 'A', 'G', 'I', 'C', '0', 0x00, 0x04, 0x00, 0x00 };
776
777 rumble_packet[6+2] = (low_frequency_rumble >> 8);
778 rumble_packet[6+3] = (high_frequency_rumble >> 8);
779
780 if (SDL_HIDAPI_SendRumble(device, rumble_packet, sizeof(rumble_packet)) != sizeof(rumble_packet)) {
781 return SDL_SetError("Couldn't send rumble packet");
782 }
783 }
784 #else
785 Uint8 rumble_packet[] = { 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
786
787 rumble_packet[3] = (low_frequency_rumble >> 8);
788 rumble_packet[4] = (high_frequency_rumble >> 8);
789
790 if (SDL_HIDAPI_SendRumble(device, rumble_packet, sizeof(rumble_packet)) != sizeof(rumble_packet)) {
791 return SDL_SetError("Couldn't send rumble packet");
792 }
793 #endif
794 #endif /* __WIN32__ */
795
796 return 0;
797 }
798
799 #ifdef __WIN32__
800 /* This is the packet format for Xbox 360 and Xbox One controllers on Windows,
801 however with this interface there is no rumble support, no guide button,
802 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
HIDAPI_DriverXbox360_HandleStatePacket(SDL_Joystick * joystick,hid_device * dev,SDL_DriverXbox360_Context * ctx,Uint8 * data,int size)807 HIDAPI_DriverXbox360_HandleStatePacket(SDL_Joystick *joystick, hid_device *dev, SDL_DriverXbox360_Context *ctx, Uint8 *data, int size)
808 {
809 #ifdef SDL_JOYSTICK_HIDAPI_WINDOWS_MATCHING
810 Uint32 match_state = ctx->match_state;
811 /* Update match_state with button bit, then fall through */
812 # define SDL_PrivateJoystickButton(joystick, button, state) if (state) match_state |= 1 << (button); else match_state &=~(1<<(button)); SDL_PrivateJoystickButton(joystick, button, state)
813 /* Grab high 4 bits of value, then fall through */
814 # 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)
815 #endif
816 Sint16 axis;
817 SDL_bool has_trigger_data = SDL_FALSE;
818
819 if (ctx->last_state[10] != data[10]) {
820 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_A, (data[10] & 0x01) ? SDL_PRESSED : SDL_RELEASED);
821 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_B, (data[10] & 0x02) ? SDL_PRESSED : SDL_RELEASED);
822 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_X, (data[10] & 0x04) ? SDL_PRESSED : SDL_RELEASED);
823 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_Y, (data[10] & 0x08) ? SDL_PRESSED : SDL_RELEASED);
824 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_LEFTSHOULDER, (data[10] & 0x10) ? SDL_PRESSED : SDL_RELEASED);
825 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER, (data[10] & 0x20) ? SDL_PRESSED : SDL_RELEASED);
826 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_BACK, (data[10] & 0x40) ? SDL_PRESSED : SDL_RELEASED);
827 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_START, (data[10] & 0x80) ? SDL_PRESSED : SDL_RELEASED);
828 }
829
830 if (ctx->last_state[11] != data[11]) {
831 SDL_bool dpad_up = SDL_FALSE;
832 SDL_bool dpad_down = SDL_FALSE;
833 SDL_bool dpad_left = SDL_FALSE;
834 SDL_bool dpad_right = SDL_FALSE;
835
836 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_LEFTSTICK, (data[11] & 0x01) ? SDL_PRESSED : SDL_RELEASED);
837 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_RIGHTSTICK, (data[11] & 0x02) ? SDL_PRESSED : SDL_RELEASED);
838
839 switch (data[11] & 0x3C) {
840 case 4:
841 dpad_up = SDL_TRUE;
842 break;
843 case 8:
844 dpad_up = SDL_TRUE;
845 dpad_right = SDL_TRUE;
846 break;
847 case 12:
848 dpad_right = SDL_TRUE;
849 break;
850 case 16:
851 dpad_right = SDL_TRUE;
852 dpad_down = SDL_TRUE;
853 break;
854 case 20:
855 dpad_down = SDL_TRUE;
856 break;
857 case 24:
858 dpad_left = SDL_TRUE;
859 dpad_down = SDL_TRUE;
860 break;
861 case 28:
862 dpad_left = SDL_TRUE;
863 break;
864 case 32:
865 dpad_up = SDL_TRUE;
866 dpad_left = SDL_TRUE;
867 break;
868 default:
869 break;
870 }
871 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_DOWN, dpad_down);
872 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_UP, dpad_up);
873 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_RIGHT, dpad_right);
874 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_LEFT, dpad_left);
875 }
876
877 axis = (int)*(Uint16*)(&data[0]) - 0x8000;
878 SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_LEFTX, axis);
879 axis = (int)*(Uint16*)(&data[2]) - 0x8000;
880 SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_LEFTY, axis);
881 axis = (int)*(Uint16*)(&data[4]) - 0x8000;
882 SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_RIGHTX, axis);
883 axis = (int)*(Uint16*)(&data[6]) - 0x8000;
884 SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_RIGHTY, axis);
885
886 #ifdef SDL_JOYSTICK_HIDAPI_WINDOWS_MATCHING
887 #undef SDL_PrivateJoystickAxis
888 #endif
889
890 #ifdef SDL_JOYSTICK_HIDAPI_WINDOWS_XINPUT
891 /* Prefer XInput over WindowsGamingInput, it continues to provide data in the background */
892 if (!has_trigger_data && ctx->xinput_enabled && ctx->xinput_correlated) {
893 has_trigger_data = SDL_TRUE;
894 }
895 #endif /* SDL_JOYSTICK_HIDAPI_WINDOWS_XINPUT */
896
897 #ifdef SDL_JOYSTICK_HIDAPI_WINDOWS_GAMING_INPUT
898 if (!has_trigger_data && ctx->wgi_correlated) {
899 has_trigger_data = SDL_TRUE;
900 }
901 #endif /* SDL_JOYSTICK_HIDAPI_WINDOWS_GAMING_INPUT */
902
903 if (!has_trigger_data) {
904 axis = (data[9] * 257) - 32768;
905 if (data[9] < 0x80) {
906 axis = -axis * 2 - 32769;
907 SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERLEFT, SDL_MIN_SINT16);
908 SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERRIGHT, axis);
909 } else if (data[9] > 0x80) {
910 axis = axis * 2 - 32767;
911 SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERLEFT, axis);
912 SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERRIGHT, SDL_MIN_SINT16);
913 } else {
914 SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERLEFT, SDL_MIN_SINT16);
915 SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERRIGHT, SDL_MIN_SINT16);
916 }
917 }
918
919 #ifdef SDL_JOYSTICK_HIDAPI_WINDOWS_MATCHING
920 ctx->match_state = match_state;
921 ctx->last_state_packet = SDL_GetTicks();
922 #undef SDL_PrivateJoystickButton
923 #endif
924 SDL_memcpy(ctx->last_state, data, SDL_min(size, sizeof(ctx->last_state)));
925 }
926
927 #ifdef SDL_JOYSTICK_RAWINPUT
928 static void
HIDAPI_DriverXbox360_HandleStatePacketFromRAWINPUT(SDL_HIDAPI_Device * device,SDL_Joystick * joystick,Uint8 * data,int size)929 HIDAPI_DriverXbox360_HandleStatePacketFromRAWINPUT(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, Uint8 *data, int size)
930 {
931 SDL_DriverXbox360_Context *ctx = (SDL_DriverXbox360_Context *)device->context;
932 HIDAPI_DriverXbox360_HandleStatePacket(joystick, NULL, ctx, data, size);
933 }
934 #endif
935
936 #else
937
938 static void
HIDAPI_DriverXbox360_HandleStatePacket(SDL_Joystick * joystick,hid_device * dev,SDL_DriverXbox360_Context * ctx,Uint8 * data,int size)939 HIDAPI_DriverXbox360_HandleStatePacket(SDL_Joystick *joystick, hid_device *dev, SDL_DriverXbox360_Context *ctx, Uint8 *data, int size)
940 {
941 Sint16 axis;
942 #ifdef __MACOSX__
943 const SDL_bool invert_y_axes = SDL_FALSE;
944 #else
945 const SDL_bool invert_y_axes = SDL_TRUE;
946 #endif
947
948 if (ctx->last_state[2] != data[2]) {
949 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_UP, (data[2] & 0x01) ? SDL_PRESSED : SDL_RELEASED);
950 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_DOWN, (data[2] & 0x02) ? SDL_PRESSED : SDL_RELEASED);
951 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_LEFT, (data[2] & 0x04) ? SDL_PRESSED : SDL_RELEASED);
952 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_RIGHT, (data[2] & 0x08) ? SDL_PRESSED : SDL_RELEASED);
953 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_START, (data[2] & 0x10) ? SDL_PRESSED : SDL_RELEASED);
954 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_BACK, (data[2] & 0x20) ? SDL_PRESSED : SDL_RELEASED);
955 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_LEFTSTICK, (data[2] & 0x40) ? SDL_PRESSED : SDL_RELEASED);
956 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_RIGHTSTICK, (data[2] & 0x80) ? SDL_PRESSED : SDL_RELEASED);
957 }
958
959 if (ctx->last_state[3] != data[3]) {
960 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_LEFTSHOULDER, (data[3] & 0x01) ? SDL_PRESSED : SDL_RELEASED);
961 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER, (data[3] & 0x02) ? SDL_PRESSED : SDL_RELEASED);
962 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_GUIDE, (data[3] & 0x04) ? SDL_PRESSED : SDL_RELEASED);
963 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_A, (data[3] & 0x10) ? SDL_PRESSED : SDL_RELEASED);
964 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_B, (data[3] & 0x20) ? SDL_PRESSED : SDL_RELEASED);
965 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_X, (data[3] & 0x40) ? SDL_PRESSED : SDL_RELEASED);
966 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_Y, (data[3] & 0x80) ? SDL_PRESSED : SDL_RELEASED);
967 }
968
969 axis = ((int)data[4] * 257) - 32768;
970 SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERLEFT, axis);
971 axis = ((int)data[5] * 257) - 32768;
972 SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERRIGHT, axis);
973 axis = *(Sint16*)(&data[6]);
974 SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_LEFTX, axis);
975 axis = *(Sint16*)(&data[8]);
976 if (invert_y_axes) {
977 axis = ~axis;
978 }
979 SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_LEFTY, axis);
980 axis = *(Sint16*)(&data[10]);
981 SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_RIGHTX, axis);
982 axis = *(Sint16*)(&data[12]);
983 if (invert_y_axes) {
984 axis = ~axis;
985 }
986 SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_RIGHTY, axis);
987
988 SDL_memcpy(ctx->last_state, data, SDL_min(size, sizeof(ctx->last_state)));
989 }
990 #endif /* __WIN32__ */
991
992 static void
HIDAPI_DriverXbox360_UpdateOtherAPIs(SDL_HIDAPI_Device * device,SDL_Joystick * joystick)993 HIDAPI_DriverXbox360_UpdateOtherAPIs(SDL_HIDAPI_Device *device, SDL_Joystick *joystick)
994 {
995 #if defined(SDL_JOYSTICK_HIDAPI_WINDOWS_MATCHING) || defined(SDL_JOYSTICK_HIDAPI_WINDOWS_XINPUT) || defined(SDL_JOYSTICK_HIDAPI_WINDOWS_GAMING_INPUT)
996 SDL_DriverXbox360_Context *ctx = (SDL_DriverXbox360_Context *)device->context;
997 SDL_bool has_trigger_data = SDL_FALSE;
998 SDL_bool correlated = SDL_FALSE;
999 #ifdef SDL_JOYSTICK_HIDAPI_WINDOWS_MATCHING
1000 WindowsMatchState match_state_xinput;
1001 #endif
1002
1003 /* Poll for trigger data once (not per-state-packet) */
1004 #ifdef SDL_JOYSTICK_HIDAPI_WINDOWS_XINPUT
1005 /* Prefer XInput over WindowsGamingInput, it continues to provide data in the background */
1006 if (!has_trigger_data && ctx->xinput_enabled && ctx->xinput_correlated) {
1007 HIDAPI_DriverXbox360_UpdateXInput();
1008 if (xinput_state[ctx->xinput_slot].connected) {
1009 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_GUIDE, (xinput_state[ctx->xinput_slot].state.Gamepad.wButtons & XINPUT_GAMEPAD_GUIDE) ? SDL_PRESSED : SDL_RELEASED);
1010 SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERLEFT, ((int)xinput_state[ctx->xinput_slot].state.Gamepad.bLeftTrigger * 257) - 32768);
1011 SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERRIGHT, ((int)xinput_state[ctx->xinput_slot].state.Gamepad.bRightTrigger * 257) - 32768);
1012 has_trigger_data = SDL_TRUE;
1013 }
1014 }
1015 #endif /* SDL_JOYSTICK_HIDAPI_WINDOWS_XINPUT */
1016
1017 #ifdef SDL_JOYSTICK_HIDAPI_WINDOWS_GAMING_INPUT
1018 if (!has_trigger_data && ctx->wgi_correlated) {
1019 HIDAPI_DriverXbox360_UpdateWindowsGamingInput(); /* May detect disconnect / cause uncorrelation */
1020 if (ctx->wgi_correlated) { /* Still connected */
1021 struct __x_ABI_CWindows_CGaming_CInput_CGamepadReading *state = &ctx->wgi_slot->state;
1022 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_GUIDE, (state->Buttons & GamepadButtons_GUIDE) ? SDL_PRESSED : SDL_RELEASED);
1023 SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERLEFT, ((int)(state->LeftTrigger * SDL_MAX_UINT16)) - 32768);
1024 SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERRIGHT, ((int)(state->RightTrigger * SDL_MAX_UINT16)) - 32768);
1025 has_trigger_data = SDL_TRUE;
1026 }
1027 }
1028 #endif /* SDL_JOYSTICK_HIDAPI_WINDOWS_GAMING_INPUT */
1029
1030 #ifdef SDL_JOYSTICK_HIDAPI_WINDOWS_MATCHING
1031 HIDAPI_DriverXbox360_FillMatchState(&match_state_xinput, ctx->match_state);
1032
1033 #ifdef SDL_JOYSTICK_HIDAPI_WINDOWS_GAMING_INPUT
1034 /* Parallel logic to WINDOWS_XINPUT below */
1035 HIDAPI_DriverXbox360_UpdateWindowsGamingInput();
1036 if (ctx->wgi_correlated) {
1037 /* We have been previously correlated, ensure we are still matching, see comments in XINPUT section */
1038 if (HIDAPI_DriverXbox360_WindowsGamingInputSlotMatches(&match_state_xinput, ctx->wgi_slot)) {
1039 ctx->wgi_uncorrelate_count = 0;
1040 } else {
1041 ++ctx->wgi_uncorrelate_count;
1042 /* Only un-correlate if this is consistent over multiple Update() calls - the timing of polling/event
1043 pumping can easily cause this to uncorrelate for a frame. 2 seemed reliable in my testing, but
1044 let's set it to 3 to be safe. An incorrect un-correlation will simply result in lower precision
1045 triggers for a frame. */
1046 if (ctx->wgi_uncorrelate_count >= 3) {
1047 #ifdef DEBUG_JOYSTICK
1048 SDL_Log("UN-Correlated joystick %d to WindowsGamingInput device #%d\n", joystick->instance_id, ctx->wgi_slot);
1049 #endif
1050 HIDAPI_DriverXbox360_MarkWindowsGamingInputSlotFree(ctx->wgi_slot);
1051 ctx->wgi_correlated = SDL_FALSE;
1052 ctx->wgi_correlation_count = 0;
1053 /* Force immediate update of triggers */
1054 HIDAPI_DriverXbox360_HandleStatePacket(joystick, NULL, ctx, ctx->last_state, sizeof(ctx->last_state));
1055 /* Force release of Guide button, it can't possibly be down on this device now. */
1056 /* It gets left down if we were actually correlated incorrectly and it was released on the WindowsGamingInput
1057 device but we didn't get a state packet. */
1058 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_GUIDE, SDL_RELEASED);
1059 }
1060 }
1061 }
1062 if (!ctx->wgi_correlated) {
1063 SDL_bool new_correlation_count = 0;
1064 if (HIDAPI_DriverXbox360_MissingWindowsGamingInputSlot()) {
1065 Uint8 correlation_id;
1066 WindowsGamingInputGamepadState *slot_idx;
1067 if (HIDAPI_DriverXbox360_GuessWindowsGamingInputSlot(&match_state_xinput, &correlation_id, &slot_idx)) {
1068 /* we match exactly one WindowsGamingInput device */
1069 /* Probably can do without wgi_correlation_count, just check and clear wgi_slot to NULL, unless we need
1070 even more frames to be sure. */
1071 if (ctx->wgi_correlation_count && ctx->wgi_slot == slot_idx) {
1072 /* was correlated previously, and still the same device */
1073 if (ctx->wgi_correlation_id + 1 == correlation_id) {
1074 /* no one else was correlated in the meantime */
1075 new_correlation_count = ctx->wgi_correlation_count + 1;
1076 if (new_correlation_count == 2) {
1077 /* correlation stayed steady and uncontested across multiple frames, guaranteed match */
1078 ctx->wgi_correlated = SDL_TRUE;
1079 #ifdef DEBUG_JOYSTICK
1080 SDL_Log("Correlated joystick %d to WindowsGamingInput device #%d\n", joystick->instance_id, slot_idx);
1081 #endif
1082 correlated = SDL_TRUE;
1083 HIDAPI_DriverXbox360_MarkWindowsGamingInputSlotUsed(ctx->wgi_slot, ctx);
1084 /* If the generalized Guide button was using us, it doesn't need to anymore */
1085 if (guide_button_candidate.joystick == joystick)
1086 guide_button_candidate.joystick = NULL;
1087 if (guide_button_candidate.last_joystick == joystick)
1088 guide_button_candidate.last_joystick = NULL;
1089 /* Force immediate update of guide button / triggers */
1090 HIDAPI_DriverXbox360_HandleStatePacket(joystick, NULL, ctx, ctx->last_state, sizeof(ctx->last_state));
1091 }
1092 } else {
1093 /* someone else also possibly correlated to this device, start over */
1094 new_correlation_count = 1;
1095 }
1096 } else {
1097 /* new possible correlation */
1098 new_correlation_count = 1;
1099 ctx->wgi_slot = slot_idx;
1100 }
1101 ctx->wgi_correlation_id = correlation_id;
1102 } else {
1103 /* Match multiple WindowsGamingInput devices, or none (possibly due to no buttons pressed) */
1104 }
1105 }
1106 ctx->wgi_correlation_count = new_correlation_count;
1107 } else {
1108 correlated = SDL_TRUE;
1109 }
1110 #endif
1111
1112 #ifdef SDL_JOYSTICK_HIDAPI_WINDOWS_XINPUT
1113 /* Parallel logic to WINDOWS_GAMING_INPUT above */
1114 if (ctx->xinput_enabled) {
1115 HIDAPI_DriverXbox360_UpdateXInput();
1116 if (ctx->xinput_correlated) {
1117 /* We have been previously correlated, ensure we are still matching */
1118 /* This is required to deal with two (mostly) un-preventable mis-correlation situations:
1119 A) Since the HID data stream does not provide an initial state (but polling XInput does), if we open
1120 5 controllers (#1-4 XInput mapped, #5 is not), and controller 1 had the A button down (and we don't
1121 know), and the user presses A on controller #5, we'll see exactly 1 controller with A down (#5) and
1122 exactly 1 XInput device with A down (#1), and incorrectly correlate. This code will then un-correlate
1123 when A is released from either controller #1 or #5.
1124 B) Since the app may not open all controllers, we could have a similar situation where only controller #5
1125 is opened, and the user holds A on controllers #1 and #5 simultaneously - again we see only 1 controller
1126 with A down and 1 XInput device with A down, and incorrectly correlate. This should be very unusual
1127 (only when apps do not open all controllers, yet are listening to Guide button presses, yet
1128 for some reason want to ignore guide button presses on the un-opened controllers, yet users are
1129 pressing buttons on the unopened controllers), and will resolve itself when either button is released
1130 and we un-correlate. We could prevent this by processing the state packets for *all* controllers,
1131 even un-opened ones, as that would allow more precise correlation.
1132 */
1133 if (HIDAPI_DriverXbox360_XInputSlotMatches(&match_state_xinput, ctx->xinput_slot)) {
1134 ctx->xinput_uncorrelate_count = 0;
1135 } else {
1136 ++ctx->xinput_uncorrelate_count;
1137 /* Only un-correlate if this is consistent over multiple Update() calls - the timing of polling/event
1138 pumping can easily cause this to uncorrelate for a frame. 2 seemed reliable in my testing, but
1139 let's set it to 3 to be safe. An incorrect un-correlation will simply result in lower precision
1140 triggers for a frame. */
1141 if (ctx->xinput_uncorrelate_count >= 3) {
1142 #ifdef DEBUG_JOYSTICK
1143 SDL_Log("UN-Correlated joystick %d to XInput device #%d\n", joystick->instance_id, ctx->xinput_slot);
1144 #endif
1145 HIDAPI_DriverXbox360_MarkXInputSlotFree(ctx->xinput_slot);
1146 ctx->xinput_correlated = SDL_FALSE;
1147 ctx->xinput_correlation_count = 0;
1148 /* Force immediate update of triggers */
1149 HIDAPI_DriverXbox360_HandleStatePacket(joystick, NULL, ctx, ctx->last_state, sizeof(ctx->last_state));
1150 /* Force release of Guide button, it can't possibly be down on this device now. */
1151 /* It gets left down if we were actually correlated incorrectly and it was released on the XInput
1152 device but we didn't get a state packet. */
1153 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_GUIDE, SDL_RELEASED);
1154 }
1155 }
1156 }
1157 if (!ctx->xinput_correlated) {
1158 SDL_bool new_correlation_count = 0;
1159 if (HIDAPI_DriverXbox360_MissingXInputSlot()) {
1160 Uint8 correlation_id = 0;
1161 Uint8 slot_idx = 0;
1162 if (HIDAPI_DriverXbox360_GuessXInputSlot(&match_state_xinput, &correlation_id, &slot_idx)) {
1163 /* we match exactly one XInput device */
1164 /* Probably can do without xinput_correlation_count, just check and clear xinput_slot to ANY, unless
1165 we need even more frames to be sure */
1166 if (ctx->xinput_correlation_count && ctx->xinput_slot == slot_idx) {
1167 /* was correlated previously, and still the same device */
1168 if (ctx->xinput_correlation_id + 1 == correlation_id) {
1169 /* no one else was correlated in the meantime */
1170 new_correlation_count = ctx->xinput_correlation_count + 1;
1171 if (new_correlation_count == 2) {
1172 /* correlation stayed steady and uncontested across multiple frames, guaranteed match */
1173 ctx->xinput_correlated = SDL_TRUE;
1174 #ifdef DEBUG_JOYSTICK
1175 SDL_Log("Correlated joystick %d to XInput device #%d\n", joystick->instance_id, slot_idx);
1176 #endif
1177 correlated = SDL_TRUE;
1178 HIDAPI_DriverXbox360_MarkXInputSlotUsed(ctx->xinput_slot);
1179 /* If the generalized Guide button was using us, it doesn't need to anymore */
1180 if (guide_button_candidate.joystick == joystick)
1181 guide_button_candidate.joystick = NULL;
1182 if (guide_button_candidate.last_joystick == joystick)
1183 guide_button_candidate.last_joystick = NULL;
1184 /* Force immediate update of guide button / triggers */
1185 HIDAPI_DriverXbox360_HandleStatePacket(joystick, NULL, ctx, ctx->last_state, sizeof(ctx->last_state));
1186 }
1187 } else {
1188 /* someone else also possibly correlated to this device, start over */
1189 new_correlation_count = 1;
1190 }
1191 } else {
1192 /* new possible correlation */
1193 new_correlation_count = 1;
1194 ctx->xinput_slot = slot_idx;
1195 }
1196 ctx->xinput_correlation_id = correlation_id;
1197 } else {
1198 /* Match multiple XInput devices, or none (possibly due to no buttons pressed) */
1199 }
1200 }
1201 ctx->xinput_correlation_count = new_correlation_count;
1202 } else {
1203 correlated = SDL_TRUE;
1204 }
1205 }
1206 #endif
1207
1208 if (!correlated) {
1209 if (!guide_button_candidate.joystick ||
1210 (ctx->last_state_packet && (
1211 !guide_button_candidate.last_state_packet ||
1212 SDL_TICKS_PASSED(ctx->last_state_packet, guide_button_candidate.last_state_packet)
1213 ))
1214 ) {
1215 guide_button_candidate.joystick = joystick;
1216 guide_button_candidate.last_state_packet = ctx->last_state_packet;
1217 }
1218 }
1219 #endif
1220 #endif
1221 }
1222
1223 static SDL_bool
HIDAPI_DriverXbox360_UpdateDevice(SDL_HIDAPI_Device * device)1224 HIDAPI_DriverXbox360_UpdateDevice(SDL_HIDAPI_Device *device)
1225 {
1226 SDL_DriverXbox360_Context *ctx = (SDL_DriverXbox360_Context *)device->context;
1227 SDL_Joystick *joystick = NULL;
1228 Uint8 data[USB_PACKET_LENGTH];
1229 int size = 0;
1230
1231 if (device->num_joysticks > 0) {
1232 joystick = SDL_JoystickFromInstanceID(device->joysticks[0]);
1233 }
1234 if (!joystick) {
1235 return SDL_FALSE;
1236 }
1237
1238 while (device->dev && (size = hid_read_timeout(device->dev, data, sizeof(data), 0)) > 0) {
1239 HIDAPI_DriverXbox360_HandleStatePacket(joystick, device->dev, ctx, data, size);
1240 }
1241
1242 if (size < 0) {
1243 /* Read error, device is disconnected */
1244 HIDAPI_JoystickDisconnected(device, joystick->instance_id, SDL_FALSE);
1245 } else {
1246 HIDAPI_DriverXbox360_UpdateOtherAPIs(device, joystick);
1247 }
1248
1249 return (size >= 0);
1250 }
1251
1252 static void
HIDAPI_DriverXbox360_CloseJoystick(SDL_HIDAPI_Device * device,SDL_Joystick * joystick)1253 HIDAPI_DriverXbox360_CloseJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick)
1254 {
1255 #if defined(SDL_JOYSTICK_HIDAPI_WINDOWS_XINPUT) || defined(SDL_JOYSTICK_HIDAPI_WINDOWS_GAMING_INPUT)
1256 SDL_DriverXbox360_Context *ctx = (SDL_DriverXbox360_Context *)device->context;
1257 #endif
1258
1259 #ifdef SDL_JOYSTICK_HIDAPI_WINDOWS_MATCHING
1260 if (guide_button_candidate.joystick == joystick)
1261 guide_button_candidate.joystick = NULL;
1262 if (guide_button_candidate.last_joystick == joystick)
1263 guide_button_candidate.last_joystick = NULL;
1264 #endif
1265
1266 #ifdef SDL_JOYSTICK_HIDAPI_WINDOWS_XINPUT
1267 xinput_device_change = SDL_TRUE;
1268 if (ctx->xinput_enabled) {
1269 if (ctx->xinput_correlated) {
1270 HIDAPI_DriverXbox360_MarkXInputSlotFree(ctx->xinput_slot);
1271 }
1272 WIN_UnloadXInputDLL();
1273 }
1274 #endif
1275 #ifdef SDL_JOYSTICK_HIDAPI_WINDOWS_GAMING_INPUT
1276 HIDAPI_DriverXbox360_QuitWindowsGamingInput(ctx);
1277 #endif
1278
1279 if (device->dev) {
1280 hid_close(device->dev);
1281 device->dev = NULL;
1282 }
1283
1284 SDL_free(device->context);
1285 device->context = NULL;
1286 }
1287
1288 static void
HIDAPI_DriverXbox360_FreeDevice(SDL_HIDAPI_Device * device)1289 HIDAPI_DriverXbox360_FreeDevice(SDL_HIDAPI_Device *device)
1290 {
1291 }
1292
1293 SDL_HIDAPI_DeviceDriver SDL_HIDAPI_DriverXbox360 =
1294 {
1295 SDL_HINT_JOYSTICK_HIDAPI_XBOX,
1296 SDL_TRUE,
1297 HIDAPI_DriverXbox360_IsSupportedDevice,
1298 HIDAPI_DriverXbox360_GetDeviceName,
1299 HIDAPI_DriverXbox360_InitDevice,
1300 HIDAPI_DriverXbox360_GetDevicePlayerIndex,
1301 HIDAPI_DriverXbox360_SetDevicePlayerIndex,
1302 HIDAPI_DriverXbox360_UpdateDevice,
1303 HIDAPI_DriverXbox360_OpenJoystick,
1304 HIDAPI_DriverXbox360_RumbleJoystick,
1305 HIDAPI_DriverXbox360_CloseJoystick,
1306 HIDAPI_DriverXbox360_FreeDevice,
1307 HIDAPI_DriverXbox360_PostUpdate,
1308 #ifdef SDL_JOYSTICK_RAWINPUT
1309 HIDAPI_DriverXbox360_HandleStatePacketFromRAWINPUT,
1310 #endif
1311 };
1312
1313 #endif /* SDL_JOYSTICK_HIDAPI_XBOX360 */
1314
1315 #endif /* SDL_JOYSTICK_HIDAPI */
1316
1317 /* vi: set ts=4 sw=4 expandtab: */
1318