sdl

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

SDL_hidapi_xbox360w.c (12342B)


      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     SDL_bool connected;
     43     Uint8 last_state[USB_PACKET_LENGTH];
     44 } SDL_DriverXbox360W_Context;
     45 
     46 
     47 static SDL_bool
     48 HIDAPI_DriverXbox360W_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)
     49 {
     50     const int XB360W_IFACE_PROTOCOL = 129; /* Wireless */
     51 
     52     if ((vendor_id == USB_VENDOR_MICROSOFT && (product_id == 0x0291 || product_id == 0x02a9 || product_id == 0x0719)) ||
     53         (type == SDL_CONTROLLER_TYPE_XBOX360 && interface_protocol == XB360W_IFACE_PROTOCOL)) {
     54         return SDL_TRUE;
     55     }
     56     return SDL_FALSE;
     57 }
     58 
     59 static const char *
     60 HIDAPI_DriverXbox360W_GetDeviceName(Uint16 vendor_id, Uint16 product_id)
     61 {
     62     return "Xbox 360 Wireless Controller";
     63 }
     64 
     65 static SDL_bool SetSlotLED(hid_device *dev, Uint8 slot)
     66 {
     67     Uint8 mode = 0x02 + slot;
     68     const Uint8 led_packet[] = { 0x00, 0x00, 0x08, (0x40 + (mode % 0x0e)), 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
     69 
     70     if (hid_write(dev, led_packet, sizeof(led_packet)) != sizeof(led_packet)) {
     71         return SDL_FALSE;
     72     }
     73     return SDL_TRUE;
     74 }
     75 
     76 static void
     77 UpdatePowerLevel(SDL_Joystick *joystick, Uint8 level)
     78 {
     79     float normalized_level = (float)level / 255.0f;
     80 
     81     if (normalized_level <= 0.05f) {
     82         joystick->epowerlevel = SDL_JOYSTICK_POWER_EMPTY;
     83     } else if (normalized_level <= 0.20f) {
     84         joystick->epowerlevel = SDL_JOYSTICK_POWER_LOW;
     85     } else if (normalized_level <= 0.70f) {
     86         joystick->epowerlevel = SDL_JOYSTICK_POWER_MEDIUM;
     87     } else {
     88         joystick->epowerlevel = SDL_JOYSTICK_POWER_FULL;
     89     }
     90 }
     91 
     92 static SDL_bool
     93 HIDAPI_DriverXbox360W_InitDevice(SDL_HIDAPI_Device *device)
     94 {
     95     SDL_DriverXbox360W_Context *ctx;
     96 
     97     /* Requests controller presence information from the wireless dongle */
     98     const Uint8 init_packet[] = { 0x08, 0x00, 0x0F, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
     99 
    100     ctx = (SDL_DriverXbox360W_Context *)SDL_calloc(1, sizeof(*ctx));
    101     if (!ctx) {
    102         SDL_OutOfMemory();
    103         return SDL_FALSE;
    104     }
    105 
    106     device->dev = hid_open_path(device->path, 0);
    107     if (!device->dev) {
    108         SDL_free(ctx);
    109         SDL_SetError("Couldn't open %s", device->path);
    110         return SDL_FALSE;
    111     }
    112     device->context = ctx;
    113 
    114     if (hid_write(device->dev, init_packet, sizeof(init_packet)) != sizeof(init_packet)) {
    115         SDL_SetError("Couldn't write init packet");
    116         return SDL_FALSE;
    117     }
    118 
    119     return SDL_TRUE;
    120 }
    121 
    122 static int
    123 HIDAPI_DriverXbox360W_GetDevicePlayerIndex(SDL_HIDAPI_Device *device, SDL_JoystickID instance_id)
    124 {
    125     return -1;
    126 }
    127 
    128 static void
    129 HIDAPI_DriverXbox360W_SetDevicePlayerIndex(SDL_HIDAPI_Device *device, SDL_JoystickID instance_id, int player_index)
    130 {
    131     if (!device->dev) {
    132         return;
    133     }
    134     SetSlotLED(device->dev, (player_index % 4));
    135 }
    136 
    137 static SDL_bool
    138 HIDAPI_DriverXbox360W_OpenJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick)
    139 {
    140     SDL_DriverXbox360W_Context *ctx = (SDL_DriverXbox360W_Context *)device->context;
    141 
    142     SDL_zeroa(ctx->last_state);
    143 
    144     /* Initialize the joystick capabilities */
    145     joystick->nbuttons = 15;
    146     joystick->naxes = SDL_CONTROLLER_AXIS_MAX;
    147     joystick->epowerlevel = SDL_JOYSTICK_POWER_UNKNOWN;
    148 
    149     return SDL_TRUE;
    150 }
    151 
    152 static int
    153 HIDAPI_DriverXbox360W_RumbleJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble)
    154 {
    155     Uint8 rumble_packet[] = { 0x00, 0x01, 0x0f, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
    156 
    157     rumble_packet[5] = (low_frequency_rumble >> 8);
    158     rumble_packet[6] = (high_frequency_rumble >> 8);
    159 
    160     if (SDL_HIDAPI_SendRumble(device, rumble_packet, sizeof(rumble_packet)) != sizeof(rumble_packet)) {
    161         return SDL_SetError("Couldn't send rumble packet");
    162     }
    163     return 0;
    164 }
    165 
    166 static int
    167 HIDAPI_DriverXbox360W_RumbleJoystickTriggers(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, Uint16 left_rumble, Uint16 right_rumble)
    168 {
    169     return SDL_Unsupported();
    170 }
    171 
    172 static SDL_bool
    173 HIDAPI_DriverXbox360W_HasJoystickLED(SDL_HIDAPI_Device *device, SDL_Joystick *joystick)
    174 {
    175     /* Doesn't have an RGB LED, so don't return true here */
    176     return SDL_FALSE;
    177 }
    178 
    179 static int
    180 HIDAPI_DriverXbox360W_SetJoystickLED(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, Uint8 red, Uint8 green, Uint8 blue)
    181 {
    182     return SDL_Unsupported();
    183 }
    184 
    185 static int
    186 HIDAPI_DriverXbox360W_SetJoystickSensorsEnabled(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, SDL_bool enabled)
    187 {
    188     return SDL_Unsupported();
    189 }
    190 
    191 static void
    192 HIDAPI_DriverXbox360W_HandleStatePacket(SDL_Joystick *joystick, hid_device *dev, SDL_DriverXbox360W_Context *ctx, Uint8 *data, int size)
    193 {
    194     Sint16 axis;
    195     const SDL_bool invert_y_axes = SDL_TRUE;
    196 
    197     if (ctx->last_state[2] != data[2]) {
    198         SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_UP, (data[2] & 0x01) ? SDL_PRESSED : SDL_RELEASED);
    199         SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_DOWN, (data[2] & 0x02) ? SDL_PRESSED : SDL_RELEASED);
    200         SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_LEFT, (data[2] & 0x04) ? SDL_PRESSED : SDL_RELEASED);
    201         SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_RIGHT, (data[2] & 0x08) ? SDL_PRESSED : SDL_RELEASED);
    202         SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_START, (data[2] & 0x10) ? SDL_PRESSED : SDL_RELEASED);
    203         SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_BACK, (data[2] & 0x20) ? SDL_PRESSED : SDL_RELEASED);
    204         SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_LEFTSTICK, (data[2] & 0x40) ? SDL_PRESSED : SDL_RELEASED);
    205         SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_RIGHTSTICK, (data[2] & 0x80) ? SDL_PRESSED : SDL_RELEASED);
    206     }
    207 
    208     if (ctx->last_state[3] != data[3]) {
    209         SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_LEFTSHOULDER, (data[3] & 0x01) ? SDL_PRESSED : SDL_RELEASED);
    210         SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER, (data[3] & 0x02) ? SDL_PRESSED : SDL_RELEASED);
    211         SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_GUIDE, (data[3] & 0x04) ? SDL_PRESSED : SDL_RELEASED);
    212         SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_A, (data[3] & 0x10) ? SDL_PRESSED : SDL_RELEASED);
    213         SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_B, (data[3] & 0x20) ? SDL_PRESSED : SDL_RELEASED);
    214         SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_X, (data[3] & 0x40) ? SDL_PRESSED : SDL_RELEASED);
    215         SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_Y, (data[3] & 0x80) ? SDL_PRESSED : SDL_RELEASED);
    216     }
    217 
    218     axis = ((int)data[4] * 257) - 32768;
    219     SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERLEFT, axis);
    220     axis = ((int)data[5] * 257) - 32768;
    221     SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERRIGHT, axis);
    222     axis = *(Sint16*)(&data[6]);
    223     SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_LEFTX, axis);
    224     axis = *(Sint16*)(&data[8]);
    225     if (invert_y_axes) {
    226         axis = ~axis;
    227     }
    228     SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_LEFTY, axis);
    229     axis = *(Sint16*)(&data[10]);
    230     SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_RIGHTX, axis);
    231     axis = *(Sint16*)(&data[12]);
    232     if (invert_y_axes) {
    233         axis = ~axis;
    234     }
    235     SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_RIGHTY, axis);
    236 
    237     SDL_memcpy(ctx->last_state, data, SDL_min(size, sizeof(ctx->last_state)));
    238 }
    239 
    240 static SDL_bool
    241 HIDAPI_DriverXbox360W_UpdateDevice(SDL_HIDAPI_Device *device)
    242 {
    243     SDL_DriverXbox360W_Context *ctx = (SDL_DriverXbox360W_Context *)device->context;
    244     SDL_Joystick *joystick = NULL;
    245     Uint8 data[USB_PACKET_LENGTH];
    246     int size;
    247 
    248     if (device->num_joysticks > 0) {
    249         joystick = SDL_JoystickFromInstanceID(device->joysticks[0]);
    250     }
    251 
    252     while ((size = hid_read_timeout(device->dev, data, sizeof(data), 0)) > 0) {
    253 #ifdef DEBUG_XBOX_PROTOCOL
    254         HIDAPI_DumpPacket("Xbox 360 wireless packet: size = %d", data, size);
    255 #endif
    256         if (size == 2 && data[0] == 0x08) {
    257             SDL_bool connected = (data[1] & 0x80) ? SDL_TRUE : SDL_FALSE;
    258 #ifdef DEBUG_JOYSTICK
    259             SDL_Log("Connected = %s\n", connected ? "TRUE" : "FALSE");
    260 #endif
    261             if (connected != ctx->connected) {
    262                 ctx->connected = connected;
    263 
    264                 if (connected) {
    265                     SDL_JoystickID joystickID;
    266 
    267                     HIDAPI_JoystickConnected(device, &joystickID);
    268 
    269                 } else if (device->num_joysticks > 0) {
    270                     HIDAPI_JoystickDisconnected(device, device->joysticks[0]);
    271                 }
    272             }
    273         } else if (size == 29 && data[0] == 0x00 && data[1] == 0x0f && data[2] == 0x00 && data[3] == 0xf0) {
    274             /* Serial number is data[7-13] */
    275 #ifdef DEBUG_JOYSTICK
    276             SDL_Log("Battery status (initial): %d\n", data[17]);
    277 #endif
    278             if (joystick) {
    279                 UpdatePowerLevel(joystick, data[17]);
    280             }
    281         } else if (size == 29 && data[0] == 0x00 && data[1] == 0x00 && data[2] == 0x00 && data[3] == 0x13) {
    282 #ifdef DEBUG_JOYSTICK
    283             SDL_Log("Battery status: %d\n", data[4]);
    284 #endif
    285             if (joystick) {
    286                 UpdatePowerLevel(joystick, data[4]);
    287             }
    288         } else if (size == 29 && data[0] == 0x00 && (data[1] & 0x01) == 0x01) {
    289             if (joystick) {
    290                 HIDAPI_DriverXbox360W_HandleStatePacket(joystick, device->dev, ctx, data+4, size-4);
    291             }
    292         }
    293     }
    294 
    295     if (joystick) {
    296         if (size < 0) {
    297             /* Read error, device is disconnected */
    298             HIDAPI_JoystickDisconnected(device, joystick->instance_id);
    299         }
    300     }
    301     return (size >= 0);
    302 }
    303 
    304 static void
    305 HIDAPI_DriverXbox360W_CloseJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick)
    306 {
    307 }
    308 
    309 static void
    310 HIDAPI_DriverXbox360W_FreeDevice(SDL_HIDAPI_Device *device)
    311 {
    312     hid_close(device->dev);
    313     device->dev = NULL;
    314 
    315     SDL_free(device->context);
    316     device->context = NULL;
    317 }
    318 
    319 SDL_HIDAPI_DeviceDriver SDL_HIDAPI_DriverXbox360W =
    320 {
    321     SDL_HINT_JOYSTICK_HIDAPI_XBOX,
    322     SDL_TRUE,
    323     HIDAPI_DriverXbox360W_IsSupportedDevice,
    324     HIDAPI_DriverXbox360W_GetDeviceName,
    325     HIDAPI_DriverXbox360W_InitDevice,
    326     HIDAPI_DriverXbox360W_GetDevicePlayerIndex,
    327     HIDAPI_DriverXbox360W_SetDevicePlayerIndex,
    328     HIDAPI_DriverXbox360W_UpdateDevice,
    329     HIDAPI_DriverXbox360W_OpenJoystick,
    330     HIDAPI_DriverXbox360W_RumbleJoystick,
    331     HIDAPI_DriverXbox360W_RumbleJoystickTriggers,
    332     HIDAPI_DriverXbox360W_HasJoystickLED,
    333     HIDAPI_DriverXbox360W_SetJoystickLED,
    334     HIDAPI_DriverXbox360W_SetJoystickSensorsEnabled,
    335     HIDAPI_DriverXbox360W_CloseJoystick,
    336     HIDAPI_DriverXbox360W_FreeDevice,
    337 };
    338 
    339 #endif /* SDL_JOYSTICK_HIDAPI_XBOX360 */
    340 
    341 #endif /* SDL_JOYSTICK_HIDAPI */
    342 
    343 /* vi: set ts=4 sw=4 expandtab: */