Przeglądaj źródła

Added a button to copy the gamepad mapping to the clipboard

Sam Lantinga 1 rok temu
rodzic
commit
4feb2f4b1a
3 zmienionych plików z 262 dodań i 0 usunięć
  1. 204 0
      test/gamepadutils.c
  2. 12 0
      test/gamepadutils.h
  3. 46 0
      test/testgamepad.c

+ 204 - 0
test/gamepadutils.c

@@ -26,6 +26,7 @@
 #include "gamepad_button_small.h"
 #include "gamepad_axis.h"
 #include "gamepad_axis_arrow.h"
+#include "gamepad_button_background.h"
 
 /* This is indexed by SDL_GamepadButton. */
 static const struct
@@ -612,6 +613,10 @@ void RenderGamepadDisplay(GamepadDisplay *ctx, SDL_Gamepad *gamepad)
     SDL_bool has_accel;
     SDL_bool has_gyro;
 
+    if (!ctx) {
+        return;
+    }
+
     SDL_GetRenderDrawColor(ctx->renderer, &r, &g, &b, &a);
 
     x = ctx->area.x + margin;
@@ -776,6 +781,12 @@ void RenderGamepadDisplay(GamepadDisplay *ctx, SDL_Gamepad *gamepad)
 
 void DestroyGamepadDisplay(GamepadDisplay *ctx)
 {
+    if (!ctx) {
+        return;
+    }
+
+    SDL_DestroyTexture(ctx->button_texture);
+    SDL_DestroyTexture(ctx->arrow_texture);
     SDL_free(ctx);
 }
 
@@ -834,6 +845,10 @@ void RenderJoystickDisplay(JoystickDisplay *ctx, SDL_Joystick *joystick)
     SDL_FRect dst, rect;
     Uint8 r, g, b, a;
 
+    if (!ctx) {
+        return;
+    }
+
     SDL_GetRenderDrawColor(ctx->renderer, &r, &g, &b, &a);
 
     x = (float)ctx->area.x + margin;
@@ -993,6 +1008,195 @@ void RenderJoystickDisplay(JoystickDisplay *ctx, SDL_Joystick *joystick)
 
 void DestroyJoystickDisplay(JoystickDisplay *ctx)
 {
+    if (!ctx) {
+        return;
+    }
+
+    SDL_DestroyTexture(ctx->button_texture);
+    SDL_DestroyTexture(ctx->arrow_texture);
     SDL_free(ctx);
 }
 
