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