Skip to content

Commit 48dfc03

Browse files
committed
Added the gamepad hint SDL_GAMECONTROLLER_USE_GAMECUBE_LABELS
This is for internal use to signal that a mapping uses positional GameCube buttons. It's set so we know whether a mapping uses the older style labeled buttons or the newer style positional buttons. If a positional mapping is used with SDL2, then it will be ignored, since the hint is marked as defaulting true and the mapping conditional is that the hint is false.
1 parent 21a7bbb commit 48dfc03

File tree

4 files changed

+132
-70
lines changed

4 files changed

+132
-70
lines changed

src/joystick/SDL_gamepad.c

Lines changed: 113 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -714,7 +714,7 @@ static GamepadMapping_t *SDL_CreateMappingForHIDAPIGamepad(SDL_GUID guid)
714714
product == USB_PRODUCT_EVORETRO_GAMECUBE_ADAPTER2 ||
715715
product == USB_PRODUCT_EVORETRO_GAMECUBE_ADAPTER3))) {
716716
// GameCube driver has 12 buttons and 6 axes
717-
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));
717+
SDL_strlcat(mapping_string, "a:b0,b:b2,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:b1,y:b3,hint:!SDL_GAMECONTROLLER_USE_GAMECUBE_LABELS:=1,", sizeof(mapping_string));
718718
} else if (vendor == USB_VENDOR_NINTENDO &&
719719
(guid.data[15] == k_eSwitchDeviceInfoControllerType_HVCLeft ||
720720
guid.data[15] == k_eSwitchDeviceInfoControllerType_HVCRight ||
@@ -781,7 +781,11 @@ static GamepadMapping_t *SDL_CreateMappingForHIDAPIGamepad(SDL_GUID guid)
781781
}
782782
} else {
783783
// All other gamepads have the standard set of 19 buttons and 6 axes
784-
SDL_strlcat(mapping_string, "a:b0,b:b1,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,", sizeof(mapping_string));
784+
if (SDL_IsJoystickGameCube(vendor, product)) {
785+
SDL_strlcat(mapping_string, "a:b0,b:b2,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b1,y:b3,hint:!SDL_GAMECONTROLLER_USE_GAMECUBE_LABELS:=1,", sizeof(mapping_string));
786+
} else {
787+
SDL_strlcat(mapping_string, "a:b0,b:b1,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,", sizeof(mapping_string));
788+
}
785789

786790
if (SDL_IsJoystickSteamController(vendor, product)) {
787791
// Steam controllers have 2 back paddle buttons
@@ -1110,7 +1114,7 @@ SDL_COMPILE_TIME_ASSERT(map_StringForGamepadButton, SDL_arraysize(map_StringForG
11101114
/*
11111115
* convert a string to its enum equivalent
11121116
*/
1113-
static SDL_GamepadButton SDL_PrivateGetGamepadButtonFromString(const char *str, bool baxy)
1117+
static SDL_GamepadButton SDL_PrivateGetGamepadButtonFromString(const char *str, bool axby, bool baxy)
11141118
{
11151119
int i;
11161120

@@ -1120,7 +1124,17 @@ static SDL_GamepadButton SDL_PrivateGetGamepadButtonFromString(const char *str,
11201124

11211125
for (i = 0; i < SDL_arraysize(map_StringForGamepadButton); ++i) {
11221126
if (SDL_strcasecmp(str, map_StringForGamepadButton[i]) == 0) {
1123-
if (baxy) {
1127+
if (axby) {
1128+
// Need to swap face buttons
1129+
switch (i) {
1130+
case SDL_GAMEPAD_BUTTON_EAST:
1131+
return SDL_GAMEPAD_BUTTON_WEST;
1132+
case SDL_GAMEPAD_BUTTON_WEST:
1133+
return SDL_GAMEPAD_BUTTON_EAST;
1134+
default:
1135+
break;
1136+
}
1137+
} else if (baxy) {
11241138
// Need to swap face buttons
11251139
switch (i) {
11261140
case SDL_GAMEPAD_BUTTON_SOUTH:
@@ -1142,7 +1156,7 @@ static SDL_GamepadButton SDL_PrivateGetGamepadButtonFromString(const char *str,
11421156
}
11431157
SDL_GamepadButton SDL_GetGamepadButtonFromString(const char *str)
11441158
{
1145-
return SDL_PrivateGetGamepadButtonFromString(str, false);
1159+
return SDL_PrivateGetGamepadButtonFromString(str, false, false);
11461160
}
11471161

11481162
/*
@@ -1169,6 +1183,7 @@ static bool SDL_PrivateParseGamepadElement(SDL_Gamepad *gamepad, const char *szG
11691183
char half_axis_output = 0;
11701184
int i;
11711185
SDL_GamepadBinding *new_bindings;
1186+
bool axby_mapping = false;
11721187
bool baxy_mapping = false;
11731188

11741189
SDL_AssertJoysticksLocked();
@@ -1179,12 +1194,17 @@ static bool SDL_PrivateParseGamepadElement(SDL_Gamepad *gamepad, const char *szG
11791194
half_axis_output = *szGameButton++;
11801195
}
11811196

1197+
if (SDL_strstr(gamepad->mapping->mapping, ",hint:SDL_GAMECONTROLLER_USE_GAMECUBE_LABELS:=1") != NULL) {
1198+
axby_mapping = true;
1199+
}
11821200
if (SDL_strstr(gamepad->mapping->mapping, ",hint:SDL_GAMECONTROLLER_USE_BUTTON_LABELS:=1") != NULL) {
11831201
baxy_mapping = true;
11841202
}
1203+
// FIXME: We fix these up when loading the mapping, does this ever get hit?
1204+
//SDL_assert(!axby_mapping && !baxy_mapping);
11851205

11861206
axis = SDL_GetGamepadAxisFromString(szGameButton);
1187-
button = SDL_PrivateGetGamepadButtonFromString(szGameButton, baxy_mapping);
1207+
button = SDL_PrivateGetGamepadButtonFromString(szGameButton, axby_mapping, baxy_mapping);
11881208
if (axis != SDL_GAMEPAD_AXIS_INVALID) {
11891209
bind.output_type = SDL_GAMEPAD_BINDTYPE_AXIS;
11901210
bind.output.axis.axis = axis;
@@ -1401,6 +1421,11 @@ static void SDL_UpdateGamepadFaceStyle(SDL_Gamepad *gamepad)
14011421
}
14021422
}
14031423

1424+
if (gamepad->face_style == SDL_GAMEPAD_FACE_STYLE_UNKNOWN &&
1425+
SDL_strstr(gamepad->mapping->mapping, "SDL_GAMECONTROLLER_USE_GAMECUBE_LABELS") != NULL) {
1426+
// This controller uses GameCube button style
1427+
gamepad->face_style = SDL_GAMEPAD_FACE_STYLE_AXBY;
1428+
}
14041429
if (gamepad->face_style == SDL_GAMEPAD_FACE_STYLE_UNKNOWN &&
14051430
SDL_strstr(gamepad->mapping->mapping, "SDL_GAMECONTROLLER_USE_BUTTON_LABELS") != NULL) {
14061431
// This controller uses Nintendo button style
@@ -1466,24 +1491,6 @@ static void SDL_FixupHIDAPIMapping(SDL_Gamepad *gamepad)
14661491
}
14671492
}
14681493

1469-
static void SDL_FixupGameCubeMapping(SDL_Gamepad *gamepad, const char *pchString)
1470-
{
1471-
if (SDL_strstr(gamepad->mapping->mapping, SDL_GAMEPAD_FACE_FIELD) != NULL) {
1472-
return;
1473-
}
1474-
1475-
for (int i = 0; i < gamepad->num_bindings; ++i) {
1476-
SDL_GamepadBinding *binding = &gamepad->bindings[i];
1477-
if (binding->output_type == SDL_GAMEPAD_BINDTYPE_BUTTON) {
1478-
if (binding->output.button == SDL_GAMEPAD_BUTTON_EAST) {
1479-
binding->output.button = SDL_GAMEPAD_BUTTON_WEST;
1480-
} else if (binding->output.button == SDL_GAMEPAD_BUTTON_WEST) {
1481-
binding->output.button = SDL_GAMEPAD_BUTTON_EAST;
1482-
}
1483-
}
1484-
}
1485-
}
1486-
14871494
/*
14881495
* Make a new button mapping struct
14891496
*/
@@ -1509,10 +1516,6 @@ static void SDL_PrivateLoadButtonMapping(SDL_Gamepad *gamepad, GamepadMapping_t
15091516
SDL_FixupHIDAPIMapping(gamepad);
15101517
}
15111518

1512-
if (SDL_IsJoystickGameCube(SDL_GetGamepadVendor(gamepad), SDL_GetGamepadProduct(gamepad))) {
1513-
SDL_FixupGameCubeMapping(gamepad, pGamepadMapping->mapping);
1514-
}
1515-
15161519
// Set the zero point for triggers
15171520
for (i = 0; i < gamepad->num_bindings; ++i) {
15181521
SDL_GamepadBinding *binding = &gamepad->bindings[i];
@@ -1983,7 +1986,37 @@ bool SDL_ReloadGamepadMappings(void)
19831986
return true;
19841987
}
19851988

1986-
static char *SDL_ConvertMappingToPositional(const char *mapping)
1989+
static char *SDL_ConvertMappingToPositionalAXBY(const char *mapping)
1990+
{
1991+
// Add space for '!' and null terminator
1992+
size_t length = SDL_strlen(mapping) + 1 + 1;
1993+
char *remapped = (char *)SDL_malloc(length);
1994+
if (remapped) {
1995+
char *button_B;
1996+
char *button_X;
1997+
char *hint;
1998+
1999+
SDL_strlcpy(remapped, mapping, length);
2000+
button_B = SDL_strstr(remapped, ",b:");
2001+
button_X = SDL_strstr(remapped, ",x:");
2002+
hint = SDL_strstr(remapped, "hint:SDL_GAMECONTROLLER_USE_GAMECUBE_LABELS");
2003+
2004+
if (button_B) {
2005+
button_B[1] = 'x';
2006+
}
2007+
if (button_X) {
2008+
button_X[1] = 'b';
2009+
}
2010+
if (hint) {
2011+
hint += 5;
2012+
SDL_memmove(hint + 1, hint, SDL_strlen(hint) + 1);
2013+
*hint = '!';
2014+
}
2015+
}
2016+
return remapped;
2017+
}
2018+
2019+
static char *SDL_ConvertMappingToPositionalBAXY(const char *mapping)
19872020
{
19882021
// Add space for '!' and null terminator
19892022
size_t length = SDL_strlen(mapping) + 1 + 1;
@@ -1996,23 +2029,23 @@ static char *SDL_ConvertMappingToPositional(const char *mapping)
19962029
char *hint;
19972030

19982031
SDL_strlcpy(remapped, mapping, length);
1999-
button_A = SDL_strstr(remapped, "a:");
2000-
button_B = SDL_strstr(remapped, "b:");
2001-
button_X = SDL_strstr(remapped, "x:");
2002-
button_Y = SDL_strstr(remapped, "y:");
2032+
button_A = SDL_strstr(remapped, ",a:");
2033+
button_B = SDL_strstr(remapped, ",b:");
2034+
button_X = SDL_strstr(remapped, ",x:");
2035+
button_Y = SDL_strstr(remapped, ",y:");
20032036
hint = SDL_strstr(remapped, "hint:SDL_GAMECONTROLLER_USE_BUTTON_LABELS");
20042037

20052038
if (button_A) {
2006-
*button_A = 'b';
2039+
button_A[1] = 'b';
20072040
}
20082041
if (button_B) {
2009-
*button_B = 'a';
2042+
button_B[1] = 'a';
20102043
}
20112044
if (button_X) {
2012-
*button_X = 'y';
2045+
button_X[1] = 'y';
20132046
}
20142047
if (button_Y) {
2015-
*button_Y = 'x';
2048+
button_Y[1] = 'x';
20162049
}
20172050
if (hint) {
20182051
hint += 5;
@@ -2028,9 +2061,11 @@ static char *SDL_ConvertMappingToPositional(const char *mapping)
20282061
*/
20292062
static int SDL_PrivateAddGamepadMapping(const char *mappingString, SDL_GamepadMappingPriority priority)
20302063
{
2064+
char *appended = NULL;
20312065
char *remapped = NULL;
20322066
char *pchGUID;
2033-
SDL_GUID jGUID;
2067+
SDL_GUID guid;
2068+
Uint16 vendor, product;
20342069
bool is_default_mapping = false;
20352070
bool is_xinput_mapping = false;
20362071
bool existing = false;
@@ -2044,6 +2079,28 @@ static int SDL_PrivateAddGamepadMapping(const char *mappingString, SDL_GamepadMa
20442079
return -1;
20452080
}
20462081

2082+
pchGUID = SDL_PrivateGetGamepadGUIDFromMappingString(mappingString);
2083+
if (!pchGUID) {
2084+
SDL_SetError("Couldn't parse GUID from %s", mappingString);
2085+
return -1;
2086+
}
2087+
if (!SDL_strcasecmp(pchGUID, "default")) {
2088+
is_default_mapping = true;
2089+
} else if (!SDL_strcasecmp(pchGUID, "xinput")) {
2090+
is_xinput_mapping = true;
2091+
}
2092+
guid = SDL_StringToGUID(pchGUID);
2093+
SDL_free(pchGUID);
2094+
2095+
SDL_GetJoystickGUIDInfo(guid, &vendor, &product, NULL, NULL);
2096+
if (SDL_IsJoystickGameCube(vendor, product) &&
2097+
SDL_strstr(mappingString, "SDL_GAMECONTROLLER_USE_GAMECUBE_LABELS") == NULL) {
2098+
SDL_asprintf(&appended, "%shint:SDL_GAMECONTROLLER_USE_GAMECUBE_LABELS:=1,", mappingString);
2099+
if (appended) {
2100+
mappingString = appended;
2101+
}
2102+
}
2103+
20472104
{ // Extract and verify the hint field
20482105
const char *tmp;
20492106

@@ -2075,18 +2132,31 @@ static int SDL_PrivateAddGamepadMapping(const char *mappingString, SDL_GamepadMa
20752132
default_value = false;
20762133
}
20772134

2078-
if (SDL_strcmp(hint, "SDL_GAMECONTROLLER_USE_BUTTON_LABELS") == 0) {
2135+
if (SDL_strcmp(hint, "SDL_GAMECONTROLLER_USE_GAMECUBE_LABELS") == 0) {
2136+
// This hint is used to signal whether the mapping uses positional buttons or not
2137+
if (negate) {
2138+
// This mapping uses positional buttons, we can use it as-is
2139+
} else {
2140+
// This mapping uses labeled buttons, we need to swap them to positional
2141+
remapped = SDL_ConvertMappingToPositionalAXBY(mappingString);
2142+
if (!remapped) {
2143+
goto done;
2144+
}
2145+
mappingString = remapped;
2146+
}
2147+
} else if (SDL_strcmp(hint, "SDL_GAMECONTROLLER_USE_BUTTON_LABELS") == 0) {
20792148
// This hint is used to signal whether the mapping uses positional buttons or not
20802149
if (negate) {
20812150
// This mapping uses positional buttons, we can use it as-is
20822151
} else {
20832152
// This mapping uses labeled buttons, we need to swap them to positional
2084-
remapped = SDL_ConvertMappingToPositional(mappingString);
2153+
remapped = SDL_ConvertMappingToPositionalBAXY(mappingString);
20852154
if (!remapped) {
20862155
goto done;
20872156
}
20882157
mappingString = remapped;
20892158
}
2159+
20902160
} else {
20912161
value = SDL_GetHintBoolean(hint, default_value);
20922162
if (negate) {
@@ -2123,20 +2193,7 @@ static int SDL_PrivateAddGamepadMapping(const char *mappingString, SDL_GamepadMa
21232193
}
21242194
#endif
21252195

2126-
pchGUID = SDL_PrivateGetGamepadGUIDFromMappingString(mappingString);
2127-
if (!pchGUID) {
2128-
SDL_SetError("Couldn't parse GUID from %s", mappingString);
2129-
goto done;
2130-
}
2131-
if (!SDL_strcasecmp(pchGUID, "default")) {
2132-
is_default_mapping = true;
2133-
} else if (!SDL_strcasecmp(pchGUID, "xinput")) {
2134-
is_xinput_mapping = true;
2135-
}
2136-
jGUID = SDL_StringToGUID(pchGUID);
2137-
SDL_free(pchGUID);
2138-
2139-
pGamepadMapping = SDL_PrivateAddMappingForGUID(jGUID, mappingString, &existing, priority);
2196+
pGamepadMapping = SDL_PrivateAddMappingForGUID(guid, mappingString, &existing, priority);
21402197
if (!pGamepadMapping) {
21412198
goto done;
21422199
}
@@ -2152,9 +2209,8 @@ static int SDL_PrivateAddGamepadMapping(const char *mappingString, SDL_GamepadMa
21522209
result = 1;
21532210
}
21542211
done:
2155-
if (remapped) {
2156-
SDL_free(remapped);
2157-
}
2212+
SDL_free(appended);
2213+
SDL_free(remapped);
21582214
return result;
21592215
}
21602216

src/joystick/SDL_joystick.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -828,8 +828,6 @@ bool SDL_InitJoysticks(void)
828828

829829
SDL_joysticks_initialized = true;
830830

831-
SDL_InitGamepadMappings();
832-
833831
SDL_LoadVIDPIDList(&old_xboxone_controllers);
834832
SDL_LoadVIDPIDList(&arcadestick_devices);
835833
SDL_LoadVIDPIDList(&blacklist_devices);
@@ -840,6 +838,8 @@ bool SDL_InitJoysticks(void)
840838
SDL_LoadVIDPIDList(&wheel_devices);
841839
SDL_LoadVIDPIDList(&zero_centered_devices);
842840

841+
SDL_InitGamepadMappings();
842+
843843
// See if we should allow joystick events while in the background
844844
SDL_AddHintCallback(SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS,
845845
SDL_JoystickAllowBackgroundEventsChanged, NULL);

test/testautomation_joystick.c

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,23 @@ static int SDLCALL TestVirtualJoystick(void *arg)
132132
SDL_UpdateJoysticks();
133133
SDLTest_AssertCheck(SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_SOUTH) == false, "SDL_GetGamepadButton(SDL_GAMEPAD_BUTTON_SOUTH) == false");
134134

135+
/* Set an explicit mapping with legacy GameCube style buttons */
136+
SDL_SetGamepadMapping(SDL_GetJoystickID(joystick), "ff0013db5669727475616c2043007601,Virtual Nintendo GameCube,a:b0,b:b1,x:b2,y:b3,back:b4,guide:b5,start:b6,leftstick:b7,rightstick:b8,leftshoulder:b9,rightshoulder:b10,dpup:b11,dpdown:b12,dpleft:b13,dpright:b14,misc1:b15,paddle1:b16,paddle2:b17,paddle3:b18,paddle4:b19,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a4,righttrigger:a5,hint:SDL_GAMECONTROLLER_USE_GAMECUBE_LABELS:=1,");
137+
{
138+
const char *name = SDL_GetGamepadName(gamepad);
139+
SDLTest_AssertCheck(name && SDL_strcmp(name, "Virtual Nintendo GameCube") == 0, "SDL_GetGamepadName() -> \"%s\" (expected \"%s\")", name, "Virtual Nintendo GameCube");
140+
}
141+
SDLTest_AssertCheck(SDL_GetGamepadButtonLabel(gamepad, SDL_GAMEPAD_BUTTON_EAST) == SDL_GAMEPAD_BUTTON_LABEL_X, "SDL_GetGamepadButtonLabel(SDL_GAMEPAD_BUTTON_EAST) == SDL_GAMEPAD_BUTTON_LABEL_X");
142+
143+
/* Set the east button and verify that the gamepad responds, mapping "B" to the west button */
144+
SDLTest_AssertCheck(SDL_SetJoystickVirtualButton(joystick, SDL_GAMEPAD_BUTTON_EAST, true), "SDL_SetJoystickVirtualButton(SDL_GAMEPAD_BUTTON_EAST, true)");
145+
SDL_UpdateJoysticks();
146+
SDLTest_AssertCheck(SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_WEST) == true, "SDL_GetGamepadButton(SDL_GAMEPAD_BUTTON_WEST) == true");
147+
148+
SDLTest_AssertCheck(SDL_SetJoystickVirtualButton(joystick, SDL_GAMEPAD_BUTTON_EAST, false), "SDL_SetJoystickVirtualButton(SDL_GAMEPAD_BUTTON_EAST, false)");
149+
SDL_UpdateJoysticks();
150+
SDLTest_AssertCheck(SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_WEST) == false, "SDL_GetGamepadButton(SDL_GAMEPAD_BUTTON_WEST) == false");
151+
135152
/* Set an explicit mapping with legacy Nintendo style buttons */
136153
SDL_SetGamepadMapping(SDL_GetJoystickID(joystick), "ff0013db5669727475616c2043007601,Virtual Nintendo Gamepad,a:b1,b:b0,x:b3,y:b2,back:b4,guide:b5,start:b6,leftstick:b7,rightstick:b8,leftshoulder:b9,rightshoulder:b10,dpup:b11,dpdown:b12,dpleft:b13,dpright:b14,misc1:b15,paddle1:b16,paddle2:b17,paddle3:b18,paddle4:b19,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a4,righttrigger:a5,hint:SDL_GAMECONTROLLER_USE_BUTTON_LABELS:=1,");
137154
{

test/testcontroller.c

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -478,17 +478,6 @@ static void CommitBindingElement(const char *binding, bool force)
478478
mapping = NULL;
479479
}
480480

481-
if (mapping && SDL_GetGamepadType(controller->gamepad) == SDL_GAMEPAD_TYPE_GAMECUBE) {
482-
if (SDL_strstr(mapping, "face:") == NULL) {
483-
char *new_mapping = NULL;
484-
SDL_asprintf(&new_mapping, "%sface:axby,", mapping);
485-
if (new_mapping) {
486-
SDL_free(mapping);
487-
mapping = new_mapping;
488-
}
489-
}
490-
}
491-
492481
/* If the controller generates multiple events for a single element, pick the best one */
493482
if (!force && binding_advance_time) {
494483
char *current = GetElementBinding(mapping, binding_element);

0 commit comments

Comments
 (0)