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