src/joystick/hidapi/SDL_hidapi_xbox360.c
author Sam Lantinga
Mon, 02 Mar 2020 09:46:37 -0800
changeset 13571 b2b43ee5f1bf
parent 13568 2c4ec356333e
child 13574 09a0db265f3d
permissions -rw-r--r--
Fixed compile 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_log.h"
    27 #include "SDL_events.h"
    28 #include "SDL_timer.h"
    29 #include "SDL_joystick.h"
    30 #include "SDL_gamecontroller.h"
    31 #include "../SDL_sysjoystick.h"
    32 #include "SDL_hidapijoystick_c.h"
    33 #include "SDL_hidapi_rumble.h"
    34 
    35 
    36 #ifdef SDL_JOYSTICK_HIDAPI_XBOX360
    37 
    38 #ifdef __WIN32__
    39 #define SDL_JOYSTICK_HIDAPI_WINDOWS_XINPUT
    40 /* This requires the Windows 10 SDK to build */
    41 /*#define SDL_JOYSTICK_HIDAPI_WINDOWS_GAMING_INPUT*/
    42 #endif
    43 
    44 #ifdef SDL_JOYSTICK_HIDAPI_WINDOWS_XINPUT
    45 #include "../../core/windows/SDL_xinput.h"
    46 #endif
    47 
    48 #ifdef SDL_JOYSTICK_HIDAPI_WINDOWS_GAMING_INPUT
    49 #include "../../core/windows/SDL_windows.h"
    50 #define COBJMACROS
    51 #include "windows.gaming.input.h"
    52 #endif
    53 
    54 
    55 typedef struct {
    56     Uint8 last_state[USB_PACKET_LENGTH];
    57 #ifdef SDL_JOYSTICK_HIDAPI_WINDOWS_XINPUT
    58     SDL_bool xinput_enabled;
    59     Uint8 xinput_slot;
    60 #endif
    61 #ifdef SDL_JOYSTICK_HIDAPI_WINDOWS_GAMING_INPUT
    62     SDL_bool coinitialized;
    63     __x_ABI_CWindows_CGaming_CInput_CIGamepadStatics *gamepad_statics;
    64     __x_ABI_CWindows_CGaming_CInput_CIGamepad *gamepad;
    65     struct __x_ABI_CWindows_CGaming_CInput_CGamepadVibration vibration;
    66 #endif
    67 } SDL_DriverXbox360_Context;
    68 
    69 
    70 #ifdef SDL_JOYSTICK_HIDAPI_WINDOWS_XINPUT
    71 static Uint8 xinput_slots;
    72 
    73 static void
    74 HIDAPI_DriverXbox360_MarkXInputSlotUsed(Uint8 xinput_slot)
    75 {
    76     if (xinput_slot != XUSER_INDEX_ANY) {
    77         xinput_slots |= (0x01 << xinput_slot);
    78     }
    79 }
    80 
    81 static void
    82 HIDAPI_DriverXbox360_MarkXInputSlotFree(Uint8 xinput_slot)
    83 {
    84     if (xinput_slot != XUSER_INDEX_ANY) {
    85         xinput_slots &= ~(0x01 << xinput_slot);
    86     }
    87 }
    88 
    89 static SDL_bool
    90 HIDAPI_DriverXbox360_MissingXInputSlot()
    91 {
    92     return xinput_slots != 0x0F;
    93 }
    94 
    95 static Uint8
    96 HIDAPI_DriverXbox360_GuessXInputSlot(WORD wButtons)
    97 {
    98     DWORD user_index;
    99     int match_count;
   100     Uint8 match_slot;
   101 
   102     if (!XINPUTGETSTATE) {
   103         return XUSER_INDEX_ANY;
   104     }
   105 
   106     match_count = 0;
   107     for (user_index = 0; user_index < XUSER_MAX_COUNT; ++user_index) {
   108         XINPUT_STATE_EX xinput_state;
   109 
   110         if (XINPUTGETSTATE(user_index, &xinput_state) == ERROR_SUCCESS) {
   111             if (xinput_state.Gamepad.wButtons == wButtons) {
   112                 ++match_count;
   113                 match_slot = (Uint8)user_index;
   114             }
   115         }
   116     }
   117     if (match_count == 1) {
   118         return match_slot;
   119     }
   120     return XUSER_INDEX_ANY;
   121 }
   122 
   123 #endif /* SDL_JOYSTICK_HIDAPI_WINDOWS_XINPUT */
   124 
   125 #ifdef SDL_JOYSTICK_HIDAPI_WINDOWS_GAMING_INPUT
   126 
   127 static void
   128 HIDAPI_DriverXbox360_InitWindowsGamingInput(SDL_DriverXbox360_Context *ctx)
   129 {
   130     /* I think this takes care of RoInitialize() in a way that is compatible with the rest of SDL */
   131     if (FAILED(WIN_CoInitialize())) {
   132         return;
   133     }
   134     ctx->coinitialized = SDL_TRUE;
   135 
   136     {
   137         static const IID SDL_IID_IGamepadStatics = { 0x8BBCE529, 0xD49C, 0x39E9, { 0x95, 0x60, 0xE4, 0x7D, 0xDE, 0x96, 0xB7, 0xC8 } };
   138         HRESULT hr;
   139         HMODULE hModule = LoadLibraryA("combase.dll");
   140         if (hModule != NULL) {
   141             typedef HRESULT (WINAPI *WindowsCreateString_t)(PCNZWCH sourceString, UINT32 length, HSTRING* string);
   142             typedef HRESULT (WINAPI *WindowsDeleteString_t)(HSTRING string);
   143             typedef HRESULT (WINAPI *RoGetActivationFactory_t)(HSTRING activatableClassId, REFIID iid, void** factory);
   144 
   145             WindowsCreateString_t WindowsCreateStringFunc = (WindowsCreateString_t)GetProcAddress(hModule, "WindowsCreateString");
   146             WindowsDeleteString_t WindowsDeleteStringFunc = (WindowsDeleteString_t)GetProcAddress(hModule, "WindowsDeleteString");
   147             RoGetActivationFactory_t RoGetActivationFactoryFunc = (RoGetActivationFactory_t)GetProcAddress(hModule, "RoGetActivationFactory");
   148             if (WindowsCreateStringFunc && WindowsDeleteStringFunc && RoGetActivationFactoryFunc) {
   149                 LPTSTR pNamespace = L"Windows.Gaming.Input.Gamepad";
   150                 HSTRING hNamespaceString;
   151 
   152                 hr = WindowsCreateStringFunc(pNamespace, SDL_wcslen(pNamespace), &hNamespaceString);
   153                 if (SUCCEEDED(hr)) {
   154                     RoGetActivationFactoryFunc(hNamespaceString, &SDL_IID_IGamepadStatics, &ctx->gamepad_statics);
   155                     WindowsDeleteStringFunc(hNamespaceString);
   156                 }
   157             }
   158             FreeLibrary(hModule);
   159         }
   160     }
   161 }
   162 
   163 static Uint8
   164 HIDAPI_DriverXbox360_GetGamepadButtonsForMatch(__x_ABI_CWindows_CGaming_CInput_CIGamepad *gamepad)
   165 {
   166     HRESULT hr;
   167     struct __x_ABI_CWindows_CGaming_CInput_CGamepadReading state;
   168     Uint8 buttons = 0;
   169 
   170     hr = __x_ABI_CWindows_CGaming_CInput_CIGamepad_GetCurrentReading(gamepad, &state);
   171     if (SUCCEEDED(hr)) {
   172         if (state.Buttons & GamepadButtons_A) {
   173             buttons |= (1 << SDL_CONTROLLER_BUTTON_A);
   174         }
   175         if (state.Buttons & GamepadButtons_B) {
   176             buttons |= (1 << SDL_CONTROLLER_BUTTON_B);
   177         }
   178         if (state.Buttons & GamepadButtons_X) {
   179             buttons |= (1 << SDL_CONTROLLER_BUTTON_X);
   180         }
   181         if (state.Buttons & GamepadButtons_Y) {
   182             buttons |= (1 << SDL_CONTROLLER_BUTTON_Y);
   183         }
   184     }
   185     return buttons;
   186 }
   187 
   188 static void
   189 HIDAPI_DriverXbox360_GuessGamepad(SDL_DriverXbox360_Context *ctx, Uint8 buttons)
   190 {
   191     HRESULT hr;
   192     __FIVectorView_1_Windows__CGaming__CInput__CGamepad *gamepads;
   193 
   194     hr = __x_ABI_CWindows_CGaming_CInput_CIGamepadStatics_get_Gamepads(ctx->gamepad_statics, &gamepads);
   195     if (SUCCEEDED(hr)) {
   196         unsigned int i, num_gamepads;
   197 
   198         hr = __FIVectorView_1_Windows__CGaming__CInput__CGamepad_get_Size(gamepads, &num_gamepads);
   199         if (SUCCEEDED(hr)) {
   200             int match_count;
   201             unsigned int match_slot;
   202 
   203             match_count = 0;
   204             for (i = 0; i < num_gamepads; ++i) {
   205                 __x_ABI_CWindows_CGaming_CInput_CIGamepad *gamepad;
   206 
   207                 hr = __FIVectorView_1_Windows__CGaming__CInput__CGamepad_GetAt(gamepads, i, &gamepad);
   208                 if (SUCCEEDED(hr)) {
   209                     Uint8 gamepad_buttons = HIDAPI_DriverXbox360_GetGamepadButtonsForMatch(gamepad);
   210                     if (buttons == gamepad_buttons) {
   211                         ++match_count;
   212                         match_slot = i;
   213                     }
   214                     __x_ABI_CWindows_CGaming_CInput_CIGamepad_Release(gamepad);
   215                 }
   216             }
   217             if (match_count == 1) {
   218                 hr = __FIVectorView_1_Windows__CGaming__CInput__CGamepad_GetAt(gamepads, match_slot, &ctx->gamepad);
   219                 if (SUCCEEDED(hr)) {
   220                 }
   221             }
   222         }
   223         __FIVectorView_1_Windows__CGaming__CInput__CGamepad_Release(gamepads);
   224     }
   225 }
   226 
   227 static void
   228 HIDAPI_DriverXbox360_QuitWindowsGamingInput(SDL_DriverXbox360_Context *ctx)
   229 {
   230     if (ctx->gamepad_statics) {
   231         __x_ABI_CWindows_CGaming_CInput_CIGamepadStatics_Release(ctx->gamepad_statics);
   232         ctx->gamepad_statics = NULL;
   233     }
   234     if (ctx->gamepad) {
   235         __x_ABI_CWindows_CGaming_CInput_CIGamepad_Release(ctx->gamepad);
   236         ctx->gamepad = NULL;
   237     }
   238 
   239     if (ctx->coinitialized) {
   240         WIN_CoUninitialize();
   241         ctx->coinitialized = SDL_FALSE;
   242     }
   243 }
   244 
   245 #endif /* SDL_JOYSTICK_HIDAPI_WINDOWS_GAMING_INPUT */
   246 
   247 #if defined(__MACOSX__)
   248 static SDL_bool
   249 IsBluetoothXboxOneController(Uint16 vendor_id, Uint16 product_id)
   250 {
   251     /* Check to see if it's the Xbox One S or Xbox One Elite Series 2 in Bluetooth mode */
   252     if (vendor_id == USB_VENDOR_MICROSOFT) {
   253         if (product_id == USB_PRODUCT_XBOX_ONE_S_REV1_BLUETOOTH ||
   254             product_id == USB_PRODUCT_XBOX_ONE_S_REV2_BLUETOOTH ||
   255             product_id == USB_PRODUCT_XBOX_ONE_ELITE_SERIES_2_BLUETOOTH) {
   256             return SDL_TRUE;
   257         }
   258     }
   259     return SDL_FALSE;
   260 }
   261 #endif
   262 
   263 static SDL_bool
   264 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)
   265 {
   266     const int XB360W_IFACE_PROTOCOL = 129; /* Wireless */
   267 
   268     if (vendor_id == USB_VENDOR_NVIDIA) {
   269         /* This is the NVIDIA Shield controller which doesn't talk Xbox controller protocol */
   270         return SDL_FALSE;
   271     }
   272     if ((vendor_id == USB_VENDOR_MICROSOFT && (product_id == 0x0291 || product_id == 0x0719)) ||
   273         (type == SDL_CONTROLLER_TYPE_XBOX360 && interface_protocol == XB360W_IFACE_PROTOCOL)) {
   274         /* This is the wireless dongle, which talks a different protocol */
   275         return SDL_FALSE;
   276     }
   277     if (interface_number > 0) {
   278         /* This is the chatpad or other input interface, not the Xbox 360 interface */
   279         return SDL_FALSE;
   280     }
   281 #if defined(__MACOSX__) || defined(__WIN32__)
   282     if (vendor_id == USB_VENDOR_MICROSOFT && product_id == 0x028e && version == 1) {
   283         /* This is the Steam Virtual Gamepad, which isn't supported by this driver */
   284         return SDL_FALSE;
   285     }
   286     if (vendor_id == USB_VENDOR_MICROSOFT && product_id == 0x02e0) {
   287         /* This is the old Bluetooth Xbox One S firmware, which isn't supported by this driver */
   288         return SDL_FALSE;
   289     }
   290 #if defined(__MACOSX__)
   291     /* Wired Xbox One controllers are handled by this driver, interfacing with
   292        the 360Controller driver available from:
   293        https://github.com/360Controller/360Controller/releases
   294 
   295        Bluetooth Xbox One controllers are handled by the SDL Xbox One driver
   296     */
   297     if (IsBluetoothXboxOneController(vendor_id, product_id)) {
   298         return SDL_FALSE;
   299     }
   300 #endif
   301     return (type == SDL_CONTROLLER_TYPE_XBOX360 || type == SDL_CONTROLLER_TYPE_XBOXONE);
   302 #else
   303     return (type == SDL_CONTROLLER_TYPE_XBOX360);
   304 #endif
   305 }
   306 
   307 static const char *
   308 HIDAPI_DriverXbox360_GetDeviceName(Uint16 vendor_id, Uint16 product_id)
   309 {
   310     return NULL;
   311 }
   312 
   313 static SDL_bool SetSlotLED(hid_device *dev, Uint8 slot)
   314 {
   315     Uint8 mode = 0x02 + slot;
   316     const Uint8 led_packet[] = { 0x01, 0x03, mode };
   317 
   318     if (hid_write(dev, led_packet, sizeof(led_packet)) != sizeof(led_packet)) {
   319         return SDL_FALSE;
   320     }
   321     return SDL_TRUE;
   322 }
   323 
   324 static SDL_bool
   325 HIDAPI_DriverXbox360_InitDevice(SDL_HIDAPI_Device *device)
   326 {
   327     return HIDAPI_JoystickConnected(device, NULL);
   328 }
   329 
   330 static int
   331 HIDAPI_DriverXbox360_GetDevicePlayerIndex(SDL_HIDAPI_Device *device, SDL_JoystickID instance_id)
   332 {
   333     return -1;
   334 }
   335 
   336 static void
   337 HIDAPI_DriverXbox360_SetDevicePlayerIndex(SDL_HIDAPI_Device *device, SDL_JoystickID instance_id, int player_index)
   338 {
   339     if (device->dev) {
   340         SetSlotLED(device->dev, (player_index % 4));
   341     }
   342 }
   343 
   344 static SDL_bool
   345 HIDAPI_DriverXbox360_OpenJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick)
   346 {
   347     SDL_DriverXbox360_Context *ctx;
   348     int player_index;
   349 
   350     ctx = (SDL_DriverXbox360_Context *)SDL_calloc(1, sizeof(*ctx));
   351     if (!ctx) {
   352         SDL_OutOfMemory();
   353         return SDL_FALSE;
   354     }
   355 
   356     device->dev = hid_open_path(device->path, 0);
   357     if (!device->dev) {
   358         SDL_free(ctx);
   359         SDL_SetError("Couldn't open %s", device->path);
   360         return SDL_FALSE;
   361     }
   362     device->context = ctx;
   363 
   364 #ifdef SDL_JOYSTICK_HIDAPI_WINDOWS_XINPUT
   365     ctx->xinput_enabled = SDL_GetHintBoolean(SDL_HINT_XINPUT_ENABLED, SDL_TRUE);
   366     if (ctx->xinput_enabled && WIN_LoadXInputDLL() < 0) {
   367         ctx->xinput_enabled = SDL_FALSE;
   368     }
   369     ctx->xinput_slot = XUSER_INDEX_ANY;
   370 #endif
   371 #ifdef SDL_JOYSTICK_HIDAPI_WINDOWS_GAMING_INPUT
   372     HIDAPI_DriverXbox360_InitWindowsGamingInput(ctx);
   373 #endif
   374 
   375     /* Set the controller LED */
   376     player_index = SDL_JoystickGetPlayerIndex(joystick);
   377     if (player_index >= 0) {
   378         SetSlotLED(device->dev, (player_index % 4));
   379     }
   380 
   381     /* Initialize the joystick capabilities */
   382     joystick->nbuttons = SDL_CONTROLLER_BUTTON_MAX;
   383     joystick->naxes = SDL_CONTROLLER_AXIS_MAX;
   384     joystick->epowerlevel = SDL_JOYSTICK_POWER_WIRED;
   385 
   386     return SDL_TRUE;
   387 }
   388 
   389 static int
   390 HIDAPI_DriverXbox360_RumbleJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble)
   391 {
   392 #if defined(SDL_JOYSTICK_HIDAPI_WINDOWS_GAMING_INPUT) || defined(SDL_JOYSTICK_HIDAPI_WINDOWS_XINPUT)
   393     SDL_DriverXbox360_Context *ctx = (SDL_DriverXbox360_Context *)device->context;
   394 #endif
   395 
   396 #ifdef __WIN32__
   397     SDL_bool rumbled = SDL_FALSE;
   398 
   399 #ifdef SDL_JOYSTICK_HIDAPI_WINDOWS_GAMING_INPUT
   400     if (!rumbled && ctx->gamepad) {
   401         HRESULT hr;
   402 
   403         ctx->vibration.LeftMotor = (DOUBLE)low_frequency_rumble / SDL_MAX_UINT16;
   404         ctx->vibration.RightMotor = (DOUBLE)high_frequency_rumble / SDL_MAX_UINT16;
   405         hr = __x_ABI_CWindows_CGaming_CInput_CIGamepad_put_Vibration(ctx->gamepad, ctx->vibration);
   406         if (SUCCEEDED(hr)) {
   407             rumbled = SDL_TRUE;
   408         }
   409     }
   410 #endif
   411 
   412 #ifdef SDL_JOYSTICK_HIDAPI_WINDOWS_XINPUT
   413     if (!rumbled && ctx->xinput_slot != XUSER_INDEX_ANY) {
   414         XINPUT_VIBRATION XVibration;
   415 
   416         if (!XINPUTSETSTATE) {
   417             return SDL_Unsupported();
   418         }
   419 
   420         XVibration.wLeftMotorSpeed = low_frequency_rumble;
   421         XVibration.wRightMotorSpeed = high_frequency_rumble;
   422         if (XINPUTSETSTATE(ctx->xinput_slot, &XVibration) == ERROR_SUCCESS) {
   423             rumbled = SDL_TRUE;
   424         } else {
   425             return SDL_SetError("XInputSetState() failed");
   426         }
   427     }
   428 #endif /* SDL_JOYSTICK_HIDAPI_WINDOWS_XINPUT */
   429 
   430 #else /* !__WIN32__ */
   431 
   432 #ifdef __MACOSX__
   433     if (IsBluetoothXboxOneController(device->vendor_id, device->product_id)) {
   434         Uint8 rumble_packet[] = { 0x03, 0x0F, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00 };
   435 
   436         rumble_packet[4] = (low_frequency_rumble >> 8);
   437         rumble_packet[5] = (high_frequency_rumble >> 8);
   438 
   439         if (SDL_HIDAPI_SendRumble(device, rumble_packet, sizeof(rumble_packet)) != sizeof(rumble_packet)) {
   440             return SDL_SetError("Couldn't send rumble packet");
   441         }
   442     } else {
   443         /* On Mac OS X the 360Controller driver uses this short report,
   444            and we need to prefix it with a magic token so hidapi passes it through untouched
   445          */
   446         Uint8 rumble_packet[] = { 'M', 'A', 'G', 'I', 'C', '0', 0x00, 0x04, 0x00, 0x00 };
   447 
   448         rumble_packet[6+2] = (low_frequency_rumble >> 8);
   449         rumble_packet[6+3] = (high_frequency_rumble >> 8);
   450 
   451         if (SDL_HIDAPI_SendRumble(device, rumble_packet, sizeof(rumble_packet)) != sizeof(rumble_packet)) {
   452             return SDL_SetError("Couldn't send rumble packet");
   453         }
   454     }
   455 #else
   456     Uint8 rumble_packet[] = { 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
   457 
   458     rumble_packet[3] = (low_frequency_rumble >> 8);
   459     rumble_packet[4] = (high_frequency_rumble >> 8);
   460 
   461     if (SDL_HIDAPI_SendRumble(device, rumble_packet, sizeof(rumble_packet)) != sizeof(rumble_packet)) {
   462         return SDL_SetError("Couldn't send rumble packet");
   463     }
   464 #endif
   465 #endif /* __WIN32__ */
   466 
   467     return 0;
   468 }
   469 
   470 #ifdef __WIN32__
   471  /* This is the packet format for Xbox 360 and Xbox One controllers on Windows,
   472     however with this interface there is no rumble support, no guide button,
   473     and the left and right triggers are tied together as a single axis.
   474 
   475     We use XInput and Windows.Gaming.Input to make up for these shortcomings.
   476   */
   477 static void
   478 HIDAPI_DriverXbox360_HandleStatePacket(SDL_Joystick *joystick, hid_device *dev, SDL_DriverXbox360_Context *ctx, Uint8 *data, int size)
   479 {
   480     Sint16 axis;
   481     SDL_bool has_trigger_data = SDL_FALSE;
   482 
   483     if (ctx->last_state[10] != data[10]) {
   484         SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_A, (data[10] & 0x01) ? SDL_PRESSED : SDL_RELEASED);
   485         SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_B, (data[10] & 0x02) ? SDL_PRESSED : SDL_RELEASED);
   486         SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_X, (data[10] & 0x04) ? SDL_PRESSED : SDL_RELEASED);
   487         SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_Y, (data[10] & 0x08) ? SDL_PRESSED : SDL_RELEASED);
   488         SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_LEFTSHOULDER, (data[10] & 0x10) ? SDL_PRESSED : SDL_RELEASED);
   489         SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER, (data[10] & 0x20) ? SDL_PRESSED : SDL_RELEASED);
   490         SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_BACK, (data[10] & 0x40) ? SDL_PRESSED : SDL_RELEASED);
   491         SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_START, (data[10] & 0x80) ? SDL_PRESSED : SDL_RELEASED);
   492     }
   493 
   494     if (ctx->last_state[11] != data[11]) {
   495         SDL_bool dpad_up = SDL_FALSE;
   496         SDL_bool dpad_down = SDL_FALSE;
   497         SDL_bool dpad_left = SDL_FALSE;
   498         SDL_bool dpad_right = SDL_FALSE;
   499 
   500         SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_LEFTSTICK, (data[11] & 0x01) ? SDL_PRESSED : SDL_RELEASED);
   501         SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_RIGHTSTICK, (data[11] & 0x02) ? SDL_PRESSED : SDL_RELEASED);
   502 
   503         switch (data[11] & 0x3C) {
   504         case 4:
   505             dpad_up = SDL_TRUE;
   506             break;
   507         case 8:
   508             dpad_up = SDL_TRUE;
   509             dpad_right = SDL_TRUE;
   510             break;
   511         case 12:
   512             dpad_right = SDL_TRUE;
   513             break;
   514         case 16:
   515             dpad_right = SDL_TRUE;
   516             dpad_down = SDL_TRUE;
   517             break;
   518         case 20:
   519             dpad_down = SDL_TRUE;
   520             break;
   521         case 24:
   522             dpad_left = SDL_TRUE;
   523             dpad_down = SDL_TRUE;
   524             break;
   525         case 28:
   526             dpad_left = SDL_TRUE;
   527             break;
   528         case 32:
   529             dpad_up = SDL_TRUE;
   530             dpad_left = SDL_TRUE;
   531             break;
   532         default:
   533             break;
   534         }
   535         SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_DOWN, dpad_down);
   536         SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_UP, dpad_up);
   537         SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_RIGHT, dpad_right);
   538         SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_LEFT, dpad_left);
   539     }
   540 
   541     axis = (int)*(Uint16*)(&data[0]) - 0x8000;
   542     SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_LEFTX, axis);
   543     axis = (int)*(Uint16*)(&data[2]) - 0x8000;
   544     SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_LEFTY, axis);
   545     axis = (int)*(Uint16*)(&data[4]) - 0x8000;
   546     SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_RIGHTX, axis);
   547     axis = (int)*(Uint16*)(&data[6]) - 0x8000;
   548     SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_RIGHTY, axis);
   549 
   550 #ifdef SDL_JOYSTICK_HIDAPI_WINDOWS_GAMING_INPUT
   551     if (ctx->gamepad_statics && !ctx->gamepad) {
   552         Uint8 buttons = 0;
   553 
   554         if (data[10] & 0x01) {
   555             buttons |= (1 << SDL_CONTROLLER_BUTTON_A);
   556         }
   557         if (data[10] & 0x02) {
   558             buttons |= (1 << SDL_CONTROLLER_BUTTON_B);
   559         }
   560         if (data[10] & 0x04) {
   561             buttons |= (1 << SDL_CONTROLLER_BUTTON_X);
   562         }
   563         if (data[10] & 0x08) {
   564             buttons |= (1 << SDL_CONTROLLER_BUTTON_Y);
   565         }
   566         if (buttons != 0) {
   567             HIDAPI_DriverXbox360_GuessGamepad(ctx, buttons);
   568         }
   569     }
   570 
   571     if (ctx->gamepad) {
   572         HRESULT hr;
   573         struct __x_ABI_CWindows_CGaming_CInput_CGamepadReading state;
   574         
   575         hr = __x_ABI_CWindows_CGaming_CInput_CIGamepad_GetCurrentReading(ctx->gamepad, &state);
   576         if (SUCCEEDED(hr)) {
   577             SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_GUIDE, (state.Buttons & 0x40000000) ? SDL_PRESSED : SDL_RELEASED);
   578             SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERLEFT, ((int)(state.LeftTrigger * SDL_MAX_UINT16)) - 32768);
   579             SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERRIGHT, ((int)(state.RightTrigger * SDL_MAX_UINT16)) - 32768);
   580             has_trigger_data = SDL_TRUE;
   581         }
   582     }
   583 #endif /* SDL_JOYSTICK_HIDAPI_WINDOWS_GAMING_INPUT */
   584 
   585 #ifdef SDL_JOYSTICK_HIDAPI_WINDOWS_XINPUT
   586     if (ctx->xinput_enabled) {
   587         if (ctx->xinput_slot == XUSER_INDEX_ANY && HIDAPI_DriverXbox360_MissingXInputSlot()) {
   588             WORD wButtons = 0;
   589 
   590             if (data[10] & 0x01) {
   591                 wButtons |= XINPUT_GAMEPAD_A;
   592             }
   593             if (data[10] & 0x02) {
   594                 wButtons |= XINPUT_GAMEPAD_B;
   595             }
   596             if (data[10] & 0x04) {
   597                 wButtons |= XINPUT_GAMEPAD_X;
   598             }
   599             if (data[10] & 0x08) {
   600                 wButtons |= XINPUT_GAMEPAD_Y;
   601             }
   602             if (wButtons != 0) {
   603                 Uint8 xinput_slot = HIDAPI_DriverXbox360_GuessXInputSlot(wButtons);
   604                 if (xinput_slot != XUSER_INDEX_ANY) {
   605                     HIDAPI_DriverXbox360_MarkXInputSlotUsed(xinput_slot);
   606                     ctx->xinput_slot = xinput_slot;
   607                 }
   608             }
   609         }
   610 
   611         if (!has_trigger_data && ctx->xinput_slot != XUSER_INDEX_ANY) {
   612             XINPUT_STATE_EX xinput_state;
   613 
   614             if (XINPUTGETSTATE(ctx->xinput_slot, &xinput_state) == ERROR_SUCCESS) {
   615                 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_GUIDE, (xinput_state.Gamepad.wButtons & XINPUT_GAMEPAD_GUIDE) ? SDL_PRESSED : SDL_RELEASED);
   616                 SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERLEFT, ((int)xinput_state.Gamepad.bLeftTrigger * 257) - 32768);
   617                 SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERRIGHT, ((int)xinput_state.Gamepad.bRightTrigger * 257) - 32768);
   618                 has_trigger_data = SDL_TRUE;
   619             }
   620         }
   621     }
   622 #endif /* SDL_JOYSTICK_HIDAPI_WINDOWS_XINPUT */
   623 
   624     if (!has_trigger_data) {
   625         axis = (data[9] * 257) - 32768;
   626         if (data[9] < 0x80) {
   627             axis = -axis * 2 - 32769;
   628             SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERRIGHT, axis);
   629         } else if (data[9] > 0x80) {
   630             axis = axis * 2 - 32767;
   631             SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERLEFT, axis);
   632         } else {
   633             SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERLEFT, SDL_MIN_SINT16);
   634             SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERRIGHT, SDL_MIN_SINT16);
   635         }
   636     }
   637 
   638     SDL_memcpy(ctx->last_state, data, SDL_min(size, sizeof(ctx->last_state)));
   639 }
   640 #else
   641 
   642 static void
   643 HIDAPI_DriverXbox360_HandleStatePacket(SDL_Joystick *joystick, hid_device *dev, SDL_DriverXbox360_Context *ctx, Uint8 *data, int size)
   644 {
   645     Sint16 axis;
   646 #ifdef __MACOSX__
   647     const SDL_bool invert_y_axes = SDL_FALSE;
   648 #else
   649     const SDL_bool invert_y_axes = SDL_TRUE;
   650 #endif
   651 
   652     if (ctx->last_state[2] != data[2]) {
   653         SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_UP, (data[2] & 0x01) ? SDL_PRESSED : SDL_RELEASED);
   654         SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_DOWN, (data[2] & 0x02) ? SDL_PRESSED : SDL_RELEASED);
   655         SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_LEFT, (data[2] & 0x04) ? SDL_PRESSED : SDL_RELEASED);
   656         SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_RIGHT, (data[2] & 0x08) ? SDL_PRESSED : SDL_RELEASED);
   657         SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_START, (data[2] & 0x10) ? SDL_PRESSED : SDL_RELEASED);
   658         SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_BACK, (data[2] & 0x20) ? SDL_PRESSED : SDL_RELEASED);
   659         SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_LEFTSTICK, (data[2] & 0x40) ? SDL_PRESSED : SDL_RELEASED);
   660         SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_RIGHTSTICK, (data[2] & 0x80) ? SDL_PRESSED : SDL_RELEASED);
   661     }
   662 
   663     if (ctx->last_state[3] != data[3]) {
   664         SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_LEFTSHOULDER, (data[3] & 0x01) ? SDL_PRESSED : SDL_RELEASED);
   665         SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER, (data[3] & 0x02) ? SDL_PRESSED : SDL_RELEASED);
   666         SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_GUIDE, (data[3] & 0x04) ? SDL_PRESSED : SDL_RELEASED);
   667         SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_A, (data[3] & 0x10) ? SDL_PRESSED : SDL_RELEASED);
   668         SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_B, (data[3] & 0x20) ? SDL_PRESSED : SDL_RELEASED);
   669         SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_X, (data[3] & 0x40) ? SDL_PRESSED : SDL_RELEASED);
   670         SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_Y, (data[3] & 0x80) ? SDL_PRESSED : SDL_RELEASED);
   671     }
   672 
   673     axis = ((int)data[4] * 257) - 32768;
   674     SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERLEFT, axis);
   675     axis = ((int)data[5] * 257) - 32768;
   676     SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERRIGHT, axis);
   677     axis = *(Sint16*)(&data[6]);
   678     SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_LEFTX, axis);
   679     axis = *(Sint16*)(&data[8]);
   680     if (invert_y_axes) {
   681         axis = ~axis;
   682     }
   683     SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_LEFTY, axis);
   684     axis = *(Sint16*)(&data[10]);
   685     SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_RIGHTX, axis);
   686     axis = *(Sint16*)(&data[12]);
   687     if (invert_y_axes) {
   688         axis = ~axis;
   689     }
   690     SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_RIGHTY, axis);
   691 
   692     SDL_memcpy(ctx->last_state, data, SDL_min(size, sizeof(ctx->last_state)));
   693 }
   694 #endif /* __WIN32__ */
   695 
   696 static SDL_bool
   697 HIDAPI_DriverXbox360_UpdateDevice(SDL_HIDAPI_Device *device)
   698 {
   699     SDL_DriverXbox360_Context *ctx = (SDL_DriverXbox360_Context *)device->context;
   700     SDL_Joystick *joystick = NULL;
   701     Uint8 data[USB_PACKET_LENGTH];
   702     int size;
   703 
   704     if (device->num_joysticks > 0) {
   705         joystick = SDL_JoystickFromInstanceID(device->joysticks[0]);
   706     }
   707     if (!joystick) {
   708         return SDL_FALSE;
   709     }
   710 
   711     while ((size = hid_read_timeout(device->dev, data, sizeof(data), 0)) > 0) {
   712         HIDAPI_DriverXbox360_HandleStatePacket(joystick, device->dev, ctx, data, size);
   713     }
   714 
   715     if (size < 0) {
   716         /* Read error, device is disconnected */
   717         HIDAPI_JoystickDisconnected(device, joystick->instance_id);
   718     }
   719     return (size >= 0);
   720 }
   721 
   722 static void
   723 HIDAPI_DriverXbox360_CloseJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick)
   724 {
   725 #if defined(SDL_JOYSTICK_HIDAPI_WINDOWS_XINPUT) || defined(SDL_JOYSTICK_HIDAPI_WINDOWS_GAMING_INPUT)
   726     SDL_DriverXbox360_Context *ctx = (SDL_DriverXbox360_Context *)device->context;
   727 #endif
   728 
   729 #ifdef SDL_JOYSTICK_HIDAPI_WINDOWS_XINPUT
   730     if (ctx->xinput_enabled) {
   731         HIDAPI_DriverXbox360_MarkXInputSlotFree(ctx->xinput_slot);
   732         WIN_UnloadXInputDLL();
   733     }
   734 #endif
   735 #ifdef SDL_JOYSTICK_HIDAPI_WINDOWS_GAMING_INPUT
   736     HIDAPI_DriverXbox360_QuitWindowsGamingInput(ctx);
   737 #endif
   738 
   739     hid_close(device->dev);
   740     device->dev = NULL;
   741 
   742     SDL_free(device->context);
   743     device->context = NULL;
   744 }
   745 
   746 static void
   747 HIDAPI_DriverXbox360_FreeDevice(SDL_HIDAPI_Device *device)
   748 {
   749 }
   750 
   751 SDL_HIDAPI_DeviceDriver SDL_HIDAPI_DriverXbox360 =
   752 {
   753     SDL_HINT_JOYSTICK_HIDAPI_XBOX,
   754     SDL_TRUE,
   755     HIDAPI_DriverXbox360_IsSupportedDevice,
   756     HIDAPI_DriverXbox360_GetDeviceName,
   757     HIDAPI_DriverXbox360_InitDevice,
   758     HIDAPI_DriverXbox360_GetDevicePlayerIndex,
   759     HIDAPI_DriverXbox360_SetDevicePlayerIndex,
   760     HIDAPI_DriverXbox360_UpdateDevice,
   761     HIDAPI_DriverXbox360_OpenJoystick,
   762     HIDAPI_DriverXbox360_RumbleJoystick,
   763     HIDAPI_DriverXbox360_CloseJoystick,
   764     HIDAPI_DriverXbox360_FreeDevice
   765 };
   766 
   767 #endif /* SDL_JOYSTICK_HIDAPI_XBOX360 */
   768 
   769 #endif /* SDL_JOYSTICK_HIDAPI */
   770 
   771 /* vi: set ts=4 sw=4 expandtab: */