Added support for the paddles on the Xbox One Elite Series 2 controller
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142
diff --git a/src/joystick/hidapi/SDL_hidapi_xboxone.c b/src/joystick/hidapi/SDL_hidapi_xboxone.c
index af09f6e..56ff2d6 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)
{
@@ -139,6 +163,21 @@ IsBluetoothXboxOneController(Uint16 vendor_id, Uint16 product_id)
}
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)
{
const Uint16 USB_VENDOR_MICROSOFT = 0x045e;
@@ -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: