From c6817a2c195da52a600a47d5ba796f33dc05dfad Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Thu, 16 Jan 2020 15:32:41 -0800 Subject: [PATCH] Added support for the paddles on the Xbox One Elite Series 2 controller --- src/joystick/hidapi/SDL_hidapi_xboxone.c | 76 ++++++++++++++++++------ 1 file changed, 58 insertions(+), 18 deletions(-) diff --git a/src/joystick/hidapi/SDL_hidapi_xboxone.c b/src/joystick/hidapi/SDL_hidapi_xboxone.c index af09f6e10..56ff2d6bd 100644 --- a/src/joystick/hidapi/SDL_hidapi_xboxone.c +++ b/src/joystick/hidapi/SDL_hidapi_xboxone.c @@ -34,6 +34,9 @@ #ifdef SDL_JOYSTICK_HIDAPI_XBOXONE +/* Define this if you want to log all packets from the controller */ +/*#define DEBUG_XBOX_PROTOCOL*/ + #define USB_PACKET_LENGTH 64 /* The amount of time to wait after hotplug to send controller init sequence */ @@ -100,8 +103,9 @@ static const SDL_DriverXboxOne_InitPacket xboxone_init_packets[] = { { 0x0000, 0x0000, 0x0000, 0x0000, xboxone_init2, sizeof(xboxone_init2), { 0x00, 0x00 } }, { 0x0000, 0x0000, 0x0000, 0x0000, xboxone_init3, sizeof(xboxone_init3), { 0x00, 0x00 } }, { 0x0000, 0x0000, 0x0000, 0x0000, xboxone_init4, sizeof(xboxone_init4), { 0x00, 0x00 } }, + /* These next packets are required for third party controllers (PowerA, PDP, HORI), - but are the wrong protocol for Microsoft Xbox controllers. + but aren't the correct protocol for Microsoft Xbox controllers. */ { 0x0000, 0x0000, 0x045e, 0x0000, xboxone_init5, sizeof(xboxone_init5), { 0x00, 0x00 } }, { 0x0000, 0x0000, 0x045e, 0x0000, xboxone_init6, sizeof(xboxone_init6), { 0x00, 0x00 } }, @@ -116,9 +120,29 @@ typedef struct { Uint8 last_state[USB_PACKET_LENGTH]; SDL_bool rumble_synchronized; Uint32 rumble_expiration; + SDL_bool has_paddles; } SDL_DriverXboxOne_Context; +#ifdef DEBUG_XBOX_PROTOCOL +static void +DumpPacket(const char *prefix, Uint8 *data, int size) +{ + int i; + char buffer[5*USB_PACKET_LENGTH]; + + SDL_snprintf(buffer, sizeof(buffer), prefix, size); + for (i = 0; i < size; ++i) { + if ((i % 8) == 0) { + SDL_snprintf(&buffer[SDL_strlen(buffer)], sizeof(buffer) - SDL_strlen(buffer), "\n%.2d: ", i); + } + SDL_snprintf(&buffer[SDL_strlen(buffer)], sizeof(buffer) - SDL_strlen(buffer), " 0x%.2x", data[i]); + } + SDL_strlcat(buffer, "\n", sizeof(buffer)); + SDL_Log("%s", buffer); +} +#endif /* DEBUG_XBOX_PROTOCOL */ + static SDL_bool IsBluetoothXboxOneController(Uint16 vendor_id, Uint16 product_id) { @@ -138,6 +162,21 @@ IsBluetoothXboxOneController(Uint16 vendor_id, Uint16 product_id) return SDL_FALSE; } +static SDL_bool +ControllerHasPaddles(Uint16 vendor_id, Uint16 product_id) +{ + const Uint16 USB_VENDOR_MICROSOFT = 0x045e; + const Uint16 USB_PRODUCT_XBOX_ONE_ELITE_SERIES_2 = 0x0b00; + + if (vendor_id == USB_VENDOR_MICROSOFT) { + if (product_id == USB_PRODUCT_XBOX_ONE_ELITE_SERIES_2) { + return SDL_TRUE; + } + /* The original Elite controller probably works here, but I don't have one to test... */ + } + return SDL_FALSE; +} + static SDL_bool ControllerNeedsRumbleSequenceSynchronized(Uint16 vendor_id, Uint16 product_id) { @@ -251,14 +290,7 @@ SendControllerInit(hid_device *dev, SDL_DriverXboxOne_Context *ctx) while ((size = hid_read_timeout(dev, data, sizeof(data), 0)) > 0) { #ifdef DEBUG_XBOX_PROTOCOL - SDL_Log("Xbox One INIT packet: size = %d\n" - " 0x%.2x 0x%.2x 0x%.2x 0x%.2x 0x%.2x 0x%.2x 0x%.2x 0x%.2x\n" - " 0x%.2x 0x%.2x 0x%.2x 0x%.2x 0x%.2x 0x%.2x 0x%.2x 0x%.2x\n" - " 0x%.2x 0x%.2x 0x%.2x 0x%.2x\n", - size, - data[0], data[1], data[2], data[3], data[4], data[5], data[6], data[7], - data[8], data[9], data[10], data[11], data[12], data[13], data[14], data[15], - data[16], data[17], data[18], data[19]); + DumpPacket("Xbox One INIT packet: size = %d", data, size); #endif if (size >= 2 && data[0] == packet->response[0] && data[1] == packet->response[1]) { got_response = SDL_TRUE; @@ -339,9 +371,10 @@ HIDAPI_DriverXboxOne_OpenJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joyst ctx->product_id = device->product_id; ctx->start_time = SDL_GetTicks(); ctx->sequence = 1; + ctx->has_paddles = ControllerHasPaddles(ctx->vendor_id, ctx->product_id); /* Initialize the joystick capabilities */ - joystick->nbuttons = SDL_CONTROLLER_BUTTON_MAX; + joystick->nbuttons = ctx->has_paddles ? SDL_CONTROLLER_BUTTON_MAX : (SDL_CONTROLLER_BUTTON_MAX + 4); joystick->naxes = SDL_CONTROLLER_AXIS_MAX; joystick->epowerlevel = SDL_JOYSTICK_POWER_WIRED; @@ -401,6 +434,20 @@ HIDAPI_DriverXboxOne_HandleStatePacket(SDL_Joystick *joystick, hid_device *dev, SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_RIGHTSTICK, (data[5] & 0x80) ? SDL_PRESSED : SDL_RELEASED); } + if (ctx->has_paddles && size >= 20) { + /* data[19] is the paddle remapping mode, 0 = no remapping */ + if (data[19] != 0) { + /* Respect that the paddles are being used for other controls and don't pass them on to the app */ + data[18] = 0; + } + if (ctx->last_state[18] != data[18]) { + SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_MAX+0, (data[18] & 0x01) ? SDL_PRESSED : SDL_RELEASED); + SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_MAX+1, (data[18] & 0x02) ? SDL_PRESSED : SDL_RELEASED); + SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_MAX+2, (data[18] & 0x04) ? SDL_PRESSED : SDL_RELEASED); + SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_MAX+3, (data[18] & 0x08) ? SDL_PRESSED : SDL_RELEASED); + } + } + axis = ((int)*(Sint16*)(&data[6]) * 64) - 32768; if (axis == 32704) { axis = 32767; @@ -464,14 +511,7 @@ HIDAPI_DriverXboxOne_UpdateDevice(SDL_HIDAPI_Device *device) while ((size = hid_read_timeout(device->dev, data, sizeof(data), 0)) > 0) { #ifdef DEBUG_XBOX_PROTOCOL - SDL_Log("Xbox One packet: size = %d\n" - " 0x%.2x 0x%.2x 0x%.2x 0x%.2x 0x%.2x 0x%.2x 0x%.2x 0x%.2x\n" - " 0x%.2x 0x%.2x 0x%.2x 0x%.2x 0x%.2x 0x%.2x 0x%.2x 0x%.2x\n" - " 0x%.2x 0x%.2x 0x%.2x 0x%.2x\n", - size, - data[0], data[1], data[2], data[3], data[4], data[5], data[6], data[7], - data[8], data[9], data[10], data[11], data[12], data[13], data[14], data[15], - data[16], data[17], data[18], data[19]); + DumpPacket("Xbox One packet: size = %d", data, size); #endif switch (data[0]) { case 0x02: