Mac and Linux SDL2 binary snapshots
Edward Rudd
2021-06-15 dec7875a6e23212021e4d9080330a42832dfe02a
source/src/joystick/SDL_gamecontroller.c
@@ -23,12 +23,12 @@
/* This is the game controller API for Simple DirectMedia Layer */
#include "SDL_events.h"
#include "SDL_assert.h"
#include "SDL_hints.h"
#include "SDL_timer.h"
#include "SDL_sysjoystick.h"
#include "SDL_joystick_c.h"
#include "SDL_gamecontrollerdb.h"
#include "usb_ids.h"
#if !SDL_EVENTS_DISABLED
#include "../events/SDL_events_c.h"
@@ -43,6 +43,7 @@
#define SDL_MINIMUM_GUIDE_BUTTON_DELAY_MS   250
#define SDL_CONTROLLER_PLATFORM_FIELD   "platform:"
#define SDL_CONTROLLER_HINT_FIELD       "hint:"
#define SDL_CONTROLLER_SDKGE_FIELD      "sdk>=:"
#define SDL_CONTROLLER_SDKLE_FIELD      "sdk<=:"
@@ -104,7 +105,6 @@
static SDL_JoystickGUID s_zeroGUID;
static ControllerMapping_t *s_pSupportedControllers = NULL;
static ControllerMapping_t *s_pDefaultMapping = NULL;
static ControllerMapping_t *s_pHIDAPIMapping = NULL;
static ControllerMapping_t *s_pXInputMapping = NULL;
/* The SDL game controller structure */
@@ -192,15 +192,16 @@
    SDL_LoadVIDPIDListFromHint(hint, &SDL_allowed_controllers);
}
static int SDL_PrivateGameControllerAxis(SDL_GameController * gamecontroller, SDL_GameControllerAxis axis, Sint16 value);
static int SDL_PrivateGameControllerButton(SDL_GameController * gamecontroller, SDL_GameControllerButton button, Uint8 state);
static ControllerMapping_t *SDL_PrivateAddMappingForGUID(SDL_JoystickGUID jGUID, const char *mappingString, SDL_bool *existing, SDL_ControllerMappingPriority priority);
static int SDL_PrivateGameControllerAxis(SDL_GameController *gamecontroller, SDL_GameControllerAxis axis, Sint16 value);
static int SDL_PrivateGameControllerButton(SDL_GameController *gamecontroller, SDL_GameControllerButton button, Uint8 state);
/*
 * If there is an existing add event in the queue, it needs to be modified
 * to have the right value for which, because the number of controllers in
 * the system is now one less.
 */
static void UpdateEventsForDeviceRemoval()
static void UpdateEventsForDeviceRemoval(int device_index)
{
    int i, num_events;
    SDL_Event *events;
@@ -218,7 +219,19 @@
    num_events = SDL_PeepEvents(events, num_events, SDL_GETEVENT, SDL_CONTROLLERDEVICEADDED, SDL_CONTROLLERDEVICEADDED);
    for (i = 0; i < num_events; ++i) {
        --events[i].cdevice.which;
        if (events[i].cdevice.which < device_index) {
            /* No change for index values lower than the removed device */
        }
        else if (events[i].cdevice.which == device_index) {
            /* Drop this event entirely */
            SDL_memmove(&events[i], &events[i + 1], sizeof(*events) * (num_events - (i + 1)));
            --i;
            --num_events;
        }
        else {
            /* Fix up the device index if greater than the removed device */
            --events[i].cdevice.which;
        }
    }
    SDL_PeepEvents(events, num_events, SDL_ADDEVENT, 0, 0);
@@ -343,6 +356,32 @@
    gamecontroller->last_hat_mask[hat] = value;
}
/* The joystick layer will _also_ send events to recenter before disconnect,
    but it has to make (sometimes incorrect) guesses at what being "centered"
    is. The game controller layer, however, can set a definite logical idle
    position, so set them all here. If we happened to already be at the
    center thanks to the joystick layer or idle hands, this won't generate
    duplicate events. */
static void RecenterGameController(SDL_GameController *gamecontroller)
{
    SDL_GameControllerButton button;
    SDL_GameControllerAxis axis;
    for (button = (SDL_GameControllerButton) 0; button < SDL_CONTROLLER_BUTTON_MAX; button++) {
        if (SDL_GameControllerGetButton(gamecontroller, button)) {
            SDL_PrivateGameControllerButton(gamecontroller, button, SDL_RELEASED);
        }
    }
    for (axis = (SDL_GameControllerAxis) 0; axis < SDL_CONTROLLER_AXIS_MAX; axis++) {
        if (SDL_GameControllerGetAxis(gamecontroller, axis) != 0) {
            SDL_PrivateGameControllerAxis(gamecontroller, axis, 0);
        }
    }
}
/*
 * Event filter to fire controller events from joystick ones
 */
@@ -399,18 +438,22 @@
    case SDL_JOYDEVICEREMOVED:
        {
            SDL_GameController *controllerlist = SDL_gamecontrollers;
            int device_index = 0;
            while (controllerlist) {
                if (controllerlist->joystick->instance_id == event->jdevice.which) {
                    SDL_Event deviceevent;
                    RecenterGameController(controllerlist);
                    deviceevent.type = SDL_CONTROLLERDEVICEREMOVED;
                    deviceevent.cdevice.which = event->jdevice.which;
                    SDL_PushEvent(&deviceevent);
                    UpdateEventsForDeviceRemoval();
                    UpdateEventsForDeviceRemoval(device_index);
                    break;
                }
                controllerlist = controllerlist->next;
                ++device_index;
            }
        }
        break;
@@ -421,31 +464,211 @@
    return 1;
}
#ifdef __ANDROID__
/*
 * Helper function to guess at a mapping based on the elements reported for this controller
 */
