SDL: Add the source application for drag and drop events (thanks Nathan!)

From 91f045639160dece14ece8d404816d8e9b8a61e9 Mon Sep 17 00:00:00 2001
From: Sam Lantinga <[EMAIL REDACTED]>
Date: Sat, 4 Nov 2023 12:40:52 -0700
Subject: [PATCH] Add the source application for drag and drop events (thanks
 Nathan!)

This is only implemented on iOS, but is useful for third party application integrations.

Fixes https://github.com/libsdl-org/SDL/issues/2024
---
 include/SDL3/SDL_events.h              |  1 +
 src/core/android/SDL_android.c         |  2 +-
 src/core/haiku/SDL_BeApp.cc            |  2 +-
 src/events/SDL_dropevents.c            | 18 ++++++++++--------
 src/events/SDL_dropevents_c.h          |  4 ++--
 src/events/SDL_events.c                |  4 ++++
 src/video/cocoa/SDL_cocoaevents.m      |  4 ++--
 src/video/cocoa/SDL_cocoawindow.m      |  4 ++--
 src/video/uikit/SDL_uikitappdelegate.m | 15 ++++++++-------
 src/video/wayland/SDL_waylandevents.c  |  6 +++---
 src/video/windows/SDL_windowsevents.c  |  2 +-
 src/video/x11/SDL_x11events.c          |  4 ++--
 12 files changed, 37 insertions(+), 29 deletions(-)

diff --git a/include/SDL3/SDL_events.h b/include/SDL3/SDL_events.h
index ad87c5ccb470..2afa92f3e58d 100644
--- a/include/SDL3/SDL_events.h
+++ b/include/SDL3/SDL_events.h
@@ -523,6 +523,7 @@ typedef struct SDL_DropEvent
     SDL_WindowID windowID;    /**< The window that was dropped on, if any */
     float x;            /**< X coordinate, relative to window (not on begin) */
     float y;            /**< Y coordinate, relative to window (not on begin) */
+    char *source;       /**< The source app that sent this drop event, or NULL if that isn't available */
     char *data;         /**< The text for SDL_EVENT_DROP_TEXT and the file name for SDL_EVENT_DROP_FILE, NULL for other events */
     char short_data[SDL_DROPEVENT_DATA_SIZE]; /**< Memory space for short data, use 'data' instead */
 } SDL_DropEvent;
diff --git a/src/core/android/SDL_android.c b/src/core/android/SDL_android.c
index 99ebd7492de7..8626a6b6f56f 100644
--- a/src/core/android/SDL_android.c
+++ b/src/core/android/SDL_android.c
@@ -878,7 +878,7 @@ JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeDropFile)(
     jstring filename)
 {
     const char *path = (*env)->GetStringUTFChars(env, filename, NULL);
-    SDL_SendDropFile(NULL, path);
+    SDL_SendDropFile(NULL, NULL, path);
     (*env)->ReleaseStringUTFChars(env, filename, path);
     SDL_SendDropComplete(NULL);
 }
diff --git a/src/core/haiku/SDL_BeApp.cc b/src/core/haiku/SDL_BeApp.cc
index 16eb58aff485..8403f9a18c6b 100644
--- a/src/core/haiku/SDL_BeApp.cc
+++ b/src/core/haiku/SDL_BeApp.cc
@@ -69,7 +69,7 @@ class SDL_BApp : public BApplication {
         entry_ref entryRef;
         for (int32 i = 0; message->FindRef("refs", i, &entryRef) == B_OK; i++) {
             BPath referencePath = BPath(&entryRef);
-            SDL_SendDropFile(NULL, referencePath.Path());
+            SDL_SendDropFile(NULL, NULL, referencePath.Path());
         }
         return;
     }
diff --git a/src/events/SDL_dropevents.c b/src/events/SDL_dropevents.c
index 211b8d3b37f3..a7b9fa7a79a7 100644
--- a/src/events/SDL_dropevents.c
+++ b/src/events/SDL_dropevents.c
@@ -27,7 +27,7 @@
 
 #include "../video/SDL_sysvideo.h" /* for SDL_Window internals. */
 
