SDL: Added ABXY button labels to the gamepad image

From ee34805053bd845976ea000a3c98c0ac8fdf2fe3 Mon Sep 17 00:00:00 2001
From: Sam Lantinga <[EMAIL REDACTED]>
Date: Tue, 11 Jul 2023 18:56:29 -0700
Subject: [PATCH] Added ABXY button labels to the gamepad image

---
 test/gamepadutils.c | 103 +++++++++++++++++++++++++++++++++++++++++---
 test/gamepadutils.h |   1 +
 2 files changed, 97 insertions(+), 7 deletions(-)

diff --git a/test/gamepadutils.c b/test/gamepadutils.c
index f399cbbc5ae7..bd3f88d51370 100644
--- a/test/gamepadutils.c
+++ b/test/gamepadutils.c
@@ -34,10 +34,10 @@ static const struct
     int x;
     int y;
 } button_positions[] = {
-    { 412, 192 }, /* SDL_GAMEPAD_BUTTON_A */
-    { 456, 157 }, /* SDL_GAMEPAD_BUTTON_B */
-    { 367, 157 }, /* SDL_GAMEPAD_BUTTON_X */
-    { 414, 126 }, /* SDL_GAMEPAD_BUTTON_Y */
+    { 413, 193 }, /* SDL_GAMEPAD_BUTTON_A */
+    { 456, 156 }, /* SDL_GAMEPAD_BUTTON_B */
+    { 372, 159 }, /* SDL_GAMEPAD_BUTTON_X */
+    { 416, 125 }, /* SDL_GAMEPAD_BUTTON_Y */
     { 199, 157 }, /* SDL_GAMEPAD_BUTTON_BACK */
     { 257, 153 }, /* SDL_GAMEPAD_BUTTON_GUIDE */
     { 314, 157 }, /* SDL_GAMEPAD_BUTTON_START */
@@ -106,6 +106,7 @@ struct GamepadImage
     int x;
     int y;
     SDL_bool showing_front;
+    SDL_bool reverse_diamond;
     SDL_bool showing_battery;
     SDL_bool showing_touchpad;
 
@@ -133,6 +134,29 @@ static SDL_Texture *CreateTexture(SDL_Renderer *renderer, unsigned char *data, u
     return texture;
 }
 
+static SDL_GamepadButton GetRemappedButton(SDL_bool reverse_diamond, SDL_GamepadButton button)
+{
+    if (reverse_diamond) {
+        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));
@@ -185,6 +209,15 @@ void SetGamepadImageShowingFront(GamepadImage *ctx, SDL_bool showing_front)
     ctx->showing_front = showing_front;
 }
 
+void SetGamepadImageReverseDiamond(GamepadImage *ctx, SDL_bool reverse_diamond)
+{
+    if (!ctx) {
+        return;
+    }
+
+    ctx->reverse_diamond = reverse_diamond;
+}
+
 void SetGamepadImageShowingBattery(GamepadImage *ctx, SDL_bool showing_battery)
 {
     if (!ctx) {
@@ -298,7 +331,7 @@ SDL_GamepadButton GetGamepadImageButtonAt(GamepadImage *ctx, float x, float y)
             rect.w = (float)ctx->button_width;
             rect.h = (float)ctx->button_height;
             if (SDL_PointInRectFloat(&point, &rect)) {
-                return (SDL_GamepadButton)i;
+                return GetRemappedButton(ctx->reverse_diamond, (SDL_GamepadButton)i);
             }
         }
     }
@@ -367,6 +400,23 @@ void UpdateGamepadImageFromGamepad(GamepadImage *ctx, SDL_Gamepad *gamepad)
         return;
     }
 
