SDL: Send drop complete events when the drop leaves the window on Cocoa, Wayland, and X11

From 731853077aeebd939d891eca9f563adcd95c8595 Mon Sep 17 00:00:00 2001
From: Frank Praznik <[EMAIL REDACTED]>
Date: Thu, 24 Oct 2024 12:06:38 -0400
Subject: [PATCH] Send drop complete events when the drop leaves the window on
 Cocoa, Wayland, and X11

This is already done on win32, however, other platforms were left in a state of limbo if a drop operation began, then never completed due to the drop leaving the window.
---
 src/video/cocoa/SDL_cocoawindow.m     | 7 +++++++
 src/video/wayland/SDL_waylandevents.c | 1 +
 src/video/x11/SDL_x11events.c         | 6 +++++-
 src/video/x11/SDL_x11video.c          | 1 +
 src/video/x11/SDL_x11video.h          | 1 +
 5 files changed, 15 insertions(+), 1 deletion(-)

diff --git a/src/video/cocoa/SDL_cocoawindow.m b/src/video/cocoa/SDL_cocoawindow.m
index 2504d9677f0fa..7afa943e52f27 100644
--- a/src/video/cocoa/SDL_cocoawindow.m
+++ b/src/video/cocoa/SDL_cocoawindow.m
@@ -88,6 +88,7 @@ - (void)doCommandBySelector:(SEL)aSelector;
 
 // Handle drag-and-drop of files onto the SDL window.
 - (NSDragOperation)draggingEntered:(id<NSDraggingInfo>)sender;
+- (void)draggingExited:(id<NSDraggingInfo>)sender;
 - (NSDragOperation)draggingUpdated:(id<NSDraggingInfo>)sender;
 - (BOOL)performDragOperation:(id<NSDraggingInfo>)sender;
 - (BOOL)wantsPeriodicDraggingUpdates;
@@ -174,6 +175,12 @@ - (NSDragOperation)draggingEntered:(id<NSDraggingInfo>)sender
     return NSDragOperationNone; // no idea what to do with this, reject it.
 }
 
+- (void)draggingExited:(id<NSDraggingInfo>)sender
+{
+    SDL_Window *sdlwindow = [self findSDLWindow];
+    SDL_SendDropComplete(sdlwindow);
+}
+
 - (NSDragOperation)draggingUpdated:(id<NSDraggingInfo>)sender
 {
     if (([sender draggingSourceOperationMask] & NSDragOperationGeneric) == NSDragOperationGeneric) {
diff --git a/src/video/wayland/SDL_waylandevents.c b/src/video/wayland/SDL_waylandevents.c
index 816fa0a7c2dbf..9f72d03c1dd5d 100644
--- a/src/video/wayland/SDL_waylandevents.c
+++ b/src/video/wayland/SDL_waylandevents.c
@@ -2134,6 +2134,7 @@ static void data_device_handle_leave(void *data, struct wl_data_device *wl_data_
 
     if (data_device->drag_offer) {
         if (data_device->dnd_window) {
+            SDL_SendDropComplete(data_device->dnd_window);
             SDL_LogTrace(SDL_LOG_CATEGORY_INPUT,
                          ". In wl_data_device_listener . data_device_handle_leave on data_offer 0x%08x from window %d for serial %d\n",
                          WAYLAND_wl_proxy_get_id((struct wl_proxy *)data_device->drag_offer->offer),
diff --git a/src/video/x11/SDL_x11events.c b/src/video/x11/SDL_x11events.c
index f0afe172c27ce..562a335c6e6e0 100644
--- a/src/video/x11/SDL_x11events.c
+++ b/src/video/x11/SDL_x11events.c
@@ -1384,7 +1384,6 @@ static void X11_DispatchEvent(SDL_VideoDevice *_this, XEvent *xevent)
         // Have we been requested to quit (or another client message?)
     case ClientMessage:
     {
-
         static int xdnd_version = 0;
 
         if (xevent->xclient.message_type == videodata->atoms.XdndEnter) {
@@ -1409,6 +1408,11 @@ static void X11_DispatchEvent(SDL_VideoDevice *_this, XEvent *xevent)
                 // pick from list of three
                 data->xdnd_req = X11_PickTargetFromAtoms(display, xevent->xclient.data.l[2], xevent->xclient.data.l[3], xevent->xclient.data.l[4]);
             }
+        } else if (xevent->xclient.message_type == videodata->atoms.XdndLeave) {
+#ifdef DEBUG_XEVENTS
+            SDL_Log("XID of source window : 0x%lx\n", xevent->xclient.data.l[0]);
+#endif
+            SDL_SendDropComplete(data->window);
         } else if (xevent->xclient.message_type == videodata->atoms.XdndPosition) {
 
 #ifdef DEBUG_XEVENTS
diff --git a/src/video/x11/SDL_x11video.c b/src/video/x11/SDL_x11video.c
index f5ed6f4fbdaea..5e9774ee0340e 100644
--- a/src/video/x11/SDL_x11video.c
+++ b/src/video/x11/SDL_x11video.c
@@ -391,6 +391,7 @@ static bool X11_VideoInit(SDL_VideoDevice *_this)
     GET_ATOM(SDL_FORMATS);
     GET_ATOM(XdndAware);
     GET_ATOM(XdndEnter);
+    GET_ATOM(XdndLeave);
     GET_ATOM(XdndPosition);
     GET_ATOM(XdndStatus);
     GET_ATOM(XdndTypeList);
diff --git a/src/video/x11/SDL_x11video.h b/src/video/x11/SDL_x11video.h
index 9509ba70f8c0f..09dfe854c7b85 100644
--- a/src/video/x11/SDL_x11video.h
+++ b/src/video/x11/SDL_x11video.h
@@ -103,6 +103,7 @@ struct SDL_VideoData
         Atom SDL_FORMATS;
         Atom XdndAware;
         Atom XdndEnter;
+        Atom XdndLeave;
         Atom XdndPosition;
         Atom XdndStatus;
         Atom XdndTypeList;