-static int SDL_SendDrop(SDL_Window *window, const SDL_EventType evtype, const char *data, float x, float y)
+static int SDL_SendDrop(SDL_Window *window, const SDL_EventType evtype, const char *source, const char *data, float x, float y)
 {
     static SDL_bool app_is_dropping = SDL_FALSE;
     static float last_drop_x = 0;
@@ -58,6 +58,9 @@ static int SDL_SendDrop(SDL_Window *window, const SDL_EventType evtype, const ch
         SDL_zero(event);
         event.type = evtype;
         event.common.timestamp = 0;
+        if (source) {
+            event.drop.source = SDL_strdup(source);
+        }
         if (data) {
             size_t len = SDL_strlen(data);
             if (len < sizeof(event.drop.short_data)) {
@@ -91,23 +94,22 @@ static int SDL_SendDrop(SDL_Window *window, const SDL_EventType evtype, const ch
     return posted;
 }
 
-int SDL_SendDropFile(SDL_Window *window, const char *file)
+int SDL_SendDropFile(SDL_Window *window, const char *source, const char *file)
 {
-    return SDL_SendDrop(window, SDL_EVENT_DROP_FILE, file, 0, 0);
+    return SDL_SendDrop(window, SDL_EVENT_DROP_FILE, source, file, 0, 0);
 }
 
-int SDL_SendDropPosition(SDL_Window *window, const char *file, float x, float y)
+int SDL_SendDropPosition(SDL_Window *window, float x, float y)
 {
-    /* Don't send 'file' since this is an malloc per position, which may be forgotten to be freed */
-    return SDL_SendDrop(window, SDL_EVENT_DROP_POSITION, NULL, x, y);
+    return SDL_SendDrop(window, SDL_EVENT_DROP_POSITION, NULL, NULL, x, y);
 }
 
 int SDL_SendDropText(SDL_Window *window, const char *text)
 {
-    return SDL_SendDrop(window, SDL_EVENT_DROP_TEXT, text, 0, 0);
+    return SDL_SendDrop(window, SDL_EVENT_DROP_TEXT, NULL, text, 0, 0);
 }
 
 int SDL_SendDropComplete(SDL_Window *window)
 {
-    return SDL_SendDrop(window, SDL_EVENT_DROP_COMPLETE, NULL, 0, 0);
+    return SDL_SendDrop(window, SDL_EVENT_DROP_COMPLETE, NULL, NULL, 0, 0);
 }
diff --git a/src/events/SDL_dropevents_c.h b/src/events/SDL_dropevents_c.h
index 9a485cd0b2ec..f2396d5d30c0 100644
--- a/src/events/SDL_dropevents_c.h
+++ b/src/events/SDL_dropevents_c.h
@@ -23,8 +23,8 @@
 #ifndef SDL_dropevents_c_h_
 #define SDL_dropevents_c_h_
 
-extern int SDL_SendDropFile(SDL_Window *window, const char *file);
-extern int SDL_SendDropPosition(SDL_Window *window, const char *file, float x, float y);
+extern int SDL_SendDropFile(SDL_Window *window, const char *source, const char *file);
+extern int SDL_SendDropPosition(SDL_Window *window, float x, float y);
 extern int SDL_SendDropText(SDL_Window *window, const char *text);
 extern int SDL_SendDropComplete(SDL_Window *window);
 
diff --git a/src/events/SDL_events.c b/src/events/SDL_events.c
index 1ce42ad5756d..3f18641a7496 100644
--- a/src/events/SDL_events.c
+++ b/src/events/SDL_events.c
@@ -1073,6 +1073,10 @@ void SDL_CleanupEvent(SDL_Event *event)
     switch (event->type) {
     case SDL_EVENT_DROP_FILE:
     case SDL_EVENT_DROP_TEXT:
+        if (event->drop.source) {
+            SDL_free(event->drop.source);
+            event->drop.data = NULL;
+        }
         if (event->drop.data && event->drop.data != event->drop.short_data) {
             SDL_free(event->drop.data);
             event->drop.data = NULL;
diff --git a/src/video/cocoa/SDL_cocoaevents.m b/src/video/cocoa/SDL_cocoaevents.m
index 221a61d91bb2..3674965c8b56 100644
--- a/src/video/cocoa/SDL_cocoaevents.m
+++ b/src/video/cocoa/SDL_cocoaevents.m
@@ -287,7 +287,7 @@ - (void)observeValueForKeyPath:(NSString *)keyPath
 
 - (BOOL)application:(NSApplication *)theApplication openFile:(NSString *)filename
 {
-    return (BOOL)SDL_SendDropFile(NULL, [filename UTF8String]) && SDL_SendDropComplete(NULL);
+    return (BOOL)SDL_SendDropFile(NULL, NULL, [filename UTF8String]) && SDL_SendDropComplete(NULL);
 }
 
 - (void)applicationDidFinishLaunching:(NSNotification *)notification
@@ -315,7 +315,7 @@ - (void)applicationDidFinishLaunching:(NSNotification *)notification
 - (void)handleURLEvent:(NSAppleEventDescriptor *)event withReplyEvent:(NSAppleEventDescriptor *)replyEvent
 {
     NSString *path = [[event paramDescriptorForKeyword:keyDirectObject] stringValue];
-    SDL_SendDropFile(NULL, [path UTF8String]);
+    SDL_SendDropFile(NULL, NULL, [path UTF8String]);
     SDL_SendDropComplete(NULL);
 }
 
diff --git a/src/video/cocoa/SDL_cocoawindow.m b/src/video/cocoa/SDL_cocoawindow.m
index f86027e0e768..970e8e1f17cd 100644
--- a/src/video/cocoa/SDL_cocoawindow.m
+++ b/src/video/cocoa/SDL_cocoawindow.m
@@ -177,7 +177,7 @@ - (NSDragOperation)draggingUpdated:(id<NSDraggingInfo>)sender
         float x, y;
         x = point.x;
         y = (sdlwindow->h - point.y);
-        SDL_SendDropPosition(sdlwindow, NULL, x, y); /* FIXME, should we get the filename */
+        SDL_SendDropPosition(sdlwindow, x, y);
         return NSDragOperationGeneric;
     }
 
@@ -242,7 +242,7 @@ - (BOOL)performDragOperation:(id<NSDraggingInfo>)sender
                 }
             }
 
-            if (!SDL_SendDropFile(sdlwindow, [[fileURL path] UTF8String])) {
+            if (!SDL_SendDropFile(sdlwindow, NULL, [[fileURL path] UTF8String])) {
                 return NO;
             }
         }
diff --git a/src/video/uikit/SDL_uikitappdelegate.m b/src/video/uikit/SDL_uikitappdelegate.m
index e9d6ca33793b..8a4c0e4605b5 100644
--- a/src/video/uikit/SDL_uikitappdelegate.m
+++ b/src/video/uikit/SDL_uikitappdelegate.m
@@ -212,7 +212,7 @@ - (instancetype)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibB
         NSArray *launchimages = [bundle objectForInfoDictionaryKey:@"UILaunchImages"];
         NSString *imagename = nil;
         UIImage *image = nil;
-        
+
 #if TARGET_OS_XR
         int screenw = SDL_XR_SCREENWIDTH;
         int screenh = SDL_XR_SCREENHEIGHT;
@@ -221,7 +221,7 @@ - (instancetype)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibB
         int screenh = (int)([UIScreen mainScreen].bounds.size.height + 0.5);
 #endif
 
-       
+
 
 #if !TARGET_OS_TV && !TARGET_OS_XR
         UIInterfaceOrientation curorient = [UIApplication sharedApplication].statusBarOrientation;
@@ -505,13 +505,14 @@ - (void)setWindow:(UIWindow *)window
     /* Do nothing. */
 }
 
-- (void)sendDropFileForURL:(NSURL *)url
+- (void)sendDropFileForURL:(NSURL *)url fromSourceApplication:(NSString *)sourceApplication
 {
     NSURL *fileURL = url.filePathURL;
+    char *sourceApplicationCString = sourceApplication ? [sourceApplication UTF8String] : NULL;
     if (fileURL != nil) {
-        SDL_SendDropFile(NULL, fileURL.path.UTF8String);
+        SDL_SendDropFile(NULL, sourceApplicationCString, fileURL.path.UTF8String);
     } else {
-        SDL_SendDropFile(NULL, url.absoluteString.UTF8String);
+        SDL_SendDropFile(NULL, sourceApplicationCString, url.absoluteString.UTF8String);
     }
     SDL_SendDropComplete(NULL);
 }
@@ -521,7 +522,7 @@ - (void)sendDropFileForURL:(NSURL *)url
 - (BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary<UIApplicationOpenURLOptionsKey, id> *)options
 {
     /* TODO: Handle options */
-    [self sendDropFileForURL:url];
+    [self sendDropFileForURL:url fromSourceApplication:NULL];
     return YES;
 }
 
@@ -529,7 +530,7 @@ - (BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDiction
 
 - (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation
 {
-    [self sendDropFileForURL:url];
+    [self sendDropFileForURL:url fromSourceApplication:sourceApplication];
     return YES;
 }
 
diff --git a/src/video/wayland/SDL_waylandevents.c b/src/video/wayland/SDL_waylandevents.c
index c68b49c3c250..2e9583cc7b76 100644
--- a/src/video/wayland/SDL_waylandevents.c
+++ b/src/video/wayland/SDL_waylandevents.c
@@ -1909,7 +1909,7 @@ static void data_device_handle_motion(void *data, struct wl_data_device *wl_data
          *      Any future implementation should cache the filenames, as otherwise this could
          *      hammer the DBus interface hundreds or even thousands of times per second.
          */
-        SDL_SendDropPosition(data_device->dnd_window, NULL, dx, dy);
+        SDL_SendDropPosition(data_device->dnd_window, dx, dy);
     }
 }
 
@@ -2055,7 +2055,7 @@ static void data_device_handle_drop(void *data, struct wl_data_device *wl_data_d
                     /* If dropped files contain a directory the list is empty */
                     if (paths && path_count > 0) {
                         for (int i = 0; i < path_count; i++) {
-                            SDL_SendDropFile(data_device->dnd_window, paths[i]);
+                            SDL_SendDropFile(data_device->dnd_window, NULL, paths[i]);
                         }
                         dbus->free_string_array(paths);
                         SDL_SendDropComplete(data_device->dnd_window);
@@ -2080,7 +2080,7 @@ static void data_device_handle_drop(void *data, struct wl_data_device *wl_data_d
                 while (token != NULL) {
                     char *fn = Wayland_URIToLocal(token);
                     if (fn) {
-                        SDL_SendDropFile(data_device->dnd_window, fn);
+                        SDL_SendDropFile(data_device->dnd_window, NULL, fn);
                     }
                     token = SDL_strtok_r(NULL, "\r\n", &saveptr);
                 }
diff --git a/src/video/windows/SDL_windowsevents.c b/src/video/windows/SDL_windowsevents.c
index f2e747ee9c92..042667c84b44 100644
--- a/src/video/windows/SDL_windowsevents.c
+++ b/src/video/windows/SDL_windowsevents.c
@@ -1478,7 +1478,7 @@ WIN_WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
             if (buffer) {
                 if (DragQueryFile(drop, i, buffer, size)) {
                     char *file = WIN_StringToUTF8(buffer);
-                    SDL_SendDropFile(data->window, file);
+                    SDL_SendDropFile(data->window, NULL, file);
                     SDL_free(file);
                 }
                 SDL_small_free(buffer, isstack);
diff --git a/src/video/x11/SDL_x11events.c b/src/video/x11/SDL_x11events.c
index d414ee66a1af..9c7afcf44520 100644
--- a/src/video/x11/SDL_x11events.c
+++ b/src/video/x11/SDL_x11events.c
@@ -1327,7 +1327,7 @@ static void X11_DispatchEvent(SDL_VideoDevice *_this, XEvent *xevent)
                 X11_XTranslateCoordinates(display, DefaultRootWindow(display), data->xwindow,
                         root_x, root_y, &window_x, &window_y, &ChildReturn);
 
-                SDL_SendDropPosition(data->window, NULL, (float)window_x, (float)window_y); /* FIXME, can we get the filename ? */
+                SDL_SendDropPosition(data->window, (float)window_x, (float)window_y);
             }
 
             /* reply with status */
@@ -1629,7 +1629,7 @@ static void X11_DispatchEvent(SDL_VideoDevice *_this, XEvent *xevent)
                         } else if (SDL_strcmp("text/uri-list", name) == 0) {
                             char *fn = X11_URIToLocal(token);
                             if (fn) {
-                                SDL_SendDropFile(data->window, fn);
+                                SDL_SendDropFile(data->window, NULL, fn);
                             }
                         }
                         token = SDL_strtok_r(NULL, "\r\n", &saveptr);