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