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