From a946a344524ed4934acf180255337ea96b362351 Mon Sep 17 00:00:00 2001 From: Sylvain Date: Mon, 6 Mar 2023 11:16:18 +0100 Subject: [PATCH] Add Drag and drop position, for x11, wayland and MACOSX --- include/SDL3/SDL_events.h | 7 +++-- src/events/SDL_dropevents.c | 26 +++++++++++++++--- src/events/SDL_dropevents_c.h | 1 + src/video/cocoa/SDL_cocoawindow.m | 16 +++++++++++ src/video/wayland/SDL_waylandevents.c | 26 ++++++++++++++++++ src/video/x11/SDL_x11events.c | 12 +++++++++ test/testdropfile.c | 39 ++++++++++++++++++++------- 7 files changed, 112 insertions(+), 15 deletions(-) diff --git a/include/SDL3/SDL_events.h b/include/SDL3/SDL_events.h index 8e35e283ef..482fc6d3cc 100644 --- a/include/SDL3/SDL_events.h +++ b/include/SDL3/SDL_events.h @@ -172,6 +172,7 @@ typedef enum SDL_EVENT_DROP_TEXT, /**< text/plain drag-and-drop event */ SDL_EVENT_DROP_BEGIN, /**< A new set of drops is beginning (NULL filename) */ SDL_EVENT_DROP_COMPLETE, /**< Current set of drops is now complete (NULL filename) */ + SDL_EVENT_DROP_POSITION, /**< Position while moving over the window */ /* Audio hotplug events */ SDL_EVENT_AUDIO_DEVICE_ADDED = 0x1100, /**< A new audio device is available */ @@ -515,10 +516,12 @@ typedef struct SDL_TouchFingerEvent */ typedef struct SDL_DropEvent { - Uint32 type; /**< ::SDL_EVENT_DROP_BEGIN or ::SDL_EVENT_DROP_FILE or ::SDL_EVENT_DROP_TEXT or ::SDL_EVENT_DROP_COMPLETE */ + Uint32 type; /**< ::SDL_EVENT_DROP_BEGIN or ::SDL_EVENT_DROP_FILE or ::SDL_EVENT_DROP_TEXT or ::SDL_EVENT_DROP_COMPLETE or ::SDL_EVENT_DROP_POSITION */ Uint64 timestamp; /**< In nanoseconds, populated using SDL_GetTicksNS() */ char *file; /**< The file name, which should be freed with SDL_free(), is NULL on begin/complete */ - SDL_WindowID windowID;/**< The window that was dropped on, if any */ + 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) */ } SDL_DropEvent; diff --git a/src/events/SDL_dropevents.c b/src/events/SDL_dropevents.c index bb303625ba..5aa4a99236 100644 --- a/src/events/SDL_dropevents.c +++ b/src/events/SDL_dropevents.c @@ -27,9 +27,11 @@ #include "../video/SDL_sysvideo.h" /* for SDL_Window internals. */ -static int SDL_SendDrop(SDL_Window *window, const SDL_EventType evtype, const char *data) +static int SDL_SendDrop(SDL_Window *window, const SDL_EventType evtype, const char *data, float x, float y) { static SDL_bool app_is_dropping = SDL_FALSE; + static float last_drop_x = 0; + static float last_drop_y = 0; int posted = 0; /* Post the event, if desired */ @@ -58,6 +60,13 @@ static int SDL_SendDrop(SDL_Window *window, const SDL_EventType evtype, const ch event.common.timestamp = 0; event.drop.file = data ? SDL_strdup(data) : NULL; event.drop.windowID = window ? window->id : 0; + + if (evtype == SDL_EVENT_DROP_POSITION) { + last_drop_x = x; + last_drop_y = y; + } + event.drop.x = last_drop_x; + event.drop.y = last_drop_y; posted = (SDL_PushEvent(&event) > 0); if (posted && (evtype == SDL_EVENT_DROP_COMPLETE)) { @@ -66,6 +75,9 @@ static int SDL_SendDrop(SDL_Window *window, const SDL_EventType evtype, const ch } else { app_is_dropping = SDL_FALSE; } + + last_drop_x = 0; + last_drop_y = 0; } } return posted; @@ -73,15 +85,21 @@ static int SDL_SendDrop(SDL_Window *window, const SDL_EventType evtype, const ch int SDL_SendDropFile(SDL_Window *window, const char *file) { - return SDL_SendDrop(window, SDL_EVENT_DROP_FILE, file); + return SDL_SendDrop(window, SDL_EVENT_DROP_FILE, file, 0, 0); +} + +int SDL_SendDropPosition(SDL_Window *window, const char *file, 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); } int SDL_SendDropText(SDL_Window *window, const char *text) { - return SDL_SendDrop(window, SDL_EVENT_DROP_TEXT, text); + return SDL_SendDrop(window, SDL_EVENT_DROP_TEXT, text, 0, 0); } int SDL_SendDropComplete(SDL_Window *window) { - return SDL_SendDrop(window, SDL_EVENT_DROP_COMPLETE, NULL); + return SDL_SendDrop(window, SDL_EVENT_DROP_COMPLETE, NULL, 0, 0); } diff --git a/src/events/SDL_dropevents_c.h b/src/events/SDL_dropevents_c.h index d10bbd3347..9a485cd0b2 100644 --- a/src/events/SDL_dropevents_c.h +++ b/src/events/SDL_dropevents_c.h @@ -24,6 +24,7 @@ #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_SendDropText(SDL_Window *window, const char *text); extern int SDL_SendDropComplete(SDL_Window *window); diff --git a/src/video/cocoa/SDL_cocoawindow.m b/src/video/cocoa/SDL_cocoawindow.m index fb4665fb48..fb0f00d6c2 100644 --- a/src/video/cocoa/SDL_cocoawindow.m +++ b/src/video/cocoa/SDL_cocoawindow.m @@ -85,6 +85,7 @@ /* Handle drag-and-drop of files onto the SDL window. */ - (NSDragOperation)draggingEntered:(id)sender; +- (NSDragOperation)draggingUpdated:(id)sender; - (BOOL)performDragOperation:(id)sender; - (BOOL)wantsPeriodicDraggingUpdates; - (BOOL)validateMenuItem:(NSMenuItem *)menuItem; @@ -158,6 +159,21 @@ return NSDragOperationNone; /* no idea what to do with this, reject it. */ } +- (NSDragOperation)draggingUpdated:(id)sender +{ + if (([sender draggingSourceOperationMask] & NSDragOperationGeneric) == NSDragOperationGeneric) { + SDL_Window *sdlwindow = [self findSDLWindow]; + NSPoint point = [sender draggingLocation]; + float x, y; + x = point.x; + y = (sdlwindow->h - point.y); + SDL_SendDropPosition(sdlwindow, NULL, x, y); /* FIXME, should we get the filename */ + return NSDragOperationGeneric; + } + + return NSDragOperationNone; /* no idea what to do with this, reject it. */ +} + - (BOOL)performDragOperation:(id)sender { @autoreleasepool { diff --git a/src/video/wayland/SDL_waylandevents.c b/src/video/wayland/SDL_waylandevents.c index 1d05b2cd45..d5a38c86bd 100644 --- a/src/video/wayland/SDL_waylandevents.c +++ b/src/video/wayland/SDL_waylandevents.c @@ -96,6 +96,8 @@ struct SDL_WaylandTouchPointList static struct SDL_WaylandTouchPointList touch_points = { NULL, NULL }; +static char *Wayland_URIToLocal(char *uri); + static void touch_add(SDL_TouchID id, float x, float y, struct wl_surface *surface) { struct SDL_WaylandTouchPoint *tp = SDL_malloc(sizeof(struct SDL_WaylandTouchPoint)); @@ -1845,6 +1847,30 @@ static void data_device_handle_leave(void *data, struct wl_data_device *wl_data_ static void data_device_handle_motion(void *data, struct wl_data_device *wl_data_device, uint32_t time, wl_fixed_t x, wl_fixed_t y) { + SDL_WaylandDataDevice *data_device = data; + + if (data_device->drag_offer != NULL) { + /* TODO: SDL Support more mime types */ + size_t length; + void *buffer = Wayland_data_offer_receive(data_device->drag_offer, + &length, FILE_MIME, SDL_TRUE); + if (buffer) { + char *saveptr = NULL; + char *token = SDL_strtokr((char *)buffer, "\r\n", &saveptr); + while (token != NULL) { + char *fn = Wayland_URIToLocal(token); + if (fn) { + double dx; + double dy; + dx = wl_fixed_to_double(x); + dy = wl_fixed_to_double(y); + SDL_SendDropPosition(data_device->dnd_window, fn, (float)dx, (float)dy); + } + token = SDL_strtokr(NULL, "\r\n", &saveptr); + } + SDL_free(buffer); + } + } } /* Decodes URI escape sequences in string buf of len bytes diff --git a/src/video/x11/SDL_x11events.c b/src/video/x11/SDL_x11events.c index 99014a8637..965a811740 100644 --- a/src/video/x11/SDL_x11events.c +++ b/src/video/x11/SDL_x11events.c @@ -1222,6 +1222,18 @@ static void X11_DispatchEvent(_THIS, XEvent *xevent) } printf("Action requested by user is : %s\n", X11_XGetAtomName(display, act)); #endif + { + /* Drag and Drop position */ + int root_x, root_y, window_x, window_y; + Window ChildReturn; + root_x = xevent->xclient.data.l[2] >> 16; + root_y = xevent->xclient.data.l[2] & 0xffff; + /* Translate from root to current window position */ + 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 ? */ + } /* reply with status */ SDL_memset(&m, 0, sizeof(XClientMessageEvent)); diff --git a/test/testdropfile.c b/test/testdropfile.c index 7d33ffe366..21b2838f81 100644 --- a/test/testdropfile.c +++ b/test/testdropfile.c @@ -29,6 +29,9 @@ int main(int argc, char *argv[]) { int i, done; SDL_Event event; + SDL_bool is_hover = SDL_FALSE; + float x = 0.0f, y = 0.0f; + unsigned int windowID = 0; /* Enable standard application logging */ SDL_LogSetPriority(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_INFO); @@ -60,12 +63,6 @@ int main(int argc, char *argv[]) quit(2); } - for (i = 0; i < state->num_windows; ++i) { - SDL_Renderer *renderer = state->renderers[i]; - SDL_SetRenderDrawColor(renderer, 0xA0, 0xA0, 0xA0, 0xFF); - SDL_RenderClear(renderer); - SDL_RenderPresent(renderer); - } SDL_SetEventEnabled(SDL_EVENT_DROP_FILE, SDL_TRUE); @@ -75,19 +72,43 @@ int main(int argc, char *argv[]) /* Check for events */ while (SDL_PollEvent(&event)) { if (event.type == SDL_EVENT_DROP_BEGIN) { - SDL_Log("Drop beginning on window %u", (unsigned int)event.drop.windowID); + SDL_Log("Drop beginning on window %u at (%f, %f)", (unsigned int)event.drop.windowID, event.drop.x, event.drop.y); } else if (event.type == SDL_EVENT_DROP_COMPLETE) { - SDL_Log("Drop complete on window %u", (unsigned int)event.drop.windowID); + is_hover = SDL_FALSE; + SDL_Log("Drop complete on window %u at (%f, %f)", (unsigned int)event.drop.windowID, event.drop.x, event.drop.y); } else if ((event.type == SDL_EVENT_DROP_FILE) || (event.type == SDL_EVENT_DROP_TEXT)) { const char *typestr = (event.type == SDL_EVENT_DROP_FILE) ? "File" : "Text"; char *dropped_filedir = event.drop.file; - SDL_Log("%s dropped on window %u: %s", typestr, (unsigned int)event.drop.windowID, dropped_filedir); + SDL_Log("%s dropped on window %u: %s at (%f, %f)", typestr, (unsigned int)event.drop.windowID, dropped_filedir, event.drop.x, event.drop.y); /* Normally you'd have to do this, but this is freed in SDLTest_CommonEvent() */ /*SDL_free(dropped_filedir);*/ + } else if (event.type == SDL_EVENT_DROP_POSITION) { + is_hover = SDL_TRUE; + x = event.drop.x; + y = event.drop.y; + windowID = event.drop.windowID; + SDL_Log("Drop position on window %u at (%f, %f) file = %s", (unsigned int)event.drop.windowID, event.drop.x, event.drop.y, event.drop.file); } SDLTest_CommonEvent(state, &event, &done); } + + for (i = 0; i < state->num_windows; ++i) { + SDL_Renderer *renderer = state->renderers[i]; + SDL_SetRenderDrawColor(renderer, 0xA0, 0xA0, 0xA0, 0xFF); + SDL_RenderClear(renderer); + if (is_hover) { + if (windowID == SDL_GetWindowID(SDL_GetRenderWindow(renderer))) { + int len = 2000; + SDL_SetRenderDrawColor(renderer, 0x0A, 0x0A, 0x0A, 0xFF); + SDL_RenderLine(renderer, x, y - len, x, y + len); + SDL_RenderLine(renderer, x - len, y, x + len, y); + } + } + SDL_RenderPresent(renderer); + } + + SDL_Delay(16); } quit(0);