+
+struct GamepadButton
+{
+    SDL_Renderer *renderer;
+    SDL_Texture *background;
+    int background_width;
+    int background_height;
+
+    SDL_FRect area;
+
+    char *label;
+    int label_width;
+    int label_height;
+
+    SDL_bool highlight;
+};
+
+GamepadButton *CreateGamepadButton(SDL_Renderer *renderer, const char *label)
+{
+    GamepadButton *ctx = SDL_calloc(1, sizeof(*ctx));
+    if (ctx) {
+        ctx->renderer = renderer;
+
+        ctx->background = CreateTexture(renderer, gamepad_button_background_bmp, gamepad_button_background_bmp_len);
+        SDL_QueryTexture(ctx->background, NULL, NULL, &ctx->background_width, &ctx->background_height);
+
+        ctx->label = SDL_strdup(label);
+        ctx->label_width = (int)(FONT_CHARACTER_SIZE * SDL_strlen(label));
+        ctx->label_height = FONT_CHARACTER_SIZE;
+    }
+    return ctx;
+}
+
+void SetGamepadButtonArea(GamepadButton *ctx, int x, int y, int w, int h)
+{
+    if (!ctx) {
+        return;
+    }
+
+    ctx->area.x = (float)x;
+    ctx->area.y = (float)y;
+    ctx->area.w = (float)w;
+    ctx->area.h = (float)h;
+}
+
+void SetGamepadButtonHighlight(GamepadButton *ctx, SDL_bool highlight)
+{
+    if (!ctx) {
+        return;
+    }
+
+    ctx->highlight = highlight;
+}
+
+int GetGamepadButtonLabelWidth(GamepadButton *ctx)
+{
+    if (!ctx) {
+        return 0;
+    }
+
+    return ctx->label_width;
+}
+
+int GetGamepadButtonLabelHeight(GamepadButton *ctx)
+{
+    if (!ctx) {
+        return 0;
+    }
+
+    return ctx->label_height;
+}
+
+SDL_bool GamepadButtonContains(GamepadButton *ctx, float x, float y)
+{
+    SDL_FPoint point;
+
+    if (!ctx) {
+        return SDL_FALSE;
+    }
+
+    point.x = x;
+    point.y = y;
+    return SDL_PointInRectFloat(&point, &ctx->area);
+}
+
+void RenderGamepadButton(GamepadButton *ctx)
+{
+    SDL_FRect src, dst;
+    float one_third_src_width;
+    float one_third_src_height;
+
+    if (!ctx) {
+        return;
+    }
+
+    one_third_src_width = (float)ctx->background_width / 3;
+    one_third_src_height = (float)ctx->background_height / 3;
+
+    if (ctx->highlight) {
+        SDL_SetTextureColorMod(ctx->background, 10, 255, 21);
+    } else {
+        SDL_SetTextureColorMod(ctx->background, 255, 255, 255);
+    }
+
+    /* Top left */
+    src.x = 0.0f;
+    src.y = 0.0f;
+    src.w = one_third_src_width;
+    src.h = one_third_src_height;
+    dst.x = ctx->area.x;
+    dst.y = ctx->area.y;
+    dst.w = src.w;
+    dst.h = src.h;
+    SDL_RenderTexture(ctx->renderer, ctx->background, &src, &dst);
+
+    /* Bottom left */
+    src.y = (float)ctx->background_height - src.h;
+    dst.y = ctx->area.y + ctx->area.h - dst.h;
+    SDL_RenderTexture(ctx->renderer, ctx->background, &src, &dst);
+
+    /* Bottom right */
+    src.x = (float)ctx->background_width - src.w;
+    dst.x = ctx->area.x + ctx->area.w - dst.w;
+    SDL_RenderTexture(ctx->renderer, ctx->background, &src, &dst);
+
+    /* Top right */
+    src.y = 0.0f;
+    dst.y = ctx->area.y;
+    SDL_RenderTexture(ctx->renderer, ctx->background, &src, &dst);
+
+    /* Left */
+    src.x = 0.0f;
+    src.y = one_third_src_height;
+    dst.x = ctx->area.x;
+    dst.y = ctx->area.y + one_third_src_height;
+    dst.w = one_third_src_width;
+    dst.h = (float)ctx->area.h - 2 * one_third_src_height;
+    SDL_RenderTexture(ctx->renderer, ctx->background, &src, &dst);
+
+    /* Right */
+    src.x = (float)ctx->background_width - one_third_src_width;
+    dst.x = ctx->area.x + ctx->area.w - one_third_src_width;
+    SDL_RenderTexture(ctx->renderer, ctx->background, &src, &dst);
+
+    /* Top */
+    src.x = one_third_src_width;
+    src.y = 0.0f;
+    dst.x = ctx->area.x + one_third_src_width;
+    dst.y = ctx->area.y;
+    dst.w = ctx->area.w - 2 * one_third_src_width;
+    dst.h = one_third_src_height;
+    SDL_RenderTexture(ctx->renderer, ctx->background, &src, &dst);
+
+    /* Bottom */
+    src.y = (float)ctx->background_height - src.h;
+    dst.y = ctx->area.y + ctx->area.h - one_third_src_height;
+    SDL_RenderTexture(ctx->renderer, ctx->background, &src, &dst);
+
+    /* Center */
+    src.x = one_third_src_width;
+    src.y = one_third_src_height;
+    dst.x = ctx->area.x + one_third_src_width;
+    dst.y = ctx->area.y + one_third_src_height;
+    dst.w = ctx->area.w - 2 * one_third_src_width;
+    dst.h = (float)ctx->area.h - 2 * one_third_src_height;
+    SDL_RenderTexture(ctx->renderer, ctx->background, &src, &dst);
+
+    /* Label */
+    dst.x = ctx->area.x + ctx->area.w / 2 - ctx->label_width / 2;
+    dst.y = ctx->area.y + ctx->area.h / 2 - ctx->label_height / 2;
+    SDLTest_DrawString(ctx->renderer, dst.x, dst.y, ctx->label);
+}
+
+void DestroyGamepadButton(GamepadButton *ctx)
+{
+    if (!ctx) {
+        return;
+    }
+
+    SDL_DestroyTexture(ctx->background);
+    SDL_free(ctx->label);
+    SDL_free(ctx);
+}

+ 12 - 0
test/gamepadutils.h

@@ -54,3 +54,15 @@ extern void SetJoystickDisplayArea(JoystickDisplay *ctx, int x, int y, int w, in
 extern void RenderJoystickDisplay(JoystickDisplay *ctx, SDL_Joystick *joystick);
 extern void DestroyJoystickDisplay(JoystickDisplay *ctx);
 
+/* Simple buttons */
+
+typedef struct GamepadButton GamepadButton;
+
+extern GamepadButton *CreateGamepadButton(SDL_Renderer *renderer, const char *label);
+extern void SetGamepadButtonArea(GamepadButton *ctx, int x, int y, int w, int h);
+extern void SetGamepadButtonHighlight(GamepadButton *ctx, SDL_bool highlight);
+extern int GetGamepadButtonLabelWidth(GamepadButton *ctx);
+extern int GetGamepadButtonLabelHeight(GamepadButton *ctx);
+extern SDL_bool GamepadButtonContains(GamepadButton *ctx, float x, float y);
+extern void RenderGamepadButton(GamepadButton *ctx);
+extern void DestroyGamepadButton(GamepadButton *ctx);

+ 46 - 0
test/testgamepad.c

@@ -27,6 +27,8 @@
 #define TITLE_HEIGHT 48
 #define PANEL_SPACING 25
 #define PANEL_WIDTH 250
+#define BUTTON_MARGIN 8
+#define BUTTON_PADDING 12
 #define GAMEPAD_WIDTH 512
 #define GAMEPAD_HEIGHT 480
 
@@ -49,6 +51,8 @@ static SDL_Renderer *screen = NULL;
 static GamepadImage *image = NULL;
 static GamepadDisplay *gamepad_elements = NULL;
 static JoystickDisplay *joystick_elements = NULL;
+static GamepadButton *copy_button = NULL;
+static SDL_bool in_copy_button = SDL_FALSE;
 static SDL_bool retval = SDL_FALSE;
 static SDL_bool done = SDL_FALSE;
 static SDL_bool set_LED = SDL_FALSE;
@@ -594,6 +598,33 @@ static void DrawGamepadInfo(SDL_Renderer *renderer)
     }
 }
 
