SDL: Added testclipboard

From f18e0233175ca8cabd7ff0bbdf11ff927b9951e0 Mon Sep 17 00:00:00 2001
From: Sam Lantinga <[EMAIL REDACTED]>
Date: Tue, 31 Dec 2024 11:57:36 -0800
Subject: [PATCH] Added testclipboard

---
 test/CMakeLists.txt  |   1 +
 test/testclipboard.c | 180 +++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 181 insertions(+)
 create mode 100644 test/testclipboard.c

diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt
index aec3eab28a98c..c6e9b4853ab50 100644
--- a/test/CMakeLists.txt
+++ b/test/CMakeLists.txt
@@ -397,6 +397,7 @@ add_sdl_test_executable(testtimer NONINTERACTIVE NONINTERACTIVE_ARGS --no-intera
 add_sdl_test_executable(testurl SOURCES testurl.c)
 add_sdl_test_executable(testver NONINTERACTIVE NOTRACKMEM SOURCES testver.c)
 add_sdl_test_executable(testcamera MAIN_CALLBACKS SOURCES testcamera.c)
+add_sdl_test_executable(testclipboard MAIN_CALLBACKS SOURCES testclipboard.c ${icon_bmp_header} DEPENDS generate-icon_bmp_header)
 add_sdl_test_executable(testviewport NEEDS_RESOURCES TESTUTILS SOURCES testviewport.c)
 add_sdl_test_executable(testwm SOURCES testwm.c)
 add_sdl_test_executable(testyuv NONINTERACTIVE NONINTERACTIVE_ARGS "--automated" NEEDS_RESOURCES TESTUTILS SOURCES testyuv.c testyuv_cvt.c)
diff --git a/test/testclipboard.c b/test/testclipboard.c
new file mode 100644
index 0000000000000..729842e6bc3b6
--- /dev/null
+++ b/test/testclipboard.c
@@ -0,0 +1,180 @@
+/*
+  Copyright (C) 1997-2024 Sam Lantinga <slouken@libsdl.org>
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely.
+*/
+
+#define SDL_MAIN_USE_CALLBACKS 1
+#include <SDL3/SDL.h>
+#include <SDL3/SDL_main.h>
+
+#include "icon.h"
+
+static SDL_Window *window = NULL;
+static SDL_Renderer *renderer = NULL;
+
+static const char *mime_types[] = {
+    "text/plain",
+    "image/bmp",
+};
+
+static const void *ClipboardDataCallback(void *userdata, const char *mime_type, size_t *size)
+{
+    if (SDL_strcmp(mime_type, "text/plain") == 0) {
+        const char *text = "Hello world!";
+        *size = SDL_strlen(text);
+        return text;
+    } else if (SDL_strcmp(mime_type, "image/bmp") == 0) {
+        *size = icon_bmp_len;
+        return icon_bmp;
+    } else {
+        return NULL;
+    }
+}
+
+SDL_AppResult SDL_AppInit(void **appstate, int argc, char *argv[])
+{
+    if (!SDL_Init(SDL_INIT_VIDEO)) {
+        SDL_Log("Couldn't initialize SDL: %s\n", SDL_GetError());
+        return SDL_APP_FAILURE;
+    }
+
+    if (!SDL_CreateWindowAndRenderer("testclipboard", 640, 480, 0, &window, &renderer)) {
+        SDL_Log("Couldn't create window and renderer: %s\n", SDL_GetError());
+        return SDL_APP_FAILURE;
+    }
+    return SDL_APP_CONTINUE;
+}
+
+
+SDL_AppResult SDL_AppEvent(void *appstate, SDL_Event *event)
+{
+    switch (event->type) {
+    case SDL_EVENT_KEY_DOWN:
+        if (event->key.key == SDLK_ESCAPE) {
+            return SDL_APP_SUCCESS;
+        }
+        if (event->key.key == SDLK_C && event->key.mod & SDL_KMOD_CTRL) {
+            SDL_SetClipboardData(ClipboardDataCallback, NULL, NULL, mime_types, SDL_arraysize(mime_types));
+            break;
+        }
+        break;
+
+    case SDL_EVENT_CLIPBOARD_UPDATE:
+        if (event->clipboard.num_mime_types > 0) {
+            int i;
+            SDL_Log("Clipboard updated:\n");
+            for (i = 0; event->clipboard.mime_types[i]; ++i) {
+                SDL_Log("    %s\n", event->clipboard.mime_types[i]);
+            }
+        } else {
+            SDL_Log("Clipboard cleared\n");
+        }
+        break;
+
+    case SDL_EVENT_QUIT:
+        return SDL_APP_SUCCESS;
+
+    default:
+        break;
+    }
+
+    return SDL_APP_CONTINUE;
+}
+
+static float PrintClipboardText(float x, float y, const char *mime_type)
+{
+    void *data = SDL_GetClipboardData(mime_type, NULL);
+    if (data) {
+        SDL_RenderDebugText(renderer, x, y, (const char *)data);
+        SDL_free(data);
+        return SDL_DEBUG_TEXT_FONT_CHARACTER_SIZE + 2.0f;
+    }
+    return 0.0f;
+}
+
+static float PrintClipboardImage(float x, float y, const char *mime_type)
+{
+    /* We don't actually need to read this data each frame, but this is a simple example */
+    if (SDL_strcmp(mime_type, "image/bmp") == 0) {
+        size_t size;
+        void *data = SDL_GetClipboardData(mime_type, &size);
+        if (data) {
+            float w = 0.0f, h = 0.0f;
+            bool rendered = false;
+            SDL_IOStream *stream = SDL_IOFromConstMem(data, size);
+            if (stream) {
+                SDL_Surface *surface = SDL_LoadBMP_IO(stream, false);
+                if (surface) {
+                    SDL_Texture *texture = SDL_CreateTextureFromSurface(renderer, surface);
+                    if (texture) {
+                        SDL_GetTextureSize(texture, &w, &h);
+
+                        SDL_FRect dst = { x, y, w, h };
+                        rendered = SDL_RenderTexture(renderer, texture, NULL, &dst);
+                        SDL_DestroyTexture(texture);
+                    }
+                    SDL_DestroySurface(surface);
+                }
+                SDL_CloseIO(stream);
+            }
+            if (!rendered) {
+                SDL_RenderDebugText(renderer, x, y, SDL_GetError());
+            }
+            SDL_free(data);
+            return h + 2.0f;
+        }
+    }
+    return 0.0f;
+}
+
+static void PrintClipboardContents(float x, float y)
+{
+    char **clipboard_mime_types = SDL_GetClipboardMimeTypes(NULL);
+    if (clipboard_mime_types) {
+        int i;
+
+        for (i = 0; clipboard_mime_types[i]; ++i) {
+            const char *mime_type = clipboard_mime_types[i];
+            SDL_RenderDebugText(renderer, x, y, mime_type);
+            y += SDL_DEBUG_TEXT_FONT_CHARACTER_SIZE + 2;
+            if (SDL_strncmp(mime_type, "text/", 5) == 0) {
+                y += PrintClipboardText(x + SDL_DEBUG_TEXT_FONT_CHARACTER_SIZE * 2, y, mime_type);
+            } else if (SDL_strncmp(mime_type, "image/", 6) == 0) {
+                y += PrintClipboardImage(x + SDL_DEBUG_TEXT_FONT_CHARACTER_SIZE * 2, y, mime_type);
+            }
+        }
+        SDL_free(clipboard_mime_types);
+    }
+}
+
+SDL_AppResult SDL_AppIterate(void *appstate)
+{
+    SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
+    SDL_RenderClear(renderer);
+
+    SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255);
+    float x = 4.0f;
+    float y = 4.0f;
+    SDL_RenderDebugText(renderer, x, y, "Press Ctrl+C to copy content to the clipboard");
+    y += SDL_DEBUG_TEXT_FONT_CHARACTER_SIZE * 2;
+    SDL_RenderDebugText(renderer, x, y, "Clipboard contents:");
+    x += SDL_DEBUG_TEXT_FONT_CHARACTER_SIZE * 2;
+    y += SDL_DEBUG_TEXT_FONT_CHARACTER_SIZE + 2;
+    PrintClipboardContents(x, y);
+
+    SDL_RenderPresent(renderer);
+
+    return SDL_APP_CONTINUE;
+}
+
+void SDL_AppQuit(void *appstate, SDL_AppResult result)
+{
+}
+