+    if (gamepad) {
+        char *mapping = SDL_GetGamepadMapping(gamepad);
+        if (mapping) {
+            SDL_GamepadType gamepad_type = SDL_GetGamepadType(gamepad);
+            if (gamepad_type == SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_PRO ||
+                gamepad_type == SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_JOYCON_LEFT ||
+                gamepad_type == SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_JOYCON_RIGHT ||
+                gamepad_type == SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_JOYCON_PAIR ||
+                SDL_strstr(mapping, "SDL_GAMECONTROLLER_USE_BUTTON_LABELS")) {
+                ctx->reverse_diamond = SDL_TRUE;
+            } else {
+                ctx->reverse_diamond = SDL_FALSE;
+            }
+            SDL_free(mapping);
+        }
+    }
+
     for (i = 0; i < SDL_GAMEPAD_BUTTON_TOUCHPAD; ++i) {
         const SDL_GamepadButton button = (SDL_GamepadButton)i;
 
@@ -421,11 +471,16 @@ void RenderGamepadImage(GamepadImage *ctx)
 {
     SDL_FRect dst;
     int i;
+    Uint8 r, g, b, a;
+    char label[32];
+    SDL_bool invert_color = SDL_FALSE;
 
     if (!ctx) {
         return;
     }
 
+    SDL_GetRenderDrawColor(ctx->renderer, &r, &g, &b, &a);
+
     dst.x = (float)ctx->x;
     dst.y = (float)ctx->y;
     dst.w = (float)ctx->gamepad_width;
@@ -438,6 +493,40 @@ void RenderGamepadImage(GamepadImage *ctx)
     }
 
     for (i = 0; i < SDL_arraysize(button_positions); ++i) {
+        SDL_GamepadButton button_position = GetRemappedButton(ctx->reverse_diamond, (SDL_GamepadButton)i);
+
+        switch (i) {
+        case SDL_GAMEPAD_BUTTON_A:
+            SDL_strlcpy(label, "A", sizeof(label));
+            break;
+        case SDL_GAMEPAD_BUTTON_B:
+            SDL_strlcpy(label, "B", sizeof(label));
+            break;
+        case SDL_GAMEPAD_BUTTON_X:
+            SDL_strlcpy(label, "X", sizeof(label));
+            break;
+        case SDL_GAMEPAD_BUTTON_Y:
+            SDL_strlcpy(label, "Y", sizeof(label));
+            break;
+        default:
+            *label = '\0';
+            break;
+        }
+        if (*label != '\0') {
+            dst.x = (float)ctx->x + button_positions[button_position].x - (float)(FONT_CHARACTER_SIZE * SDL_strlen(label)) / 2;
+            dst.y = (float)ctx->y + button_positions[button_position].y - (float)FONT_CHARACTER_SIZE / 2;
+            dst.w = (float)FONT_CHARACTER_SIZE;
+            dst.h = (float)FONT_CHARACTER_SIZE;
+
+            if (button_position == SDL_GAMEPAD_BUTTON_B || button_position == SDL_GAMEPAD_BUTTON_X) {
+                SDL_SetRenderDrawColor(ctx->renderer, ~r, ~g, ~b, a);
+                SDLTest_DrawString(ctx->renderer, dst.x, dst.y, label);
+                SDL_SetRenderDrawColor(ctx->renderer, r, g, b, a);
+            } else {
+                SDLTest_DrawString(ctx->renderer, dst.x, dst.y, label);
+            }
+        }
+
         if (ctx->buttons[i]) {
             SDL_bool on_front = SDL_TRUE;
 
@@ -445,8 +534,8 @@ void RenderGamepadImage(GamepadImage *ctx)
                 on_front = SDL_FALSE;
             }
             if (on_front == ctx->showing_front) {
-                dst.x = (float)ctx->x + button_positions[i].x - ctx->button_width / 2;
-                dst.y = (float)ctx->y + button_positions[i].y - ctx->button_height / 2;
+                dst.x = (float)ctx->x + button_positions[button_position].x - ctx->button_width / 2;
+                dst.y = (float)ctx->y + button_positions[button_position].y - ctx->button_height / 2;
                 dst.w = (float)ctx->button_width;
                 dst.h = (float)ctx->button_height;
                 SDL_RenderTexture(ctx->renderer, ctx->button_texture, NULL, &dst);
diff --git a/test/gamepadutils.h b/test/gamepadutils.h
index fbfc77cbf81f..6a9c7f480c2b 100644
--- a/test/gamepadutils.h
+++ b/test/gamepadutils.h
@@ -17,6 +17,7 @@ typedef struct GamepadImage GamepadImage;
 extern GamepadImage *CreateGamepadImage(SDL_Renderer *renderer);
 extern void SetGamepadImagePosition(GamepadImage *ctx, int x, int y);
 extern void SetGamepadImageShowingFront(GamepadImage *ctx, SDL_bool showing_front);
+extern void SetGamepadImageReverseDiamond(GamepadImage *ctx, SDL_bool reverse_diamond);
 extern void SetGamepadImageShowingBattery(GamepadImage *ctx, SDL_bool showing_battery);
 extern void SetGamepadImageShowingTouchpad(GamepadImage *ctx, SDL_bool showing_touchpad);
 extern void GetGamepadImageArea(GamepadImage *ctx, int *x, int *y, int *width, int *height);