From b271e92c6e55db4212f9216d32c72349c3d94458 Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Mon, 17 Jul 2023 12:14:37 -0700 Subject: [PATCH] Added the ability to specify a gamepad type in the mapping Also renamed most cases of SDL_GAMEPAD_TYPE_UNKNOWN to SDL_GAMEPAD_TYPE_STANDARD, and SDL_GetGamepadType() will return SDL_GAMEPAD_TYPE_UNKNOWN only if the gamepad is invalid. --- docs/README-migration.md | 5 +- include/SDL3/SDL_gamepad.h | 74 ++++++-- include/SDL3/SDL_oldnames.h | 10 +- src/dynapi/SDL_dynapi.sym | 4 + src/dynapi/SDL_dynapi_overrides.h | 4 + src/dynapi/SDL_dynapi_procs.h | 4 + src/joystick/SDL_gamepad.c | 122 +++++++++++-- src/joystick/SDL_joystick.c | 12 +- src/joystick/hidapi/SDL_hidapi_switch.c | 16 +- src/joystick/hidapi/SDL_hidapijoystick.c | 8 +- src/joystick/sort_controllers.py | 4 + test/gamepadutils.c | 218 ++++++++++++++++++++++- test/gamepadutils.h | 25 ++- test/testcontroller.c | 89 ++++++++- 14 files changed, 530 insertions(+), 65 deletions(-) diff --git a/docs/README-migration.md b/docs/README-migration.md index 68152ecc7..19b30414b 100644 --- a/docs/README-migration.md +++ b/docs/README-migration.md @@ -390,17 +390,14 @@ The following symbols have been renamed: * SDL_CONTROLLER_BUTTON_TOUCHPAD => SDL_GAMEPAD_BUTTON_TOUCHPAD * SDL_CONTROLLER_BUTTON_X => SDL_GAMEPAD_BUTTON_X * SDL_CONTROLLER_BUTTON_Y => SDL_GAMEPAD_BUTTON_Y -* SDL_CONTROLLER_TYPE_AMAZON_LUNA => SDL_GAMEPAD_TYPE_AMAZON_LUNA -* SDL_CONTROLLER_TYPE_GOOGLE_STADIA => SDL_GAMEPAD_TYPE_GOOGLE_STADIA * SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_JOYCON_LEFT => SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_JOYCON_LEFT * SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_JOYCON_PAIR => SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_JOYCON_PAIR * SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_JOYCON_RIGHT => SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_JOYCON_RIGHT * SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_PRO => SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_PRO -* SDL_CONTROLLER_TYPE_NVIDIA_SHIELD => SDL_GAMEPAD_TYPE_NVIDIA_SHIELD * SDL_CONTROLLER_TYPE_PS3 => SDL_GAMEPAD_TYPE_PS3 * SDL_CONTROLLER_TYPE_PS4 => SDL_GAMEPAD_TYPE_PS4 * SDL_CONTROLLER_TYPE_PS5 => SDL_GAMEPAD_TYPE_PS5 -* SDL_CONTROLLER_TYPE_UNKNOWN => SDL_GAMEPAD_TYPE_UNKNOWN +* SDL_CONTROLLER_TYPE_UNKNOWN => SDL_GAMEPAD_TYPE_STANDARD * SDL_CONTROLLER_TYPE_VIRTUAL => SDL_GAMEPAD_TYPE_VIRTUAL * SDL_CONTROLLER_TYPE_XBOX360 => SDL_GAMEPAD_TYPE_XBOX360 * SDL_CONTROLLER_TYPE_XBOXONE => SDL_GAMEPAD_TYPE_XBOXONE diff --git a/include/SDL3/SDL_gamepad.h b/include/SDL3/SDL_gamepad.h index 8773d7d29..aaa6e2b97 100644 --- a/include/SDL3/SDL_gamepad.h +++ b/include/SDL3/SDL_gamepad.h @@ -61,6 +61,7 @@ typedef struct SDL_Gamepad SDL_Gamepad; typedef enum { SDL_GAMEPAD_TYPE_UNKNOWN = 0, + SDL_GAMEPAD_TYPE_STANDARD, SDL_GAMEPAD_TYPE_XBOX360, SDL_GAMEPAD_TYPE_XBOXONE, SDL_GAMEPAD_TYPE_PS3, @@ -70,6 +71,7 @@ typedef enum SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_JOYCON_LEFT, SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_JOYCON_RIGHT, SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_JOYCON_PAIR, + SDL_GAMEPAD_TYPE_MAX } SDL_GamepadType; /** @@ -411,6 +413,18 @@ extern DECLSPEC Uint16 SDLCALL SDL_GetGamepadInstanceProductVersion(SDL_Joystick */ extern DECLSPEC SDL_GamepadType SDLCALL SDL_GetGamepadInstanceType(SDL_JoystickID instance_id); +/** + * Get the type of a gamepad, ignoring any mapping override. + * + * This can be called before any gamepads are opened. + * + * \param instance_id the joystick instance ID + * \returns the gamepad type. + * + * \since This function is available since SDL 3.0.0. + */ +extern DECLSPEC SDL_GamepadType SDLCALL SDL_GetRealGamepadInstanceType(SDL_JoystickID instance_id); + /** * Get the mapping of a gamepad. * @@ -481,9 +495,6 @@ extern DECLSPEC SDL_JoystickID SDLCALL SDL_GetGamepadInstanceID(SDL_Gamepad *gam /** * Get the implementation-dependent name for an opened gamepad. * - * This is the same name as returned by SDL_GetGamepadNameForIndex(), but it - * takes a gamepad identifier instead of the (unstable) device index. - * * \param gamepad a gamepad identifier previously returned by * SDL_OpenGamepad() * \returns the implementation dependent name for the gamepad, or NULL if @@ -499,9 +510,6 @@ extern DECLSPEC const char *SDLCALL SDL_GetGamepadName(SDL_Gamepad *gamepad); /** * Get the implementation-dependent path for an opened gamepad. * - * This is the same path as returned by SDL_GetGamepadNameForIndex(), but it - * takes a gamepad identifier instead of the (unstable) device index. - * * \param gamepad a gamepad identifier previously returned by * SDL_OpenGamepad() * \returns the implementation dependent path for the gamepad, or NULL if @@ -514,18 +522,29 @@ extern DECLSPEC const char *SDLCALL SDL_GetGamepadName(SDL_Gamepad *gamepad); extern DECLSPEC const char *SDLCALL SDL_GetGamepadPath(SDL_Gamepad *gamepad); /** - * Get the type of this currently opened gamepad - * - * This is the same name as returned by SDL_GetGamepadInstanceType(), but it - * takes a gamepad identifier instead of the (unstable) device index. + * Get the type of an opened gamepad. * * \param gamepad the gamepad object to query. - * \returns the gamepad type. + * \returns the gamepad type, or SDL_GAMEPAD_TYPE_INVALID if it's not available. * * \since This function is available since SDL 3.0.0. + * + * \sa SDL_GetGamepadInstanceType */ extern DECLSPEC SDL_GamepadType SDLCALL SDL_GetGamepadType(SDL_Gamepad *gamepad); +/** + * Get the type of an opened gamepad, ignoring any mapping override. + * + * \param gamepad the gamepad object to query. + * \returns the gamepad type, or SDL_GAMEPAD_TYPE_INVALID if it's not available. + * + * \since This function is available since SDL 3.0.0. + * + * \sa SDL_GetRealGamepadInstanceType + */ +extern DECLSPEC SDL_GamepadType SDLCALL SDL_GetRealGamepadType(SDL_Gamepad *gamepad); + /** * Get the player index of an opened gamepad. * @@ -697,6 +716,39 @@ extern DECLSPEC SDL_bool SDLCALL SDL_GamepadEventsEnabled(void); */ extern DECLSPEC void SDLCALL SDL_UpdateGamepads(void); +/** + * Convert a string into SDL_GamepadType enum. + * + * This function is called internally to translate SDL_Gamepad mapping strings + * for the underlying joystick device into the consistent SDL_Gamepad mapping. + * You do not normally need to call this function unless you are parsing + * SDL_Gamepad mappings in your own code. + * + * \param str string representing a SDL_GamepadType type + * \returns the SDL_GamepadType enum corresponding to the input string, or + * `SDL_GAMEPAD_TYPE_INVALID` if no match was found. + * + * \since This function is available since SDL 3.0.0. + * + * \sa SDL_GetGamepadStringForType + */ +extern DECLSPEC SDL_GamepadType SDLCALL SDL_GetGamepadTypeFromString(const char *str); + +/** + * Convert from an SDL_GamepadType enum to a string. + * + * The caller should not SDL_free() the returned string. + * + * \param type an enum value for a given SDL_GamepadType + * \returns a string for the given type, or NULL if an invalid type is + * specified. The string returned is of the format used by + * SDL_Gamepad mapping strings. + * + * \since This function is available since SDL 3.0.0. + * + * \sa SDL_GetGamepadTypeFromString + */ +extern DECLSPEC const char *SDLCALL SDL_GetGamepadStringForType(SDL_GamepadType type); /** * Convert a string into SDL_GamepadAxis enum. diff --git a/include/SDL3/SDL_oldnames.h b/include/SDL3/SDL_oldnames.h index 3a502e8c6..494f5669f 100644 --- a/include/SDL3/SDL_oldnames.h +++ b/include/SDL3/SDL_oldnames.h @@ -184,17 +184,14 @@ #define SDL_CONTROLLER_BUTTON_TOUCHPAD SDL_GAMEPAD_BUTTON_TOUCHPAD #define SDL_CONTROLLER_BUTTON_X SDL_GAMEPAD_BUTTON_X #define SDL_CONTROLLER_BUTTON_Y SDL_GAMEPAD_BUTTON_Y -#define SDL_CONTROLLER_TYPE_AMAZON_LUNA SDL_GAMEPAD_TYPE_AMAZON_LUNA -#define SDL_CONTROLLER_TYPE_GOOGLE_STADIA SDL_GAMEPAD_TYPE_GOOGLE_STADIA #define SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_JOYCON_LEFT SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_JOYCON_LEFT #define SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_JOYCON_PAIR SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_JOYCON_PAIR #define SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_JOYCON_RIGHT SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_JOYCON_RIGHT #define SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_PRO SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_PRO -#define SDL_CONTROLLER_TYPE_NVIDIA_SHIELD SDL_GAMEPAD_TYPE_NVIDIA_SHIELD #define SDL_CONTROLLER_TYPE_PS3 SDL_GAMEPAD_TYPE_PS3 #define SDL_CONTROLLER_TYPE_PS4 SDL_GAMEPAD_TYPE_PS4 #define SDL_CONTROLLER_TYPE_PS5 SDL_GAMEPAD_TYPE_PS5 -#define SDL_CONTROLLER_TYPE_UNKNOWN SDL_GAMEPAD_TYPE_UNKNOWN +#define SDL_CONTROLLER_TYPE_UNKNOWN SDL_GAMEPAD_TYPE_STANDARD #define SDL_CONTROLLER_TYPE_VIRTUAL SDL_GAMEPAD_TYPE_VIRTUAL #define SDL_CONTROLLER_TYPE_XBOX360 SDL_GAMEPAD_TYPE_XBOX360 #define SDL_CONTROLLER_TYPE_XBOXONE SDL_GAMEPAD_TYPE_XBOXONE @@ -626,17 +623,14 @@ #define SDL_CONTROLLER_BUTTON_TOUCHPAD SDL_CONTROLLER_BUTTON_TOUCHPAD_renamed_SDL_GAMEPAD_BUTTON_TOUCHPAD #define SDL_CONTROLLER_BUTTON_X SDL_CONTROLLER_BUTTON_X_renamed_SDL_GAMEPAD_BUTTON_X #define SDL_CONTROLLER_BUTTON_Y SDL_CONTROLLER_BUTTON_Y_renamed_SDL_GAMEPAD_BUTTON_Y -#define SDL_CONTROLLER_TYPE_AMAZON_LUNA SDL_CONTROLLER_TYPE_AMAZON_LUNA_renamed_SDL_GAMEPAD_TYPE_AMAZON_LUNA -#define SDL_CONTROLLER_TYPE_GOOGLE_STADIA SDL_CONTROLLER_TYPE_GOOGLE_STADIA_renamed_SDL_GAMEPAD_TYPE_GOOGLE_STADIA #define SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_JOYCON_LEFT SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_JOYCON_LEFT_renamed_SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_JOYCON_LEFT #define SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_JOYCON_PAIR SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_JOYCON_PAIR_renamed_SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_JOYCON_PAIR #define SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_JOYCON_RIGHT SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_JOYCON_RIGHT_renamed_SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_JOYCON_RIGHT #define SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_PRO SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_PRO_renamed_SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_PRO -#define SDL_CONTROLLER_TYPE_NVIDIA_SHIELD SDL_CONTROLLER_TYPE_NVIDIA_SHIELD_renamed_SDL_GAMEPAD_TYPE_NVIDIA_SHIELD #define SDL_CONTROLLER_TYPE_PS3 SDL_CONTROLLER_TYPE_PS3_renamed_SDL_GAMEPAD_TYPE_PS3 #define SDL_CONTROLLER_TYPE_PS4 SDL_CONTROLLER_TYPE_PS4_renamed_SDL_GAMEPAD_TYPE_PS4 #define SDL_CONTROLLER_TYPE_PS5 SDL_CONTROLLER_TYPE_PS5_renamed_SDL_GAMEPAD_TYPE_PS5 -#define SDL_CONTROLLER_TYPE_UNKNOWN SDL_CONTROLLER_TYPE_UNKNOWN_renamed_SDL_GAMEPAD_TYPE_UNKNOWN +#define SDL_CONTROLLER_TYPE_UNKNOWN SDL_CONTROLLER_TYPE_UNKNOWN_renamed_SDL_GAMEPAD_TYPE_STANDARD #define SDL_CONTROLLER_TYPE_VIRTUAL SDL_CONTROLLER_TYPE_VIRTUAL_renamed_SDL_GAMEPAD_TYPE_VIRTUAL #define SDL_CONTROLLER_TYPE_XBOX360 SDL_CONTROLLER_TYPE_XBOX360_renamed_SDL_GAMEPAD_TYPE_XBOX360 #define SDL_CONTROLLER_TYPE_XBOXONE SDL_CONTROLLER_TYPE_XBOXONE_renamed_SDL_GAMEPAD_TYPE_XBOXONE diff --git a/src/dynapi/SDL_dynapi.sym b/src/dynapi/SDL_dynapi.sym index d919e7bb1..c30562e6a 100644 --- a/src/dynapi/SDL_dynapi.sym +++ b/src/dynapi/SDL_dynapi.sym @@ -871,6 +871,10 @@ SDL3_0.0.0 { SDL_GetGamepadPowerLevel; SDL_SetGamepadMapping; SDL_strndup; + SDL_GetGamepadTypeFromString; + SDL_GetGamepadStringForType; + SDL_GetRealGamepadInstanceType; + SDL_GetRealGamepadType; # extra symbols go here (don't modify this line) local: *; }; diff --git a/src/dynapi/SDL_dynapi_overrides.h b/src/dynapi/SDL_dynapi_overrides.h index 145111f84..601873d69 100644 --- a/src/dynapi/SDL_dynapi_overrides.h +++ b/src/dynapi/SDL_dynapi_overrides.h @@ -897,3 +897,7 @@ #define SDL_GetGamepadPowerLevel SDL_GetGamepadPowerLevel_REAL #define SDL_SetGamepadMapping SDL_SetGamepadMapping_REAL #define SDL_strndup SDL_strndup_REAL +#define SDL_GetGamepadTypeFromString SDL_GetGamepadTypeFromString_REAL +#define SDL_GetGamepadStringForType SDL_GetGamepadStringForType_REAL +#define SDL_GetRealGamepadInstanceType SDL_GetRealGamepadInstanceType_REAL +#define SDL_GetRealGamepadType SDL_GetRealGamepadType_REAL diff --git a/src/dynapi/SDL_dynapi_procs.h b/src/dynapi/SDL_dynapi_procs.h index d1fb03fdb..29cd07682 100644 --- a/src/dynapi/SDL_dynapi_procs.h +++ b/src/dynapi/SDL_dynapi_procs.h @@ -942,3 +942,7 @@ SDL_DYNAPI_PROC(SDL_JoystickID,SDL_GetGamepadInstanceID,(SDL_Gamepad *a),(a),ret SDL_DYNAPI_PROC(SDL_JoystickPowerLevel,SDL_GetGamepadPowerLevel,(SDL_Gamepad *a),(a),return) SDL_DYNAPI_PROC(int,SDL_SetGamepadMapping,(SDL_JoystickID a, const char *b),(a,b),return) SDL_DYNAPI_PROC(char*,SDL_strndup,(const char *a, size_t b),(a,b),return) +SDL_DYNAPI_PROC(SDL_GamepadType,SDL_GetGamepadTypeFromString,(const char *a),(a),return) +SDL_DYNAPI_PROC(const char*,SDL_GetGamepadStringForType,(SDL_GamepadType a),(a),return) +SDL_DYNAPI_PROC(SDL_GamepadType,SDL_GetRealGamepadInstanceType,(SDL_JoystickID a),(a),return) +SDL_DYNAPI_PROC(SDL_GamepadType,SDL_GetRealGamepadType,(SDL_Gamepad *a),(a),return) diff --git a/src/joystick/SDL_gamepad.c b/src/joystick/SDL_gamepad.c index cce3e713f..dc6d74c63 100644 --- a/src/joystick/SDL_gamepad.c +++ b/src/joystick/SDL_gamepad.c @@ -43,6 +43,8 @@ #define SDL_GAMEPAD_CRC_FIELD "crc:" #define SDL_GAMEPAD_CRC_FIELD_SIZE 4 /* hard-coded for speed */ +#define SDL_GAMEPAD_TYPE_FIELD "type:" +#define SDL_GAMEPAD_TYPE_FIELD_SIZE SDL_strlen(SDL_GAMEPAD_TYPE_FIELD) #define SDL_GAMEPAD_PLATFORM_FIELD "platform:" #define SDL_GAMEPAD_PLATFORM_FIELD_SIZE SDL_strlen(SDL_GAMEPAD_PLATFORM_FIELD) #define SDL_GAMEPAD_HINT_FIELD "hint:" @@ -864,22 +866,71 @@ static GamepadMapping_t *SDL_PrivateGetGamepadMappingForGUID(SDL_JoystickGUID gu return mapping; } +static const char *map_StringForGamepadType[] = { + "unknown", + "standard", + "xbox360", + "xboxone", + "ps3", + "ps4", + "ps5", + "switchpro", + "joyconleft", + "joyconright", + "joyconpair" +}; +SDL_COMPILE_TIME_ASSERT(map_StringForGamepadType, SDL_arraysize(map_StringForGamepadType) == SDL_GAMEPAD_TYPE_MAX); + +/* + * convert a string to its enum equivalent + */ +SDL_GamepadType SDL_GetGamepadTypeFromString(const char *str) +{ + int i; + + if (str == NULL || str[0] == '\0') { + return SDL_GAMEPAD_TYPE_UNKNOWN; + } + + if (*str == '+' || *str == '-') { + ++str; + } + + for (i = 0; i < SDL_arraysize(map_StringForGamepadType); ++i) { + if (SDL_strcasecmp(str, map_StringForGamepadType[i]) == 0) { + return (SDL_GamepadType)i; + } + } + return SDL_GAMEPAD_TYPE_UNKNOWN; +} + +/* + * convert an enum to its string equivalent + */ +const char *SDL_GetGamepadStringForType(SDL_GamepadType type) +{ + if (type >= SDL_GAMEPAD_TYPE_STANDARD && type < SDL_GAMEPAD_TYPE_MAX) { + return map_StringForGamepadType[type]; + } + return NULL; +} + static const char *map_StringForGamepadAxis[] = { "leftx", "lefty", "rightx", "righty", "lefttrigger", - "righttrigger", - NULL + "righttrigger" }; +SDL_COMPILE_TIME_ASSERT(map_StringForGamepadAxis, SDL_arraysize(map_StringForGamepadAxis) == SDL_GAMEPAD_AXIS_MAX); /* * convert a string to its enum equivalent */ SDL_GamepadAxis SDL_GetGamepadAxisFromString(const char *str) { - int entry; + int i; if (str == NULL || str[0] == '\0') { return SDL_GAMEPAD_AXIS_INVALID; @@ -889,9 +940,9 @@ SDL_GamepadAxis SDL_GetGamepadAxisFromString(const char *str) ++str; } - for (entry = 0; map_StringForGamepadAxis[entry]; ++entry) { - if (SDL_strcasecmp(str, map_StringForGamepadAxis[entry]) == 0) { - return (SDL_GamepadAxis)entry; + for (i = 0; i < SDL_arraysize(map_StringForGamepadAxis); ++i) { + if (SDL_strcasecmp(str, map_StringForGamepadAxis[i]) == 0) { + return (SDL_GamepadAxis)i; } } return SDL_GAMEPAD_AXIS_INVALID; @@ -929,23 +980,24 @@ static const char *map_StringForGamepadButton[] = { "paddle2", "paddle3", "paddle4", - "touchpad", - NULL + "touchpad" }; +SDL_COMPILE_TIME_ASSERT(map_StringForGamepadButton, SDL_arraysize(map_StringForGamepadButton) == SDL_GAMEPAD_BUTTON_MAX); /* * convert a string to its enum equivalent */ SDL_GamepadButton SDL_GetGamepadButtonFromString(const char *str) { - int entry; + int i; + if (str == NULL || str[0] == '\0') { return SDL_GAMEPAD_BUTTON_INVALID; } - for (entry = 0; map_StringForGamepadButton[entry]; ++entry) { - if (SDL_strcasecmp(str, map_StringForGamepadButton[entry]) == 0) { - return (SDL_GamepadButton)entry; + for (i = 0; i < SDL_arraysize(map_StringForGamepadButton); ++i) { + if (SDL_strcasecmp(str, map_StringForGamepadButton[i]) == 0) { + return (SDL_GamepadButton)i; } } return SDL_GAMEPAD_BUTTON_INVALID; @@ -2048,6 +2100,37 @@ Uint16 SDL_GetGamepadInstanceProductVersion(SDL_JoystickID instance_id) } SDL_GamepadType SDL_GetGamepadInstanceType(SDL_JoystickID instance_id) +{ + SDL_GamepadType type = SDL_GAMEPAD_TYPE_UNKNOWN; + + SDL_LockJoysticks(); + { + GamepadMapping_t *mapping = SDL_PrivateGetGamepadMapping(instance_id); + if (mapping != NULL) { + char *type_string, *comma; + + type_string = SDL_strstr(mapping->mapping, SDL_GAMEPAD_TYPE_FIELD); + if (type_string != NULL) { + type_string += SDL_GAMEPAD_TYPE_FIELD_SIZE; + comma = SDL_strchr(type_string, ','); + if (comma != NULL) { + *comma = '\0'; + type = SDL_GetGamepadTypeFromString(type_string); + *comma = ','; + } + } + + } + } + SDL_UnlockJoysticks(); + + if (type != SDL_GAMEPAD_TYPE_UNKNOWN) { + return type; + } + return SDL_GetRealGamepadInstanceType(instance_id); +} + +SDL_GamepadType SDL_GetRealGamepadInstanceType(SDL_JoystickID instance_id) { return SDL_GetGamepadTypeFromGUID(SDL_GetJoystickInstanceGUID(instance_id), SDL_GetJoystickInstanceName(instance_id)); } @@ -2753,6 +2836,21 @@ const char *SDL_GetGamepadPath(SDL_Gamepad *gamepad) } SDL_GamepadType SDL_GetGamepadType(SDL_Gamepad *gamepad) +{ + SDL_JoystickID instance_id = 0; + + SDL_LockJoysticks(); + { + CHECK_GAMEPAD_MAGIC(gamepad, SDL_GAMEPAD_TYPE_UNKNOWN); + + instance_id = gamepad->joystick->instance_id; + } + SDL_UnlockJoysticks(); + + return SDL_GetGamepadInstanceType(instance_id); +} + +SDL_GamepadType SDL_GetRealGamepadType(SDL_Gamepad *gamepad) { SDL_Joystick *joystick = SDL_GetGamepadJoystick(gamepad); diff --git a/src/joystick/SDL_joystick.c b/src/joystick/SDL_joystick.c index a4742b610..e2373fba9 100644 --- a/src/joystick/SDL_joystick.c +++ b/src/joystick/SDL_joystick.c @@ -2271,7 +2271,7 @@ void SDL_SetJoystickGUIDCRC(SDL_JoystickGUID *guid, Uint16 crc) SDL_GamepadType SDL_GetGamepadTypeFromVIDPID(Uint16 vendor, Uint16 product, const char *name, SDL_bool forUI) { - SDL_GamepadType type = SDL_GAMEPAD_TYPE_UNKNOWN; + SDL_GamepadType type = SDL_GAMEPAD_TYPE_STANDARD; if (vendor == 0x0000 && product == 0x0000) { /* Some devices are only identifiable by their name */ @@ -2284,7 +2284,7 @@ SDL_GamepadType SDL_GetGamepadTypeFromVIDPID(Uint16 vendor, Uint16 product, cons } } else if (vendor == 0x0001 && product == 0x0001) { - type = SDL_GAMEPAD_TYPE_UNKNOWN; + type = SDL_GAMEPAD_TYPE_STANDARD; } else if (vendor == USB_VENDOR_MICROSOFT && product == USB_PRODUCT_XBOX_ONE_XINPUT_CONTROLLER) { type = SDL_GAMEPAD_TYPE_XBOXONE; @@ -2295,7 +2295,7 @@ SDL_GamepadType SDL_GetGamepadTypeFromVIDPID(Uint16 vendor, Uint16 product, cons } else if (vendor == USB_VENDOR_NINTENDO && product == USB_PRODUCT_NINTENDO_SWITCH_JOYCON_RIGHT) { if (name && SDL_strstr(name, "NES Controller") != NULL) { /* We don't have a type for the Nintendo Online NES Controller */ - type = SDL_GAMEPAD_TYPE_UNKNOWN; + type = SDL_GAMEPAD_TYPE_STANDARD; } else { type = SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_JOYCON_RIGHT; } @@ -2331,7 +2331,7 @@ SDL_GamepadType SDL_GetGamepadTypeFromVIDPID(Uint16 vendor, Uint16 product, cons if (forUI) { type = SDL_GAMEPAD_TYPE_PS4; } else { - type = SDL_GAMEPAD_TYPE_UNKNOWN; + type = SDL_GAMEPAD_TYPE_STANDARD; } break; case k_eControllerType_SwitchProController: @@ -2342,7 +2342,7 @@ SDL_GamepadType SDL_GetGamepadTypeFromVIDPID(Uint16 vendor, Uint16 product, cons if (forUI) { type = SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_PRO; } else { - type = SDL_GAMEPAD_TYPE_UNKNOWN; + type = SDL_GAMEPAD_TYPE_STANDARD; } break; default: @@ -2359,7 +2359,7 @@ SDL_GamepadType SDL_GetGamepadTypeFromGUID(SDL_JoystickGUID guid, const char *na SDL_GetJoystickGUIDInfo(guid, &vendor, &product, NULL, NULL); type = SDL_GetGamepadTypeFromVIDPID(vendor, product, name, SDL_TRUE); - if (type == SDL_GAMEPAD_TYPE_UNKNOWN) { + if (type == SDL_GAMEPAD_TYPE_STANDARD) { if (SDL_IsJoystickXInput(guid)) { /* This is probably an Xbox One controller */ return SDL_GAMEPAD_TYPE_XBOXONE; diff --git a/src/joystick/hidapi/SDL_hidapi_switch.c b/src/joystick/hidapi/SDL_hidapi_switch.c index 349de5683..ff4f4ea9a 100644 --- a/src/joystick/hidapi/SDL_hidapi_switch.c +++ b/src/joystick/hidapi/SDL_hidapi_switch.c @@ -1219,40 +1219,40 @@ static void UpdateDeviceIdentity(SDL_HIDAPI_Device *device) break; case k_eSwitchDeviceInfoControllerType_HVCLeft: HIDAPI_SetDeviceName(device, "Nintendo HVC Controller (1)"); - device->type = SDL_GAMEPAD_TYPE_UNKNOWN; + device->type = SDL_GAMEPAD_TYPE_STANDARD; break; case k_eSwitchDeviceInfoControllerType_HVCRight: HIDAPI_SetDeviceName(device, "Nintendo HVC Controller (2)"); - device->type = SDL_GAMEPAD_TYPE_UNKNOWN; + device->type = SDL_GAMEPAD_TYPE_STANDARD; break; case k_eSwitchDeviceInfoControllerType_NESLeft: HIDAPI_SetDeviceName(device, "Nintendo NES Controller (L)"); - device->type = SDL_GAMEPAD_TYPE_UNKNOWN; + device->type = SDL_GAMEPAD_TYPE_STANDARD; break; case k_eSwitchDeviceInfoControllerType_NESRight: HIDAPI_SetDeviceName(device, "Nintendo NES Controller (R)"); - device->type = SDL_GAMEPAD_TYPE_UNKNOWN; + device->type = SDL_GAMEPAD_TYPE_STANDARD; break; case k_eSwitchDeviceInfoControllerType_SNES: HIDAPI_SetDeviceName(device, "Nintendo SNES Controller"); HIDAPI_SetDeviceProduct(device, USB_VENDOR_NINTENDO, USB_PRODUCT_NINTENDO_SNES_CONTROLLER); - device->type = SDL_GAMEPAD_TYPE_UNKNOWN; + device->type = SDL_GAMEPAD_TYPE_STANDARD; break; case k_eSwitchDeviceInfoControllerType_N64: HIDAPI_SetDeviceName(device, "Nintendo N64 Controller"); HIDAPI_SetDeviceProduct(device, USB_VENDOR_NINTENDO, USB_PRODUCT_NINTENDO_N64_CONTROLLER); - device->type = SDL_GAMEPAD_TYPE_UNKNOWN; + device->type = SDL_GAMEPAD_TYPE_STANDARD; break; case k_eSwitchDeviceInfoControllerType_SEGA_Genesis: HIDAPI_SetDeviceName(device, "Nintendo SEGA Genesis Controller"); HIDAPI_SetDeviceProduct(device, USB_VENDOR_NINTENDO, USB_PRODUCT_NINTENDO_SEGA_GENESIS_CONTROLLER); - device->type = SDL_GAMEPAD_TYPE_UNKNOWN; + device->type = SDL_GAMEPAD_TYPE_STANDARD; break; case k_eSwitchDeviceInfoControllerType_Unknown: /* We couldn't read the device info for this controller, might not be fully compliant */ return; default: - device->type = SDL_GAMEPAD_TYPE_UNKNOWN; + device->type = SDL_GAMEPAD_TYPE_STANDARD; break; } device->guid.data[15] = ctx->m_eControllerType; diff --git a/src/joystick/hidapi/SDL_hidapijoystick.c b/src/joystick/hidapi/SDL_hidapijoystick.c index ec4d78d9a..bf045ca5d 100644 --- a/src/joystick/hidapi/SDL_hidapijoystick.c +++ b/src/joystick/hidapi/SDL_hidapijoystick.c @@ -139,7 +139,7 @@ SDL_bool HIDAPI_SupportsPlaystationDetection(Uint16 vendor, Uint16 product) /* If we already know the controller is a different type, don't try to detect it. * This fixes a hang with the HORIPAD for Nintendo Switch (0x0f0d/0x00c1) */ - if (SDL_GetGamepadTypeFromVIDPID(vendor, product, NULL, SDL_FALSE) != SDL_GAMEPAD_TYPE_UNKNOWN) { + if (SDL_GetGamepadTypeFromVIDPID(vendor, product, NULL, SDL_FALSE) != SDL_GAMEPAD_TYPE_STANDARD) { return SDL_FALSE; } @@ -208,7 +208,7 @@ static SDL_GamepadType SDL_GetJoystickGameControllerProtocol(const char *name, U static const int XBONE_IFACE_SUBCLASS = 71; static const int XBONE_IFACE_PROTOCOL = 208; - SDL_GamepadType type = SDL_GAMEPAD_TYPE_UNKNOWN; + SDL_GamepadType type = SDL_GAMEPAD_TYPE_STANDARD; /* This code should match the checks in libusb/hid.c and HIDDeviceManager.java */ if (interface_class == LIBUSB_CLASS_VENDOR_SPEC && @@ -283,7 +283,7 @@ static SDL_GamepadType SDL_GetJoystickGameControllerProtocol(const char *name, U } } - if (type == SDL_GAMEPAD_TYPE_UNKNOWN) { + if (type == SDL_GAMEPAD_TYPE_STANDARD) { type = SDL_GetGamepadTypeFromVIDPID(vendor, product, name, SDL_FALSE); } return type; @@ -1284,7 +1284,7 @@ SDL_JoystickType HIDAPI_GetJoystickTypeFromGUID(SDL_JoystickGUID guid) SDL_GamepadType HIDAPI_GetGamepadTypeFromGUID(SDL_JoystickGUID guid) { SDL_HIDAPI_Device *device; - SDL_GamepadType type = SDL_GAMEPAD_TYPE_UNKNOWN; + SDL_GamepadType type = SDL_GAMEPAD_TYPE_STANDARD; SDL_LockJoysticks(); for (device = SDL_HIDAPI_devices; device; device = device->next) { diff --git a/src/joystick/sort_controllers.py b/src/joystick/sort_controllers.py index 084741ada..84701b9a0 100755 --- a/src/joystick/sort_controllers.py +++ b/src/joystick/sort_controllers.py @@ -71,6 +71,10 @@ def save_controller(line): print("Controller '%s' not unique, skipping" % name) return + pos = find_element("type", bindings) + if pos >= 0: + bindings.insert(0, bindings.pop(pos)) + pos = find_element("platform", bindings) if pos >= 0: bindings.insert(0, bindings.pop(pos)) diff --git a/test/gamepadutils.c b/test/gamepadutils.c index d3c2f1240..0661fe83e 100644 --- a/test/gamepadutils.c +++ b/test/gamepadutils.c @@ -1268,6 +1268,177 @@ void DestroyGamepadDisplay(GamepadDisplay *ctx) SDL_free(ctx); } +struct GamepadTypeDisplay +{ + SDL_Renderer *renderer; + + int type_highlighted; + SDL_bool type_pressed; + int type_selected; + SDL_GamepadType real_type; + + SDL_Rect area; +}; + +GamepadTypeDisplay *CreateGamepadTypeDisplay(SDL_Renderer *renderer) +{ + GamepadTypeDisplay *ctx = SDL_calloc(1, sizeof(*ctx)); + if (ctx) { + ctx->renderer = renderer; + + ctx->type_highlighted = SDL_GAMEPAD_TYPE_UNSELECTED; + ctx->type_selected = SDL_GAMEPAD_TYPE_UNSELECTED; + ctx->real_type = SDL_GAMEPAD_TYPE_UNKNOWN; + } + return ctx; +} + +void SetGamepadTypeDisplayArea(GamepadTypeDisplay *ctx, const SDL_Rect *area) +{ + if (!ctx) { + return; + } + + SDL_copyp(&ctx->area, area); +} + +void SetGamepadTypeDisplayHighlight(GamepadTypeDisplay *ctx, int type, SDL_bool pressed) +{ + if (!ctx) { + return; + } + + ctx->type_highlighted = type; + ctx->type_pressed = pressed; +} + +void SetGamepadTypeDisplaySelected(GamepadTypeDisplay *ctx, int type) +{ + if (!ctx) { + return; + } + + ctx->type_selected = type; +} + +void SetGamepadTypeDisplayRealType(GamepadTypeDisplay *ctx, SDL_GamepadType type) +{ + if (!ctx) { + return; + } + + ctx->real_type = type; +} + +int GetGamepadTypeDisplayAt(GamepadTypeDisplay *ctx, float x, float y) +{ + int i; + const float margin = 8.0f; + const float line_height = 16.0f; + SDL_FRect highlight; + SDL_FPoint point; + + if (!ctx) { + return SDL_GAMEPAD_TYPE_UNSELECTED; + } + + point.x = x; + point.y = y; + + x = ctx->area.x + margin; + y = ctx->area.y + margin; + + for (i = SDL_GAMEPAD_TYPE_UNKNOWN; i < SDL_GAMEPAD_TYPE_MAX; ++i) { + highlight.x = x; + highlight.y = y; + highlight.w = (float)ctx->area.w - (margin * 2); + highlight.h = (float)line_height; + + if (SDL_PointInRectFloat(&point, &highlight)) { + return i; + } + + y += line_height; + } + return SDL_GAMEPAD_TYPE_UNSELECTED; +} + +static void RenderGamepadTypeHighlight(GamepadTypeDisplay *ctx, int type, const SDL_FRect *area) +{ + if (type == ctx->type_highlighted || type == ctx->type_selected) { + Uint8 r, g, b, a; + + SDL_GetRenderDrawColor(ctx->renderer, &r, &g, &b, &a); + + if (type == ctx->type_highlighted) { + if (ctx->type_pressed) { + SDL_SetRenderDrawColor(ctx->renderer, PRESSED_COLOR); + } else { + SDL_SetRenderDrawColor(ctx->renderer, HIGHLIGHT_COLOR); + } + } else { + SDL_SetRenderDrawColor(ctx->renderer, SELECTED_COLOR); + } + SDL_RenderFillRect(ctx->renderer, area); + + SDL_SetRenderDrawColor(ctx->renderer, r, g, b, a); + } +} + +void RenderGamepadTypeDisplay(GamepadTypeDisplay *ctx) +{ + float x, y; + int i; + char text[128]; + const float margin = 8.0f; + const float line_height = 16.0f; + SDL_FPoint dst; + SDL_FRect highlight; + + if (!ctx) { + return; + } + + x = ctx->area.x + margin; + y = ctx->area.y + margin; + + for (i = SDL_GAMEPAD_TYPE_UNKNOWN; i < SDL_GAMEPAD_TYPE_MAX; ++i) { + highlight.x = x; + highlight.y = y; + highlight.w = (float)ctx->area.w - (margin * 2); + highlight.h = (float)line_height; + RenderGamepadTypeHighlight(ctx, i, &highlight); + + if (i == SDL_GAMEPAD_TYPE_UNKNOWN) { + if (ctx->real_type == SDL_GAMEPAD_TYPE_UNKNOWN || + ctx->real_type == SDL_GAMEPAD_TYPE_STANDARD) { + SDL_strlcpy(text, "Auto (Standard)", sizeof(text)); + } else { + SDL_snprintf(text, sizeof(text), "Auto (%s)", GetGamepadTypeString(ctx->real_type)); + } + } else if (i == SDL_GAMEPAD_TYPE_STANDARD) { + SDL_strlcpy(text, "Standard", sizeof(text)); + } else { + SDL_strlcpy(text, GetGamepadTypeString((SDL_GamepadType)i), sizeof(text)); + } + + dst.x = x + margin; + dst.y = y + line_height / 2 - FONT_CHARACTER_SIZE / 2; + SDLTest_DrawString(ctx->renderer, dst.x, dst.y, text); + + y += line_height; + } +} + +void DestroyGamepadTypeDisplay(GamepadTypeDisplay *ctx) +{ + if (!ctx) { + return; + } + + SDL_free(ctx); +} + struct JoystickDisplay { @@ -2185,13 +2356,14 @@ static char *JoinMapping(MappingParts *parts) } length += 1; - /* The sort order is: crc, platform, *, sdk, hint */ + /* The sort order is: crc, platform, type, *, sdk, hint */ sort_order = SDL_stack_alloc(MappingSortEntry, parts->num_elements); for (i = 0; i < parts->num_elements; ++i) { sort_order[i].parts = parts; sort_order[i].index = i; } SDL_qsort(sort_order, parts->num_elements, sizeof(*sort_order), SortMapping); + MoveSortedEntry("type", sort_order, parts->num_elements, SDL_TRUE); MoveSortedEntry("platform", sort_order, parts->num_elements, SDL_TRUE); MoveSortedEntry("crc", sort_order, parts->num_elements, SDL_TRUE); MoveSortedEntry("sdk>=", sort_order, parts->num_elements, SDL_FALSE); @@ -2334,7 +2506,7 @@ static char *SetMappingValue(char *mapping, const char *key, const char *value) return mapping; } -static char *RemoveMappingKey(char *mapping, const char *key) +static char *RemoveMappingValue(char *mapping, const char *key) { MappingParts parts; int i; @@ -2447,14 +2619,46 @@ char *SetMappingName(char *mapping, const char *name) return RecreateMapping(&parts, mapping); } -char *GetMappingType(const char *mapping) + +const char *GetGamepadTypeString(SDL_GamepadType type) { - return GetMappingValue(mapping, "type"); + switch (type) { + case SDL_GAMEPAD_TYPE_XBOX360: + return "Xbox 360"; + case SDL_GAMEPAD_TYPE_XBOXONE: + return "Xbox One"; + case SDL_GAMEPAD_TYPE_PS3: + return "PS3"; + case SDL_GAMEPAD_TYPE_PS4: + return "PS4"; + case SDL_GAMEPAD_TYPE_PS5: + return "PS5"; + case SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_PRO: + return "Nintendo Switch"; + case SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_JOYCON_LEFT: + return "Joy-Con (L)"; + case SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_JOYCON_RIGHT: + return "Joy-Con (R)"; + case SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_JOYCON_PAIR: + return "Joy-Con Pair"; + default: + return ""; + } } -char *SetMappingType(char *mapping, const char *type) +SDL_GamepadType GetMappingType(const char *mapping) { - return SetMappingValue(mapping, "type", type); + return SDL_GetGamepadTypeFromString(GetMappingValue(mapping, "type")); +} + +char *SetMappingType(char *mapping, SDL_GamepadType type) +{ + const char *type_string = SDL_GetGamepadStringForType(type); + if (type_string == NULL || type == SDL_GAMEPAD_TYPE_UNKNOWN) { + return RemoveMappingValue(mapping, "type"); + } else { + return SetMappingValue(mapping, "type", type_string); + } } static const char *GetElementKey(int element) @@ -2527,7 +2731,7 @@ char *SetElementBinding(char *mapping, int element, const char *binding) if (binding) { return SetMappingValue(mapping, GetElementKey(element), binding); } else { - return RemoveMappingKey(mapping, GetElementKey(element)); + return RemoveMappingValue(mapping, GetElementKey(element)); } } diff --git a/test/gamepadutils.h b/test/gamepadutils.h index 738b1a820..f0b62f9fa 100644 --- a/test/gamepadutils.h +++ b/test/gamepadutils.h @@ -92,6 +92,24 @@ extern void SetGamepadDisplaySelected(GamepadDisplay *ctx, int element); extern void RenderGamepadDisplay(GamepadDisplay *ctx, SDL_Gamepad *gamepad); extern void DestroyGamepadDisplay(GamepadDisplay *ctx); +/* Gamepad type display */ + +enum +{ + SDL_GAMEPAD_TYPE_UNSELECTED = -1 +}; + +typedef struct GamepadTypeDisplay GamepadTypeDisplay; + +extern GamepadTypeDisplay *CreateGamepadTypeDisplay(SDL_Renderer *renderer); +extern void SetGamepadTypeDisplayArea(GamepadTypeDisplay *ctx, const SDL_Rect *area); +extern int GetGamepadTypeDisplayAt(GamepadTypeDisplay *ctx, float x, float y); +extern void SetGamepadTypeDisplayHighlight(GamepadTypeDisplay *ctx, int type, SDL_bool pressed); +extern void SetGamepadTypeDisplaySelected(GamepadTypeDisplay *ctx, int type); +extern void SetGamepadTypeDisplayRealType(GamepadTypeDisplay *ctx, SDL_GamepadType type); +extern void RenderGamepadTypeDisplay(GamepadTypeDisplay *ctx); +extern void DestroyGamepadTypeDisplay(GamepadTypeDisplay *ctx); + /* Joystick element display */ typedef struct JoystickDisplay JoystickDisplay; @@ -131,11 +149,14 @@ extern char *GetMappingName(const char *mapping); /* Set the name in a mapping, freeing the mapping passed in and returning a new mapping */ extern char *SetMappingName(char *mapping, const char *name); +/* Get the friendly string for an SDL_GamepadType */ +extern const char *GetGamepadTypeString(SDL_GamepadType type); + /* Return the type from a mapping, which should be freed using SDL_free(), or NULL if there is no type specified */ -extern char *GetMappingType(const char *mapping); +extern SDL_GamepadType GetMappingType(const char *mapping); /* Set the type in a mapping, freeing the mapping passed in and returning a new mapping */ -extern char *SetMappingType(char *mapping, const char *type); +extern char *SetMappingType(char *mapping, SDL_GamepadType type); /* Return true if a mapping has this element bound */ extern SDL_bool MappingHasElement(const char *mapping, int element); diff --git a/test/testcontroller.c b/test/testcontroller.c index d8e67159e..6ea7c1660 100644 --- a/test/testcontroller.c +++ b/test/testcontroller.c @@ -68,6 +68,7 @@ static SDL_Renderer *screen = NULL; static ControllerDisplayMode display_mode = CONTROLLER_MODE_TESTING; static GamepadImage *image = NULL; static GamepadDisplay *gamepad_elements = NULL; +static GamepadTypeDisplay *gamepad_type = NULL; static JoystickDisplay *joystick_elements = NULL; static GamepadButton *setup_mapping_button = NULL; static GamepadButton *done_mapping_button = NULL; @@ -89,6 +90,9 @@ static Uint64 binding_advance_time = 0; static SDL_FRect title_area; static SDL_bool title_highlighted; static SDL_bool title_pressed; +static SDL_FRect type_area; +static SDL_bool type_highlighted; +static SDL_bool type_pressed; static char *controller_name; static SDL_Joystick *virtual_joystick = NULL; static SDL_GamepadAxis virtual_axis_active = SDL_GAMEPAD_AXIS_INVALID; @@ -210,8 +214,12 @@ static void ClearButtonHighlights(void) title_highlighted = SDL_FALSE; title_pressed = SDL_FALSE; + type_highlighted = SDL_FALSE; + type_pressed = SDL_FALSE; + ClearGamepadImage(image); SetGamepadDisplayHighlight(gamepad_elements, SDL_GAMEPAD_ELEMENT_INVALID, SDL_FALSE); + SetGamepadTypeDisplayHighlight(gamepad_type, SDL_GAMEPAD_TYPE_UNSELECTED, SDL_FALSE); SetGamepadButtonHighlight(setup_mapping_button, SDL_FALSE, SDL_FALSE); SetGamepadButtonHighlight(done_mapping_button, SDL_FALSE, SDL_FALSE); SetGamepadButtonHighlight(cancel_button, SDL_FALSE, SDL_FALSE); @@ -241,6 +249,14 @@ static void UpdateButtonHighlights(float x, float y, SDL_bool button_down) title_pressed = SDL_FALSE; } + if (SDL_PointInRectFloat(&point, &type_area)) { + type_highlighted = SDL_TRUE; + type_pressed = button_down; + } else { + type_highlighted = SDL_FALSE; + type_pressed = SDL_FALSE; + } + if (controller->joystick != virtual_joystick) { gamepad_highlight_element = GetGamepadImageElementAt(image, x, y); } @@ -249,6 +265,11 @@ static void UpdateButtonHighlights(float x, float y, SDL_bool button_down) } SetGamepadDisplayHighlight(gamepad_elements, gamepad_highlight_element, button_down); + if (binding_element == SDL_GAMEPAD_ELEMENT_TYPE) { + int gamepad_highlight_type = GetGamepadTypeDisplayAt(gamepad_type, x, y); + SetGamepadTypeDisplayHighlight(gamepad_type, gamepad_highlight_type, button_down); + } + joystick_highlight_element = GetJoystickDisplayElementAt(joystick_elements, controller->joystick, x, y); SetJoystickDisplayHighlight(joystick_elements, joystick_highlight_element, button_down); SDL_free(joystick_highlight_element); @@ -606,6 +627,19 @@ static void PasteControllerName(void) CommitControllerName(); } +static void CommitGamepadType(SDL_GamepadType type) +{ + char *mapping = NULL; + + if (controller->mapping) { + mapping = SDL_strdup(controller->mapping); + } else { + mapping = NULL; + } + mapping = SetMappingType(mapping, type); + SetAndFreeGamepadMapping(mapping); +} + static const char *GetBindingInstruction(void) { switch (binding_element) { @@ -1097,6 +1131,7 @@ static void DrawGamepadWaiting(SDL_Renderer *renderer) static void DrawGamepadInfo(SDL_Renderer *renderer) { + const char *type; const char *serial; char text[128]; float x, y; @@ -1116,9 +1151,24 @@ static void DrawGamepadInfo(SDL_Renderer *renderer) SDL_SetRenderDrawColor(renderer, r, g, b, a); } + if (type_highlighted) { + Uint8 r, g, b, a; + + SDL_GetRenderDrawColor(renderer, &r, &g, &b, &a); + + if (type_pressed) { + SDL_SetRenderDrawColor(renderer, PRESSED_COLOR); + } else { + SDL_SetRenderDrawColor(renderer, HIGHLIGHT_COLOR); + } + SDL_RenderFillRect(renderer, &type_area); + + SDL_SetRenderDrawColor(renderer, r, g, b, a); + } + if (controller_name && *controller_name) { - x = (float)SCREEN_WIDTH / 2 - (FONT_CHARACTER_SIZE * SDL_strlen(controller_name)) / 2; - y = (float)TITLE_HEIGHT / 2 - FONT_CHARACTER_SIZE / 2; + x = title_area.x + title_area.w / 2 - (FONT_CHARACTER_SIZE * SDL_strlen(controller_name)) / 2; + y = title_area.y + title_area.h / 2 - FONT_CHARACTER_SIZE / 2; SDLTest_DrawString(renderer, x, y, controller_name); } @@ -1129,6 +1179,11 @@ static void DrawGamepadInfo(SDL_Renderer *renderer) SDLTest_DrawString(renderer, x, y, text); } + type = GetGamepadTypeString(SDL_GetGamepadType(controller->gamepad)); + x = type_area.x + type_area.w / 2 - (FONT_CHARACTER_SIZE * SDL_strlen(type)) / 2; + y = type_area.y + type_area.h / 2 - FONT_CHARACTER_SIZE / 2; + SDLTest_DrawString(renderer, x, y, type); + if (display_mode == CONTROLLER_MODE_TESTING) { SDL_snprintf(text, SDL_arraysize(text), "VID: 0x%.4x PID: 0x%.4x", SDL_GetJoystickVendor(controller->joystick), @@ -1185,6 +1240,8 @@ static void DrawBindingTips(SDL_Renderer *renderer) if (binding_element == SDL_GAMEPAD_ELEMENT_NAME) { text = "(press RETURN to complete)"; + } else if (binding_element == SDL_GAMEPAD_ELEMENT_TYPE) { + text = "(press ESC to cancel)"; } else { bound_A = MappingHasElement(controller->mapping, SDL_GAMEPAD_BUTTON_A); bound_B = MappingHasElement(controller->mapping, SDL_GAMEPAD_BUTTON_B); @@ -1467,6 +1524,14 @@ static void loop(void *arg) PasteMapping(); } else if (title_pressed) { SetCurrentBindingElement(SDL_GAMEPAD_ELEMENT_NAME, SDL_FALSE); + } else if (type_pressed) { + SetCurrentBindingElement(SDL_GAMEPAD_ELEMENT_TYPE, SDL_FALSE); + } else if (binding_element == SDL_GAMEPAD_ELEMENT_TYPE) { + int type = GetGamepadTypeDisplayAt(gamepad_type, event.button.x, event.button.y); + if (type != SDL_GAMEPAD_TYPE_UNSELECTED) { + CommitGamepadType((SDL_GamepadType)type); + StopBinding(); + } } else { int gamepad_element = SDL_GAMEPAD_ELEMENT_INVALID; char *joystick_element; @@ -1599,7 +1664,12 @@ static void loop(void *arg) } RenderGamepadImage(image); - RenderGamepadDisplay(gamepad_elements, controller->gamepad); + if (binding_element == SDL_GAMEPAD_ELEMENT_TYPE) { + SetGamepadTypeDisplayRealType(gamepad_type, SDL_GetRealGamepadType(controller->gamepad)); + RenderGamepadTypeDisplay(gamepad_type); + } else { + RenderGamepadDisplay(gamepad_elements, controller->gamepad); + } RenderJoystickDisplay(joystick_elements, controller->joystick); if (display_mode == CONTROLLER_MODE_TESTING) { @@ -1741,6 +1811,11 @@ int main(int argc, char *argv[]) title_area.x = (float)PANEL_WIDTH + PANEL_SPACING; title_area.y = (float)TITLE_HEIGHT / 2 - title_area.h / 2; + type_area.w = (float)PANEL_WIDTH - 2 * BUTTON_MARGIN; + type_area.h = (float)FONT_CHARACTER_SIZE + 2 * BUTTON_MARGIN; + type_area.x = (float)BUTTON_MARGIN; + type_area.y = (float)TITLE_HEIGHT / 2 - type_area.h / 2; + image = CreateGamepadImage(screen); if (image == NULL) { SDL_DestroyRenderer(screen); @@ -1756,6 +1831,13 @@ int main(int argc, char *argv[]) area.h = GAMEPAD_HEIGHT; SetGamepadDisplayArea(gamepad_elements, &area); + gamepad_type = CreateGamepadTypeDisplay(screen); + area.x = 0; + area.y = TITLE_HEIGHT; + area.w = PANEL_WIDTH; + area.h = GAMEPAD_HEIGHT; + SetGamepadTypeDisplayArea(gamepad_type, &area); + joystick_elements = CreateJoystickDisplay(screen); area.x = PANEL_WIDTH + PANEL_SPACING + GAMEPAD_WIDTH + PANEL_SPACING; area.y = TITLE_HEIGHT; @@ -1829,6 +1911,7 @@ int main(int argc, char *argv[]) } DestroyGamepadImage(image); DestroyGamepadDisplay(gamepad_elements); + DestroyGamepadTypeDisplay(gamepad_type); DestroyJoystickDisplay(joystick_elements); DestroyGamepadButton(copy_button); SDL_DestroyRenderer(screen);