sdl

FORK: Simple Directmedia Layer
git clone https://git.neptards.moe/neptards/sdl.git
Log | Files | Refs

SDL_hidapi_xbox360.c (12955B)


      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 /* Define this if you want to log all packets from the controller */
     38 /*#define DEBUG_XBOX_PROTOCOL*/
     39 
     40 
     41 typedef struct {
     42     Uint8 last_state[USB_PACKET_LENGTH];
     43 } SDL_DriverXbox360_Context;
     44 
     45 #if defined(__MACOSX__)
     46 static SDL_bool
     47 IsBluetoothXboxOneController(Uint16 vendor_id, Uint16 product_id)
     48 {
     49     /* Check to see if it's the Xbox One S or Xbox One Elite Series 2 in Bluetooth mode */
     50     if (vendor_id == USB_VENDOR_MICROSOFT) {
     51         if (product_id == USB_PRODUCT_XBOX_ONE_S_REV1_BLUETOOTH ||
     52             product_id == USB_PRODUCT_XBOX_ONE_S_REV2_BLUETOOTH ||
     53             product_id == USB_PRODUCT_XBOX_ONE_ELITE_SERIES_2_BLUETOOTH) {
     54             return SDL_TRUE;
     55         }
     56     }
     57     return SDL_FALSE;
     58 }
     59 #endif
     60 
     61 static SDL_bool
     62 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)
     63 {
     64     const int XB360W_IFACE_PROTOCOL = 129; /* Wireless */
     65 
     66     if (vendor_id == USB_VENDOR_NVIDIA) {
     67         /* This is the NVIDIA Shield controller which doesn't talk Xbox controller protocol */
     68         return SDL_FALSE;
     69     }
     70     if ((vendor_id == USB_VENDOR_MICROSOFT && (product_id == 0x0291 || product_id == 0x0719)) ||
     71         (type == SDL_CONTROLLER_TYPE_XBOX360 && interface_protocol == XB360W_IFACE_PROTOCOL)) {
     72         /* This is the wireless dongle, which talks a different protocol */
     73         return SDL_FALSE;
     74     }
     75     if (interface_number > 0) {
     76         /* This is the chatpad or other input interface, not the Xbox 360 interface */
     77         return SDL_FALSE;
     78     }
     79 #if defined(__MACOSX__) || defined(__WIN32__)
     80     if (vendor_id == USB_VENDOR_MICROSOFT && product_id == 0x028e && version == 1) {
     81         /* This is the Steam Virtual Gamepad, which isn't supported by this driver */
     82         return SDL_FALSE;
     83     }
     84 #endif
     85 #if defined(__MACOSX__)
     86     /* Wired Xbox One controllers are handled by this driver, interfacing with
     87        the 360Controller driver available from:
     88        https://github.com/360Controller/360Controller/releases
     89 
     90        Bluetooth Xbox One controllers are handled by the SDL Xbox One driver
     91     */
     92     if (IsBluetoothXboxOneController(vendor_id, product_id)) {
     93         return SDL_FALSE;
     94     }
     95     return (type == SDL_CONTROLLER_TYPE_XBOX360 || type == SDL_CONTROLLER_TYPE_XBOXONE) ? SDL_TRUE : SDL_FALSE;
     96 #else
     97     return (type == SDL_CONTROLLER_TYPE_XBOX360) ? SDL_TRUE : SDL_FALSE;
     98 #endif
     99 }
    100 
    101 static const char *
    102 HIDAPI_DriverXbox360_GetDeviceName(Uint16 vendor_id, Uint16 product_id)
    103 {
    104     return NULL;
    105 }
    106 
    107 static SDL_bool SetSlotLED(hid_device *dev, Uint8 slot)
    108 {
    109     Uint8 mode = 0x02 + slot;
    110     const Uint8 led_packet[] = { 0x01, 0x03, mode };
    111 
    112     if (hid_write(dev, led_packet, sizeof(led_packet)) != sizeof(led_packet)) {
    113         return SDL_FALSE;
    114     }
    115     return SDL_TRUE;
    116 }
    117 
    118 static SDL_bool
    119 HIDAPI_DriverXbox360_InitDevice(SDL_HIDAPI_Device *device)
    120 {
    121     return HIDAPI_JoystickConnected(device, NULL);
    122 }
    123 
    124 static int
    125 HIDAPI_DriverXbox360_GetDevicePlayerIndex(SDL_HIDAPI_Device *device, SDL_JoystickID instance_id)
    126 {
    127     return -1;
    128 }
    129 
    130 static void
    131 HIDAPI_DriverXbox360_SetDevicePlayerIndex(SDL_HIDAPI_Device *device, SDL_JoystickID instance_id, int player_index)
    132 {
    133     if (!device->dev) {
    134         return;
    135     }
    136     SetSlotLED(device->dev, (player_index % 4));
    137 }
    138 
    139 static SDL_bool
    140 HIDAPI_DriverXbox360_OpenJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick)
    141 {
    142     SDL_DriverXbox360_Context *ctx;
    143     int player_index;
    144 
    145     ctx = (SDL_DriverXbox360_Context *)SDL_calloc(1, sizeof(*ctx));
    146     if (!ctx) {
    147         SDL_OutOfMemory();
    148         return SDL_FALSE;
    149     }
    150 
    151     device->dev = hid_open_path(device->path, 0);
    152     if (!device->dev) {
    153         SDL_SetError("Couldn't open %s", device->path);
    154         SDL_free(ctx);
    155         return SDL_FALSE;
    156     }
    157     device->context = ctx;
    158 
    159     /* Set the controller LED */
    160     player_index = SDL_JoystickGetPlayerIndex(joystick);
    161     if (player_index >= 0) {
    162         SetSlotLED(device->dev, (player_index % 4));
    163     }
    164 
    165     /* Initialize the joystick capabilities */
    166     joystick->nbuttons = 15;
    167     joystick->naxes = SDL_CONTROLLER_AXIS_MAX;
    168     joystick->epowerlevel = SDL_JOYSTICK_POWER_WIRED;
    169 
    170     return SDL_TRUE;
    171 }
    172 
    173 static int
    174 HIDAPI_DriverXbox360_RumbleJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble)
    175 {
    176 #ifdef __MACOSX__
    177     if (IsBluetoothXboxOneController(device->vendor_id, device->product_id)) {
    178         Uint8 rumble_packet[] = { 0x03, 0x0F, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00 };
    179 
    180         rumble_packet[4] = (low_frequency_rumble >> 8);
    181         rumble_packet[5] = (high_frequency_rumble >> 8);
    182 
    183         if (SDL_HIDAPI_SendRumble(device, rumble_packet, sizeof(rumble_packet)) != sizeof(rumble_packet)) {
    184             return SDL_SetError("Couldn't send rumble packet");
    185         }
    186     } else {
    187         /* On Mac OS X the 360Controller driver uses this short report,
    188            and we need to prefix it with a magic token so hidapi passes it through untouched
    189          */
    190         Uint8 rumble_packet[] = { 'M', 'A', 'G', 'I', 'C', '0', 0x00, 0x04, 0x00, 0x00 };
    191 
    192         rumble_packet[6+2] = (low_frequency_rumble >> 8);
    193         rumble_packet[6+3] = (high_frequency_rumble >> 8);
    194 
    195         if (SDL_HIDAPI_SendRumble(device, rumble_packet, sizeof(rumble_packet)) != sizeof(rumble_packet)) {
    196             return SDL_SetError("Couldn't send rumble packet");
    197         }
    198     }
    199 #else
    200     Uint8 rumble_packet[] = { 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
    201 
    202     rumble_packet[3] = (low_frequency_rumble >> 8);
    203     rumble_packet[4] = (high_frequency_rumble >> 8);
    204 
    205     if (SDL_HIDAPI_SendRumble(device, rumble_packet, sizeof(rumble_packet)) != sizeof(rumble_packet)) {
    206         return SDL_SetError("Couldn't send rumble packet");
    207     }
    208 #endif
    209     return 0;
    210 }
    211 
    212 static int
    213 HIDAPI_DriverXbox360_RumbleJoystickTriggers(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, Uint16 left_rumble, Uint16 right_rumble)
    214 {
    215     return SDL_Unsupported();
    216 }
    217 
    218 static SDL_bool
    219 HIDAPI_DriverXbox360_HasJoystickLED(SDL_HIDAPI_Device *device, SDL_Joystick *joystick)
    220 {
    221     /* Doesn't have an RGB LED, so don't return true here */
    222     return SDL_FALSE;
    223 }
    224 
    225 static int
    226 HIDAPI_DriverXbox360_SetJoystickLED(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, Uint8 red, Uint8 green, Uint8 blue)
    227 {
    228     return SDL_Unsupported();
    229 }
    230 
    231 static int
    232 HIDAPI_DriverXbox360_SetJoystickSensorsEnabled(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, SDL_bool enabled)
    233 {
    234     return SDL_Unsupported();
    235 }
    236 
    237 static void
    238 HIDAPI_DriverXbox360_HandleStatePacket(SDL_Joystick *joystick, SDL_DriverXbox360_Context *ctx, Uint8 *data, int size)
    239 {
    240     Sint16 axis;
    241 #ifdef __MACOSX__
    242     const SDL_bool invert_y_axes = SDL_FALSE;
    243 #else
    244     const SDL_bool invert_y_axes = SDL_TRUE;
    245 #endif
    246 
    247     if (ctx->last_state[2] != data[2]) {
    248         SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_UP, (data[2] & 0x01) ? SDL_PRESSED : SDL_RELEASED);
    249         SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_DOWN, (data[2] & 0x02) ? SDL_PRESSED : SDL_RELEASED);
    250         SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_LEFT, (data[2] & 0x04) ? SDL_PRESSED : SDL_RELEASED);
    251         SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_RIGHT, (data[2] & 0x08) ? SDL_PRESSED : SDL_RELEASED);
    252         SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_START, (data[2] & 0x10) ? SDL_PRESSED : SDL_RELEASED);
    253         SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_BACK, (data[2] & 0x20) ? SDL_PRESSED : SDL_RELEASED);
    254         SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_LEFTSTICK, (data[2] & 0x40) ? SDL_PRESSED : SDL_RELEASED);
    255         SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_RIGHTSTICK, (data[2] & 0x80) ? SDL_PRESSED : SDL_RELEASED);
    256     }
    257 
    258     if (ctx->last_state[3] != data[3]) {
    259         SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_LEFTSHOULDER, (data[3] & 0x01) ? SDL_PRESSED : SDL_RELEASED);
    260         SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER, (data[3] & 0x02) ? SDL_PRESSED : SDL_RELEASED);
    261         SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_GUIDE, (data[3] & 0x04) ? SDL_PRESSED : SDL_RELEASED);
    262         SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_A, (data[3] & 0x10) ? SDL_PRESSED : SDL_RELEASED);
    263         SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_B, (data[3] & 0x20) ? SDL_PRESSED : SDL_RELEASED);
    264         SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_X, (data[3] & 0x40) ? SDL_PRESSED : SDL_RELEASED);
    265         SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_Y, (data[3] & 0x80) ? SDL_PRESSED : SDL_RELEASED);
    266     }
    267 
    268     axis = ((int)data[4] * 257) - 32768;
    269     SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERLEFT, axis);
    270     axis = ((int)data[5] * 257) - 32768;
    271     SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERRIGHT, axis);
    272     axis = *(Sint16*)(&data[6]);
    273     SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_LEFTX, axis);
    274     axis = *(Sint16*)(&data[8]);
    275     if (invert_y_axes) {
    276         axis = ~axis;
    277     }
    278     SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_LEFTY, axis);
    279     axis = *(Sint16*)(&data[10]);
    280     SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_RIGHTX, axis);
    281     axis = *(Sint16*)(&data[12]);
    282     if (invert_y_axes) {
    283         axis = ~axis;
    284     }
    285     SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_RIGHTY, axis);
    286 
    287     SDL_memcpy(ctx->last_state, data, SDL_min(size, sizeof(ctx->last_state)));
    288 }
    289 
    290 static SDL_bool
    291 HIDAPI_DriverXbox360_UpdateDevice(SDL_HIDAPI_Device *device)
    292 {
    293     SDL_DriverXbox360_Context *ctx = (SDL_DriverXbox360_Context *)device->context;
    294     SDL_Joystick *joystick = NULL;
    295     Uint8 data[USB_PACKET_LENGTH];
    296     int size = 0;
    297 
    298     if (device->num_joysticks > 0) {
    299         joystick = SDL_JoystickFromInstanceID(device->joysticks[0]);
    300     }
    301     if (!joystick) {
    302         return SDL_FALSE;
    303     }
    304 
    305     while ((size = hid_read_timeout(device->dev, data, sizeof(data), 0)) > 0) {
    306 #ifdef DEBUG_XBOX_PROTOCOL
    307         HIDAPI_DumpPacket("Xbox 360 packet: size = %d", data, size);
    308 #endif
    309         if (data[0] == 0x00) {
    310             HIDAPI_DriverXbox360_HandleStatePacket(joystick, ctx, data, size);
    311         }
    312     }
    313 
    314     if (size < 0) {
    315         /* Read error, device is disconnected */
    316         HIDAPI_JoystickDisconnected(device, joystick->instance_id);
    317     }
    318     return (size >= 0);
    319 }
    320 
    321 static void
    322 HIDAPI_DriverXbox360_CloseJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick)
    323 {
    324     if (device->dev) {
    325         hid_close(device->dev);
    326         device->dev = NULL;
    327     }
    328 
    329     SDL_free(device->context);
    330     device->context = NULL;
    331 }
    332 
    333 static void
    334 HIDAPI_DriverXbox360_FreeDevice(SDL_HIDAPI_Device *device)
    335 {
    336 }
    337 
    338 SDL_HIDAPI_DeviceDriver SDL_HIDAPI_DriverXbox360 =
    339 {
    340     SDL_HINT_JOYSTICK_HIDAPI_XBOX,
    341     SDL_TRUE,
    342     HIDAPI_DriverXbox360_IsSupportedDevice,
    343     HIDAPI_DriverXbox360_GetDeviceName,
    344     HIDAPI_DriverXbox360_InitDevice,
    345     HIDAPI_DriverXbox360_GetDevicePlayerIndex,
    346     HIDAPI_DriverXbox360_SetDevicePlayerIndex,
    347     HIDAPI_DriverXbox360_UpdateDevice,
    348     HIDAPI_DriverXbox360_OpenJoystick,
    349     HIDAPI_DriverXbox360_RumbleJoystick,
    350     HIDAPI_DriverXbox360_RumbleJoystickTriggers,
    351     HIDAPI_DriverXbox360_HasJoystickLED,
    352     HIDAPI_DriverXbox360_SetJoystickLED,
    353     HIDAPI_DriverXbox360_SetJoystickSensorsEnabled,
    354     HIDAPI_DriverXbox360_CloseJoystick,
    355     HIDAPI_DriverXbox360_FreeDevice,
    356 };
    357 
    358 #endif /* SDL_JOYSTICK_HIDAPI_XBOX360 */
    359 
    360 #endif /* SDL_JOYSTICK_HIDAPI */
    361 
    362 /* vi: set ts=4 sw=4 expandtab: */