From 8e855f2fbc7e50813daa8f6f0375dad0a3590fbf Mon Sep 17 00:00:00 2001 From: "Ryan C. Gordon" Date: Tue, 5 Jan 2016 01:42:00 -0500 Subject: [PATCH] Added SDL_DROPBEGIN and SDL_DROPCOMPLETE events, plus window IDs for drops. This allows an app to know when a set of drops are coming in a grouping of some sort (for example, a user selected multiple files and dropped them all on the window with a single drag), and when that set is complete. This also adds a window ID to the drop events, so the app can determine to which window a given drop was delivered. For application-level drops (for example, you launched an app by dropping a file on its icon), the window ID will be zero. --- include/SDL_events.h | 7 ++-- src/events/SDL_dropevents.c | 51 ++++++++++++++++++++++----- src/events/SDL_dropevents_c.h | 5 +-- src/video/SDL_sysvideo.h | 1 + src/video/cocoa/SDL_cocoaevents.m | 2 +- src/video/cocoa/SDL_cocoawindow.m | 16 ++++++++- src/video/windows/SDL_windowsevents.c | 3 +- src/video/x11/SDL_x11events.c | 6 ++-- test/testdropfile.c | 8 +++-- 9 files changed, 79 insertions(+), 20 deletions(-) diff --git a/include/SDL_events.h b/include/SDL_events.h index 04d6d88d1..edb89ef49 100644 --- a/include/SDL_events.h +++ b/include/SDL_events.h @@ -137,6 +137,8 @@ typedef enum /* Drag and drop events */ SDL_DROPFILE = 0x1000, /**< The system requests a file open */ SDL_DROPTEXT, /**< text/plain drag-and-drop event */ + SDL_DROPBEGIN, /**< A new set of drops is beginning (NULL filename) */ + SDL_DROPCOMPLETE, /**< Current set of drops is now complete (NULL filename) */ /* Audio hotplug events */ SDL_AUDIODEVICEADDED = 0x1100, /**< A new audio device is available */ @@ -462,9 +464,10 @@ typedef struct SDL_DollarGestureEvent */ typedef struct SDL_DropEvent { - Uint32 type; /**< ::SDL_DROPFILE */ + Uint32 type; /**< ::SDL_DROPBEGIN or ::SDL_DROPFILE or ::SDL_DROPTEXT or ::SDL_DROPCOMPLETE */ Uint32 timestamp; - char *file; /**< The file name, which should be freed with SDL_free() */ + char *file; /**< The file name, which should be freed with SDL_free(), is NULL on begin/complete */ + Uint32 windowID; /**< The window that was dropped on, if any */ } SDL_DropEvent; diff --git a/src/events/SDL_dropevents.c b/src/events/SDL_dropevents.c index ff68d7d20..ba4dcd6b0 100644 --- a/src/events/SDL_dropevents.c +++ b/src/events/SDL_dropevents.c @@ -26,34 +26,69 @@ #include "SDL_events_c.h" #include "SDL_dropevents_c.h" +#include "../video/SDL_sysvideo.h" /* for SDL_Window internals. */ + static int -SDL_SendDrop(const SDL_EventType evtype, const char *data) +SDL_SendDrop(SDL_Window *window, const SDL_EventType evtype, const char *data) { - int posted; + static SDL_bool app_is_dropping = SDL_FALSE; + int posted = 0; /* Post the event, if desired */ - posted = 0; if (SDL_GetEventState(evtype) == SDL_ENABLE) { + const SDL_bool need_begin = window ? !window->is_dropping : !app_is_dropping; SDL_Event event; + + if (need_begin) { + SDL_zero(event); + event.type = SDL_DROPBEGIN; + event.drop.windowID = window->id; + posted = (SDL_PushEvent(&event) > 0); + if (!posted) { + return 0; + } + if (window) { + window->is_dropping = SDL_TRUE; + } else { + app_is_dropping = SDL_TRUE; + } + } + SDL_zero(event); event.type = evtype; - event.drop.file = SDL_strdup(data); + event.drop.file = data ? SDL_strdup(data) : NULL; + event.drop.windowID = window ? window->id : 0; posted = (SDL_PushEvent(&event) > 0); + + if (posted && (evtype == SDL_DROPCOMPLETE)) { + if (window) { + window->is_dropping = SDL_FALSE; + } else { + app_is_dropping = SDL_FALSE; + } + } } return posted; } int -SDL_SendDropFile(const char *file) +SDL_SendDropFile(SDL_Window *window, const char *file) { - return SDL_SendDrop(SDL_DROPFILE, file); + return SDL_SendDrop(window, SDL_DROPFILE, file); } int -SDL_SendDropText(const char *text) +SDL_SendDropText(SDL_Window *window, const char *text) { - return SDL_SendDrop(SDL_DROPTEXT, text); + return SDL_SendDrop(window, SDL_DROPTEXT, text); } +int +SDL_SendDropComplete(SDL_Window *window) +{ + return SDL_SendDrop(window, SDL_DROPCOMPLETE, NULL); +} + + /* vi: set ts=4 sw=4 expandtab: */ diff --git a/src/events/SDL_dropevents_c.h b/src/events/SDL_dropevents_c.h index 73ca13994..a7adb8560 100644 --- a/src/events/SDL_dropevents_c.h +++ b/src/events/SDL_dropevents_c.h @@ -23,8 +23,9 @@ #ifndef _SDL_dropevents_c_h #define _SDL_dropevents_c_h -extern int SDL_SendDropFile(const char *file); -extern int SDL_SendDropText(const char *text); +extern int SDL_SendDropFile(SDL_Window *window, const char *file); +extern int SDL_SendDropText(SDL_Window *window, const char *text); +extern int SDL_SendDropComplete(SDL_Window *window); #endif /* _SDL_dropevents_c_h */ diff --git a/src/video/SDL_sysvideo.h b/src/video/SDL_sysvideo.h index 4ce883be6..09e5613bd 100644 --- a/src/video/SDL_sysvideo.h +++ b/src/video/SDL_sysvideo.h @@ -95,6 +95,7 @@ struct SDL_Window SDL_bool is_hiding; SDL_bool is_destroying; + SDL_bool is_dropping; /* drag/drop in progress, expecting SDL_SendDropComplete(). */ SDL_WindowShaper *shaper; diff --git a/src/video/cocoa/SDL_cocoaevents.m b/src/video/cocoa/SDL_cocoaevents.m index e0da11fd4..c0916fe85 100644 --- a/src/video/cocoa/SDL_cocoaevents.m +++ b/src/video/cocoa/SDL_cocoaevents.m @@ -176,7 +176,7 @@ - (BOOL)application:(NSApplication *)theApplication openFile:(NSString *)filename { - return (BOOL)SDL_SendDropFile([filename UTF8String]); + return (BOOL)SDL_SendDropFile(NULL, [filename UTF8String]) && SDL_SendDropComplete(NULL); } @end diff --git a/src/video/cocoa/SDL_cocoawindow.m b/src/video/cocoa/SDL_cocoawindow.m index 43f227ed8..a74d97f88 100644 --- a/src/video/cocoa/SDL_cocoawindow.m +++ b/src/video/cocoa/SDL_cocoawindow.m @@ -116,9 +116,12 @@ - (BOOL)performDragOperation:(id )sender { @autoreleasepool { + SDL_VideoDevice *_this = SDL_GetVideoDevice(); NSPasteboard *pasteboard = [sender draggingPasteboard]; NSArray *types = [NSArray arrayWithObject:NSFilenamesPboardType]; NSString *desiredType = [pasteboard availableTypeFromArray:types]; + SDL_Window *sdlwindow = nil; + if (desiredType == nil) { return NO; /* can't accept anything that's being dropped here. */ } @@ -157,11 +160,22 @@ } } - if (!SDL_SendDropFile([[fileURL path] UTF8String])) { + /* !!! FIXME: is there a better way to do this? */ + if (_this) { + for (sdlwindow = _this->windows; sdlwindow; sdlwindow = sdlwindow->next) { + NSWindow *nswindow = ((SDL_WindowData *) sdlwindow->driverdata)->nswindow; + if (nswindow == self) { + break; + } + } + } + + if (!SDL_SendDropFile(sdlwindow, [[fileURL path] UTF8String])) { return NO; } } + SDL_SendDropComplete(sdlwindow); return YES; }} diff --git a/src/video/windows/SDL_windowsevents.c b/src/video/windows/SDL_windowsevents.c index 61420894f..3fc99375a 100644 --- a/src/video/windows/SDL_windowsevents.c +++ b/src/video/windows/SDL_windowsevents.c @@ -907,12 +907,13 @@ 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(file); + SDL_SendDropFile(data->window, file); SDL_free(file); } SDL_stack_free(buffer); } } + SDL_SendDropComplete(data->window); DragFinish(drop); return 0; } diff --git a/src/video/x11/SDL_x11events.c b/src/video/x11/SDL_x11events.c index 0cfc37b56..312dd087b 100644 --- a/src/video/x11/SDL_x11events.c +++ b/src/video/x11/SDL_x11events.c @@ -1230,17 +1230,17 @@ X11_DispatchEvent(_THIS) char *token = strtok((char *) p.data, "\r\n"); while (token != NULL) { if (SDL_strcmp("text/plain", name)==0) { - SDL_SendDropText(token); + SDL_SendDropText(data->window, token); } else if (SDL_strcmp("text/uri-list", name)==0) { char *fn = X11_URIToLocal(token); if (fn) { - SDL_SendDropFile(fn); + SDL_SendDropFile(data->window, fn); } } token = strtok(NULL, "\r\n"); } + SDL_SendDropComplete(data->window); } - X11_XFree(p.data); /* send reply */ diff --git a/test/testdropfile.c b/test/testdropfile.c index 85d6a80fa..b729b2f64 100644 --- a/test/testdropfile.c +++ b/test/testdropfile.c @@ -77,10 +77,14 @@ main(int argc, char *argv[]) while (SDL_PollEvent(&event)) { SDLTest_CommonEvent(state, &event, &done); - if ((event.type == SDL_DROPFILE) || (event.type == SDL_DROPTEXT)) { + if (event.type == SDL_DROPBEGIN) { + SDL_Log("Drop beginning on window %u", (unsigned int) event.drop.windowID); + } else if (event.type == SDL_DROPCOMPLETE) { + SDL_Log("Drop complete on window %u", (unsigned int) event.drop.windowID); + } else if ((event.type == SDL_DROPFILE) || (event.type == SDL_DROPTEXT)) { const char *typestr = (event.type == SDL_DROPFILE) ? "File" : "Text"; char *dropped_filedir = event.drop.file; - SDL_Log("%s dropped on window: %s", typestr, dropped_filedir); + SDL_Log("%s dropped on window %u: %s", typestr, (unsigned int) event.drop.windowID, dropped_filedir); SDL_free(dropped_filedir); } }