static ControllerMapping_t *SDL_CreateMappingForAndroidController(SDL_JoystickGUID guid)
{
    SDL_bool existing;
    char mapping_string[1024];
    int button_mask;
    int axis_mask;
    button_mask = SDL_SwapLE16(*(Uint16*)(&guid.data[sizeof(guid.data)-4]));
    axis_mask = SDL_SwapLE16(*(Uint16*)(&guid.data[sizeof(guid.data)-2]));
    if (!button_mask && !axis_mask) {
        /* Accelerometer, shouldn't have a game controller mapping */
        return NULL;
    }
    SDL_strlcpy(mapping_string, "none,*,", sizeof(mapping_string));
    if (button_mask & (1 << SDL_CONTROLLER_BUTTON_A)) {
        SDL_strlcat(mapping_string, "a:b0,", sizeof(mapping_string));
    }
    if (button_mask & (1 << SDL_CONTROLLER_BUTTON_B)) {
        SDL_strlcat(mapping_string, "b:b1,", sizeof(mapping_string));
    } else if (button_mask & (1 << SDL_CONTROLLER_BUTTON_BACK)) {
        /* Use the back button as "B" for easy UI navigation with TV remotes */
        SDL_strlcat(mapping_string, "b:b4,", sizeof(mapping_string));
        button_mask &= ~(1 << SDL_CONTROLLER_BUTTON_BACK);
    }
    if (button_mask & (1 << SDL_CONTROLLER_BUTTON_X)) {
        SDL_strlcat(mapping_string, "x:b2,", sizeof(mapping_string));
    }
    if (button_mask & (1 << SDL_CONTROLLER_BUTTON_Y)) {
        SDL_strlcat(mapping_string, "y:b3,", sizeof(mapping_string));
    }
    if (button_mask & (1 << SDL_CONTROLLER_BUTTON_BACK)) {
        SDL_strlcat(mapping_string, "back:b4,", sizeof(mapping_string));
    }
    if (button_mask & (1 << SDL_CONTROLLER_BUTTON_GUIDE)) {
        /* The guide button generally isn't functional (or acts as a home button) on most Android controllers before Android 11 */
        if (SDL_GetAndroidSDKVersion() >= 30 /* Android 11 */) {
            SDL_strlcat(mapping_string, "guide:b5,", sizeof(mapping_string));
        }
    }
    if (button_mask & (1 << SDL_CONTROLLER_BUTTON_START)) {
        SDL_strlcat(mapping_string, "start:b6,", sizeof(mapping_string));
    }
    if (button_mask & (1 << SDL_CONTROLLER_BUTTON_LEFTSTICK)) {
        SDL_strlcat(mapping_string, "leftstick:b7,", sizeof(mapping_string));
    }
    if (button_mask & (1 << SDL_CONTROLLER_BUTTON_RIGHTSTICK)) {
        SDL_strlcat(mapping_string, "rightstick:b8,", sizeof(mapping_string));
    }
    if (button_mask & (1 << SDL_CONTROLLER_BUTTON_LEFTSHOULDER)) {
        SDL_strlcat(mapping_string, "leftshoulder:b9,", sizeof(mapping_string));
    }
    if (button_mask & (1 << SDL_CONTROLLER_BUTTON_RIGHTSHOULDER)) {
        SDL_strlcat(mapping_string, "rightshoulder:b10,", sizeof(mapping_string));
    }
    if (button_mask & (1 << SDL_CONTROLLER_BUTTON_DPAD_UP)) {
        SDL_strlcat(mapping_string, "dpup:b11,", sizeof(mapping_string));
    }
    if (button_mask & (1 << SDL_CONTROLLER_BUTTON_DPAD_DOWN)) {
        SDL_strlcat(mapping_string, "dpdown:b12,", sizeof(mapping_string));
    }
    if (button_mask & (1 << SDL_CONTROLLER_BUTTON_DPAD_LEFT)) {
        SDL_strlcat(mapping_string, "dpleft:b13,", sizeof(mapping_string));
    }
    if (button_mask & (1 << SDL_CONTROLLER_BUTTON_DPAD_RIGHT)) {
        SDL_strlcat(mapping_string, "dpright:b14,", sizeof(mapping_string));
    }
    if (axis_mask & (1 << SDL_CONTROLLER_AXIS_LEFTX)) {
        SDL_strlcat(mapping_string, "leftx:a0,", sizeof(mapping_string));
    }
    if (axis_mask & (1 << SDL_CONTROLLER_AXIS_LEFTY)) {
        SDL_strlcat(mapping_string, "lefty:a1,", sizeof(mapping_string));
    }
    if (axis_mask & (1 << SDL_CONTROLLER_AXIS_RIGHTX)) {
        SDL_strlcat(mapping_string, "rightx:a2,", sizeof(mapping_string));
    }
    if (axis_mask & (1 << SDL_CONTROLLER_AXIS_RIGHTY)) {
        SDL_strlcat(mapping_string, "righty:a3,", sizeof(mapping_string));
    }
    if (axis_mask & (1 << SDL_CONTROLLER_AXIS_TRIGGERLEFT)) {
        SDL_strlcat(mapping_string, "lefttrigger:a4,", sizeof(mapping_string));
    }
    if (axis_mask & (1 << SDL_CONTROLLER_AXIS_TRIGGERRIGHT)) {
        SDL_strlcat(mapping_string, "righttrigger:a5,", sizeof(mapping_string));
    }
    return SDL_PrivateAddMappingForGUID(guid, mapping_string,
                      &existing, SDL_CONTROLLER_MAPPING_PRIORITY_DEFAULT);
}
#endif /* __ANDROID__ */
/*
 * Helper function to guess at a mapping for HIDAPI controllers
 */
