Added support for the Xbox Elite controller paddles with firmware version 5.13+
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 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185
diff --git a/src/joystick/SDL_joystick.c b/src/joystick/SDL_joystick.c
index 009e9a0..0ac7021 100644
--- a/src/joystick/SDL_joystick.c
+++ b/src/joystick/SDL_joystick.c
@@ -2229,7 +2229,8 @@ SDL_IsJoystickXboxOneElite(Uint16 vendor_id, Uint16 product_id)
if (vendor_id == USB_VENDOR_MICROSOFT) {
if (product_id == USB_PRODUCT_XBOX_ONE_ELITE_SERIES_1 ||
product_id == USB_PRODUCT_XBOX_ONE_ELITE_SERIES_2 ||
- product_id == USB_PRODUCT_XBOX_ONE_ELITE_SERIES_2_BLUETOOTH) {
+ product_id == USB_PRODUCT_XBOX_ONE_ELITE_SERIES_2_BLUETOOTH ||
+ product_id == USB_PRODUCT_XBOX_ONE_ELITE_SERIES_2_BLE) {
return SDL_TRUE;
}
}
diff --git a/src/joystick/hidapi/SDL_hidapi_xboxone.c b/src/joystick/hidapi/SDL_hidapi_xboxone.c
index 3c1e00e..6e58fe9 100644
--- a/src/joystick/hidapi/SDL_hidapi_xboxone.c
+++ b/src/joystick/hidapi/SDL_hidapi_xboxone.c
@@ -125,8 +125,10 @@ typedef struct {
SDL_bool has_guide_packet;
SDL_bool has_color_led;
SDL_bool has_paddles;
+ SDL_bool has_unmapped_state;
SDL_bool has_trigger_rumble;
SDL_bool has_share_button;
+ Uint8 last_paddle_state;
Uint8 low_frequency_rumble;
Uint8 high_frequency_rumble;
Uint8 left_trigger_rumble;
@@ -608,6 +610,70 @@ HIDAPI_DriverXboxOne_SetJoystickSensorsEnabled(SDL_HIDAPI_Device *device, SDL_Jo
return SDL_Unsupported();
}
+/*
+ * The Xbox One Elite controller with 5.13+ firmware sends the unmapped state in a separate packet.
+ * We can use this to send the paddle state when they aren't mapped
+ */
+static void
+HIDAPI_DriverXboxOne_HandleUnmappedStatePacket(SDL_Joystick *joystick, SDL_DriverXboxOne_Context *ctx, Uint8 *data, int size)
+{
+ Uint8 profile;
+ int paddle_index;
+ int button1_bit;
+ int button2_bit;
+ int button3_bit;
+ int button4_bit;
+ SDL_bool paddles_mapped;
+
+ if (size == 21) {
+ /* XBox One Elite Series 2 */
+ paddle_index = 18;
+ button1_bit = 0x01;
+ button2_bit = 0x02;
+ button3_bit = 0x04;
+ button4_bit = 0x08;
+ profile = data[19];
+
+ if (profile == 0) {
+ paddles_mapped = SDL_FALSE;
+ } else if (SDL_memcmp(&data[4], &ctx->last_state[4], 14) == 0) {
+ /* We're using a profile, but paddles aren't mapped */
+ paddles_mapped = SDL_FALSE;
+ } else {
+ /* Something is mapped, we can't use the paddles */
+ paddles_mapped = SDL_TRUE;
+ }
+
+ } else {
+ /* Unknown format */
+ return;
+ }
+#ifdef DEBUG_XBOX_PROTOCOL
+ SDL_Log(">>> Paddles: %d,%d,%d,%d mapped = %s\n",
+ (data[paddle_index] & button1_bit) ? 1 : 0,
+ (data[paddle_index] & button2_bit) ? 1 : 0,
+ (data[paddle_index] & button3_bit) ? 1 : 0,
+ (data[paddle_index] & button4_bit) ? 1 : 0,
+ paddles_mapped ? "TRUE" : "FALSE"
+ );
+#endif
+
+ if (paddles_mapped) {
+ /* Respect that the paddles are being used for other controls and don't pass them on to the app */
+ data[paddle_index] = 0;
+ }
+
+ if (ctx->last_paddle_state != data[paddle_index]) {
+ int nButton = SDL_CONTROLLER_BUTTON_MISC1 + ctx->has_share_button; /* Next available button */
+ SDL_PrivateJoystickButton(joystick, nButton++, (data[paddle_index] & button1_bit) ? SDL_PRESSED : SDL_RELEASED);
+ SDL_PrivateJoystickButton(joystick, nButton++, (data[paddle_index] & button2_bit) ? SDL_PRESSED : SDL_RELEASED);
+ SDL_PrivateJoystickButton(joystick, nButton++, (data[paddle_index] & button3_bit) ? SDL_PRESSED : SDL_RELEASED);
+ SDL_PrivateJoystickButton(joystick, nButton++, (data[paddle_index] & button4_bit) ? SDL_PRESSED : SDL_RELEASED);
+ ctx->last_paddle_state = data[paddle_index];
+ }
+ ctx->has_unmapped_state = SDL_TRUE;
+}
+
static void
HIDAPI_DriverXboxOne_HandleStatePacket(SDL_Joystick *joystick, SDL_DriverXboxOne_Context *ctx, Uint8 *data, int size)
{
@@ -674,7 +740,7 @@ HIDAPI_DriverXboxOne_HandleStatePacket(SDL_Joystick *joystick, SDL_DriverXboxOne
P3: 0x04 (A) P1: 0x01 (B)
P4: 0x08 (X) P2: 0x02 (Y)
*/
- if (ctx->has_paddles && (size == 33 || size == 38 || size == 50)) {
+ if (ctx->has_paddles && !ctx->has_unmapped_state && (size == 33 || size == 38 || size == 50)) {
int paddle_index;
int button1_bit;
int button2_bit;
@@ -726,12 +792,13 @@ HIDAPI_DriverXboxOne_HandleStatePacket(SDL_Joystick *joystick, SDL_DriverXboxOne
data[paddle_index] = 0;
}
- if (ctx->last_state[paddle_index] != data[paddle_index]) {
+ if (ctx->last_paddle_state != data[paddle_index]) {
int nButton = SDL_CONTROLLER_BUTTON_MISC1 + ctx->has_share_button; /* Next available button */
SDL_PrivateJoystickButton(joystick, nButton++, (data[paddle_index] & button1_bit) ? SDL_PRESSED : SDL_RELEASED);
SDL_PrivateJoystickButton(joystick, nButton++, (data[paddle_index] & button2_bit) ? SDL_PRESSED : SDL_RELEASED);
SDL_PrivateJoystickButton(joystick, nButton++, (data[paddle_index] & button3_bit) ? SDL_PRESSED : SDL_RELEASED);
SDL_PrivateJoystickButton(joystick, nButton++, (data[paddle_index] & button4_bit) ? SDL_PRESSED : SDL_RELEASED);
+ ctx->last_paddle_state = data[paddle_index];
}
}
@@ -844,7 +911,7 @@ HIDAPI_DriverXboxOneBluetooth_HandleButtons(SDL_Joystick *joystick, SDL_DriverXb
P3: 0x04 (A) P1: 0x01 (B)
P4: 0x08 (X) P2: 0x02 (Y)
*/
- if (ctx->has_paddles && (size == 39 || size == 55)) {
+ if (ctx->has_paddles && (size == 20 || size == 39 || size == 55)) {
int paddle_index;
int button1_bit;
int button2_bit;
@@ -860,7 +927,7 @@ HIDAPI_DriverXboxOneBluetooth_HandleButtons(SDL_Joystick *joystick, SDL_DriverXb
button3_bit = 0x04;
button4_bit = 0x08;
paddles_mapped = (data[35] != 0);
- } else /* if (size == 39) */ {
+ } else if (size == 39) {
/* Updated firmware for the Xbox Elite Series 2 controller */
paddle_index = 17;
button1_bit = 0x01;
@@ -868,6 +935,14 @@ HIDAPI_DriverXboxOneBluetooth_HandleButtons(SDL_Joystick *joystick, SDL_DriverXb
button3_bit = 0x04;
button4_bit = 0x08;
paddles_mapped = (data[19] != 0);
+ } else /* if (size == 20) */ {
+ /* Updated firmware for the Xbox Elite Series 2 controller (5.13+) */
+ paddle_index = 19;
+ button1_bit = 0x01;
+ button2_bit = 0x02;
+ button3_bit = 0x04;
+ button4_bit = 0x08;
+ paddles_mapped = (data[17] != 0);
}
#ifdef DEBUG_XBOX_PROTOCOL
@@ -885,12 +960,13 @@ HIDAPI_DriverXboxOneBluetooth_HandleButtons(SDL_Joystick *joystick, SDL_DriverXb
data[paddle_index] = 0;
}
- if (ctx->last_state[paddle_index] != data[paddle_index]) {
+ if (ctx->last_paddle_state != data[paddle_index]) {
int nButton = SDL_CONTROLLER_BUTTON_MISC1; /* Next available button */
SDL_PrivateJoystickButton(joystick, nButton++, (data[paddle_index] & button1_bit) ? SDL_PRESSED : SDL_RELEASED);
SDL_PrivateJoystickButton(joystick, nButton++, (data[paddle_index] & button2_bit) ? SDL_PRESSED : SDL_RELEASED);
SDL_PrivateJoystickButton(joystick, nButton++, (data[paddle_index] & button3_bit) ? SDL_PRESSED : SDL_RELEASED);
SDL_PrivateJoystickButton(joystick, nButton++, (data[paddle_index] & button4_bit) ? SDL_PRESSED : SDL_RELEASED);
+ ctx->last_paddle_state = data[paddle_index];
}
}
}
@@ -1187,6 +1263,12 @@ HIDAPI_DriverXboxOne_UpdateDevice(SDL_HIDAPI_Device *device)
}
HIDAPI_DriverXboxOne_HandleModePacket(joystick, ctx, data, size);
break;
+ case 0x0C:
+ if (joystick == NULL) {
+ break;
+ }
+ HIDAPI_DriverXboxOne_HandleUnmappedStatePacket(joystick, ctx, data, size);
+ break;
case 0x1E:
/* If the packet starts with this:
0x1E 0x30 0x07 0x10 0x04 0x00