| | |
| | | /* |
| | | Simple DirectMedia Layer |
| | | Copyright (C) 1997-2018 Sam Lantinga <slouken@libsdl.org> |
| | | Copyright (C) 1997-2020 Sam Lantinga <slouken@libsdl.org> |
| | | |
| | | This software is provided 'as-is', without any express or implied |
| | | warranty. In no event will the authors be held liable for any damages |
| | |
| | | /* Many controllers turn the center button into an instantaneous button press */ |
| | | #define SDL_MINIMUM_GUIDE_BUTTON_DELAY_MS 250 |
| | | |
| | | #define SDL_CONTROLLER_PLATFORM_FIELD "platform:" |
| | | #define SDL_CONTROLLER_PLATFORM_FIELD "platform:" |
| | | #define SDL_CONTROLLER_SDKGE_FIELD "sdk>=:" |
| | | #define SDL_CONTROLLER_SDKLE_FIELD "sdk<=:" |
| | | |
| | | /* a list of currently opened game controllers */ |
| | | static SDL_GameController *SDL_gamecontrollers = NULL; |
| | |
| | | { |
| | | int i, num_events; |
| | | SDL_Event *events; |
| | | SDL_bool isstack; |
| | | |
| | | num_events = SDL_PeepEvents(NULL, 0, SDL_PEEKEVENT, SDL_CONTROLLERDEVICEADDED, SDL_CONTROLLERDEVICEADDED); |
| | | if (num_events <= 0) { |
| | | return; |
| | | } |
| | | |
| | | events = SDL_stack_alloc(SDL_Event, num_events); |
| | | events = SDL_small_alloc(SDL_Event, num_events, &isstack); |
| | | if (!events) { |
| | | return; |
| | | } |
| | |
| | | } |
| | | SDL_PeepEvents(events, num_events, SDL_ADDEVENT, 0, 0); |
| | | |
| | | SDL_stack_free(events); |
| | | SDL_small_free(events, isstack); |
| | | } |
| | | |
| | | static SDL_bool HasSameOutput(SDL_ExtendedGameControllerBind *a, SDL_ExtendedGameControllerBind *b) |
| | |
| | | int i = 0; |
| | | const char *pchPos = pchString; |
| | | |
| | | SDL_zero(szGameButton); |
| | | SDL_zero(szJoystickButton); |
| | | SDL_zeroa(szGameButton); |
| | | SDL_zeroa(szJoystickButton); |
| | | |
| | | while (pchPos && *pchPos) { |
| | | if (*pchPos == ':') { |
| | |
| | | i = 0; |
| | | bGameButton = SDL_TRUE; |
| | | SDL_PrivateGameControllerParseElement(gamecontroller, szGameButton, szJoystickButton); |
| | | SDL_zero(szGameButton); |
| | | SDL_zero(szJoystickButton); |
| | | SDL_zeroa(szGameButton); |
| | | SDL_zeroa(szJoystickButton); |
| | | |
| | | } else if (bGameButton) { |
| | | if (i >= sizeof(szGameButton)) { |
| | |
| | | pchPos++; |
| | | } |
| | | |
| | | SDL_PrivateGameControllerParseElement(gamecontroller, szGameButton, szJoystickButton); |
| | | |
| | | /* No more values if the string was terminated by a comma. Don't report an error. */ |
| | | if (szGameButton[0] != '\0' || szJoystickButton[0] != '\0') { |
| | | SDL_PrivateGameControllerParseElement(gamecontroller, szGameButton, szJoystickButton); |
| | | } |
| | | } |
| | | |
| | | /* |
| | |
| | | |
| | | gamecontroller->name = pchName; |
| | | gamecontroller->num_bindings = 0; |
| | | SDL_memset(gamecontroller->last_match_axis, 0, gamecontroller->joystick->naxes * sizeof(*gamecontroller->last_match_axis)); |
| | | if (gamecontroller->joystick->naxes) { |
| | | SDL_memset(gamecontroller->last_match_axis, 0, gamecontroller->joystick->naxes * sizeof(*gamecontroller->last_match_axis)); |
| | | } |
| | | |
| | | SDL_PrivateGameControllerParseControllerConfigString(gamecontroller, pchMapping); |
| | | |
| | |
| | | for ( pPrevMapping = s_pSupportedControllers, pCurrMapping = pPrevMapping->next; |
| | | pCurrMapping; |
| | | pPrevMapping = pCurrMapping, pCurrMapping = pCurrMapping->next ) { |
| | | continue; |
| | | /* continue; */ |
| | | } |
| | | pPrevMapping->next = pControllerMapping; |
| | | } else { |
| | |
| | | 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); |
| | | } |
| | |
| | | /* The Linux driver xpad.c maps the wireless dpad to buttons */ |
| | | SDL_bool existing; |
| | | mapping = SDL_PrivateAddMappingForGUID(guid, |
| | | "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,", |
| | | "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", |
| | | &existing, SDL_CONTROLLER_MAPPING_PRIORITY_DEFAULT); |
| | | } |
| | | } |
| | |
| | | } |
| | | } |
| | | } |
| | | |
| | | |
| | | line = line_end + 1; |
| | | } |
| | | |
| | |
| | | if (!mappingString) { |
| | | return SDL_InvalidParamError("mappingString"); |
| | | } |
| | | |
| | | #ifdef ANDROID |
| | | { /* Extract and verify the SDK version */ |
| | | const char *tmp; |
| | | |
| | | tmp = SDL_strstr(mappingString, SDL_CONTROLLER_SDKGE_FIELD); |
| | | if (tmp != NULL) { |
| | | tmp += SDL_strlen(SDL_CONTROLLER_SDKGE_FIELD); |
| | | if (!(SDL_GetAndroidSDKVersion() >= SDL_atoi(tmp))) { |
| | | return SDL_SetError("SDK version %d < minimum version %d", SDL_GetAndroidSDKVersion(), SDL_atoi(tmp)); |
| | | } |
| | | } |
| | | tmp = SDL_strstr(mappingString, SDL_CONTROLLER_SDKLE_FIELD); |
| | | if (tmp != NULL) { |
| | | tmp += SDL_strlen(SDL_CONTROLLER_SDKLE_FIELD); |
| | | if (!(SDL_GetAndroidSDKVersion() <= SDL_atoi(tmp))) { |
| | | return SDL_SetError("SDK version %d > maximum version %d", SDL_GetAndroidSDKVersion(), SDL_atoi(tmp)); |
| | | } |
| | | } |
| | | } |
| | | #endif |
| | | |
| | | pchGUID = SDL_PrivateGetControllerGUIDFromMappingString(mappingString); |
| | | if (!pchGUID) { |
| | |
| | | |
| | | /* |
| | | * Fill the given buffer with the expected controller mapping filepath. |
| | | * Usually this will just be CONTROLLER_MAPPING_FILE, but for Android, |
| | | * we want to get the internal storage path. |
| | | * Usually this will just be SDL_HINT_GAMECONTROLLERCONFIG_FILE, but for |
| | | * Android, we want to get the internal storage path. |
| | | */ |
| | | static SDL_bool SDL_GetControllerMappingFilePath(char *path, size_t size) |
| | | { |
| | | #ifdef CONTROLLER_MAPPING_FILE |
| | | #define STRING(X) SDL_STRINGIFY_ARG(X) |
| | | return SDL_strlcpy(path, STRING(CONTROLLER_MAPPING_FILE), size) < size; |
| | | #elif defined(__ANDROID__) |
| | | const char *hint = SDL_GetHint(SDL_HINT_GAMECONTROLLERCONFIG_FILE); |
| | | if (hint && *hint) { |
| | | return SDL_strlcpy(path, hint, size) < size; |
| | | } |
| | | |
| | | #if defined(__ANDROID__) |
| | | return SDL_snprintf(path, size, "%s/controller_map.txt", SDL_AndroidGetInternalStoragePath()) < size; |
| | | #else |
| | | return SDL_FALSE; |
| | |
| | | |
| | | |
| | | /** |
| | | * Get the type of a game controller. |
| | | */ |
| | | SDL_GameControllerType |
| | | SDL_GameControllerTypeForIndex(int joystick_index) |
| | | { |
| | | return SDL_GetJoystickGameControllerTypeFromGUID(SDL_JoystickGetDeviceGUID(joystick_index), SDL_JoystickNameForIndex(joystick_index)); |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Get the mapping of a game controller. |
| | | * This can be called before any controllers are opened. |
| | | * If no mapping can be found, this function returns NULL. |
| | |
| | | Uint16 product; |
| | | Uint16 version; |
| | | Uint32 vidpid; |
| | | |
| | | #if defined(__LINUX__) |
| | | if (name && SDL_strstr(name, "Controller Motion Sensors")) { |
| | | /* Don't treat the PS3 and PS4 motion controls as a separate game controller */ |
| | | return SDL_TRUE; |
| | | } |
| | | #endif |
| | | |
| | | if (SDL_allowed_controllers.num_entries == 0 && |
| | | SDL_ignored_controllers.num_entries == 0) { |
| | |
| | | float normalized_value = (float)(value - binding->input.axis.axis_min) / (binding->input.axis.axis_max - binding->input.axis.axis_min); |
| | | value = binding->output.axis.axis_min + (int)(normalized_value * (binding->output.axis.axis_max - binding->output.axis.axis_min)); |
| | | } |
| | | } else { |
| | | value = 0; |
| | | } |
| | | } else if (binding->inputType == SDL_CONTROLLER_BINDTYPE_BUTTON) { |
| | | value = SDL_JoystickGetButton(gamecontroller->joystick, binding->input.button); |
| | |
| | | } |
| | | } |
| | | |
| | | SDL_GameControllerType |
| | | SDL_GameControllerGetType(SDL_GameController *gamecontroller) |
| | | { |
| | | return SDL_GetJoystickGameControllerTypeFromGUID(SDL_JoystickGetGUID(SDL_GameControllerGetJoystick(gamecontroller)), SDL_JoystickName(SDL_GameControllerGetJoystick(gamecontroller))); |
| | | } |
| | | |
| | | int |
| | | SDL_GameControllerGetPlayerIndex(SDL_GameController *gamecontroller) |
| | | { |
| | | return SDL_JoystickGetPlayerIndex(SDL_GameControllerGetJoystick(gamecontroller)); |
| | | } |
| | | |
| | | /** |
| | | * Set the player index of an opened game controller |
| | | */ |
| | | void |
| | | SDL_GameControllerSetPlayerIndex(SDL_GameController *gamecontroller, int player_index) |
| | | { |
| | | SDL_JoystickSetPlayerIndex(SDL_GameControllerGetJoystick(gamecontroller), player_index); |
| | | } |
| | | |
| | | Uint16 |
| | |
| | | |
| | | |
| | | /* |
| | | * Find the SDL_GameController that owns this instance id |
| | | * Return the SDL_GameController associated with an instance id. |
| | | */ |
| | | SDL_GameController * |
| | | SDL_GameControllerFromInstanceID(SDL_JoystickID joyid) |
| | |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Return the SDL_GameController associated with a player index. |
| | | */ |
| | | SDL_GameController *SDL_GameControllerFromPlayerIndex(int player_index) |
| | | { |
| | | SDL_Joystick *joystick = SDL_JoystickFromPlayerIndex(player_index); |
| | | if (joystick) { |
| | | return SDL_GameControllerFromInstanceID(joystick->instance_id); |
| | | } |
| | | return NULL; |
| | | } |
| | | |
| | | |
| | | /* |
| | | * Get the SDL joystick layer binding for this controller axis mapping |
| | | */ |