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