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