From b271e92c6e55db4212f9216d32c72349c3d94458 Mon Sep 17 00:00:00 2001
From: Sam Lantinga <[EMAIL REDACTED]>
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 68152ecc7cc9..19b30414b836 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 8773d7d291bf..aaa6e2b97d2d 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 3a502e8c6570..494f5669f93d 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 d919e7bb1b39..c30562e6ae6d 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 145111f84c1f..601873d690ff 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 d1fb03fdb5d2..29cd07682a79 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 cce3e713fb07..dc6d74c6314d 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 a4742b610a33..e2373fba90bc 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 349de568343f..ff4f4ea9a307 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 ec4d78d9a9e1..bf045ca5d686 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 084741ada489..84701b9a0ff9 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 d3c2f12404c1..0661fe83e7aa 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
(Patch may be truncated, please check the link at the top of this post.)