src/joystick/hidapi/SDL_hidapi_xbox360.c
author Ryan C. Gordon <icculus@icculus.org>
Wed, 20 May 2020 17:01:25 -0400
changeset 13840 df7f6873371a
parent 13758 dcf07006dda6
permissions -rw-r--r--
hidapi: Fix compiler warning.
     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 
   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
   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
   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
   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
   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
   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
   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
   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
   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
   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
   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
   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
   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
   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
   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
   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
   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
   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 *
   634 HIDAPI_DriverXbox360_GetDeviceName(Uint16 vendor_id, Uint16 product_id)
   635 {
   636     return NULL;
   637 }
   638 
   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
   651 HIDAPI_DriverXbox360_InitDevice(SDL_HIDAPI_Device *device)
   652 {
   653     return HIDAPI_JoystickConnected(device, NULL, SDL_FALSE);
   654 }
   655 
   656 static int
   657 HIDAPI_DriverXbox360_GetDevicePlayerIndex(SDL_HIDAPI_Device *device, SDL_JoystickID instance_id)
   658 {
   659     return -1;
   660 }
   661 
   662 static void
   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
   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
   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
   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
   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
   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
   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
  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
  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
  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: */