Path: blob/master/thirdparty/sdl/joystick/SDL_gamepad.c
9903 views
/*1Simple DirectMedia Layer2Copyright (C) 1997-2025 Sam Lantinga <[email protected]>34This software is provided 'as-is', without any express or implied5warranty. In no event will the authors be held liable for any damages6arising from the use of this software.78Permission is granted to anyone to use this software for any purpose,9including commercial applications, and to alter it and redistribute it10freely, subject to the following restrictions:11121. The origin of this software must not be misrepresented; you must not13claim that you wrote the original software. If you use this software14in a product, an acknowledgment in the product documentation would be15appreciated but is not required.162. Altered source versions must be plainly marked as such, and must not be17misrepresented as being the original software.183. This notice may not be removed or altered from any source distribution.19*/20#include "SDL_internal.h"2122// This is the gamepad API for Simple DirectMedia Layer2324#include "SDL_sysjoystick.h"25#include "SDL_joystick_c.h"26#include "SDL_steam_virtual_gamepad.h"27#include "SDL_gamepad_c.h"28#include "SDL_gamepad_db.h"29#include "controller_type.h"30#include "usb_ids.h"31#include "hidapi/SDL_hidapi_nintendo.h"32#include "../events/SDL_events_c.h"333435#ifdef SDL_PLATFORM_ANDROID36#endif3738// Many gamepads turn the center button into an instantaneous button press39#define SDL_MINIMUM_GUIDE_BUTTON_DELAY_MS 2504041#define SDL_GAMEPAD_CRC_FIELD "crc:"42#define SDL_GAMEPAD_CRC_FIELD_SIZE 4 // hard-coded for speed43#define SDL_GAMEPAD_TYPE_FIELD "type:"44#define SDL_GAMEPAD_TYPE_FIELD_SIZE SDL_strlen(SDL_GAMEPAD_TYPE_FIELD)45#define SDL_GAMEPAD_FACE_FIELD "face:"46#define SDL_GAMEPAD_FACE_FIELD_SIZE 5 // hard-coded for speed47#define SDL_GAMEPAD_PLATFORM_FIELD "platform:"48#define SDL_GAMEPAD_PLATFORM_FIELD_SIZE SDL_strlen(SDL_GAMEPAD_PLATFORM_FIELD)49#define SDL_GAMEPAD_HINT_FIELD "hint:"50#define SDL_GAMEPAD_HINT_FIELD_SIZE SDL_strlen(SDL_GAMEPAD_HINT_FIELD)51#define SDL_GAMEPAD_SDKGE_FIELD "sdk>=:"52#define SDL_GAMEPAD_SDKGE_FIELD_SIZE SDL_strlen(SDL_GAMEPAD_SDKGE_FIELD)53#define SDL_GAMEPAD_SDKLE_FIELD "sdk<=:"54#define SDL_GAMEPAD_SDKLE_FIELD_SIZE SDL_strlen(SDL_GAMEPAD_SDKLE_FIELD)5556static bool SDL_gamepads_initialized;57static SDL_Gamepad *SDL_gamepads SDL_GUARDED_BY(SDL_joystick_lock) = NULL;5859// The face button style of a gamepad60typedef enum61{62SDL_GAMEPAD_FACE_STYLE_UNKNOWN,63SDL_GAMEPAD_FACE_STYLE_ABXY,64SDL_GAMEPAD_FACE_STYLE_BAYX,65SDL_GAMEPAD_FACE_STYLE_SONY,66} SDL_GamepadFaceStyle;6768// our hard coded list of mapping support69typedef enum70{71SDL_GAMEPAD_MAPPING_PRIORITY_DEFAULT,72SDL_GAMEPAD_MAPPING_PRIORITY_API,73SDL_GAMEPAD_MAPPING_PRIORITY_USER,74} SDL_GamepadMappingPriority;7576#define _guarded SDL_GUARDED_BY(SDL_joystick_lock)7778typedef struct GamepadMapping_t79{80SDL_GUID guid _guarded;81char *name _guarded;82char *mapping _guarded;83SDL_GamepadMappingPriority priority _guarded;84struct GamepadMapping_t *next _guarded;85} GamepadMapping_t;8687typedef struct88{89int refcount _guarded;90SDL_JoystickID *joysticks _guarded;91GamepadMapping_t **joystick_mappings _guarded;9293int num_changed_mappings _guarded;94GamepadMapping_t **changed_mappings _guarded;9596} MappingChangeTracker;9798#undef _guarded99100static SDL_GUID s_zeroGUID;101static GamepadMapping_t *s_pSupportedGamepads SDL_GUARDED_BY(SDL_joystick_lock) = NULL;102static GamepadMapping_t *s_pDefaultMapping SDL_GUARDED_BY(SDL_joystick_lock) = NULL;103static GamepadMapping_t *s_pXInputMapping SDL_GUARDED_BY(SDL_joystick_lock) = NULL;104static MappingChangeTracker *s_mappingChangeTracker SDL_GUARDED_BY(SDL_joystick_lock) = NULL;105static SDL_HashTable *s_gamepadInstanceIDs SDL_GUARDED_BY(SDL_joystick_lock) = NULL;106107#define _guarded SDL_GUARDED_BY(SDL_joystick_lock)108109// The SDL gamepad structure110struct SDL_Gamepad111{112SDL_Joystick *joystick _guarded; // underlying joystick device113int ref_count _guarded;114115const char *name _guarded;116SDL_GamepadType type _guarded;117SDL_GamepadFaceStyle face_style _guarded;118GamepadMapping_t *mapping _guarded;119int num_bindings _guarded;120SDL_GamepadBinding *bindings _guarded;121SDL_GamepadBinding **last_match_axis _guarded;122Uint8 *last_hat_mask _guarded;123Uint64 guide_button_down _guarded;124125struct SDL_Gamepad *next _guarded; // pointer to next gamepad we have allocated126};127128#undef _guarded129130#define CHECK_GAMEPAD_MAGIC(gamepad, result) \131if (!SDL_ObjectValid(gamepad, SDL_OBJECT_TYPE_GAMEPAD) || \132!SDL_IsJoystickValid(gamepad->joystick)) { \133SDL_InvalidParamError("gamepad"); \134SDL_UnlockJoysticks(); \135return result; \136}137138static SDL_vidpid_list SDL_allowed_gamepads = {139SDL_HINT_GAMECONTROLLER_IGNORE_DEVICES_EXCEPT, 0, 0, NULL,140NULL, 0, 0, NULL,1410, NULL,142false143};144static SDL_vidpid_list SDL_ignored_gamepads = {145SDL_HINT_GAMECONTROLLER_IGNORE_DEVICES, 0, 0, NULL,146NULL, 0, 0, NULL,1470, NULL,148false149};150151static GamepadMapping_t *SDL_PrivateAddMappingForGUID(SDL_GUID jGUID, const char *mappingString, bool *existing, SDL_GamepadMappingPriority priority);152static void SDL_PrivateLoadButtonMapping(SDL_Gamepad *gamepad, GamepadMapping_t *pGamepadMapping);153static GamepadMapping_t *SDL_PrivateGetGamepadMapping(SDL_JoystickID instance_id, bool create_mapping);154static void SDL_SendGamepadAxis(Uint64 timestamp, SDL_Gamepad *gamepad, SDL_GamepadAxis axis, Sint16 value);155static void SDL_SendGamepadButton(Uint64 timestamp, SDL_Gamepad *gamepad, SDL_GamepadButton button, bool down);156157static bool HasSameOutput(SDL_GamepadBinding *a, SDL_GamepadBinding *b)158{159if (a->output_type != b->output_type) {160return false;161}162163if (a->output_type == SDL_GAMEPAD_BINDTYPE_AXIS) {164return a->output.axis.axis == b->output.axis.axis;165} else {166return a->output.button == b->output.button;167}168}169170static void ResetOutput(Uint64 timestamp, SDL_Gamepad *gamepad, SDL_GamepadBinding *bind)171{172if (bind->output_type == SDL_GAMEPAD_BINDTYPE_AXIS) {173SDL_SendGamepadAxis(timestamp, gamepad, bind->output.axis.axis, 0);174} else {175SDL_SendGamepadButton(timestamp, gamepad, bind->output.button, false);176}177}178179static void HandleJoystickAxis(Uint64 timestamp, SDL_Gamepad *gamepad, int axis, int value)180{181int i;182SDL_GamepadBinding *last_match;183SDL_GamepadBinding *match = NULL;184185SDL_AssertJoysticksLocked();186187last_match = gamepad->last_match_axis[axis];188for (i = 0; i < gamepad->num_bindings; ++i) {189SDL_GamepadBinding *binding = &gamepad->bindings[i];190if (binding->input_type == SDL_GAMEPAD_BINDTYPE_AXIS &&191axis == binding->input.axis.axis) {192if (binding->input.axis.axis_min < binding->input.axis.axis_max) {193if (value >= binding->input.axis.axis_min &&194value <= binding->input.axis.axis_max) {195match = binding;196break;197}198} else {199if (value >= binding->input.axis.axis_max &&200value <= binding->input.axis.axis_min) {201match = binding;202break;203}204}205}206}207208if (last_match && (!match || !HasSameOutput(last_match, match))) {209// Clear the last input that this axis generated210ResetOutput(timestamp, gamepad, last_match);211}212213if (match) {214if (match->output_type == SDL_GAMEPAD_BINDTYPE_AXIS) {215if (match->input.axis.axis_min != match->output.axis.axis_min || match->input.axis.axis_max != match->output.axis.axis_max) {216float normalized_value = (float)(value - match->input.axis.axis_min) / (match->input.axis.axis_max - match->input.axis.axis_min);217value = match->output.axis.axis_min + (int)(normalized_value * (match->output.axis.axis_max - match->output.axis.axis_min));218}219SDL_SendGamepadAxis(timestamp, gamepad, match->output.axis.axis, (Sint16)value);220} else {221bool down;222int threshold = match->input.axis.axis_min + (match->input.axis.axis_max - match->input.axis.axis_min) / 2;223if (match->input.axis.axis_max < match->input.axis.axis_min) {224down = (value <= threshold);225} else {226down = (value >= threshold);227}228SDL_SendGamepadButton(timestamp, gamepad, match->output.button, down);229}230}231gamepad->last_match_axis[axis] = match;232}233234static void HandleJoystickButton(Uint64 timestamp, SDL_Gamepad *gamepad, int button, bool down)235{236int i;237238SDL_AssertJoysticksLocked();239240for (i = 0; i < gamepad->num_bindings; ++i) {241SDL_GamepadBinding *binding = &gamepad->bindings[i];242if (binding->input_type == SDL_GAMEPAD_BINDTYPE_BUTTON &&243button == binding->input.button) {244if (binding->output_type == SDL_GAMEPAD_BINDTYPE_AXIS) {245int value = down ? binding->output.axis.axis_max : binding->output.axis.axis_min;246SDL_SendGamepadAxis(timestamp, gamepad, binding->output.axis.axis, (Sint16)value);247} else {248SDL_SendGamepadButton(timestamp, gamepad, binding->output.button, down);249}250break;251}252}253}254255static void HandleJoystickHat(Uint64 timestamp, SDL_Gamepad *gamepad, int hat, Uint8 value)256{257int i;258Uint8 last_mask, changed_mask;259260SDL_AssertJoysticksLocked();261262last_mask = gamepad->last_hat_mask[hat];263changed_mask = (last_mask ^ value);264for (i = 0; i < gamepad->num_bindings; ++i) {265SDL_GamepadBinding *binding = &gamepad->bindings[i];266if (binding->input_type == SDL_GAMEPAD_BINDTYPE_HAT && hat == binding->input.hat.hat) {267if ((changed_mask & binding->input.hat.hat_mask) != 0) {268if (value & binding->input.hat.hat_mask) {269if (binding->output_type == SDL_GAMEPAD_BINDTYPE_AXIS) {270SDL_SendGamepadAxis(timestamp, gamepad, binding->output.axis.axis, (Sint16)binding->output.axis.axis_max);271} else {272SDL_SendGamepadButton(timestamp, gamepad, binding->output.button, true);273}274} else {275ResetOutput(timestamp, gamepad, binding);276}277}278}279}280gamepad->last_hat_mask[hat] = value;281}282283/* The joystick layer will _also_ send events to recenter before disconnect,284but it has to make (sometimes incorrect) guesses at what being "centered"285is. The gamepad layer, however, can set a definite logical idle286position, so set them all here. If we happened to already be at the287center thanks to the joystick layer or idle hands, this won't generate288duplicate events. */289static void RecenterGamepad(SDL_Gamepad *gamepad)290{291int i;292Uint64 timestamp = SDL_GetTicksNS();293294for (i = 0; i < SDL_GAMEPAD_BUTTON_COUNT; ++i) {295SDL_GamepadButton button = (SDL_GamepadButton)i;296if (SDL_GetGamepadButton(gamepad, button)) {297SDL_SendGamepadButton(timestamp, gamepad, button, false);298}299}300301for (i = 0; i < SDL_GAMEPAD_AXIS_COUNT; ++i) {302SDL_GamepadAxis axis = (SDL_GamepadAxis)i;303if (SDL_GetGamepadAxis(gamepad, axis) != 0) {304SDL_SendGamepadAxis(timestamp, gamepad, axis, 0);305}306}307}308309void SDL_PrivateGamepadAdded(SDL_JoystickID instance_id)310{311SDL_Event event;312313if (!SDL_gamepads_initialized) {314return;315}316317event.type = SDL_EVENT_GAMEPAD_ADDED;318event.common.timestamp = 0;319event.gdevice.which = instance_id;320SDL_PushEvent(&event);321}322323void SDL_PrivateGamepadRemoved(SDL_JoystickID instance_id)324{325SDL_Event event;326SDL_Gamepad *gamepad;327328SDL_AssertJoysticksLocked();329330if (!SDL_gamepads_initialized) {331return;332}333334for (gamepad = SDL_gamepads; gamepad; gamepad = gamepad->next) {335if (gamepad->joystick->instance_id == instance_id) {336RecenterGamepad(gamepad);337break;338}339}340341event.type = SDL_EVENT_GAMEPAD_REMOVED;342event.common.timestamp = 0;343event.gdevice.which = instance_id;344SDL_PushEvent(&event);345}346347static void SDL_PrivateGamepadRemapped(SDL_JoystickID instance_id)348{349SDL_Event event;350351if (!SDL_gamepads_initialized || SDL_IsJoystickBeingAdded()) {352return;353}354355event.type = SDL_EVENT_GAMEPAD_REMAPPED;356event.common.timestamp = 0;357event.gdevice.which = instance_id;358SDL_PushEvent(&event);359}360361/*362* Event filter to fire gamepad events from joystick ones363*/364static bool SDLCALL SDL_GamepadEventWatcher(void *userdata, SDL_Event *event)365{366SDL_Gamepad *gamepad;367368switch (event->type) {369case SDL_EVENT_JOYSTICK_AXIS_MOTION:370{371SDL_AssertJoysticksLocked();372373for (gamepad = SDL_gamepads; gamepad; gamepad = gamepad->next) {374if (gamepad->joystick->instance_id == event->jaxis.which) {375HandleJoystickAxis(event->common.timestamp, gamepad, event->jaxis.axis, event->jaxis.value);376break;377}378}379} break;380case SDL_EVENT_JOYSTICK_BUTTON_DOWN:381case SDL_EVENT_JOYSTICK_BUTTON_UP:382{383SDL_AssertJoysticksLocked();384385for (gamepad = SDL_gamepads; gamepad; gamepad = gamepad->next) {386if (gamepad->joystick->instance_id == event->jbutton.which) {387HandleJoystickButton(event->common.timestamp, gamepad, event->jbutton.button, event->jbutton.down);388break;389}390}391} break;392case SDL_EVENT_JOYSTICK_HAT_MOTION:393{394SDL_AssertJoysticksLocked();395396for (gamepad = SDL_gamepads; gamepad; gamepad = gamepad->next) {397if (gamepad->joystick->instance_id == event->jhat.which) {398HandleJoystickHat(event->common.timestamp, gamepad, event->jhat.hat, event->jhat.value);399break;400}401}402} break;403case SDL_EVENT_JOYSTICK_UPDATE_COMPLETE:404{405SDL_AssertJoysticksLocked();406407if (SDL_EventEnabled(SDL_EVENT_GAMEPAD_UPDATE_COMPLETE)) {408for (gamepad = SDL_gamepads; gamepad; gamepad = gamepad->next) {409if (gamepad->joystick->instance_id == event->jdevice.which) {410SDL_Event deviceevent;411412deviceevent.type = SDL_EVENT_GAMEPAD_UPDATE_COMPLETE;413deviceevent.common.timestamp = event->jdevice.timestamp;414deviceevent.gdevice.which = event->jdevice.which;415SDL_PushEvent(&deviceevent);416break;417}418}419}420} break;421default:422break;423}424425return true;426}427428/* SDL defines sensor orientation relative to the device natural429orientation, so when it's changed orientation to be used as a430gamepad, change the sensor orientation to match.431*/432static void AdjustSensorOrientation(const SDL_Joystick *joystick, const float *src, float *dst)433{434unsigned int i, j;435436SDL_AssertJoysticksLocked();437438for (i = 0; i < 3; ++i) {439dst[i] = 0.0f;440for (j = 0; j < 3; ++j) {441dst[i] += joystick->sensor_transform[i][j] * src[j];442}443}444}445446/*447* Event filter to fire gamepad sensor events from system sensor events448*449* We don't use SDL_GamepadEventWatcher() for this because we want to450* deliver gamepad sensor events when system sensor events are disabled,451* and we also need to avoid a potential deadlock where joystick event452* delivery locks the joysticks and then the event queue, but sensor453* event delivery would lock the event queue and then from within the454* event watcher function lock the joysticks.455*/456void SDL_GamepadSensorWatcher(Uint64 timestamp, SDL_SensorID sensor, Uint64 sensor_timestamp, float *data, int num_values)457{458SDL_Gamepad *gamepad;459460SDL_LockJoysticks();461for (gamepad = SDL_gamepads; gamepad; gamepad = gamepad->next) {462if (gamepad->joystick->accel && gamepad->joystick->accel_sensor == sensor) {463float gamepad_data[3];464AdjustSensorOrientation(gamepad->joystick, data, gamepad_data);465SDL_SendJoystickSensor(timestamp, gamepad->joystick, SDL_SENSOR_ACCEL, sensor_timestamp, gamepad_data, SDL_arraysize(gamepad_data));466}467if (gamepad->joystick->gyro && gamepad->joystick->gyro_sensor == sensor) {468float gamepad_data[3];469AdjustSensorOrientation(gamepad->joystick, data, gamepad_data);470SDL_SendJoystickSensor(timestamp, gamepad->joystick, SDL_SENSOR_GYRO, sensor_timestamp, gamepad_data, SDL_arraysize(gamepad_data));471}472}473SDL_UnlockJoysticks();474}475476static void PushMappingChangeTracking(void)477{478MappingChangeTracker *tracker;479int i, num_joysticks;480481SDL_AssertJoysticksLocked();482483if (s_mappingChangeTracker) {484++s_mappingChangeTracker->refcount;485return;486}487s_mappingChangeTracker = (MappingChangeTracker *)SDL_calloc(1, sizeof(*tracker));488s_mappingChangeTracker->refcount = 1;489490// Save the list of joysticks and associated mappings491tracker = s_mappingChangeTracker;492tracker->joysticks = SDL_GetJoysticks(&num_joysticks);493if (!tracker->joysticks) {494return;495}496if (num_joysticks == 0) {497return;498}499tracker->joystick_mappings = (GamepadMapping_t **)SDL_malloc(num_joysticks * sizeof(*tracker->joystick_mappings));500if (!tracker->joystick_mappings) {501return;502}503for (i = 0; i < num_joysticks; ++i) {504tracker->joystick_mappings[i] = SDL_PrivateGetGamepadMapping(tracker->joysticks[i], false);505}506}507508static void AddMappingChangeTracking(GamepadMapping_t *mapping)509{510MappingChangeTracker *tracker;511int num_mappings;512GamepadMapping_t **new_mappings;513514SDL_AssertJoysticksLocked();515516SDL_assert(s_mappingChangeTracker != NULL);517tracker = s_mappingChangeTracker;518num_mappings = tracker->num_changed_mappings;519new_mappings = (GamepadMapping_t **)SDL_realloc(tracker->changed_mappings, (num_mappings + 1) * sizeof(*new_mappings));520if (new_mappings) {521tracker->changed_mappings = new_mappings;522tracker->changed_mappings[num_mappings] = mapping;523tracker->num_changed_mappings = (num_mappings + 1);524}525}526527static bool HasMappingChangeTracking(MappingChangeTracker *tracker, GamepadMapping_t *mapping)528{529int i;530531SDL_AssertJoysticksLocked();532533for (i = 0; i < tracker->num_changed_mappings; ++i) {534if (tracker->changed_mappings[i] == mapping) {535return true;536}537}538return false;539}540541static void PopMappingChangeTracking(void)542{543int i;544MappingChangeTracker *tracker;545546SDL_AssertJoysticksLocked();547548SDL_assert(s_mappingChangeTracker != NULL);549tracker = s_mappingChangeTracker;550--tracker->refcount;551if (tracker->refcount > 0) {552return;553}554s_mappingChangeTracker = NULL;555556// Now check to see what gamepads changed because of the mapping changes557if (tracker->joysticks && tracker->joystick_mappings) {558for (i = 0; tracker->joysticks[i]; ++i) {559// Looking up the new mapping might create one and associate it with the gamepad (and generate events)560SDL_JoystickID joystick = tracker->joysticks[i];561SDL_Gamepad *gamepad = SDL_GetGamepadFromID(joystick);562GamepadMapping_t *new_mapping = SDL_PrivateGetGamepadMapping(joystick, false);563GamepadMapping_t *old_mapping = gamepad ? gamepad->mapping : tracker->joystick_mappings[i];564565if (new_mapping && !old_mapping) {566SDL_InsertIntoHashTable(s_gamepadInstanceIDs, (void *)(uintptr_t)joystick, (const void *)true, true);567SDL_PrivateGamepadAdded(joystick);568} else if (old_mapping && !new_mapping) {569SDL_InsertIntoHashTable(s_gamepadInstanceIDs, (void *)(uintptr_t)joystick, (const void *)false, true);570SDL_PrivateGamepadRemoved(joystick);571} else if (old_mapping != new_mapping || HasMappingChangeTracking(tracker, new_mapping)) {572if (gamepad) {573SDL_PrivateLoadButtonMapping(gamepad, new_mapping);574}575SDL_PrivateGamepadRemapped(joystick);576}577}578}579580SDL_free(tracker->joysticks);581SDL_free(tracker->joystick_mappings);582SDL_free(tracker->changed_mappings);583SDL_free(tracker);584}585586#ifdef SDL_PLATFORM_ANDROID587/*588* Helper function to guess at a mapping based on the elements reported for this gamepad589*/590static GamepadMapping_t *SDL_CreateMappingForAndroidGamepad(SDL_GUID guid)591{592const int face_button_mask = ((1 << SDL_GAMEPAD_BUTTON_SOUTH) |593(1 << SDL_GAMEPAD_BUTTON_EAST) |594(1 << SDL_GAMEPAD_BUTTON_WEST) |595(1 << SDL_GAMEPAD_BUTTON_NORTH));596bool existing;597char mapping_string[1024];598int button_mask;599int axis_mask;600601button_mask = SDL_Swap16LE(*(Uint16 *)(&guid.data[sizeof(guid.data) - 4]));602axis_mask = SDL_Swap16LE(*(Uint16 *)(&guid.data[sizeof(guid.data) - 2]));603if (!button_mask && !axis_mask) {604// Accelerometer, shouldn't have a gamepad mapping605return NULL;606}607if (!(button_mask & face_button_mask)) {608// We don't know what buttons or axes are supported, don't make up a mapping609return NULL;610}611612SDL_strlcpy(mapping_string, "none,*,", sizeof(mapping_string));613614if (button_mask & (1 << SDL_GAMEPAD_BUTTON_SOUTH)) {615SDL_strlcat(mapping_string, "a:b0,", sizeof(mapping_string));616}617if (button_mask & (1 << SDL_GAMEPAD_BUTTON_EAST)) {618SDL_strlcat(mapping_string, "b:b1,", sizeof(mapping_string));619} else if (button_mask & (1 << SDL_GAMEPAD_BUTTON_BACK)) {620// Use the back button as "B" for easy UI navigation with TV remotes621SDL_strlcat(mapping_string, "b:b4,", sizeof(mapping_string));622button_mask &= ~(1 << SDL_GAMEPAD_BUTTON_BACK);623}624if (button_mask & (1 << SDL_GAMEPAD_BUTTON_WEST)) {625SDL_strlcat(mapping_string, "x:b2,", sizeof(mapping_string));626}627if (button_mask & (1 << SDL_GAMEPAD_BUTTON_NORTH)) {628SDL_strlcat(mapping_string, "y:b3,", sizeof(mapping_string));629}630if (button_mask & (1 << SDL_GAMEPAD_BUTTON_BACK)) {631SDL_strlcat(mapping_string, "back:b4,", sizeof(mapping_string));632}633if (button_mask & (1 << SDL_GAMEPAD_BUTTON_GUIDE)) {634// The guide button generally isn't functional (or acts as a home button) on most Android gamepads before Android 11635if (SDL_GetAndroidSDKVersion() >= 30 /* Android 11 */) {636SDL_strlcat(mapping_string, "guide:b5,", sizeof(mapping_string));637}638}639if (button_mask & (1 << SDL_GAMEPAD_BUTTON_START)) {640SDL_strlcat(mapping_string, "start:b6,", sizeof(mapping_string));641}642if (button_mask & (1 << SDL_GAMEPAD_BUTTON_LEFT_STICK)) {643SDL_strlcat(mapping_string, "leftstick:b7,", sizeof(mapping_string));644}645if (button_mask & (1 << SDL_GAMEPAD_BUTTON_RIGHT_STICK)) {646SDL_strlcat(mapping_string, "rightstick:b8,", sizeof(mapping_string));647}648if (button_mask & (1 << SDL_GAMEPAD_BUTTON_LEFT_SHOULDER)) {649SDL_strlcat(mapping_string, "leftshoulder:b9,", sizeof(mapping_string));650}651if (button_mask & (1 << SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER)) {652SDL_strlcat(mapping_string, "rightshoulder:b10,", sizeof(mapping_string));653}654if (button_mask & (1 << SDL_GAMEPAD_BUTTON_DPAD_UP)) {655SDL_strlcat(mapping_string, "dpup:b11,", sizeof(mapping_string));656}657if (button_mask & (1 << SDL_GAMEPAD_BUTTON_DPAD_DOWN)) {658SDL_strlcat(mapping_string, "dpdown:b12,", sizeof(mapping_string));659}660if (button_mask & (1 << SDL_GAMEPAD_BUTTON_DPAD_LEFT)) {661SDL_strlcat(mapping_string, "dpleft:b13,", sizeof(mapping_string));662}663if (button_mask & (1 << SDL_GAMEPAD_BUTTON_DPAD_RIGHT)) {664SDL_strlcat(mapping_string, "dpright:b14,", sizeof(mapping_string));665}666if (axis_mask & (1 << SDL_GAMEPAD_AXIS_LEFTX)) {667SDL_strlcat(mapping_string, "leftx:a0,", sizeof(mapping_string));668}669if (axis_mask & (1 << SDL_GAMEPAD_AXIS_LEFTY)) {670SDL_strlcat(mapping_string, "lefty:a1,", sizeof(mapping_string));671}672if (axis_mask & (1 << SDL_GAMEPAD_AXIS_RIGHTX)) {673SDL_strlcat(mapping_string, "rightx:a2,", sizeof(mapping_string));674}675if (axis_mask & (1 << SDL_GAMEPAD_AXIS_RIGHTY)) {676SDL_strlcat(mapping_string, "righty:a3,", sizeof(mapping_string));677}678if (axis_mask & (1 << SDL_GAMEPAD_AXIS_LEFT_TRIGGER)) {679SDL_strlcat(mapping_string, "lefttrigger:a4,", sizeof(mapping_string));680}681if (axis_mask & (1 << SDL_GAMEPAD_AXIS_RIGHT_TRIGGER)) {682SDL_strlcat(mapping_string, "righttrigger:a5,", sizeof(mapping_string));683}684685return SDL_PrivateAddMappingForGUID(guid, mapping_string, &existing, SDL_GAMEPAD_MAPPING_PRIORITY_DEFAULT);686}687#endif // SDL_PLATFORM_ANDROID688689/*690* Helper function to guess at a mapping for HIDAPI gamepads691*/692static GamepadMapping_t *SDL_CreateMappingForHIDAPIGamepad(SDL_GUID guid)693{694bool existing;695char mapping_string[1024];696Uint16 vendor;697Uint16 product;698699SDL_strlcpy(mapping_string, "none,*,", sizeof(mapping_string));700701SDL_GetJoystickGUIDInfo(guid, &vendor, &product, NULL, NULL);702703if ((vendor == USB_VENDOR_NINTENDO && product == USB_PRODUCT_NINTENDO_GAMECUBE_ADAPTER) ||704(vendor == USB_VENDOR_DRAGONRISE &&705(product == USB_PRODUCT_EVORETRO_GAMECUBE_ADAPTER1 ||706product == USB_PRODUCT_EVORETRO_GAMECUBE_ADAPTER2))) {707// GameCube driver has 12 buttons and 6 axes708SDL_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));709} else if (vendor == USB_VENDOR_NINTENDO &&710(guid.data[15] == k_eSwitchDeviceInfoControllerType_HVCLeft ||711guid.data[15] == k_eSwitchDeviceInfoControllerType_HVCRight ||712guid.data[15] == k_eSwitchDeviceInfoControllerType_NESLeft ||713guid.data[15] == k_eSwitchDeviceInfoControllerType_NESRight ||714guid.data[15] == k_eSwitchDeviceInfoControllerType_SNES ||715guid.data[15] == k_eSwitchDeviceInfoControllerType_N64 ||716guid.data[15] == k_eSwitchDeviceInfoControllerType_SEGA_Genesis ||717guid.data[15] == k_eWiiExtensionControllerType_None ||718guid.data[15] == k_eWiiExtensionControllerType_Nunchuk ||719guid.data[15] == k_eSwitchDeviceInfoControllerType_JoyConLeft ||720guid.data[15] == k_eSwitchDeviceInfoControllerType_JoyConRight)) {721switch (guid.data[15]) {722case k_eSwitchDeviceInfoControllerType_HVCLeft:723SDL_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));724break;725case k_eSwitchDeviceInfoControllerType_HVCRight:726SDL_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));727break;728case k_eSwitchDeviceInfoControllerType_NESLeft:729case k_eSwitchDeviceInfoControllerType_NESRight:730SDL_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));731break;732case k_eSwitchDeviceInfoControllerType_SNES:733SDL_strlcat(mapping_string, "a:b0,b:b1,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,lefttrigger:a4,rightshoulder:b10,righttrigger:a5,start:b6,x:b2,y:b3,hint:!SDL_GAMECONTROLLER_USE_BUTTON_LABELS:=1,", sizeof(mapping_string));734break;735case k_eSwitchDeviceInfoControllerType_N64:736SDL_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));737break;738case k_eSwitchDeviceInfoControllerType_SEGA_Genesis:739SDL_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));740break;741case k_eWiiExtensionControllerType_None:742SDL_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));743break;744case k_eWiiExtensionControllerType_Nunchuk:745{746// FIXME: Should we map this to the left or right side?747const bool map_nunchuck_left_side = true;748749if (map_nunchuck_left_side) {750SDL_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));751} else {752SDL_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));753}754} break;755default:756if (SDL_GetHintBoolean(SDL_HINT_JOYSTICK_HIDAPI_VERTICAL_JOY_CONS, false)) {757// Vertical mode758if (guid.data[15] == k_eSwitchDeviceInfoControllerType_JoyConLeft) {759SDL_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));760} else {761SDL_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));762}763} else {764// Mini gamepad mode765if (guid.data[15] == k_eSwitchDeviceInfoControllerType_JoyConLeft) {766SDL_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));767} else {768SDL_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));769}770}771break;772}773} else {774// All other gamepads have the standard set of 19 buttons and 6 axes775SDL_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));776777if (SDL_IsJoystickSteamController(vendor, product)) {778// Steam controllers have 2 back paddle buttons779SDL_strlcat(mapping_string, "paddle1:b12,paddle2:b11,", sizeof(mapping_string));780} else if (SDL_IsJoystickNintendoSwitchPro(vendor, product) ||781SDL_IsJoystickNintendoSwitchProInputOnly(vendor, product)) {782// Nintendo Switch Pro controllers have a screenshot button783SDL_strlcat(mapping_string, "misc1:b11,", sizeof(mapping_string));784} else if (SDL_IsJoystickNintendoSwitchJoyConPair(vendor, product)) {785// The Nintendo Switch Joy-Con combined controllers has a share button and paddles786SDL_strlcat(mapping_string, "misc1:b11,paddle1:b12,paddle2:b13,paddle3:b14,paddle4:b15,", sizeof(mapping_string));787} else if (SDL_IsJoystickAmazonLunaController(vendor, product)) {788// Amazon Luna Controller has a mic button under the guide button789SDL_strlcat(mapping_string, "misc1:b11,", sizeof(mapping_string));790} else if (SDL_IsJoystickGoogleStadiaController(vendor, product)) {791// The Google Stadia controller has a share button and a Google Assistant button792SDL_strlcat(mapping_string, "misc1:b11,misc2:b12", sizeof(mapping_string));793} else if (SDL_IsJoystickNVIDIASHIELDController(vendor, product)) {794// The NVIDIA SHIELD controller has a share button between back and start buttons795SDL_strlcat(mapping_string, "misc1:b11,", sizeof(mapping_string));796797if (product == USB_PRODUCT_NVIDIA_SHIELD_CONTROLLER_V103) {798// The original SHIELD controller has a touchpad and plus/minus buttons as well799SDL_strlcat(mapping_string, "touchpad:b12,misc2:b13,misc3:b14", sizeof(mapping_string));800}801} else if (SDL_IsJoystickHoriSteamController(vendor, product)) {802/* The Wireless HORIPad for Steam has QAM, Steam, Capsense L/R Sticks, 2 rear buttons, and 2 misc buttons */803SDL_strlcat(mapping_string, "paddle1:b13,paddle2:b12,paddle3:b15,paddle4:b14,misc2:b11,misc3:b16,misc4:b17", sizeof(mapping_string));804} else {805switch (SDL_GetGamepadTypeFromGUID(guid, NULL)) {806case SDL_GAMEPAD_TYPE_PS4:807// PS4 controllers have an additional touchpad button808SDL_strlcat(mapping_string, "touchpad:b11,", sizeof(mapping_string));809break;810case SDL_GAMEPAD_TYPE_PS5:811// PS5 controllers have a microphone button and an additional touchpad button812SDL_strlcat(mapping_string, "touchpad:b11,misc1:b12,", sizeof(mapping_string));813// DualSense Edge controllers have paddles814if (SDL_IsJoystickDualSenseEdge(vendor, product)) {815SDL_strlcat(mapping_string, "paddle1:b16,paddle2:b15,paddle3:b14,paddle4:b13,", sizeof(mapping_string));816}817break;818case SDL_GAMEPAD_TYPE_XBOXONE:819if (SDL_IsJoystickXboxOneElite(vendor, product)) {820// XBox One Elite Controllers have 4 back paddle buttons821SDL_strlcat(mapping_string, "paddle1:b11,paddle2:b13,paddle3:b12,paddle4:b14,", sizeof(mapping_string));822} else if (SDL_IsJoystickXboxSeriesX(vendor, product)) {823// XBox Series X Controllers have a share button under the guide button824SDL_strlcat(mapping_string, "misc1:b11,", sizeof(mapping_string));825}826break;827default:828if (vendor == 0 && product == 0) {829// This is a Bluetooth Nintendo Switch Pro controller830SDL_strlcat(mapping_string, "misc1:b11,", sizeof(mapping_string));831}832break;833}834}835}836837return SDL_PrivateAddMappingForGUID(guid, mapping_string, &existing, SDL_GAMEPAD_MAPPING_PRIORITY_DEFAULT);838}839840/*841* Helper function to guess at a mapping for RAWINPUT gamepads842*/843static GamepadMapping_t *SDL_CreateMappingForRAWINPUTGamepad(SDL_GUID guid)844{845bool existing;846char mapping_string[1024];847848SDL_strlcpy(mapping_string, "none,*,", sizeof(mapping_string));849SDL_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));850851return SDL_PrivateAddMappingForGUID(guid, mapping_string, &existing, SDL_GAMEPAD_MAPPING_PRIORITY_DEFAULT);852}853854/*855* Helper function to guess at a mapping for WGI gamepads856*/857static GamepadMapping_t *SDL_CreateMappingForWGIGamepad(SDL_GUID guid)858{859bool existing;860char mapping_string[1024];861862if (guid.data[15] != SDL_JOYSTICK_TYPE_GAMEPAD) {863return NULL;864}865866SDL_strlcpy(mapping_string, "none,*,", sizeof(mapping_string));867SDL_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));868869return SDL_PrivateAddMappingForGUID(guid, mapping_string, &existing, SDL_GAMEPAD_MAPPING_PRIORITY_DEFAULT);870}871872/*873* Helper function to scan the mappings database for a gamepad with the specified GUID874*/875static GamepadMapping_t *SDL_PrivateMatchGamepadMappingForGUID(SDL_GUID guid, bool match_version, bool exact_match_crc)876{877GamepadMapping_t *mapping, *best_match = NULL;878Uint16 crc = 0;879880SDL_AssertJoysticksLocked();881882SDL_GetJoystickGUIDInfo(guid, NULL, NULL, NULL, &crc);883884// Clear the CRC from the GUID for matching, the mappings never include it in the GUID885SDL_SetJoystickGUIDCRC(&guid, 0);886887if (!match_version) {888SDL_SetJoystickGUIDVersion(&guid, 0);889}890891for (mapping = s_pSupportedGamepads; mapping; mapping = mapping->next) {892SDL_GUID mapping_guid;893894if (SDL_memcmp(&mapping->guid, &s_zeroGUID, sizeof(mapping->guid)) == 0) {895continue;896}897898SDL_memcpy(&mapping_guid, &mapping->guid, sizeof(mapping_guid));899if (!match_version) {900SDL_SetJoystickGUIDVersion(&mapping_guid, 0);901}902903if (SDL_memcmp(&guid, &mapping_guid, sizeof(guid)) == 0) {904const char *crc_string = SDL_strstr(mapping->mapping, SDL_GAMEPAD_CRC_FIELD);905if (crc_string) {906Uint16 mapping_crc = (Uint16)SDL_strtol(crc_string + SDL_GAMEPAD_CRC_FIELD_SIZE, NULL, 16);907if (mapping_crc != crc) {908// This mapping specified a CRC and they don't match909continue;910}911912// An exact match, including CRC913return mapping;914} else if (crc && exact_match_crc) {915return NULL;916}917918if (!best_match) {919best_match = mapping;920}921}922}923return best_match;924}925926/*927* Helper function to scan the mappings database for a gamepad with the specified GUID928*/929static GamepadMapping_t *SDL_PrivateGetGamepadMappingForGUID(SDL_GUID guid, bool adding_mapping)930{931GamepadMapping_t *mapping;932933mapping = SDL_PrivateMatchGamepadMappingForGUID(guid, true, adding_mapping);934if (mapping) {935return mapping;936}937938if (adding_mapping) {939// We didn't find an existing mapping940return NULL;941}942943// Try harder to get the best match, or create a mapping944945if (SDL_JoystickGUIDUsesVersion(guid)) {946// Try again, ignoring the version947mapping = SDL_PrivateMatchGamepadMappingForGUID(guid, false, false);948if (mapping) {949return mapping;950}951}952953#ifdef SDL_JOYSTICK_XINPUT954if (SDL_IsJoystickXInput(guid)) {955// This is an XInput device956return s_pXInputMapping;957}958#endif959if (SDL_IsJoystickHIDAPI(guid)) {960mapping = SDL_CreateMappingForHIDAPIGamepad(guid);961} else if (SDL_IsJoystickRAWINPUT(guid)) {962mapping = SDL_CreateMappingForRAWINPUTGamepad(guid);963} else if (SDL_IsJoystickWGI(guid)) {964mapping = SDL_CreateMappingForWGIGamepad(guid);965} else if (SDL_IsJoystickVIRTUAL(guid)) {966// We'll pick up a robust mapping in VIRTUAL_JoystickGetGamepadMapping967#ifdef SDL_PLATFORM_ANDROID968} else {969mapping = SDL_CreateMappingForAndroidGamepad(guid);970#endif971}972return mapping;973}974975static const char *map_StringForGamepadType[] = {976"unknown",977"standard",978"xbox360",979"xboxone",980"ps3",981"ps4",982"ps5",983"switchpro",984"joyconleft",985"joyconright",986"joyconpair"987};988SDL_COMPILE_TIME_ASSERT(map_StringForGamepadType, SDL_arraysize(map_StringForGamepadType) == SDL_GAMEPAD_TYPE_COUNT);989990/*991* convert a string to its enum equivalent992*/993SDL_GamepadType SDL_GetGamepadTypeFromString(const char *str)994{995int i;996997if (!str || str[0] == '\0') {998return SDL_GAMEPAD_TYPE_UNKNOWN;999}10001001if (*str == '+' || *str == '-') {1002++str;1003}10041005for (i = 0; i < SDL_arraysize(map_StringForGamepadType); ++i) {1006if (SDL_strcasecmp(str, map_StringForGamepadType[i]) == 0) {1007return (SDL_GamepadType)i;1008}1009}1010return SDL_GAMEPAD_TYPE_UNKNOWN;1011}10121013/*1014* convert an enum to its string equivalent1015*/1016const char *SDL_GetGamepadStringForType(SDL_GamepadType type)1017{1018if (type >= SDL_GAMEPAD_TYPE_STANDARD && type < SDL_GAMEPAD_TYPE_COUNT) {1019return map_StringForGamepadType[type];1020}1021return NULL;1022}10231024static const char *map_StringForGamepadAxis[] = {1025"leftx",1026"lefty",1027"rightx",1028"righty",1029"lefttrigger",1030"righttrigger"1031};1032SDL_COMPILE_TIME_ASSERT(map_StringForGamepadAxis, SDL_arraysize(map_StringForGamepadAxis) == SDL_GAMEPAD_AXIS_COUNT);10331034/*1035* convert a string to its enum equivalent1036*/1037SDL_GamepadAxis SDL_GetGamepadAxisFromString(const char *str)1038{1039int i;10401041if (!str || str[0] == '\0') {1042return SDL_GAMEPAD_AXIS_INVALID;1043}10441045if (*str == '+' || *str == '-') {1046++str;1047}10481049for (i = 0; i < SDL_arraysize(map_StringForGamepadAxis); ++i) {1050if (SDL_strcasecmp(str, map_StringForGamepadAxis[i]) == 0) {1051return (SDL_GamepadAxis)i;1052}1053}1054return SDL_GAMEPAD_AXIS_INVALID;1055}10561057/*1058* convert an enum to its string equivalent1059*/1060const char *SDL_GetGamepadStringForAxis(SDL_GamepadAxis axis)1061{1062if (axis > SDL_GAMEPAD_AXIS_INVALID && axis < SDL_GAMEPAD_AXIS_COUNT) {1063return map_StringForGamepadAxis[axis];1064}1065return NULL;1066}10671068static const char *map_StringForGamepadButton[] = {1069"a",1070"b",1071"x",1072"y",1073"back",1074"guide",1075"start",1076"leftstick",1077"rightstick",1078"leftshoulder",1079"rightshoulder",1080"dpup",1081"dpdown",1082"dpleft",1083"dpright",1084"misc1",1085"paddle1",1086"paddle2",1087"paddle3",1088"paddle4",1089"touchpad",1090"misc2",1091"misc3",1092"misc4",1093"misc5",1094"misc6"1095};1096SDL_COMPILE_TIME_ASSERT(map_StringForGamepadButton, SDL_arraysize(map_StringForGamepadButton) == SDL_GAMEPAD_BUTTON_COUNT);10971098/*1099* convert a string to its enum equivalent1100*/1101static SDL_GamepadButton SDL_PrivateGetGamepadButtonFromString(const char *str, bool baxy)1102{1103int i;11041105if (!str || str[0] == '\0') {1106return SDL_GAMEPAD_BUTTON_INVALID;1107}11081109for (i = 0; i < SDL_arraysize(map_StringForGamepadButton); ++i) {1110if (SDL_strcasecmp(str, map_StringForGamepadButton[i]) == 0) {1111if (baxy) {1112// Need to swap face buttons1113switch (i) {1114case SDL_GAMEPAD_BUTTON_SOUTH:1115return SDL_GAMEPAD_BUTTON_EAST;1116case SDL_GAMEPAD_BUTTON_EAST:1117return SDL_GAMEPAD_BUTTON_SOUTH;1118case SDL_GAMEPAD_BUTTON_WEST:1119return SDL_GAMEPAD_BUTTON_NORTH;1120case SDL_GAMEPAD_BUTTON_NORTH:1121return SDL_GAMEPAD_BUTTON_WEST;1122default:1123break;1124}1125}1126return (SDL_GamepadButton)i;1127}1128}1129return SDL_GAMEPAD_BUTTON_INVALID;1130}1131SDL_GamepadButton SDL_GetGamepadButtonFromString(const char *str)1132{1133return SDL_PrivateGetGamepadButtonFromString(str, false);1134}11351136/*1137* convert an enum to its string equivalent1138*/1139const char *SDL_GetGamepadStringForButton(SDL_GamepadButton button)1140{1141if (button > SDL_GAMEPAD_BUTTON_INVALID && button < SDL_GAMEPAD_BUTTON_COUNT) {1142return map_StringForGamepadButton[button];1143}1144return NULL;1145}11461147/*1148* given a gamepad button name and a joystick name update our mapping structure with it1149*/1150static bool SDL_PrivateParseGamepadElement(SDL_Gamepad *gamepad, const char *szGameButton, const char *szJoystickButton)1151{1152SDL_GamepadBinding bind;1153SDL_GamepadButton button;1154SDL_GamepadAxis axis;1155bool invert_input = false;1156char half_axis_input = 0;1157char half_axis_output = 0;1158int i;1159SDL_GamepadBinding *new_bindings;1160bool baxy_mapping = false;11611162SDL_AssertJoysticksLocked();11631164SDL_zero(bind);11651166if (*szGameButton == '+' || *szGameButton == '-') {1167half_axis_output = *szGameButton++;1168}11691170if (SDL_strstr(gamepad->mapping->mapping, ",hint:SDL_GAMECONTROLLER_USE_BUTTON_LABELS:=1") != NULL) {1171baxy_mapping = true;1172}11731174axis = SDL_GetGamepadAxisFromString(szGameButton);1175button = SDL_PrivateGetGamepadButtonFromString(szGameButton, baxy_mapping);1176if (axis != SDL_GAMEPAD_AXIS_INVALID) {1177bind.output_type = SDL_GAMEPAD_BINDTYPE_AXIS;1178bind.output.axis.axis = axis;1179if (axis == SDL_GAMEPAD_AXIS_LEFT_TRIGGER || axis == SDL_GAMEPAD_AXIS_RIGHT_TRIGGER) {1180bind.output.axis.axis_min = 0;1181bind.output.axis.axis_max = SDL_JOYSTICK_AXIS_MAX;1182} else {1183if (half_axis_output == '+') {1184bind.output.axis.axis_min = 0;1185bind.output.axis.axis_max = SDL_JOYSTICK_AXIS_MAX;1186} else if (half_axis_output == '-') {1187bind.output.axis.axis_min = 0;1188bind.output.axis.axis_max = SDL_JOYSTICK_AXIS_MIN;1189} else {1190bind.output.axis.axis_min = SDL_JOYSTICK_AXIS_MIN;1191bind.output.axis.axis_max = SDL_JOYSTICK_AXIS_MAX;1192}1193}1194} else if (button != SDL_GAMEPAD_BUTTON_INVALID) {1195bind.output_type = SDL_GAMEPAD_BINDTYPE_BUTTON;1196bind.output.button = button;1197} else {1198return false;1199}12001201if (*szJoystickButton == '+' || *szJoystickButton == '-') {1202half_axis_input = *szJoystickButton++;1203}1204if (szJoystickButton[SDL_strlen(szJoystickButton) - 1] == '~') {1205invert_input = true;1206}12071208if (szJoystickButton[0] == 'a' && SDL_isdigit((unsigned char)szJoystickButton[1])) {1209bind.input_type = SDL_GAMEPAD_BINDTYPE_AXIS;1210bind.input.axis.axis = SDL_atoi(&szJoystickButton[1]);1211if (half_axis_input == '+') {1212bind.input.axis.axis_min = 0;1213bind.input.axis.axis_max = SDL_JOYSTICK_AXIS_MAX;1214} else if (half_axis_input == '-') {1215bind.input.axis.axis_min = 0;1216bind.input.axis.axis_max = SDL_JOYSTICK_AXIS_MIN;1217} else {1218bind.input.axis.axis_min = SDL_JOYSTICK_AXIS_MIN;1219bind.input.axis.axis_max = SDL_JOYSTICK_AXIS_MAX;1220}1221if (invert_input) {1222int tmp = bind.input.axis.axis_min;1223bind.input.axis.axis_min = bind.input.axis.axis_max;1224bind.input.axis.axis_max = tmp;1225}1226} else if (szJoystickButton[0] == 'b' && SDL_isdigit((unsigned char)szJoystickButton[1])) {1227bind.input_type = SDL_GAMEPAD_BINDTYPE_BUTTON;1228bind.input.button = SDL_atoi(&szJoystickButton[1]);1229} else if (szJoystickButton[0] == 'h' && SDL_isdigit((unsigned char)szJoystickButton[1]) &&1230szJoystickButton[2] == '.' && SDL_isdigit((unsigned char)szJoystickButton[3])) {1231int hat = SDL_atoi(&szJoystickButton[1]);1232int mask = SDL_atoi(&szJoystickButton[3]);1233bind.input_type = SDL_GAMEPAD_BINDTYPE_HAT;1234bind.input.hat.hat = hat;1235bind.input.hat.hat_mask = mask;1236} else {1237return false;1238}12391240for (i = 0; i < gamepad->num_bindings; ++i) {1241if (SDL_memcmp(&gamepad->bindings[i], &bind, sizeof(bind)) == 0) {1242// We already have this binding, could be different face button names?1243return true;1244}1245}12461247++gamepad->num_bindings;1248new_bindings = (SDL_GamepadBinding *)SDL_realloc(gamepad->bindings, gamepad->num_bindings * sizeof(*gamepad->bindings));1249if (!new_bindings) {1250SDL_free(gamepad->bindings);1251gamepad->num_bindings = 0;1252gamepad->bindings = NULL;1253return false;1254}1255gamepad->bindings = new_bindings;1256gamepad->bindings[gamepad->num_bindings - 1] = bind;1257return true;1258}12591260/*1261* given a gamepad mapping string update our mapping object1262*/1263static bool SDL_PrivateParseGamepadConfigString(SDL_Gamepad *gamepad, const char *pchString)1264{1265char szGameButton[20];1266char szJoystickButton[20];1267bool bGameButton = true;1268int i = 0;1269const char *pchPos = pchString;12701271SDL_zeroa(szGameButton);1272SDL_zeroa(szJoystickButton);12731274while (pchPos && *pchPos) {1275if (*pchPos == ':') {1276i = 0;1277bGameButton = false;1278} else if (*pchPos == ' ') {12791280} else if (*pchPos == ',') {1281i = 0;1282bGameButton = true;1283SDL_PrivateParseGamepadElement(gamepad, szGameButton, szJoystickButton);1284SDL_zeroa(szGameButton);1285SDL_zeroa(szJoystickButton);12861287} else if (bGameButton) {1288if (i >= sizeof(szGameButton)) {1289szGameButton[sizeof(szGameButton) - 1] = '\0';1290return SDL_SetError("Button name too large: %s", szGameButton);1291}1292szGameButton[i] = *pchPos;1293i++;1294} else {1295if (i >= sizeof(szJoystickButton)) {1296szJoystickButton[sizeof(szJoystickButton) - 1] = '\0';1297return SDL_SetError("Joystick button name too large: %s", szJoystickButton);1298}1299szJoystickButton[i] = *pchPos;1300i++;1301}1302pchPos++;1303}13041305// No more values if the string was terminated by a comma. Don't report an error.1306if (szGameButton[0] != '\0' || szJoystickButton[0] != '\0') {1307SDL_PrivateParseGamepadElement(gamepad, szGameButton, szJoystickButton);1308}1309return true;1310}13111312static void SDL_UpdateGamepadType(SDL_Gamepad *gamepad)1313{1314char *type_string, *comma;13151316SDL_AssertJoysticksLocked();13171318gamepad->type = SDL_GAMEPAD_TYPE_UNKNOWN;13191320type_string = SDL_strstr(gamepad->mapping->mapping, SDL_GAMEPAD_TYPE_FIELD);1321if (type_string) {1322type_string += SDL_GAMEPAD_TYPE_FIELD_SIZE;1323comma = SDL_strchr(type_string, ',');1324if (comma) {1325*comma = '\0';1326gamepad->type = SDL_GetGamepadTypeFromString(type_string);1327*comma = ',';1328} else {1329gamepad->type = SDL_GetGamepadTypeFromString(type_string);1330}1331}1332if (gamepad->type == SDL_GAMEPAD_TYPE_UNKNOWN) {1333gamepad->type = SDL_GetRealGamepadTypeForID(gamepad->joystick->instance_id);1334}1335}13361337static SDL_GamepadFaceStyle SDL_GetGamepadFaceStyleFromString(const char *string)1338{1339if (SDL_strcmp(string, "abxy") == 0) {1340return SDL_GAMEPAD_FACE_STYLE_ABXY;1341} else if (SDL_strcmp(string, "bayx") == 0) {1342return SDL_GAMEPAD_FACE_STYLE_BAYX;1343} else if (SDL_strcmp(string, "sony") == 0) {1344return SDL_GAMEPAD_FACE_STYLE_SONY;1345} else {1346return SDL_GAMEPAD_FACE_STYLE_UNKNOWN;1347}1348}13491350static SDL_GamepadFaceStyle SDL_GetGamepadFaceStyleForGamepadType(SDL_GamepadType type)1351{1352switch (type) {1353case SDL_GAMEPAD_TYPE_PS3:1354case SDL_GAMEPAD_TYPE_PS4:1355case SDL_GAMEPAD_TYPE_PS5:1356return SDL_GAMEPAD_FACE_STYLE_SONY;1357case SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_PRO:1358case SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_JOYCON_LEFT:1359case SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_JOYCON_RIGHT:1360case SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_JOYCON_PAIR:1361return SDL_GAMEPAD_FACE_STYLE_BAYX;1362default:1363return SDL_GAMEPAD_FACE_STYLE_ABXY;1364}1365}13661367static void SDL_UpdateGamepadFaceStyle(SDL_Gamepad *gamepad)1368{1369char *face_string, *comma;13701371SDL_AssertJoysticksLocked();13721373gamepad->face_style = SDL_GAMEPAD_FACE_STYLE_UNKNOWN;13741375face_string = SDL_strstr(gamepad->mapping->mapping, SDL_GAMEPAD_FACE_FIELD);1376if (face_string) {1377face_string += SDL_GAMEPAD_TYPE_FIELD_SIZE;1378comma = SDL_strchr(face_string, ',');1379if (comma) {1380*comma = '\0';1381gamepad->face_style = SDL_GetGamepadFaceStyleFromString(face_string);1382*comma = ',';1383} else {1384gamepad->face_style = SDL_GetGamepadFaceStyleFromString(face_string);1385}1386}13871388if (gamepad->face_style == SDL_GAMEPAD_FACE_STYLE_UNKNOWN &&1389SDL_strstr(gamepad->mapping->mapping, "SDL_GAMECONTROLLER_USE_BUTTON_LABELS") != NULL) {1390// This controller uses Nintendo button style1391gamepad->face_style = SDL_GAMEPAD_FACE_STYLE_BAYX;1392}1393if (gamepad->face_style == SDL_GAMEPAD_FACE_STYLE_UNKNOWN) {1394gamepad->face_style = SDL_GetGamepadFaceStyleForGamepadType(gamepad->type);1395}1396}13971398static void SDL_FixupHIDAPIMapping(SDL_Gamepad *gamepad)1399{1400// Check to see if we need fixup1401bool need_fixup = false;1402for (int i = 0; i < gamepad->num_bindings; ++i) {1403SDL_GamepadBinding *binding = &gamepad->bindings[i];1404if (binding->output_type == SDL_GAMEPAD_BINDTYPE_BUTTON &&1405binding->output.button >= SDL_GAMEPAD_BUTTON_DPAD_UP) {1406if (binding->input_type == SDL_GAMEPAD_BINDTYPE_BUTTON &&1407binding->input.button == binding->output.button) {1408// Old style binding1409need_fixup = true;1410}1411break;1412}1413}1414if (!need_fixup) {1415return;1416}14171418for (int i = 0; i < gamepad->num_bindings; ++i) {1419SDL_GamepadBinding *binding = &gamepad->bindings[i];1420if (binding->input_type == SDL_GAMEPAD_BINDTYPE_BUTTON &&1421binding->output_type == SDL_GAMEPAD_BINDTYPE_BUTTON) {1422switch (binding->output.button) {1423case SDL_GAMEPAD_BUTTON_DPAD_UP:1424binding->input_type = SDL_GAMEPAD_BINDTYPE_HAT;1425binding->input.hat.hat = 0;1426binding->input.hat.hat_mask = SDL_HAT_UP;1427break;1428case SDL_GAMEPAD_BUTTON_DPAD_DOWN:1429binding->input_type = SDL_GAMEPAD_BINDTYPE_HAT;1430binding->input.hat.hat = 0;1431binding->input.hat.hat_mask = SDL_HAT_DOWN;1432break;1433case SDL_GAMEPAD_BUTTON_DPAD_LEFT:1434binding->input_type = SDL_GAMEPAD_BINDTYPE_HAT;1435binding->input.hat.hat = 0;1436binding->input.hat.hat_mask = SDL_HAT_LEFT;1437break;1438case SDL_GAMEPAD_BUTTON_DPAD_RIGHT:1439binding->input_type = SDL_GAMEPAD_BINDTYPE_HAT;1440binding->input.hat.hat = 0;1441binding->input.hat.hat_mask = SDL_HAT_RIGHT;1442break;1443default:1444if (binding->output.button > SDL_GAMEPAD_BUTTON_DPAD_RIGHT) {1445binding->input.button -= 4;1446}1447break;1448}1449}1450}1451}14521453/*1454* Make a new button mapping struct1455*/1456static void SDL_PrivateLoadButtonMapping(SDL_Gamepad *gamepad, GamepadMapping_t *pGamepadMapping)1457{1458int i;14591460SDL_AssertJoysticksLocked();14611462gamepad->name = pGamepadMapping->name;1463gamepad->num_bindings = 0;1464gamepad->mapping = pGamepadMapping;1465if (gamepad->joystick->naxes != 0 && gamepad->last_match_axis) {1466SDL_memset(gamepad->last_match_axis, 0, gamepad->joystick->naxes * sizeof(*gamepad->last_match_axis));1467}14681469SDL_UpdateGamepadType(gamepad);1470SDL_UpdateGamepadFaceStyle(gamepad);14711472SDL_PrivateParseGamepadConfigString(gamepad, pGamepadMapping->mapping);14731474if (SDL_IsJoystickHIDAPI(pGamepadMapping->guid)) {1475SDL_FixupHIDAPIMapping(gamepad);1476}14771478// Set the zero point for triggers1479for (i = 0; i < gamepad->num_bindings; ++i) {1480SDL_GamepadBinding *binding = &gamepad->bindings[i];1481if (binding->input_type == SDL_GAMEPAD_BINDTYPE_AXIS &&1482binding->output_type == SDL_GAMEPAD_BINDTYPE_AXIS &&1483(binding->output.axis.axis == SDL_GAMEPAD_AXIS_LEFT_TRIGGER ||1484binding->output.axis.axis == SDL_GAMEPAD_AXIS_RIGHT_TRIGGER)) {1485if (binding->input.axis.axis < gamepad->joystick->naxes) {1486gamepad->joystick->axes[binding->input.axis.axis].value =1487gamepad->joystick->axes[binding->input.axis.axis].zero = (Sint16)binding->input.axis.axis_min;1488}1489}1490}1491}14921493/*1494* grab the guid string from a mapping string1495*/1496static char *SDL_PrivateGetGamepadGUIDFromMappingString(const char *pMapping)1497{1498const char *pFirstComma = SDL_strchr(pMapping, ',');1499if (pFirstComma) {1500char *pchGUID = (char *)SDL_malloc(pFirstComma - pMapping + 1);1501if (!pchGUID) {1502return NULL;1503}1504SDL_memcpy(pchGUID, pMapping, pFirstComma - pMapping);1505pchGUID[pFirstComma - pMapping] = '\0';15061507// Convert old style GUIDs to the new style in 2.0.51508#if defined(SDL_PLATFORM_WIN32) || defined(SDL_PLATFORM_WINGDK)1509if (SDL_strlen(pchGUID) == 32 &&1510SDL_memcmp(&pchGUID[20], "504944564944", 12) == 0) {1511SDL_memcpy(&pchGUID[20], "000000000000", 12);1512SDL_memcpy(&pchGUID[16], &pchGUID[4], 4);1513SDL_memcpy(&pchGUID[8], &pchGUID[0], 4);1514SDL_memcpy(&pchGUID[0], "03000000", 8);1515}1516#elif defined(SDL_PLATFORM_MACOS)1517if (SDL_strlen(pchGUID) == 32 &&1518SDL_memcmp(&pchGUID[4], "000000000000", 12) == 0 &&1519SDL_memcmp(&pchGUID[20], "000000000000", 12) == 0) {1520SDL_memcpy(&pchGUID[20], "000000000000", 12);1521SDL_memcpy(&pchGUID[8], &pchGUID[0], 4);1522SDL_memcpy(&pchGUID[0], "03000000", 8);1523}1524#endif1525return pchGUID;1526}1527return NULL;1528}15291530/*1531* grab the name string from a mapping string1532*/1533static char *SDL_PrivateGetGamepadNameFromMappingString(const char *pMapping)1534{1535const char *pFirstComma, *pSecondComma;1536char *pchName;15371538pFirstComma = SDL_strchr(pMapping, ',');1539if (!pFirstComma) {1540return NULL;1541}15421543pSecondComma = SDL_strchr(pFirstComma + 1, ',');1544if (!pSecondComma) {1545return NULL;1546}15471548pchName = (char *)SDL_malloc(pSecondComma - pFirstComma);1549if (!pchName) {1550return NULL;1551}1552SDL_memcpy(pchName, pFirstComma + 1, pSecondComma - pFirstComma);1553pchName[pSecondComma - pFirstComma - 1] = 0;1554return pchName;1555}15561557/*1558* grab the button mapping string from a mapping string1559*/1560static char *SDL_PrivateGetGamepadMappingFromMappingString(const char *pMapping)1561{1562const char *pFirstComma, *pSecondComma;1563char *result;1564size_t length;15651566pFirstComma = SDL_strchr(pMapping, ',');1567if (!pFirstComma) {1568return NULL;1569}15701571pSecondComma = SDL_strchr(pFirstComma + 1, ',');1572if (!pSecondComma) {1573return NULL;1574}15751576// Skip whitespace1577while (SDL_isspace(pSecondComma[1])) {1578++pSecondComma;1579}15801581result = SDL_strdup(pSecondComma + 1); // mapping is everything after the 3rd comma15821583// Trim whitespace1584length = SDL_strlen(result);1585while (length > 0 && SDL_isspace(result[length - 1])) {1586--length;1587}1588result[length] = '\0';15891590return result;1591}15921593/*1594* Helper function to add a mapping for a guid1595*/1596static GamepadMapping_t *SDL_PrivateAddMappingForGUID(SDL_GUID jGUID, const char *mappingString, bool *existing, SDL_GamepadMappingPriority priority)1597{1598char *pchName;1599char *pchMapping;1600GamepadMapping_t *pGamepadMapping;1601Uint16 crc;16021603SDL_AssertJoysticksLocked();16041605pchName = SDL_PrivateGetGamepadNameFromMappingString(mappingString);1606if (!pchName) {1607SDL_SetError("Couldn't parse name from %s", mappingString);1608return NULL;1609}16101611pchMapping = SDL_PrivateGetGamepadMappingFromMappingString(mappingString);1612if (!pchMapping) {1613SDL_free(pchName);1614SDL_SetError("Couldn't parse %s", mappingString);1615return NULL;1616}16171618// Fix up the GUID and the mapping with the CRC, if needed1619SDL_GetJoystickGUIDInfo(jGUID, NULL, NULL, NULL, &crc);1620if (crc) {1621// Make sure the mapping has the CRC1622char *new_mapping;1623const char *optional_comma;1624size_t mapping_length;1625char *crc_end = "";1626char *crc_string = SDL_strstr(pchMapping, SDL_GAMEPAD_CRC_FIELD);1627if (crc_string) {1628crc_end = SDL_strchr(crc_string, ',');1629if (crc_end) {1630++crc_end;1631} else {1632crc_end = "";1633}1634*crc_string = '\0';1635}16361637// Make sure there's a comma before the CRC1638mapping_length = SDL_strlen(pchMapping);1639if (mapping_length == 0 || pchMapping[mapping_length - 1] == ',') {1640optional_comma = "";1641} else {1642optional_comma = ",";1643}16441645if (SDL_asprintf(&new_mapping, "%s%s%s%.4x,%s", pchMapping, optional_comma, SDL_GAMEPAD_CRC_FIELD, crc, crc_end) >= 0) {1646SDL_free(pchMapping);1647pchMapping = new_mapping;1648}1649} else {1650// Make sure the GUID has the CRC, for matching purposes1651char *crc_string = SDL_strstr(pchMapping, SDL_GAMEPAD_CRC_FIELD);1652if (crc_string) {1653crc = (Uint16)SDL_strtol(crc_string + SDL_GAMEPAD_CRC_FIELD_SIZE, NULL, 16);1654if (crc) {1655SDL_SetJoystickGUIDCRC(&jGUID, crc);1656}1657}1658}16591660PushMappingChangeTracking();16611662pGamepadMapping = SDL_PrivateGetGamepadMappingForGUID(jGUID, true);1663if (pGamepadMapping) {1664// Only overwrite the mapping if the priority is the same or higher.1665if (pGamepadMapping->priority <= priority) {1666// Update existing mapping1667SDL_free(pGamepadMapping->name);1668pGamepadMapping->name = pchName;1669SDL_free(pGamepadMapping->mapping);1670pGamepadMapping->mapping = pchMapping;1671pGamepadMapping->priority = priority;1672} else {1673SDL_free(pchName);1674SDL_free(pchMapping);1675}1676if (existing) {1677*existing = true;1678}1679AddMappingChangeTracking(pGamepadMapping);1680} else {1681pGamepadMapping = (GamepadMapping_t *)SDL_malloc(sizeof(*pGamepadMapping));1682if (!pGamepadMapping) {1683PopMappingChangeTracking();1684SDL_free(pchName);1685SDL_free(pchMapping);1686return NULL;1687}1688// Clear the CRC, we've already added it to the mapping1689if (crc) {1690SDL_SetJoystickGUIDCRC(&jGUID, 0);1691}1692pGamepadMapping->guid = jGUID;1693pGamepadMapping->name = pchName;1694pGamepadMapping->mapping = pchMapping;1695pGamepadMapping->next = NULL;1696pGamepadMapping->priority = priority;16971698if (s_pSupportedGamepads) {1699// Add the mapping to the end of the list1700GamepadMapping_t *pCurrMapping, *pPrevMapping;17011702for (pPrevMapping = s_pSupportedGamepads, pCurrMapping = pPrevMapping->next;1703pCurrMapping;1704pPrevMapping = pCurrMapping, pCurrMapping = pCurrMapping->next) {1705// continue;1706}1707pPrevMapping->next = pGamepadMapping;1708} else {1709s_pSupportedGamepads = pGamepadMapping;1710}1711if (existing) {1712*existing = false;1713}1714}17151716PopMappingChangeTracking();17171718return pGamepadMapping;1719}17201721/*1722* Helper function to determine pre-calculated offset to certain joystick mappings1723*/1724static GamepadMapping_t *SDL_PrivateGetGamepadMappingForNameAndGUID(const char *name, SDL_GUID guid)1725{1726GamepadMapping_t *mapping;17271728SDL_AssertJoysticksLocked();17291730mapping = SDL_PrivateGetGamepadMappingForGUID(guid, false);1731#ifdef SDL_PLATFORM_LINUX1732if (!mapping && name) {1733if (SDL_strstr(name, "Xbox 360 Wireless Receiver")) {1734// The Linux driver xpad.c maps the wireless dpad to buttons1735bool existing;1736mapping = SDL_PrivateAddMappingForGUID(guid,1737"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,",1738&existing, SDL_GAMEPAD_MAPPING_PRIORITY_DEFAULT);1739}1740}1741#endif // SDL_PLATFORM_LINUX17421743return mapping;1744}17451746static void SDL_PrivateAppendToMappingString(char *mapping_string,1747size_t mapping_string_len,1748const char *input_name,1749SDL_InputMapping *mapping)1750{1751char buffer[16];1752if (mapping->kind == EMappingKind_None) {1753return;1754}17551756SDL_strlcat(mapping_string, input_name, mapping_string_len);1757SDL_strlcat(mapping_string, ":", mapping_string_len);1758switch (mapping->kind) {1759case EMappingKind_Button:1760(void)SDL_snprintf(buffer, sizeof(buffer), "b%u", mapping->target);1761break;1762case EMappingKind_Axis:1763(void)SDL_snprintf(buffer, sizeof(buffer), "%sa%u%s",1764mapping->half_axis_positive ? "+" :1765mapping->half_axis_negative ? "-" : "",1766mapping->target,1767mapping->axis_reversed ? "~" : "");1768break;1769case EMappingKind_Hat:1770(void)SDL_snprintf(buffer, sizeof(buffer), "h%i.%i", mapping->target >> 4, mapping->target & 0x0F);1771break;1772default:1773SDL_assert(false);1774}17751776SDL_strlcat(mapping_string, buffer, mapping_string_len);1777SDL_strlcat(mapping_string, ",", mapping_string_len);1778}17791780static GamepadMapping_t *SDL_PrivateGenerateAutomaticGamepadMapping(const char *name,1781SDL_GUID guid,1782SDL_GamepadMapping *raw_map)1783{1784bool existing;1785char name_string[128];1786char mapping[1024];17871788// Remove any commas in the name1789SDL_strlcpy(name_string, name, sizeof(name_string));1790{1791char *spot;1792for (spot = name_string; *spot; ++spot) {1793if (*spot == ',') {1794*spot = ' ';1795}1796}1797}1798(void)SDL_snprintf(mapping, sizeof(mapping), "none,%s,", name_string);1799SDL_PrivateAppendToMappingString(mapping, sizeof(mapping), "a", &raw_map->a);1800SDL_PrivateAppendToMappingString(mapping, sizeof(mapping), "b", &raw_map->b);1801SDL_PrivateAppendToMappingString(mapping, sizeof(mapping), "x", &raw_map->x);1802SDL_PrivateAppendToMappingString(mapping, sizeof(mapping), "y", &raw_map->y);1803SDL_PrivateAppendToMappingString(mapping, sizeof(mapping), "back", &raw_map->back);1804SDL_PrivateAppendToMappingString(mapping, sizeof(mapping), "guide", &raw_map->guide);1805SDL_PrivateAppendToMappingString(mapping, sizeof(mapping), "start", &raw_map->start);1806SDL_PrivateAppendToMappingString(mapping, sizeof(mapping), "leftstick", &raw_map->leftstick);1807SDL_PrivateAppendToMappingString(mapping, sizeof(mapping), "rightstick", &raw_map->rightstick);1808SDL_PrivateAppendToMappingString(mapping, sizeof(mapping), "leftshoulder", &raw_map->leftshoulder);1809SDL_PrivateAppendToMappingString(mapping, sizeof(mapping), "rightshoulder", &raw_map->rightshoulder);1810SDL_PrivateAppendToMappingString(mapping, sizeof(mapping), "dpup", &raw_map->dpup);1811SDL_PrivateAppendToMappingString(mapping, sizeof(mapping), "dpdown", &raw_map->dpdown);1812SDL_PrivateAppendToMappingString(mapping, sizeof(mapping), "dpleft", &raw_map->dpleft);1813SDL_PrivateAppendToMappingString(mapping, sizeof(mapping), "dpright", &raw_map->dpright);1814SDL_PrivateAppendToMappingString(mapping, sizeof(mapping), "misc1", &raw_map->misc1);1815SDL_PrivateAppendToMappingString(mapping, sizeof(mapping), "misc2", &raw_map->misc2);1816SDL_PrivateAppendToMappingString(mapping, sizeof(mapping), "misc3", &raw_map->misc3);1817SDL_PrivateAppendToMappingString(mapping, sizeof(mapping), "misc4", &raw_map->misc4);1818SDL_PrivateAppendToMappingString(mapping, sizeof(mapping), "misc5", &raw_map->misc5);1819SDL_PrivateAppendToMappingString(mapping, sizeof(mapping), "misc6", &raw_map->misc6);1820/* Keep using paddle1-4 in the generated mapping so that it can be1821* reused with SDL2 */1822SDL_PrivateAppendToMappingString(mapping, sizeof(mapping), "paddle1", &raw_map->right_paddle1);1823SDL_PrivateAppendToMappingString(mapping, sizeof(mapping), "paddle2", &raw_map->left_paddle1);1824SDL_PrivateAppendToMappingString(mapping, sizeof(mapping), "paddle3", &raw_map->right_paddle2);1825SDL_PrivateAppendToMappingString(mapping, sizeof(mapping), "paddle4", &raw_map->left_paddle2);1826SDL_PrivateAppendToMappingString(mapping, sizeof(mapping), "leftx", &raw_map->leftx);1827SDL_PrivateAppendToMappingString(mapping, sizeof(mapping), "lefty", &raw_map->lefty);1828SDL_PrivateAppendToMappingString(mapping, sizeof(mapping), "rightx", &raw_map->rightx);1829SDL_PrivateAppendToMappingString(mapping, sizeof(mapping), "righty", &raw_map->righty);1830SDL_PrivateAppendToMappingString(mapping, sizeof(mapping), "lefttrigger", &raw_map->lefttrigger);1831SDL_PrivateAppendToMappingString(mapping, sizeof(mapping), "righttrigger", &raw_map->righttrigger);1832SDL_PrivateAppendToMappingString(mapping, sizeof(mapping), "touchpad", &raw_map->touchpad);18331834return SDL_PrivateAddMappingForGUID(guid, mapping, &existing, SDL_GAMEPAD_MAPPING_PRIORITY_DEFAULT);1835}18361837static GamepadMapping_t *SDL_PrivateGetGamepadMapping(SDL_JoystickID instance_id, bool create_mapping)1838{1839const char *name;1840SDL_GUID guid;1841GamepadMapping_t *mapping;18421843SDL_AssertJoysticksLocked();18441845name = SDL_GetJoystickNameForID(instance_id);1846guid = SDL_GetJoystickGUIDForID(instance_id);1847mapping = SDL_PrivateGetGamepadMappingForNameAndGUID(name, guid);1848if (!mapping && create_mapping) {1849SDL_GamepadMapping raw_map;18501851SDL_zero(raw_map);1852if (SDL_PrivateJoystickGetAutoGamepadMapping(instance_id, &raw_map)) {1853mapping = SDL_PrivateGenerateAutomaticGamepadMapping(name, guid, &raw_map);1854}1855}18561857if (!mapping) {1858mapping = s_pDefaultMapping;1859}1860return mapping;1861}18621863/*1864* Add or update an entry into the Mappings Database1865*/1866int SDL_AddGamepadMappingsFromIO(SDL_IOStream *src, bool closeio)1867{1868const char *platform = SDL_GetPlatform();1869int gamepads = 0;1870char *buf, *line, *line_end, *tmp, *comma, line_platform[64];1871size_t db_size;1872size_t platform_len;18731874buf = (char *)SDL_LoadFile_IO(src, &db_size, closeio);1875if (!buf) {1876SDL_SetError("Could not allocate space to read DB into memory");1877return -1;1878}1879line = buf;18801881SDL_LockJoysticks();18821883PushMappingChangeTracking();18841885while (line < buf + db_size) {1886line_end = SDL_strchr(line, '\n');1887if (line_end) {1888*line_end = '\0';1889} else {1890line_end = buf + db_size;1891}18921893// Extract and verify the platform1894tmp = SDL_strstr(line, SDL_GAMEPAD_PLATFORM_FIELD);1895if (tmp) {1896tmp += SDL_GAMEPAD_PLATFORM_FIELD_SIZE;1897comma = SDL_strchr(tmp, ',');1898if (comma) {1899platform_len = comma - tmp + 1;1900if (platform_len + 1 < SDL_arraysize(line_platform)) {1901SDL_strlcpy(line_platform, tmp, platform_len);1902if (SDL_strncasecmp(line_platform, platform, platform_len) == 0 &&1903SDL_AddGamepadMapping(line) > 0) {1904gamepads++;1905}1906}1907}1908}19091910line = line_end + 1;1911}19121913PopMappingChangeTracking();19141915SDL_UnlockJoysticks();19161917SDL_free(buf);1918return gamepads;1919}19201921int SDL_AddGamepadMappingsFromFile(const char *file)1922{1923return SDL_AddGamepadMappingsFromIO(SDL_IOFromFile(file, "rb"), true);1924}19251926bool SDL_ReloadGamepadMappings(void)1927{1928SDL_Gamepad *gamepad;19291930SDL_LockJoysticks();19311932PushMappingChangeTracking();19331934for (gamepad = SDL_gamepads; gamepad; gamepad = gamepad->next) {1935AddMappingChangeTracking(gamepad->mapping);1936}19371938SDL_QuitGamepadMappings();1939SDL_InitGamepadMappings();19401941PopMappingChangeTracking();19421943SDL_UnlockJoysticks();19441945return true;1946}19471948static char *SDL_ConvertMappingToPositional(const char *mapping)1949{1950// Add space for '!' and null terminator1951size_t length = SDL_strlen(mapping) + 1 + 1;1952char *remapped = (char *)SDL_malloc(length);1953if (remapped) {1954char *button_A;1955char *button_B;1956char *button_X;1957char *button_Y;1958char *hint;19591960SDL_strlcpy(remapped, mapping, length);1961button_A = SDL_strstr(remapped, "a:");1962button_B = SDL_strstr(remapped, "b:");1963button_X = SDL_strstr(remapped, "x:");1964button_Y = SDL_strstr(remapped, "y:");1965hint = SDL_strstr(remapped, "hint:SDL_GAMECONTROLLER_USE_BUTTON_LABELS");19661967if (button_A) {1968*button_A = 'b';1969}1970if (button_B) {1971*button_B = 'a';1972}1973if (button_X) {1974*button_X = 'y';1975}1976if (button_Y) {1977*button_Y = 'x';1978}1979if (hint) {1980hint += 5;1981SDL_memmove(hint + 1, hint, SDL_strlen(hint) + 1);1982*hint = '!';1983}1984}1985return remapped;1986}19871988/*1989* Add or update an entry into the Mappings Database with a priority1990*/1991static int SDL_PrivateAddGamepadMapping(const char *mappingString, SDL_GamepadMappingPriority priority)1992{1993char *remapped = NULL;1994char *pchGUID;1995SDL_GUID jGUID;1996bool is_default_mapping = false;1997bool is_xinput_mapping = false;1998bool existing = false;1999GamepadMapping_t *pGamepadMapping;2000int result = -1;20012002SDL_AssertJoysticksLocked();20032004if (!mappingString) {2005SDL_InvalidParamError("mappingString");2006return -1;2007}20082009{ // Extract and verify the hint field2010const char *tmp;20112012tmp = SDL_strstr(mappingString, SDL_GAMEPAD_HINT_FIELD);2013if (tmp) {2014bool default_value, value, negate;2015int len;2016char hint[128];20172018tmp += SDL_GAMEPAD_HINT_FIELD_SIZE;20192020if (*tmp == '!') {2021negate = true;2022++tmp;2023} else {2024negate = false;2025}20262027len = 0;2028while (*tmp && *tmp != ',' && *tmp != ':' && len < (sizeof(hint) - 1)) {2029hint[len++] = *tmp++;2030}2031hint[len] = '\0';20322033if (tmp[0] == ':' && tmp[1] == '=') {2034tmp += 2;2035default_value = SDL_atoi(tmp);2036} else {2037default_value = false;2038}20392040if (SDL_strcmp(hint, "SDL_GAMECONTROLLER_USE_BUTTON_LABELS") == 0) {2041// This hint is used to signal whether the mapping uses positional buttons or not2042if (negate) {2043// This mapping uses positional buttons, we can use it as-is2044} else {2045// This mapping uses labeled buttons, we need to swap them to positional2046remapped = SDL_ConvertMappingToPositional(mappingString);2047if (!remapped) {2048goto done;2049}2050mappingString = remapped;2051}2052} else {2053value = SDL_GetHintBoolean(hint, default_value);2054if (negate) {2055value = !value;2056}2057if (!value) {2058result = 0;2059goto done;2060}2061}2062}2063}20642065#ifdef ANDROID2066{ // Extract and verify the SDK version2067const char *tmp;20682069tmp = SDL_strstr(mappingString, SDL_GAMEPAD_SDKGE_FIELD);2070if (tmp) {2071tmp += SDL_GAMEPAD_SDKGE_FIELD_SIZE;2072if (!(SDL_GetAndroidSDKVersion() >= SDL_atoi(tmp))) {2073SDL_SetError("SDK version %d < minimum version %d", SDL_GetAndroidSDKVersion(), SDL_atoi(tmp));2074goto done;2075}2076}2077tmp = SDL_strstr(mappingString, SDL_GAMEPAD_SDKLE_FIELD);2078if (tmp) {2079tmp += SDL_GAMEPAD_SDKLE_FIELD_SIZE;2080if (!(SDL_GetAndroidSDKVersion() <= SDL_atoi(tmp))) {2081SDL_SetError("SDK version %d > maximum version %d", SDL_GetAndroidSDKVersion(), SDL_atoi(tmp));2082goto done;2083}2084}2085}2086#endif20872088pchGUID = SDL_PrivateGetGamepadGUIDFromMappingString(mappingString);2089if (!pchGUID) {2090SDL_SetError("Couldn't parse GUID from %s", mappingString);2091goto done;2092}2093if (!SDL_strcasecmp(pchGUID, "default")) {2094is_default_mapping = true;2095} else if (!SDL_strcasecmp(pchGUID, "xinput")) {2096is_xinput_mapping = true;2097}2098jGUID = SDL_StringToGUID(pchGUID);2099SDL_free(pchGUID);21002101pGamepadMapping = SDL_PrivateAddMappingForGUID(jGUID, mappingString, &existing, priority);2102if (!pGamepadMapping) {2103goto done;2104}21052106if (existing) {2107result = 0;2108} else {2109if (is_default_mapping) {2110s_pDefaultMapping = pGamepadMapping;2111} else if (is_xinput_mapping) {2112s_pXInputMapping = pGamepadMapping;2113}2114result = 1;2115}2116done:2117if (remapped) {2118SDL_free(remapped);2119}2120return result;2121}21222123/*2124* Add or update an entry into the Mappings Database2125*/2126int SDL_AddGamepadMapping(const char *mapping)2127{2128int result;21292130SDL_LockJoysticks();2131{2132result = SDL_PrivateAddGamepadMapping(mapping, SDL_GAMEPAD_MAPPING_PRIORITY_API);2133}2134SDL_UnlockJoysticks();21352136return result;2137}21382139/*2140* Create a mapping string for a mapping2141*/2142static char *CreateMappingString(GamepadMapping_t *mapping, SDL_GUID guid)2143{2144char *pMappingString, *pPlatformString;2145char pchGUID[33];2146size_t needed;2147bool need_platform = false;2148const char *platform = NULL;21492150SDL_AssertJoysticksLocked();21512152SDL_GUIDToString(guid, pchGUID, sizeof(pchGUID));21532154// allocate enough memory for GUID + ',' + name + ',' + mapping + \02155needed = SDL_strlen(pchGUID) + 1 + SDL_strlen(mapping->name) + 1 + SDL_strlen(mapping->mapping) + 1;21562157if (!SDL_strstr(mapping->mapping, SDL_GAMEPAD_PLATFORM_FIELD)) {2158// add memory for ',' + platform:PLATFORM2159need_platform = true;2160if (mapping->mapping[SDL_strlen(mapping->mapping) - 1] != ',') {2161needed += 1;2162}2163platform = SDL_GetPlatform();2164needed += SDL_GAMEPAD_PLATFORM_FIELD_SIZE + SDL_strlen(platform) + 1;2165}21662167pMappingString = (char *)SDL_malloc(needed);2168if (!pMappingString) {2169return NULL;2170}21712172(void)SDL_snprintf(pMappingString, needed, "%s,%s,%s", pchGUID, mapping->name, mapping->mapping);21732174if (need_platform) {2175if (mapping->mapping[SDL_strlen(mapping->mapping) - 1] != ',') {2176SDL_strlcat(pMappingString, ",", needed);2177}2178SDL_strlcat(pMappingString, SDL_GAMEPAD_PLATFORM_FIELD, needed);2179SDL_strlcat(pMappingString, platform, needed);2180SDL_strlcat(pMappingString, ",", needed);2181}21822183// Make sure multiple platform strings haven't made their way into the mapping2184pPlatformString = SDL_strstr(pMappingString, SDL_GAMEPAD_PLATFORM_FIELD);2185if (pPlatformString) {2186pPlatformString = SDL_strstr(pPlatformString + 1, SDL_GAMEPAD_PLATFORM_FIELD);2187if (pPlatformString) {2188*pPlatformString = '\0';2189}2190}2191return pMappingString;2192}21932194char **SDL_GetGamepadMappings(int *count)2195{2196int num_mappings = 0;2197char **result = NULL;2198char **mappings = NULL;21992200if (count) {2201*count = 0;2202}22032204SDL_LockJoysticks();22052206for (GamepadMapping_t *mapping = s_pSupportedGamepads; mapping; mapping = mapping->next) {2207if (SDL_memcmp(&mapping->guid, &s_zeroGUID, sizeof(mapping->guid)) == 0) {2208continue;2209}2210num_mappings++;2211}22122213size_t final_allocation = sizeof (char *); // for the NULL terminator element.2214bool failed = false;2215mappings = (char **) SDL_calloc(num_mappings + 1, sizeof (char *));2216if (!mappings) {2217failed = true;2218} else {2219int i = 0;2220for (GamepadMapping_t *mapping = s_pSupportedGamepads; mapping; mapping = mapping->next) {2221if (SDL_memcmp(&mapping->guid, &s_zeroGUID, sizeof(mapping->guid)) == 0) {2222continue;2223}22242225char *mappingstr = CreateMappingString(mapping, mapping->guid);2226if (!mappingstr) {2227failed = true;2228break; // error string is already set.2229}22302231SDL_assert(i < num_mappings);2232mappings[i++] = mappingstr;22332234final_allocation += SDL_strlen(mappingstr) + 1 + sizeof (char *);2235}2236}22372238SDL_UnlockJoysticks();22392240if (!failed) {2241result = (char **) SDL_malloc(final_allocation);2242if (result) {2243final_allocation -= (sizeof (char *) * num_mappings + 1);2244char *strptr = (char *) (result + (num_mappings + 1));2245for (int i = 0; i < num_mappings; i++) {2246result[i] = strptr;2247const size_t slen = SDL_strlcpy(strptr, mappings[i], final_allocation) + 1;2248SDL_assert(final_allocation >= slen);2249final_allocation -= slen;2250strptr += slen;2251}2252result[num_mappings] = NULL;22532254if (count) {2255*count = num_mappings;2256}2257}2258}22592260if (mappings) {2261for (int i = 0; i < num_mappings; i++) {2262SDL_free(mappings[i]);2263}2264SDL_free(mappings);2265}22662267return result;2268}22692270/*2271* Get the mapping string for this GUID2272*/2273char *SDL_GetGamepadMappingForGUID(SDL_GUID guid)2274{2275char *result;22762277SDL_LockJoysticks();2278{2279GamepadMapping_t *mapping = SDL_PrivateGetGamepadMappingForGUID(guid, false);2280if (mapping) {2281result = CreateMappingString(mapping, guid);2282} else {2283SDL_SetError("Mapping not available");2284result = NULL;2285}2286}2287SDL_UnlockJoysticks();22882289return result;2290}22912292/*2293* Get the mapping string for this device2294*/2295char *SDL_GetGamepadMapping(SDL_Gamepad *gamepad)2296{2297char *result;22982299SDL_LockJoysticks();2300{2301CHECK_GAMEPAD_MAGIC(gamepad, NULL);23022303result = CreateMappingString(gamepad->mapping, gamepad->joystick->guid);2304}2305SDL_UnlockJoysticks();23062307return result;2308}23092310/*2311* Set the mapping string for this device2312*/2313bool SDL_SetGamepadMapping(SDL_JoystickID instance_id, const char *mapping)2314{2315SDL_GUID guid = SDL_GetJoystickGUIDForID(instance_id);2316bool result = false;23172318if (SDL_memcmp(&guid, &s_zeroGUID, sizeof(guid)) == 0) {2319return SDL_InvalidParamError("instance_id");2320}23212322if (!mapping) {2323mapping = "*,*,";2324}23252326SDL_LockJoysticks();2327{2328if (SDL_PrivateAddMappingForGUID(guid, mapping, NULL, SDL_GAMEPAD_MAPPING_PRIORITY_API)) {2329result = true;2330}2331}2332SDL_UnlockJoysticks();23332334return result;2335}23362337static void SDL_LoadGamepadHints(void)2338{2339const char *hint = SDL_GetHint(SDL_HINT_GAMECONTROLLERCONFIG);2340if (hint && hint[0]) {2341char *pTempMappings = SDL_strdup(hint);2342char *pUserMappings = pTempMappings;23432344PushMappingChangeTracking();23452346while (pUserMappings) {2347char *pchNewLine = NULL;23482349pchNewLine = SDL_strchr(pUserMappings, '\n');2350if (pchNewLine) {2351*pchNewLine = '\0';2352}23532354SDL_PrivateAddGamepadMapping(pUserMappings, SDL_GAMEPAD_MAPPING_PRIORITY_USER);23552356if (pchNewLine) {2357pUserMappings = pchNewLine + 1;2358} else {2359pUserMappings = NULL;2360}2361}23622363PopMappingChangeTracking();23642365SDL_free(pTempMappings);2366}2367}23682369/*2370* Fill the given buffer with the expected gamepad mapping filepath.2371* Usually this will just be SDL_HINT_GAMECONTROLLERCONFIG_FILE, but for2372* Android, we want to get the internal storage path.2373*/2374static bool SDL_GetGamepadMappingFilePath(char *path, size_t size)2375{2376const char *hint = SDL_GetHint(SDL_HINT_GAMECONTROLLERCONFIG_FILE);2377if (hint && *hint) {2378return SDL_strlcpy(path, hint, size) < size;2379}23802381#ifdef SDL_PLATFORM_ANDROID2382return SDL_snprintf(path, size, "%s/gamepad_map.txt", SDL_GetAndroidInternalStoragePath()) < size;2383#else2384return false;2385#endif2386}23872388/*2389* Initialize the gamepad system, mostly load our DB of gamepad config mappings2390*/2391bool SDL_InitGamepadMappings(void)2392{2393char szGamepadMapPath[1024];2394int i = 0;2395const char *pMappingString = NULL;23962397SDL_AssertJoysticksLocked();23982399PushMappingChangeTracking();24002401pMappingString = s_GamepadMappings[i];2402while (pMappingString) {2403SDL_PrivateAddGamepadMapping(pMappingString, SDL_GAMEPAD_MAPPING_PRIORITY_DEFAULT);24042405i++;2406pMappingString = s_GamepadMappings[i];2407}24082409if (SDL_GetGamepadMappingFilePath(szGamepadMapPath, sizeof(szGamepadMapPath))) {2410SDL_AddGamepadMappingsFromFile(szGamepadMapPath);2411}24122413// load in any user supplied config2414SDL_LoadGamepadHints();24152416SDL_LoadVIDPIDList(&SDL_allowed_gamepads);2417SDL_LoadVIDPIDList(&SDL_ignored_gamepads);24182419PopMappingChangeTracking();24202421return true;2422}24232424bool SDL_InitGamepads(void)2425{2426int i;2427SDL_JoystickID *joysticks;24282429SDL_gamepads_initialized = true;24302431// Watch for joystick events and fire gamepad ones if needed2432SDL_AddEventWatch(SDL_GamepadEventWatcher, NULL);24332434// Send added events for gamepads currently attached2435joysticks = SDL_GetJoysticks(NULL);2436if (joysticks) {2437for (i = 0; joysticks[i]; ++i) {2438if (SDL_IsGamepad(joysticks[i])) {2439SDL_PrivateGamepadAdded(joysticks[i]);2440}2441}2442SDL_free(joysticks);2443}24442445return true;2446}24472448bool SDL_HasGamepad(void)2449{2450int num_joysticks = 0;2451int num_gamepads = 0;2452SDL_JoystickID *joysticks = SDL_GetJoysticks(&num_joysticks);2453if (joysticks) {2454int i;2455for (i = num_joysticks - 1; i >= 0 && num_gamepads == 0; --i) {2456if (SDL_IsGamepad(joysticks[i])) {2457++num_gamepads;2458}2459}2460SDL_free(joysticks);2461}2462if (num_gamepads > 0) {2463return true;2464}2465return false;2466}24672468SDL_JoystickID *SDL_GetGamepads(int *count)2469{2470int num_joysticks = 0;2471int num_gamepads = 0;2472SDL_JoystickID *joysticks = SDL_GetJoysticks(&num_joysticks);2473if (joysticks) {2474int i;2475for (i = num_joysticks - 1; i >= 0; --i) {2476if (SDL_IsGamepad(joysticks[i])) {2477++num_gamepads;2478} else {2479SDL_memmove(&joysticks[i], &joysticks[i+1], (num_gamepads + 1) * sizeof(joysticks[i]));2480}2481}2482}2483if (count) {2484*count = num_gamepads;2485}2486return joysticks;2487}24882489const char *SDL_GetGamepadNameForID(SDL_JoystickID instance_id)2490{2491const char *result = NULL;24922493SDL_LockJoysticks();2494{2495GamepadMapping_t *mapping = SDL_PrivateGetGamepadMapping(instance_id, true);2496if (mapping) {2497if (SDL_strcmp(mapping->name, "*") == 0) {2498result = SDL_GetJoystickNameForID(instance_id);2499} else {2500result = SDL_GetPersistentString(mapping->name);2501}2502}2503}2504SDL_UnlockJoysticks();25052506return result;2507}25082509const char *SDL_GetGamepadPathForID(SDL_JoystickID instance_id)2510{2511return SDL_GetJoystickPathForID(instance_id);2512}25132514int SDL_GetGamepadPlayerIndexForID(SDL_JoystickID instance_id)2515{2516return SDL_GetJoystickPlayerIndexForID(instance_id);2517}25182519SDL_GUID SDL_GetGamepadGUIDForID(SDL_JoystickID instance_id)2520{2521return SDL_GetJoystickGUIDForID(instance_id);2522}25232524Uint16 SDL_GetGamepadVendorForID(SDL_JoystickID instance_id)2525{2526return SDL_GetJoystickVendorForID(instance_id);2527}25282529Uint16 SDL_GetGamepadProductForID(SDL_JoystickID instance_id)2530{2531return SDL_GetJoystickProductForID(instance_id);2532}25332534Uint16 SDL_GetGamepadProductVersionForID(SDL_JoystickID instance_id)2535{2536return SDL_GetJoystickProductVersionForID(instance_id);2537}25382539SDL_GamepadType SDL_GetGamepadTypeForID(SDL_JoystickID instance_id)2540{2541SDL_GamepadType type = SDL_GAMEPAD_TYPE_UNKNOWN;25422543SDL_LockJoysticks();2544{2545GamepadMapping_t *mapping = SDL_PrivateGetGamepadMapping(instance_id, true);2546if (mapping) {2547char *type_string, *comma;25482549type_string = SDL_strstr(mapping->mapping, SDL_GAMEPAD_TYPE_FIELD);2550if (type_string) {2551type_string += SDL_GAMEPAD_TYPE_FIELD_SIZE;2552comma = SDL_strchr(type_string, ',');2553if (comma) {2554*comma = '\0';2555type = SDL_GetGamepadTypeFromString(type_string);2556*comma = ',';2557}2558}2559}2560}2561SDL_UnlockJoysticks();25622563if (type != SDL_GAMEPAD_TYPE_UNKNOWN) {2564return type;2565}2566return SDL_GetRealGamepadTypeForID(instance_id);2567}25682569SDL_GamepadType SDL_GetRealGamepadTypeForID(SDL_JoystickID instance_id)2570{2571SDL_GamepadType type = SDL_GAMEPAD_TYPE_UNKNOWN;2572const SDL_SteamVirtualGamepadInfo *info;25732574SDL_LockJoysticks();2575{2576info = SDL_GetJoystickVirtualGamepadInfoForID(instance_id);2577if (info) {2578type = info->type;2579} else {2580type = SDL_GetGamepadTypeFromGUID(SDL_GetJoystickGUIDForID(instance_id), SDL_GetJoystickNameForID(instance_id));2581}2582}2583SDL_UnlockJoysticks();25842585return type;2586}25872588char *SDL_GetGamepadMappingForID(SDL_JoystickID instance_id)2589{2590char *result = NULL;25912592SDL_LockJoysticks();2593{2594GamepadMapping_t *mapping = SDL_PrivateGetGamepadMapping(instance_id, true);2595if (mapping) {2596char pchGUID[33];2597SDL_GUID guid = SDL_GetJoystickGUIDForID(instance_id);2598SDL_GUIDToString(guid, pchGUID, sizeof(pchGUID));2599SDL_asprintf(&result, "%s,%s,%s", pchGUID, mapping->name, mapping->mapping);2600}2601}2602SDL_UnlockJoysticks();26032604return result;2605}26062607/*2608* Return 1 if the joystick with this name and GUID is a supported gamepad2609*/2610bool SDL_IsGamepadNameAndGUID(const char *name, SDL_GUID guid)2611{2612bool result;26132614SDL_LockJoysticks();2615{2616if (s_pDefaultMapping || SDL_PrivateGetGamepadMappingForNameAndGUID(name, guid) != NULL) {2617result = true;2618} else {2619result = false;2620}2621}2622SDL_UnlockJoysticks();26232624return result;2625}26262627/*2628* Return 1 if the joystick at this device index is a supported gamepad2629*/2630bool SDL_IsGamepad(SDL_JoystickID instance_id)2631{2632bool result;26332634SDL_LockJoysticks();2635{2636const void *value;2637if (SDL_FindInHashTable(s_gamepadInstanceIDs, (void *)(uintptr_t)instance_id, &value)) {2638result = (bool)(uintptr_t)value;2639} else {2640if (SDL_PrivateGetGamepadMapping(instance_id, true) != NULL) {2641result = true;2642} else {2643result = false;2644}26452646if (!s_gamepadInstanceIDs) {2647s_gamepadInstanceIDs = SDL_CreateHashTable(0, false, SDL_HashID, SDL_KeyMatchID, NULL, NULL);2648}2649SDL_InsertIntoHashTable(s_gamepadInstanceIDs, (void *)(uintptr_t)instance_id, (void *)(uintptr_t)result, true);2650}2651}2652SDL_UnlockJoysticks();26532654return result;2655}26562657/*2658* Return 1 if the gamepad should be ignored by SDL2659*/2660bool SDL_ShouldIgnoreGamepad(Uint16 vendor_id, Uint16 product_id, Uint16 version, const char *name)2661{2662#ifdef SDL_PLATFORM_LINUX2663if (SDL_endswith(name, " Motion Sensors")) {2664// Don't treat the PS3 and PS4 motion controls as a separate gamepad2665return true;2666}2667if (SDL_strncmp(name, "Nintendo ", 9) == 0 && SDL_strstr(name, " IMU") != NULL) {2668// Don't treat the Nintendo IMU as a separate gamepad2669return true;2670}2671if (SDL_endswith(name, " Accelerometer") ||2672SDL_endswith(name, " IR") ||2673SDL_endswith(name, " Motion Plus") ||2674SDL_endswith(name, " Nunchuk")) {2675// Don't treat the Wii extension controls as a separate gamepad2676return true;2677}2678#endif26792680if (name && SDL_strcmp(name, "uinput-fpc") == 0) {2681// The Google Pixel fingerprint sensor reports itself as a joystick2682return true;2683}26842685#ifdef SDL_PLATFORM_WIN322686if (SDL_GetHintBoolean("SDL_GAMECONTROLLER_ALLOW_STEAM_VIRTUAL_GAMEPAD", false) &&2687SDL_GetHintBoolean("STEAM_COMPAT_PROTON", false)) {2688// We are launched by Steam and running under Proton2689// We can't tell whether this controller is a Steam Virtual Gamepad,2690// so assume that Proton is doing the appropriate filtering of controllers2691// and anything we see here is fine to use.2692return false;2693}2694#endif // SDL_PLATFORM_WIN3226952696if (SDL_IsJoystickSteamVirtualGamepad(vendor_id, product_id, version)) {2697return !SDL_GetHintBoolean("SDL_GAMECONTROLLER_ALLOW_STEAM_VIRTUAL_GAMEPAD", false);2698}26992700if (SDL_allowed_gamepads.num_included_entries > 0) {2701if (SDL_VIDPIDInList(vendor_id, product_id, &SDL_allowed_gamepads)) {2702return false;2703}2704return true;2705} else {2706if (SDL_VIDPIDInList(vendor_id, product_id, &SDL_ignored_gamepads)) {2707return true;2708}2709return false;2710}2711}27122713/*2714* Open a gamepad for use2715*2716* This function returns a gamepad identifier, or NULL if an error occurred.2717*/2718SDL_Gamepad *SDL_OpenGamepad(SDL_JoystickID instance_id)2719{2720SDL_Gamepad *gamepad;2721SDL_Gamepad *gamepadlist;2722GamepadMapping_t *pSupportedGamepad = NULL;27232724SDL_LockJoysticks();27252726gamepadlist = SDL_gamepads;2727// If the gamepad is already open, return it2728while (gamepadlist) {2729if (instance_id == gamepadlist->joystick->instance_id) {2730gamepad = gamepadlist;2731++gamepad->ref_count;2732SDL_UnlockJoysticks();2733return gamepad;2734}2735gamepadlist = gamepadlist->next;2736}27372738// Find a gamepad mapping2739pSupportedGamepad = SDL_PrivateGetGamepadMapping(instance_id, true);2740if (!pSupportedGamepad) {2741SDL_SetError("Couldn't find mapping for device (%" SDL_PRIu32 ")", instance_id);2742SDL_UnlockJoysticks();2743return NULL;2744}27452746// Create and initialize the gamepad2747gamepad = (SDL_Gamepad *)SDL_calloc(1, sizeof(*gamepad));2748if (!gamepad) {2749SDL_UnlockJoysticks();2750return NULL;2751}2752SDL_SetObjectValid(gamepad, SDL_OBJECT_TYPE_GAMEPAD, true);27532754gamepad->joystick = SDL_OpenJoystick(instance_id);2755if (!gamepad->joystick) {2756SDL_SetObjectValid(gamepad, SDL_OBJECT_TYPE_GAMEPAD, false);2757SDL_free(gamepad);2758SDL_UnlockJoysticks();2759return NULL;2760}27612762if (gamepad->joystick->naxes) {2763gamepad->last_match_axis = (SDL_GamepadBinding **)SDL_calloc(gamepad->joystick->naxes, sizeof(*gamepad->last_match_axis));2764if (!gamepad->last_match_axis) {2765SDL_SetObjectValid(gamepad, SDL_OBJECT_TYPE_GAMEPAD, false);2766SDL_CloseJoystick(gamepad->joystick);2767SDL_free(gamepad);2768SDL_UnlockJoysticks();2769return NULL;2770}2771}2772if (gamepad->joystick->nhats) {2773gamepad->last_hat_mask = (Uint8 *)SDL_calloc(gamepad->joystick->nhats, sizeof(*gamepad->last_hat_mask));2774if (!gamepad->last_hat_mask) {2775SDL_SetObjectValid(gamepad, SDL_OBJECT_TYPE_GAMEPAD, false);2776SDL_CloseJoystick(gamepad->joystick);2777SDL_free(gamepad->last_match_axis);2778SDL_free(gamepad);2779SDL_UnlockJoysticks();2780return NULL;2781}2782}27832784SDL_PrivateLoadButtonMapping(gamepad, pSupportedGamepad);27852786// Add the gamepad to list2787++gamepad->ref_count;2788// Link the gamepad in the list2789gamepad->next = SDL_gamepads;2790SDL_gamepads = gamepad;27912792SDL_UnlockJoysticks();27932794return gamepad;2795}27962797/*2798* Manually pump for gamepad updates.2799*/2800void SDL_UpdateGamepads(void)2801{2802// Just for API completeness; the joystick API does all the work.2803SDL_UpdateJoysticks();2804}28052806/**2807* Return whether a gamepad has a given axis2808*/2809bool SDL_GamepadHasAxis(SDL_Gamepad *gamepad, SDL_GamepadAxis axis)2810{2811bool result = false;28122813SDL_LockJoysticks();2814{2815int i;28162817CHECK_GAMEPAD_MAGIC(gamepad, false);28182819for (i = 0; i < gamepad->num_bindings; ++i) {2820const SDL_GamepadBinding *binding = &gamepad->bindings[i];2821if (binding->output_type == SDL_GAMEPAD_BINDTYPE_AXIS && binding->output.axis.axis == axis) {2822result = true;2823break;2824}2825}2826}2827SDL_UnlockJoysticks();28282829return result;2830}28312832/*2833* Get the current state of an axis control on a gamepad2834*/2835Sint16 SDL_GetGamepadAxis(SDL_Gamepad *gamepad, SDL_GamepadAxis axis)2836{2837Sint16 result = 0;28382839SDL_LockJoysticks();2840{2841int i;28422843CHECK_GAMEPAD_MAGIC(gamepad, 0);28442845for (i = 0; i < gamepad->num_bindings; ++i) {2846const SDL_GamepadBinding *binding = &gamepad->bindings[i];2847if (binding->output_type == SDL_GAMEPAD_BINDTYPE_AXIS && binding->output.axis.axis == axis) {2848int value = 0;2849bool valid_input_range;2850bool valid_output_range;28512852if (binding->input_type == SDL_GAMEPAD_BINDTYPE_AXIS) {2853value = SDL_GetJoystickAxis(gamepad->joystick, binding->input.axis.axis);2854if (binding->input.axis.axis_min < binding->input.axis.axis_max) {2855valid_input_range = (value >= binding->input.axis.axis_min && value <= binding->input.axis.axis_max);2856} else {2857valid_input_range = (value >= binding->input.axis.axis_max && value <= binding->input.axis.axis_min);2858}2859if (valid_input_range) {2860if (binding->input.axis.axis_min != binding->output.axis.axis_min || binding->input.axis.axis_max != binding->output.axis.axis_max) {2861float normalized_value = (float)(value - binding->input.axis.axis_min) / (binding->input.axis.axis_max - binding->input.axis.axis_min);2862value = binding->output.axis.axis_min + (int)(normalized_value * (binding->output.axis.axis_max - binding->output.axis.axis_min));2863}2864} else {2865value = 0;2866}2867} else if (binding->input_type == SDL_GAMEPAD_BINDTYPE_BUTTON) {2868if (SDL_GetJoystickButton(gamepad->joystick, binding->input.button)) {2869value = binding->output.axis.axis_max;2870}2871} else if (binding->input_type == SDL_GAMEPAD_BINDTYPE_HAT) {2872int hat_mask = SDL_GetJoystickHat(gamepad->joystick, binding->input.hat.hat);2873if (hat_mask & binding->input.hat.hat_mask) {2874value = binding->output.axis.axis_max;2875}2876}28772878if (binding->output.axis.axis_min < binding->output.axis.axis_max) {2879valid_output_range = (value >= binding->output.axis.axis_min && value <= binding->output.axis.axis_max);2880} else {2881valid_output_range = (value >= binding->output.axis.axis_max && value <= binding->output.axis.axis_min);2882}2883// If the value is zero, there might be another binding that makes it non-zero2884if (value != 0 && valid_output_range) {2885result = (Sint16)value;2886break;2887}2888}2889}2890}2891SDL_UnlockJoysticks();28922893return result;2894}28952896/**2897* Return whether a gamepad has a given button2898*/2899bool SDL_GamepadHasButton(SDL_Gamepad *gamepad, SDL_GamepadButton button)2900{2901bool result = false;29022903SDL_LockJoysticks();2904{2905int i;29062907CHECK_GAMEPAD_MAGIC(gamepad, false);29082909for (i = 0; i < gamepad->num_bindings; ++i) {2910const SDL_GamepadBinding *binding = &gamepad->bindings[i];2911if (binding->output_type == SDL_GAMEPAD_BINDTYPE_BUTTON && binding->output.button == button) {2912result = true;2913break;2914}2915}2916}2917SDL_UnlockJoysticks();29182919return result;2920}29212922/*2923* Get the current state of a button on a gamepad2924*/2925bool SDL_GetGamepadButton(SDL_Gamepad *gamepad, SDL_GamepadButton button)2926{2927bool result = false;29282929SDL_LockJoysticks();2930{2931int i;29322933CHECK_GAMEPAD_MAGIC(gamepad, false);29342935for (i = 0; i < gamepad->num_bindings; ++i) {2936const SDL_GamepadBinding *binding = &gamepad->bindings[i];2937if (binding->output_type == SDL_GAMEPAD_BINDTYPE_BUTTON && binding->output.button == button) {2938if (binding->input_type == SDL_GAMEPAD_BINDTYPE_AXIS) {2939bool valid_input_range;29402941int value = SDL_GetJoystickAxis(gamepad->joystick, binding->input.axis.axis);2942int threshold = binding->input.axis.axis_min + (binding->input.axis.axis_max - binding->input.axis.axis_min) / 2;2943if (binding->input.axis.axis_min < binding->input.axis.axis_max) {2944valid_input_range = (value >= binding->input.axis.axis_min && value <= binding->input.axis.axis_max);2945if (valid_input_range) {2946result |= (value >= threshold);2947}2948} else {2949valid_input_range = (value >= binding->input.axis.axis_max && value <= binding->input.axis.axis_min);2950if (valid_input_range) {2951result |= (value <= threshold);2952}2953}2954} else if (binding->input_type == SDL_GAMEPAD_BINDTYPE_BUTTON) {2955result |= SDL_GetJoystickButton(gamepad->joystick, binding->input.button);2956} else if (binding->input_type == SDL_GAMEPAD_BINDTYPE_HAT) {2957int hat_mask = SDL_GetJoystickHat(gamepad->joystick, binding->input.hat.hat);2958result |= ((hat_mask & binding->input.hat.hat_mask) != 0);2959}2960}2961}2962}2963SDL_UnlockJoysticks();29642965return result;2966}29672968/**2969* Get the label of a button on a gamepad.2970*/2971static SDL_GamepadButtonLabel SDL_GetGamepadButtonLabelForFaceStyle(SDL_GamepadFaceStyle face_style, SDL_GamepadButton button)2972{2973SDL_GamepadButtonLabel label = SDL_GAMEPAD_BUTTON_LABEL_UNKNOWN;29742975switch (face_style) {2976case SDL_GAMEPAD_FACE_STYLE_ABXY:2977switch (button) {2978case SDL_GAMEPAD_BUTTON_SOUTH:2979label = SDL_GAMEPAD_BUTTON_LABEL_A;2980break;2981case SDL_GAMEPAD_BUTTON_EAST:2982label = SDL_GAMEPAD_BUTTON_LABEL_B;2983break;2984case SDL_GAMEPAD_BUTTON_WEST:2985label = SDL_GAMEPAD_BUTTON_LABEL_X;2986break;2987case SDL_GAMEPAD_BUTTON_NORTH:2988label = SDL_GAMEPAD_BUTTON_LABEL_Y;2989break;2990default:2991break;2992}2993break;2994case SDL_GAMEPAD_FACE_STYLE_BAYX:2995switch (button) {2996case SDL_GAMEPAD_BUTTON_SOUTH:2997label = SDL_GAMEPAD_BUTTON_LABEL_B;2998break;2999case SDL_GAMEPAD_BUTTON_EAST:3000label = SDL_GAMEPAD_BUTTON_LABEL_A;3001break;3002case SDL_GAMEPAD_BUTTON_WEST:3003label = SDL_GAMEPAD_BUTTON_LABEL_Y;3004break;3005case SDL_GAMEPAD_BUTTON_NORTH:3006label = SDL_GAMEPAD_BUTTON_LABEL_X;3007break;3008default:3009break;3010}3011break;3012case SDL_GAMEPAD_FACE_STYLE_SONY:3013switch (button) {3014case SDL_GAMEPAD_BUTTON_SOUTH:3015label = SDL_GAMEPAD_BUTTON_LABEL_CROSS;3016break;3017case SDL_GAMEPAD_BUTTON_EAST:3018label = SDL_GAMEPAD_BUTTON_LABEL_CIRCLE;3019break;3020case SDL_GAMEPAD_BUTTON_WEST:3021label = SDL_GAMEPAD_BUTTON_LABEL_SQUARE;3022break;3023case SDL_GAMEPAD_BUTTON_NORTH:3024label = SDL_GAMEPAD_BUTTON_LABEL_TRIANGLE;3025break;3026default:3027break;3028}3029break;3030default:3031break;3032}3033return label;3034}30353036/**3037* Get the label of a button on a gamepad.3038*/3039SDL_GamepadButtonLabel SDL_GetGamepadButtonLabelForType(SDL_GamepadType type, SDL_GamepadButton button)3040{3041return SDL_GetGamepadButtonLabelForFaceStyle(SDL_GetGamepadFaceStyleForGamepadType(type), button);3042}30433044/**3045* Get the label of a button on a gamepad.3046*/3047SDL_GamepadButtonLabel SDL_GetGamepadButtonLabel(SDL_Gamepad *gamepad, SDL_GamepadButton button)3048{3049SDL_GamepadFaceStyle face_style;30503051SDL_LockJoysticks();3052{3053CHECK_GAMEPAD_MAGIC(gamepad, SDL_GAMEPAD_BUTTON_LABEL_UNKNOWN);30543055face_style = gamepad->face_style;3056}3057SDL_UnlockJoysticks();30583059return SDL_GetGamepadButtonLabelForFaceStyle(face_style, button);3060}30613062/**3063* Get the number of touchpads on a gamepad.3064*/3065int SDL_GetNumGamepadTouchpads(SDL_Gamepad *gamepad)3066{3067int result = 0;30683069SDL_LockJoysticks();3070{3071SDL_Joystick *joystick = SDL_GetGamepadJoystick(gamepad);3072if (joystick) {3073result = joystick->ntouchpads;3074}3075}3076SDL_UnlockJoysticks();30773078return result;3079}30803081/**3082* Get the number of supported simultaneous fingers on a touchpad on a gamepad.3083*/3084int SDL_GetNumGamepadTouchpadFingers(SDL_Gamepad *gamepad, int touchpad)3085{3086int result = 0;30873088SDL_LockJoysticks();3089{3090SDL_Joystick *joystick = SDL_GetGamepadJoystick(gamepad);3091if (joystick) {3092if (touchpad >= 0 && touchpad < joystick->ntouchpads) {3093result = joystick->touchpads[touchpad].nfingers;3094}3095}3096}3097SDL_UnlockJoysticks();30983099return result;3100}31013102/**3103* Get the current state of a finger on a touchpad on a gamepad.3104*/3105bool SDL_GetGamepadTouchpadFinger(SDL_Gamepad *gamepad, int touchpad, int finger, bool *down, float *x, float *y, float *pressure)3106{3107bool result = false;31083109SDL_LockJoysticks();3110{3111SDL_Joystick *joystick = SDL_GetGamepadJoystick(gamepad);3112if (joystick) {3113if (touchpad >= 0 && touchpad < joystick->ntouchpads) {3114SDL_JoystickTouchpadInfo *touchpad_info = &joystick->touchpads[touchpad];3115if (finger >= 0 && finger < touchpad_info->nfingers) {3116SDL_JoystickTouchpadFingerInfo *info = &touchpad_info->fingers[finger];31173118if (down) {3119*down = info->down;3120}3121if (x) {3122*x = info->x;3123}3124if (y) {3125*y = info->y;3126}3127if (pressure) {3128*pressure = info->pressure;3129}3130result = true;3131} else {3132result = SDL_InvalidParamError("finger");3133}3134} else {3135result = SDL_InvalidParamError("touchpad");3136}3137}3138}3139SDL_UnlockJoysticks();31403141return result;3142}31433144/**3145* Return whether a gamepad has a particular sensor.3146*/3147bool SDL_GamepadHasSensor(SDL_Gamepad *gamepad, SDL_SensorType type)3148{3149bool result = false;31503151SDL_LockJoysticks();3152{3153SDL_Joystick *joystick = SDL_GetGamepadJoystick(gamepad);3154if (joystick) {3155int i;3156for (i = 0; i < joystick->nsensors; ++i) {3157if (joystick->sensors[i].type == type) {3158result = true;3159break;3160}3161}3162}3163}3164SDL_UnlockJoysticks();31653166return result;3167}31683169/*3170* Set whether data reporting for a gamepad sensor is enabled3171*/3172bool SDL_SetGamepadSensorEnabled(SDL_Gamepad *gamepad, SDL_SensorType type, bool enabled)3173{3174SDL_LockJoysticks();3175{3176SDL_Joystick *joystick = SDL_GetGamepadJoystick(gamepad);3177if (joystick) {3178int i;3179for (i = 0; i < joystick->nsensors; ++i) {3180SDL_JoystickSensorInfo *sensor = &joystick->sensors[i];31813182if (sensor->type == type) {3183if (sensor->enabled == (enabled != false)) {3184SDL_UnlockJoysticks();3185return true;3186}31873188if (type == SDL_SENSOR_ACCEL && joystick->accel_sensor) {3189if (enabled) {3190joystick->accel = SDL_OpenSensor(joystick->accel_sensor);3191if (!joystick->accel) {3192SDL_UnlockJoysticks();3193return false;3194}3195} else {3196if (joystick->accel) {3197SDL_CloseSensor(joystick->accel);3198joystick->accel = NULL;3199}3200}3201} else if (type == SDL_SENSOR_GYRO && joystick->gyro_sensor) {3202if (enabled) {3203joystick->gyro = SDL_OpenSensor(joystick->gyro_sensor);3204if (!joystick->gyro) {3205SDL_UnlockJoysticks();3206return false;3207}3208} else {3209if (joystick->gyro) {3210SDL_CloseSensor(joystick->gyro);3211joystick->gyro = NULL;3212}3213}3214} else {3215if (enabled) {3216if (joystick->nsensors_enabled == 0) {3217if (!joystick->driver->SetSensorsEnabled(joystick, true)) {3218SDL_UnlockJoysticks();3219return false;3220}3221}3222++joystick->nsensors_enabled;3223} else {3224if (joystick->nsensors_enabled == 1) {3225if (!joystick->driver->SetSensorsEnabled(joystick, false)) {3226SDL_UnlockJoysticks();3227return false;3228}3229}3230--joystick->nsensors_enabled;3231}3232}32333234sensor->enabled = enabled;3235SDL_UnlockJoysticks();3236return true;3237}3238}3239}3240}3241SDL_UnlockJoysticks();32423243return SDL_Unsupported();3244}32453246/*3247* Query whether sensor data reporting is enabled for a gamepad3248*/3249bool SDL_GamepadSensorEnabled(SDL_Gamepad *gamepad, SDL_SensorType type)3250{3251bool result = false;32523253SDL_LockJoysticks();3254{3255SDL_Joystick *joystick = SDL_GetGamepadJoystick(gamepad);3256if (joystick) {3257int i;3258for (i = 0; i < joystick->nsensors; ++i) {3259if (joystick->sensors[i].type == type) {3260result = joystick->sensors[i].enabled;3261break;3262}3263}3264}3265}3266SDL_UnlockJoysticks();32673268return result;3269}32703271/*3272* Get the data rate of a gamepad sensor.3273*/3274float SDL_GetGamepadSensorDataRate(SDL_Gamepad *gamepad, SDL_SensorType type)3275{3276float result = 0.0f;32773278SDL_LockJoysticks();3279{3280SDL_Joystick *joystick = SDL_GetGamepadJoystick(gamepad);3281if (joystick) {3282int i;3283for (i = 0; i < joystick->nsensors; ++i) {3284SDL_JoystickSensorInfo *sensor = &joystick->sensors[i];32853286if (sensor->type == type) {3287result = sensor->rate;3288break;3289}3290}3291}3292}3293SDL_UnlockJoysticks();32943295return result;3296}32973298/*3299* Get the current state of a gamepad sensor.3300*/3301bool SDL_GetGamepadSensorData(SDL_Gamepad *gamepad, SDL_SensorType type, float *data, int num_values)3302{3303SDL_LockJoysticks();3304{3305SDL_Joystick *joystick = SDL_GetGamepadJoystick(gamepad);3306if (joystick) {3307int i;3308for (i = 0; i < joystick->nsensors; ++i) {3309SDL_JoystickSensorInfo *sensor = &joystick->sensors[i];33103311if (sensor->type == type) {3312num_values = SDL_min(num_values, SDL_arraysize(sensor->data));3313SDL_memcpy(data, sensor->data, num_values * sizeof(*data));3314SDL_UnlockJoysticks();3315return true;3316}3317}3318}3319}3320SDL_UnlockJoysticks();33213322return SDL_Unsupported();3323}33243325SDL_JoystickID SDL_GetGamepadID(SDL_Gamepad *gamepad)3326{3327SDL_Joystick *joystick = SDL_GetGamepadJoystick(gamepad);33283329if (!joystick) {3330return 0;3331}3332return SDL_GetJoystickID(joystick);3333}33343335SDL_PropertiesID SDL_GetGamepadProperties(SDL_Gamepad *gamepad)3336{3337SDL_PropertiesID result = 0;33383339SDL_LockJoysticks();3340{3341CHECK_GAMEPAD_MAGIC(gamepad, 0);33423343result = SDL_GetJoystickProperties(gamepad->joystick);3344}3345SDL_UnlockJoysticks();33463347return result;3348}33493350const char *SDL_GetGamepadName(SDL_Gamepad *gamepad)3351{3352const char *result = NULL;33533354SDL_LockJoysticks();3355{3356CHECK_GAMEPAD_MAGIC(gamepad, NULL);33573358if (SDL_strcmp(gamepad->name, "*") == 0 ||3359gamepad->joystick->steam_handle != 0) {3360result = SDL_GetJoystickName(gamepad->joystick);3361} else {3362result = SDL_GetPersistentString(gamepad->name);3363}3364}3365SDL_UnlockJoysticks();33663367return result;3368}33693370const char *SDL_GetGamepadPath(SDL_Gamepad *gamepad)3371{3372SDL_Joystick *joystick = SDL_GetGamepadJoystick(gamepad);33733374if (!joystick) {3375return NULL;3376}3377return SDL_GetJoystickPath(joystick);3378}33793380SDL_GamepadType SDL_GetGamepadType(SDL_Gamepad *gamepad)3381{3382SDL_GamepadType type;3383const SDL_SteamVirtualGamepadInfo *info;33843385SDL_LockJoysticks();3386{3387CHECK_GAMEPAD_MAGIC(gamepad, SDL_GAMEPAD_TYPE_UNKNOWN);33883389info = SDL_GetJoystickVirtualGamepadInfoForID(gamepad->joystick->instance_id);3390if (info) {3391type = info->type;3392} else {3393type = gamepad->type;3394}3395}3396SDL_UnlockJoysticks();33973398return type;3399}34003401SDL_GamepadType SDL_GetRealGamepadType(SDL_Gamepad *gamepad)3402{3403SDL_Joystick *joystick = SDL_GetGamepadJoystick(gamepad);34043405if (!joystick) {3406return SDL_GAMEPAD_TYPE_UNKNOWN;3407}3408return SDL_GetGamepadTypeFromGUID(SDL_GetJoystickGUID(joystick), SDL_GetJoystickName(joystick));3409}34103411int SDL_GetGamepadPlayerIndex(SDL_Gamepad *gamepad)3412{3413SDL_Joystick *joystick = SDL_GetGamepadJoystick(gamepad);34143415if (!joystick) {3416return -1;3417}3418return SDL_GetJoystickPlayerIndex(joystick);3419}34203421/**3422* Set the player index of an opened gamepad3423*/3424bool SDL_SetGamepadPlayerIndex(SDL_Gamepad *gamepad, int player_index)3425{3426SDL_Joystick *joystick = SDL_GetGamepadJoystick(gamepad);34273428if (!joystick) {3429// SDL_SetError() will have been called already by SDL_GetGamepadJoystick()3430return false;3431}3432return SDL_SetJoystickPlayerIndex(joystick, player_index);3433}34343435Uint16 SDL_GetGamepadVendor(SDL_Gamepad *gamepad)3436{3437SDL_Joystick *joystick = SDL_GetGamepadJoystick(gamepad);34383439if (!joystick) {3440return 0;3441}3442return SDL_GetJoystickVendor(joystick);3443}34443445Uint16 SDL_GetGamepadProduct(SDL_Gamepad *gamepad)3446{3447SDL_Joystick *joystick = SDL_GetGamepadJoystick(gamepad);34483449if (!joystick) {3450return 0;3451}3452return SDL_GetJoystickProduct(joystick);3453}34543455Uint16 SDL_GetGamepadProductVersion(SDL_Gamepad *gamepad)3456{3457SDL_Joystick *joystick = SDL_GetGamepadJoystick(gamepad);34583459if (!joystick) {3460return 0;3461}3462return SDL_GetJoystickProductVersion(joystick);3463}34643465Uint16 SDL_GetGamepadFirmwareVersion(SDL_Gamepad *gamepad)3466{3467SDL_Joystick *joystick = SDL_GetGamepadJoystick(gamepad);34683469if (!joystick) {3470return 0;3471}3472return SDL_GetJoystickFirmwareVersion(joystick);3473}34743475const char * SDL_GetGamepadSerial(SDL_Gamepad *gamepad)3476{3477SDL_Joystick *joystick = SDL_GetGamepadJoystick(gamepad);34783479if (!joystick) {3480return NULL;3481}3482return SDL_GetJoystickSerial(joystick);34833484}34853486Uint64 SDL_GetGamepadSteamHandle(SDL_Gamepad *gamepad)3487{3488Uint64 handle = 0;34893490SDL_LockJoysticks();3491{3492CHECK_GAMEPAD_MAGIC(gamepad, 0);34933494handle = gamepad->joystick->steam_handle;3495}3496SDL_UnlockJoysticks();34973498return handle;3499}35003501SDL_JoystickConnectionState SDL_GetGamepadConnectionState(SDL_Gamepad *gamepad)3502{3503SDL_Joystick *joystick = SDL_GetGamepadJoystick(gamepad);35043505if (!joystick) {3506return SDL_JOYSTICK_CONNECTION_INVALID;3507}3508return SDL_GetJoystickConnectionState(joystick);3509}35103511SDL_PowerState SDL_GetGamepadPowerInfo(SDL_Gamepad *gamepad, int *percent)3512{3513SDL_Joystick *joystick = SDL_GetGamepadJoystick(gamepad);35143515if (percent) {3516*percent = -1;3517}3518if (!joystick) {3519return SDL_POWERSTATE_ERROR;3520}3521return SDL_GetJoystickPowerInfo(joystick, percent);3522}35233524/*3525* Return if the gamepad in question is currently attached to the system,3526* \return 0 if not plugged in, 1 if still present.3527*/3528bool SDL_GamepadConnected(SDL_Gamepad *gamepad)3529{3530SDL_Joystick *joystick = SDL_GetGamepadJoystick(gamepad);35313532if (!joystick) {3533return false;3534}3535return SDL_JoystickConnected(joystick);3536}35373538/*3539* Get the joystick for this gamepad3540*/3541SDL_Joystick *SDL_GetGamepadJoystick(SDL_Gamepad *gamepad)3542{3543SDL_Joystick *joystick;35443545SDL_LockJoysticks();3546{3547CHECK_GAMEPAD_MAGIC(gamepad, NULL);35483549joystick = gamepad->joystick;3550}3551SDL_UnlockJoysticks();35523553return joystick;3554}35553556/*3557* Return the SDL_Gamepad associated with an instance id.3558*/3559SDL_Gamepad *SDL_GetGamepadFromID(SDL_JoystickID joyid)3560{3561SDL_Gamepad *gamepad;35623563SDL_LockJoysticks();3564gamepad = SDL_gamepads;3565while (gamepad) {3566if (gamepad->joystick->instance_id == joyid) {3567SDL_UnlockJoysticks();3568return gamepad;3569}3570gamepad = gamepad->next;3571}3572SDL_UnlockJoysticks();3573return NULL;3574}35753576/**3577* Return the SDL_Gamepad associated with a player index.3578*/3579SDL_Gamepad *SDL_GetGamepadFromPlayerIndex(int player_index)3580{3581SDL_Gamepad *result = NULL;35823583SDL_LockJoysticks();3584{3585SDL_Joystick *joystick = SDL_GetJoystickFromPlayerIndex(player_index);3586if (joystick) {3587result = SDL_GetGamepadFromID(joystick->instance_id);3588}3589}3590SDL_UnlockJoysticks();35913592return result;3593}35943595/*3596* Get the SDL joystick layer bindings for this gamepad3597*/3598SDL_GamepadBinding **SDL_GetGamepadBindings(SDL_Gamepad *gamepad, int *count)3599{3600SDL_GamepadBinding **bindings = NULL;36013602if (count) {3603*count = 0;3604}36053606SDL_LockJoysticks();3607{3608CHECK_GAMEPAD_MAGIC(gamepad, NULL);36093610size_t pointers_size = ((gamepad->num_bindings + 1) * sizeof(SDL_GamepadBinding *));3611size_t elements_size = (gamepad->num_bindings * sizeof(SDL_GamepadBinding));3612bindings = (SDL_GamepadBinding **)SDL_malloc(pointers_size + elements_size);3613if (bindings) {3614SDL_GamepadBinding *binding = (SDL_GamepadBinding *)((Uint8 *)bindings + pointers_size);3615int i;3616for (i = 0; i < gamepad->num_bindings; ++i, ++binding) {3617bindings[i] = binding;3618SDL_copyp(binding, &gamepad->bindings[i]);3619}3620bindings[i] = NULL;36213622if (count) {3623*count = gamepad->num_bindings;3624}3625}3626}3627SDL_UnlockJoysticks();36283629return bindings;3630}36313632bool SDL_RumbleGamepad(SDL_Gamepad *gamepad, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble, Uint32 duration_ms)3633{3634SDL_Joystick *joystick = SDL_GetGamepadJoystick(gamepad);36353636if (!joystick) {3637return false;3638}3639return SDL_RumbleJoystick(joystick, low_frequency_rumble, high_frequency_rumble, duration_ms);3640}36413642bool SDL_RumbleGamepadTriggers(SDL_Gamepad *gamepad, Uint16 left_rumble, Uint16 right_rumble, Uint32 duration_ms)3643{3644SDL_Joystick *joystick = SDL_GetGamepadJoystick(gamepad);36453646if (!joystick) {3647return false;3648}3649return SDL_RumbleJoystickTriggers(joystick, left_rumble, right_rumble, duration_ms);3650}36513652bool SDL_SetGamepadLED(SDL_Gamepad *gamepad, Uint8 red, Uint8 green, Uint8 blue)3653{3654SDL_Joystick *joystick = SDL_GetGamepadJoystick(gamepad);36553656if (!joystick) {3657return false;3658}3659return SDL_SetJoystickLED(joystick, red, green, blue);3660}36613662bool SDL_SendGamepadEffect(SDL_Gamepad *gamepad, const void *data, int size)3663{3664SDL_Joystick *joystick = SDL_GetGamepadJoystick(gamepad);36653666if (!joystick) {3667return false;3668}3669return SDL_SendJoystickEffect(joystick, data, size);3670}36713672void SDL_CloseGamepad(SDL_Gamepad *gamepad)3673{3674SDL_Gamepad *gamepadlist, *gamepadlistprev;36753676SDL_LockJoysticks();36773678if (!SDL_ObjectValid(gamepad, SDL_OBJECT_TYPE_GAMEPAD)) {3679SDL_UnlockJoysticks();3680return;3681}36823683// First decrement ref count3684if (--gamepad->ref_count > 0) {3685SDL_UnlockJoysticks();3686return;3687}36883689SDL_CloseJoystick(gamepad->joystick);36903691gamepadlist = SDL_gamepads;3692gamepadlistprev = NULL;3693while (gamepadlist) {3694if (gamepad == gamepadlist) {3695if (gamepadlistprev) {3696// unlink this entry3697gamepadlistprev->next = gamepadlist->next;3698} else {3699SDL_gamepads = gamepad->next;3700}3701break;3702}3703gamepadlistprev = gamepadlist;3704gamepadlist = gamepadlist->next;3705}37063707SDL_SetObjectValid(gamepad, SDL_OBJECT_TYPE_GAMEPAD, false);3708SDL_free(gamepad->bindings);3709SDL_free(gamepad->last_match_axis);3710SDL_free(gamepad->last_hat_mask);3711SDL_free(gamepad);37123713SDL_UnlockJoysticks();3714}37153716/*3717* Quit the gamepad subsystem3718*/3719void SDL_QuitGamepads(void)3720{3721SDL_Gamepad *gamepad;37223723SDL_LockJoysticks();37243725for (gamepad = SDL_gamepads; gamepad; gamepad = gamepad->next) {3726SDL_PrivateGamepadRemoved(gamepad->joystick->instance_id);3727}37283729SDL_gamepads_initialized = false;37303731SDL_RemoveEventWatch(SDL_GamepadEventWatcher, NULL);37323733while (SDL_gamepads) {3734SDL_gamepads->ref_count = 1;3735SDL_CloseGamepad(SDL_gamepads);3736}37373738SDL_UnlockJoysticks();3739}37403741void SDL_QuitGamepadMappings(void)3742{3743GamepadMapping_t *pGamepadMap;37443745SDL_AssertJoysticksLocked();37463747while (s_pSupportedGamepads) {3748pGamepadMap = s_pSupportedGamepads;3749s_pSupportedGamepads = s_pSupportedGamepads->next;3750SDL_free(pGamepadMap->name);3751SDL_free(pGamepadMap->mapping);3752SDL_free(pGamepadMap);3753}37543755SDL_FreeVIDPIDList(&SDL_allowed_gamepads);3756SDL_FreeVIDPIDList(&SDL_ignored_gamepads);37573758if (s_gamepadInstanceIDs) {3759SDL_DestroyHashTable(s_gamepadInstanceIDs);3760s_gamepadInstanceIDs = NULL;3761}3762}37633764/*3765* Event filter to transform joystick events into appropriate gamepad ones3766*/3767static void SDL_SendGamepadAxis(Uint64 timestamp, SDL_Gamepad *gamepad, SDL_GamepadAxis axis, Sint16 value)3768{3769SDL_AssertJoysticksLocked();37703771// translate the event, if desired3772if (SDL_EventEnabled(SDL_EVENT_GAMEPAD_AXIS_MOTION)) {3773SDL_Event event;3774event.type = SDL_EVENT_GAMEPAD_AXIS_MOTION;3775event.common.timestamp = timestamp;3776event.gaxis.which = gamepad->joystick->instance_id;3777event.gaxis.axis = axis;3778event.gaxis.value = value;3779SDL_PushEvent(&event);3780}3781}37823783static void SDL_SendGamepadButton(Uint64 timestamp, SDL_Gamepad *gamepad, SDL_GamepadButton button, bool down)3784{3785SDL_Event event;37863787SDL_AssertJoysticksLocked();37883789if (button == SDL_GAMEPAD_BUTTON_INVALID) {3790return;3791}37923793if (down) {3794event.type = SDL_EVENT_GAMEPAD_BUTTON_DOWN;3795} else {3796event.type = SDL_EVENT_GAMEPAD_BUTTON_UP;3797}37983799if (button == SDL_GAMEPAD_BUTTON_GUIDE) {3800Uint64 now = SDL_GetTicks();3801if (down) {3802gamepad->guide_button_down = now;38033804if (gamepad->joystick->delayed_guide_button) {3805// Skip duplicate press3806return;3807}3808} else {3809if (now < (gamepad->guide_button_down + SDL_MINIMUM_GUIDE_BUTTON_DELAY_MS)) {3810gamepad->joystick->delayed_guide_button = true;3811return;3812}3813gamepad->joystick->delayed_guide_button = false;3814}3815}38163817// translate the event, if desired3818if (SDL_EventEnabled(event.type)) {3819event.common.timestamp = timestamp;3820event.gbutton.which = gamepad->joystick->instance_id;3821event.gbutton.button = button;3822event.gbutton.down = down;3823SDL_PushEvent(&event);3824}3825}38263827static const Uint32 SDL_gamepad_event_list[] = {3828SDL_EVENT_GAMEPAD_AXIS_MOTION,3829SDL_EVENT_GAMEPAD_BUTTON_DOWN,3830SDL_EVENT_GAMEPAD_BUTTON_UP,3831SDL_EVENT_GAMEPAD_ADDED,3832SDL_EVENT_GAMEPAD_REMOVED,3833SDL_EVENT_GAMEPAD_REMAPPED,3834SDL_EVENT_GAMEPAD_TOUCHPAD_DOWN,3835SDL_EVENT_GAMEPAD_TOUCHPAD_MOTION,3836SDL_EVENT_GAMEPAD_TOUCHPAD_UP,3837SDL_EVENT_GAMEPAD_SENSOR_UPDATE,3838};38393840void SDL_SetGamepadEventsEnabled(bool enabled)3841{3842unsigned int i;38433844for (i = 0; i < SDL_arraysize(SDL_gamepad_event_list); ++i) {3845SDL_SetEventEnabled(SDL_gamepad_event_list[i], enabled);3846}3847}38483849bool SDL_GamepadEventsEnabled(void)3850{3851bool enabled = false;3852unsigned int i;38533854for (i = 0; i < SDL_arraysize(SDL_gamepad_event_list); ++i) {3855enabled = SDL_EventEnabled(SDL_gamepad_event_list[i]);3856if (enabled) {3857break;3858}3859}3860return enabled;3861}38623863void SDL_GamepadHandleDelayedGuideButton(SDL_Joystick *joystick)3864{3865SDL_Gamepad *gamepad;38663867SDL_AssertJoysticksLocked();38683869for (gamepad = SDL_gamepads; gamepad; gamepad = gamepad->next) {3870if (gamepad->joystick == joystick) {3871SDL_SendGamepadButton(0, gamepad, SDL_GAMEPAD_BUTTON_GUIDE, false);38723873// Make sure we send an update complete event for this change3874if (!gamepad->joystick->update_complete) {3875gamepad->joystick->update_complete = SDL_GetTicksNS();3876}3877break;3878}3879}3880}38813882const char *SDL_GetGamepadAppleSFSymbolsNameForButton(SDL_Gamepad *gamepad, SDL_GamepadButton button)3883{3884const char *result = NULL;3885#ifdef SDL_JOYSTICK_MFI3886const char *IOS_GetAppleSFSymbolsNameForButton(SDL_Gamepad *gamepad, SDL_GamepadButton button);38873888SDL_LockJoysticks();3889{3890CHECK_GAMEPAD_MAGIC(gamepad, NULL);38913892result = IOS_GetAppleSFSymbolsNameForButton(gamepad, button);3893}3894SDL_UnlockJoysticks();3895#endif3896return result;3897}38983899const char *SDL_GetGamepadAppleSFSymbolsNameForAxis(SDL_Gamepad *gamepad, SDL_GamepadAxis axis)3900{3901const char *result = NULL;3902#ifdef SDL_JOYSTICK_MFI3903const char *IOS_GetAppleSFSymbolsNameForAxis(SDL_Gamepad *gamepad, SDL_GamepadAxis axis);39043905SDL_LockJoysticks();3906{3907CHECK_GAMEPAD_MAGIC(gamepad, NULL);39083909result = IOS_GetAppleSFSymbolsNameForAxis(gamepad, axis);3910}3911SDL_UnlockJoysticks();3912#endif3913return result;3914}391539163917