+static void CopyMappingToClipboard()
+{
+    char *mapping = SDL_GetGamepadMapping(gamepad);
+    if (mapping) {
+        const char *name = SDL_GetGamepadName(gamepad);
+        char *wildcard = SDL_strchr(mapping, '*');
+        if (wildcard && name && *name) {
+            char *text;
+            size_t size;
+
+            /* Personalize the mapping for this controller */
+            *wildcard++ = '\0';
+            size = SDL_strlen(mapping) + SDL_strlen(name) + SDL_strlen(wildcard) + 1;
+            text = SDL_malloc(size);
+            if (!text) {
+                return;
+            }
+            SDL_snprintf(text, size, "%s%s%s", mapping, name, wildcard);
+            SDL_SetClipboardText(text);
+            SDL_free(text);
+        } else {
+            SDL_SetClipboardText(mapping);
+        }
+        SDL_free(mapping);
+    }
+}
+
 static void loop(void *arg)
 {
     SDL_Event event;
@@ -687,18 +718,24 @@ static void loop(void *arg)
             if (virtual_joystick) {
                 VirtualGamepadMouseDown(event.button.x, event.button.y);
             }
+            SetGamepadButtonHighlight(copy_button, GamepadButtonContains(copy_button, event.button.x, event.button.y));
             break;
 
         case SDL_EVENT_MOUSE_BUTTON_UP:
             if (virtual_joystick) {
                 VirtualGamepadMouseUp(event.button.x, event.button.y);
             }
+            if (GamepadButtonContains(copy_button, event.button.x, event.button.y)) {
+                CopyMappingToClipboard();
+            }
+            SetGamepadButtonHighlight(copy_button, SDL_FALSE);
             break;
 
         case SDL_EVENT_MOUSE_MOTION:
             if (virtual_joystick) {
                 VirtualGamepadMouseMotion(event.motion.x, event.motion.y);
             }
+            SetGamepadButtonHighlight(copy_button, event.motion.state && GamepadButtonContains(copy_button, event.motion.x, event.motion.y));
             break;
 
         case SDL_EVENT_KEY_DOWN:
@@ -743,6 +780,8 @@ static void loop(void *arg)
         RenderGamepadDisplay(gamepad_elements, gamepad);
         RenderJoystickDisplay(joystick_elements, SDL_GetGamepadJoystick(gamepad));
 
+        RenderGamepadButton(copy_button);
+
         DrawGamepadInfo(screen);
 
         /* Update LED based on left thumbstick position */
@@ -811,6 +850,7 @@ int main(int argc, char *argv[])
     int i;
     float content_scale;
     int screen_width, screen_height;
+    int button_width, button_height;
     int gamepad_index = -1;
     SDLTest_CommonState *state;
 
@@ -924,6 +964,11 @@ int main(int argc, char *argv[])
     joystick_elements = CreateJoystickDisplay(screen);
     SetJoystickDisplayArea(joystick_elements, PANEL_WIDTH + PANEL_SPACING + GAMEPAD_WIDTH + PANEL_SPACING, TITLE_HEIGHT, PANEL_WIDTH, GAMEPAD_HEIGHT);
 
+    copy_button = CreateGamepadButton(screen, "Copy to Clipboard");
+    button_width = GetGamepadButtonLabelWidth(copy_button) + 2 * BUTTON_PADDING;
+    button_height = GetGamepadButtonLabelHeight(copy_button) + 2 * BUTTON_PADDING;
+    SetGamepadButtonArea(copy_button, BUTTON_MARGIN, SCREEN_HEIGHT - BUTTON_MARGIN - button_height, button_width, button_height);
+
     /* Process the initial gamepad list */
     loop(NULL);
 
@@ -953,6 +998,7 @@ int main(int argc, char *argv[])
     DestroyGamepadImage(image);
     DestroyGamepadDisplay(gamepad_elements);
     DestroyJoystickDisplay(joystick_elements);
+    DestroyGamepadButton(copy_button);
     SDL_DestroyRenderer(screen);
     SDL_DestroyWindow(window);
     SDL_QuitSubSystem(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK | SDL_INIT_GAMEPAD);