1/*
2 Simple DirectMedia Layer
3 Copyright (C) 1997-2025 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_c.h"
26#include "../SDL_sysjoystick.h"
27#include "SDL_hidapijoystick_c.h"
28#include "SDL_hidapi_rumble.h"
29
30#ifdef SDL_JOYSTICK_HIDAPI_XBOXONE
31
32// Define this if you want verbose logging of the init sequence
33// #define DEBUG_JOYSTICK
34
35// Define this if you want to log all packets from the controller
36// #define DEBUG_XBOX_PROTOCOL
37
38#if defined(SDL_PLATFORM_WIN32) || defined(SDL_PLATFORM_WINGDK)
39#define XBOX_ONE_DRIVER_ACTIVE 1
40#else
41#define XBOX_ONE_DRIVER_ACTIVE 0
42#endif
43
44#define CONTROLLER_IDENTIFY_TIMEOUT_MS 100
45#define CONTROLLER_PREPARE_INPUT_TIMEOUT_MS 50
46
47// Deadzone thresholds
48#define XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE 7849
49#define XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE 8689
50#define XINPUT_GAMEPAD_TRIGGER_THRESHOLD -25058 // Uint8 30 scaled to Sint16 full range
51
52enum
53{
54 SDL_GAMEPAD_BUTTON_XBOX_SHARE_BUTTON = 11
55};
56
57// Power on
58static const Uint8 xbox_init_power_on[] = {
59 0x05, 0x20, 0x00, 0x01, 0x00
60};
61// Enable LED
62static const Uint8 xbox_init_enable_led[] = {
63 0x0A, 0x20, 0x00, 0x03, 0x00, 0x01, 0x14
64};
65// This controller passed security check
66static const Uint8 xbox_init_security_passed[] = {
67 0x06, 0x20, 0x00, 0x02, 0x01, 0x00
68};
69// Some PowerA controllers need to actually start the rumble motors
70static const Uint8 xbox_init_powera_rumble[] = {
71 0x09, 0x00, 0x00, 0x09, 0x00, 0x0F, 0x00, 0x00,
72 0x1D, 0x1D, 0xFF, 0x00, 0x00
73};
74// Setup rumble (not needed for Microsoft controllers, but it doesn't hurt)
75static const Uint8 xbox_init_rumble[] = {
76 0x09, 0x00, 0x00, 0x09, 0x00, 0x0F, 0x00, 0x00,
77 0x00, 0x00, 0xFF, 0x00, 0xEB
78};
79
80/*
81 * This specifies the selection of init packets that a gamepad
82 * will be sent on init *and* the order in which they will be
83 * sent. The correct sequence number will be added when the
84 * packet is going to be sent.
85 */
86typedef struct
87{
88 Uint16 vendor_id;
89 Uint16 product_id;
90 const Uint8 *data;
91 int size;
92} SDL_DriverXboxOne_InitPacket;
93
94static const SDL_DriverXboxOne_InitPacket xboxone_init_packets[] = {
95 { 0x0000, 0x0000, xbox_init_power_on, sizeof(xbox_init_power_on) },
96 { 0x0000, 0x0000, xbox_init_enable_led, sizeof(xbox_init_enable_led) },
97 { 0x0000, 0x0000, xbox_init_security_passed, sizeof(xbox_init_security_passed) },
98 { 0x24c6, 0x541a, xbox_init_powera_rumble, sizeof(xbox_init_powera_rumble) },
99 { 0x24c6, 0x542a, xbox_init_powera_rumble, sizeof(xbox_init_powera_rumble) },
100 { 0x24c6, 0x543a, xbox_init_powera_rumble, sizeof(xbox_init_powera_rumble) },
101 { 0x0000, 0x0000, xbox_init_rumble, sizeof(xbox_init_rumble) },
102};
103
104typedef enum
105{
106 XBOX_ONE_INIT_STATE_ANNOUNCED,
107 XBOX_ONE_INIT_STATE_IDENTIFYING,
108 XBOX_ONE_INIT_STATE_STARTUP,
109 XBOX_ONE_INIT_STATE_PREPARE_INPUT,
110 XBOX_ONE_INIT_STATE_COMPLETE,
111} SDL_XboxOneInitState;
112
113typedef enum
114{
115 XBOX_ONE_RUMBLE_STATE_IDLE,
116 XBOX_ONE_RUMBLE_STATE_QUEUED,
117 XBOX_ONE_RUMBLE_STATE_BUSY
118} SDL_XboxOneRumbleState;
119
120typedef struct
121{
122 SDL_HIDAPI_Device *device;
123 Uint16 vendor_id;
124 Uint16 product_id;
125 SDL_XboxOneInitState init_state;
126 Uint64 start_time;
127 Uint8 sequence;
128 Uint64 send_time;
129 bool has_guide_packet;
130 bool has_color_led;
131 bool has_paddles;
132 bool has_unmapped_state;
133 bool has_trigger_rumble;
134 bool has_share_button;
135 Uint8 last_paddle_state;
136 Uint8 low_frequency_rumble;
137 Uint8 high_frequency_rumble;
138 Uint8 left_trigger_rumble;
139 Uint8 right_trigger_rumble;
140 SDL_XboxOneRumbleState rumble_state;
141 Uint64 rumble_time;
142 bool rumble_pending;
143 Uint8 last_state[USB_PACKET_LENGTH];
144 Uint8 *chunk_buffer;
145 Uint32 chunk_length;
146} SDL_DriverXboxOne_Context;
147
148static bool ControllerHasColorLED(Uint16 vendor_id, Uint16 product_id)
149{
150 return vendor_id == USB_VENDOR_MICROSOFT && product_id == USB_PRODUCT_XBOX_ONE_ELITE_SERIES_2;
151}
152
153static bool ControllerHasPaddles(Uint16 vendor_id, Uint16 product_id)
154{
155 return SDL_IsJoystickXboxOneElite(vendor_id, product_id);
156}
157
158static bool ControllerHasTriggerRumble(Uint16 vendor_id, Uint16 product_id)
159{
160 // All the Microsoft Xbox One controllers have trigger rumble
161 if (vendor_id == USB_VENDOR_MICROSOFT) {
162 return true;
163 }
164
165 /* It turns out other controllers a mixed bag as to whether they support
166 trigger rumble or not, and when they do it's often a buzz rather than
167 the vibration of the Microsoft trigger rumble, so for now just pretend
168 that it is not available.
169 */
170 return false;
171}
172
173static bool ControllerHasShareButton(Uint16 vendor_id, Uint16 product_id)
174{
175 return SDL_IsJoystickXboxSeriesX(vendor_id, product_id);
176}
177
178static int GetHomeLEDBrightness(const char *hint)
179{
180 const int MAX_VALUE = 50;
181 int value = 20;
182
183 if (hint && *hint) {
184 if (SDL_strchr(hint, '.') != NULL) {
185 value = (int)(MAX_VALUE * SDL_atof(hint));
186 } else if (!SDL_GetStringBoolean(hint, true)) {
187 value = 0;
188 }
189 }
190 return value;
191}
192
193static void SetHomeLED(SDL_DriverXboxOne_Context *ctx, int value)
194{
195 Uint8 led_packet[] = { 0x0A, 0x20, 0x00, 0x03, 0x00, 0x00, 0x00 };
196
197 if (value > 0) {
198 led_packet[5] = 0x01;
199 led_packet[6] = (Uint8)value;
200 }
201 SDL_HIDAPI_SendRumble(ctx->device, led_packet, sizeof(led_packet));
202}
203
204static void SDLCALL SDL_HomeLEDHintChanged(void *userdata, const char *name, const char *oldValue, const char *hint)
205{
206 SDL_DriverXboxOne_Context *ctx = (SDL_DriverXboxOne_Context *)userdata;
207
208 if (hint && *hint) {
209 SetHomeLED(ctx, GetHomeLEDBrightness(hint));
210 }
211}
212
213static void SetInitState(SDL_DriverXboxOne_Context *ctx, SDL_XboxOneInitState state)
214{
215#ifdef DEBUG_JOYSTICK
216 SDL_Log("Setting init state %d", state);
217#endif
218 ctx->init_state = state;
219}
220
221static Uint8 GetNextPacketSequence(SDL_DriverXboxOne_Context *ctx)
222{
223 ++ctx->sequence;
224 if (!ctx->sequence) {
225 ctx->sequence = 1;
226 }
227 return ctx->sequence;
228}
229
230static bool SendProtocolPacket(SDL_DriverXboxOne_Context *ctx, const Uint8 *data, int size)
231{
232#ifdef DEBUG_XBOX_PROTOCOL
233 HIDAPI_DumpPacket("Xbox One sending packet: size = %d", data, size);
234#endif
235
236 ctx->send_time = SDL_GetTicks();
237
238 if (!SDL_HIDAPI_LockRumble()) {
239 return false;
240 }
241 if (SDL_HIDAPI_SendRumbleAndUnlock(ctx->device, data, size) != size) {
242 return false;
243 }
244 return true;
245}
246
247#if 0
248static bool SendSerialRequest(SDL_DriverXboxOne_Context *ctx)
249{
250 Uint8 packet[] = { 0x1E, 0x20, 0x00, 0x01, 0x04 };
251
252 packet[2] = GetNextPacketSequence(ctx);
253
254 /* Request the serial number
255 * Sending this should be done only after startup is complete.
256 * It will cancel the announce packet if sent before that, and will be
257 * ignored if sent during the startup sequence.
258 */
259 if (!SendProtocolPacket(ctx, packet, sizeof(packet))) {
260 SDL_SetError("Couldn't send serial request packet");
261 return false;
262 }
263 return true;
264}
265#endif
266
267static bool ControllerSendsAnnouncement(Uint16 vendor_id, Uint16 product_id)
268{
269 if (vendor_id == USB_VENDOR_PDP && product_id == 0x0246) {
270 // The PDP Rock Candy (PID 0x0246) doesn't send the announce packet on Linux for some reason
271 return false;
272 }
273 return true;
274}
275
276static bool SendIdentificationRequest(SDL_DriverXboxOne_Context *ctx)
277{
278 // Request identification, sent in response to announce packet
279 Uint8 packet[] = {
280 0x04, 0x20, 0x00, 0x00
281 };
282
283 packet[2] = GetNextPacketSequence(ctx);
284
285 if (!SendProtocolPacket(ctx, packet, sizeof(packet))) {
286 SDL_SetError("Couldn't send identification request packet");
287 return false;
288 }
289 return true;
290}
291
292static bool SendControllerStartup(SDL_DriverXboxOne_Context *ctx)
293{
294 Uint16 vendor_id = ctx->vendor_id;
295 Uint16 product_id = ctx->product_id;
296 Uint8 init_packet[USB_PACKET_LENGTH];
297 size_t i;
298
299 for (i = 0; i < SDL_arraysize(xboxone_init_packets); ++i) {
300 const SDL_DriverXboxOne_InitPacket *packet = &xboxone_init_packets[i];
301
302 if (packet->vendor_id && (vendor_id != packet->vendor_id)) {
303 continue;
304 }
305
306 if (packet->product_id && (product_id != packet->product_id)) {
307 continue;
308 }
309
310 SDL_memcpy(init_packet, packet->data, packet->size);
311 init_packet[2] = GetNextPacketSequence(ctx);
312
313 if (init_packet[0] == 0x0A) {
314 // Get the initial brightness value
315 int brightness = GetHomeLEDBrightness(SDL_GetHint(SDL_HINT_JOYSTICK_HIDAPI_XBOX_ONE_HOME_LED));
316 init_packet[5] = (brightness > 0) ? 0x01 : 0x00;
317 init_packet[6] = (Uint8)brightness;
318 }
319
320 if (!SendProtocolPacket(ctx, init_packet, packet->size)) {
321 SDL_SetError("Couldn't send initialization packet");
322 return false;
323 }
324
325 // Wait to process the rumble packet
326 if (packet->data == xbox_init_powera_rumble) {
327 SDL_Delay(10);
328 }
329 }
330 return true;
331}
332
333static void HIDAPI_DriverXboxOne_RegisterHints(SDL_HintCallback callback, void *userdata)
334{
335 SDL_AddHintCallback(SDL_HINT_JOYSTICK_HIDAPI_XBOX, callback, userdata);
336 SDL_AddHintCallback(SDL_HINT_JOYSTICK_HIDAPI_XBOX_ONE, callback, userdata);
337}
338
339static void HIDAPI_DriverXboxOne_UnregisterHints(SDL_HintCallback callback, void *userdata)
340{
341 SDL_RemoveHintCallback(SDL_HINT_JOYSTICK_HIDAPI_XBOX, callback, userdata);
342 SDL_RemoveHintCallback(SDL_HINT_JOYSTICK_HIDAPI_XBOX_ONE, callback, userdata);
343}
344
345static bool HIDAPI_DriverXboxOne_IsEnabled(void)
346{
347 return SDL_GetHintBoolean(SDL_HINT_JOYSTICK_HIDAPI_XBOX_ONE,
348 SDL_GetHintBoolean(SDL_HINT_JOYSTICK_HIDAPI_XBOX, SDL_GetHintBoolean(SDL_HINT_JOYSTICK_HIDAPI, SDL_HIDAPI_DEFAULT)));
349}
350
351static bool HIDAPI_DriverXboxOne_IsSupportedDevice(SDL_HIDAPI_Device *device, const char *name, SDL_GamepadType type, Uint16 vendor_id, Uint16 product_id, Uint16 version, int interface_number, int interface_class, int interface_subclass, int interface_protocol)
352{
353#if defined(SDL_PLATFORM_MACOS) && defined(SDL_JOYSTICK_MFI)
354 if (!SDL_IsJoystickBluetoothXboxOne(vendor_id, product_id)) {
355 // On macOS we get a shortened version of the real report and
356 // you can't write output reports for wired controllers, so
357 // we'll just use the GCController support instead.
358 return false;
359 }
360#endif
361 return (type == SDL_GAMEPAD_TYPE_XBOXONE);
362}
363
364static bool HIDAPI_DriverXboxOne_InitDevice(SDL_HIDAPI_Device *device)
365{
366 SDL_DriverXboxOne_Context *ctx;
367
368 ctx = (SDL_DriverXboxOne_Context *)SDL_calloc(1, sizeof(*ctx));
369 if (!ctx) {
370 return false;
371 }
372 ctx->device = device;
373
374 device->context = ctx;
375
376 ctx->vendor_id = device->vendor_id;
377 ctx->product_id = device->product_id;
378 ctx->start_time = SDL_GetTicks();
379 ctx->sequence = 0;
380 ctx->has_color_led = ControllerHasColorLED(ctx->vendor_id, ctx->product_id);
381 ctx->has_paddles = ControllerHasPaddles(ctx->vendor_id, ctx->product_id);
382 ctx->has_trigger_rumble = ControllerHasTriggerRumble(ctx->vendor_id, ctx->product_id);
383 ctx->has_share_button = ControllerHasShareButton(ctx->vendor_id, ctx->product_id);
384
385 // Assume that the controller is correctly initialized when we start
386 if (!ControllerSendsAnnouncement(device->vendor_id, device->product_id)) {
387 // Jump into the startup sequence for this controller
388 ctx->init_state = XBOX_ONE_INIT_STATE_STARTUP;
389 } else {
390 ctx->init_state = XBOX_ONE_INIT_STATE_COMPLETE;
391 }
392
393#ifdef DEBUG_JOYSTICK
394 SDL_Log("Controller version: %d (0x%.4x)", device->version, device->version);
395#endif
396
397 device->type = SDL_GAMEPAD_TYPE_XBOXONE;
398
399 return HIDAPI_JoystickConnected(device, NULL);
400}
401
402static int HIDAPI_DriverXboxOne_GetDevicePlayerIndex(SDL_HIDAPI_Device *device, SDL_JoystickID instance_id)
403{
404 return -1;
405}
406
407static void HIDAPI_DriverXboxOne_SetDevicePlayerIndex(SDL_HIDAPI_Device *device, SDL_JoystickID instance_id, int player_index)
408{
409}
410
411static bool HIDAPI_DriverXboxOne_OpenJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick)
412{
413 SDL_DriverXboxOne_Context *ctx = (SDL_DriverXboxOne_Context *)device->context;
414
415 SDL_AssertJoysticksLocked();
416
417 ctx->low_frequency_rumble = 0;
418 ctx->high_frequency_rumble = 0;
419 ctx->left_trigger_rumble = 0;
420 ctx->right_trigger_rumble = 0;
421 ctx->rumble_state = XBOX_ONE_RUMBLE_STATE_IDLE;
422 ctx->rumble_time = 0;
423 ctx->rumble_pending = false;
424 SDL_zeroa(ctx->last_state);
425
426 // Initialize the joystick capabilities
427 joystick->nbuttons = 11;
428 if (ctx->has_share_button) {
429 joystick->nbuttons += 1;
430 }
431 if (ctx->has_paddles) {
432 joystick->nbuttons += 4;
433 }
434 joystick->naxes = SDL_GAMEPAD_AXIS_COUNT;
435 joystick->nhats = 1;
436
437 SDL_AddHintCallback(SDL_HINT_JOYSTICK_HIDAPI_XBOX_ONE_HOME_LED,
438 SDL_HomeLEDHintChanged, ctx);
439 return true;
440}
441
442static void HIDAPI_DriverXboxOne_RumbleSent(void *userdata)
443{
444 SDL_DriverXboxOne_Context *ctx = (SDL_DriverXboxOne_Context *)userdata;
445 ctx->rumble_time = SDL_GetTicks();
446}
447
448static bool HIDAPI_DriverXboxOne_UpdateRumble(SDL_DriverXboxOne_Context *ctx)
449{
450 if (ctx->rumble_state == XBOX_ONE_RUMBLE_STATE_QUEUED) {
451 if (ctx->rumble_time) {
452 ctx->rumble_state = XBOX_ONE_RUMBLE_STATE_BUSY;
453 }
454 }
455
456 if (ctx->rumble_state == XBOX_ONE_RUMBLE_STATE_BUSY) {
457 const int RUMBLE_BUSY_TIME_MS = ctx->device->is_bluetooth ? 50 : 10;
458 if (SDL_GetTicks() >= (ctx->rumble_time + RUMBLE_BUSY_TIME_MS)) {
459 ctx->rumble_time = 0;
460 ctx->rumble_state = XBOX_ONE_RUMBLE_STATE_IDLE;
461 }
462 }
463
464 if (!ctx->rumble_pending) {
465 return true;
466 }
467
468 if (ctx->rumble_state != XBOX_ONE_RUMBLE_STATE_IDLE) {
469 return true;
470 }
471
472 // We're no longer pending, even if we fail to send the rumble below
473 ctx->rumble_pending = false;
474
475 if (!SDL_HIDAPI_LockRumble()) {
476 return false;
477 }
478
479 if (ctx->device->is_bluetooth) {
480 Uint8 rumble_packet[] = { 0x03, 0x0F, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0xEB };
481
482 rumble_packet[2] = ctx->left_trigger_rumble;
483 rumble_packet[3] = ctx->right_trigger_rumble;
484 rumble_packet[4] = ctx->low_frequency_rumble;
485 rumble_packet[5] = ctx->high_frequency_rumble;
486
487 if (SDL_HIDAPI_SendRumbleWithCallbackAndUnlock(ctx->device, rumble_packet, sizeof(rumble_packet), HIDAPI_DriverXboxOne_RumbleSent, ctx) != sizeof(rumble_packet)) {
488 return SDL_SetError("Couldn't send rumble packet");
489 }
490 } else {
491 Uint8 rumble_packet[] = { 0x09, 0x00, 0x00, 0x09, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0xEB };
492
493 rumble_packet[6] = ctx->left_trigger_rumble;
494 rumble_packet[7] = ctx->right_trigger_rumble;
495 rumble_packet[8] = ctx->low_frequency_rumble;
496 rumble_packet[9] = ctx->high_frequency_rumble;
497
498 if (SDL_HIDAPI_SendRumbleWithCallbackAndUnlock(ctx->device, rumble_packet, sizeof(rumble_packet), HIDAPI_DriverXboxOne_RumbleSent, ctx) != sizeof(rumble_packet)) {
499 return SDL_SetError("Couldn't send rumble packet");
500 }
501 }
502
503 ctx->rumble_state = XBOX_ONE_RUMBLE_STATE_QUEUED;
504
505 return true;
506}
507
508static bool HIDAPI_DriverXboxOne_RumbleJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble)
509{
510 SDL_DriverXboxOne_Context *ctx = (SDL_DriverXboxOne_Context *)device->context;
511
512 // Magnitude is 1..100 so scale the 16-bit input here
513 ctx->low_frequency_rumble = (Uint8)(low_frequency_rumble / 655);
514 ctx->high_frequency_rumble = (Uint8)(high_frequency_rumble / 655);
515 ctx->rumble_pending = true;
516
517 return HIDAPI_DriverXboxOne_UpdateRumble(ctx);
518}
519
520static bool HIDAPI_DriverXboxOne_RumbleJoystickTriggers(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, Uint16 left_rumble, Uint16 right_rumble)
521{
522 SDL_DriverXboxOne_Context *ctx = (SDL_DriverXboxOne_Context *)device->context;
523
524 if (!ctx->has_trigger_rumble) {
525 return SDL_Unsupported();
526 }
527
528 // Magnitude is 1..100 so scale the 16-bit input here
529 ctx->left_trigger_rumble = (Uint8)(left_rumble / 655);
530 ctx->right_trigger_rumble = (Uint8)(right_rumble / 655);
531 ctx->rumble_pending = true;
532
533 return HIDAPI_DriverXboxOne_UpdateRumble(ctx);
534}
535
536static Uint32 HIDAPI_DriverXboxOne_GetJoystickCapabilities(SDL_HIDAPI_Device *device, SDL_Joystick *joystick)
537{
538 SDL_DriverXboxOne_Context *ctx = (SDL_DriverXboxOne_Context *)device->context;
539 Uint32 result = 0;
540
541 result |= SDL_JOYSTICK_CAP_RUMBLE;
542 if (ctx->has_trigger_rumble) {
543 result |= SDL_JOYSTICK_CAP_TRIGGER_RUMBLE;
544 }
545
546 if (ctx->has_color_led) {
547 result |= SDL_JOYSTICK_CAP_RGB_LED;
548 }
549
550 return result;
551}
552
553static bool HIDAPI_DriverXboxOne_SetJoystickLED(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, Uint8 red, Uint8 green, Uint8 blue)
554{
555 SDL_DriverXboxOne_Context *ctx = (SDL_DriverXboxOne_Context *)device->context;
556
557 if (ctx->has_color_led) {
558 Uint8 led_packet[] = { 0x0E, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00 };
559
560 led_packet[5] = 0x00; // Whiteness? Sets white intensity when RGB is 0, seems additive
561 led_packet[6] = red;
562 led_packet[7] = green;
563 led_packet[8] = blue;
564
565 if (SDL_HIDAPI_SendRumble(device, led_packet, sizeof(led_packet)) != sizeof(led_packet)) {
566 return SDL_SetError("Couldn't send LED packet");
567 }
568 return true;
569 } else {
570 return SDL_Unsupported();
571 }
572}
573
574static bool HIDAPI_DriverXboxOne_SendJoystickEffect(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, const void *data, int size)
575{
576 return SDL_Unsupported();
577}
578
579static bool HIDAPI_DriverXboxOne_SetJoystickSensorsEnabled(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, bool enabled)
580{
581 return SDL_Unsupported();
582}
583
584/*
585 * The Xbox One Elite controller with 5.13+ firmware sends the unmapped state in a separate packet.
586 * We can use this to send the paddle state when they aren't mapped
587 */
588static void HIDAPI_DriverXboxOne_HandleUnmappedStatePacket(SDL_Joystick *joystick, SDL_DriverXboxOne_Context *ctx, Uint8 *data, int size)
589{
590 Uint8 profile;
591 int paddle_index;
592 int button1_bit;
593 int button2_bit;
594 int button3_bit;
595 int button4_bit;
596 bool paddles_mapped;
597 Uint64 timestamp = SDL_GetTicksNS();
598
599 if (size == 17) {
600 // XBox One Elite Series 2
601 paddle_index = 14;
602 button1_bit = 0x01;
603 button2_bit = 0x02;
604 button3_bit = 0x04;
605 button4_bit = 0x08;
606 profile = data[15];
607
608 if (profile == 0) {
609 paddles_mapped = false;
610 } else if (SDL_memcmp(&data[0], &ctx->last_state[0], 14) == 0) {
611 // We're using a profile, but paddles aren't mapped
612 paddles_mapped = false;
613 } else {
614 // Something is mapped, we can't use the paddles
615 paddles_mapped = true;
616 }
617
618 } else {
619 // Unknown format
620 return;
621 }
622#ifdef DEBUG_XBOX_PROTOCOL
623 SDL_Log(">>> Paddles: %d,%d,%d,%d mapped = %s",
624 (data[paddle_index] & button1_bit) ? 1 : 0,
625 (data[paddle_index] & button2_bit) ? 1 : 0,
626 (data[paddle_index] & button3_bit) ? 1 : 0,
627 (data[paddle_index] & button4_bit) ? 1 : 0,
628 paddles_mapped ? "TRUE" : "FALSE");
629#endif
630
631 if (paddles_mapped) {
632 // Respect that the paddles are being used for other controls and don't pass them on to the app
633 data[paddle_index] = 0;
634 }
635
636 if (ctx->last_paddle_state != data[paddle_index]) {
637 Uint8 nButton = (Uint8)(SDL_GAMEPAD_BUTTON_XBOX_SHARE_BUTTON + ctx->has_share_button); // Next available button
638 SDL_SendJoystickButton(timestamp, joystick, nButton++, ((data[paddle_index] & button1_bit) != 0));
639 SDL_SendJoystickButton(timestamp, joystick, nButton++, ((data[paddle_index] & button2_bit) != 0));
640 SDL_SendJoystickButton(timestamp, joystick, nButton++, ((data[paddle_index] & button3_bit) != 0));
641 SDL_SendJoystickButton(timestamp, joystick, nButton++, ((data[paddle_index] & button4_bit) != 0));
642 ctx->last_paddle_state = data[paddle_index];
643 }
644 ctx->has_unmapped_state = true;
645}
646
647static void HIDAPI_DriverXboxOne_HandleStatePacket(SDL_Joystick *joystick, SDL_DriverXboxOne_Context *ctx, Uint8 *data, int size)
648{
649 Sint16 axis;
650 Uint64 timestamp = SDL_GetTicksNS();
651
652 // Enable paddles on the Xbox Elite controller when connected over USB
653 if (ctx->has_paddles && !ctx->has_unmapped_state && size == 46) {
654 Uint8 packet[] = { 0x4d, 0x00, 0x00, 0x02, 0x07, 0x00 };
655
656#ifdef DEBUG_JOYSTICK
657 SDL_Log("Enabling paddles on XBox Elite 2");
658#endif
659 SDL_HIDAPI_SendRumble(ctx->device, packet, sizeof(packet));
660 }
661
662 if (ctx->last_state[0] != data[0]) {
663 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_START, ((data[0] & 0x04) != 0));
664 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_BACK, ((data[0] & 0x08) != 0));
665 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_SOUTH, ((data[0] & 0x10) != 0));
666 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_EAST, ((data[0] & 0x20) != 0));
667 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_WEST, ((data[0] & 0x40) != 0));
668 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_NORTH, ((data[0] & 0x80) != 0));
669 }
670
671 if (ctx->last_state[1] != data[1]) {
672 Uint8 hat = 0;
673
674 if (data[1] & 0x01) {
675 hat |= SDL_HAT_UP;
676 }
677 if (data[1] & 0x02) {
678 hat |= SDL_HAT_DOWN;
679 }
680 if (data[1] & 0x04) {
681 hat |= SDL_HAT_LEFT;
682 }
683 if (data[1] & 0x08) {
684 hat |= SDL_HAT_RIGHT;
685 }
686 SDL_SendJoystickHat(timestamp, joystick, 0, hat);
687
688 if (ctx->vendor_id == USB_VENDOR_RAZER && ctx->product_id == USB_PRODUCT_RAZER_ATROX) {
689 // The Razer Atrox has the right and left shoulder bits reversed
690 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_LEFT_SHOULDER, ((data[1] & 0x20) != 0));
691 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER, ((data[1] & 0x10) != 0));
692 } else {
693 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_LEFT_SHOULDER, ((data[1] & 0x10) != 0));
694 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER, ((data[1] & 0x20) != 0));
695 }
696 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_LEFT_STICK, ((data[1] & 0x40) != 0));
697 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_RIGHT_STICK, ((data[1] & 0x80) != 0));
698 }
699
700 if (ctx->has_share_button) {
701 /* Xbox Series X firmware version 5.0, report is 32 bytes, share button is in byte 14
702 * Xbox Series X firmware version 5.1, report is 40 bytes, share button is in byte 14
703 * Xbox Series X firmware version 5.5, report is 44 bytes, share button is in byte 18
704 * Victrix Gambit Tournament Controller, report is 46 bytes, share button is in byte 28
705 * ThrustMaster eSwap PRO Controller Xbox, report is 60 bytes, share button is in byte 42
706 */
707 if (size < 44) {
708 if (ctx->last_state[14] != data[14]) {
709 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_XBOX_SHARE_BUTTON, ((data[14] & 0x01) != 0));
710 }
711 } else if (size == 44) {
712 if (ctx->last_state[18] != data[18]) {
713 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_XBOX_SHARE_BUTTON, ((data[18] & 0x01) != 0));
714 }
715 } else if (size == 46) {
716 if (ctx->last_state[28] != data[28]) {
717 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_XBOX_SHARE_BUTTON, ((data[28] & 0x01) != 0));
718 }
719 } else if (size == 60) {
720 if (ctx->last_state[42] != data[42]) {
721 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_XBOX_SHARE_BUTTON, ((data[42] & 0x01) != 0));
722 }
723 }
724 }
725
726 /* Xbox One S report is 14 bytes
727 Xbox One Elite Series 1 report is 29 bytes, paddles in data[28], mode in data[28] & 0x10, both modes have mapped paddles by default
728 Paddle bits:
729 P3: 0x01 (A) P1: 0x02 (B)
730 P4: 0x04 (X) P2: 0x08 (Y)
731 Xbox One Elite Series 2 4.x firmware report is 34 bytes, paddles in data[14], mode in data[15], mode 0 has no mapped paddles by default
732 Paddle bits:
733 P3: 0x04 (A) P1: 0x01 (B)
734 P4: 0x08 (X) P2: 0x02 (Y)
735 Xbox One Elite Series 2 5.x firmware report is 46 bytes, paddles in data[18], mode in data[19], mode 0 has no mapped paddles by default
736 Paddle bits:
737 P3: 0x04 (A) P1: 0x01 (B)
738 P4: 0x08 (X) P2: 0x02 (Y)
739 Xbox One Elite Series 2 5.17+ firmware report is 47 bytes, paddles in data[14], mode in data[20], mode 0 has no mapped paddles by default
740 Paddle bits:
741 P3: 0x04 (A) P1: 0x01 (B)
742 P4: 0x08 (X) P2: 0x02 (Y)
743 */
744 if (ctx->has_paddles && !ctx->has_unmapped_state && (size == 29 || size == 34 || size == 46 || size == 47)) {
745 int paddle_index;
746 int button1_bit;
747 int button2_bit;
748 int button3_bit;
749 int button4_bit;
750 bool paddles_mapped;
751
752 if (size == 29) {
753 // XBox One Elite Series 1
754 paddle_index = 28;
755 button1_bit = 0x02;
756 button2_bit = 0x08;
757 button3_bit = 0x01;
758 button4_bit = 0x04;
759
760 // The mapped controller state is at offset 0, the raw state is at offset 14, compare them to see if the paddles are mapped
761 paddles_mapped = (SDL_memcmp(&data[0], &data[14], 2) != 0);
762
763 } else if (size == 34) {
764 // XBox One Elite Series 2
765 paddle_index = 14;
766 button1_bit = 0x01;
767 button2_bit = 0x02;
768 button3_bit = 0x04;
769 button4_bit = 0x08;
770 paddles_mapped = (data[15] != 0);
771
772 } else if (size == 46) {
773 // XBox One Elite Series 2
774 paddle_index = 18;
775 button1_bit = 0x01;
776 button2_bit = 0x02;
777 button3_bit = 0x04;
778 button4_bit = 0x08;
779 paddles_mapped = (data[19] != 0);
780 } else /* if (size == 47) */ {
781 // XBox One Elite Series 2
782 paddle_index = 14;
783 button1_bit = 0x01;
784 button2_bit = 0x02;
785 button3_bit = 0x04;
786 button4_bit = 0x08;
787 paddles_mapped = (data[20] != 0);
788 }
789#ifdef DEBUG_XBOX_PROTOCOL
790 SDL_Log(">>> Paddles: %d,%d,%d,%d mapped = %s",
791 (data[paddle_index] & button1_bit) ? 1 : 0,
792 (data[paddle_index] & button2_bit) ? 1 : 0,
793 (data[paddle_index] & button3_bit) ? 1 : 0,
794 (data[paddle_index] & button4_bit) ? 1 : 0,
795 paddles_mapped ? "TRUE" : "FALSE");
796#endif
797
798 if (paddles_mapped) {
799 // Respect that the paddles are being used for other controls and don't pass them on to the app
800 data[paddle_index] = 0;
801 }
802
803 if (ctx->last_paddle_state != data[paddle_index]) {
804 Uint8 nButton = (Uint8)(SDL_GAMEPAD_BUTTON_XBOX_SHARE_BUTTON + ctx->has_share_button); // Next available button
805 SDL_SendJoystickButton(timestamp, joystick, nButton++, ((data[paddle_index] & button1_bit) != 0));
806 SDL_SendJoystickButton(timestamp, joystick, nButton++, ((data[paddle_index] & button2_bit) != 0));
807 SDL_SendJoystickButton(timestamp, joystick, nButton++, ((data[paddle_index] & button3_bit) != 0));
808 SDL_SendJoystickButton(timestamp, joystick, nButton++, ((data[paddle_index] & button4_bit) != 0));
809 ctx->last_paddle_state = data[paddle_index];
810 }
811 }
812
813 axis = ((int)SDL_Swap16LE(*(Sint16 *)(&data[2])) * 64) - 32768;
814 if (axis == 32704) {
815 axis = 32767;
816 }
817 if (axis == -32768 && size == 26 && (data[18] & 0x80)) {
818 axis = 32767;
819 }
820 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFT_TRIGGER, axis);
821
822 axis = ((int)SDL_Swap16LE(*(Sint16 *)(&data[4])) * 64) - 32768;
823 if (axis == -32768 && size == 26 && (data[18] & 0x40)) {
824 axis = 32767;
825 }
826 if (axis == 32704) {
827 axis = 32767;
828 }
829 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHT_TRIGGER, axis);
830
831 axis = SDL_Swap16LE(*(Sint16 *)(&data[6]));
832 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTX, axis);
833 axis = SDL_Swap16LE(*(Sint16 *)(&data[8]));
834 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTY, ~axis);
835 axis = SDL_Swap16LE(*(Sint16 *)(&data[10]));
836 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHTX, axis);
837 axis = SDL_Swap16LE(*(Sint16 *)(&data[12]));
838 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHTY, ~axis);
839
840 SDL_memcpy(ctx->last_state, data, SDL_min(size, sizeof(ctx->last_state)));
841
842 // We don't have the unmapped state for this packet
843 ctx->has_unmapped_state = false;
844}
845
846static void HIDAPI_DriverXboxOne_HandleStatusPacket(SDL_DriverXboxOne_Context *ctx, const Uint8 *data, int size)
847{
848 if (ctx->init_state < XBOX_ONE_INIT_STATE_COMPLETE) {
849 SetInitState(ctx, XBOX_ONE_INIT_STATE_COMPLETE);
850 }
851}
852
853static void HIDAPI_DriverXboxOne_HandleModePacket(SDL_Joystick *joystick, SDL_DriverXboxOne_Context *ctx, const Uint8 *data, int size)
854{
855 Uint64 timestamp = SDL_GetTicksNS();
856
857 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_GUIDE, ((data[0] & 0x01) != 0));
858}
859
860/*
861 * Xbox One S with firmware 3.1.1221 uses a 16 byte packet and the GUIDE button in a separate packet
862 */
863static void HIDAPI_DriverXboxOneBluetooth_HandleButtons16(Uint64 timestamp, SDL_Joystick *joystick, SDL_DriverXboxOne_Context *ctx, const Uint8 *data, int size)
864{
865 if (ctx->last_state[14] != data[14]) {
866 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_SOUTH, ((data[14] & 0x01) != 0));
867 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_EAST, ((data[14] & 0x02) != 0));
868 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_WEST, ((data[14] & 0x04) != 0));
869 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_NORTH, ((data[14] & 0x08) != 0));
870 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_LEFT_SHOULDER, ((data[14] & 0x10) != 0));
871 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER, ((data[14] & 0x20) != 0));
872 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_BACK, ((data[14] & 0x40) != 0));
873 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_START, ((data[14] & 0x80) != 0));
874 }
875
876 if (ctx->last_state[15] != data[15]) {
877 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_LEFT_STICK, ((data[15] & 0x01) != 0));
878 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_RIGHT_STICK, ((data[15] & 0x02) != 0));
879 }
880}
881
882/*
883 * Xbox One S with firmware 4.8.1923 uses a 17 byte packet with BACK button in byte 16 and the GUIDE button in a separate packet (on Windows), or in byte 15 (on Linux)
884 * Xbox One S with firmware 5.x uses a 17 byte packet with BACK and GUIDE buttons in byte 15
885 * Xbox One Elite Series 2 with firmware 4.7.1872 uses a 55 byte packet with BACK button in byte 16, paddles starting at byte 33, and the GUIDE button in a separate packet
886 * Xbox One Elite Series 2 with firmware 4.8.1908 uses a 33 byte packet with BACK button in byte 16, paddles starting at byte 17, and the GUIDE button in a separate packet
887 * Xbox One Elite Series 2 with firmware 5.11.3112 uses a 19 byte packet with BACK and GUIDE buttons in byte 15
888 * Xbox Series X with firmware 5.5.2641 uses a 17 byte packet with BACK and GUIDE buttons in byte 15, and SHARE button in byte 17
889 */
890static void HIDAPI_DriverXboxOneBluetooth_HandleButtons(Uint64 timestamp, SDL_Joystick *joystick, SDL_DriverXboxOne_Context *ctx, Uint8 *data, int size)
891{
892 if (ctx->last_state[14] != data[14]) {
893 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_SOUTH, ((data[14] & 0x01) != 0));
894 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_EAST, ((data[14] & 0x02) != 0));
895 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_WEST, ((data[14] & 0x08) != 0));
896 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_NORTH, ((data[14] & 0x10) != 0));
897 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_LEFT_SHOULDER, ((data[14] & 0x40) != 0));
898 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER, ((data[14] & 0x80) != 0));
899 }
900
901 if (ctx->last_state[15] != data[15]) {
902 if (!ctx->has_guide_packet) {
903 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_GUIDE, ((data[15] & 0x10) != 0));
904 }
905 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_START, ((data[15] & 0x08) != 0));
906 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_LEFT_STICK, ((data[15] & 0x20) != 0));
907 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_RIGHT_STICK, ((data[15] & 0x40) != 0));
908 }
909
910 if (ctx->has_share_button) {
911 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_BACK, ((data[15] & 0x04) != 0));
912 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_XBOX_SHARE_BUTTON, ((data[16] & 0x01) != 0));
913 } else {
914 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_BACK, ((data[15] & 0x04) || ((data[16] & 0x01)) != 0));
915 }
916
917 /*
918 Paddle bits:
919 P3: 0x04 (A) P1: 0x01 (B)
920 P4: 0x08 (X) P2: 0x02 (Y)
921 */
922 if (ctx->has_paddles && (size == 20 || size == 39 || size == 55)) {
923 int paddle_index;
924 int button1_bit;
925 int button2_bit;
926 int button3_bit;
927 int button4_bit;
928 bool paddles_mapped;
929
930 if (size == 55) {
931 // Initial firmware for the Xbox Elite Series 2 controller
932 paddle_index = 33;
933 button1_bit = 0x01;
934 button2_bit = 0x02;
935 button3_bit = 0x04;
936 button4_bit = 0x08;
937 paddles_mapped = (data[35] != 0);
938 } else if (size == 39) {
939 // Updated firmware for the Xbox Elite Series 2 controller
940 paddle_index = 17;
941 button1_bit = 0x01;
942 button2_bit = 0x02;
943 button3_bit = 0x04;
944 button4_bit = 0x08;
945 paddles_mapped = (data[19] != 0);
946 } else /* if (size == 20) */ {
947 // Updated firmware for the Xbox Elite Series 2 controller (5.13+)
948 paddle_index = 19;
949 button1_bit = 0x01;
950 button2_bit = 0x02;
951 button3_bit = 0x04;
952 button4_bit = 0x08;
953 paddles_mapped = (data[17] != 0);
954 }
955
956#ifdef DEBUG_XBOX_PROTOCOL
957 SDL_Log(">>> Paddles: %d,%d,%d,%d mapped = %s",
958 (data[paddle_index] & button1_bit) ? 1 : 0,
959 (data[paddle_index] & button2_bit) ? 1 : 0,
960 (data[paddle_index] & button3_bit) ? 1 : 0,
961 (data[paddle_index] & button4_bit) ? 1 : 0,
962 paddles_mapped ? "TRUE" : "FALSE");
963#endif
964
965 if (paddles_mapped) {
966 // Respect that the paddles are being used for other controls and don't pass them on to the app
967 data[paddle_index] = 0;
968 }
969
970 if (ctx->last_paddle_state != data[paddle_index]) {
971 Uint8 nButton = SDL_GAMEPAD_BUTTON_XBOX_SHARE_BUTTON; // Next available button
972 SDL_SendJoystickButton(timestamp, joystick, nButton++, ((data[paddle_index] & button1_bit) != 0));
973 SDL_SendJoystickButton(timestamp, joystick, nButton++, ((data[paddle_index] & button2_bit) != 0));
974 SDL_SendJoystickButton(timestamp, joystick, nButton++, ((data[paddle_index] & button3_bit) != 0));
975 SDL_SendJoystickButton(timestamp, joystick, nButton++, ((data[paddle_index] & button4_bit) != 0));
976 ctx->last_paddle_state = data[paddle_index];
977 }
978 }
979}
980
981static void HIDAPI_DriverXboxOneBluetooth_HandleStatePacket(SDL_Joystick *joystick, SDL_DriverXboxOne_Context *ctx, Uint8 *data, int size)
982{
983 Sint16 axis;
984 Uint64 timestamp = SDL_GetTicksNS();
985
986 if (size == 16) {
987 // Original Xbox One S, with separate report for guide button
988 HIDAPI_DriverXboxOneBluetooth_HandleButtons16(timestamp, joystick, ctx, data, size);
989 } else if (size > 16) {
990 HIDAPI_DriverXboxOneBluetooth_HandleButtons(timestamp, joystick, ctx, data, size);
991 } else {
992#ifdef DEBUG_XBOX_PROTOCOL
993 SDL_Log("Unknown Bluetooth state packet format");
994#endif
995 return;
996 }
997
998 if (ctx->last_state[13] != data[13]) {
999 Uint8 hat;
1000
1001 switch (data[13]) {
1002 case 1:
1003 hat = SDL_HAT_UP;
1004 break;
1005 case 2:
1006 hat = SDL_HAT_RIGHTUP;
1007 break;
1008 case 3:
1009 hat = SDL_HAT_RIGHT;
1010 break;
1011 case 4:
1012 hat = SDL_HAT_RIGHTDOWN;
1013 break;
1014 case 5:
1015 hat = SDL_HAT_DOWN;
1016 break;
1017 case 6:
1018 hat = SDL_HAT_LEFTDOWN;
1019 break;
1020 case 7:
1021 hat = SDL_HAT_LEFT;
1022 break;
1023 case 8:
1024 hat = SDL_HAT_LEFTUP;
1025 break;
1026 default:
1027 hat = SDL_HAT_CENTERED;
1028 break;
1029 }
1030 SDL_SendJoystickHat(timestamp, joystick, 0, hat);
1031 }
1032
1033 axis = ((int)SDL_Swap16LE(*(Sint16 *)(&data[9])) * 64) - 32768;
1034 if (axis == 32704) {
1035 axis = 32767;
1036 }
1037 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFT_TRIGGER, axis);
1038
1039 axis = ((int)SDL_Swap16LE(*(Sint16 *)(&data[11])) * 64) - 32768;
1040 if (axis == 32704) {
1041 axis = 32767;
1042 }
1043 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHT_TRIGGER, axis);
1044
1045 axis = (int)SDL_Swap16LE(*(Uint16 *)(&data[1])) - 0x8000;
1046 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTX, axis);
1047 axis = (int)SDL_Swap16LE(*(Uint16 *)(&data[3])) - 0x8000;
1048 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTY, axis);
1049 axis = (int)SDL_Swap16LE(*(Uint16 *)(&data[5])) - 0x8000;
1050 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHTX, axis);
1051 axis = (int)SDL_Swap16LE(*(Uint16 *)(&data[7])) - 0x8000;
1052 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHTY, axis);
1053
1054 SDL_memcpy(ctx->last_state, data, SDL_min(size, sizeof(ctx->last_state)));
1055}
1056
1057static void HIDAPI_DriverXboxOneBluetooth_HandleGuidePacket(SDL_Joystick *joystick, SDL_DriverXboxOne_Context *ctx, const Uint8 *data, int size)
1058{
1059 Uint64 timestamp = SDL_GetTicksNS();
1060
1061 ctx->has_guide_packet = true;
1062 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_GUIDE, ((data[1] & 0x01) != 0));
1063}
1064
1065static void HIDAPI_DriverXboxOneBluetooth_HandleBatteryPacket(SDL_Joystick *joystick, SDL_DriverXboxOne_Context *ctx, const Uint8 *data, int size)
1066{
1067 Uint8 flags = data[1];
1068 bool on_usb = (((flags & 0x0C) >> 2) == 0);
1069 SDL_PowerState state;
1070 int percent = 0;
1071
1072 // Mapped percentage value from:
1073 // https://learn.microsoft.com/en-us/gaming/gdk/_content/gc/reference/input/gameinput/interfaces/igameinputdevice/methods/igameinputdevice_getbatterystate
1074 switch (flags & 0x03) {
1075 case 0:
1076 percent = 10;
1077 break;
1078 case 1:
1079 percent = 40;
1080 break;
1081 case 2:
1082 percent = 70;
1083 break;
1084 case 3:
1085 percent = 100;
1086 break;
1087 }
1088 if (on_usb) {
1089 state = SDL_POWERSTATE_CHARGING;
1090 } else {
1091 state = SDL_POWERSTATE_ON_BATTERY;
1092 }
1093 SDL_SendJoystickPowerInfo(joystick, state, percent);
1094}
1095
1096static void HIDAPI_DriverXboxOne_HandleSerialIDPacket(SDL_DriverXboxOne_Context *ctx, const Uint8 *data, int size)
1097{
1098 char serial[29];
1099 int i;
1100
1101 for (i = 0; i < 14; ++i) {
1102 SDL_uitoa(data[2 + i], &serial[i * 2], 16);
1103 }
1104 serial[i * 2] = '\0';
1105
1106#ifdef DEBUG_JOYSTICK
1107 SDL_Log("Setting serial number to %s", serial);
1108#endif
1109 HIDAPI_SetDeviceSerial(ctx->device, serial);
1110}
1111
1112static bool HIDAPI_DriverXboxOne_UpdateInitState(SDL_DriverXboxOne_Context *ctx)
1113{
1114 SDL_XboxOneInitState prev_state;
1115 do {
1116 prev_state = ctx->init_state;
1117
1118 switch (ctx->init_state) {
1119 case XBOX_ONE_INIT_STATE_ANNOUNCED:
1120 if (XBOX_ONE_DRIVER_ACTIVE) {
1121 // The driver is taking care of identification
1122 SetInitState(ctx, XBOX_ONE_INIT_STATE_COMPLETE);
1123 } else {
1124 SendIdentificationRequest(ctx);
1125 SetInitState(ctx, XBOX_ONE_INIT_STATE_IDENTIFYING);
1126 }
1127 break;
1128 case XBOX_ONE_INIT_STATE_IDENTIFYING:
1129 if (SDL_GetTicks() >= (ctx->send_time + CONTROLLER_IDENTIFY_TIMEOUT_MS)) {
1130 // We haven't heard anything, let's move on
1131#ifdef DEBUG_JOYSTICK
1132 SDL_Log("Identification request timed out after %llu ms", (SDL_GetTicks() - ctx->send_time));
1133#endif
1134 SetInitState(ctx, XBOX_ONE_INIT_STATE_STARTUP);
1135 }
1136 break;
1137 case XBOX_ONE_INIT_STATE_STARTUP:
1138 if (XBOX_ONE_DRIVER_ACTIVE) {
1139 // The driver is taking care of startup
1140 SetInitState(ctx, XBOX_ONE_INIT_STATE_COMPLETE);
1141 } else {
1142 SendControllerStartup(ctx);
1143 SetInitState(ctx, XBOX_ONE_INIT_STATE_PREPARE_INPUT);
1144 }
1145 break;
1146 case XBOX_ONE_INIT_STATE_PREPARE_INPUT:
1147 if (SDL_GetTicks() >= (ctx->send_time + CONTROLLER_PREPARE_INPUT_TIMEOUT_MS)) {
1148#ifdef DEBUG_JOYSTICK
1149 SDL_Log("Prepare input complete after %llu ms", (SDL_GetTicks() - ctx->send_time));
1150#endif
1151 SetInitState(ctx, XBOX_ONE_INIT_STATE_COMPLETE);
1152 }
1153 break;
1154 case XBOX_ONE_INIT_STATE_COMPLETE:
1155 break;
1156 }
1157
1158 } while (ctx->init_state != prev_state);
1159
1160 return true;
1161}
1162
1163/* GIP protocol handling adapted under the Zlib license with permission from @medusalix:
1164 * https://github.com/medusalix/xone/blob/master/bus/protocol.h
1165 * https://github.com/medusalix/xone/blob/master/bus/protocol.c
1166 */
1167#define GIP_HEADER_MIN_LENGTH 3
1168
1169// Internal commands
1170#define GIP_CMD_ACKNOWLEDGE 0x01
1171#define GIP_CMD_ANNOUNCE 0x02
1172#define GIP_CMD_STATUS 0x03
1173#define GIP_CMD_IDENTIFY 0x04
1174#define GIP_CMD_POWER 0x05
1175#define GIP_CMD_AUTHENTICATE 0x06
1176#define GIP_CMD_VIRTUAL_KEY 0x07
1177#define GIP_CMD_AUDIO_CONTROL 0x08
1178#define GIP_CMD_LED 0x0A
1179#define GIP_CMD_HID_REPORT 0x0B
1180#define GIP_CMD_FIRMWARE 0x0C
1181#define GIP_CMD_SERIAL_NUMBER 0x1E
1182#define GIP_CMD_AUDIO_SAMPLES 0x60
1183
1184// External commands
1185#define GIP_CMD_RUMBLE 0x09
1186#define GIP_CMD_UNMAPPED_STATE 0x0C
1187#define GIP_CMD_INPUT 0x20
1188
1189// Header option flags
1190#define GIP_OPT_ACKNOWLEDGE 0x10
1191#define GIP_OPT_INTERNAL 0x20
1192#define GIP_OPT_CHUNK_START 0x40
1193#define GIP_OPT_CHUNK 0x80
1194
1195#pragma pack(push, 1)
1196
1197struct gip_header {
1198 Uint8 command;
1199 Uint8 options;
1200 Uint8 sequence;
1201 Uint32 packet_length;
1202 Uint32 chunk_offset;
1203};
1204
1205struct gip_pkt_acknowledge {
1206 Uint8 unknown;
1207 Uint8 command;
1208 Uint8 options;
1209 Uint16 length;
1210 Uint8 padding[2];
1211 Uint16 remaining;
1212};
1213
1214#pragma pack(pop)
1215
1216static int EncodeVariableInt(Uint8 *buf, Uint32 val)
1217{
1218 int i;
1219
1220 for (i = 0; i < sizeof(val); i++) {
1221 buf[i] = (Uint8)val;
1222 if (val > 0x7F) {
1223 buf[i] |= 0x80;
1224 }
1225
1226 val >>= 7;
1227 if (!val) {
1228 break;
1229 }
1230 }
1231 return i + 1;
1232}
1233
1234static int DecodeVariableInt(const Uint8 *data, int len, void *out)
1235{
1236 int i;
1237 Uint32 val = 0;
1238
1239 for (i = 0; i < sizeof(val) && i < len; i++) {
1240 val |= (data[i] & 0x7F) << (i * 7);
1241
1242 if (!(data[i] & 0x80)) {
1243 break;
1244 }
1245 }
1246 SDL_memcpy(out, &val, sizeof(val));
1247 return i + 1;
1248}
1249
1250static int HIDAPI_GIP_GetActualHeaderLength(struct gip_header *hdr)
1251{
1252 Uint32 pkt_len = hdr->packet_length;
1253 Uint32 chunk_offset = hdr->chunk_offset;
1254 int len = GIP_HEADER_MIN_LENGTH;
1255
1256 do {
1257 len++;
1258 pkt_len >>= 7;
1259 } while (pkt_len);
1260
1261 if (hdr->options & GIP_OPT_CHUNK) {
1262 while (chunk_offset) {
1263 len++;
1264 chunk_offset >>= 7;
1265 }
1266 }
1267
1268 return len;
1269}
1270
1271static int HIDAPI_GIP_GetHeaderLength(struct gip_header *hdr)
1272{
1273 int len = HIDAPI_GIP_GetActualHeaderLength(hdr);
1274
1275 // Header length must be even
1276 return len + (len % 2);
1277}
1278
1279static void HIDAPI_GIP_EncodeHeader(struct gip_header *hdr, Uint8 *buf)
1280{
1281 int hdr_len = 0;
1282
1283 buf[hdr_len++] = hdr->command;
1284 buf[hdr_len++] = hdr->options;
1285 buf[hdr_len++] = hdr->sequence;
1286
1287 hdr_len += EncodeVariableInt(buf + hdr_len, hdr->packet_length);
1288
1289 // Header length must be even
1290 if (HIDAPI_GIP_GetActualHeaderLength(hdr) % 2) {
1291 buf[hdr_len - 1] |= 0x80;
1292 buf[hdr_len++] = 0;
1293 }
1294
1295 if (hdr->options & GIP_OPT_CHUNK) {
1296 EncodeVariableInt(buf + hdr_len, hdr->chunk_offset);
1297 }
1298}
1299
1300static int HIDAPI_GIP_DecodeHeader(struct gip_header *hdr, const Uint8 *data, int len)
1301{
1302 int hdr_len = 0;
1303
1304 hdr->command = data[hdr_len++];
1305 hdr->options = data[hdr_len++];
1306 hdr->sequence = data[hdr_len++];
1307 hdr->packet_length = 0;
1308 hdr->chunk_offset = 0;
1309
1310 hdr_len += DecodeVariableInt(data + hdr_len, len - hdr_len, &hdr->packet_length);
1311
1312 if (hdr->options & GIP_OPT_CHUNK) {
1313 hdr_len += DecodeVariableInt(data + hdr_len, len - hdr_len, &hdr->chunk_offset);
1314 }
1315 return hdr_len;
1316}
1317
1318static bool HIDAPI_GIP_SendPacket(SDL_DriverXboxOne_Context *ctx, struct gip_header *hdr, const void *data)
1319{
1320 Uint8 packet[USB_PACKET_LENGTH];
1321 int hdr_len, size;
1322
1323 hdr_len = HIDAPI_GIP_GetHeaderLength(hdr);
1324 size = (hdr_len + hdr->packet_length);
1325 if (size > sizeof(packet)) {
1326 SDL_SetError("Couldn't send GIP packet, size (%d) too large", size);
1327 return false;
1328 }
1329
1330 if (!hdr->sequence) {
1331 hdr->sequence = GetNextPacketSequence(ctx);
1332 }
1333
1334 HIDAPI_GIP_EncodeHeader(hdr, packet);
1335 if (data) {
1336 SDL_memcpy(&packet[hdr_len], data, hdr->packet_length);
1337 }
1338
1339 if (!SendProtocolPacket(ctx, packet, size)) {
1340 SDL_SetError("Couldn't send protocol packet");
1341 return false;
1342 }
1343 return true;
1344}
1345
1346static bool HIDAPI_GIP_AcknowledgePacket(SDL_DriverXboxOne_Context *ctx, struct gip_header *ack)
1347{
1348 if (XBOX_ONE_DRIVER_ACTIVE) {
1349 // The driver is taking care of acks
1350 return true;
1351 } else {
1352 struct gip_header hdr;
1353 struct gip_pkt_acknowledge pkt;
1354
1355 SDL_zero(hdr);
1356 hdr.command = GIP_CMD_ACKNOWLEDGE;
1357 hdr.options = GIP_OPT_INTERNAL;
1358 hdr.sequence = ack->sequence;
1359 hdr.packet_length = sizeof(pkt);
1360
1361 SDL_zero(pkt);
1362 pkt.command = ack->command;
1363 pkt.options = GIP_OPT_INTERNAL;
1364 pkt.length = SDL_Swap16LE((Uint16)(ack->chunk_offset + ack->packet_length));
1365
1366 if ((ack->options & GIP_OPT_CHUNK) && ctx->chunk_buffer) {
1367 pkt.remaining = SDL_Swap16LE((Uint16)(ctx->chunk_length - pkt.length));
1368 }
1369
1370 return HIDAPI_GIP_SendPacket(ctx, &hdr, &pkt);
1371 }
1372}
1373
1374static bool HIDAPI_GIP_DispatchPacket(SDL_Joystick *joystick, SDL_DriverXboxOne_Context *ctx, struct gip_header *hdr, Uint8 *data, Uint32 size)
1375{
1376 if ((hdr->options & 0x0F) != 0) {
1377 // This is a packet for a device plugged into the controller, skip it
1378 return true;
1379 }
1380
1381 if (hdr->options & GIP_OPT_INTERNAL) {
1382 switch (hdr->command) {
1383 case GIP_CMD_ACKNOWLEDGE:
1384 // Ignore this packet
1385 break;
1386 case GIP_CMD_ANNOUNCE:
1387 // Controller is connected and waiting for initialization
1388 /* The data bytes are:
1389 0x02 0x20 NN 0x1c, where NN is the packet sequence
1390 then 6 bytes of wireless MAC address
1391 then 2 bytes padding
1392 then 16-bit VID
1393 then 16-bit PID
1394 then 16-bit firmware version quartet AA.BB.CC.DD
1395 e.g. 0x05 0x00 0x05 0x00 0x51 0x0a 0x00 0x00
1396 is firmware version 5.5.2641.0, and product version 0x0505 = 1285
1397 then 8 bytes of unknown data
1398 */
1399#ifdef DEBUG_JOYSTICK
1400 SDL_Log("Controller announce after %llu ms", (SDL_GetTicks() - ctx->start_time));
1401#endif
1402 SetInitState(ctx, XBOX_ONE_INIT_STATE_ANNOUNCED);
1403 break;
1404 case GIP_CMD_STATUS:
1405 // Controller status update
1406 HIDAPI_DriverXboxOne_HandleStatusPacket(ctx, data, size);
1407 break;
1408 case GIP_CMD_IDENTIFY:
1409#ifdef DEBUG_JOYSTICK
1410 SDL_Log("Identification request completed after %llu ms", (SDL_GetTicks() - ctx->send_time));
1411#endif
1412#ifdef DEBUG_XBOX_PROTOCOL
1413 HIDAPI_DumpPacket("Xbox One identification data: size = %d", data, size);
1414#endif
1415 SetInitState(ctx, XBOX_ONE_INIT_STATE_STARTUP);
1416 break;
1417 case GIP_CMD_POWER:
1418 // Ignore this packet
1419 break;
1420 case GIP_CMD_AUTHENTICATE:
1421 // Ignore this packet
1422 break;
1423 case GIP_CMD_VIRTUAL_KEY:
1424 if (!joystick) {
1425 break;
1426 }
1427 HIDAPI_DriverXboxOne_HandleModePacket(joystick, ctx, data, size);
1428 break;
1429 case GIP_CMD_SERIAL_NUMBER:
1430 /* If the packet starts with this:
1431 0x1E 0x30 0x00 0x10 0x04 0x00
1432 then the next 14 bytes are the controller serial number
1433 e.g. 0x30 0x39 0x37 0x31 0x32 0x33 0x33 0x32 0x33 0x35 0x34 0x30 0x33 0x36
1434 is serial number "3039373132333332333534303336"
1435
1436 The controller sends that in response to this request:
1437 0x1E 0x20 0x00 0x01 0x04
1438 */
1439 HIDAPI_DriverXboxOne_HandleSerialIDPacket(ctx, data, size);
1440 break;
1441 default:
1442#ifdef DEBUG_JOYSTICK
1443 SDL_Log("Unknown Xbox One packet: 0x%.2x", hdr->command);
1444#endif
1445 break;
1446 }
1447 } else {
1448 switch (hdr->command) {
1449 case GIP_CMD_INPUT:
1450 if (ctx->init_state < XBOX_ONE_INIT_STATE_COMPLETE) {
1451 SetInitState(ctx, XBOX_ONE_INIT_STATE_COMPLETE);
1452
1453 // Ignore the first input, it may be spurious
1454#ifdef DEBUG_JOYSTICK
1455 SDL_Log("Controller ignoring spurious input");
1456#endif
1457 break;
1458 }
1459 if (!joystick) {
1460 break;
1461 }
1462 HIDAPI_DriverXboxOne_HandleStatePacket(joystick, ctx, data, size);
1463 break;
1464 case GIP_CMD_UNMAPPED_STATE:
1465 if (!joystick) {
1466 break;
1467 }
1468 HIDAPI_DriverXboxOne_HandleUnmappedStatePacket(joystick, ctx, data, size);
1469 break;
1470 default:
1471#ifdef DEBUG_JOYSTICK
1472 SDL_Log("Unknown Xbox One packet: 0x%.2x", hdr->command);
1473#endif
1474 break;
1475 }
1476 }
1477 return true;
1478}
1479
1480static void HIDAPI_GIP_DestroyChunkBuffer(SDL_DriverXboxOne_Context *ctx)
1481{
1482 if (ctx->chunk_buffer) {
1483 SDL_free(ctx->chunk_buffer);
1484 ctx->chunk_buffer = NULL;
1485 ctx->chunk_length = 0;
1486 }
1487}
1488
1489static bool HIDAPI_GIP_CreateChunkBuffer(SDL_DriverXboxOne_Context *ctx, Uint32 size)
1490{
1491 HIDAPI_GIP_DestroyChunkBuffer(ctx);
1492
1493 ctx->chunk_buffer = (Uint8 *)SDL_malloc(size);
1494 if (ctx->chunk_buffer) {
1495 ctx->chunk_length = size;
1496 return true;
1497 } else {
1498 return false;
1499 }
1500}
1501
1502static bool HIDAPI_GIP_ProcessPacketChunked(SDL_Joystick *joystick, SDL_DriverXboxOne_Context *ctx, struct gip_header *hdr, Uint8 *data)
1503{
1504 bool result;
1505
1506 if (!ctx->chunk_buffer) {
1507 return false;
1508 }
1509
1510 if ((hdr->chunk_offset + hdr->packet_length) > ctx->chunk_length) {
1511 return false;
1512 }
1513
1514 if (hdr->packet_length) {
1515 SDL_memcpy(ctx->chunk_buffer + hdr->chunk_offset, data, hdr->packet_length);
1516 return true;
1517 }
1518
1519 result = HIDAPI_GIP_DispatchPacket(joystick, ctx, hdr, ctx->chunk_buffer, ctx->chunk_length);
1520
1521 HIDAPI_GIP_DestroyChunkBuffer(ctx);
1522
1523 return result;
1524}
1525
1526static bool HIDAPI_GIP_ProcessPacket(SDL_Joystick *joystick, SDL_DriverXboxOne_Context *ctx, struct gip_header *hdr, Uint8 *data)
1527{
1528 if (hdr->options & GIP_OPT_CHUNK_START) {
1529 if (!HIDAPI_GIP_CreateChunkBuffer(ctx, hdr->chunk_offset)) {
1530 return false;
1531 }
1532 ctx->chunk_length = hdr->chunk_offset;
1533
1534 hdr->chunk_offset = 0;
1535 }
1536
1537 if (hdr->options & GIP_OPT_ACKNOWLEDGE) {
1538 if (!HIDAPI_GIP_AcknowledgePacket(ctx, hdr)) {
1539 return false;
1540 }
1541 }
1542
1543 if (hdr->options & GIP_OPT_CHUNK) {
1544 return HIDAPI_GIP_ProcessPacketChunked(joystick, ctx, hdr, data);
1545 } else {
1546 return HIDAPI_GIP_DispatchPacket(joystick, ctx, hdr, data, hdr->packet_length);
1547 }
1548}
1549
1550static bool HIDAPI_GIP_ProcessData(SDL_Joystick *joystick, SDL_DriverXboxOne_Context *ctx, Uint8 *data, int size)
1551{
1552 struct gip_header hdr;
1553 int hdr_len;
1554
1555 while (size > GIP_HEADER_MIN_LENGTH) {
1556 hdr_len = HIDAPI_GIP_DecodeHeader(&hdr, data, size);
1557 if ((hdr_len + hdr.packet_length) > (Uint32)size) {
1558 // On macOS we get a shortened version of the real report
1559 hdr.packet_length = (Uint32)(size - hdr_len);
1560 }
1561
1562 if (!HIDAPI_GIP_ProcessPacket(joystick, ctx, &hdr, data + hdr_len)) {
1563 return false;
1564 }
1565
1566 data += hdr_len + hdr.packet_length;
1567 size -= hdr_len + hdr.packet_length;
1568 }
1569 return true;
1570}
1571
1572static bool HIDAPI_DriverXboxOne_UpdateDevice(SDL_HIDAPI_Device *device)
1573{
1574 SDL_DriverXboxOne_Context *ctx = (SDL_DriverXboxOne_Context *)device->context;
1575 SDL_Joystick *joystick = NULL;
1576 Uint8 data[USB_PACKET_LENGTH];
1577 int size;
1578
1579 if (device->num_joysticks > 0) {
1580 joystick = SDL_GetJoystickFromID(device->joysticks[0]);
1581 } else {
1582 return false;
1583 }
1584
1585 while ((size = SDL_hid_read_timeout(device->dev, data, sizeof(data), 0)) > 0) {
1586#ifdef DEBUG_XBOX_PROTOCOL
1587 HIDAPI_DumpPacket("Xbox One packet: size = %d", data, size);
1588#endif
1589 if (device->is_bluetooth) {
1590 switch (data[0]) {
1591 case 0x01:
1592 if (!joystick) {
1593 break;
1594 }
1595 if (size >= 16) {
1596 HIDAPI_DriverXboxOneBluetooth_HandleStatePacket(joystick, ctx, data, size);
1597 } else {
1598#ifdef DEBUG_JOYSTICK
1599 SDL_Log("Unknown Xbox One Bluetooth packet size: %d", size);
1600#endif
1601 }
1602 break;
1603 case 0x02:
1604 if (!joystick) {
1605 break;
1606 }
1607 HIDAPI_DriverXboxOneBluetooth_HandleGuidePacket(joystick, ctx, data, size);
1608 break;
1609 case 0x04:
1610 if (!joystick) {
1611 break;
1612 }
1613 HIDAPI_DriverXboxOneBluetooth_HandleBatteryPacket(joystick, ctx, data, size);
1614 break;
1615 default:
1616#ifdef DEBUG_JOYSTICK
1617 SDL_Log("Unknown Xbox One packet: 0x%.2x", data[0]);
1618#endif
1619 break;
1620 }
1621 } else {
1622 HIDAPI_GIP_ProcessData(joystick, ctx, data, size);
1623 }
1624 }
1625
1626 HIDAPI_DriverXboxOne_UpdateInitState(ctx);
1627 HIDAPI_DriverXboxOne_UpdateRumble(ctx);
1628
1629 if (size < 0) {
1630 // Read error, device is disconnected
1631 HIDAPI_JoystickDisconnected(device, device->joysticks[0]);
1632 }
1633 return (size >= 0);
1634}
1635
1636static void HIDAPI_DriverXboxOne_CloseJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick)
1637{
1638 SDL_DriverXboxOne_Context *ctx = (SDL_DriverXboxOne_Context *)device->context;
1639
1640 SDL_RemoveHintCallback(SDL_HINT_JOYSTICK_HIDAPI_XBOX_ONE_HOME_LED,
1641 SDL_HomeLEDHintChanged, ctx);
1642}
1643
1644static void HIDAPI_DriverXboxOne_FreeDevice(SDL_HIDAPI_Device *device)
1645{
1646 SDL_DriverXboxOne_Context *ctx = (SDL_DriverXboxOne_Context *)device->context;
1647
1648 HIDAPI_GIP_DestroyChunkBuffer(ctx);
1649}
1650
1651SDL_HIDAPI_DeviceDriver SDL_HIDAPI_DriverXboxOne = {
1652 SDL_HINT_JOYSTICK_HIDAPI_XBOX_ONE,
1653 true,
1654 HIDAPI_DriverXboxOne_RegisterHints,
1655 HIDAPI_DriverXboxOne_UnregisterHints,
1656 HIDAPI_DriverXboxOne_IsEnabled,
1657 HIDAPI_DriverXboxOne_IsSupportedDevice,
1658 HIDAPI_DriverXboxOne_InitDevice,
1659 HIDAPI_DriverXboxOne_GetDevicePlayerIndex,
1660 HIDAPI_DriverXboxOne_SetDevicePlayerIndex,
1661 HIDAPI_DriverXboxOne_UpdateDevice,
1662 HIDAPI_DriverXboxOne_OpenJoystick,
1663 HIDAPI_DriverXboxOne_RumbleJoystick,
1664 HIDAPI_DriverXboxOne_RumbleJoystickTriggers,
1665 HIDAPI_DriverXboxOne_GetJoystickCapabilities,
1666 HIDAPI_DriverXboxOne_SetJoystickLED,
1667 HIDAPI_DriverXboxOne_SendJoystickEffect,
1668 HIDAPI_DriverXboxOne_SetJoystickSensorsEnabled,
1669 HIDAPI_DriverXboxOne_CloseJoystick,
1670 HIDAPI_DriverXboxOne_FreeDevice,
1671};
1672
1673#endif // SDL_JOYSTICK_HIDAPI_XBOXONE
1674
1675#endif // SDL_JOYSTICK_HIDAPI
1676