SDL: Added in a MIME-type to the X11 clipboard. (#4385)

From a20319489378dbf4c6fec012f3160540643d2f88 Mon Sep 17 00:00:00 2001
From: Adam <[EMAIL REDACTED]>
Date: Wed, 28 Jul 2021 14:06:51 -0400
Subject: [PATCH] Added in a MIME-type to the X11 clipboard. (#4385)

---
 src/video/x11/SDL_x11clipboard.c | 80 ++++++++++++++++++++++++++------
 src/video/x11/SDL_x11clipboard.h | 14 +++++-
 src/video/x11/SDL_x11events.c    | 58 ++++++++++++++---------
 3 files changed, 114 insertions(+), 38 deletions(-)

diff --git a/src/video/x11/SDL_x11clipboard.c b/src/video/x11/SDL_x11clipboard.c
index ad34b3a56a..7a50ce2de3 100644
--- a/src/video/x11/SDL_x11clipboard.c
+++ b/src/video/x11/SDL_x11clipboard.c
@@ -27,14 +27,7 @@
 #include "SDL_events.h"
 #include "SDL_x11video.h"
 #include "SDL_timer.h"
-
-
-/* If you don't support UTF-8, you might use XA_STRING here */
-#ifdef X_HAVE_UTF8_STRING
-#define TEXT_FORMAT X11_XInternAtom(display, "UTF8_STRING", False)
-#else
-#define TEXT_FORMAT XA_STRING
-#endif
+#include "SDL_x11clipboard.h"
 
 /* Get any application owned window handle for clipboard association */
 static Window
@@ -59,19 +52,77 @@ GetWindow(_THIS)
     return data->clipboard_window;
 }
 
+
 /* We use our own cut-buffer for intermediate storage instead of  
    XA_CUT_BUFFER0 because their use isn't really defined for holding UTF8. */ 
 Atom
-X11_GetSDLCutBufferClipboardType(Display *display)
+X11_GetSDLCutBufferClipboardType(Display *display, enum ESDLX11ClipboardMimeType mime_type)
 {
-    return X11_XInternAtom(display, "SDL_CUTBUFFER", False);
+    switch (mime_type) {
+        case SDL_X11_CLIPBOARD_MIME_TYPE_STRING:
+        case SDL_X11_CLIPBOARD_MIME_TYPE_TEXT_PLAIN:
+        #ifdef X_HAVE_UTF8_STRING
+        case SDL_X11_CLIPBOARD_MIME_TYPE_TEXT_PLAIN_UTF8:
+        #endif
+        case SDL_X11_CLIPBOARD_MIME_TYPE_TEXT:
+            return X11_XInternAtom(display, "SDL_CUTBUFFER", False);
+        default:
+            SDL_SetError("Can't find mime_type.");
+            return XA_STRING;
+    }
 }
 
+Atom
+X11_GetSDLCutBufferClipboardExternalFormat(Display *display, enum ESDLX11ClipboardMimeType mime_type)
+{
+    switch (mime_type) {
+        case SDL_X11_CLIPBOARD_MIME_TYPE_STRING:
+            /* If you don't support UTF-8, you might use XA_STRING here */
+            #ifdef X_HAVE_UTF8_STRING
+            return X11_XInternAtom(display, "UTF8_STRING", False);
+            #else
+            return  XA_STRING;
+            #endif
+        case SDL_X11_CLIPBOARD_MIME_TYPE_TEXT_PLAIN:
+            return X11_XInternAtom(display, "text/plain", False);
+        #ifdef X_HAVE_UTF8_STRING
+        case SDL_X11_CLIPBOARD_MIME_TYPE_TEXT_PLAIN_UTF8:
+            return X11_XInternAtom(display, "text/plain;charset=utf-8", False);
+        #endif
+        case SDL_X11_CLIPBOARD_MIME_TYPE_TEXT:
+            return X11_XInternAtom(display, "TEXT", False);
+        default:
+            SDL_SetError("Can't find mime_type.");
+            return XA_STRING;
+    }
+}
+Atom
+X11_GetSDLCutBufferClipboardInternalFormat(Display *display, enum ESDLX11ClipboardMimeType mime_type)
+{
+    switch (mime_type) {
+        case SDL_X11_CLIPBOARD_MIME_TYPE_STRING:
+        case SDL_X11_CLIPBOARD_MIME_TYPE_TEXT_PLAIN:
+        #ifdef X_HAVE_UTF8_STRING
+        case SDL_X11_CLIPBOARD_MIME_TYPE_TEXT_PLAIN_UTF8:
+        #endif
+        case SDL_X11_CLIPBOARD_MIME_TYPE_TEXT:
+            /* If you don't support UTF-8, you might use XA_STRING here */
+            #ifdef X_HAVE_UTF8_STRING
+            return X11_XInternAtom(display, "UTF8_STRING", False);
+            #else
+            return  XA_STRING;
+            #endif
+        default:
+            SDL_SetError("Can't find mime_type.");
+            return XA_STRING;
+    }
+}
+
+
 int
 X11_SetClipboardText(_THIS, const char *text)
 {
     Display *display = ((SDL_VideoData *) _this->driverdata)->display;
-    Atom format;
     Window window;
     Atom XA_CLIPBOARD = X11_XInternAtom(display, "CLIPBOARD", 0);
 
@@ -82,9 +133,8 @@ X11_SetClipboardText(_THIS, const char *text)
     }
 
     /* Save the selection on the root window */
-    format = TEXT_FORMAT;
     X11_XChangeProperty(display, DefaultRootWindow(display),
-        X11_GetSDLCutBufferClipboardType(display), format, 8, PropModeReplace,
+        X11_GetSDLCutBufferClipboardType(display, SDL_X11_CLIPBOARD_MIME_TYPE_STRING), X11_GetSDLCutBufferClipboardInternalFormat(display, SDL_X11_CLIPBOARD_MIME_TYPE_STRING), 8, PropModeReplace,
         (const unsigned char *)text, SDL_strlen(text));
 
     if (XA_CLIPBOARD != None &&
@@ -125,7 +175,7 @@ X11_GetClipboardText(_THIS)
 
     /* Get the window that holds the selection */
     window = GetWindow(_this);
-    format = TEXT_FORMAT;
+    format = X11_GetSDLCutBufferClipboardInternalFormat(display, SDL_X11_CLIPBOARD_MIME_TYPE_STRING);
     owner = X11_XGetSelectionOwner(display, XA_CLIPBOARD);
     if (owner == None) {
         /* Fall back to ancient X10 cut-buffers which do not support UTF8 strings*/
@@ -134,7 +184,7 @@ X11_GetClipboardText(_THIS)
         format = XA_STRING;
     } else if (owner == window) {
         owner = DefaultRootWindow(display);
-        selection = X11_GetSDLCutBufferClipboardType(display);
+        selection = X11_GetSDLCutBufferClipboardType(display, SDL_X11_CLIPBOARD_MIME_TYPE_STRING);
     } else {
         /* Request that the selection owner copy the data to our window */
         owner = window;
diff --git a/src/video/x11/SDL_x11clipboard.h b/src/video/x11/SDL_x11clipboard.h
index 09b7f515e4..9a61f5a078 100644
--- a/src/video/x11/SDL_x11clipboard.h
+++ b/src/video/x11/SDL_x11clipboard.h
@@ -23,10 +23,22 @@
 #ifndef SDL_x11clipboard_h_
 #define SDL_x11clipboard_h_
 
+enum ESDLX11ClipboardMimeType {
+    SDL_X11_CLIPBOARD_MIME_TYPE_STRING,
+    SDL_X11_CLIPBOARD_MIME_TYPE_TEXT_PLAIN,
+    #ifdef X_HAVE_UTF8_STRING
+    SDL_X11_CLIPBOARD_MIME_TYPE_TEXT_PLAIN_UTF8,
+    #endif
+    SDL_X11_CLIPBOARD_MIME_TYPE_TEXT,
+    SDL_X11_CLIPBOARD_MIME_TYPE_MAX
+};
+
 extern int X11_SetClipboardText(_THIS, const char *text);
 extern char *X11_GetClipboardText(_THIS);
 extern SDL_bool X11_HasClipboardText(_THIS);
-extern Atom X11_GetSDLCutBufferClipboardType(Display *display);
+extern Atom X11_GetSDLCutBufferClipboardType(Display *display, enum ESDLX11ClipboardMimeType mime_type);
+extern Atom X11_GetSDLCutBufferClipboardExternalFormat(Display *display, enum ESDLX11ClipboardMimeType mime_type);
+extern Atom X11_GetSDLCutBufferClipboardInternalFormat(Display *display, enum ESDLX11ClipboardMimeType mime_type);
 
 #endif /* SDL_x11clipboard_h_ */
 
diff --git a/src/video/x11/SDL_x11events.c b/src/video/x11/SDL_x11events.c
index 8d19a2e6d6..d3b02f5685 100644
--- a/src/video/x11/SDL_x11events.c
+++ b/src/video/x11/SDL_x11events.c
@@ -562,6 +562,7 @@ X11_UpdateUserTime(SDL_WindowData *data, const unsigned long latest)
 static void
 X11_HandleClipboardEvent(_THIS, const XEvent *xevent)
 {
+    int i;
     SDL_VideoData *videodata = (SDL_VideoData *) _this->driverdata;
     Display *display = videodata->display;
 
@@ -573,10 +574,12 @@ X11_HandleClipboardEvent(_THIS, const XEvent *xevent)
         case SelectionRequest: {
             const XSelectionRequestEvent *req = &xevent->xselectionrequest;
             XEvent sevent;
-            int seln_format;
+            int seln_format, mime_formats;
             unsigned long nbytes;
             unsigned long overflow;
-            unsigned char *seln_data;
+            unsigned char *seln_data;            
+            Atom supportedFormats[SDL_X11_CLIPBOARD_MIME_TYPE_MAX+1];
+            Atom XA_TARGETS = X11_XInternAtom(display, "TARGETS", 0);
 
 #ifdef DEBUG_XEVENTS
             printf("window CLIPBOARD: SelectionRequest (requestor = %ld, target = %ld)\n",
@@ -594,27 +597,38 @@ X11_HandleClipboardEvent(_THIS, const XEvent *xevent)
             /* !!! FIXME: We were probably storing this on the root window
                because an SDL window might go away...? but we don't have to do
                this now (or ever, really). */
-            if (X11_XGetWindowProperty(display, DefaultRootWindow(display),
-                    X11_GetSDLCutBufferClipboardType(display), 0, INT_MAX/4, False, req->target,
-                    &sevent.xselection.target, &seln_format, &nbytes,
-                    &overflow, &seln_data) == Success) {
-                /* !!! FIXME: cache atoms */
-                Atom XA_TARGETS = X11_XInternAtom(display, "TARGETS", 0);
-                if (sevent.xselection.target == req->target) {
-                    X11_XChangeProperty(display, req->requestor, req->property,
-                        sevent.xselection.target, seln_format, PropModeReplace,
-                        seln_data, nbytes);
-                    sevent.xselection.property = req->property;
-                } else if (XA_TARGETS == req->target) {
-                    Atom SupportedFormats[] = { XA_TARGETS, sevent.xselection.target };
-                    X11_XChangeProperty(display, req->requestor, req->property,
-                        XA_ATOM, 32, PropModeReplace,
-                        (unsigned char*)SupportedFormats,
-                        SDL_arraysize(SupportedFormats));
-                    sevent.xselection.property = req->property;
-                    sevent.xselection.target = XA_TARGETS;
+            
+            if (req->target == XA_TARGETS) {
+                supportedFormats[0] = XA_TARGETS;
+                mime_formats = 1;
+                for (i = 0; i < SDL_X11_CLIPBOARD_MIME_TYPE_MAX; ++i) 
+                    supportedFormats[mime_formats++] = X11_GetSDLCutBufferClipboardExternalFormat(display, i);
+                X11_XChangeProperty(display, req->requestor, req->property,
+                    XA_ATOM, 32, PropModeReplace,
+                    (unsigned char*)supportedFormats,
+                    mime_formats);
+                sevent.xselection.property = req->property;
+                sevent.xselection.target = XA_TARGETS;
+            } else {
+                for (i = 0; i < SDL_X11_CLIPBOARD_MIME_TYPE_MAX; ++i) {
+                    if (X11_GetSDLCutBufferClipboardExternalFormat(display, i) != req->target)
+                        continue;
+                    if (X11_XGetWindowProperty(display, DefaultRootWindow(display),
+                        X11_GetSDLCutBufferClipboardType(display, i), 0, INT_MAX/4, False, X11_GetSDLCutBufferClipboardInternalFormat(display, i),
+                        &sevent.xselection.target, &seln_format, &nbytes,
+                        &overflow, &seln_data) == Success) {
+                            if (seln_format != None) {
+                                X11_XChangeProperty(display, req->requestor, req->property,
+                                    sevent.xselection.target, seln_format, PropModeReplace,
+                                    seln_data, nbytes);
+                                sevent.xselection.property = req->property;
+                                X11_XFree(seln_data);
+                                break;
+                            } else {
+                                X11_XFree(seln_data);
+                            }
+                    }
                 }
-                X11_XFree(seln_data);
             }
             X11_XSendEvent(display, req->requestor, False, 0, &sevent);
             X11_XSync(display, False);