static ControllerMapping_t *SDL_CreateMappingForHIDAPIController(SDL_JoystickGUID guid)
{
    SDL_bool existing;
    char mapping_string[1024];
    Uint16 vendor;
    Uint16 product;
    SDL_strlcpy(mapping_string, "none,*,", sizeof(mapping_string));
    SDL_GetJoystickGUIDInfo(guid, &vendor, &product, NULL);
    if (vendor == USB_VENDOR_NINTENDO && product == USB_PRODUCT_NINTENDO_GAMECUBE_ADAPTER) {
        /* GameCube driver has 12 buttons and 6 axes */
        SDL_strlcat(mapping_string, "a:b0,b:b1,dpdown:b6,dpleft:b4,dpright:b5,dpup:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b9,righttrigger:a5,rightx:a2,righty:a3,start:b8,x:b2,y:b3,", sizeof(mapping_string));
    } else {
        /* All other controllers have the standard set of 19 buttons and 6 axes */
        if (!SDL_IsJoystickNintendoSwitchPro(vendor, product) ||
            SDL_GetHintBoolean(SDL_HINT_GAMECONTROLLER_USE_BUTTON_LABELS, SDL_TRUE)) {
            SDL_strlcat(mapping_string, "a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,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));
        } else {
            /* Nintendo Switch Pro Controller with swapped face buttons to match Xbox Controller physical layout */
            SDL_strlcat(mapping_string, "a:b1,b:b0,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b3,y:b2,", sizeof(mapping_string));
        }
        if (SDL_IsJoystickXboxOneSeriesX(vendor, product)) {
            /* XBox One Series X Controllers have a share button under the guide button */
            SDL_strlcat(mapping_string, "misc1:b15,", sizeof(mapping_string));
        } else if (SDL_IsJoystickXboxOneElite(vendor, product)) {
            /* XBox One Elite Controllers have 4 back paddle buttons */
            SDL_strlcat(mapping_string, "paddle1:b15,paddle2:b17,paddle3:b16,paddle4:b18,", sizeof(mapping_string));
        } else if (SDL_IsJoystickSteamController(vendor, product)) {
            /* Steam controllers have 2 back paddle buttons */
            SDL_strlcat(mapping_string, "paddle1:b16,paddle2:b15,", sizeof(mapping_string));
        } else {
            switch (SDL_GetJoystickGameControllerTypeFromGUID(guid, NULL)) {
            case SDL_CONTROLLER_TYPE_PS4:
                /* PS4 controllers have an additional touchpad button */
                SDL_strlcat(mapping_string, "touchpad:b15,", sizeof(mapping_string));
                break;
            case SDL_CONTROLLER_TYPE_PS5:
                /* PS5 controllers have a microphone button and an additional touchpad button */
                SDL_strlcat(mapping_string, "misc1:b15,touchpad:b16", sizeof(mapping_string));
                break;
            case SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_PRO:
                /* Nintendo Switch Pro controllers have a screenshot button */
                SDL_strlcat(mapping_string, "misc1:b15,", sizeof(mapping_string));
                break;
            default:
                break;
            }
        }
    }
    return SDL_PrivateAddMappingForGUID(guid, mapping_string,
                      &existing, SDL_CONTROLLER_MAPPING_PRIORITY_DEFAULT);
}
/*
 * Helper function to guess at a mapping for RAWINPUT controllers
 */
static ControllerMapping_t *SDL_CreateMappingForRAWINPUTController(SDL_JoystickGUID guid)
{
    SDL_bool existing;
    char mapping_string[1024];
    SDL_strlcpy(mapping_string, "none,*,", sizeof(mapping_string));
    SDL_strlcat(mapping_string, "a:b0,b:b1,x:b2,y:b3,back:b6,guide:b10,start:b7,leftstick:b8,rightstick:b9,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a4,righttrigger:a5,", sizeof(mapping_string));
    return SDL_PrivateAddMappingForGUID(guid, mapping_string,
                      &existing, SDL_CONTROLLER_MAPPING_PRIORITY_DEFAULT);
}
/*
 * Helper function to scan the mappings database for a controller with the specified GUID
 */
static ControllerMapping_t *SDL_PrivateGetControllerMappingForGUID(SDL_JoystickGUID *guid, SDL_bool exact_match)
static ControllerMapping_t *SDL_PrivateGetControllerMappingForGUID(SDL_JoystickGUID guid, SDL_bool exact_match)
{
    ControllerMapping_t *pSupportedController = s_pSupportedControllers;
    while (pSupportedController) {
        if (SDL_memcmp(guid, &pSupportedController->guid, sizeof(*guid)) == 0) {
            return pSupportedController;
    ControllerMapping_t *mapping = s_pSupportedControllers;
    while (mapping) {
        if (SDL_memcmp(&guid, &mapping->guid, sizeof(guid)) == 0) {
            return mapping;
        }
        pSupportedController = pSupportedController->next;
        mapping = mapping->next;
    }
    if (!exact_match) {
        if (SDL_IsJoystickHIDAPI(*guid)) {
            /* This is a HIDAPI device */
            return s_pHIDAPIMapping;
        }
#if SDL_JOYSTICK_XINPUT
        if (SDL_IsJoystickXInput(*guid)) {
        if (SDL_IsJoystickXInput(guid)) {
            /* This is an XInput device */
            return s_pXInputMapping;
        }
#endif
#ifdef __ANDROID__
        if (!mapping && !SDL_IsJoystickHIDAPI(guid)) {
            mapping = SDL_CreateMappingForAndroidController(guid);
        }
#endif
        if (!mapping && SDL_IsJoystickHIDAPI(guid)) {
            mapping = SDL_CreateMappingForHIDAPIController(guid);
        }
        if (!mapping && SDL_IsJoystickRAWINPUT(guid)) {
            mapping = SDL_CreateMappingForRAWINPUTController(guid);
        }
    }
    return NULL;
    return mapping;
}
static const char* map_StringForControllerAxis[] = {
@@ -507,6 +730,12 @@
    "dpdown",
    "dpleft",
    "dpright",
    "misc1",
    "paddle1",
    "paddle2",
    "paddle3",
    "paddle4",
    "touchpad",
    NULL
};
@@ -845,7 +1074,7 @@
        return NULL;
    }
    pControllerMapping = SDL_PrivateGetControllerMappingForGUID(&jGUID, SDL_TRUE);
    pControllerMapping = SDL_PrivateGetControllerMappingForGUID(jGUID, SDL_TRUE);
    if (pControllerMapping) {
        /* Only overwrite the mapping if the priority is the same or higher. */
        if (pControllerMapping->priority <= priority) {
@@ -894,130 +1123,6 @@
    return pControllerMapping;
}
#ifdef __ANDROID__
/*
 * Helper function to guess at a mapping based on the elements reported for this controller
 */
static ControllerMapping_t *SDL_CreateMappingForAndroidController(const char *name, SDL_JoystickGUID guid)
{
    SDL_bool existing;
    char name_string[128];
    char mapping_string[1024];
    int button_mask;
    int axis_mask;
    button_mask = SDL_SwapLE16(*(Uint16*)(&guid.data[sizeof(guid.data)-4]));
    axis_mask = SDL_SwapLE16(*(Uint16*)(&guid.data[sizeof(guid.data)-2]));
    if (!button_mask && !axis_mask) {
        /* Accelerometer, shouldn't have a game controller mapping */
        return NULL;
    }
    /* Remove any commas in the name */
    SDL_strlcpy(name_string, name, sizeof(name_string));
    {
        char *spot;
        for (spot = name_string; *spot; ++spot) {
            if (*spot == ',') {
                *spot = ' ';
            }
        }
    }
    SDL_snprintf(mapping_string, sizeof(mapping_string), "none,%s,", name_string);
    if (button_mask & (1 << SDL_CONTROLLER_BUTTON_A)) {
        SDL_strlcat(mapping_string, "a:b0,", sizeof(mapping_string));
    }
    if (button_mask & (1 << SDL_CONTROLLER_BUTTON_B)) {
        SDL_strlcat(mapping_string, "b:b1,", sizeof(mapping_string));
    } else if (button_mask & (1 << SDL_CONTROLLER_BUTTON_BACK)) {
        /* Use the back button as "B" for easy UI navigation with TV remotes */
        SDL_strlcat(mapping_string, "b:b4,", sizeof(mapping_string));
        button_mask &= ~(1 << SDL_CONTROLLER_BUTTON_BACK);
    }
    if (button_mask & (1 << SDL_CONTROLLER_BUTTON_X)) {
        SDL_strlcat(mapping_string, "x:b2,", sizeof(mapping_string));
    }
    if (button_mask & (1 << SDL_CONTROLLER_BUTTON_Y)) {
        SDL_strlcat(mapping_string, "y:b3,", sizeof(mapping_string));
    }
    if (button_mask & (1 << SDL_CONTROLLER_BUTTON_BACK)) {
        SDL_strlcat(mapping_string, "back:b4,", sizeof(mapping_string));
    }
#if 0 /* The guide button generally isn't functional (or acts as a home button) on most Android controllers */
    if (button_mask & (1 << SDL_CONTROLLER_BUTTON_GUIDE)) {
        SDL_strlcat(mapping_string, "guide:b5,", sizeof(mapping_string));
#if 0 /* Actually this will be done in Steam */
    } else if (button_mask & (1 << SDL_CONTROLLER_BUTTON_START)) {
        /* The guide button doesn't exist, use the start button instead,
           so you can do Steam guide button chords and open the Steam overlay.
         */
        SDL_strlcat(mapping_string, "guide:b6,", sizeof(mapping_string));
        button_mask &= ~(1 << SDL_CONTROLLER_BUTTON_START);
#endif
    }
#endif
    if (button_mask & (1 << SDL_CONTROLLER_BUTTON_START)) {
        SDL_strlcat(mapping_string, "start:b6,", sizeof(mapping_string));
    }
    if (button_mask & (1 << SDL_CONTROLLER_BUTTON_LEFTSTICK)) {
        SDL_strlcat(mapping_string, "leftstick:b7,", sizeof(mapping_string));
    }
    if (button_mask & (1 << SDL_CONTROLLER_BUTTON_RIGHTSTICK)) {
        SDL_strlcat(mapping_string, "rightstick:b8,", sizeof(mapping_string));
    }
    if (button_mask & (1 << SDL_CONTROLLER_BUTTON_LEFTSHOULDER)) {
        SDL_strlcat(mapping_string, "leftshoulder:b9,", sizeof(mapping_string));
    }
    if (button_mask & (1 << SDL_CONTROLLER_BUTTON_RIGHTSHOULDER)) {
        SDL_strlcat(mapping_string, "rightshoulder:b10,", sizeof(mapping_string));
    }
    if (button_mask & (1 << SDL_CONTROLLER_BUTTON_DPAD_UP)) {
        SDL_strlcat(mapping_string, "dpup:b11,", sizeof(mapping_string));
    }
    if (button_mask & (1 << SDL_CONTROLLER_BUTTON_DPAD_DOWN)) {
        SDL_strlcat(mapping_string, "dpdown:b12,", sizeof(mapping_string));
    }
    if (button_mask & (1 << SDL_CONTROLLER_BUTTON_DPAD_LEFT)) {
        SDL_strlcat(mapping_string, "dpleft:b13,", sizeof(mapping_string));
    }
    if (button_mask & (1 << SDL_CONTROLLER_BUTTON_DPAD_RIGHT)) {
        SDL_strlcat(mapping_string, "dpright:b14,", sizeof(mapping_string));
    }
    if (axis_mask & (1 << SDL_CONTROLLER_AXIS_LEFTX)) {
        SDL_strlcat(mapping_string, "leftx:a0,", sizeof(mapping_string));
    }
    if (axis_mask & (1 << SDL_CONTROLLER_AXIS_LEFTY)) {
        SDL_strlcat(mapping_string, "lefty:a1,", sizeof(mapping_string));
    }
    if (axis_mask & (1 << SDL_CONTROLLER_AXIS_RIGHTX)) {
        SDL_strlcat(mapping_string, "rightx:a2,", sizeof(mapping_string));
    }
    if (axis_mask & (1 << SDL_CONTROLLER_AXIS_RIGHTY)) {
        SDL_strlcat(mapping_string, "righty:a3,", sizeof(mapping_string));
    }
    if (axis_mask & (1 << SDL_CONTROLLER_AXIS_TRIGGERLEFT)) {
        SDL_strlcat(mapping_string, "lefttrigger:a4,", sizeof(mapping_string));
    }
    if (axis_mask & (1 << SDL_CONTROLLER_AXIS_TRIGGERRIGHT)) {
        SDL_strlcat(mapping_string, "righttrigger:a5,", sizeof(mapping_string));
    }
    /* Remove trailing comma */
    {
        int pos = (int)SDL_strlen(mapping_string) - 1;
        if (pos >= 0) {
            if (mapping_string[pos] == ',') {
                mapping_string[pos] = '\0';
            }
        }
    }
    return SDL_PrivateAddMappingForGUID(guid, mapping_string,
                      &existing, SDL_CONTROLLER_MAPPING_PRIORITY_DEFAULT);
}
#endif /* __ANDROID__ */
/*
 * Helper function to determine pre-calculated offset to certain joystick mappings
 */
@@ -1025,7 +1130,7 @@
{
    ControllerMapping_t *mapping;
    mapping = SDL_PrivateGetControllerMappingForGUID(&guid, SDL_FALSE);
    mapping = SDL_PrivateGetControllerMappingForGUID(guid, SDL_FALSE);
#ifdef __LINUX__
    if (!mapping && name) {
        if (SDL_strstr(name, "Xbox 360 Wireless Receiver")) {
@@ -1038,20 +1143,100 @@
    }
#endif /* __LINUX__ */
    if (!mapping && name) {
    if (!mapping && name && !SDL_IsJoystickWGI(guid)) {
        if (SDL_strstr(name, "Xbox") || SDL_strstr(name, "X-Box") || SDL_strstr(name, "XBOX")) {
            mapping = s_pXInputMapping;
        }
    }
#ifdef __ANDROID__
    if (!mapping && name && !SDL_IsJoystickHIDAPI(guid)) {
        mapping = SDL_CreateMappingForAndroidController(name, guid);
    }
#endif
    if (!mapping) {
        mapping = s_pDefaultMapping;
    }
    return mapping;
}
static void SDL_PrivateAppendToMappingString(char *mapping_string,
                                             size_t mapping_string_len,
                                             const char *input_name,
                                             SDL_InputMapping *mapping)
{
    char buffer[16];
    if (mapping->kind == EMappingKind_None) {
        return;
    }
    SDL_strlcat(mapping_string, input_name, mapping_string_len);
    SDL_strlcat(mapping_string, ":", mapping_string_len);
    switch (mapping->kind) {
        case EMappingKind_Button:
            SDL_snprintf(buffer, sizeof(buffer), "b%i", mapping->target);
            break;
        case EMappingKind_Axis:
            SDL_snprintf(buffer, sizeof(buffer), "a%i", mapping->target);
            break;
        case EMappingKind_Hat:
            SDL_snprintf(buffer, sizeof(buffer), "h%i.%i", mapping->target >> 4, mapping->target & 0x0F);
            break;
        default:
            SDL_assert(SDL_FALSE);
    }
    SDL_strlcat(mapping_string, buffer, mapping_string_len);
    SDL_strlcat(mapping_string, ",", mapping_string_len);
}
static ControllerMapping_t *SDL_PrivateGenerateAutomaticControllerMapping(const char *name,
                                                                          SDL_JoystickGUID guid,
                                                                          SDL_GamepadMapping *raw_map)
{
    SDL_bool existing;
    char name_string[128];
    char mapping[1024];
    /* Remove any commas in the name */
    SDL_strlcpy(name_string, name, sizeof(name_string));
    {
        char *spot;
        for (spot = name_string; *spot; ++spot) {
            if (*spot == ',') {
                *spot = ' ';
            }
        }
    }
    SDL_snprintf(mapping, sizeof(mapping), "none,%s,", name_string);
    SDL_PrivateAppendToMappingString(mapping, sizeof(mapping), "a", &raw_map->a);
    SDL_PrivateAppendToMappingString(mapping, sizeof(mapping), "b", &raw_map->b);
    SDL_PrivateAppendToMappingString(mapping, sizeof(mapping), "x", &raw_map->x);
    SDL_PrivateAppendToMappingString(mapping, sizeof(mapping), "y", &raw_map->y);
    SDL_PrivateAppendToMappingString(mapping, sizeof(mapping), "back", &raw_map->back);
    SDL_PrivateAppendToMappingString(mapping, sizeof(mapping), "guide", &raw_map->guide);
    SDL_PrivateAppendToMappingString(mapping, sizeof(mapping), "start", &raw_map->start);
    SDL_PrivateAppendToMappingString(mapping, sizeof(mapping), "leftstick", &raw_map->leftstick);
    SDL_PrivateAppendToMappingString(mapping, sizeof(mapping), "rightstick", &raw_map->rightstick);
    SDL_PrivateAppendToMappingString(mapping, sizeof(mapping), "leftshoulder", &raw_map->leftshoulder);
    SDL_PrivateAppendToMappingString(mapping, sizeof(mapping), "rightshoulder", &raw_map->rightshoulder);
    SDL_PrivateAppendToMappingString(mapping, sizeof(mapping), "dpup", &raw_map->dpup);
    SDL_PrivateAppendToMappingString(mapping, sizeof(mapping), "dpdown", &raw_map->dpdown);
    SDL_PrivateAppendToMappingString(mapping, sizeof(mapping), "dpleft", &raw_map->dpleft);
    SDL_PrivateAppendToMappingString(mapping, sizeof(mapping), "dpright", &raw_map->dpright);
    SDL_PrivateAppendToMappingString(mapping, sizeof(mapping), "leftx", &raw_map->leftx);
    SDL_PrivateAppendToMappingString(mapping, sizeof(mapping), "lefty", &raw_map->lefty);
    SDL_PrivateAppendToMappingString(mapping, sizeof(mapping), "rightx", &raw_map->rightx);
    SDL_PrivateAppendToMappingString(mapping, sizeof(mapping), "righty", &raw_map->righty);
    SDL_PrivateAppendToMappingString(mapping, sizeof(mapping), "lefttrigger", &raw_map->lefttrigger);
    SDL_PrivateAppendToMappingString(mapping, sizeof(mapping), "righttrigger", &raw_map->righttrigger);
    /* Remove trailing comma */
    {
        int pos = (int)SDL_strlen(mapping) - 1;
        if (pos >= 0) {
            if (mapping[pos] == ',') {
                mapping[pos] = '\0';
            }
        }
    }
    return SDL_PrivateAddMappingForGUID(guid, mapping,
                      &existing, SDL_CONTROLLER_MAPPING_PRIORITY_DEFAULT);
}
static ControllerMapping_t *SDL_PrivateGetControllerMapping(int device_index)
@@ -1071,6 +1256,15 @@
    name = SDL_JoystickNameForIndex(device_index);
    guid = SDL_JoystickGetDeviceGUID(device_index);
    mapping = SDL_PrivateGetControllerMappingForNameAndGUID(name, guid);
    if (!mapping) {
        SDL_GamepadMapping raw_map;
        SDL_zero(raw_map);
        if (SDL_PrivateJoystickGetAutoGamepadMapping(device_index, &raw_map)) {
            mapping = SDL_PrivateGenerateAutomaticControllerMapping(name, guid, &raw_map);
        }
    }
    SDL_UnlockJoysticks();
    return mapping;
}
@@ -1155,13 +1349,53 @@
    char *pchGUID;
    SDL_JoystickGUID jGUID;
    SDL_bool is_default_mapping = SDL_FALSE;
    SDL_bool is_hidapi_mapping = SDL_FALSE;
    SDL_bool is_xinput_mapping = SDL_FALSE;
    SDL_bool existing = SDL_FALSE;
    ControllerMapping_t *pControllerMapping;
    if (!mappingString) {
        return SDL_InvalidParamError("mappingString");
    }
    { /* Extract and verify the hint field */
        const char *tmp;
        tmp = SDL_strstr(mappingString, SDL_CONTROLLER_HINT_FIELD);
        if (tmp != NULL) {
            SDL_bool default_value, value, negate;
            int len;
            char hint[128];
            tmp += SDL_strlen(SDL_CONTROLLER_HINT_FIELD);
            if (*tmp == '!') {
                negate = SDL_TRUE;
                ++tmp;
            } else {
                negate = SDL_FALSE;
            }
            len = 0;
            while (*tmp && *tmp != ',' && *tmp != ':' && len < (sizeof(hint) - 1)) {
                hint[len++] = *tmp++;
            }
            hint[len] = '\0';
            if (tmp[0] == ':' && tmp[1] == '=') {
                tmp += 2;
                default_value = SDL_atoi(tmp);
            } else {
                default_value = SDL_FALSE;
            }
            value = SDL_GetHintBoolean(hint, default_value);
            if (negate) {
                value = !value;
            }
            if (!value) {
                return 0;
            }
        }
    }
#ifdef ANDROID
@@ -1191,8 +1425,6 @@
    }
    if (!SDL_strcasecmp(pchGUID, "default")) {
        is_default_mapping = SDL_TRUE;
    } else if (!SDL_strcasecmp(pchGUID, "hidapi")) {
        is_hidapi_mapping = SDL_TRUE;
    } else if (!SDL_strcasecmp(pchGUID, "xinput")) {
        is_xinput_mapping = SDL_TRUE;
    }
@@ -1209,8 +1441,6 @@
    } else {
        if (is_default_mapping) {
            s_pDefaultMapping = pControllerMapping;
        } else if (is_hidapi_mapping) {
            s_pHIDAPIMapping = pControllerMapping;
        } else if (is_xinput_mapping) {
            s_pXInputMapping = pControllerMapping;
        }
@@ -1285,7 +1515,7 @@
SDL_GameControllerMappingForGUID(SDL_JoystickGUID guid)
{
    char *pMappingString = NULL;
    ControllerMapping_t *mapping = SDL_PrivateGetControllerMappingForGUID(&guid, SDL_FALSE);
    ControllerMapping_t *mapping = SDL_PrivateGetControllerMappingForGUID(guid, SDL_FALSE);
    if (mapping) {
        char pchGUID[33];
        size_t needed;
@@ -1306,7 +1536,7 @@
 * Get the mapping string for this device
 */
char *
SDL_GameControllerMapping(SDL_GameController * gamecontroller)
SDL_GameControllerMapping(SDL_GameController *gamecontroller)
{
    if (!gamecontroller) {
        return NULL;
@@ -1517,7 +1747,7 @@
    Uint32 vidpid;
#if defined(__LINUX__)
    if (name && SDL_strstr(name, "Controller Motion Sensors")) {
    if (name && SDL_strstr(name, "Motion Sensors")) {
        /* Don't treat the PS3 and PS4 motion controls as a separate game controller */
        return SDL_TRUE;
    }
@@ -1663,11 +1893,21 @@
    SDL_JoystickUpdate();
}
/**
 *  Return whether a game controller has a given axis
 */
SDL_bool
SDL_GameControllerHasAxis(SDL_GameController *gamecontroller, SDL_GameControllerAxis axis)
{
    SDL_GameControllerButtonBind bind = SDL_GameControllerGetBindForAxis(gamecontroller, axis);
    return (bind.bindType != SDL_CONTROLLER_BINDTYPE_NONE) ? SDL_TRUE : SDL_FALSE;
}
/*
 * Get the current state of an axis control on a controller
 */
Sint16
SDL_GameControllerGetAxis(SDL_GameController * gamecontroller, SDL_GameControllerAxis axis)
SDL_GameControllerGetAxis(SDL_GameController *gamecontroller, SDL_GameControllerAxis axis)
{
    int i;
@@ -1722,11 +1962,21 @@
    return 0;
}
/**
 *  Return whether a game controller has a given button
 */
SDL_bool
SDL_GameControllerHasButton(SDL_GameController *gamecontroller, SDL_GameControllerButton button)
{
    SDL_GameControllerButtonBind bind = SDL_GameControllerGetBindForButton(gamecontroller, button);
    return (bind.bindType != SDL_CONTROLLER_BINDTYPE_NONE) ? SDL_TRUE : SDL_FALSE;
}
/*
 * Get the current state of a button on a controller
 */
Uint8
SDL_GameControllerGetButton(SDL_GameController * gamecontroller, SDL_GameControllerButton button)
SDL_GameControllerGetButton(SDL_GameController *gamecontroller, SDL_GameControllerButton button)
{
    int i;
@@ -1763,8 +2013,178 @@
    return SDL_RELEASED;
}
/**
 *  Get the number of touchpads on a game controller.
 */
int
SDL_GameControllerGetNumTouchpads(SDL_GameController *gamecontroller)
{
    SDL_Joystick *joystick = SDL_GameControllerGetJoystick(gamecontroller);
    if (joystick) {
        return joystick->ntouchpads;
    }
    return 0;
}
/**
 *  Get the number of supported simultaneous fingers on a touchpad on a game controller.
 */
int SDL_GameControllerGetNumTouchpadFingers(SDL_GameController *gamecontroller, int touchpad)
{
    SDL_Joystick *joystick = SDL_GameControllerGetJoystick(gamecontroller);
    if (joystick && touchpad >= 0 && touchpad < joystick->ntouchpads) {
        return joystick->touchpads[touchpad].nfingers;
    }
    return 0;
}
/**
 *  Get the current state of a finger on a touchpad on a game controller.
 */
int
SDL_GameControllerGetTouchpadFinger(SDL_GameController *gamecontroller, int touchpad, int finger, Uint8 *state, float *x, float *y, float *pressure)
{
    SDL_Joystick *joystick = SDL_GameControllerGetJoystick(gamecontroller);
    if (joystick ) {
        if (touchpad >= 0 && touchpad < joystick->ntouchpads) {
            SDL_JoystickTouchpadInfo *touchpad_info = &joystick->touchpads[touchpad];
            if (finger >= 0 && finger < touchpad_info->nfingers) {
                SDL_JoystickTouchpadFingerInfo *info = &touchpad_info->fingers[finger];
                if (state) {
                    *state = info->state;
                }
                if (x) {
                    *x = info->x;
                }
                if (y) {
                    *y = info->y;
                }
                if (pressure) {
                    *pressure = info->pressure;
                }
                return 0;
            } else {
                return SDL_InvalidParamError("finger");
            }
        } else {
            return SDL_InvalidParamError("touchpad");
        }
    } else {
        return SDL_InvalidParamError("gamecontroller");
    }
}
/**
 *  Return whether a game controller has a particular sensor.
 */
SDL_bool
SDL_GameControllerHasSensor(SDL_GameController *gamecontroller, SDL_SensorType type)
{
    SDL_Joystick *joystick = SDL_GameControllerGetJoystick(gamecontroller);
    int i;
    if (joystick) {
        for (i = 0; i < joystick->nsensors; ++i) {
            if (joystick->sensors[i].type == type) {
                return SDL_TRUE;
            }
        }
    }
    return SDL_FALSE;
}
/*
 *  Set whether data reporting for a game controller sensor is enabled
 */
int SDL_GameControllerSetSensorEnabled(SDL_GameController *gamecontroller, SDL_SensorType type, SDL_bool enabled)
{
    SDL_Joystick *joystick = SDL_GameControllerGetJoystick(gamecontroller);
    int i;
    if (!joystick) {
        return SDL_InvalidParamError("gamecontroller");
    }
    for (i = 0; i < joystick->nsensors; ++i) {
        SDL_JoystickSensorInfo *sensor = &joystick->sensors[i];
        if (sensor->type == type) {
            if (sensor->enabled == enabled) {
                return 0;
            }
            if (enabled) {
                if (joystick->nsensors_enabled == 0) {
                    if (joystick->driver->SetSensorsEnabled(joystick, SDL_TRUE) < 0) {
                        return -1;
                    }
                }
                ++joystick->nsensors_enabled;
            } else {
                if (joystick->nsensors_enabled == 1) {
                    if (joystick->driver->SetSensorsEnabled(joystick, SDL_FALSE) < 0) {
                        return -1;
                    }
                }
                --joystick->nsensors_enabled;
            }
            sensor->enabled = enabled;
            return 0;
        }
    }
    return SDL_Unsupported();
}
/*
 *  Query whether sensor data reporting is enabled for a game controller
 */
SDL_bool SDL_GameControllerIsSensorEnabled(SDL_GameController *gamecontroller, SDL_SensorType type)
{
    SDL_Joystick *joystick = SDL_GameControllerGetJoystick(gamecontroller);
    int i;
    if (joystick) {
        for (i = 0; i < joystick->nsensors; ++i) {
            if (joystick->sensors[i].type == type) {
                return joystick->sensors[i].enabled;
            }
        }
    }
    return SDL_FALSE;
}
/*
 *  Get the current state of a game controller sensor.
 */
int
SDL_GameControllerGetSensorData(SDL_GameController *gamecontroller, SDL_SensorType type, float *data, int num_values)
{
    SDL_Joystick *joystick = SDL_GameControllerGetJoystick(gamecontroller);
    int i;
    if (!joystick) {
        return SDL_InvalidParamError("gamecontroller");
    }
    for (i = 0; i < joystick->nsensors; ++i) {
        SDL_JoystickSensorInfo *sensor = &joystick->sensors[i];
        if (sensor->type == type) {
            num_values = SDL_min(num_values, SDL_arraysize(sensor->data));
            SDL_memcpy(data, sensor->data, num_values*sizeof(*data));
            return 0;
        }
    }
    return SDL_Unsupported();
}
const char *
SDL_GameControllerName(SDL_GameController * gamecontroller)
SDL_GameControllerName(SDL_GameController *gamecontroller)
{
    if (!gamecontroller)
        return NULL;
@@ -1798,21 +2218,27 @@
}
Uint16
SDL_GameControllerGetVendor(SDL_GameController * gamecontroller)
SDL_GameControllerGetVendor(SDL_GameController *gamecontroller)
{
    return SDL_JoystickGetVendor(SDL_GameControllerGetJoystick(gamecontroller));
}
Uint16
SDL_GameControllerGetProduct(SDL_GameController * gamecontroller)
SDL_GameControllerGetProduct(SDL_GameController *gamecontroller)
{
    return SDL_JoystickGetProduct(SDL_GameControllerGetJoystick(gamecontroller));
}
Uint16
SDL_GameControllerGetProductVersion(SDL_GameController * gamecontroller)
SDL_GameControllerGetProductVersion(SDL_GameController *gamecontroller)
{
    return SDL_JoystickGetProductVersion(SDL_GameControllerGetJoystick(gamecontroller));
}
const char *
SDL_GameControllerGetSerial(SDL_GameController *gamecontroller)
{
    return SDL_JoystickGetSerial(SDL_GameControllerGetJoystick(gamecontroller));
}
/*
@@ -1820,7 +2246,7 @@
 *  \return 0 if not plugged in, 1 if still present.
 */
SDL_bool
SDL_GameControllerGetAttached(SDL_GameController * gamecontroller)
SDL_GameControllerGetAttached(SDL_GameController *gamecontroller)
{
    if (!gamecontroller)
        return SDL_FALSE;
@@ -1831,7 +2257,8 @@
/*
 * Get the joystick for this controller
 */
SDL_Joystick *SDL_GameControllerGetJoystick(SDL_GameController * gamecontroller)
SDL_Joystick *
SDL_GameControllerGetJoystick(SDL_GameController *gamecontroller)
{
    if (!gamecontroller)
        return NULL;
@@ -1878,7 +2305,7 @@
/*
 * Get the SDL joystick layer binding for this controller axis mapping
 */
SDL_GameControllerButtonBind SDL_GameControllerGetBindForAxis(SDL_GameController * gamecontroller, SDL_GameControllerAxis axis)
SDL_GameControllerButtonBind SDL_GameControllerGetBindForAxis(SDL_GameController *gamecontroller, SDL_GameControllerAxis axis)
{
    int i;
    SDL_GameControllerButtonBind bind;
@@ -1910,7 +2337,7 @@
/*
 * Get the SDL joystick layer binding for this controller button mapping
 */
SDL_GameControllerButtonBind SDL_GameControllerGetBindForButton(SDL_GameController * gamecontroller, SDL_GameControllerButton button)
SDL_GameControllerButtonBind SDL_GameControllerGetBindForButton(SDL_GameController *gamecontroller, SDL_GameControllerButton button)
{
    int i;
    SDL_GameControllerButtonBind bind;
@@ -1944,8 +2371,26 @@
    return SDL_JoystickRumble(SDL_GameControllerGetJoystick(gamecontroller), low_frequency_rumble, high_frequency_rumble, duration_ms);
}
int
SDL_GameControllerRumbleTriggers(SDL_GameController *gamecontroller, Uint16 left_rumble, Uint16 right_rumble, Uint32 duration_ms)
{
    return SDL_JoystickRumbleTriggers(SDL_GameControllerGetJoystick(gamecontroller), left_rumble, right_rumble, duration_ms);
}
SDL_bool
SDL_GameControllerHasLED(SDL_GameController *gamecontroller)
{
    return SDL_JoystickHasLED(SDL_GameControllerGetJoystick(gamecontroller));
}
int
SDL_GameControllerSetLED(SDL_GameController *gamecontroller, Uint8 red, Uint8 green, Uint8 blue)
{
    return SDL_JoystickSetLED(SDL_GameControllerGetJoystick(gamecontroller), red, green, blue);
}
void
SDL_GameControllerClose(SDL_GameController * gamecontroller)
SDL_GameControllerClose(SDL_GameController *gamecontroller)
{
    SDL_GameController *gamecontrollerlist, *gamecontrollerlistprev;
@@ -2035,7 +2480,7 @@
 * Event filter to transform joystick events into appropriate game controller ones
 */
static int
SDL_PrivateGameControllerAxis(SDL_GameController * gamecontroller, SDL_GameControllerAxis axis, Sint16 value)
SDL_PrivateGameControllerAxis(SDL_GameController *gamecontroller, SDL_GameControllerAxis axis, Sint16 value)
{
    int posted;
@@ -2059,7 +2504,7 @@
 * Event filter to transform joystick events into appropriate game controller ones
 */
static int
SDL_PrivateGameControllerButton(SDL_GameController * gamecontroller, SDL_GameControllerButton button, Uint8 state)
SDL_PrivateGameControllerButton(SDL_GameController *gamecontroller, SDL_GameControllerButton button, Uint8 state)
{
    int posted;
#if !SDL_EVENTS_DISABLED
@@ -2091,7 +2536,7 @@
                return (0);
            }
        } else {
            if (!SDL_TICKS_PASSED(now, gamecontroller->guide_button_down+SDL_MINIMUM_GUIDE_BUTTON_DELAY_MS) && !gamecontroller->joystick->force_recentering) {
            if (!SDL_TICKS_PASSED(now, gamecontroller->guide_button_down+SDL_MINIMUM_GUIDE_BUTTON_DELAY_MS)) {
                gamecontroller->joystick->delayed_guide_button = SDL_TRUE;
                return (0);
            }