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// This is the gamepad API for Simple DirectMedia Layer
24
25#include "SDL_sysjoystick.h"
26#include "SDL_joystick_c.h"
27#include "SDL_steam_virtual_gamepad.h"
28#include "SDL_gamepad_c.h"
29#include "SDL_gamepad_db.h"
30#include "controller_type.h"
31#include "usb_ids.h"
32#include "hidapi/SDL_hidapi_nintendo.h"
33#include "../events/SDL_events_c.h"
34
35
36#ifdef SDL_PLATFORM_ANDROID
37#endif
38
39// Many gamepads turn the center button into an instantaneous button press
40#define SDL_MINIMUM_GUIDE_BUTTON_DELAY_MS 250
41
42#define SDL_GAMEPAD_CRC_FIELD "crc:"
43#define SDL_GAMEPAD_CRC_FIELD_SIZE 4 // hard-coded for speed
44#define SDL_GAMEPAD_TYPE_FIELD "type:"
45#define SDL_GAMEPAD_TYPE_FIELD_SIZE SDL_strlen(SDL_GAMEPAD_TYPE_FIELD)
46#define SDL_GAMEPAD_FACE_FIELD "face:"
47#define SDL_GAMEPAD_FACE_FIELD_SIZE 5 // hard-coded for speed
48#define SDL_GAMEPAD_PLATFORM_FIELD "platform:"
49#define SDL_GAMEPAD_PLATFORM_FIELD_SIZE SDL_strlen(SDL_GAMEPAD_PLATFORM_FIELD)
50#define SDL_GAMEPAD_HINT_FIELD "hint:"
51#define SDL_GAMEPAD_HINT_FIELD_SIZE SDL_strlen(SDL_GAMEPAD_HINT_FIELD)
52#define SDL_GAMEPAD_SDKGE_FIELD "sdk>=:"
53#define SDL_GAMEPAD_SDKGE_FIELD_SIZE SDL_strlen(SDL_GAMEPAD_SDKGE_FIELD)
54#define SDL_GAMEPAD_SDKLE_FIELD "sdk<=:"
55#define SDL_GAMEPAD_SDKLE_FIELD_SIZE SDL_strlen(SDL_GAMEPAD_SDKLE_FIELD)
56
57static bool SDL_gamepads_initialized;
58static SDL_Gamepad *SDL_gamepads SDL_GUARDED_BY(SDL_joystick_lock) = NULL;
59
60// The face button style of a gamepad
61typedef enum
62{
63 SDL_GAMEPAD_FACE_STYLE_UNKNOWN,
64 SDL_GAMEPAD_FACE_STYLE_ABXY,
65 SDL_GAMEPAD_FACE_STYLE_BAYX,
66 SDL_GAMEPAD_FACE_STYLE_SONY,
67} SDL_GamepadFaceStyle;
68
69// our hard coded list of mapping support
70typedef enum
71{
72 SDL_GAMEPAD_MAPPING_PRIORITY_DEFAULT,
73 SDL_GAMEPAD_MAPPING_PRIORITY_API,
74 SDL_GAMEPAD_MAPPING_PRIORITY_USER,
75} SDL_GamepadMappingPriority;
76
77#define _guarded SDL_GUARDED_BY(SDL_joystick_lock)
78
79typedef struct GamepadMapping_t
80{
81 SDL_GUID guid _guarded;
82 char *name _guarded;
83 char *mapping _guarded;
84 SDL_GamepadMappingPriority priority _guarded;
85 struct GamepadMapping_t *next _guarded;
86} GamepadMapping_t;
87
88typedef struct
89{
90 int refcount _guarded;
91 SDL_JoystickID *joysticks _guarded;
92 GamepadMapping_t **joystick_mappings _guarded;
93
94 int num_changed_mappings _guarded;
95 GamepadMapping_t **changed_mappings _guarded;
96
97} MappingChangeTracker;
98
99#undef _guarded
100
101static SDL_GUID s_zeroGUID;
102static GamepadMapping_t *s_pSupportedGamepads SDL_GUARDED_BY(SDL_joystick_lock) = NULL;
103static GamepadMapping_t *s_pDefaultMapping SDL_GUARDED_BY(SDL_joystick_lock) = NULL;
104static GamepadMapping_t *s_pXInputMapping SDL_GUARDED_BY(SDL_joystick_lock) = NULL;
105static MappingChangeTracker *s_mappingChangeTracker SDL_GUARDED_BY(SDL_joystick_lock) = NULL;
106static SDL_HashTable *s_gamepadInstanceIDs SDL_GUARDED_BY(SDL_joystick_lock) = NULL;
107
108#define _guarded SDL_GUARDED_BY(SDL_joystick_lock)
109
110// The SDL gamepad structure
111struct SDL_Gamepad
112{
113 SDL_Joystick *joystick _guarded; // underlying joystick device
114 int ref_count _guarded;
115
116 const char *name _guarded;
117 SDL_GamepadType type _guarded;
118 SDL_GamepadFaceStyle face_style _guarded;
119 GamepadMapping_t *mapping _guarded;
120 int num_bindings _guarded;
121 SDL_GamepadBinding *bindings _guarded;
122 SDL_GamepadBinding **last_match_axis _guarded;
123 Uint8 *last_hat_mask _guarded;
124 Uint64 guide_button_down _guarded;
125
126 struct SDL_Gamepad *next _guarded; // pointer to next gamepad we have allocated
127};
128
129#undef _guarded
130
131#define CHECK_GAMEPAD_MAGIC(gamepad, result) \
132 if (!SDL_ObjectValid(gamepad, SDL_OBJECT_TYPE_GAMEPAD) || \
133 !SDL_IsJoystickValid(gamepad->joystick)) { \
134 SDL_InvalidParamError("gamepad"); \
135 SDL_UnlockJoysticks(); \
136 return result; \
137 }
138
139static SDL_vidpid_list SDL_allowed_gamepads = {
140 SDL_HINT_GAMECONTROLLER_IGNORE_DEVICES_EXCEPT, 0, 0, NULL,
141 NULL, 0, 0, NULL,
142 0, NULL,
143 false
144};
145static SDL_vidpid_list SDL_ignored_gamepads = {
146 SDL_HINT_GAMECONTROLLER_IGNORE_DEVICES, 0, 0, NULL,
147 NULL, 0, 0, NULL,
148 0, NULL,
149 false
150};
151
152static GamepadMapping_t *SDL_PrivateAddMappingForGUID(SDL_GUID jGUID, const char *mappingString, bool *existing, SDL_GamepadMappingPriority priority);
153static void SDL_PrivateLoadButtonMapping(SDL_Gamepad *gamepad, GamepadMapping_t *pGamepadMapping);
154static GamepadMapping_t *SDL_PrivateGetGamepadMapping(SDL_JoystickID instance_id, bool create_mapping);
155static void SDL_SendGamepadAxis(Uint64 timestamp, SDL_Gamepad *gamepad, SDL_GamepadAxis axis, Sint16 value);
156static void SDL_SendGamepadButton(Uint64 timestamp, SDL_Gamepad *gamepad, SDL_GamepadButton button, bool down);
157
158static bool HasSameOutput(SDL_GamepadBinding *a, SDL_GamepadBinding *b)
159{
160 if (a->output_type != b->output_type) {
161 return false;
162 }
163
164 if (a->output_type == SDL_GAMEPAD_BINDTYPE_AXIS) {
165 return a->output.axis.axis == b->output.axis.axis;
166 } else {
167 return a->output.button == b->output.button;
168 }
169}
170
171static void ResetOutput(Uint64 timestamp, SDL_Gamepad *gamepad, SDL_GamepadBinding *bind)
172{
173 if (bind->output_type == SDL_GAMEPAD_BINDTYPE_AXIS) {
174 SDL_SendGamepadAxis(timestamp, gamepad, bind->output.axis.axis, 0);
175 } else {
176 SDL_SendGamepadButton(timestamp, gamepad, bind->output.button, false);
177 }
178}
179
180static void HandleJoystickAxis(Uint64 timestamp, SDL_Gamepad *gamepad, int axis, int value)
181{
182 int i;
183 SDL_GamepadBinding *last_match;
184 SDL_GamepadBinding *match = NULL;
185
186 SDL_AssertJoysticksLocked();
187
188 last_match = gamepad->last_match_axis[axis];
189 for (i = 0; i < gamepad->num_bindings; ++i) {
190 SDL_GamepadBinding *binding = &gamepad->bindings[i];
191 if (binding->input_type == SDL_GAMEPAD_BINDTYPE_AXIS &&
192 axis == binding->input.axis.axis) {
193 if (binding->input.axis.axis_min < binding->input.axis.axis_max) {
194 if (value >= binding->input.axis.axis_min &&
195 value <= binding->input.axis.axis_max) {
196 match = binding;
197 break;
198 }
199 } else {
200 if (value >= binding->input.axis.axis_max &&
201 value <= binding->input.axis.axis_min) {
202 match = binding;
203 break;
204 }
205 }
206 }
207 }
208
209 if (last_match && (!match || !HasSameOutput(last_match, match))) {
210 // Clear the last input that this axis generated
211 ResetOutput(timestamp, gamepad, last_match);
212 }
213
214 if (match) {
215 if (match->output_type == SDL_GAMEPAD_BINDTYPE_AXIS) {
216 if (match->input.axis.axis_min != match->output.axis.axis_min || match->input.axis.axis_max != match->output.axis.axis_max) {
217 float normalized_value = (float)(value - match->input.axis.axis_min) / (match->input.axis.axis_max - match->input.axis.axis_min);
218 value = match->output.axis.axis_min + (int)(normalized_value * (match->output.axis.axis_max - match->output.axis.axis_min));
219 }
220 SDL_SendGamepadAxis(timestamp, gamepad, match->output.axis.axis, (Sint16)value);
221 } else {
222 bool down;
223 int threshold = match->input.axis.axis_min + (match->input.axis.axis_max - match->input.axis.axis_min) / 2;
224 if (match->input.axis.axis_max < match->input.axis.axis_min) {
225 down = (value <= threshold);
226 } else {
227 down = (value >= threshold);
228 }
229 SDL_SendGamepadButton(timestamp, gamepad, match->output.button, down);
230 }
231 }
232 gamepad->last_match_axis[axis] = match;
233}
234
235static void HandleJoystickButton(Uint64 timestamp, SDL_Gamepad *gamepad, int button, bool down)
236{
237 int i;
238
239 SDL_AssertJoysticksLocked();
240
241 for (i = 0; i < gamepad->num_bindings; ++i) {
242 SDL_GamepadBinding *binding = &gamepad->bindings[i];
243 if (binding->input_type == SDL_GAMEPAD_BINDTYPE_BUTTON &&
244 button == binding->input.button) {
245 if (binding->output_type == SDL_GAMEPAD_BINDTYPE_AXIS) {
246 int value = down ? binding->output.axis.axis_max : binding->output.axis.axis_min;
247 SDL_SendGamepadAxis(timestamp, gamepad, binding->output.axis.axis, (Sint16)value);
248 } else {
249 SDL_SendGamepadButton(timestamp, gamepad, binding->output.button, down);
250 }
251 break;
252 }
253 }
254}
255
256static void HandleJoystickHat(Uint64 timestamp, SDL_Gamepad *gamepad, int hat, Uint8 value)
257{
258 int i;
259 Uint8 last_mask, changed_mask;
260
261 SDL_AssertJoysticksLocked();
262
263 last_mask = gamepad->last_hat_mask[hat];
264 changed_mask = (last_mask ^ value);
265 for (i = 0; i < gamepad->num_bindings; ++i) {
266 SDL_GamepadBinding *binding = &gamepad->bindings[i];
267 if (binding->input_type == SDL_GAMEPAD_BINDTYPE_HAT && hat == binding->input.hat.hat) {
268 if ((changed_mask & binding->input.hat.hat_mask) != 0) {
269 if (value & binding->input.hat.hat_mask) {
270 if (binding->output_type == SDL_GAMEPAD_BINDTYPE_AXIS) {
271 SDL_SendGamepadAxis(timestamp, gamepad, binding->output.axis.axis, (Sint16)binding->output.axis.axis_max);
272 } else {
273 SDL_SendGamepadButton(timestamp, gamepad, binding->output.button, true);
274 }
275 } else {
276 ResetOutput(timestamp, gamepad, binding);
277 }
278 }
279 }
280 }
281 gamepad->last_hat_mask[hat] = value;
282}
283
284/* The joystick layer will _also_ send events to recenter before disconnect,
285 but it has to make (sometimes incorrect) guesses at what being "centered"
286 is. The gamepad layer, however, can set a definite logical idle
287 position, so set them all here. If we happened to already be at the
288 center thanks to the joystick layer or idle hands, this won't generate
289 duplicate events. */
290static void RecenterGamepad(SDL_Gamepad *gamepad)
291{
292 int i;
293 Uint64 timestamp = SDL_GetTicksNS();
294
295 for (i = 0; i < SDL_GAMEPAD_BUTTON_COUNT; ++i) {
296 SDL_GamepadButton button = (SDL_GamepadButton)i;
297 if (SDL_GetGamepadButton(gamepad, button)) {
298 SDL_SendGamepadButton(timestamp, gamepad, button, false);
299 }
300 }
301
302 for (i = 0; i < SDL_GAMEPAD_AXIS_COUNT; ++i) {
303 SDL_GamepadAxis axis = (SDL_GamepadAxis)i;
304 if (SDL_GetGamepadAxis(gamepad, axis) != 0) {
305 SDL_SendGamepadAxis(timestamp, gamepad, axis, 0);
306 }
307 }
308}
309
310void SDL_PrivateGamepadAdded(SDL_JoystickID instance_id)
311{
312 SDL_Event event;
313
314 if (!SDL_gamepads_initialized) {
315 return;
316 }
317
318 event.type = SDL_EVENT_GAMEPAD_ADDED;
319 event.common.timestamp = 0;
320 event.gdevice.which = instance_id;
321 SDL_PushEvent(&event);
322}
323
324void SDL_PrivateGamepadRemoved(SDL_JoystickID instance_id)
325{
326 SDL_Event event;
327 SDL_Gamepad *gamepad;
328
329 SDL_AssertJoysticksLocked();
330
331 if (!SDL_gamepads_initialized) {
332 return;
333 }
334
335 for (gamepad = SDL_gamepads; gamepad; gamepad = gamepad->next) {
336 if (gamepad->joystick->instance_id == instance_id) {
337 RecenterGamepad(gamepad);
338 break;
339 }
340 }
341
342 event.type = SDL_EVENT_GAMEPAD_REMOVED;
343 event.common.timestamp = 0;
344 event.gdevice.which = instance_id;
345 SDL_PushEvent(&event);
346}
347
348static void SDL_PrivateGamepadRemapped(SDL_JoystickID instance_id)
349{
350 SDL_Event event;
351
352 if (!SDL_gamepads_initialized || SDL_IsJoystickBeingAdded()) {
353 return;
354 }
355
356 event.type = SDL_EVENT_GAMEPAD_REMAPPED;
357 event.common.timestamp = 0;
358 event.gdevice.which = instance_id;
359 SDL_PushEvent(&event);
360}
361
362/*
363 * Event filter to fire gamepad events from joystick ones
364 */
365static bool SDLCALL SDL_GamepadEventWatcher(void *userdata, SDL_Event *event)
366{
367 SDL_Gamepad *gamepad;
368
369 switch (event->type) {
370 case SDL_EVENT_JOYSTICK_AXIS_MOTION:
371 {
372 SDL_AssertJoysticksLocked();
373
374 for (gamepad = SDL_gamepads; gamepad; gamepad = gamepad->next) {
375 if (gamepad->joystick->instance_id == event->jaxis.which) {
376 HandleJoystickAxis(event->common.timestamp, gamepad, event->jaxis.axis, event->jaxis.value);
377 break;
378 }
379 }
380 } break;
381 case SDL_EVENT_JOYSTICK_BUTTON_DOWN:
382 case SDL_EVENT_JOYSTICK_BUTTON_UP:
383 {
384 SDL_AssertJoysticksLocked();
385
386 for (gamepad = SDL_gamepads; gamepad; gamepad = gamepad->next) {
387 if (gamepad->joystick->instance_id == event->jbutton.which) {
388 HandleJoystickButton(event->common.timestamp, gamepad, event->jbutton.button, event->jbutton.down);
389 break;
390 }
391 }
392 } break;
393 case SDL_EVENT_JOYSTICK_HAT_MOTION:
394 {
395 SDL_AssertJoysticksLocked();
396
397 for (gamepad = SDL_gamepads; gamepad; gamepad = gamepad->next) {
398 if (gamepad->joystick->instance_id == event->jhat.which) {
399 HandleJoystickHat(event->common.timestamp, gamepad, event->jhat.hat, event->jhat.value);
400 break;
401 }
402 }
403 } break;
404 case SDL_EVENT_JOYSTICK_UPDATE_COMPLETE:
405 {
406 SDL_AssertJoysticksLocked();
407
408 for (gamepad = SDL_gamepads; gamepad; gamepad = gamepad->next) {
409 if (gamepad->joystick->instance_id == event->jdevice.which) {
410 SDL_Event deviceevent;
411
412 deviceevent.type = SDL_EVENT_GAMEPAD_UPDATE_COMPLETE;
413 deviceevent.common.timestamp = event->jdevice.timestamp;
414 deviceevent.gdevice.which = event->jdevice.which;
415 SDL_PushEvent(&deviceevent);
416 break;
417 }
418 }
419 } break;
420 default:
421 break;
422 }
423
424 return true;
425}
426
427/* SDL defines sensor orientation relative to the device natural
428 orientation, so when it's changed orientation to be used as a
429 gamepad, change the sensor orientation to match.
430 */
431static void AdjustSensorOrientation(const SDL_Joystick *joystick, const float *src, float *dst)
432{
433 unsigned int i, j;
434
435 SDL_AssertJoysticksLocked();
436
437 for (i = 0; i < 3; ++i) {
438 dst[i] = 0.0f;
439 for (j = 0; j < 3; ++j) {
440 dst[i] += joystick->sensor_transform[i][j] * src[j];
441 }
442 }
443}
444
445/*
446 * Event filter to fire gamepad sensor events from system sensor events
447 *
448 * We don't use SDL_GamepadEventWatcher() for this because we want to
449 * deliver gamepad sensor events when system sensor events are disabled,
450 * and we also need to avoid a potential deadlock where joystick event
451 * delivery locks the joysticks and then the event queue, but sensor
452 * event delivery would lock the event queue and then from within the
453 * event watcher function lock the joysticks.
454 */
455void SDL_GamepadSensorWatcher(Uint64 timestamp, SDL_SensorID sensor, Uint64 sensor_timestamp, float *data, int num_values)
456{
457 SDL_Gamepad *gamepad;
458
459 SDL_LockJoysticks();
460 for (gamepad = SDL_gamepads; gamepad; gamepad = gamepad->next) {
461 if (gamepad->joystick->accel && gamepad->joystick->accel_sensor == sensor) {
462 float gamepad_data[3];
463 AdjustSensorOrientation(gamepad->joystick, data, gamepad_data);
464 SDL_SendJoystickSensor(timestamp, gamepad->joystick, SDL_SENSOR_ACCEL, sensor_timestamp, gamepad_data, SDL_arraysize(gamepad_data));
465 }
466 if (gamepad->joystick->gyro && gamepad->joystick->gyro_sensor == sensor) {
467 float gamepad_data[3];
468 AdjustSensorOrientation(gamepad->joystick, data, gamepad_data);
469 SDL_SendJoystickSensor(timestamp, gamepad->joystick, SDL_SENSOR_GYRO, sensor_timestamp, gamepad_data, SDL_arraysize(gamepad_data));
470 }
471 }
472 SDL_UnlockJoysticks();
473}
474
475static void PushMappingChangeTracking(void)
476{
477 MappingChangeTracker *tracker;
478 int i, num_joysticks;
479
480 SDL_AssertJoysticksLocked();
481
482 if (s_mappingChangeTracker) {
483 ++s_mappingChangeTracker->refcount;
484 return;
485 }
486 s_mappingChangeTracker = (MappingChangeTracker *)SDL_calloc(1, sizeof(*tracker));
487 s_mappingChangeTracker->refcount = 1;
488
489 // Save the list of joysticks and associated mappings
490 tracker = s_mappingChangeTracker;
491 tracker->joysticks = SDL_GetJoysticks(&num_joysticks);
492 if (!tracker->joysticks) {
493 return;
494 }
495 if (num_joysticks == 0) {
496 return;
497 }
498 tracker->joystick_mappings = (GamepadMapping_t **)SDL_malloc(num_joysticks * sizeof(*tracker->joystick_mappings));
499 if (!tracker->joystick_mappings) {
500 return;
501 }
502 for (i = 0; i < num_joysticks; ++i) {
503 tracker->joystick_mappings[i] = SDL_PrivateGetGamepadMapping(tracker->joysticks[i], false);
504 }
505}
506
507static void AddMappingChangeTracking(GamepadMapping_t *mapping)
508{
509 MappingChangeTracker *tracker;
510 int num_mappings;
511 GamepadMapping_t **new_mappings;
512
513 SDL_AssertJoysticksLocked();
514
515 SDL_assert(s_mappingChangeTracker != NULL);
516 tracker = s_mappingChangeTracker;
517 num_mappings = tracker->num_changed_mappings;
518 new_mappings = (GamepadMapping_t **)SDL_realloc(tracker->changed_mappings, (num_mappings + 1) * sizeof(*new_mappings));
519 if (new_mappings) {
520 tracker->changed_mappings = new_mappings;
521 tracker->changed_mappings[num_mappings] = mapping;
522 tracker->num_changed_mappings = (num_mappings + 1);
523 }
524}
525
526static bool HasMappingChangeTracking(MappingChangeTracker *tracker, GamepadMapping_t *mapping)
527{
528 int i;
529
530 SDL_AssertJoysticksLocked();
531
532 for (i = 0; i < tracker->num_changed_mappings; ++i) {
533 if (tracker->changed_mappings[i] == mapping) {
534 return true;
535 }
536 }
537 return false;
538}
539
540static void PopMappingChangeTracking(void)
541{
542 int i;
543 MappingChangeTracker *tracker;
544
545 SDL_AssertJoysticksLocked();
546
547 SDL_assert(s_mappingChangeTracker != NULL);
548 tracker = s_mappingChangeTracker;
549 --tracker->refcount;
550 if (tracker->refcount > 0) {
551 return;
552 }
553 s_mappingChangeTracker = NULL;
554
555 // Now check to see what gamepads changed because of the mapping changes
556 if (tracker->joysticks && tracker->joystick_mappings) {
557 for (i = 0; tracker->joysticks[i]; ++i) {
558 // Looking up the new mapping might create one and associate it with the gamepad (and generate events)
559 SDL_JoystickID joystick = tracker->joysticks[i];
560 SDL_Gamepad *gamepad = SDL_GetGamepadFromID(joystick);
561 GamepadMapping_t *new_mapping = SDL_PrivateGetGamepadMapping(joystick, false);
562 GamepadMapping_t *old_mapping = gamepad ? gamepad->mapping : tracker->joystick_mappings[i];
563
564 if (new_mapping && !old_mapping) {
565 SDL_InsertIntoHashTable(s_gamepadInstanceIDs, (void *)(uintptr_t)joystick, (const void *)true, true);
566 SDL_PrivateGamepadAdded(joystick);
567 } else if (old_mapping && !new_mapping) {
568 SDL_InsertIntoHashTable(s_gamepadInstanceIDs, (void *)(uintptr_t)joystick, (const void *)false, true);
569 SDL_PrivateGamepadRemoved(joystick);
570 } else if (old_mapping != new_mapping || HasMappingChangeTracking(tracker, new_mapping)) {
571 if (gamepad) {
572 SDL_PrivateLoadButtonMapping(gamepad, new_mapping);
573 }
574 SDL_PrivateGamepadRemapped(joystick);
575 }
576 }
577 }
578
579 SDL_free(tracker->joysticks);
580 SDL_free(tracker->joystick_mappings);
581 SDL_free(tracker->changed_mappings);
582 SDL_free(tracker);
583}
584
585#ifdef SDL_PLATFORM_ANDROID
586/*
587 * Helper function to guess at a mapping based on the elements reported for this gamepad
588 */
589static GamepadMapping_t *SDL_CreateMappingForAndroidGamepad(SDL_GUID guid)
590{
591 const int face_button_mask = ((1 << SDL_GAMEPAD_BUTTON_SOUTH) |
592 (1 << SDL_GAMEPAD_BUTTON_EAST) |
593 (1 << SDL_GAMEPAD_BUTTON_WEST) |
594 (1 << SDL_GAMEPAD_BUTTON_NORTH));
595 bool existing;
596 char mapping_string[1024];
597 int button_mask;
598 int axis_mask;
599
600 button_mask = SDL_Swap16LE(*(Uint16 *)(&guid.data[sizeof(guid.data) - 4]));
601 axis_mask = SDL_Swap16LE(*(Uint16 *)(&guid.data[sizeof(guid.data) - 2]));
602 if (!button_mask && !axis_mask) {
603 // Accelerometer, shouldn't have a gamepad mapping
604 return NULL;
605 }
606 if (!(button_mask & face_button_mask)) {
607 // We don't know what buttons or axes are supported, don't make up a mapping
608 return NULL;
609 }
610
611 SDL_strlcpy(mapping_string, "none,*,", sizeof(mapping_string));
612
613 if (button_mask & (1 << SDL_GAMEPAD_BUTTON_SOUTH)) {
614 SDL_strlcat(mapping_string, "a:b0,", sizeof(mapping_string));
615 }
616 if (button_mask & (1 << SDL_GAMEPAD_BUTTON_EAST)) {
617 SDL_strlcat(mapping_string, "b:b1,", sizeof(mapping_string));
618 } else if (button_mask & (1 << SDL_GAMEPAD_BUTTON_BACK)) {
619 // Use the back button as "B" for easy UI navigation with TV remotes
620 SDL_strlcat(mapping_string, "b:b4,", sizeof(mapping_string));
621 button_mask &= ~(1 << SDL_GAMEPAD_BUTTON_BACK);
622 }
623 if (button_mask & (1 << SDL_GAMEPAD_BUTTON_WEST)) {
624 SDL_strlcat(mapping_string, "x:b2,", sizeof(mapping_string));
625 }
626 if (button_mask & (1 << SDL_GAMEPAD_BUTTON_NORTH)) {
627 SDL_strlcat(mapping_string, "y:b3,", sizeof(mapping_string));
628 }
629 if (button_mask & (1 << SDL_GAMEPAD_BUTTON_BACK)) {
630 SDL_strlcat(mapping_string, "back:b4,", sizeof(mapping_string));
631 }
632 if (button_mask & (1 << SDL_GAMEPAD_BUTTON_GUIDE)) {
633 // The guide button generally isn't functional (or acts as a home button) on most Android gamepads before Android 11
634 if (SDL_GetAndroidSDKVersion() >= 30 /* Android 11 */) {
635 SDL_strlcat(mapping_string, "guide:b5,", sizeof(mapping_string));
636 }
637 }
638 if (button_mask & (1 << SDL_GAMEPAD_BUTTON_START)) {
639 SDL_strlcat(mapping_string, "start:b6,", sizeof(mapping_string));
640 }
641 if (button_mask & (1 << SDL_GAMEPAD_BUTTON_LEFT_STICK)) {
642 SDL_strlcat(mapping_string, "leftstick:b7,", sizeof(mapping_string));
643 }
644 if (button_mask & (1 << SDL_GAMEPAD_BUTTON_RIGHT_STICK)) {
645 SDL_strlcat(mapping_string, "rightstick:b8,", sizeof(mapping_string));
646 }
647 if (button_mask & (1 << SDL_GAMEPAD_BUTTON_LEFT_SHOULDER)) {
648 SDL_strlcat(mapping_string, "leftshoulder:b9,", sizeof(mapping_string));
649 }
650 if (button_mask & (1 << SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER)) {
651 SDL_strlcat(mapping_string, "rightshoulder:b10,", sizeof(mapping_string));
652 }
653 if (button_mask & (1 << SDL_GAMEPAD_BUTTON_DPAD_UP)) {
654 SDL_strlcat(mapping_string, "dpup:b11,", sizeof(mapping_string));
655 }
656 if (button_mask & (1 << SDL_GAMEPAD_BUTTON_DPAD_DOWN)) {
657 SDL_strlcat(mapping_string, "dpdown:b12,", sizeof(mapping_string));
658 }
659 if (button_mask & (1 << SDL_GAMEPAD_BUTTON_DPAD_LEFT)) {
660 SDL_strlcat(mapping_string, "dpleft:b13,", sizeof(mapping_string));
661 }
662 if (button_mask & (1 << SDL_GAMEPAD_BUTTON_DPAD_RIGHT)) {
663 SDL_strlcat(mapping_string, "dpright:b14,", sizeof(mapping_string));
664 }
665 if (axis_mask & (1 << SDL_GAMEPAD_AXIS_LEFTX)) {
666 SDL_strlcat(mapping_string, "leftx:a0,", sizeof(mapping_string));
667 }
668 if (axis_mask & (1 << SDL_GAMEPAD_AXIS_LEFTY)) {
669 SDL_strlcat(mapping_string, "lefty:a1,", sizeof(mapping_string));
670 }
671 if (axis_mask & (1 << SDL_GAMEPAD_AXIS_RIGHTX)) {
672 SDL_strlcat(mapping_string, "rightx:a2,", sizeof(mapping_string));
673 }
674 if (axis_mask & (1 << SDL_GAMEPAD_AXIS_RIGHTY)) {
675 SDL_strlcat(mapping_string, "righty:a3,", sizeof(mapping_string));
676 }
677 if (axis_mask & (1 << SDL_GAMEPAD_AXIS_LEFT_TRIGGER)) {
678 SDL_strlcat(mapping_string, "lefttrigger:a4,", sizeof(mapping_string));
679 }
680 if (axis_mask & (1 << SDL_GAMEPAD_AXIS_RIGHT_TRIGGER)) {
681 SDL_strlcat(mapping_string, "righttrigger:a5,", sizeof(mapping_string));
682 }
683
684 return SDL_PrivateAddMappingForGUID(guid, mapping_string, &existing, SDL_GAMEPAD_MAPPING_PRIORITY_DEFAULT);
685}
686#endif // SDL_PLATFORM_ANDROID
687
688/*
689 * Helper function to guess at a mapping for HIDAPI gamepads
690 */
691static GamepadMapping_t *SDL_CreateMappingForHIDAPIGamepad(SDL_GUID guid)
692{
693 bool existing;
694 char mapping_string[1024];
695 Uint16 vendor;
696 Uint16 product;
697
698 SDL_strlcpy(mapping_string, "none,*,", sizeof(mapping_string));
699
700 SDL_GetJoystickGUIDInfo(guid, &vendor, &product, NULL, NULL);
701
702 if (SDL_IsJoystickWheel(vendor, product)) {
703 // We don't want to pick up Logitech FFB wheels here
704 // Some versions of WINE will also not treat devices that show up as gamepads as wheels
705 return NULL;
706 }
707
708 if ((vendor == USB_VENDOR_NINTENDO && product == USB_PRODUCT_NINTENDO_GAMECUBE_ADAPTER) ||
709 (vendor == USB_VENDOR_DRAGONRISE &&
710 (product == USB_PRODUCT_EVORETRO_GAMECUBE_ADAPTER1 ||
711 product == USB_PRODUCT_EVORETRO_GAMECUBE_ADAPTER2))) {
712 // GameCube driver has 12 buttons and 6 axes
713 SDL_strlcat(mapping_string, "a:b0,b:b1,dpdown:b6,dpleft:b4,dpright:b5,dpup:b7,lefttrigger:a4,leftx:a0,lefty:a1~,rightshoulder:b9,righttrigger:a5,rightx:a2,righty:a3~,start:b8,x:b2,y:b3,", sizeof(mapping_string));
714 } else if (vendor == USB_VENDOR_NINTENDO &&
715 (guid.data[15] == k_eSwitchDeviceInfoControllerType_HVCLeft ||
716 guid.data[15] == k_eSwitchDeviceInfoControllerType_HVCRight ||
717 guid.data[15] == k_eSwitchDeviceInfoControllerType_NESLeft ||
718 guid.data[15] == k_eSwitchDeviceInfoControllerType_NESRight ||
719 guid.data[15] == k_eSwitchDeviceInfoControllerType_SNES ||
720 guid.data[15] == k_eSwitchDeviceInfoControllerType_N64 ||
721 guid.data[15] == k_eSwitchDeviceInfoControllerType_SEGA_Genesis ||
722 guid.data[15] == k_eWiiExtensionControllerType_None ||
723 guid.data[15] == k_eWiiExtensionControllerType_Nunchuk ||
724 guid.data[15] == k_eSwitchDeviceInfoControllerType_JoyConLeft ||
725 guid.data[15] == k_eSwitchDeviceInfoControllerType_JoyConRight)) {
726 switch (guid.data[15]) {
727 case k_eSwitchDeviceInfoControllerType_HVCLeft:
728 SDL_strlcat(mapping_string, "a:b0,b:b1,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,rightshoulder:b10,start:b6,", sizeof(mapping_string));
729 break;
730 case k_eSwitchDeviceInfoControllerType_HVCRight:
731 SDL_strlcat(mapping_string, "a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,rightshoulder:b10,", sizeof(mapping_string));
732 break;
733 case k_eSwitchDeviceInfoControllerType_NESLeft:
734 case k_eSwitchDeviceInfoControllerType_NESRight:
735 SDL_strlcat(mapping_string, "a:b0,b:b1,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,rightshoulder:b10,start:b6,", sizeof(mapping_string));
736 break;
737 case k_eSwitchDeviceInfoControllerType_SNES:
738 SDL_strlcat(mapping_string, "a:b0,b:b1,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,lefttrigger:a4,rightshoulder:b10,righttrigger:a5,start:b6,x:b2,y:b3,", sizeof(mapping_string));
739 break;
740 case k_eSwitchDeviceInfoControllerType_N64:
741 SDL_strlcat(mapping_string, "a:b0,b:b1,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,righttrigger:a5,start:b6,x:b2,y:b3,misc1:b11,", sizeof(mapping_string));
742 break;
743 case k_eSwitchDeviceInfoControllerType_SEGA_Genesis:
744 SDL_strlcat(mapping_string, "a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b5,leftshoulder:b9,rightshoulder:b10,righttrigger:a5,start:b6,x:b2,y:b3,misc1:b11,", sizeof(mapping_string));
745 break;
746 case k_eWiiExtensionControllerType_None:
747 SDL_strlcat(mapping_string, "a:b0,b:b1,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b5,start:b6,x:b2,y:b3,", sizeof(mapping_string));
748 break;
749 case k_eWiiExtensionControllerType_Nunchuk:
750 {
751 // FIXME: Should we map this to the left or right side?
752 const bool map_nunchuck_left_side = true;
753
754 if (map_nunchuck_left_side) {
755 SDL_strlcat(mapping_string, "a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,lefttrigger:a4,leftx:a0,lefty:a1,start:b6,x:b2,y:b3,", sizeof(mapping_string));
756 } else {
757 SDL_strlcat(mapping_string, "a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,rightshoulder:b9,righttrigger:a4,rightx:a0,righty:a1,start:b6,x:b2,y:b3,", sizeof(mapping_string));
758 }
759 } break;
760 default:
761 if (SDL_GetHintBoolean(SDL_HINT_JOYSTICK_HIDAPI_VERTICAL_JOY_CONS, false)) {
762 // Vertical mode
763 if (guid.data[15] == k_eSwitchDeviceInfoControllerType_JoyConLeft) {
764 SDL_strlcat(mapping_string, "back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,misc1:b11,paddle2:b13,paddle4:b15,", sizeof(mapping_string));
765 } else {
766 SDL_strlcat(mapping_string, "a:b0,b:b1,guide:b5,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,paddle1:b12,paddle3:b14,", sizeof(mapping_string));
767 }
768 } else {
769 // Mini gamepad mode
770 if (guid.data[15] == k_eSwitchDeviceInfoControllerType_JoyConLeft) {
771 SDL_strlcat(mapping_string, "a:b0,b:b1,guide:b5,leftshoulder:b9,leftstick:b7,leftx:a0,lefty:a1,rightshoulder:b10,start:b6,x:b2,y:b3,paddle2:b13,paddle4:b15,", sizeof(mapping_string));
772 } else {
773 SDL_strlcat(mapping_string, "a:b0,b:b1,guide:b5,leftshoulder:b9,leftstick:b7,leftx:a0,lefty:a1,rightshoulder:b10,start:b6,x:b2,y:b3,paddle1:b12,paddle3:b14,", sizeof(mapping_string));
774 }
775 }
776 break;
777 }
778 } else {
779 // All other gamepads have the standard set of 19 buttons and 6 axes
780 SDL_strlcat(mapping_string, "a:b0,b:b1,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,", sizeof(mapping_string));
781
782 if (SDL_IsJoystickXboxSeriesX(vendor, product)) {
783 // XBox Series X Controllers have a share button under the guide button
784 SDL_strlcat(mapping_string, "misc1:b11,", sizeof(mapping_string));
785 } else if (SDL_IsJoystickXboxOneElite(vendor, product)) {
786 // XBox One Elite Controllers have 4 back paddle buttons
787 SDL_strlcat(mapping_string, "paddle1:b11,paddle2:b13,paddle3:b12,paddle4:b14,", sizeof(mapping_string));
788 } else if (SDL_IsJoystickSteamController(vendor, product)) {
789 // Steam controllers have 2 back paddle buttons
790 SDL_strlcat(mapping_string, "paddle1:b12,paddle2:b11,", sizeof(mapping_string));
791 } else if (SDL_IsJoystickNintendoSwitchPro(vendor, product) ||
792 SDL_IsJoystickNintendoSwitchProInputOnly(vendor, product)) {
793 // Nintendo Switch Pro controllers have a screenshot button
794 SDL_strlcat(mapping_string, "misc1:b11,", sizeof(mapping_string));
795 } else if (SDL_IsJoystickNintendoSwitchJoyConPair(vendor, product)) {
796 // The Nintendo Switch Joy-Con combined controllers has a share button and paddles
797 SDL_strlcat(mapping_string, "misc1:b11,paddle1:b12,paddle2:b13,paddle3:b14,paddle4:b15,", sizeof(mapping_string));
798 } else if (SDL_IsJoystickAmazonLunaController(vendor, product)) {
799 // Amazon Luna Controller has a mic button under the guide button
800 SDL_strlcat(mapping_string, "misc1:b11,", sizeof(mapping_string));
801 } else if (SDL_IsJoystickGoogleStadiaController(vendor, product)) {
802 // The Google Stadia controller has a share button and a Google Assistant button
803 SDL_strlcat(mapping_string, "misc1:b11,misc2:b12", sizeof(mapping_string));
804 } else if (SDL_IsJoystickNVIDIASHIELDController(vendor, product)) {
805 // The NVIDIA SHIELD controller has a share button between back and start buttons
806 SDL_strlcat(mapping_string, "misc1:b11,", sizeof(mapping_string));
807
808 if (product == USB_PRODUCT_NVIDIA_SHIELD_CONTROLLER_V103) {
809 // The original SHIELD controller has a touchpad and plus/minus buttons as well
810 SDL_strlcat(mapping_string, "touchpad:b12,misc2:b13,misc3:b14", sizeof(mapping_string));
811 }
812 } else if (SDL_IsJoystickHoriSteamController(vendor, product)) {
813 /* The Wireless HORIPad for Steam has QAM, Steam, Capsense L/R Sticks, 2 rear buttons, and 2 misc buttons */
814 SDL_strlcat(mapping_string, "paddle1:b13,paddle2:b12,paddle3:b15,paddle4:b14,misc2:b11,misc3:b16,misc4:b17", sizeof(mapping_string));
815 } else {
816 switch (SDL_GetGamepadTypeFromGUID(guid, NULL)) {
817 case SDL_GAMEPAD_TYPE_PS4:
818 // PS4 controllers have an additional touchpad button
819 SDL_strlcat(mapping_string, "touchpad:b11,", sizeof(mapping_string));
820 break;
821 case SDL_GAMEPAD_TYPE_PS5:
822 // PS5 controllers have a microphone button and an additional touchpad button
823 SDL_strlcat(mapping_string, "touchpad:b11,misc1:b12,", sizeof(mapping_string));
824 // DualSense Edge controllers have paddles
825 if (SDL_IsJoystickDualSenseEdge(vendor, product)) {
826 SDL_strlcat(mapping_string, "paddle1:b16,paddle2:b15,paddle3:b14,paddle4:b13,", sizeof(mapping_string));
827 }
828 break;
829 default:
830 if (vendor == 0 && product == 0) {
831 // This is a Bluetooth Nintendo Switch Pro controller
832 SDL_strlcat(mapping_string, "misc1:b11,", sizeof(mapping_string));
833 }
834 break;
835 }
836 }
837 }
838
839 return SDL_PrivateAddMappingForGUID(guid, mapping_string, &existing, SDL_GAMEPAD_MAPPING_PRIORITY_DEFAULT);
840}
841
842/*
843 * Helper function to guess at a mapping for RAWINPUT gamepads
844 */
845static GamepadMapping_t *SDL_CreateMappingForRAWINPUTGamepad(SDL_GUID guid)
846{
847 bool existing;
848 char mapping_string[1024];
849
850 SDL_strlcpy(mapping_string, "none,*,", sizeof(mapping_string));
851 SDL_strlcat(mapping_string, "a:b0,b:b1,x:b2,y:b3,back:b6,guide:b10,start:b7,leftstick:b8,rightstick:b9,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a4,righttrigger:a5,", sizeof(mapping_string));
852
853 return SDL_PrivateAddMappingForGUID(guid, mapping_string, &existing, SDL_GAMEPAD_MAPPING_PRIORITY_DEFAULT);
854}
855
856/*
857 * Helper function to guess at a mapping for WGI gamepads
858 */
859static GamepadMapping_t *SDL_CreateMappingForWGIGamepad(SDL_GUID guid)
860{
861 bool existing;
862 char mapping_string[1024];
863
864 if (guid.data[15] != SDL_JOYSTICK_TYPE_GAMEPAD) {
865 return NULL;
866 }
867
868 SDL_strlcpy(mapping_string, "none,*,", sizeof(mapping_string));
869 SDL_strlcat(mapping_string, "a:b0,b:b1,x:b2,y:b3,back:b6,start:b7,leftstick:b8,rightstick:b9,leftshoulder:b4,rightshoulder:b5,dpup:b10,dpdown:b12,dpleft:b13,dpright:b11,leftx:a1,lefty:a0~,rightx:a3,righty:a2~,lefttrigger:a4,righttrigger:a5,", sizeof(mapping_string));
870
871 return SDL_PrivateAddMappingForGUID(guid, mapping_string, &existing, SDL_GAMEPAD_MAPPING_PRIORITY_DEFAULT);
872}
873
874/*
875 * Helper function to scan the mappings database for a gamepad with the specified GUID
876 */
877static GamepadMapping_t *SDL_PrivateMatchGamepadMappingForGUID(SDL_GUID guid, bool match_version, bool exact_match_crc)
878{
879 GamepadMapping_t *mapping, *best_match = NULL;
880 Uint16 crc = 0;
881
882 SDL_AssertJoysticksLocked();
883
884 SDL_GetJoystickGUIDInfo(guid, NULL, NULL, NULL, &crc);
885
886 // Clear the CRC from the GUID for matching, the mappings never include it in the GUID
887 SDL_SetJoystickGUIDCRC(&guid, 0);
888
889 if (!match_version) {
890 SDL_SetJoystickGUIDVersion(&guid, 0);
891 }
892
893 for (mapping = s_pSupportedGamepads; mapping; mapping = mapping->next) {
894 SDL_GUID mapping_guid;
895
896 if (SDL_memcmp(&mapping->guid, &s_zeroGUID, sizeof(mapping->guid)) == 0) {
897 continue;
898 }
899
900 SDL_memcpy(&mapping_guid, &mapping->guid, sizeof(mapping_guid));
901 if (!match_version) {
902 SDL_SetJoystickGUIDVersion(&mapping_guid, 0);
903 }
904
905 if (SDL_memcmp(&guid, &mapping_guid, sizeof(guid)) == 0) {
906 const char *crc_string = SDL_strstr(mapping->mapping, SDL_GAMEPAD_CRC_FIELD);
907 if (crc_string) {
908 Uint16 mapping_crc = (Uint16)SDL_strtol(crc_string + SDL_GAMEPAD_CRC_FIELD_SIZE, NULL, 16);
909 if (mapping_crc != crc) {
910 // This mapping specified a CRC and they don't match
911 continue;
912 }
913
914 // An exact match, including CRC
915 return mapping;
916 } else if (crc && exact_match_crc) {
917 return NULL;
918 }
919
920 if (!best_match) {
921 best_match = mapping;
922 }
923 }
924 }
925 return best_match;
926}
927
928/*
929 * Helper function to scan the mappings database for a gamepad with the specified GUID
930 */
931static GamepadMapping_t *SDL_PrivateGetGamepadMappingForGUID(SDL_GUID guid, bool adding_mapping)
932{
933 GamepadMapping_t *mapping;
934
935 mapping = SDL_PrivateMatchGamepadMappingForGUID(guid, true, adding_mapping);
936 if (mapping) {
937 return mapping;
938 }
939
940 if (adding_mapping) {
941 // We didn't find an existing mapping
942 return NULL;
943 }
944
945 // Try harder to get the best match, or create a mapping
946
947 if (SDL_JoystickGUIDUsesVersion(guid)) {
948 // Try again, ignoring the version
949 mapping = SDL_PrivateMatchGamepadMappingForGUID(guid, false, false);
950 if (mapping) {
951 return mapping;
952 }
953 }
954
955#ifdef SDL_JOYSTICK_XINPUT
956 if (SDL_IsJoystickXInput(guid)) {
957 // This is an XInput device
958 return s_pXInputMapping;
959 }
960#endif
961 if (SDL_IsJoystickHIDAPI(guid)) {
962 mapping = SDL_CreateMappingForHIDAPIGamepad(guid);
963 } else if (SDL_IsJoystickRAWINPUT(guid)) {
964 mapping = SDL_CreateMappingForRAWINPUTGamepad(guid);
965 } else if (SDL_IsJoystickWGI(guid)) {
966 mapping = SDL_CreateMappingForWGIGamepad(guid);
967 } else if (SDL_IsJoystickVIRTUAL(guid)) {
968 // We'll pick up a robust mapping in VIRTUAL_JoystickGetGamepadMapping
969#ifdef SDL_PLATFORM_ANDROID
970 } else {
971 mapping = SDL_CreateMappingForAndroidGamepad(guid);
972#endif
973 }
974 return mapping;
975}
976
977static const char *map_StringForGamepadType[] = {
978 "unknown",
979 "standard",
980 "xbox360",
981 "xboxone",
982 "ps3",
983 "ps4",
984 "ps5",
985 "switchpro",
986 "joyconleft",
987 "joyconright",
988 "joyconpair"
989};
990SDL_COMPILE_TIME_ASSERT(map_StringForGamepadType, SDL_arraysize(map_StringForGamepadType) == SDL_GAMEPAD_TYPE_COUNT);
991
992/*
993 * convert a string to its enum equivalent
994 */
995SDL_GamepadType SDL_GetGamepadTypeFromString(const char *str)
996{
997 int i;
998
999 if (!str || str[0] == '\0') {
1000 return SDL_GAMEPAD_TYPE_UNKNOWN;
1001 }
1002
1003 if (*str == '+' || *str == '-') {
1004 ++str;
1005 }
1006
1007 for (i = 0; i < SDL_arraysize(map_StringForGamepadType); ++i) {
1008 if (SDL_strcasecmp(str, map_StringForGamepadType[i]) == 0) {
1009 return (SDL_GamepadType)i;
1010 }
1011 }
1012 return SDL_GAMEPAD_TYPE_UNKNOWN;
1013}
1014
1015/*
1016 * convert an enum to its string equivalent
1017 */
1018const char *SDL_GetGamepadStringForType(SDL_GamepadType type)
1019{
1020 if (type >= SDL_GAMEPAD_TYPE_STANDARD && type < SDL_GAMEPAD_TYPE_COUNT) {
1021 return map_StringForGamepadType[type];
1022 }
1023 return NULL;
1024}
1025
1026static const char *map_StringForGamepadAxis[] = {
1027 "leftx",
1028 "lefty",
1029 "rightx",
1030 "righty",
1031 "lefttrigger",
1032 "righttrigger"
1033};
1034SDL_COMPILE_TIME_ASSERT(map_StringForGamepadAxis, SDL_arraysize(map_StringForGamepadAxis) == SDL_GAMEPAD_AXIS_COUNT);
1035
1036/*
1037 * convert a string to its enum equivalent
1038 */
1039SDL_GamepadAxis SDL_GetGamepadAxisFromString(const char *str)
1040{
1041 int i;
1042
1043 if (!str || str[0] == '\0') {
1044 return SDL_GAMEPAD_AXIS_INVALID;
1045 }
1046
1047 if (*str == '+' || *str == '-') {
1048 ++str;
1049 }
1050
1051 for (i = 0; i < SDL_arraysize(map_StringForGamepadAxis); ++i) {
1052 if (SDL_strcasecmp(str, map_StringForGamepadAxis[i]) == 0) {
1053 return (SDL_GamepadAxis)i;
1054 }
1055 }
1056 return SDL_GAMEPAD_AXIS_INVALID;
1057}
1058
1059/*
1060 * convert an enum to its string equivalent
1061 */
1062const char *SDL_GetGamepadStringForAxis(SDL_GamepadAxis axis)
1063{
1064 if (axis > SDL_GAMEPAD_AXIS_INVALID && axis < SDL_GAMEPAD_AXIS_COUNT) {
1065 return map_StringForGamepadAxis[axis];
1066 }
1067 return NULL;
1068}
1069
1070static const char *map_StringForGamepadButton[] = {
1071 "a",
1072 "b",
1073 "x",
1074 "y",
1075 "back",
1076 "guide",
1077 "start",
1078 "leftstick",
1079 "rightstick",
1080 "leftshoulder",
1081 "rightshoulder",
1082 "dpup",
1083 "dpdown",
1084 "dpleft",
1085 "dpright",
1086 "misc1",
1087 "paddle1",
1088 "paddle2",
1089 "paddle3",
1090 "paddle4",
1091 "touchpad",
1092 "misc2",
1093 "misc3",
1094 "misc4",
1095 "misc5",
1096 "misc6"
1097};
1098SDL_COMPILE_TIME_ASSERT(map_StringForGamepadButton, SDL_arraysize(map_StringForGamepadButton) == SDL_GAMEPAD_BUTTON_COUNT);
1099
1100/*
1101 * convert a string to its enum equivalent
1102 */
1103static SDL_GamepadButton SDL_PrivateGetGamepadButtonFromString(const char *str, bool baxy)
1104{
1105 int i;
1106
1107 if (!str || str[0] == '\0') {
1108 return SDL_GAMEPAD_BUTTON_INVALID;
1109 }
1110
1111 for (i = 0; i < SDL_arraysize(map_StringForGamepadButton); ++i) {
1112 if (SDL_strcasecmp(str, map_StringForGamepadButton[i]) == 0) {
1113 if (baxy) {
1114 // Need to swap face buttons
1115 switch (i) {
1116 case SDL_GAMEPAD_BUTTON_SOUTH:
1117 return SDL_GAMEPAD_BUTTON_EAST;
1118 case SDL_GAMEPAD_BUTTON_EAST:
1119 return SDL_GAMEPAD_BUTTON_SOUTH;
1120 case SDL_GAMEPAD_BUTTON_WEST:
1121 return SDL_GAMEPAD_BUTTON_NORTH;
1122 case SDL_GAMEPAD_BUTTON_NORTH:
1123 return SDL_GAMEPAD_BUTTON_WEST;
1124 default:
1125 break;
1126 }
1127 }
1128 return (SDL_GamepadButton)i;
1129 }
1130 }
1131 return SDL_GAMEPAD_BUTTON_INVALID;
1132}
1133SDL_GamepadButton SDL_GetGamepadButtonFromString(const char *str)
1134{
1135 return SDL_PrivateGetGamepadButtonFromString(str, false);
1136}
1137
1138/*
1139 * convert an enum to its string equivalent
1140 */
1141const char *SDL_GetGamepadStringForButton(SDL_GamepadButton button)
1142{
1143 if (button > SDL_GAMEPAD_BUTTON_INVALID && button < SDL_GAMEPAD_BUTTON_COUNT) {
1144 return map_StringForGamepadButton[button];
1145 }
1146 return NULL;
1147}
1148
1149/*
1150 * given a gamepad button name and a joystick name update our mapping structure with it
1151 */
1152static bool SDL_PrivateParseGamepadElement(SDL_Gamepad *gamepad, const char *szGameButton, const char *szJoystickButton)
1153{
1154 SDL_GamepadBinding bind;
1155 SDL_GamepadButton button;
1156 SDL_GamepadAxis axis;
1157 bool invert_input = false;
1158 char half_axis_input = 0;
1159 char half_axis_output = 0;
1160 int i;
1161 SDL_GamepadBinding *new_bindings;
1162 bool baxy_mapping = false;
1163
1164 SDL_AssertJoysticksLocked();
1165
1166 SDL_zero(bind);
1167
1168 if (*szGameButton == '+' || *szGameButton == '-') {
1169 half_axis_output = *szGameButton++;
1170 }
1171
1172 if (SDL_strstr(gamepad->mapping->mapping, ",hint:SDL_GAMECONTROLLER_USE_BUTTON_LABELS:=1") != NULL) {
1173 baxy_mapping = true;
1174 }
1175
1176 axis = SDL_GetGamepadAxisFromString(szGameButton);
1177 button = SDL_PrivateGetGamepadButtonFromString(szGameButton, baxy_mapping);
1178 if (axis != SDL_GAMEPAD_AXIS_INVALID) {
1179 bind.output_type = SDL_GAMEPAD_BINDTYPE_AXIS;
1180 bind.output.axis.axis = axis;
1181 if (axis == SDL_GAMEPAD_AXIS_LEFT_TRIGGER || axis == SDL_GAMEPAD_AXIS_RIGHT_TRIGGER) {
1182 bind.output.axis.axis_min = 0;
1183 bind.output.axis.axis_max = SDL_JOYSTICK_AXIS_MAX;
1184 } else {
1185 if (half_axis_output == '+') {
1186 bind.output.axis.axis_min = 0;
1187 bind.output.axis.axis_max = SDL_JOYSTICK_AXIS_MAX;
1188 } else if (half_axis_output == '-') {
1189 bind.output.axis.axis_min = 0;
1190 bind.output.axis.axis_max = SDL_JOYSTICK_AXIS_MIN;
1191 } else {
1192 bind.output.axis.axis_min = SDL_JOYSTICK_AXIS_MIN;
1193 bind.output.axis.axis_max = SDL_JOYSTICK_AXIS_MAX;
1194 }
1195 }
1196 } else if (button != SDL_GAMEPAD_BUTTON_INVALID) {
1197 bind.output_type = SDL_GAMEPAD_BINDTYPE_BUTTON;
1198 bind.output.button = button;
1199 } else {
1200 return false;
1201 }
1202
1203 if (*szJoystickButton == '+' || *szJoystickButton == '-') {
1204 half_axis_input = *szJoystickButton++;
1205 }
1206 if (szJoystickButton[SDL_strlen(szJoystickButton) - 1] == '~') {
1207 invert_input = true;
1208 }
1209
1210 if (szJoystickButton[0] == 'a' && SDL_isdigit((unsigned char)szJoystickButton[1])) {
1211 bind.input_type = SDL_GAMEPAD_BINDTYPE_AXIS;
1212 bind.input.axis.axis = SDL_atoi(&szJoystickButton[1]);
1213 if (half_axis_input == '+') {
1214 bind.input.axis.axis_min = 0;
1215 bind.input.axis.axis_max = SDL_JOYSTICK_AXIS_MAX;
1216 } else if (half_axis_input == '-') {
1217 bind.input.axis.axis_min = 0;
1218 bind.input.axis.axis_max = SDL_JOYSTICK_AXIS_MIN;
1219 } else {
1220 bind.input.axis.axis_min = SDL_JOYSTICK_AXIS_MIN;
1221 bind.input.axis.axis_max = SDL_JOYSTICK_AXIS_MAX;
1222 }
1223 if (invert_input) {
1224 int tmp = bind.input.axis.axis_min;
1225 bind.input.axis.axis_min = bind.input.axis.axis_max;
1226 bind.input.axis.axis_max = tmp;
1227 }
1228 } else if (szJoystickButton[0] == 'b' && SDL_isdigit((unsigned char)szJoystickButton[1])) {
1229 bind.input_type = SDL_GAMEPAD_BINDTYPE_BUTTON;
1230 bind.input.button = SDL_atoi(&szJoystickButton[1]);
1231 } else if (szJoystickButton[0] == 'h' && SDL_isdigit((unsigned char)szJoystickButton[1]) &&
1232 szJoystickButton[2] == '.' && SDL_isdigit((unsigned char)szJoystickButton[3])) {
1233 int hat = SDL_atoi(&szJoystickButton[1]);
1234 int mask = SDL_atoi(&szJoystickButton[3]);
1235 bind.input_type = SDL_GAMEPAD_BINDTYPE_HAT;
1236 bind.input.hat.hat = hat;
1237 bind.input.hat.hat_mask = mask;
1238 } else {
1239 return false;
1240 }
1241
1242 for (i = 0; i < gamepad->num_bindings; ++i) {
1243 if (SDL_memcmp(&gamepad->bindings[i], &bind, sizeof(bind)) == 0) {
1244 // We already have this binding, could be different face button names?
1245 return true;
1246 }
1247 }
1248
1249 ++gamepad->num_bindings;
1250 new_bindings = (SDL_GamepadBinding *)SDL_realloc(gamepad->bindings, gamepad->num_bindings * sizeof(*gamepad->bindings));
1251 if (!new_bindings) {
1252 SDL_free(gamepad->bindings);
1253 gamepad->num_bindings = 0;
1254 gamepad->bindings = NULL;
1255 return false;
1256 }
1257 gamepad->bindings = new_bindings;
1258 gamepad->bindings[gamepad->num_bindings - 1] = bind;
1259 return true;
1260}
1261
1262/*
1263 * given a gamepad mapping string update our mapping object
1264 */
1265static bool SDL_PrivateParseGamepadConfigString(SDL_Gamepad *gamepad, const char *pchString)
1266{
1267 char szGameButton[20];
1268 char szJoystickButton[20];
1269 bool bGameButton = true;
1270 int i = 0;
1271 const char *pchPos = pchString;
1272
1273 SDL_zeroa(szGameButton);
1274 SDL_zeroa(szJoystickButton);
1275
1276 while (pchPos && *pchPos) {
1277 if (*pchPos == ':') {
1278 i = 0;
1279 bGameButton = false;
1280 } else if (*pchPos == ' ') {
1281
1282 } else if (*pchPos == ',') {
1283 i = 0;
1284 bGameButton = true;
1285 SDL_PrivateParseGamepadElement(gamepad, szGameButton, szJoystickButton);
1286 SDL_zeroa(szGameButton);
1287 SDL_zeroa(szJoystickButton);
1288
1289 } else if (bGameButton) {
1290 if (i >= sizeof(szGameButton)) {
1291 szGameButton[sizeof(szGameButton) - 1] = '\0';
1292 return SDL_SetError("Button name too large: %s", szGameButton);
1293 }
1294 szGameButton[i] = *pchPos;
1295 i++;
1296 } else {
1297 if (i >= sizeof(szJoystickButton)) {
1298 szJoystickButton[sizeof(szJoystickButton) - 1] = '\0';
1299 return SDL_SetError("Joystick button name too large: %s", szJoystickButton);
1300 }
1301 szJoystickButton[i] = *pchPos;
1302 i++;
1303 }
1304 pchPos++;
1305 }
1306
1307 // No more values if the string was terminated by a comma. Don't report an error.
1308 if (szGameButton[0] != '\0' || szJoystickButton[0] != '\0') {
1309 SDL_PrivateParseGamepadElement(gamepad, szGameButton, szJoystickButton);
1310 }
1311 return true;
1312}
1313
1314static void SDL_UpdateGamepadType(SDL_Gamepad *gamepad)
1315{
1316 char *type_string, *comma;
1317
1318 SDL_AssertJoysticksLocked();
1319
1320 gamepad->type = SDL_GAMEPAD_TYPE_UNKNOWN;
1321
1322 type_string = SDL_strstr(gamepad->mapping->mapping, SDL_GAMEPAD_TYPE_FIELD);
1323 if (type_string) {
1324 type_string += SDL_GAMEPAD_TYPE_FIELD_SIZE;
1325 comma = SDL_strchr(type_string, ',');
1326 if (comma) {
1327 *comma = '\0';
1328 gamepad->type = SDL_GetGamepadTypeFromString(type_string);
1329 *comma = ',';
1330 } else {
1331 gamepad->type = SDL_GetGamepadTypeFromString(type_string);
1332 }
1333 }
1334 if (gamepad->type == SDL_GAMEPAD_TYPE_UNKNOWN) {
1335 gamepad->type = SDL_GetRealGamepadTypeForID(gamepad->joystick->instance_id);
1336 }
1337}
1338
1339static SDL_GamepadFaceStyle SDL_GetGamepadFaceStyleFromString(const char *string)
1340{
1341 if (SDL_strcmp(string, "abxy") == 0) {
1342 return SDL_GAMEPAD_FACE_STYLE_ABXY;
1343 } else if (SDL_strcmp(string, "bayx") == 0) {
1344 return SDL_GAMEPAD_FACE_STYLE_BAYX;
1345 } else if (SDL_strcmp(string, "sony") == 0) {
1346 return SDL_GAMEPAD_FACE_STYLE_SONY;
1347 } else {
1348 return SDL_GAMEPAD_FACE_STYLE_UNKNOWN;
1349 }
1350}
1351
1352static SDL_GamepadFaceStyle SDL_GetGamepadFaceStyleForGamepadType(SDL_GamepadType type)
1353{
1354 switch (type) {
1355 case SDL_GAMEPAD_TYPE_PS3:
1356 case SDL_GAMEPAD_TYPE_PS4:
1357 case SDL_GAMEPAD_TYPE_PS5:
1358 return SDL_GAMEPAD_FACE_STYLE_SONY;
1359 case SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_PRO:
1360 case SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_JOYCON_LEFT:
1361 case SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_JOYCON_RIGHT:
1362 case SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_JOYCON_PAIR:
1363 return SDL_GAMEPAD_FACE_STYLE_BAYX;
1364 default:
1365 return SDL_GAMEPAD_FACE_STYLE_ABXY;
1366 }
1367}
1368
1369static void SDL_UpdateGamepadFaceStyle(SDL_Gamepad *gamepad)
1370{
1371 char *face_string, *comma;
1372
1373 SDL_AssertJoysticksLocked();
1374
1375 gamepad->face_style = SDL_GAMEPAD_FACE_STYLE_UNKNOWN;
1376
1377 face_string = SDL_strstr(gamepad->mapping->mapping, SDL_GAMEPAD_FACE_FIELD);
1378 if (face_string) {
1379 face_string += SDL_GAMEPAD_TYPE_FIELD_SIZE;
1380 comma = SDL_strchr(face_string, ',');
1381 if (comma) {
1382 *comma = '\0';
1383 gamepad->face_style = SDL_GetGamepadFaceStyleFromString(face_string);
1384 *comma = ',';
1385 } else {
1386 gamepad->face_style = SDL_GetGamepadFaceStyleFromString(face_string);
1387 }
1388 }
1389
1390 if (gamepad->face_style == SDL_GAMEPAD_FACE_STYLE_UNKNOWN &&
1391 SDL_strstr(gamepad->mapping->mapping, "SDL_GAMECONTROLLER_USE_BUTTON_LABELS") != NULL) {
1392 // This controller uses Nintendo button style
1393 gamepad->face_style = SDL_GAMEPAD_FACE_STYLE_BAYX;
1394 }
1395 if (gamepad->face_style == SDL_GAMEPAD_FACE_STYLE_UNKNOWN) {
1396 gamepad->face_style = SDL_GetGamepadFaceStyleForGamepadType(gamepad->type);
1397 }
1398}
1399
1400static void SDL_FixupHIDAPIMapping(SDL_Gamepad *gamepad)
1401{
1402 // Check to see if we need fixup
1403 bool need_fixup = false;
1404 for (int i = 0; i < gamepad->num_bindings; ++i) {
1405 SDL_GamepadBinding *binding = &gamepad->bindings[i];
1406 if (binding->output_type == SDL_GAMEPAD_BINDTYPE_BUTTON &&
1407 binding->output.button >= SDL_GAMEPAD_BUTTON_DPAD_UP) {
1408 if (binding->input_type == SDL_GAMEPAD_BINDTYPE_BUTTON &&
1409 binding->input.button == binding->output.button) {
1410 // Old style binding
1411 need_fixup = true;
1412 }
1413 break;
1414 }
1415 }
1416 if (!need_fixup) {
1417 return;
1418 }
1419
1420 for (int i = 0; i < gamepad->num_bindings; ++i) {
1421 SDL_GamepadBinding *binding = &gamepad->bindings[i];
1422 if (binding->input_type == SDL_GAMEPAD_BINDTYPE_BUTTON &&
1423 binding->output_type == SDL_GAMEPAD_BINDTYPE_BUTTON) {
1424 switch (binding->output.button) {
1425 case SDL_GAMEPAD_BUTTON_DPAD_UP:
1426 binding->input_type = SDL_GAMEPAD_BINDTYPE_HAT;
1427 binding->input.hat.hat = 0;
1428 binding->input.hat.hat_mask = SDL_HAT_UP;
1429 break;
1430 case SDL_GAMEPAD_BUTTON_DPAD_DOWN:
1431 binding->input_type = SDL_GAMEPAD_BINDTYPE_HAT;
1432 binding->input.hat.hat = 0;
1433 binding->input.hat.hat_mask = SDL_HAT_DOWN;
1434 break;
1435 case SDL_GAMEPAD_BUTTON_DPAD_LEFT:
1436 binding->input_type = SDL_GAMEPAD_BINDTYPE_HAT;
1437 binding->input.hat.hat = 0;
1438 binding->input.hat.hat_mask = SDL_HAT_LEFT;
1439 break;
1440 case SDL_GAMEPAD_BUTTON_DPAD_RIGHT:
1441 binding->input_type = SDL_GAMEPAD_BINDTYPE_HAT;
1442 binding->input.hat.hat = 0;
1443 binding->input.hat.hat_mask = SDL_HAT_RIGHT;
1444 break;
1445 default:
1446 if (binding->output.button > SDL_GAMEPAD_BUTTON_DPAD_RIGHT) {
1447 binding->input.button -= 4;
1448 }
1449 break;
1450 }
1451 }
1452 }
1453}
1454
1455/*
1456 * Make a new button mapping struct
1457 */
1458static void SDL_PrivateLoadButtonMapping(SDL_Gamepad *gamepad, GamepadMapping_t *pGamepadMapping)
1459{
1460 int i;
1461
1462 SDL_AssertJoysticksLocked();
1463
1464 gamepad->name = pGamepadMapping->name;
1465 gamepad->num_bindings = 0;
1466 gamepad->mapping = pGamepadMapping;
1467 if (gamepad->joystick->naxes != 0 && gamepad->last_match_axis) {
1468 SDL_memset(gamepad->last_match_axis, 0, gamepad->joystick->naxes * sizeof(*gamepad->last_match_axis));
1469 }
1470
1471 SDL_UpdateGamepadType(gamepad);
1472 SDL_UpdateGamepadFaceStyle(gamepad);
1473
1474 SDL_PrivateParseGamepadConfigString(gamepad, pGamepadMapping->mapping);
1475
1476 if (SDL_IsJoystickHIDAPI(pGamepadMapping->guid)) {
1477 SDL_FixupHIDAPIMapping(gamepad);
1478 }
1479
1480 // Set the zero point for triggers
1481 for (i = 0; i < gamepad->num_bindings; ++i) {
1482 SDL_GamepadBinding *binding = &gamepad->bindings[i];
1483 if (binding->input_type == SDL_GAMEPAD_BINDTYPE_AXIS &&
1484 binding->output_type == SDL_GAMEPAD_BINDTYPE_AXIS &&
1485 (binding->output.axis.axis == SDL_GAMEPAD_AXIS_LEFT_TRIGGER ||
1486 binding->output.axis.axis == SDL_GAMEPAD_AXIS_RIGHT_TRIGGER)) {
1487 if (binding->input.axis.axis < gamepad->joystick->naxes) {
1488 gamepad->joystick->axes[binding->input.axis.axis].value =
1489 gamepad->joystick->axes[binding->input.axis.axis].zero = (Sint16)binding->input.axis.axis_min;
1490 }
1491 }
1492 }
1493}
1494
1495/*
1496 * grab the guid string from a mapping string
1497 */
1498static char *SDL_PrivateGetGamepadGUIDFromMappingString(const char *pMapping)
1499{
1500 const char *pFirstComma = SDL_strchr(pMapping, ',');
1501 if (pFirstComma) {
1502 char *pchGUID = (char *)SDL_malloc(pFirstComma - pMapping + 1);
1503 if (!pchGUID) {
1504 return NULL;
1505 }
1506 SDL_memcpy(pchGUID, pMapping, pFirstComma - pMapping);
1507 pchGUID[pFirstComma - pMapping] = '\0';
1508
1509 // Convert old style GUIDs to the new style in 2.0.5
1510#if defined(SDL_PLATFORM_WIN32) || defined(SDL_PLATFORM_WINGDK)
1511 if (SDL_strlen(pchGUID) == 32 &&
1512 SDL_memcmp(&pchGUID[20], "504944564944", 12) == 0) {
1513 SDL_memcpy(&pchGUID[20], "000000000000", 12);
1514 SDL_memcpy(&pchGUID[16], &pchGUID[4], 4);
1515 SDL_memcpy(&pchGUID[8], &pchGUID[0], 4);
1516 SDL_memcpy(&pchGUID[0], "03000000", 8);
1517 }
1518#elif defined(SDL_PLATFORM_MACOS)
1519 if (SDL_strlen(pchGUID) == 32 &&
1520 SDL_memcmp(&pchGUID[4], "000000000000", 12) == 0 &&
1521 SDL_memcmp(&pchGUID[20], "000000000000", 12) == 0) {
1522 SDL_memcpy(&pchGUID[20], "000000000000", 12);
1523 SDL_memcpy(&pchGUID[8], &pchGUID[0], 4);
1524 SDL_memcpy(&pchGUID[0], "03000000", 8);
1525 }
1526#endif
1527 return pchGUID;
1528 }
1529 return NULL;
1530}
1531
1532/*
1533 * grab the name string from a mapping string
1534 */
1535static char *SDL_PrivateGetGamepadNameFromMappingString(const char *pMapping)
1536{
1537 const char *pFirstComma, *pSecondComma;
1538 char *pchName;
1539
1540 pFirstComma = SDL_strchr(pMapping, ',');
1541 if (!pFirstComma) {
1542 return NULL;
1543 }
1544
1545 pSecondComma = SDL_strchr(pFirstComma + 1, ',');
1546 if (!pSecondComma) {
1547 return NULL;
1548 }
1549
1550 pchName = (char *)SDL_malloc(pSecondComma - pFirstComma);
1551 if (!pchName) {
1552 return NULL;
1553 }
1554 SDL_memcpy(pchName, pFirstComma + 1, pSecondComma - pFirstComma);
1555 pchName[pSecondComma - pFirstComma - 1] = 0;
1556 return pchName;
1557}
1558
1559/*
1560 * grab the button mapping string from a mapping string
1561 */
1562static char *SDL_PrivateGetGamepadMappingFromMappingString(const char *pMapping)
1563{
1564 const char *pFirstComma, *pSecondComma;
1565 char *result;
1566 size_t length;
1567
1568 pFirstComma = SDL_strchr(pMapping, ',');
1569 if (!pFirstComma) {
1570 return NULL;
1571 }
1572
1573 pSecondComma = SDL_strchr(pFirstComma + 1, ',');
1574 if (!pSecondComma) {
1575 return NULL;
1576 }
1577
1578 // Skip whitespace
1579 while (SDL_isspace(pSecondComma[1])) {
1580 ++pSecondComma;
1581 }
1582
1583 result = SDL_strdup(pSecondComma + 1); // mapping is everything after the 3rd comma
1584
1585 // Trim whitespace
1586 length = SDL_strlen(result);
1587 while (length > 0 && SDL_isspace(result[length - 1])) {
1588 --length;
1589 }
1590 result[length] = '\0';
1591
1592 return result;
1593}
1594
1595/*
1596 * Helper function to add a mapping for a guid
1597 */
1598static GamepadMapping_t *SDL_PrivateAddMappingForGUID(SDL_GUID jGUID, const char *mappingString, bool *existing, SDL_GamepadMappingPriority priority)
1599{
1600 char *pchName;
1601 char *pchMapping;
1602 GamepadMapping_t *pGamepadMapping;
1603 Uint16 crc;
1604
1605 SDL_AssertJoysticksLocked();
1606
1607 pchName = SDL_PrivateGetGamepadNameFromMappingString(mappingString);
1608 if (!pchName) {
1609 SDL_SetError("Couldn't parse name from %s", mappingString);
1610 return NULL;
1611 }
1612
1613 pchMapping = SDL_PrivateGetGamepadMappingFromMappingString(mappingString);
1614 if (!pchMapping) {
1615 SDL_free(pchName);
1616 SDL_SetError("Couldn't parse %s", mappingString);
1617 return NULL;
1618 }
1619
1620 // Fix up the GUID and the mapping with the CRC, if needed
1621 SDL_GetJoystickGUIDInfo(jGUID, NULL, NULL, NULL, &crc);
1622 if (crc) {
1623 // Make sure the mapping has the CRC
1624 char *new_mapping;
1625 const char *optional_comma;
1626 size_t mapping_length;
1627 char *crc_end = "";
1628 char *crc_string = SDL_strstr(pchMapping, SDL_GAMEPAD_CRC_FIELD);
1629 if (crc_string) {
1630 crc_end = SDL_strchr(crc_string, ',');
1631 if (crc_end) {
1632 ++crc_end;
1633 } else {
1634 crc_end = "";
1635 }
1636 *crc_string = '\0';
1637 }
1638
1639 // Make sure there's a comma before the CRC
1640 mapping_length = SDL_strlen(pchMapping);
1641 if (mapping_length == 0 || pchMapping[mapping_length - 1] == ',') {
1642 optional_comma = "";
1643 } else {
1644 optional_comma = ",";
1645 }
1646
1647 if (SDL_asprintf(&new_mapping, "%s%s%s%.4x,%s", pchMapping, optional_comma, SDL_GAMEPAD_CRC_FIELD, crc, crc_end) >= 0) {
1648 SDL_free(pchMapping);
1649 pchMapping = new_mapping;
1650 }
1651 } else {
1652 // Make sure the GUID has the CRC, for matching purposes
1653 char *crc_string = SDL_strstr(pchMapping, SDL_GAMEPAD_CRC_FIELD);
1654 if (crc_string) {
1655 crc = (Uint16)SDL_strtol(crc_string + SDL_GAMEPAD_CRC_FIELD_SIZE, NULL, 16);
1656 if (crc) {
1657 SDL_SetJoystickGUIDCRC(&jGUID, crc);
1658 }
1659 }
1660 }
1661
1662 PushMappingChangeTracking();
1663
1664 pGamepadMapping = SDL_PrivateGetGamepadMappingForGUID(jGUID, true);
1665 if (pGamepadMapping) {
1666 // Only overwrite the mapping if the priority is the same or higher.
1667 if (pGamepadMapping->priority <= priority) {
1668 // Update existing mapping
1669 SDL_free(pGamepadMapping->name);
1670 pGamepadMapping->name = pchName;
1671 SDL_free(pGamepadMapping->mapping);
1672 pGamepadMapping->mapping = pchMapping;
1673 pGamepadMapping->priority = priority;
1674 } else {
1675 SDL_free(pchName);
1676 SDL_free(pchMapping);
1677 }
1678 if (existing) {
1679 *existing = true;
1680 }
1681 AddMappingChangeTracking(pGamepadMapping);
1682 } else {
1683 pGamepadMapping = (GamepadMapping_t *)SDL_malloc(sizeof(*pGamepadMapping));
1684 if (!pGamepadMapping) {
1685 PopMappingChangeTracking();
1686 SDL_free(pchName);
1687 SDL_free(pchMapping);
1688 return NULL;
1689 }
1690 // Clear the CRC, we've already added it to the mapping
1691 if (crc) {
1692 SDL_SetJoystickGUIDCRC(&jGUID, 0);
1693 }
1694 pGamepadMapping->guid = jGUID;
1695 pGamepadMapping->name = pchName;
1696 pGamepadMapping->mapping = pchMapping;
1697 pGamepadMapping->next = NULL;
1698 pGamepadMapping->priority = priority;
1699
1700 if (s_pSupportedGamepads) {
1701 // Add the mapping to the end of the list
1702 GamepadMapping_t *pCurrMapping, *pPrevMapping;
1703
1704 for (pPrevMapping = s_pSupportedGamepads, pCurrMapping = pPrevMapping->next;
1705 pCurrMapping;
1706 pPrevMapping = pCurrMapping, pCurrMapping = pCurrMapping->next) {
1707 // continue;
1708 }
1709 pPrevMapping->next = pGamepadMapping;
1710 } else {
1711 s_pSupportedGamepads = pGamepadMapping;
1712 }
1713 if (existing) {
1714 *existing = false;
1715 }
1716 }
1717
1718 PopMappingChangeTracking();
1719
1720 return pGamepadMapping;
1721}
1722
1723/*
1724 * Helper function to determine pre-calculated offset to certain joystick mappings
1725 */
1726static GamepadMapping_t *SDL_PrivateGetGamepadMappingForNameAndGUID(const char *name, SDL_GUID guid)
1727{
1728 GamepadMapping_t *mapping;
1729
1730 SDL_AssertJoysticksLocked();
1731
1732 mapping = SDL_PrivateGetGamepadMappingForGUID(guid, false);
1733#ifdef SDL_PLATFORM_LINUX
1734 if (!mapping && name) {
1735 if (SDL_strstr(name, "Xbox 360 Wireless Receiver")) {
1736 // The Linux driver xpad.c maps the wireless dpad to buttons
1737 bool existing;
1738 mapping = SDL_PrivateAddMappingForGUID(guid,
1739 "none,X360 Wireless Controller,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b11,dpright:b12,dpup:b13,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
1740 &existing, SDL_GAMEPAD_MAPPING_PRIORITY_DEFAULT);
1741 }
1742 }
1743#endif // SDL_PLATFORM_LINUX
1744
1745 return mapping;
1746}
1747
1748static void SDL_PrivateAppendToMappingString(char *mapping_string,
1749 size_t mapping_string_len,
1750 const char *input_name,
1751 SDL_InputMapping *mapping)
1752{
1753 char buffer[16];
1754 if (mapping->kind == EMappingKind_None) {
1755 return;
1756 }
1757
1758 SDL_strlcat(mapping_string, input_name, mapping_string_len);
1759 SDL_strlcat(mapping_string, ":", mapping_string_len);
1760 switch (mapping->kind) {
1761 case EMappingKind_Button:
1762 (void)SDL_snprintf(buffer, sizeof(buffer), "b%u", mapping->target);
1763 break;
1764 case EMappingKind_Axis:
1765 (void)SDL_snprintf(buffer, sizeof(buffer), "%sa%u%s",
1766 mapping->half_axis_positive ? "+" :
1767 mapping->half_axis_negative ? "-" : "",
1768 mapping->target,
1769 mapping->axis_reversed ? "~" : "");
1770 break;
1771 case EMappingKind_Hat:
1772 (void)SDL_snprintf(buffer, sizeof(buffer), "h%i.%i", mapping->target >> 4, mapping->target & 0x0F);
1773 break;
1774 default:
1775 SDL_assert(false);
1776 }
1777
1778 SDL_strlcat(mapping_string, buffer, mapping_string_len);
1779 SDL_strlcat(mapping_string, ",", mapping_string_len);
1780}
1781
1782static GamepadMapping_t *SDL_PrivateGenerateAutomaticGamepadMapping(const char *name,
1783 SDL_GUID guid,
1784 SDL_GamepadMapping *raw_map)
1785{
1786 bool existing;
1787 char name_string[128];
1788 char mapping[1024];
1789
1790 // Remove any commas in the name
1791 SDL_strlcpy(name_string, name, sizeof(name_string));
1792 {
1793 char *spot;
1794 for (spot = name_string; *spot; ++spot) {
1795 if (*spot == ',') {
1796 *spot = ' ';
1797 }
1798 }
1799 }
1800 (void)SDL_snprintf(mapping, sizeof(mapping), "none,%s,", name_string);
1801 SDL_PrivateAppendToMappingString(mapping, sizeof(mapping), "a", &raw_map->a);
1802 SDL_PrivateAppendToMappingString(mapping, sizeof(mapping), "b", &raw_map->b);
1803 SDL_PrivateAppendToMappingString(mapping, sizeof(mapping), "x", &raw_map->x);
1804 SDL_PrivateAppendToMappingString(mapping, sizeof(mapping), "y", &raw_map->y);
1805 SDL_PrivateAppendToMappingString(mapping, sizeof(mapping), "back", &raw_map->back);
1806 SDL_PrivateAppendToMappingString(mapping, sizeof(mapping), "guide", &raw_map->guide);
1807 SDL_PrivateAppendToMappingString(mapping, sizeof(mapping), "start", &raw_map->start);
1808 SDL_PrivateAppendToMappingString(mapping, sizeof(mapping), "leftstick", &raw_map->leftstick);
1809 SDL_PrivateAppendToMappingString(mapping, sizeof(mapping), "rightstick", &raw_map->rightstick);
1810 SDL_PrivateAppendToMappingString(mapping, sizeof(mapping), "leftshoulder", &raw_map->leftshoulder);
1811 SDL_PrivateAppendToMappingString(mapping, sizeof(mapping), "rightshoulder", &raw_map->rightshoulder);
1812 SDL_PrivateAppendToMappingString(mapping, sizeof(mapping), "dpup", &raw_map->dpup);
1813 SDL_PrivateAppendToMappingString(mapping, sizeof(mapping), "dpdown", &raw_map->dpdown);
1814 SDL_PrivateAppendToMappingString(mapping, sizeof(mapping), "dpleft", &raw_map->dpleft);
1815 SDL_PrivateAppendToMappingString(mapping, sizeof(mapping), "dpright", &raw_map->dpright);
1816 SDL_PrivateAppendToMappingString(mapping, sizeof(mapping), "misc1", &raw_map->misc1);
1817 SDL_PrivateAppendToMappingString(mapping, sizeof(mapping), "misc2", &raw_map->misc2);
1818 SDL_PrivateAppendToMappingString(mapping, sizeof(mapping), "misc3", &raw_map->misc3);
1819 SDL_PrivateAppendToMappingString(mapping, sizeof(mapping), "misc4", &raw_map->misc4);
1820 SDL_PrivateAppendToMappingString(mapping, sizeof(mapping), "misc5", &raw_map->misc5);
1821 SDL_PrivateAppendToMappingString(mapping, sizeof(mapping), "misc6", &raw_map->misc6);
1822 /* Keep using paddle1-4 in the generated mapping so that it can be
1823 * reused with SDL2 */
1824 SDL_PrivateAppendToMappingString(mapping, sizeof(mapping), "paddle1", &raw_map->right_paddle1);
1825 SDL_PrivateAppendToMappingString(mapping, sizeof(mapping), "paddle2", &raw_map->left_paddle1);
1826 SDL_PrivateAppendToMappingString(mapping, sizeof(mapping), "paddle3", &raw_map->right_paddle2);
1827 SDL_PrivateAppendToMappingString(mapping, sizeof(mapping), "paddle4", &raw_map->left_paddle2);
1828 SDL_PrivateAppendToMappingString(mapping, sizeof(mapping), "leftx", &raw_map->leftx);
1829 SDL_PrivateAppendToMappingString(mapping, sizeof(mapping), "lefty", &raw_map->lefty);
1830 SDL_PrivateAppendToMappingString(mapping, sizeof(mapping), "rightx", &raw_map->rightx);
1831 SDL_PrivateAppendToMappingString(mapping, sizeof(mapping), "righty", &raw_map->righty);
1832 SDL_PrivateAppendToMappingString(mapping, sizeof(mapping), "lefttrigger", &raw_map->lefttrigger);
1833 SDL_PrivateAppendToMappingString(mapping, sizeof(mapping), "righttrigger", &raw_map->righttrigger);
1834 SDL_PrivateAppendToMappingString(mapping, sizeof(mapping), "touchpad", &raw_map->touchpad);
1835
1836 return SDL_PrivateAddMappingForGUID(guid, mapping, &existing, SDL_GAMEPAD_MAPPING_PRIORITY_DEFAULT);
1837}
1838
1839static GamepadMapping_t *SDL_PrivateGetGamepadMapping(SDL_JoystickID instance_id, bool create_mapping)
1840{
1841 const char *name;
1842 SDL_GUID guid;
1843 GamepadMapping_t *mapping;
1844
1845 SDL_AssertJoysticksLocked();
1846
1847 name = SDL_GetJoystickNameForID(instance_id);
1848 guid = SDL_GetJoystickGUIDForID(instance_id);
1849 mapping = SDL_PrivateGetGamepadMappingForNameAndGUID(name, guid);
1850 if (!mapping && create_mapping) {
1851 SDL_GamepadMapping raw_map;
1852
1853 SDL_zero(raw_map);
1854 if (SDL_PrivateJoystickGetAutoGamepadMapping(instance_id, &raw_map)) {
1855 mapping = SDL_PrivateGenerateAutomaticGamepadMapping(name, guid, &raw_map);
1856 }
1857 }
1858
1859 if (!mapping) {
1860 mapping = s_pDefaultMapping;
1861 }
1862 return mapping;
1863}
1864
1865/*
1866 * Add or update an entry into the Mappings Database
1867 */
1868int SDL_AddGamepadMappingsFromIO(SDL_IOStream *src, bool closeio)
1869{
1870 const char *platform = SDL_GetPlatform();
1871 int gamepads = 0;
1872 char *buf, *line, *line_end, *tmp, *comma, line_platform[64];
1873 size_t db_size;
1874 size_t platform_len;
1875
1876 buf = (char *)SDL_LoadFile_IO(src, &db_size, closeio);
1877 if (!buf) {
1878 SDL_SetError("Could not allocate space to read DB into memory");
1879 return -1;
1880 }
1881 line = buf;
1882
1883 SDL_LockJoysticks();
1884
1885 PushMappingChangeTracking();
1886
1887 while (line < buf + db_size) {
1888 line_end = SDL_strchr(line, '\n');
1889 if (line_end) {
1890 *line_end = '\0';
1891 } else {
1892 line_end = buf + db_size;
1893 }
1894
1895 // Extract and verify the platform
1896 tmp = SDL_strstr(line, SDL_GAMEPAD_PLATFORM_FIELD);
1897 if (tmp) {
1898 tmp += SDL_GAMEPAD_PLATFORM_FIELD_SIZE;
1899 comma = SDL_strchr(tmp, ',');
1900 if (comma) {
1901 platform_len = comma - tmp + 1;
1902 if (platform_len + 1 < SDL_arraysize(line_platform)) {
1903 SDL_strlcpy(line_platform, tmp, platform_len);
1904 if (SDL_strncasecmp(line_platform, platform, platform_len) == 0 &&
1905 SDL_AddGamepadMapping(line) > 0) {
1906 gamepads++;
1907 }
1908 }
1909 }
1910 }
1911
1912 line = line_end + 1;
1913 }
1914
1915 PopMappingChangeTracking();
1916
1917 SDL_UnlockJoysticks();
1918
1919 SDL_free(buf);
1920 return gamepads;
1921}
1922
1923int SDL_AddGamepadMappingsFromFile(const char *file)
1924{
1925 return SDL_AddGamepadMappingsFromIO(SDL_IOFromFile(file, "rb"), true);
1926}
1927
1928bool SDL_ReloadGamepadMappings(void)
1929{
1930 SDL_Gamepad *gamepad;
1931
1932 SDL_LockJoysticks();
1933
1934 PushMappingChangeTracking();
1935
1936 for (gamepad = SDL_gamepads; gamepad; gamepad = gamepad->next) {
1937 AddMappingChangeTracking(gamepad->mapping);
1938 }
1939
1940 SDL_QuitGamepadMappings();
1941 SDL_InitGamepadMappings();
1942
1943 PopMappingChangeTracking();
1944
1945 SDL_UnlockJoysticks();
1946
1947 return true;
1948}
1949
1950static char *SDL_ConvertMappingToPositional(const char *mapping)
1951{
1952 // Add space for '!' and null terminator
1953 size_t length = SDL_strlen(mapping) + 1 + 1;
1954 char *remapped = (char *)SDL_malloc(length);
1955 if (remapped) {
1956 char *button_A;
1957 char *button_B;
1958 char *button_X;
1959 char *button_Y;
1960 char *hint;
1961
1962 SDL_strlcpy(remapped, mapping, length);
1963 button_A = SDL_strstr(remapped, "a:");
1964 button_B = SDL_strstr(remapped, "b:");
1965 button_X = SDL_strstr(remapped, "x:");
1966 button_Y = SDL_strstr(remapped, "y:");
1967 hint = SDL_strstr(remapped, "hint:SDL_GAMECONTROLLER_USE_BUTTON_LABELS");
1968
1969 if (button_A) {
1970 *button_A = 'b';
1971 }
1972 if (button_B) {
1973 *button_B = 'a';
1974 }
1975 if (button_X) {
1976 *button_X = 'y';
1977 }
1978 if (button_Y) {
1979 *button_Y = 'x';
1980 }
1981 if (hint) {
1982 hint += 5;
1983 SDL_memmove(hint + 1, hint, SDL_strlen(hint) + 1);
1984 *hint = '!';
1985 }
1986 }
1987 return remapped;
1988}
1989
1990/*
1991 * Add or update an entry into the Mappings Database with a priority
1992 */
1993static int SDL_PrivateAddGamepadMapping(const char *mappingString, SDL_GamepadMappingPriority priority)
1994{
1995 char *remapped = NULL;
1996 char *pchGUID;
1997 SDL_GUID jGUID;
1998 bool is_default_mapping = false;
1999 bool is_xinput_mapping = false;
2000 bool existing = false;
2001 GamepadMapping_t *pGamepadMapping;
2002 int result = -1;
2003
2004 SDL_AssertJoysticksLocked();
2005
2006 if (!mappingString) {
2007 SDL_InvalidParamError("mappingString");
2008 return -1;
2009 }
2010
2011 { // Extract and verify the hint field
2012 const char *tmp;
2013
2014 tmp = SDL_strstr(mappingString, SDL_GAMEPAD_HINT_FIELD);
2015 if (tmp) {
2016 bool default_value, value, negate;
2017 int len;
2018 char hint[128];
2019
2020 tmp += SDL_GAMEPAD_HINT_FIELD_SIZE;
2021
2022 if (*tmp == '!') {
2023 negate = true;
2024 ++tmp;
2025 } else {
2026 negate = false;
2027 }
2028
2029 len = 0;
2030 while (*tmp && *tmp != ',' && *tmp != ':' && len < (sizeof(hint) - 1)) {
2031 hint[len++] = *tmp++;
2032 }
2033 hint[len] = '\0';
2034
2035 if (tmp[0] == ':' && tmp[1] == '=') {
2036 tmp += 2;
2037 default_value = SDL_atoi(tmp);
2038 } else {
2039 default_value = false;
2040 }
2041
2042 if (SDL_strcmp(hint, "SDL_GAMECONTROLLER_USE_BUTTON_LABELS") == 0) {
2043 // This hint is used to signal whether the mapping uses positional buttons or not
2044 if (negate) {
2045 // This mapping uses positional buttons, we can use it as-is
2046 } else {
2047 // This mapping uses labeled buttons, we need to swap them to positional
2048 remapped = SDL_ConvertMappingToPositional(mappingString);
2049 if (!remapped) {
2050 goto done;
2051 }
2052 mappingString = remapped;
2053 }
2054 } else {
2055 value = SDL_GetHintBoolean(hint, default_value);
2056 if (negate) {
2057 value = !value;
2058 }
2059 if (!value) {
2060 result = 0;
2061 goto done;
2062 }
2063 }
2064 }
2065 }
2066
2067#ifdef ANDROID
2068 { // Extract and verify the SDK version
2069 const char *tmp;
2070
2071 tmp = SDL_strstr(mappingString, SDL_GAMEPAD_SDKGE_FIELD);
2072 if (tmp) {
2073 tmp += SDL_GAMEPAD_SDKGE_FIELD_SIZE;
2074 if (!(SDL_GetAndroidSDKVersion() >= SDL_atoi(tmp))) {
2075 SDL_SetError("SDK version %d < minimum version %d", SDL_GetAndroidSDKVersion(), SDL_atoi(tmp));
2076 goto done;
2077 }
2078 }
2079 tmp = SDL_strstr(mappingString, SDL_GAMEPAD_SDKLE_FIELD);
2080 if (tmp) {
2081 tmp += SDL_GAMEPAD_SDKLE_FIELD_SIZE;
2082 if (!(SDL_GetAndroidSDKVersion() <= SDL_atoi(tmp))) {
2083 SDL_SetError("SDK version %d > maximum version %d", SDL_GetAndroidSDKVersion(), SDL_atoi(tmp));
2084 goto done;
2085 }
2086 }
2087 }
2088#endif
2089
2090 pchGUID = SDL_PrivateGetGamepadGUIDFromMappingString(mappingString);
2091 if (!pchGUID) {
2092 SDL_SetError("Couldn't parse GUID from %s", mappingString);
2093 goto done;
2094 }
2095 if (!SDL_strcasecmp(pchGUID, "default")) {
2096 is_default_mapping = true;
2097 } else if (!SDL_strcasecmp(pchGUID, "xinput")) {
2098 is_xinput_mapping = true;
2099 }
2100 jGUID = SDL_StringToGUID(pchGUID);
2101 SDL_free(pchGUID);
2102
2103 pGamepadMapping = SDL_PrivateAddMappingForGUID(jGUID, mappingString, &existing, priority);
2104 if (!pGamepadMapping) {
2105 goto done;
2106 }
2107
2108 if (existing) {
2109 result = 0;
2110 } else {
2111 if (is_default_mapping) {
2112 s_pDefaultMapping = pGamepadMapping;
2113 } else if (is_xinput_mapping) {
2114 s_pXInputMapping = pGamepadMapping;
2115 }
2116 result = 1;
2117 }
2118done:
2119 if (remapped) {
2120 SDL_free(remapped);
2121 }
2122 return result;
2123}
2124
2125/*
2126 * Add or update an entry into the Mappings Database
2127 */
2128int SDL_AddGamepadMapping(const char *mapping)
2129{
2130 int result;
2131
2132 SDL_LockJoysticks();
2133 {
2134 result = SDL_PrivateAddGamepadMapping(mapping, SDL_GAMEPAD_MAPPING_PRIORITY_API);
2135 }
2136 SDL_UnlockJoysticks();
2137
2138 return result;
2139}
2140
2141/*
2142 * Create a mapping string for a mapping
2143 */
2144static char *CreateMappingString(GamepadMapping_t *mapping, SDL_GUID guid)
2145{
2146 char *pMappingString, *pPlatformString;
2147 char pchGUID[33];
2148 size_t needed;
2149 bool need_platform = false;
2150 const char *platform = NULL;
2151
2152 SDL_AssertJoysticksLocked();
2153
2154 SDL_GUIDToString(guid, pchGUID, sizeof(pchGUID));
2155
2156 // allocate enough memory for GUID + ',' + name + ',' + mapping + \0
2157 needed = SDL_strlen(pchGUID) + 1 + SDL_strlen(mapping->name) + 1 + SDL_strlen(mapping->mapping) + 1;
2158
2159 if (!SDL_strstr(mapping->mapping, SDL_GAMEPAD_PLATFORM_FIELD)) {
2160 // add memory for ',' + platform:PLATFORM
2161 need_platform = true;
2162 if (mapping->mapping[SDL_strlen(mapping->mapping) - 1] != ',') {
2163 needed += 1;
2164 }
2165 platform = SDL_GetPlatform();
2166 needed += SDL_GAMEPAD_PLATFORM_FIELD_SIZE + SDL_strlen(platform) + 1;
2167 }
2168
2169 pMappingString = (char *)SDL_malloc(needed);
2170 if (!pMappingString) {
2171 return NULL;
2172 }
2173
2174 (void)SDL_snprintf(pMappingString, needed, "%s,%s,%s", pchGUID, mapping->name, mapping->mapping);
2175
2176 if (need_platform) {
2177 if (mapping->mapping[SDL_strlen(mapping->mapping) - 1] != ',') {
2178 SDL_strlcat(pMappingString, ",", needed);
2179 }
2180 SDL_strlcat(pMappingString, SDL_GAMEPAD_PLATFORM_FIELD, needed);
2181 SDL_strlcat(pMappingString, platform, needed);
2182 SDL_strlcat(pMappingString, ",", needed);
2183 }
2184
2185 // Make sure multiple platform strings haven't made their way into the mapping
2186 pPlatformString = SDL_strstr(pMappingString, SDL_GAMEPAD_PLATFORM_FIELD);
2187 if (pPlatformString) {
2188 pPlatformString = SDL_strstr(pPlatformString + 1, SDL_GAMEPAD_PLATFORM_FIELD);
2189 if (pPlatformString) {
2190 *pPlatformString = '\0';
2191 }
2192 }
2193 return pMappingString;
2194}
2195
2196char **SDL_GetGamepadMappings(int *count)
2197{
2198 int num_mappings = 0;
2199 char **result = NULL;
2200 char **mappings = NULL;
2201
2202 if (count) {
2203 *count = 0;
2204 }
2205
2206 SDL_LockJoysticks();
2207
2208 for (GamepadMapping_t *mapping = s_pSupportedGamepads; mapping; mapping = mapping->next) {
2209 if (SDL_memcmp(&mapping->guid, &s_zeroGUID, sizeof(mapping->guid)) == 0) {
2210 continue;
2211 }
2212 num_mappings++;
2213 }
2214
2215 size_t final_allocation = sizeof (char *); // for the NULL terminator element.
2216 bool failed = false;
2217 mappings = (char **) SDL_calloc(num_mappings + 1, sizeof (char *));
2218 if (!mappings) {
2219 failed = true;
2220 } else {
2221 int i = 0;
2222 for (GamepadMapping_t *mapping = s_pSupportedGamepads; mapping; mapping = mapping->next) {
2223 if (SDL_memcmp(&mapping->guid, &s_zeroGUID, sizeof(mapping->guid)) == 0) {
2224 continue;
2225 }
2226
2227 char *mappingstr = CreateMappingString(mapping, mapping->guid);
2228 if (!mappingstr) {
2229 failed = true;
2230 break; // error string is already set.
2231 }
2232
2233 SDL_assert(i < num_mappings);
2234 mappings[i++] = mappingstr;
2235
2236 final_allocation += SDL_strlen(mappingstr) + 1 + sizeof (char *);
2237 }
2238 }
2239
2240 SDL_UnlockJoysticks();
2241
2242 if (!failed) {
2243 result = (char **) SDL_malloc(final_allocation);
2244 if (result) {
2245 final_allocation -= (sizeof (char *) * num_mappings + 1);
2246 char *strptr = (char *) (result + (num_mappings + 1));
2247 for (int i = 0; i < num_mappings; i++) {
2248 result[i] = strptr;
2249 const size_t slen = SDL_strlcpy(strptr, mappings[i], final_allocation) + 1;
2250 SDL_assert(final_allocation >= slen);
2251 final_allocation -= slen;
2252 strptr += slen;
2253 }
2254 result[num_mappings] = NULL;
2255
2256 if (count) {
2257 *count = num_mappings;
2258 }
2259 }
2260 }
2261
2262 if (mappings) {
2263 for (int i = 0; i < num_mappings; i++) {
2264 SDL_free(mappings[i]);
2265 }
2266 SDL_free(mappings);
2267 }
2268
2269 return result;
2270}
2271
2272/*
2273 * Get the mapping string for this GUID
2274 */
2275char *SDL_GetGamepadMappingForGUID(SDL_GUID guid)
2276{
2277 char *result;
2278
2279 SDL_LockJoysticks();
2280 {
2281 GamepadMapping_t *mapping = SDL_PrivateGetGamepadMappingForGUID(guid, false);
2282 if (mapping) {
2283 result = CreateMappingString(mapping, guid);
2284 } else {
2285 SDL_SetError("Mapping not available");
2286 result = NULL;
2287 }
2288 }
2289 SDL_UnlockJoysticks();
2290
2291 return result;
2292}
2293
2294/*
2295 * Get the mapping string for this device
2296 */
2297char *SDL_GetGamepadMapping(SDL_Gamepad *gamepad)
2298{
2299 char *result;
2300
2301 SDL_LockJoysticks();
2302 {
2303 CHECK_GAMEPAD_MAGIC(gamepad, NULL);
2304
2305 result = CreateMappingString(gamepad->mapping, gamepad->joystick->guid);
2306 }
2307 SDL_UnlockJoysticks();
2308
2309 return result;
2310}
2311
2312/*
2313 * Set the mapping string for this device
2314 */
2315bool SDL_SetGamepadMapping(SDL_JoystickID instance_id, const char *mapping)
2316{
2317 SDL_GUID guid = SDL_GetJoystickGUIDForID(instance_id);
2318 bool result = false;
2319
2320 if (SDL_memcmp(&guid, &s_zeroGUID, sizeof(guid)) == 0) {
2321 return SDL_InvalidParamError("instance_id");
2322 }
2323
2324 if (!mapping) {
2325 mapping = "*,*,";
2326 }
2327
2328 SDL_LockJoysticks();
2329 {
2330 if (SDL_PrivateAddMappingForGUID(guid, mapping, NULL, SDL_GAMEPAD_MAPPING_PRIORITY_API)) {
2331 result = true;
2332 }
2333 }
2334 SDL_UnlockJoysticks();
2335
2336 return result;
2337}
2338
2339static void SDL_LoadGamepadHints(void)
2340{
2341 const char *hint = SDL_GetHint(SDL_HINT_GAMECONTROLLERCONFIG);
2342 if (hint && hint[0]) {
2343 char *pTempMappings = SDL_strdup(hint);
2344 char *pUserMappings = pTempMappings;
2345
2346 PushMappingChangeTracking();
2347
2348 while (pUserMappings) {
2349 char *pchNewLine = NULL;
2350
2351 pchNewLine = SDL_strchr(pUserMappings, '\n');
2352 if (pchNewLine) {
2353 *pchNewLine = '\0';
2354 }
2355
2356 SDL_PrivateAddGamepadMapping(pUserMappings, SDL_GAMEPAD_MAPPING_PRIORITY_USER);
2357
2358 if (pchNewLine) {
2359 pUserMappings = pchNewLine + 1;
2360 } else {
2361 pUserMappings = NULL;
2362 }
2363 }
2364
2365 PopMappingChangeTracking();
2366
2367 SDL_free(pTempMappings);
2368 }
2369}
2370
2371/*
2372 * Fill the given buffer with the expected gamepad mapping filepath.
2373 * Usually this will just be SDL_HINT_GAMECONTROLLERCONFIG_FILE, but for
2374 * Android, we want to get the internal storage path.
2375 */
2376static bool SDL_GetGamepadMappingFilePath(char *path, size_t size)
2377{
2378 const char *hint = SDL_GetHint(SDL_HINT_GAMECONTROLLERCONFIG_FILE);
2379 if (hint && *hint) {
2380 return SDL_strlcpy(path, hint, size) < size;
2381 }
2382
2383#ifdef SDL_PLATFORM_ANDROID
2384 return SDL_snprintf(path, size, "%s/gamepad_map.txt", SDL_GetAndroidInternalStoragePath()) < size;
2385#else
2386 return false;
2387#endif
2388}
2389
2390/*
2391 * Initialize the gamepad system, mostly load our DB of gamepad config mappings
2392 */
2393bool SDL_InitGamepadMappings(void)
2394{
2395 char szGamepadMapPath[1024];
2396 int i = 0;
2397 const char *pMappingString = NULL;
2398
2399 SDL_AssertJoysticksLocked();
2400
2401 PushMappingChangeTracking();
2402
2403 pMappingString = s_GamepadMappings[i];
2404 while (pMappingString) {
2405 SDL_PrivateAddGamepadMapping(pMappingString, SDL_GAMEPAD_MAPPING_PRIORITY_DEFAULT);
2406
2407 i++;
2408 pMappingString = s_GamepadMappings[i];
2409 }
2410
2411 if (SDL_GetGamepadMappingFilePath(szGamepadMapPath, sizeof(szGamepadMapPath))) {
2412 SDL_AddGamepadMappingsFromFile(szGamepadMapPath);
2413 }
2414
2415 // load in any user supplied config
2416 SDL_LoadGamepadHints();
2417
2418 SDL_LoadVIDPIDList(&SDL_allowed_gamepads);
2419 SDL_LoadVIDPIDList(&SDL_ignored_gamepads);
2420
2421 PopMappingChangeTracking();
2422
2423 return true;
2424}
2425
2426bool SDL_InitGamepads(void)
2427{
2428 int i;
2429 SDL_JoystickID *joysticks;
2430
2431 SDL_gamepads_initialized = true;
2432
2433 // Watch for joystick events and fire gamepad ones if needed
2434 SDL_AddEventWatch(SDL_GamepadEventWatcher, NULL);
2435
2436 // Send added events for gamepads currently attached
2437 joysticks = SDL_GetJoysticks(NULL);
2438 if (joysticks) {
2439 for (i = 0; joysticks[i]; ++i) {
2440 if (SDL_IsGamepad(joysticks[i])) {
2441 SDL_PrivateGamepadAdded(joysticks[i]);
2442 }
2443 }
2444 SDL_free(joysticks);
2445 }
2446
2447 return true;
2448}
2449
2450bool SDL_HasGamepad(void)
2451{
2452 int num_joysticks = 0;
2453 int num_gamepads = 0;
2454 SDL_JoystickID *joysticks = SDL_GetJoysticks(&num_joysticks);
2455 if (joysticks) {
2456 int i;
2457 for (i = num_joysticks - 1; i >= 0 && num_gamepads == 0; --i) {
2458 if (SDL_IsGamepad(joysticks[i])) {
2459 ++num_gamepads;
2460 }
2461 }
2462 SDL_free(joysticks);
2463 }
2464 if (num_gamepads > 0) {
2465 return true;
2466 }
2467 return false;
2468}
2469
2470SDL_JoystickID *SDL_GetGamepads(int *count)
2471{
2472 int num_joysticks = 0;
2473 int num_gamepads = 0;
2474 SDL_JoystickID *joysticks = SDL_GetJoysticks(&num_joysticks);
2475 if (joysticks) {
2476 int i;
2477 for (i = num_joysticks - 1; i >= 0; --i) {
2478 if (SDL_IsGamepad(joysticks[i])) {
2479 ++num_gamepads;
2480 } else {
2481 SDL_memmove(&joysticks[i], &joysticks[i+1], (num_gamepads + 1) * sizeof(joysticks[i]));
2482 }
2483 }
2484 }
2485 if (count) {
2486 *count = num_gamepads;
2487 }
2488 return joysticks;
2489}
2490
2491const char *SDL_GetGamepadNameForID(SDL_JoystickID instance_id)
2492{
2493 const char *result = NULL;
2494
2495 SDL_LockJoysticks();
2496 {
2497 GamepadMapping_t *mapping = SDL_PrivateGetGamepadMapping(instance_id, true);
2498 if (mapping) {
2499 if (SDL_strcmp(mapping->name, "*") == 0) {
2500 result = SDL_GetJoystickNameForID(instance_id);
2501 } else {
2502 result = SDL_GetPersistentString(mapping->name);
2503 }
2504 }
2505 }
2506 SDL_UnlockJoysticks();
2507
2508 return result;
2509}
2510
2511const char *SDL_GetGamepadPathForID(SDL_JoystickID instance_id)
2512{
2513 return SDL_GetJoystickPathForID(instance_id);
2514}
2515
2516int SDL_GetGamepadPlayerIndexForID(SDL_JoystickID instance_id)
2517{
2518 return SDL_GetJoystickPlayerIndexForID(instance_id);
2519}
2520
2521SDL_GUID SDL_GetGamepadGUIDForID(SDL_JoystickID instance_id)
2522{
2523 return SDL_GetJoystickGUIDForID(instance_id);
2524}
2525
2526Uint16 SDL_GetGamepadVendorForID(SDL_JoystickID instance_id)
2527{
2528 return SDL_GetJoystickVendorForID(instance_id);
2529}
2530
2531Uint16 SDL_GetGamepadProductForID(SDL_JoystickID instance_id)
2532{
2533 return SDL_GetJoystickProductForID(instance_id);
2534}
2535
2536Uint16 SDL_GetGamepadProductVersionForID(SDL_JoystickID instance_id)
2537{
2538 return SDL_GetJoystickProductVersionForID(instance_id);
2539}
2540
2541SDL_GamepadType SDL_GetGamepadTypeForID(SDL_JoystickID instance_id)
2542{
2543 SDL_GamepadType type = SDL_GAMEPAD_TYPE_UNKNOWN;
2544
2545 SDL_LockJoysticks();
2546 {
2547 GamepadMapping_t *mapping = SDL_PrivateGetGamepadMapping(instance_id, true);
2548 if (mapping) {
2549 char *type_string, *comma;
2550
2551 type_string = SDL_strstr(mapping->mapping, SDL_GAMEPAD_TYPE_FIELD);
2552 if (type_string) {
2553 type_string += SDL_GAMEPAD_TYPE_FIELD_SIZE;
2554 comma = SDL_strchr(type_string, ',');
2555 if (comma) {
2556 *comma = '\0';
2557 type = SDL_GetGamepadTypeFromString(type_string);
2558 *comma = ',';
2559 }
2560 }
2561 }
2562 }
2563 SDL_UnlockJoysticks();
2564
2565 if (type != SDL_GAMEPAD_TYPE_UNKNOWN) {
2566 return type;
2567 }
2568 return SDL_GetRealGamepadTypeForID(instance_id);
2569}
2570
2571SDL_GamepadType SDL_GetRealGamepadTypeForID(SDL_JoystickID instance_id)
2572{
2573 SDL_GamepadType type = SDL_GAMEPAD_TYPE_UNKNOWN;
2574 const SDL_SteamVirtualGamepadInfo *info;
2575
2576 SDL_LockJoysticks();
2577 {
2578 info = SDL_GetJoystickVirtualGamepadInfoForID(instance_id);
2579 if (info) {
2580 type = info->type;
2581 } else {
2582 type = SDL_GetGamepadTypeFromGUID(SDL_GetJoystickGUIDForID(instance_id), SDL_GetJoystickNameForID(instance_id));
2583 }
2584 }
2585 SDL_UnlockJoysticks();
2586
2587 return type;
2588}
2589
2590char *SDL_GetGamepadMappingForID(SDL_JoystickID instance_id)
2591{
2592 char *result = NULL;
2593
2594 SDL_LockJoysticks();
2595 {
2596 GamepadMapping_t *mapping = SDL_PrivateGetGamepadMapping(instance_id, true);
2597 if (mapping) {
2598 char pchGUID[33];
2599 SDL_GUID guid = SDL_GetJoystickGUIDForID(instance_id);
2600 SDL_GUIDToString(guid, pchGUID, sizeof(pchGUID));
2601 SDL_asprintf(&result, "%s,%s,%s", pchGUID, mapping->name, mapping->mapping);
2602 }
2603 }
2604 SDL_UnlockJoysticks();
2605
2606 return result;
2607}
2608
2609/*
2610 * Return 1 if the joystick with this name and GUID is a supported gamepad
2611 */
2612bool SDL_IsGamepadNameAndGUID(const char *name, SDL_GUID guid)
2613{
2614 bool result;
2615
2616 SDL_LockJoysticks();
2617 {
2618 if (s_pDefaultMapping || SDL_PrivateGetGamepadMappingForNameAndGUID(name, guid) != NULL) {
2619 result = true;
2620 } else {
2621 result = false;
2622 }
2623 }
2624 SDL_UnlockJoysticks();
2625
2626 return result;
2627}
2628
2629/*
2630 * Return 1 if the joystick at this device index is a supported gamepad
2631 */
2632bool SDL_IsGamepad(SDL_JoystickID instance_id)
2633{
2634 bool result;
2635
2636 SDL_LockJoysticks();
2637 {
2638 const void *value;
2639 if (SDL_FindInHashTable(s_gamepadInstanceIDs, (void *)(uintptr_t)instance_id, &value)) {
2640 result = (bool)(uintptr_t)value;
2641 } else {
2642 if (SDL_PrivateGetGamepadMapping(instance_id, true) != NULL) {
2643 result = true;
2644 } else {
2645 result = false;
2646 }
2647
2648 if (!s_gamepadInstanceIDs) {
2649 s_gamepadInstanceIDs = SDL_CreateHashTable(0, false, SDL_HashID, SDL_KeyMatchID, NULL, NULL);
2650 }
2651 SDL_InsertIntoHashTable(s_gamepadInstanceIDs, (void *)(uintptr_t)instance_id, (void *)(uintptr_t)result, true);
2652 }
2653 }
2654 SDL_UnlockJoysticks();
2655
2656 return result;
2657}
2658
2659/*
2660 * Return 1 if the gamepad should be ignored by SDL
2661 */
2662bool SDL_ShouldIgnoreGamepad(Uint16 vendor_id, Uint16 product_id, Uint16 version, const char *name)
2663{
2664#ifdef SDL_PLATFORM_LINUX
2665 if (SDL_endswith(name, " Motion Sensors")) {
2666 // Don't treat the PS3 and PS4 motion controls as a separate gamepad
2667 return true;
2668 }
2669 if (SDL_strncmp(name, "Nintendo ", 9) == 0 && SDL_strstr(name, " IMU") != NULL) {
2670 // Don't treat the Nintendo IMU as a separate gamepad
2671 return true;
2672 }
2673 if (SDL_endswith(name, " Accelerometer") ||
2674 SDL_endswith(name, " IR") ||
2675 SDL_endswith(name, " Motion Plus") ||
2676 SDL_endswith(name, " Nunchuk")) {
2677 // Don't treat the Wii extension controls as a separate gamepad
2678 return true;
2679 }
2680#endif
2681
2682 if (name && SDL_strcmp(name, "uinput-fpc") == 0) {
2683 // The Google Pixel fingerprint sensor reports itself as a joystick
2684 return true;
2685 }
2686
2687#ifdef SDL_PLATFORM_WIN32
2688 if (SDL_GetHintBoolean("SDL_GAMECONTROLLER_ALLOW_STEAM_VIRTUAL_GAMEPAD", false) &&
2689 SDL_GetHintBoolean("STEAM_COMPAT_PROTON", false)) {
2690 // We are launched by Steam and running under Proton
2691 // We can't tell whether this controller is a Steam Virtual Gamepad,
2692 // so assume that Proton is doing the appropriate filtering of controllers
2693 // and anything we see here is fine to use.
2694 return false;
2695 }
2696#endif // SDL_PLATFORM_WIN32
2697
2698 if (SDL_IsJoystickSteamVirtualGamepad(vendor_id, product_id, version)) {
2699 return !SDL_GetHintBoolean("SDL_GAMECONTROLLER_ALLOW_STEAM_VIRTUAL_GAMEPAD", false);
2700 }
2701
2702 if (SDL_allowed_gamepads.num_included_entries > 0) {
2703 if (SDL_VIDPIDInList(vendor_id, product_id, &SDL_allowed_gamepads)) {
2704 return false;
2705 }
2706 return true;
2707 } else {
2708 if (SDL_VIDPIDInList(vendor_id, product_id, &SDL_ignored_gamepads)) {
2709 return true;
2710 }
2711 return false;
2712 }
2713}
2714
2715/*
2716 * Open a gamepad for use
2717 *
2718 * This function returns a gamepad identifier, or NULL if an error occurred.
2719 */
2720SDL_Gamepad *SDL_OpenGamepad(SDL_JoystickID instance_id)
2721{
2722 SDL_Gamepad *gamepad;
2723 SDL_Gamepad *gamepadlist;
2724 GamepadMapping_t *pSupportedGamepad = NULL;
2725
2726 SDL_LockJoysticks();
2727
2728 gamepadlist = SDL_gamepads;
2729 // If the gamepad is already open, return it
2730 while (gamepadlist) {
2731 if (instance_id == gamepadlist->joystick->instance_id) {
2732 gamepad = gamepadlist;
2733 ++gamepad->ref_count;
2734 SDL_UnlockJoysticks();
2735 return gamepad;
2736 }
2737 gamepadlist = gamepadlist->next;
2738 }
2739
2740 // Find a gamepad mapping
2741 pSupportedGamepad = SDL_PrivateGetGamepadMapping(instance_id, true);
2742 if (!pSupportedGamepad) {
2743 SDL_SetError("Couldn't find mapping for device (%" SDL_PRIu32 ")", instance_id);
2744 SDL_UnlockJoysticks();
2745 return NULL;
2746 }
2747
2748 // Create and initialize the gamepad
2749 gamepad = (SDL_Gamepad *)SDL_calloc(1, sizeof(*gamepad));
2750 if (!gamepad) {
2751 SDL_UnlockJoysticks();
2752 return NULL;
2753 }
2754 SDL_SetObjectValid(gamepad, SDL_OBJECT_TYPE_GAMEPAD, true);
2755
2756 gamepad->joystick = SDL_OpenJoystick(instance_id);
2757 if (!gamepad->joystick) {
2758 SDL_free(gamepad);
2759 SDL_UnlockJoysticks();
2760 return NULL;
2761 }
2762
2763 if (gamepad->joystick->naxes) {
2764 gamepad->last_match_axis = (SDL_GamepadBinding **)SDL_calloc(gamepad->joystick->naxes, sizeof(*gamepad->last_match_axis));
2765 if (!gamepad->last_match_axis) {
2766 SDL_CloseJoystick(gamepad->joystick);
2767 SDL_free(gamepad);
2768 SDL_UnlockJoysticks();
2769 return NULL;
2770 }
2771 }
2772 if (gamepad->joystick->nhats) {
2773 gamepad->last_hat_mask = (Uint8 *)SDL_calloc(gamepad->joystick->nhats, sizeof(*gamepad->last_hat_mask));
2774 if (!gamepad->last_hat_mask) {
2775 SDL_CloseJoystick(gamepad->joystick);
2776 SDL_free(gamepad->last_match_axis);
2777 SDL_free(gamepad);
2778 SDL_UnlockJoysticks();
2779 return NULL;
2780 }
2781 }
2782
2783 SDL_PrivateLoadButtonMapping(gamepad, pSupportedGamepad);
2784
2785 // Add the gamepad to list
2786 ++gamepad->ref_count;
2787 // Link the gamepad in the list
2788 gamepad->next = SDL_gamepads;
2789 SDL_gamepads = gamepad;
2790
2791 SDL_UnlockJoysticks();
2792
2793 return gamepad;
2794}
2795
2796/*
2797 * Manually pump for gamepad updates.
2798 */
2799void SDL_UpdateGamepads(void)
2800{
2801 // Just for API completeness; the joystick API does all the work.
2802 SDL_UpdateJoysticks();
2803}
2804
2805/**
2806 * Return whether a gamepad has a given axis
2807 */
2808bool SDL_GamepadHasAxis(SDL_Gamepad *gamepad, SDL_GamepadAxis axis)
2809{
2810 bool result = false;
2811
2812 SDL_LockJoysticks();
2813 {
2814 int i;
2815
2816 CHECK_GAMEPAD_MAGIC(gamepad, false);
2817
2818 for (i = 0; i < gamepad->num_bindings; ++i) {
2819 const SDL_GamepadBinding *binding = &gamepad->bindings[i];
2820 if (binding->output_type == SDL_GAMEPAD_BINDTYPE_AXIS && binding->output.axis.axis == axis) {
2821 result = true;
2822 break;
2823 }
2824 }
2825 }
2826 SDL_UnlockJoysticks();
2827
2828 return result;
2829}
2830
2831/*
2832 * Get the current state of an axis control on a gamepad
2833 */
2834Sint16 SDL_GetGamepadAxis(SDL_Gamepad *gamepad, SDL_GamepadAxis axis)
2835{
2836 Sint16 result = 0;
2837
2838 SDL_LockJoysticks();
2839 {
2840 int i;
2841
2842 CHECK_GAMEPAD_MAGIC(gamepad, 0);
2843
2844 for (i = 0; i < gamepad->num_bindings; ++i) {
2845 const SDL_GamepadBinding *binding = &gamepad->bindings[i];
2846 if (binding->output_type == SDL_GAMEPAD_BINDTYPE_AXIS && binding->output.axis.axis == axis) {
2847 int value = 0;
2848 bool valid_input_range;
2849 bool valid_output_range;
2850
2851 if (binding->input_type == SDL_GAMEPAD_BINDTYPE_AXIS) {
2852 value = SDL_GetJoystickAxis(gamepad->joystick, binding->input.axis.axis);
2853 if (binding->input.axis.axis_min < binding->input.axis.axis_max) {
2854 valid_input_range = (value >= binding->input.axis.axis_min && value <= binding->input.axis.axis_max);
2855 } else {
2856 valid_input_range = (value >= binding->input.axis.axis_max && value <= binding->input.axis.axis_min);
2857 }
2858 if (valid_input_range) {
2859 if (binding->input.axis.axis_min != binding->output.axis.axis_min || binding->input.axis.axis_max != binding->output.axis.axis_max) {
2860 float normalized_value = (float)(value - binding->input.axis.axis_min) / (binding->input.axis.axis_max - binding->input.axis.axis_min);
2861 value = binding->output.axis.axis_min + (int)(normalized_value * (binding->output.axis.axis_max - binding->output.axis.axis_min));
2862 }
2863 } else {
2864 value = 0;
2865 }
2866 } else if (binding->input_type == SDL_GAMEPAD_BINDTYPE_BUTTON) {
2867 if (SDL_GetJoystickButton(gamepad->joystick, binding->input.button)) {
2868 value = binding->output.axis.axis_max;
2869 }
2870 } else if (binding->input_type == SDL_GAMEPAD_BINDTYPE_HAT) {
2871 int hat_mask = SDL_GetJoystickHat(gamepad->joystick, binding->input.hat.hat);
2872 if (hat_mask & binding->input.hat.hat_mask) {
2873 value = binding->output.axis.axis_max;
2874 }
2875 }
2876
2877 if (binding->output.axis.axis_min < binding->output.axis.axis_max) {
2878 valid_output_range = (value >= binding->output.axis.axis_min && value <= binding->output.axis.axis_max);
2879 } else {
2880 valid_output_range = (value >= binding->output.axis.axis_max && value <= binding->output.axis.axis_min);
2881 }
2882 // If the value is zero, there might be another binding that makes it non-zero
2883 if (value != 0 && valid_output_range) {
2884 result = (Sint16)value;
2885 break;
2886 }
2887 }
2888 }
2889 }
2890 SDL_UnlockJoysticks();
2891
2892 return result;
2893}
2894
2895/**
2896 * Return whether a gamepad has a given button
2897 */
2898bool SDL_GamepadHasButton(SDL_Gamepad *gamepad, SDL_GamepadButton button)
2899{
2900 bool result = false;
2901
2902 SDL_LockJoysticks();
2903 {
2904 int i;
2905
2906 CHECK_GAMEPAD_MAGIC(gamepad, false);
2907
2908 for (i = 0; i < gamepad->num_bindings; ++i) {
2909 const SDL_GamepadBinding *binding = &gamepad->bindings[i];
2910 if (binding->output_type == SDL_GAMEPAD_BINDTYPE_BUTTON && binding->output.button == button) {
2911 result = true;
2912 break;
2913 }
2914 }
2915 }
2916 SDL_UnlockJoysticks();
2917
2918 return result;
2919}
2920
2921/*
2922 * Get the current state of a button on a gamepad
2923 */
2924bool SDL_GetGamepadButton(SDL_Gamepad *gamepad, SDL_GamepadButton button)
2925{
2926 bool result = false;
2927
2928 SDL_LockJoysticks();
2929 {
2930 int i;
2931
2932 CHECK_GAMEPAD_MAGIC(gamepad, false);
2933
2934 for (i = 0; i < gamepad->num_bindings; ++i) {
2935 const SDL_GamepadBinding *binding = &gamepad->bindings[i];
2936 if (binding->output_type == SDL_GAMEPAD_BINDTYPE_BUTTON && binding->output.button == button) {
2937 if (binding->input_type == SDL_GAMEPAD_BINDTYPE_AXIS) {
2938 bool valid_input_range;
2939
2940 int value = SDL_GetJoystickAxis(gamepad->joystick, binding->input.axis.axis);
2941 int threshold = binding->input.axis.axis_min + (binding->input.axis.axis_max - binding->input.axis.axis_min) / 2;
2942 if (binding->input.axis.axis_min < binding->input.axis.axis_max) {
2943 valid_input_range = (value >= binding->input.axis.axis_min && value <= binding->input.axis.axis_max);
2944 if (valid_input_range) {
2945 result |= (value >= threshold);
2946 }
2947 } else {
2948 valid_input_range = (value >= binding->input.axis.axis_max && value <= binding->input.axis.axis_min);
2949 if (valid_input_range) {
2950 result |= (value <= threshold);
2951 }
2952 }
2953 } else if (binding->input_type == SDL_GAMEPAD_BINDTYPE_BUTTON) {
2954 result |= SDL_GetJoystickButton(gamepad->joystick, binding->input.button);
2955 } else if (binding->input_type == SDL_GAMEPAD_BINDTYPE_HAT) {
2956 int hat_mask = SDL_GetJoystickHat(gamepad->joystick, binding->input.hat.hat);
2957 result |= ((hat_mask & binding->input.hat.hat_mask) != 0);
2958 }
2959 }
2960 }
2961 }
2962 SDL_UnlockJoysticks();
2963
2964 return result;
2965}
2966
2967/**
2968 * Get the label of a button on a gamepad.
2969 */
2970static SDL_GamepadButtonLabel SDL_GetGamepadButtonLabelForFaceStyle(SDL_GamepadFaceStyle face_style, SDL_GamepadButton button)
2971{
2972 SDL_GamepadButtonLabel label = SDL_GAMEPAD_BUTTON_LABEL_UNKNOWN;
2973
2974 switch (face_style) {
2975 case SDL_GAMEPAD_FACE_STYLE_ABXY:
2976 switch (button) {
2977 case SDL_GAMEPAD_BUTTON_SOUTH:
2978 label = SDL_GAMEPAD_BUTTON_LABEL_A;
2979 break;
2980 case SDL_GAMEPAD_BUTTON_EAST:
2981 label = SDL_GAMEPAD_BUTTON_LABEL_B;
2982 break;
2983 case SDL_GAMEPAD_BUTTON_WEST:
2984 label = SDL_GAMEPAD_BUTTON_LABEL_X;
2985 break;
2986 case SDL_GAMEPAD_BUTTON_NORTH:
2987 label = SDL_GAMEPAD_BUTTON_LABEL_Y;
2988 break;
2989 default:
2990 break;
2991 }
2992 break;
2993 case SDL_GAMEPAD_FACE_STYLE_BAYX:
2994 switch (button) {
2995 case SDL_GAMEPAD_BUTTON_SOUTH:
2996 label = SDL_GAMEPAD_BUTTON_LABEL_B;
2997 break;
2998 case SDL_GAMEPAD_BUTTON_EAST:
2999 label = SDL_GAMEPAD_BUTTON_LABEL_A;
3000 break;
3001 case SDL_GAMEPAD_BUTTON_WEST:
3002 label = SDL_GAMEPAD_BUTTON_LABEL_Y;
3003 break;
3004 case SDL_GAMEPAD_BUTTON_NORTH:
3005 label = SDL_GAMEPAD_BUTTON_LABEL_X;
3006 break;
3007 default:
3008 break;
3009 }
3010 break;
3011 case SDL_GAMEPAD_FACE_STYLE_SONY:
3012 switch (button) {
3013 case SDL_GAMEPAD_BUTTON_SOUTH:
3014 label = SDL_GAMEPAD_BUTTON_LABEL_CROSS;
3015 break;
3016 case SDL_GAMEPAD_BUTTON_EAST:
3017 label = SDL_GAMEPAD_BUTTON_LABEL_CIRCLE;
3018 break;
3019 case SDL_GAMEPAD_BUTTON_WEST:
3020 label = SDL_GAMEPAD_BUTTON_LABEL_SQUARE;
3021 break;
3022 case SDL_GAMEPAD_BUTTON_NORTH:
3023 label = SDL_GAMEPAD_BUTTON_LABEL_TRIANGLE;
3024 break;
3025 default:
3026 break;
3027 }
3028 break;
3029 default:
3030 break;
3031 }
3032 return label;
3033}
3034
3035/**
3036 * Get the label of a button on a gamepad.
3037 */
3038SDL_GamepadButtonLabel SDL_GetGamepadButtonLabelForType(SDL_GamepadType type, SDL_GamepadButton button)
3039{
3040 return SDL_GetGamepadButtonLabelForFaceStyle(SDL_GetGamepadFaceStyleForGamepadType(type), button);
3041}
3042
3043/**
3044 * Get the label of a button on a gamepad.
3045 */
3046SDL_GamepadButtonLabel SDL_GetGamepadButtonLabel(SDL_Gamepad *gamepad, SDL_GamepadButton button)
3047{
3048 SDL_GamepadFaceStyle face_style;
3049
3050 SDL_LockJoysticks();
3051 {
3052 CHECK_GAMEPAD_MAGIC(gamepad, SDL_GAMEPAD_BUTTON_LABEL_UNKNOWN);
3053
3054 face_style = gamepad->face_style;
3055 }
3056 SDL_UnlockJoysticks();
3057
3058 return SDL_GetGamepadButtonLabelForFaceStyle(face_style, button);
3059}
3060
3061/**
3062 * Get the number of touchpads on a gamepad.
3063 */
3064int SDL_GetNumGamepadTouchpads(SDL_Gamepad *gamepad)
3065{
3066 int result = 0;
3067
3068 SDL_LockJoysticks();
3069 {
3070 SDL_Joystick *joystick = SDL_GetGamepadJoystick(gamepad);
3071 if (joystick) {
3072 result = joystick->ntouchpads;
3073 }
3074 }
3075 SDL_UnlockJoysticks();
3076
3077 return result;
3078}
3079
3080/**
3081 * Get the number of supported simultaneous fingers on a touchpad on a gamepad.
3082 */
3083int SDL_GetNumGamepadTouchpadFingers(SDL_Gamepad *gamepad, int touchpad)
3084{
3085 int result = 0;
3086
3087 SDL_LockJoysticks();
3088 {
3089 SDL_Joystick *joystick = SDL_GetGamepadJoystick(gamepad);
3090 if (joystick) {
3091 if (touchpad >= 0 && touchpad < joystick->ntouchpads) {
3092 result = joystick->touchpads[touchpad].nfingers;
3093 }
3094 }
3095 }
3096 SDL_UnlockJoysticks();
3097
3098 return result;
3099}
3100
3101/**
3102 * Get the current state of a finger on a touchpad on a gamepad.
3103 */
3104bool SDL_GetGamepadTouchpadFinger(SDL_Gamepad *gamepad, int touchpad, int finger, bool *down, float *x, float *y, float *pressure)
3105{
3106 bool result = false;
3107
3108 SDL_LockJoysticks();
3109 {
3110 SDL_Joystick *joystick = SDL_GetGamepadJoystick(gamepad);
3111 if (joystick) {
3112 if (touchpad >= 0 && touchpad < joystick->ntouchpads) {
3113 SDL_JoystickTouchpadInfo *touchpad_info = &joystick->touchpads[touchpad];
3114 if (finger >= 0 && finger < touchpad_info->nfingers) {
3115 SDL_JoystickTouchpadFingerInfo *info = &touchpad_info->fingers[finger];
3116
3117 if (down) {
3118 *down = info->down;
3119 }
3120 if (x) {
3121 *x = info->x;
3122 }
3123 if (y) {
3124 *y = info->y;
3125 }
3126 if (pressure) {
3127 *pressure = info->pressure;
3128 }
3129 result = true;
3130 } else {
3131 result = SDL_InvalidParamError("finger");
3132 }
3133 } else {
3134 result = SDL_InvalidParamError("touchpad");
3135 }
3136 }
3137 }
3138 SDL_UnlockJoysticks();
3139
3140 return result;
3141}
3142
3143/**
3144 * Return whether a gamepad has a particular sensor.
3145 */
3146bool SDL_GamepadHasSensor(SDL_Gamepad *gamepad, SDL_SensorType type)
3147{
3148 bool result = false;
3149
3150 SDL_LockJoysticks();
3151 {
3152 SDL_Joystick *joystick = SDL_GetGamepadJoystick(gamepad);
3153 if (joystick) {
3154 int i;
3155 for (i = 0; i < joystick->nsensors; ++i) {
3156 if (joystick->sensors[i].type == type) {
3157 result = true;
3158 break;
3159 }
3160 }
3161 }
3162 }
3163 SDL_UnlockJoysticks();
3164
3165 return result;
3166}
3167
3168/*
3169 * Set whether data reporting for a gamepad sensor is enabled
3170 */
3171bool SDL_SetGamepadSensorEnabled(SDL_Gamepad *gamepad, SDL_SensorType type, bool enabled)
3172{
3173 SDL_LockJoysticks();
3174 {
3175 SDL_Joystick *joystick = SDL_GetGamepadJoystick(gamepad);
3176 if (joystick) {
3177 int i;
3178 for (i = 0; i < joystick->nsensors; ++i) {
3179 SDL_JoystickSensorInfo *sensor = &joystick->sensors[i];
3180
3181 if (sensor->type == type) {
3182 if (sensor->enabled == (enabled != false)) {
3183 SDL_UnlockJoysticks();
3184 return true;
3185 }
3186
3187 if (type == SDL_SENSOR_ACCEL && joystick->accel_sensor) {
3188 if (enabled) {
3189 joystick->accel = SDL_OpenSensor(joystick->accel_sensor);
3190 if (!joystick->accel) {
3191 SDL_UnlockJoysticks();
3192 return false;
3193 }
3194 } else {
3195 if (joystick->accel) {
3196 SDL_CloseSensor(joystick->accel);
3197 joystick->accel = NULL;
3198 }
3199 }
3200 } else if (type == SDL_SENSOR_GYRO && joystick->gyro_sensor) {
3201 if (enabled) {
3202 joystick->gyro = SDL_OpenSensor(joystick->gyro_sensor);
3203 if (!joystick->gyro) {
3204 SDL_UnlockJoysticks();
3205 return false;
3206 }
3207 } else {
3208 if (joystick->gyro) {
3209 SDL_CloseSensor(joystick->gyro);
3210 joystick->gyro = NULL;
3211 }
3212 }
3213 } else {
3214 if (enabled) {
3215 if (joystick->nsensors_enabled == 0) {
3216 if (!joystick->driver->SetSensorsEnabled(joystick, true)) {
3217 SDL_UnlockJoysticks();
3218 return false;
3219 }
3220 }
3221 ++joystick->nsensors_enabled;
3222 } else {
3223 if (joystick->nsensors_enabled == 1) {
3224 if (!joystick->driver->SetSensorsEnabled(joystick, false)) {
3225 SDL_UnlockJoysticks();
3226 return false;
3227 }
3228 }
3229 --joystick->nsensors_enabled;
3230 }
3231 }
3232
3233 sensor->enabled = enabled;
3234 SDL_UnlockJoysticks();
3235 return true;
3236 }
3237 }
3238 }
3239 }
3240 SDL_UnlockJoysticks();
3241
3242 return SDL_Unsupported();
3243}
3244
3245/*
3246 * Query whether sensor data reporting is enabled for a gamepad
3247 */
3248bool SDL_GamepadSensorEnabled(SDL_Gamepad *gamepad, SDL_SensorType type)
3249{
3250 bool result = false;
3251
3252 SDL_LockJoysticks();
3253 {
3254 SDL_Joystick *joystick = SDL_GetGamepadJoystick(gamepad);
3255 if (joystick) {
3256 int i;
3257 for (i = 0; i < joystick->nsensors; ++i) {
3258 if (joystick->sensors[i].type == type) {
3259 result = joystick->sensors[i].enabled;
3260 break;
3261 }
3262 }
3263 }
3264 }
3265 SDL_UnlockJoysticks();
3266
3267 return result;
3268}
3269
3270/*
3271 * Get the data rate of a gamepad sensor.
3272 */
3273float SDL_GetGamepadSensorDataRate(SDL_Gamepad *gamepad, SDL_SensorType type)
3274{
3275 float result = 0.0f;
3276
3277 SDL_LockJoysticks();
3278 {
3279 SDL_Joystick *joystick = SDL_GetGamepadJoystick(gamepad);
3280 if (joystick) {
3281 int i;
3282 for (i = 0; i < joystick->nsensors; ++i) {
3283 SDL_JoystickSensorInfo *sensor = &joystick->sensors[i];
3284
3285 if (sensor->type == type) {
3286 result = sensor->rate;
3287 break;
3288 }
3289 }
3290 }
3291 }
3292 SDL_UnlockJoysticks();
3293
3294 return result;
3295}
3296
3297/*
3298 * Get the current state of a gamepad sensor.
3299 */
3300bool SDL_GetGamepadSensorData(SDL_Gamepad *gamepad, SDL_SensorType type, float *data, int num_values)
3301{
3302 SDL_LockJoysticks();
3303 {
3304 SDL_Joystick *joystick = SDL_GetGamepadJoystick(gamepad);
3305 if (joystick) {
3306 int i;
3307 for (i = 0; i < joystick->nsensors; ++i) {
3308 SDL_JoystickSensorInfo *sensor = &joystick->sensors[i];
3309
3310 if (sensor->type == type) {
3311 num_values = SDL_min(num_values, SDL_arraysize(sensor->data));
3312 SDL_memcpy(data, sensor->data, num_values * sizeof(*data));
3313 SDL_UnlockJoysticks();
3314 return true;
3315 }
3316 }
3317 }
3318 }
3319 SDL_UnlockJoysticks();
3320
3321 return SDL_Unsupported();
3322}
3323
3324SDL_JoystickID SDL_GetGamepadID(SDL_Gamepad *gamepad)
3325{
3326 SDL_Joystick *joystick = SDL_GetGamepadJoystick(gamepad);
3327
3328 if (!joystick) {
3329 return 0;
3330 }
3331 return SDL_GetJoystickID(joystick);
3332}
3333
3334SDL_PropertiesID SDL_GetGamepadProperties(SDL_Gamepad *gamepad)
3335{
3336 SDL_PropertiesID result = 0;
3337
3338 SDL_LockJoysticks();
3339 {
3340 CHECK_GAMEPAD_MAGIC(gamepad, 0);
3341
3342 result = SDL_GetJoystickProperties(gamepad->joystick);
3343 }
3344 SDL_UnlockJoysticks();
3345
3346 return result;
3347}
3348
3349const char *SDL_GetGamepadName(SDL_Gamepad *gamepad)
3350{
3351 const char *result = NULL;
3352
3353 SDL_LockJoysticks();
3354 {
3355 CHECK_GAMEPAD_MAGIC(gamepad, NULL);
3356
3357 if (SDL_strcmp(gamepad->name, "*") == 0 ||
3358 gamepad->joystick->steam_handle != 0) {
3359 result = SDL_GetJoystickName(gamepad->joystick);
3360 } else {
3361 result = SDL_GetPersistentString(gamepad->name);
3362 }
3363 }
3364 SDL_UnlockJoysticks();
3365
3366 return result;
3367}
3368
3369const char *SDL_GetGamepadPath(SDL_Gamepad *gamepad)
3370{
3371 SDL_Joystick *joystick = SDL_GetGamepadJoystick(gamepad);
3372
3373 if (!joystick) {
3374 return NULL;
3375 }
3376 return SDL_GetJoystickPath(joystick);
3377}
3378
3379SDL_GamepadType SDL_GetGamepadType(SDL_Gamepad *gamepad)
3380{
3381 SDL_GamepadType type;
3382 const SDL_SteamVirtualGamepadInfo *info;
3383
3384 SDL_LockJoysticks();
3385 {
3386 CHECK_GAMEPAD_MAGIC(gamepad, SDL_GAMEPAD_TYPE_UNKNOWN);
3387
3388 info = SDL_GetJoystickVirtualGamepadInfoForID(gamepad->joystick->instance_id);
3389 if (info) {
3390 type = info->type;
3391 } else {
3392 type = gamepad->type;
3393 }
3394 }
3395 SDL_UnlockJoysticks();
3396
3397 return type;
3398}
3399
3400SDL_GamepadType SDL_GetRealGamepadType(SDL_Gamepad *gamepad)
3401{
3402 SDL_Joystick *joystick = SDL_GetGamepadJoystick(gamepad);
3403
3404 if (!joystick) {
3405 return SDL_GAMEPAD_TYPE_UNKNOWN;
3406 }
3407 return SDL_GetGamepadTypeFromGUID(SDL_GetJoystickGUID(joystick), SDL_GetJoystickName(joystick));
3408}
3409
3410int SDL_GetGamepadPlayerIndex(SDL_Gamepad *gamepad)
3411{
3412 SDL_Joystick *joystick = SDL_GetGamepadJoystick(gamepad);
3413
3414 if (!joystick) {
3415 return -1;
3416 }
3417 return SDL_GetJoystickPlayerIndex(joystick);
3418}
3419
3420/**
3421 * Set the player index of an opened gamepad
3422 */
3423bool SDL_SetGamepadPlayerIndex(SDL_Gamepad *gamepad, int player_index)
3424{
3425 SDL_Joystick *joystick = SDL_GetGamepadJoystick(gamepad);
3426
3427 if (!joystick) {
3428 // SDL_SetError() will have been called already by SDL_GetGamepadJoystick()
3429 return false;
3430 }
3431 return SDL_SetJoystickPlayerIndex(joystick, player_index);
3432}
3433
3434Uint16 SDL_GetGamepadVendor(SDL_Gamepad *gamepad)
3435{
3436 SDL_Joystick *joystick = SDL_GetGamepadJoystick(gamepad);
3437
3438 if (!joystick) {
3439 return 0;
3440 }
3441 return SDL_GetJoystickVendor(joystick);
3442}
3443
3444Uint16 SDL_GetGamepadProduct(SDL_Gamepad *gamepad)
3445{
3446 SDL_Joystick *joystick = SDL_GetGamepadJoystick(gamepad);
3447
3448 if (!joystick) {
3449 return 0;
3450 }
3451 return SDL_GetJoystickProduct(joystick);
3452}
3453
3454Uint16 SDL_GetGamepadProductVersion(SDL_Gamepad *gamepad)
3455{
3456 SDL_Joystick *joystick = SDL_GetGamepadJoystick(gamepad);
3457
3458 if (!joystick) {
3459 return 0;
3460 }
3461 return SDL_GetJoystickProductVersion(joystick);
3462}
3463
3464Uint16 SDL_GetGamepadFirmwareVersion(SDL_Gamepad *gamepad)
3465{
3466 SDL_Joystick *joystick = SDL_GetGamepadJoystick(gamepad);
3467
3468 if (!joystick) {
3469 return 0;
3470 }
3471 return SDL_GetJoystickFirmwareVersion(joystick);
3472}
3473
3474const char * SDL_GetGamepadSerial(SDL_Gamepad *gamepad)
3475{
3476 SDL_Joystick *joystick = SDL_GetGamepadJoystick(gamepad);
3477
3478 if (!joystick) {
3479 return NULL;
3480 }
3481 return SDL_GetJoystickSerial(joystick);
3482
3483}
3484
3485Uint64 SDL_GetGamepadSteamHandle(SDL_Gamepad *gamepad)
3486{
3487 Uint64 handle = 0;
3488
3489 SDL_LockJoysticks();
3490 {
3491 CHECK_GAMEPAD_MAGIC(gamepad, 0);
3492
3493 handle = gamepad->joystick->steam_handle;
3494 }
3495 SDL_UnlockJoysticks();
3496
3497 return handle;
3498}
3499
3500SDL_JoystickConnectionState SDL_GetGamepadConnectionState(SDL_Gamepad *gamepad)
3501{
3502 SDL_Joystick *joystick = SDL_GetGamepadJoystick(gamepad);
3503
3504 if (!joystick) {
3505 return SDL_JOYSTICK_CONNECTION_INVALID;
3506 }
3507 return SDL_GetJoystickConnectionState(joystick);
3508}
3509
3510SDL_PowerState SDL_GetGamepadPowerInfo(SDL_Gamepad *gamepad, int *percent)
3511{
3512 SDL_Joystick *joystick = SDL_GetGamepadJoystick(gamepad);
3513
3514 if (percent) {
3515 *percent = -1;
3516 }
3517 if (!joystick) {
3518 return SDL_POWERSTATE_ERROR;
3519 }
3520 return SDL_GetJoystickPowerInfo(joystick, percent);
3521}
3522
3523/*
3524 * Return if the gamepad in question is currently attached to the system,
3525 * \return 0 if not plugged in, 1 if still present.
3526 */
3527bool SDL_GamepadConnected(SDL_Gamepad *gamepad)
3528{
3529 SDL_Joystick *joystick = SDL_GetGamepadJoystick(gamepad);
3530
3531 if (!joystick) {
3532 return false;
3533 }
3534 return SDL_JoystickConnected(joystick);
3535}
3536
3537/*
3538 * Get the joystick for this gamepad
3539 */
3540SDL_Joystick *SDL_GetGamepadJoystick(SDL_Gamepad *gamepad)
3541{
3542 SDL_Joystick *joystick;
3543
3544 SDL_LockJoysticks();
3545 {
3546 CHECK_GAMEPAD_MAGIC(gamepad, NULL);
3547
3548 joystick = gamepad->joystick;
3549 }
3550 SDL_UnlockJoysticks();
3551
3552 return joystick;
3553}
3554
3555/*
3556 * Return the SDL_Gamepad associated with an instance id.
3557 */
3558SDL_Gamepad *SDL_GetGamepadFromID(SDL_JoystickID joyid)
3559{
3560 SDL_Gamepad *gamepad;
3561
3562 SDL_LockJoysticks();
3563 gamepad = SDL_gamepads;
3564 while (gamepad) {
3565 if (gamepad->joystick->instance_id == joyid) {
3566 SDL_UnlockJoysticks();
3567 return gamepad;
3568 }
3569 gamepad = gamepad->next;
3570 }
3571 SDL_UnlockJoysticks();
3572 return NULL;
3573}
3574
3575/**
3576 * Return the SDL_Gamepad associated with a player index.
3577 */
3578SDL_Gamepad *SDL_GetGamepadFromPlayerIndex(int player_index)
3579{
3580 SDL_Gamepad *result = NULL;
3581
3582 SDL_LockJoysticks();
3583 {
3584 SDL_Joystick *joystick = SDL_GetJoystickFromPlayerIndex(player_index);
3585 if (joystick) {
3586 result = SDL_GetGamepadFromID(joystick->instance_id);
3587 }
3588 }
3589 SDL_UnlockJoysticks();
3590
3591 return result;
3592}
3593
3594/*
3595 * Get the SDL joystick layer bindings for this gamepad
3596 */
3597SDL_GamepadBinding **SDL_GetGamepadBindings(SDL_Gamepad *gamepad, int *count)
3598{
3599 SDL_GamepadBinding **bindings = NULL;
3600
3601 if (count) {
3602 *count = 0;
3603 }
3604
3605 SDL_LockJoysticks();
3606 {
3607 CHECK_GAMEPAD_MAGIC(gamepad, NULL);
3608
3609 size_t pointers_size = ((gamepad->num_bindings + 1) * sizeof(SDL_GamepadBinding *));
3610 size_t elements_size = (gamepad->num_bindings * sizeof(SDL_GamepadBinding));
3611 bindings = (SDL_GamepadBinding **)SDL_malloc(pointers_size + elements_size);
3612 if (bindings) {
3613 SDL_GamepadBinding *binding = (SDL_GamepadBinding *)((Uint8 *)bindings + pointers_size);
3614 int i;
3615 for (i = 0; i < gamepad->num_bindings; ++i, ++binding) {
3616 bindings[i] = binding;
3617 SDL_copyp(binding, &gamepad->bindings[i]);
3618 }
3619 bindings[i] = NULL;
3620
3621 if (count) {
3622 *count = gamepad->num_bindings;
3623 }
3624 }
3625 }
3626 SDL_UnlockJoysticks();
3627
3628 return bindings;
3629}
3630
3631bool SDL_RumbleGamepad(SDL_Gamepad *gamepad, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble, Uint32 duration_ms)
3632{
3633 SDL_Joystick *joystick = SDL_GetGamepadJoystick(gamepad);
3634
3635 if (!joystick) {
3636 return false;
3637 }
3638 return SDL_RumbleJoystick(joystick, low_frequency_rumble, high_frequency_rumble, duration_ms);
3639}
3640
3641bool SDL_RumbleGamepadTriggers(SDL_Gamepad *gamepad, Uint16 left_rumble, Uint16 right_rumble, Uint32 duration_ms)
3642{
3643 SDL_Joystick *joystick = SDL_GetGamepadJoystick(gamepad);
3644
3645 if (!joystick) {
3646 return false;
3647 }
3648 return SDL_RumbleJoystickTriggers(joystick, left_rumble, right_rumble, duration_ms);
3649}
3650
3651bool SDL_SetGamepadLED(SDL_Gamepad *gamepad, Uint8 red, Uint8 green, Uint8 blue)
3652{
3653 SDL_Joystick *joystick = SDL_GetGamepadJoystick(gamepad);
3654
3655 if (!joystick) {
3656 return false;
3657 }
3658 return SDL_SetJoystickLED(joystick, red, green, blue);
3659}
3660
3661bool SDL_SendGamepadEffect(SDL_Gamepad *gamepad, const void *data, int size)
3662{
3663 SDL_Joystick *joystick = SDL_GetGamepadJoystick(gamepad);
3664
3665 if (!joystick) {
3666 return false;
3667 }
3668 return SDL_SendJoystickEffect(joystick, data, size);
3669}
3670
3671void SDL_CloseGamepad(SDL_Gamepad *gamepad)
3672{
3673 SDL_Gamepad *gamepadlist, *gamepadlistprev;
3674
3675 SDL_LockJoysticks();
3676
3677 if (!SDL_ObjectValid(gamepad, SDL_OBJECT_TYPE_GAMEPAD)) {
3678 SDL_UnlockJoysticks();
3679 return;
3680 }
3681
3682 // First decrement ref count
3683 if (--gamepad->ref_count > 0) {
3684 SDL_UnlockJoysticks();
3685 return;
3686 }
3687
3688 SDL_CloseJoystick(gamepad->joystick);
3689
3690 gamepadlist = SDL_gamepads;
3691 gamepadlistprev = NULL;
3692 while (gamepadlist) {
3693 if (gamepad == gamepadlist) {
3694 if (gamepadlistprev) {
3695 // unlink this entry
3696 gamepadlistprev->next = gamepadlist->next;
3697 } else {
3698 SDL_gamepads = gamepad->next;
3699 }
3700 break;
3701 }
3702 gamepadlistprev = gamepadlist;
3703 gamepadlist = gamepadlist->next;
3704 }
3705
3706 SDL_SetObjectValid(gamepad, SDL_OBJECT_TYPE_GAMEPAD, false);
3707 SDL_free(gamepad->bindings);
3708 SDL_free(gamepad->last_match_axis);
3709 SDL_free(gamepad->last_hat_mask);
3710 SDL_free(gamepad);
3711
3712 SDL_UnlockJoysticks();
3713}
3714
3715/*
3716 * Quit the gamepad subsystem
3717 */
3718void SDL_QuitGamepads(void)
3719{
3720 SDL_Gamepad *gamepad;
3721
3722 SDL_LockJoysticks();
3723
3724 for (gamepad = SDL_gamepads; gamepad; gamepad = gamepad->next) {
3725 SDL_PrivateGamepadRemoved(gamepad->joystick->instance_id);
3726 }
3727
3728 SDL_gamepads_initialized = false;
3729
3730 SDL_RemoveEventWatch(SDL_GamepadEventWatcher, NULL);
3731
3732 while (SDL_gamepads) {
3733 SDL_gamepads->ref_count = 1;
3734 SDL_CloseGamepad(SDL_gamepads);
3735 }
3736
3737 SDL_UnlockJoysticks();
3738}
3739
3740void SDL_QuitGamepadMappings(void)
3741{
3742 GamepadMapping_t *pGamepadMap;
3743
3744 SDL_AssertJoysticksLocked();
3745
3746 while (s_pSupportedGamepads) {
3747 pGamepadMap = s_pSupportedGamepads;
3748 s_pSupportedGamepads = s_pSupportedGamepads->next;
3749 SDL_free(pGamepadMap->name);
3750 SDL_free(pGamepadMap->mapping);
3751 SDL_free(pGamepadMap);
3752 }
3753
3754 SDL_FreeVIDPIDList(&SDL_allowed_gamepads);
3755 SDL_FreeVIDPIDList(&SDL_ignored_gamepads);
3756
3757 if (s_gamepadInstanceIDs) {
3758 SDL_DestroyHashTable(s_gamepadInstanceIDs);
3759 s_gamepadInstanceIDs = NULL;
3760 }
3761}
3762
3763/*
3764 * Event filter to transform joystick events into appropriate gamepad ones
3765 */
3766static void SDL_SendGamepadAxis(Uint64 timestamp, SDL_Gamepad *gamepad, SDL_GamepadAxis axis, Sint16 value)
3767{
3768 SDL_AssertJoysticksLocked();
3769
3770 // translate the event, if desired
3771 if (SDL_EventEnabled(SDL_EVENT_GAMEPAD_AXIS_MOTION)) {
3772 SDL_Event event;
3773 event.type = SDL_EVENT_GAMEPAD_AXIS_MOTION;
3774 event.common.timestamp = timestamp;
3775 event.gaxis.which = gamepad->joystick->instance_id;
3776 event.gaxis.axis = axis;
3777 event.gaxis.value = value;
3778 SDL_PushEvent(&event);
3779 }
3780}
3781
3782static void SDL_SendGamepadButton(Uint64 timestamp, SDL_Gamepad *gamepad, SDL_GamepadButton button, bool down)
3783{
3784 SDL_Event event;
3785
3786 SDL_AssertJoysticksLocked();
3787
3788 if (button == SDL_GAMEPAD_BUTTON_INVALID) {
3789 return;
3790 }
3791
3792 if (down) {
3793 event.type = SDL_EVENT_GAMEPAD_BUTTON_DOWN;
3794 } else {
3795 event.type = SDL_EVENT_GAMEPAD_BUTTON_UP;
3796 }
3797
3798 if (button == SDL_GAMEPAD_BUTTON_GUIDE) {
3799 Uint64 now = SDL_GetTicks();
3800 if (down) {
3801 gamepad->guide_button_down = now;
3802
3803 if (gamepad->joystick->delayed_guide_button) {
3804 // Skip duplicate press
3805 return;
3806 }
3807 } else {
3808 if (now < (gamepad->guide_button_down + SDL_MINIMUM_GUIDE_BUTTON_DELAY_MS)) {
3809 gamepad->joystick->delayed_guide_button = true;
3810 return;
3811 }
3812 gamepad->joystick->delayed_guide_button = false;
3813 }
3814 }
3815
3816 // translate the event, if desired
3817 if (SDL_EventEnabled(event.type)) {
3818 event.common.timestamp = timestamp;
3819 event.gbutton.which = gamepad->joystick->instance_id;
3820 event.gbutton.button = button;
3821 event.gbutton.down = down;
3822 SDL_PushEvent(&event);
3823 }
3824}
3825
3826static const Uint32 SDL_gamepad_event_list[] = {
3827 SDL_EVENT_GAMEPAD_AXIS_MOTION,
3828 SDL_EVENT_GAMEPAD_BUTTON_DOWN,
3829 SDL_EVENT_GAMEPAD_BUTTON_UP,
3830 SDL_EVENT_GAMEPAD_ADDED,
3831 SDL_EVENT_GAMEPAD_REMOVED,
3832 SDL_EVENT_GAMEPAD_REMAPPED,
3833 SDL_EVENT_GAMEPAD_TOUCHPAD_DOWN,
3834 SDL_EVENT_GAMEPAD_TOUCHPAD_MOTION,
3835 SDL_EVENT_GAMEPAD_TOUCHPAD_UP,
3836 SDL_EVENT_GAMEPAD_SENSOR_UPDATE,
3837};
3838
3839void SDL_SetGamepadEventsEnabled(bool enabled)
3840{
3841 unsigned int i;
3842
3843 for (i = 0; i < SDL_arraysize(SDL_gamepad_event_list); ++i) {
3844 SDL_SetEventEnabled(SDL_gamepad_event_list[i], enabled);
3845 }
3846}
3847
3848bool SDL_GamepadEventsEnabled(void)
3849{
3850 bool enabled = false;
3851 unsigned int i;
3852
3853 for (i = 0; i < SDL_arraysize(SDL_gamepad_event_list); ++i) {
3854 enabled = SDL_EventEnabled(SDL_gamepad_event_list[i]);
3855 if (enabled) {
3856 break;
3857 }
3858 }
3859 return enabled;
3860}
3861
3862void SDL_GamepadHandleDelayedGuideButton(SDL_Joystick *joystick)
3863{
3864 SDL_Gamepad *gamepad;
3865
3866 SDL_AssertJoysticksLocked();
3867
3868 for (gamepad = SDL_gamepads; gamepad; gamepad = gamepad->next) {
3869 if (gamepad->joystick == joystick) {
3870 SDL_SendGamepadButton(0, gamepad, SDL_GAMEPAD_BUTTON_GUIDE, false);
3871
3872 // Make sure we send an update complete event for this change
3873 if (!gamepad->joystick->update_complete) {
3874 gamepad->joystick->update_complete = SDL_GetTicksNS();
3875 }
3876 break;
3877 }
3878 }
3879}
3880
3881const char *SDL_GetGamepadAppleSFSymbolsNameForButton(SDL_Gamepad *gamepad, SDL_GamepadButton button)
3882{
3883 const char *result = NULL;
3884#ifdef SDL_JOYSTICK_MFI
3885 const char *IOS_GetAppleSFSymbolsNameForButton(SDL_Gamepad *gamepad, SDL_GamepadButton button);
3886
3887 SDL_LockJoysticks();
3888 {
3889 CHECK_GAMEPAD_MAGIC(gamepad, NULL);
3890
3891 result = IOS_GetAppleSFSymbolsNameForButton(gamepad, button);
3892 }
3893 SDL_UnlockJoysticks();
3894#endif
3895 return result;
3896}
3897
3898const char *SDL_GetGamepadAppleSFSymbolsNameForAxis(SDL_Gamepad *gamepad, SDL_GamepadAxis axis)
3899{
3900 const char *result = NULL;
3901#ifdef SDL_JOYSTICK_MFI
3902 const char *IOS_GetAppleSFSymbolsNameForAxis(SDL_Gamepad *gamepad, SDL_GamepadAxis axis);
3903
3904 SDL_LockJoysticks();
3905 {
3906 CHECK_GAMEPAD_MAGIC(gamepad, NULL);
3907
3908 result = IOS_GetAppleSFSymbolsNameForAxis(gamepad, axis);
3909 }
3910 SDL_UnlockJoysticks();
3911#endif
3912 return result;
3913}
3914