src/joystick/hidapi/SDL_hidapi_xbox360.c
author Sam Lantinga <slouken@libsdl.org>
Wed, 15 Aug 2018 23:35:54 -0700
changeset 12119 648377d0e573
parent 12118 2b80f9635ee5
child 12161 12a877a0ccb5
permissions -rw-r--r--
Use a single hint for both Xbox 360 and Xbox One controllers, since they are often the same driver.
     1 /*
     2   Simple DirectMedia Layer
     3   Copyright (C) 1997-2018 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 
    34 
    35 #ifdef SDL_JOYSTICK_HIDAPI_XBOX360
    36 
    37 #ifdef __WIN32__
    38 #include "../../core/windows/SDL_xinput.h"
    39 #endif
    40 
    41 #define USB_PACKET_LENGTH   64
    42 
    43 
    44 typedef struct {
    45     Uint8 last_state[USB_PACKET_LENGTH];
    46     Uint32 rumble_expiration;
    47 #ifdef __WIN32__
    48     SDL_bool xinput_enabled;
    49     Uint8 xinput_slot;
    50 #endif
    51 } SDL_DriverXbox360_Context;
    52 
    53 
    54 #ifdef __WIN32__
    55 static Uint8 xinput_slots;
    56 
    57 static void
    58 HIDAPI_DriverXbox360_MarkXInputSlotUsed(Uint8 xinput_slot)
    59 {
    60     if (xinput_slot != XUSER_INDEX_ANY) {
    61         xinput_slots |= (0x01 << xinput_slot);
    62     }
    63 }
    64 
    65 static void
    66 HIDAPI_DriverXbox360_MarkXInputSlotFree(Uint8 xinput_slot)
    67 {
    68     if (xinput_slot != XUSER_INDEX_ANY) {
    69         xinput_slots &= ~(0x01 << xinput_slot);
    70     }
    71 }
    72 
    73 static SDL_bool
    74 HIDAPI_DriverXbox360_MissingXInputSlot()
    75 {
    76     return xinput_slots != 0x0F;
    77 }
    78 
    79 static Uint8
    80 HIDAPI_DriverXbox360_GuessXInputSlot(WORD wButtons)
    81 {
    82     DWORD user_index;
    83     int match_count;
    84     Uint8 match_slot;
    85 
    86     if (!XINPUTGETSTATE) {
    87         return XUSER_INDEX_ANY;
    88     }
    89 
    90     match_count = 0;
    91     for (user_index = 0; user_index < XUSER_MAX_COUNT; ++user_index) {
    92         XINPUT_STATE_EX xinput_state;
    93 
    94         if (XINPUTGETSTATE(user_index, &xinput_state) == ERROR_SUCCESS) {
    95             if (xinput_state.Gamepad.wButtons == wButtons) {
    96                 ++match_count;
    97                 match_slot = (Uint8)user_index;
    98             }
    99         }
   100     }
   101     if (match_count == 1) {
   102         return match_slot;
   103     }
   104     return XUSER_INDEX_ANY;
   105 }
   106 
   107 #endif /* __WIN32__ */
   108 
   109 static SDL_bool
   110 HIDAPI_DriverXbox360_IsSupportedDevice(Uint16 vendor_id, Uint16 product_id, Uint16 version, int interface_number, Uint16 usage_page, Uint16 usage)
   111 {
   112 #if defined(__MACOSX__) || defined(__WIN32__)
   113     if (vendor_id == 0x045e && product_id == 0x028e && version == 1) {
   114         /* This is the Steam Virtual Gamepad, which isn't supported by this driver */
   115         return SDL_FALSE;
   116     }
   117     return SDL_IsJoystickXbox360(vendor_id, product_id) || SDL_IsJoystickXboxOne(vendor_id, product_id);
   118 #else
   119     return SDL_IsJoystickXbox360(vendor_id, product_id);
   120 #endif
   121 }
   122 
   123 static const char *
   124 HIDAPI_DriverXbox360_GetDeviceName(Uint16 vendor_id, Uint16 product_id)
   125 {
   126     return HIDAPI_XboxControllerName(vendor_id, product_id);
   127 }
   128 
   129 static SDL_bool SetSlotLED(hid_device *dev, Uint8 slot)
   130 {
   131     const Uint8 led_packet[] = { 0x01, 0x03, (2 + slot) };
   132 
   133     if (hid_write(dev, led_packet, sizeof(led_packet)) != sizeof(led_packet)) {
   134         return SDL_FALSE;
   135     }
   136     return SDL_TRUE;
   137 }
   138 
   139 static SDL_bool
   140 HIDAPI_DriverXbox360_Init(SDL_Joystick *joystick, hid_device *dev, Uint16 vendor_id, Uint16 product_id, void **context)
   141 {
   142     SDL_DriverXbox360_Context *ctx;
   143 
   144     ctx = (SDL_DriverXbox360_Context *)SDL_calloc(1, sizeof(*ctx));
   145     if (!ctx) {
   146         SDL_OutOfMemory();
   147         return SDL_FALSE;
   148     }
   149 #ifdef __WIN32__
   150     ctx->xinput_enabled = SDL_GetHintBoolean(SDL_HINT_XINPUT_ENABLED, SDL_TRUE);
   151     if (ctx->xinput_enabled && WIN_LoadXInputDLL() < 0) {
   152         ctx->xinput_enabled = SDL_FALSE;
   153     }
   154     ctx->xinput_slot = XUSER_INDEX_ANY;
   155 #endif
   156     *context = ctx;
   157 
   158     /* Set the controller LED */
   159     SetSlotLED(dev, (joystick->instance_id % 4));
   160 
   161     /* Initialize the joystick capabilities */
   162     joystick->nbuttons = SDL_CONTROLLER_BUTTON_MAX;
   163     joystick->naxes = SDL_CONTROLLER_AXIS_MAX;
   164     joystick->epowerlevel = SDL_JOYSTICK_POWER_WIRED;
   165 
   166     return SDL_TRUE;
   167 }
   168 
   169 static int
   170 HIDAPI_DriverXbox360_Rumble(SDL_Joystick *joystick, hid_device *dev, void *context, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble, Uint32 duration_ms)
   171 {
   172     SDL_DriverXbox360_Context *ctx = (SDL_DriverXbox360_Context *)context;
   173 #ifdef __WIN32__
   174     if (ctx->xinput_slot != XUSER_INDEX_ANY) {
   175         XINPUT_VIBRATION XVibration;
   176 
   177         if (!XINPUTSETSTATE) {
   178             return SDL_Unsupported();
   179         }
   180 
   181         XVibration.wLeftMotorSpeed = low_frequency_rumble;
   182         XVibration.wRightMotorSpeed = high_frequency_rumble;
   183         if (XINPUTSETSTATE(ctx->xinput_slot, &XVibration) != ERROR_SUCCESS) {
   184             return SDL_SetError("XInputSetState() failed");
   185         }
   186     }
   187 #else
   188 #ifdef __MACOSX__
   189     /* On Mac OS X the 360Controller driver uses this short report,
   190        and we need to prefix it with a magic token so hidapi passes it through untouched
   191      */
   192     Uint8 rumble_packet[] = { 'M', 'A', 'G', 'I', 'C', '0', 0x00, 0x04, 0x00, 0x00 };
   193 
   194     rumble_packet[6+2] = (low_frequency_rumble >> 8);
   195     rumble_packet[6+3] = (high_frequency_rumble >> 8);
   196 #else
   197     Uint8 rumble_packet[] = { 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
   198 
   199     rumble_packet[3] = (low_frequency_rumble >> 8);
   200     rumble_packet[4] = (high_frequency_rumble >> 8);
   201 #endif
   202 
   203     if (hid_write(dev, rumble_packet, sizeof(rumble_packet)) != sizeof(rumble_packet)) {
   204         return SDL_SetError("Couldn't send rumble packet");
   205     }
   206 #endif /* __WIN32__ */
   207 
   208     if ((low_frequency_rumble || high_frequency_rumble) && duration_ms) {
   209         ctx->rumble_expiration = SDL_GetTicks() + duration_ms;
   210     } else {
   211         ctx->rumble_expiration = 0;
   212     }
   213     return 0;
   214 }
   215 
   216 #ifdef __WIN32__
   217  /* This is the packet format for Xbox 360 and Xbox One controllers on Windows,
   218     however with this interface there is no rumble support, no guide button,
   219     and the left and right triggers are tied together as a single axis.
   220   */
   221 static void
   222 HIDAPI_DriverXbox360_HandleStatePacket(SDL_Joystick *joystick, hid_device *dev, SDL_DriverXbox360_Context *ctx, Uint8 *data, int size)
   223 {
   224     Sint16 axis;
   225 
   226     if (ctx->last_state[10] != data[10]) {
   227         SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_A, (data[10] & 0x01) ? SDL_PRESSED : SDL_RELEASED);
   228         SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_B, (data[10] & 0x02) ? SDL_PRESSED : SDL_RELEASED);
   229         SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_X, (data[10] & 0x04) ? SDL_PRESSED : SDL_RELEASED);
   230         SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_Y, (data[10] & 0x08) ? SDL_PRESSED : SDL_RELEASED);
   231         SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_LEFTSHOULDER, (data[10] & 0x10) ? SDL_PRESSED : SDL_RELEASED);
   232         SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER, (data[10] & 0x20) ? SDL_PRESSED : SDL_RELEASED);
   233         SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_BACK, (data[10] & 0x40) ? SDL_PRESSED : SDL_RELEASED);
   234         SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_START, (data[10] & 0x80) ? SDL_PRESSED : SDL_RELEASED);
   235     }
   236 
   237     if (ctx->last_state[11] != data[11]) {
   238         SDL_bool dpad_up = SDL_FALSE;
   239         SDL_bool dpad_down = SDL_FALSE;
   240         SDL_bool dpad_left = SDL_FALSE;
   241         SDL_bool dpad_right = SDL_FALSE;
   242 
   243         SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_LEFTSTICK, (data[11] & 0x01) ? SDL_PRESSED : SDL_RELEASED);
   244         SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_RIGHTSTICK, (data[11] & 0x02) ? SDL_PRESSED : SDL_RELEASED);
   245 
   246         switch (data[11] & 0x3C) {
   247         case 4:
   248             dpad_up = SDL_TRUE;
   249             break;
   250         case 8:
   251             dpad_up = SDL_TRUE;
   252             dpad_right = SDL_TRUE;
   253             break;
   254         case 12:
   255             dpad_right = SDL_TRUE;
   256             break;
   257         case 16:
   258             dpad_right = SDL_TRUE;
   259             dpad_down = SDL_TRUE;
   260             break;
   261         case 20:
   262             dpad_down = SDL_TRUE;
   263             break;
   264         case 24:
   265             dpad_left = SDL_TRUE;
   266             dpad_down = SDL_TRUE;
   267             break;
   268         case 28:
   269             dpad_left = SDL_TRUE;
   270             break;
   271         case 32:
   272             dpad_up = SDL_TRUE;
   273             dpad_left = SDL_TRUE;
   274             break;
   275         default:
   276             break;
   277         }
   278         SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_DOWN, dpad_down);
   279         SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_UP, dpad_up);
   280         SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_RIGHT, dpad_right);
   281         SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_LEFT, dpad_left);
   282     }
   283 
   284     axis = (int)*(Uint16*)(&data[0]) - 0x8000;
   285     SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_LEFTX, axis);
   286     axis = (int)*(Uint16*)(&data[2]) - 0x8000;
   287     SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_LEFTY, axis);
   288     axis = (int)*(Uint16*)(&data[4]) - 0x8000;
   289     SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_RIGHTX, axis);
   290     axis = (int)*(Uint16*)(&data[6]) - 0x8000;
   291     SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_RIGHTY, axis);
   292 
   293     if (ctx->xinput_slot == XUSER_INDEX_ANY && HIDAPI_DriverXbox360_MissingXInputSlot()) {
   294         WORD wButtons = 0;
   295 
   296         if (data[10] & 0x01) {
   297             wButtons |= XINPUT_GAMEPAD_A;
   298         }
   299         if (data[10] & 0x02) {
   300             wButtons |= XINPUT_GAMEPAD_B;
   301         }
   302         if (data[10] & 0x04) {
   303             wButtons |= XINPUT_GAMEPAD_X;
   304         }
   305         if (data[10] & 0x08) {
   306             wButtons |= XINPUT_GAMEPAD_Y;
   307         }
   308         if (wButtons != 0) {
   309             Uint8 xinput_slot = HIDAPI_DriverXbox360_GuessXInputSlot(wButtons);
   310             if (xinput_slot != XUSER_INDEX_ANY) {
   311                 HIDAPI_DriverXbox360_MarkXInputSlotUsed(xinput_slot);
   312                 ctx->xinput_slot = xinput_slot;
   313             }
   314         }
   315     }
   316 
   317     if (ctx->xinput_slot == XUSER_INDEX_ANY) {
   318         axis = (data[9] * 257) - 32768;
   319         if (data[9] < 0x80) {
   320             axis = -axis * 2 - 32769;
   321             SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERRIGHT, axis);
   322         } else if (data[9] > 0x80) {
   323             axis = axis * 2 - 32767;
   324             SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERLEFT, axis);
   325         } else {
   326             SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERLEFT, SDL_MIN_SINT16);
   327             SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERRIGHT, SDL_MIN_SINT16);
   328         }
   329     } else {
   330         XINPUT_STATE_EX xinput_state;
   331 
   332         if (XINPUTGETSTATE(ctx->xinput_slot, &xinput_state) == ERROR_SUCCESS) {
   333             SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_GUIDE, (xinput_state.Gamepad.wButtons & XINPUT_GAMEPAD_GUIDE) ? SDL_PRESSED : SDL_RELEASED);
   334             SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERLEFT, ((int)xinput_state.Gamepad.bLeftTrigger * 257) - 32768);
   335             SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERRIGHT, ((int)xinput_state.Gamepad.bRightTrigger * 257) - 32768);
   336         }
   337     }
   338 
   339     SDL_memcpy(ctx->last_state, data, SDL_min(size, sizeof(ctx->last_state)));
   340 }
   341 #else
   342 
   343 static void
   344 HIDAPI_DriverXbox360_HandleStatePacket(SDL_Joystick *joystick, hid_device *dev, SDL_DriverXbox360_Context *ctx, Uint8 *data, int size)
   345 {
   346     Sint16 axis;
   347 #ifdef __MACOSX__
   348     const SDL_bool invert_y_axes = SDL_FALSE;
   349 #else
   350     const SDL_bool invert_y_axes = SDL_TRUE;
   351 #endif
   352 
   353     if (ctx->last_state[2] != data[2]) {
   354         SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_UP, (data[2] & 0x01) ? SDL_PRESSED : SDL_RELEASED);
   355         SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_DOWN, (data[2] & 0x02) ? SDL_PRESSED : SDL_RELEASED);
   356         SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_LEFT, (data[2] & 0x04) ? SDL_PRESSED : SDL_RELEASED);
   357         SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_RIGHT, (data[2] & 0x08) ? SDL_PRESSED : SDL_RELEASED);
   358         SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_START, (data[2] & 0x10) ? SDL_PRESSED : SDL_RELEASED);
   359         SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_BACK, (data[2] & 0x20) ? SDL_PRESSED : SDL_RELEASED);
   360         SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_LEFTSTICK, (data[2] & 0x40) ? SDL_PRESSED : SDL_RELEASED);
   361         SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_RIGHTSTICK, (data[2] & 0x80) ? SDL_PRESSED : SDL_RELEASED);
   362     }
   363 
   364     if (ctx->last_state[3] != data[3]) {
   365         SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_LEFTSHOULDER, (data[3] & 0x01) ? SDL_PRESSED : SDL_RELEASED);
   366         SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER, (data[3] & 0x02) ? SDL_PRESSED : SDL_RELEASED);
   367         SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_GUIDE, (data[3] & 0x04) ? SDL_PRESSED : SDL_RELEASED);
   368         SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_A, (data[3] & 0x10) ? SDL_PRESSED : SDL_RELEASED);
   369         SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_B, (data[3] & 0x20) ? SDL_PRESSED : SDL_RELEASED);
   370         SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_X, (data[3] & 0x40) ? SDL_PRESSED : SDL_RELEASED);
   371         SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_Y, (data[3] & 0x80) ? SDL_PRESSED : SDL_RELEASED);
   372     }
   373 
   374     axis = ((int)data[4] * 257) - 32768;
   375     SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERLEFT, axis);
   376     axis = ((int)data[5] * 257) - 32768;
   377     SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERRIGHT, axis);
   378     axis = *(Sint16*)(&data[6]);
   379     SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_LEFTX, axis);
   380     axis = *(Sint16*)(&data[8]);
   381     if (invert_y_axes) {
   382         axis = ~axis;
   383     }
   384     SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_LEFTY, axis);
   385     axis = *(Sint16*)(&data[10]);
   386     SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_RIGHTX, axis);
   387     axis = *(Sint16*)(&data[12]);
   388     if (invert_y_axes) {
   389         axis = ~axis;
   390     }
   391     SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_RIGHTY, axis);
   392 
   393     SDL_memcpy(ctx->last_state, data, SDL_min(size, sizeof(ctx->last_state)));
   394 }
   395 #endif /* __WIN32__ */
   396 
   397 static SDL_bool
   398 HIDAPI_DriverXbox360_Update(SDL_Joystick *joystick, hid_device *dev, void *context)
   399 {
   400     SDL_DriverXbox360_Context *ctx = (SDL_DriverXbox360_Context *)context;
   401     Uint8 data[USB_PACKET_LENGTH];
   402     int size;
   403 
   404     while ((size = hid_read_timeout(dev, data, sizeof(data), 0)) > 0) {
   405 #ifdef __WIN32__
   406         HIDAPI_DriverXbox360_HandleStatePacket(joystick, dev, ctx, data, size);
   407 #else
   408         switch (data[0]) {
   409         case 0x00:
   410             HIDAPI_DriverXbox360_HandleStatePacket(joystick, dev, ctx, data, size);
   411             break;
   412         default:
   413 #ifdef DEBUG_JOYSTICK
   414             SDL_Log("Unknown Xbox 360 packet: 0x%.2x\n", data[0]);
   415 #endif
   416             break;
   417         }
   418 #endif /* __WIN32__ */
   419     }
   420 
   421     if (ctx->rumble_expiration) {
   422         Uint32 now = SDL_GetTicks();
   423         if (SDL_TICKS_PASSED(now, ctx->rumble_expiration)) {
   424             HIDAPI_DriverXbox360_Rumble(joystick, dev, context, 0, 0, 0);
   425         }
   426     }
   427 
   428     return (size >= 0);
   429 }
   430 
   431 static void
   432 HIDAPI_DriverXbox360_Quit(SDL_Joystick *joystick, hid_device *dev, void *context)
   433 {
   434 #ifdef __WIN32__
   435     SDL_DriverXbox360_Context *ctx = (SDL_DriverXbox360_Context *)context;
   436 
   437     if (ctx->xinput_enabled) {
   438         HIDAPI_DriverXbox360_MarkXInputSlotFree(ctx->xinput_slot);
   439         WIN_UnloadXInputDLL();
   440     }
   441 #endif
   442     SDL_free(context);
   443 }
   444 
   445 SDL_HIDAPI_DeviceDriver SDL_HIDAPI_DriverXbox360 =
   446 {
   447     SDL_HINT_JOYSTICK_HIDAPI_XBOX,
   448     SDL_TRUE,
   449     HIDAPI_DriverXbox360_IsSupportedDevice,
   450     HIDAPI_DriverXbox360_GetDeviceName,
   451     HIDAPI_DriverXbox360_Init,
   452     HIDAPI_DriverXbox360_Rumble,
   453     HIDAPI_DriverXbox360_Update,
   454     HIDAPI_DriverXbox360_Quit
   455 };
   456 
   457 #endif /* SDL_JOYSTICK_HIDAPI_XBOX360 */
   458 
   459 #endif /* SDL_JOYSTICK_HIDAPI */
   460 
   461 /* vi: set ts=4 sw=4 expandtab: */