SDL now represents gamepad buttons as positional elements with a separate label

This gives applications and binding systems a clearer view of what the hardware is so they can make intelligent decisions about how to present things to the user.

Gamepad mappings continue to use abxy for the face buttons for simplicity and compatibility with earlier versions of SDL, however the "SDL_GAMECONTROLLER_USE_BUTTON_LABELS" hint no longer has any effect.

Fixes https://github.com/libsdl-org/SDL/issues/6117
This commit is contained in:
Sam Lantinga
2023-11-06 13:07:12 -08:00
parent 8708ba7393
commit 2991b9f6ac
32 changed files with 852 additions and 611 deletions

View File

@@ -38,10 +38,10 @@ static const struct
int x;
int y;
} button_positions[] = {
{ 413, 190 }, /* SDL_GAMEPAD_BUTTON_A */
{ 456, 156 }, /* SDL_GAMEPAD_BUTTON_B */
{ 372, 159 }, /* SDL_GAMEPAD_BUTTON_X */
{ 415, 127 }, /* SDL_GAMEPAD_BUTTON_Y */
{ 413, 190 }, /* SDL_GAMEPAD_BUTTON_SOUTH */
{ 456, 156 }, /* SDL_GAMEPAD_BUTTON_EAST */
{ 372, 159 }, /* SDL_GAMEPAD_BUTTON_WEST */
{ 415, 127 }, /* SDL_GAMEPAD_BUTTON_NORTH */
{ 199, 157 }, /* SDL_GAMEPAD_BUTTON_BACK */
{ 257, 153 }, /* SDL_GAMEPAD_BUTTON_GUIDE */
{ 314, 157 }, /* SDL_GAMEPAD_BUTTON_START */
@@ -120,7 +120,7 @@ struct GamepadImage
int y;
SDL_bool showing_front;
SDL_bool showing_touchpad;
GamepadImageFaceStyle face_style;
SDL_GamepadType type;
ControllerDisplayMode display_mode;
SDL_bool elements[SDL_GAMEPAD_ELEMENT_MAX];
@@ -146,29 +146,6 @@ static SDL_Texture *CreateTexture(SDL_Renderer *renderer, unsigned char *data, u
return texture;
}
static SDL_GamepadButton GetRemappedButton(GamepadImageFaceStyle face_style, SDL_GamepadButton button)
{
if (face_style == GAMEPAD_IMAGE_FACE_BAYX) {
switch (button) {
case SDL_GAMEPAD_BUTTON_A:
button = SDL_GAMEPAD_BUTTON_B;
break;
case SDL_GAMEPAD_BUTTON_B:
button = SDL_GAMEPAD_BUTTON_A;
break;
case SDL_GAMEPAD_BUTTON_X:
button = SDL_GAMEPAD_BUTTON_Y;
break;
case SDL_GAMEPAD_BUTTON_Y:
button = SDL_GAMEPAD_BUTTON_X;
break;
default:
break;
}
}
return button;
}
GamepadImage *CreateGamepadImage(SDL_Renderer *renderer)
{
GamepadImage *ctx = SDL_calloc(1, sizeof(*ctx));
@@ -241,22 +218,22 @@ void SetGamepadImageShowingFront(GamepadImage *ctx, SDL_bool showing_front)
ctx->showing_front = showing_front;
}
void SetGamepadImageFaceStyle(GamepadImage *ctx, GamepadImageFaceStyle face_style)
void SetGamepadImageFaceButtonType(GamepadImage *ctx, SDL_GamepadType type)
{
if (!ctx) {
return;
}
ctx->face_style = face_style;
ctx->type = type;
}
GamepadImageFaceStyle GetGamepadImageFaceStyle(GamepadImage *ctx)
SDL_GamepadType GetGamepadImageType(GamepadImage *ctx)
{
if (!ctx) {
return GAMEPAD_IMAGE_FACE_BLANK;
return SDL_GAMEPAD_TYPE_UNKNOWN;
}
return ctx->face_style;
return ctx->type;
}
void SetGamepadImageDisplayMode(GamepadImage *ctx, ControllerDisplayMode display_mode)
@@ -407,7 +384,7 @@ int GetGamepadImageElementAt(GamepadImage *ctx, float x, float y)
rect.w = (float)ctx->button_width;
rect.h = (float)ctx->button_height;
if (SDL_PointInRectFloat(&point, &rect)) {
return GetRemappedButton(ctx->face_style, (SDL_GamepadButton)i);
return (SDL_GamepadButton)i;
}
}
}
@@ -440,29 +417,15 @@ void UpdateGamepadImageFromGamepad(GamepadImage *ctx, SDL_Gamepad *gamepad)
return;
}
ctx->type = SDL_GetGamepadType(gamepad);
char *mapping = SDL_GetGamepadMapping(gamepad);
SDL_GamepadType gamepad_type = SDL_GetGamepadType(gamepad);
switch (gamepad_type) {
case SDL_GAMEPAD_TYPE_PS3:
case SDL_GAMEPAD_TYPE_PS4:
case SDL_GAMEPAD_TYPE_PS5:
ctx->face_style = GAMEPAD_IMAGE_FACE_SONY;
break;
case SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_PRO:
case SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_JOYCON_LEFT:
case SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_JOYCON_RIGHT:
case SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_JOYCON_PAIR:
ctx->face_style = GAMEPAD_IMAGE_FACE_BAYX;
break;
default:
if (mapping && SDL_strstr(mapping, "SDL_GAMECONTROLLER_USE_BUTTON_LABELS")) {
ctx->face_style = GAMEPAD_IMAGE_FACE_BAYX;
} else {
ctx->face_style = GAMEPAD_IMAGE_FACE_ABXY;
if (mapping) {
if (SDL_strstr(mapping, "SDL_GAMECONTROLLER_USE_BUTTON_LABELS")) {
/* Just for display purposes */
ctx->type = SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_PRO;
}
break;
SDL_free(mapping);
}
SDL_free(mapping);
for (i = 0; i < SDL_GAMEPAD_BUTTON_TOUCHPAD; ++i) {
const SDL_GamepadButton button = (SDL_GamepadButton)i;
@@ -557,7 +520,7 @@ void RenderGamepadImage(GamepadImage *ctx)
for (i = 0; i < SDL_arraysize(button_positions); ++i) {
if (ctx->elements[i]) {
SDL_GamepadButton button_position = GetRemappedButton(ctx->face_style, (SDL_GamepadButton)i);
SDL_GamepadButton button_position = (SDL_GamepadButton)i;
SDL_bool on_front = SDL_TRUE;
if (i >= SDL_GAMEPAD_BUTTON_RIGHT_PADDLE1 && i <= SDL_GAMEPAD_BUTTON_LEFT_PADDLE2) {
@@ -579,14 +542,14 @@ void RenderGamepadImage(GamepadImage *ctx)
dst.w = (float)ctx->face_width;
dst.h = (float)ctx->face_height;
switch (ctx->face_style) {
case GAMEPAD_IMAGE_FACE_ABXY:
switch (SDL_GetGamepadButtonLabelForType(ctx->type, SDL_GAMEPAD_BUTTON_SOUTH)) {
case SDL_GAMEPAD_BUTTON_LABEL_A:
SDL_RenderTexture(ctx->renderer, ctx->face_abxy_texture, NULL, &dst);
break;
case GAMEPAD_IMAGE_FACE_BAYX:
case SDL_GAMEPAD_BUTTON_LABEL_B:
SDL_RenderTexture(ctx->renderer, ctx->face_bayx_texture, NULL, &dst);
break;
case GAMEPAD_IMAGE_FACE_SONY:
case SDL_GAMEPAD_BUTTON_LABEL_CROSS:
SDL_RenderTexture(ctx->renderer, ctx->face_sony_texture, NULL, &dst);
break;
default:
@@ -665,10 +628,10 @@ void DestroyGamepadImage(GamepadImage *ctx)
static const char *gamepad_button_names[] = {
"A",
"B",
"X",
"Y",
"South",
"East",
"West",
"North",
"Back",
"Guide",
"Start",
@@ -801,9 +764,45 @@ static SDL_bool GetBindingString(const char *label, char *mapping, char *text, s
static SDL_bool GetButtonBindingString(SDL_GamepadButton button, char *mapping, char *text, size_t size)
{
char label[32];
SDL_bool baxy_mapping = SDL_FALSE;
SDL_snprintf(label, sizeof(label), ",%s:", SDL_GetGamepadStringForButton(button));
return GetBindingString(label, mapping, text, size);
if (GetBindingString(label, mapping, text, size)) {
return SDL_TRUE;
}
/* Try the legacy button names */
if (SDL_strstr(mapping, ",hint:SDL_GAMECONTROLLER_USE_BUTTON_LABELS:=1") != NULL) {
baxy_mapping = SDL_TRUE;
}
switch (button) {
case SDL_GAMEPAD_BUTTON_SOUTH:
if (baxy_mapping) {
return GetBindingString(",b:", mapping, text, size);
} else {
return GetBindingString(",a:", mapping, text, size);
}
case SDL_GAMEPAD_BUTTON_EAST:
if (baxy_mapping) {
return GetBindingString(",a:", mapping, text, size);
} else {
return GetBindingString(",b:", mapping, text, size);
}
case SDL_GAMEPAD_BUTTON_WEST:
if (baxy_mapping) {
return GetBindingString(",y:", mapping, text, size);
} else {
return GetBindingString(",x:", mapping, text, size);
}
case SDL_GAMEPAD_BUTTON_NORTH:
if (baxy_mapping) {
return GetBindingString(",x:", mapping, text, size);
} else {
return GetBindingString(",y:", mapping, text, size);
}
default:
return SDL_FALSE;
}
}
static SDL_bool GetAxisBindingString(SDL_GamepadAxis axis, int direction, char *mapping, char *text, size_t size)
@@ -2224,9 +2223,11 @@ static int FindMappingKey(const MappingParts *parts, const char *key)
{
int i;
for (i = 0; i < parts->num_elements; ++i) {
if (SDL_strcmp(key, parts->keys[i]) == 0) {
return i;
if (key) {
for (i = 0; i < parts->num_elements; ++i) {
if (SDL_strcmp(key, parts->keys[i]) == 0) {
return i;
}
}
}
return -1;
@@ -2243,6 +2244,79 @@ static void RemoveMappingValueAt(MappingParts *parts, int index)
}
}
static void ConvertBAXYMapping(MappingParts *parts)
{
int i;
SDL_bool baxy_mapping = SDL_FALSE;
for (i = 0; i < parts->num_elements; ++i) {
const char *key = parts->keys[i];
const char *value = parts->values[i];
if (SDL_strcmp(key, "hint") == 0 &&
SDL_strcmp(value, "SDL_GAMECONTROLLER_USE_BUTTON_LABELS:=1") == 0) {
baxy_mapping = SDL_TRUE;
}
}
if (!baxy_mapping) {
return;
}
/* Swap buttons, invert hint */
for (i = 0; i < parts->num_elements; ++i) {
char *key = parts->keys[i];
char *value = parts->values[i];
if (SDL_strcmp(key, "a") == 0) {
parts->keys[i] = SDL_strdup("b");
SDL_free(key);
} else if (SDL_strcmp(key, "b") == 0) {
parts->keys[i] = SDL_strdup("a");
SDL_free(key);
} else if (SDL_strcmp(key, "x") == 0) {
parts->keys[i] = SDL_strdup("y");
SDL_free(key);
} else if (SDL_strcmp(key, "y") == 0) {
parts->keys[i] = SDL_strdup("x");
SDL_free(key);
} else if (SDL_strcmp(key, "hint") == 0 &&
SDL_strcmp(value, "SDL_GAMECONTROLLER_USE_BUTTON_LABELS:=1") == 0) {
parts->values[i] = SDL_strdup("!SDL_GAMECONTROLLER_USE_BUTTON_LABELS:=1");
SDL_free(value);
}
}
}
static void UseLegacyButtonNames(MappingParts *parts)
{
int i;
for (i = 0; i < parts->num_elements; ++i) {
char *key = parts->keys[i];
if (SDL_strcmp(key, "s") == 0) {
parts->keys[i] = SDL_strdup("a");
SDL_free(key);
} else if (SDL_strcmp(key, "e") == 0) {
parts->keys[i] = SDL_strdup("b");
SDL_free(key);
} else if (SDL_strcmp(key, "w") == 0) {
parts->keys[i] = SDL_strdup("x");
SDL_free(key);
} else if (SDL_strcmp(key, "n") == 0) {
parts->keys[i] = SDL_strdup("y");
SDL_free(key);
}
}
}
static void UpdateLegacyElements(MappingParts *parts)
{
ConvertBAXYMapping(parts);
UseLegacyButtonNames(parts);
}
static SDL_bool CombineMappingAxes(MappingParts *parts)
{
int i, matching, axis;
@@ -2337,6 +2411,7 @@ static char *JoinMapping(MappingParts *parts)
const char *name;
MappingSortEntry *sort_order;
UpdateLegacyElements(parts);
CombineMappingAxes(parts);
guid = parts->guid;
@@ -2420,6 +2495,43 @@ static char *RecreateMapping(MappingParts *parts, char *mapping)
return mapping;
}
static const char *GetLegacyKey(const char *key, SDL_bool baxy)
{
if (SDL_strcmp(key, SDL_GetGamepadStringForButton(SDL_GAMEPAD_BUTTON_SOUTH)) == 0) {
if (baxy) {
return "b";
} else {
return "a";
}
}
if (SDL_strcmp(key, SDL_GetGamepadStringForButton(SDL_GAMEPAD_BUTTON_EAST)) == 0) {
if (baxy) {
return "a";
} else {
return "b";
}
}
if (SDL_strcmp(key, SDL_GetGamepadStringForButton(SDL_GAMEPAD_BUTTON_WEST)) == 0) {
if (baxy) {
return "y";
} else {
return "x";
}
}
if (SDL_strcmp(key, SDL_GetGamepadStringForButton(SDL_GAMEPAD_BUTTON_NORTH)) == 0) {
if (baxy) {
return "y";
} else {
return "x";
}
}
return key;
}
static SDL_bool MappingHasKey(const char *mapping, const char *key)
{
int i;
@@ -2428,6 +2540,14 @@ static SDL_bool MappingHasKey(const char *mapping, const char *key)
SplitMapping(mapping, &parts);
i = FindMappingKey(&parts, key);
if (i < 0) {
SDL_bool baxy_mapping = SDL_FALSE;
if (SDL_strstr(mapping, ",hint:SDL_GAMECONTROLLER_USE_BUTTON_LABELS:=1") != NULL) {
baxy_mapping = SDL_TRUE;
}
i = FindMappingKey(&parts, GetLegacyKey(key, baxy_mapping));
}
if (i >= 0) {
result = SDL_TRUE;
}
@@ -2444,6 +2564,14 @@ static char *GetMappingValue(const char *mapping, const char *key)
SplitMapping(mapping, &parts);
i = FindMappingKey(&parts, key);
if (i < 0) {
SDL_bool baxy_mapping = SDL_FALSE;
if (SDL_strstr(mapping, ",hint:SDL_GAMECONTROLLER_USE_BUTTON_LABELS:=1") != NULL) {
baxy_mapping = SDL_TRUE;
}
i = FindMappingKey(&parts, GetLegacyKey(key, baxy_mapping));
}
if (i >= 0) {
value = parts.values[i];
parts.values[i] = NULL; /* So we don't free it */

View File

@@ -20,14 +20,6 @@ typedef enum
CONTROLLER_MODE_BINDING,
} ControllerDisplayMode;
typedef enum
{
GAMEPAD_IMAGE_FACE_BLANK,
GAMEPAD_IMAGE_FACE_ABXY,
GAMEPAD_IMAGE_FACE_BAYX,
GAMEPAD_IMAGE_FACE_SONY,
} GamepadImageFaceStyle;
enum
{
SDL_GAMEPAD_ELEMENT_INVALID = -1,
@@ -63,8 +55,8 @@ extern GamepadImage *CreateGamepadImage(SDL_Renderer *renderer);
extern void SetGamepadImagePosition(GamepadImage *ctx, int x, int y);
extern void GetGamepadImageArea(GamepadImage *ctx, SDL_Rect *area);
extern void SetGamepadImageShowingFront(GamepadImage *ctx, SDL_bool showing_front);
extern void SetGamepadImageFaceStyle(GamepadImage *ctx, GamepadImageFaceStyle face_style);
extern GamepadImageFaceStyle GetGamepadImageFaceStyle(GamepadImage *ctx);
extern void SetGamepadImageType(GamepadImage *ctx, SDL_GamepadType type);
extern SDL_GamepadType GetGamepadImageType(GamepadImage *ctx);
extern void SetGamepadImageDisplayMode(GamepadImage *ctx, ControllerDisplayMode display_mode);
extern int GetGamepadImageButtonWidth(GamepadImage *ctx);
extern int GetGamepadImageButtonHeight(GamepadImage *ctx);

View File

@@ -20,6 +20,7 @@ static int TestVirtualJoystick(void *arg)
{
SDL_VirtualJoystickDesc desc;
SDL_Joystick *joystick = NULL;
SDL_Gamepad *gamepad = NULL;
SDL_JoystickID device_id;
SDLTest_AssertCheck(SDL_InitSubSystem(SDL_INIT_GAMEPAD) == 0, "SDL_InitSubSystem(SDL_INIT_GAMEPAD)");
@@ -52,13 +53,65 @@ static int TestVirtualJoystick(void *arg)
SDLTest_AssertCheck(SDL_GetNumJoystickHats(joystick) == desc.nhats, "SDL_GetNumJoystickHats()");
SDLTest_AssertCheck(SDL_GetNumJoystickButtons(joystick) == desc.nbuttons, "SDL_GetNumJoystickButtons()");
SDLTest_AssertCheck(SDL_SetJoystickVirtualButton(joystick, SDL_GAMEPAD_BUTTON_A, SDL_PRESSED) == 0, "SDL_SetJoystickVirtualButton(SDL_GAMEPAD_BUTTON_A, SDL_PRESSED)");
SDLTest_AssertCheck(SDL_SetJoystickVirtualButton(joystick, SDL_GAMEPAD_BUTTON_SOUTH, SDL_PRESSED) == 0, "SDL_SetJoystickVirtualButton(SDL_GAMEPAD_BUTTON_SOUTH, SDL_PRESSED)");
SDL_UpdateJoysticks();
SDLTest_AssertCheck(SDL_GetJoystickButton(joystick, SDL_GAMEPAD_BUTTON_A) == SDL_PRESSED, "SDL_GetJoystickButton(SDL_GAMEPAD_BUTTON_A) == SDL_PRESSED");
SDLTest_AssertCheck(SDL_SetJoystickVirtualButton(joystick, SDL_GAMEPAD_BUTTON_A, SDL_RELEASED) == 0, "SDL_SetJoystickVirtualButton(SDL_GAMEPAD_BUTTON_A, SDL_RELEASED)");
SDLTest_AssertCheck(SDL_GetJoystickButton(joystick, SDL_GAMEPAD_BUTTON_SOUTH) == SDL_PRESSED, "SDL_GetJoystickButton(SDL_GAMEPAD_BUTTON_SOUTH) == SDL_PRESSED");
SDLTest_AssertCheck(SDL_SetJoystickVirtualButton(joystick, SDL_GAMEPAD_BUTTON_SOUTH, SDL_RELEASED) == 0, "SDL_SetJoystickVirtualButton(SDL_GAMEPAD_BUTTON_SOUTH, SDL_RELEASED)");
SDL_UpdateJoysticks();
SDLTest_AssertCheck(SDL_GetJoystickButton(joystick, SDL_GAMEPAD_BUTTON_A) == SDL_RELEASED, "SDL_GetJoystickButton(SDL_GAMEPAD_BUTTON_A) == SDL_RELEASED");
SDLTest_AssertCheck(SDL_GetJoystickButton(joystick, SDL_GAMEPAD_BUTTON_SOUTH) == SDL_RELEASED, "SDL_GetJoystickButton(SDL_GAMEPAD_BUTTON_SOUTH) == SDL_RELEASED");
gamepad = SDL_OpenGamepad(SDL_GetJoystickInstanceID(joystick));
SDLTest_AssertCheck(gamepad != NULL, "SDL_OpenGamepad() succeeded");
if (gamepad) {
SDLTest_AssertCheck(SDL_strcmp(SDL_GetGamepadName(gamepad), desc.name) == 0, "SDL_GetGamepadName()");
SDLTest_AssertCheck(SDL_GetGamepadVendor(gamepad) == desc.vendor_id, "SDL_GetGamepadVendor()");
SDLTest_AssertCheck(SDL_GetGamepadProduct(gamepad) == desc.product_id, "SDL_GetGamepadProduct()");
/* Set an explicit mapping with a different name */
SDL_SetGamepadMapping(SDL_GetJoystickInstanceID(joystick), "ff0013db5669727475616c2043007601,Virtual Gamepad,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,");
SDLTest_AssertCheck(SDL_strcmp(SDL_GetGamepadName(gamepad), "Virtual Gamepad") == 0, "SDL_GetGamepadName() == Virtual Gamepad");
SDLTest_AssertCheck(SDL_GetGamepadButtonLabel(gamepad, SDL_GAMEPAD_BUTTON_SOUTH) == SDL_GAMEPAD_BUTTON_LABEL_A, "SDL_GetGamepadButtonLabel(SDL_GAMEPAD_BUTTON_SOUTH) == SDL_GAMEPAD_BUTTON_LABEL_A");
/* Set the south button and verify that the gamepad responds */
SDLTest_AssertCheck(SDL_SetJoystickVirtualButton(joystick, SDL_GAMEPAD_BUTTON_SOUTH, SDL_PRESSED) == 0, "SDL_SetJoystickVirtualButton(SDL_GAMEPAD_BUTTON_SOUTH, SDL_PRESSED)");
SDL_UpdateJoysticks();
SDLTest_AssertCheck(SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_SOUTH) == SDL_PRESSED, "SDL_GetGamepadButton(SDL_GAMEPAD_BUTTON_SOUTH) == SDL_PRESSED");
SDLTest_AssertCheck(SDL_SetJoystickVirtualButton(joystick, SDL_GAMEPAD_BUTTON_SOUTH, SDL_RELEASED) == 0, "SDL_SetJoystickVirtualButton(SDL_GAMEPAD_BUTTON_SOUTH, SDL_RELEASED)");
SDL_UpdateJoysticks();
SDLTest_AssertCheck(SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_SOUTH) == SDL_RELEASED, "SDL_GetGamepadButton(SDL_GAMEPAD_BUTTON_SOUTH) == SDL_RELEASED");
/* Set an explicit mapping with legacy Nintendo style buttons */
SDL_SetGamepadMapping(SDL_GetJoystickInstanceID(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,");
SDLTest_AssertCheck(SDL_strcmp(SDL_GetGamepadName(gamepad), "Virtual Nintendo Gamepad") == 0, "SDL_GetGamepadName() == Virtual Nintendo Gamepad");
SDLTest_AssertCheck(SDL_GetGamepadButtonLabel(gamepad, SDL_GAMEPAD_BUTTON_SOUTH) == SDL_GAMEPAD_BUTTON_LABEL_B, "SDL_GetGamepadButtonLabel(SDL_GAMEPAD_BUTTON_SOUTH) == SDL_GAMEPAD_BUTTON_LABEL_B");
/* Set the south button and verify that the gamepad responds */
SDLTest_AssertCheck(SDL_SetJoystickVirtualButton(joystick, SDL_GAMEPAD_BUTTON_SOUTH, SDL_PRESSED) == 0, "SDL_SetJoystickVirtualButton(SDL_GAMEPAD_BUTTON_SOUTH, SDL_PRESSED)");
SDL_UpdateJoysticks();
SDLTest_AssertCheck(SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_SOUTH) == SDL_PRESSED, "SDL_GetGamepadButton(SDL_GAMEPAD_BUTTON_SOUTH) == SDL_PRESSED");
SDLTest_AssertCheck(SDL_SetJoystickVirtualButton(joystick, SDL_GAMEPAD_BUTTON_SOUTH, SDL_RELEASED) == 0, "SDL_SetJoystickVirtualButton(SDL_GAMEPAD_BUTTON_SOUTH, SDL_RELEASED)");
SDL_UpdateJoysticks();
SDLTest_AssertCheck(SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_SOUTH) == SDL_RELEASED, "SDL_GetGamepadButton(SDL_GAMEPAD_BUTTON_SOUTH) == SDL_RELEASED");
/* Set an explicit mapping with PS4 style buttons */
SDL_SetGamepadMapping(SDL_GetJoystickInstanceID(joystick), "ff0013db5669727475616c2043007601,Virtual PS4 Gamepad,type:ps4,cross:b0,circle:b1,square:b2,triangle: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,");
SDLTest_AssertCheck(SDL_strcmp(SDL_GetGamepadName(gamepad), "Virtual PS4 Gamepad") == 0, "SDL_GetGamepadName() == Virtual PS4 Gamepad");
SDLTest_AssertCheck(SDL_GetGamepadButtonLabel(gamepad, SDL_GAMEPAD_BUTTON_SOUTH) == SDL_GAMEPAD_BUTTON_LABEL_CROSS, "SDL_GetGamepadButtonLabel(SDL_GAMEPAD_BUTTON_SOUTH) == SDL_GAMEPAD_BUTTON_LABEL_CROSS");
/* Set the south button and verify that the gamepad responds */
SDLTest_AssertCheck(SDL_SetJoystickVirtualButton(joystick, SDL_GAMEPAD_BUTTON_SOUTH, SDL_PRESSED) == 0, "SDL_SetJoystickVirtualButton(SDL_GAMEPAD_BUTTON_SOUTH, SDL_PRESSED)");
SDL_UpdateJoysticks();
SDLTest_AssertCheck(SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_SOUTH) == SDL_PRESSED, "SDL_GetGamepadButton(SDL_GAMEPAD_BUTTON_SOUTH) == SDL_PRESSED");
SDLTest_AssertCheck(SDL_SetJoystickVirtualButton(joystick, SDL_GAMEPAD_BUTTON_SOUTH, SDL_RELEASED) == 0, "SDL_SetJoystickVirtualButton(SDL_GAMEPAD_BUTTON_SOUTH, SDL_RELEASED)");
SDL_UpdateJoysticks();
SDLTest_AssertCheck(SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_SOUTH) == SDL_RELEASED, "SDL_GetGamepadButton(SDL_GAMEPAD_BUTTON_SOUTH) == SDL_RELEASED");
SDL_CloseGamepad(gamepad);
}
SDL_CloseJoystick(joystick);
}

View File

@@ -102,10 +102,10 @@ static SDL_GamepadButton virtual_button_active = SDL_GAMEPAD_BUTTON_INVALID;
static int s_arrBindingOrder[] = {
/* Standard sequence */
SDL_GAMEPAD_BUTTON_A,
SDL_GAMEPAD_BUTTON_B,
SDL_GAMEPAD_BUTTON_X,
SDL_GAMEPAD_BUTTON_Y,
SDL_GAMEPAD_BUTTON_SOUTH,
SDL_GAMEPAD_BUTTON_EAST,
SDL_GAMEPAD_BUTTON_WEST,
SDL_GAMEPAD_BUTTON_NORTH,
SDL_GAMEPAD_BUTTON_DPAD_LEFT,
SDL_GAMEPAD_BUTTON_DPAD_RIGHT,
SDL_GAMEPAD_BUTTON_DPAD_UP,
@@ -336,7 +336,7 @@ static void SetCurrentBindingElement(int element, SDL_bool flow)
last_binding_element = binding_element;
}
binding_element = element;
binding_flow = flow || (element == SDL_GAMEPAD_BUTTON_A);
binding_flow = flow || (element == SDL_GAMEPAD_BUTTON_SOUTH);
binding_advance_time = 0;
for (i = 0; i < controller->num_axes; ++i) {
@@ -498,8 +498,10 @@ static void CommitBindingElement(const char *binding, SDL_bool force)
if (!ignore_binding && binding_flow && !force) {
int existing = GetElementForBinding(mapping, binding);
if (existing != SDL_GAMEPAD_ELEMENT_INVALID) {
if (existing == SDL_GAMEPAD_BUTTON_A) {
if (binding_element == SDL_GAMEPAD_BUTTON_A) {
SDL_GamepadButton action_forward = SDL_GAMEPAD_BUTTON_SOUTH;
SDL_GamepadButton action_backward = SDL_GAMEPAD_BUTTON_EAST;
if (existing == action_forward) {
if (binding_element == action_forward) {
/* Just move on to the next one */
ignore_binding = SDL_TRUE;
SetNextBindingElement();
@@ -509,9 +511,9 @@ static void CommitBindingElement(const char *binding, SDL_bool force)
direction = 1;
force = SDL_TRUE;
}
} else if (existing == SDL_GAMEPAD_BUTTON_B) {
if (binding_element != SDL_GAMEPAD_BUTTON_A &&
last_binding_element != SDL_GAMEPAD_BUTTON_A) {
} else if (existing == action_backward) {
if (binding_element != action_forward &&
last_binding_element != action_forward) {
/* Clear the current binding and move to the previous one */
binding = NULL;
direction = -1;
@@ -521,8 +523,8 @@ static void CommitBindingElement(const char *binding, SDL_bool force)
/* We're rebinding the same thing, just move to the next one */
ignore_binding = SDL_TRUE;
SetNextBindingElement();
} else if (binding_element != SDL_GAMEPAD_BUTTON_A &&
binding_element != SDL_GAMEPAD_BUTTON_B) {
} else if (binding_element != action_forward &&
binding_element != action_backward) {
ignore_binding = SDL_TRUE;
}
}
@@ -572,7 +574,7 @@ static void SetDisplayMode(ControllerDisplayMode mode)
if (MappingHasBindings(backup_mapping)) {
SetCurrentBindingElement(SDL_GAMEPAD_ELEMENT_INVALID, SDL_FALSE);
} else {
SetCurrentBindingElement(SDL_GAMEPAD_BUTTON_A, SDL_TRUE);
SetCurrentBindingElement(SDL_GAMEPAD_BUTTON_SOUTH, SDL_TRUE);
}
} else {
if (backup_mapping) {
@@ -701,30 +703,31 @@ static const char *GetBindingInstruction(void)
switch (binding_element) {
case SDL_GAMEPAD_ELEMENT_INVALID:
return "Select an element to bind from the list on the left";
case SDL_GAMEPAD_BUTTON_A:
if (GetGamepadImageFaceStyle(image) == GAMEPAD_IMAGE_FACE_SONY) {
return "Press the Cross (X) button";
} else {
case SDL_GAMEPAD_BUTTON_SOUTH:
case SDL_GAMEPAD_BUTTON_EAST:
case SDL_GAMEPAD_BUTTON_WEST:
case SDL_GAMEPAD_BUTTON_NORTH:
switch (SDL_GetGamepadButtonLabelForType(GetGamepadImageType(image), (SDL_GamepadButton)binding_element)) {
case SDL_GAMEPAD_BUTTON_LABEL_A:
return "Press the A button";
}
case SDL_GAMEPAD_BUTTON_B:
if (GetGamepadImageFaceStyle(image) == GAMEPAD_IMAGE_FACE_SONY) {
return "Press the Circle button";
} else {
case SDL_GAMEPAD_BUTTON_LABEL_B:
return "Press the B button";
}
case SDL_GAMEPAD_BUTTON_X:
if (GetGamepadImageFaceStyle(image) == GAMEPAD_IMAGE_FACE_SONY) {
return "Press the Square button";
} else {
case SDL_GAMEPAD_BUTTON_LABEL_X:
return "Press the X button";
}
case SDL_GAMEPAD_BUTTON_Y:
if (GetGamepadImageFaceStyle(image) == GAMEPAD_IMAGE_FACE_SONY) {
return "Press the Triangle button";
} else {
case SDL_GAMEPAD_BUTTON_LABEL_Y:
return "Press the Y button";
case SDL_GAMEPAD_BUTTON_LABEL_CROSS:
return "Press the Cross (X) button";
case SDL_GAMEPAD_BUTTON_LABEL_CIRCLE:
return "Press the Circle button";
case SDL_GAMEPAD_BUTTON_LABEL_SQUARE:
return "Press the Square button";
case SDL_GAMEPAD_BUTTON_LABEL_TRIANGLE:
return "Press the Triangle button";
default:
return "";
}
break;
case SDL_GAMEPAD_BUTTON_BACK:
return "Press the left center button (Back/View/Share)";
case SDL_GAMEPAD_BUTTON_GUIDE:
@@ -1298,6 +1301,30 @@ static void DrawGamepadInfo(SDL_Renderer *renderer)
}
}
static const char *GetButtonLabel(SDL_GamepadType type, SDL_GamepadButton button)
{
switch (SDL_GetGamepadButtonLabelForType(type, button)) {
case SDL_GAMEPAD_BUTTON_LABEL_A:
return "A";
case SDL_GAMEPAD_BUTTON_LABEL_B:
return "B";
case SDL_GAMEPAD_BUTTON_LABEL_X:
return "X";
case SDL_GAMEPAD_BUTTON_LABEL_Y:
return "Y";
case SDL_GAMEPAD_BUTTON_LABEL_CROSS:
return "Cross (X)";
case SDL_GAMEPAD_BUTTON_LABEL_CIRCLE:
return "Circle";
case SDL_GAMEPAD_BUTTON_LABEL_SQUARE:
return "Square";
case SDL_GAMEPAD_BUTTON_LABEL_TRIANGLE:
return "Triangle";
default:
return "UNKNOWN";
}
}
static void DrawBindingTips(SDL_Renderer *renderer)
{
const char *text;
@@ -1317,7 +1344,6 @@ static void DrawBindingTips(SDL_Renderer *renderer)
} else {
Uint8 r, g, b, a;
SDL_FRect rect;
SDL_bool bound_A, bound_B;
y -= (FONT_CHARACTER_SIZE + BUTTON_MARGIN) / 2;
@@ -1339,10 +1365,15 @@ static void DrawBindingTips(SDL_Renderer *renderer)
} 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);
if (binding_flow && bound_A && bound_B) {
text = "(press A to skip, B to go back, and ESC to cancel)";
SDL_GamepadType type = GetGamepadImageType(image);
SDL_GamepadButton action_forward = SDL_GAMEPAD_BUTTON_SOUTH;
SDL_bool bound_forward = MappingHasElement(controller->mapping, action_forward);
SDL_GamepadButton action_backward = SDL_GAMEPAD_BUTTON_EAST;
SDL_bool bound_backward = MappingHasElement(controller->mapping, action_backward);
if (binding_flow && bound_forward && bound_backward) {
static char dynamic_text[128];
SDL_snprintf(dynamic_text, sizeof(dynamic_text), "(press %s to skip, %s to go back, and ESC to cancel)", GetButtonLabel(type, action_forward), GetButtonLabel(type, action_backward));
text = dynamic_text;
} else {
text = "(press SPACE to clear binding and ESC to cancel)";
}