diff --git a/README.md b/README.md index 06fa7d2d..d5916f58 100644 --- a/README.md +++ b/README.md @@ -46,6 +46,7 @@ The following dependencies are needed by the examples and test programs: ## Changelog - Added native monitor handle access to native API + - Added `glfwSetDropCallback` and `GLFWdropfun` for receiving dropped files - [Cocoa] Bugfix: Using a 1x1 cursor for hidden mode caused some screen recorders to fail diff --git a/include/GLFW/glfw3.h b/include/GLFW/glfw3.h index b229bb9e..c94c3481 100644 --- a/include/GLFW/glfw3.h +++ b/include/GLFW/glfw3.h @@ -779,13 +779,14 @@ typedef void (* GLFWcharfun)(GLFWwindow*,unsigned int); * This is the function signature for drop callbacks. * * @param[in] window The window that received the event. - * @param[in] string The string descriptor for the dropped object. + * @param[in] count The number of dropped objects. + * @param[in] names The names of the dropped object. * * @sa glfwSetDropCallback * * @ingroup input */ -typedef void (* GLFWdropfun)(GLFWwindow*,const char*); +typedef void (* GLFWdropfun)(GLFWwindow*,int,const char**); /*! @brief The function signature for monitor configuration callbacks. * diff --git a/src/cocoa_window.m b/src/cocoa_window.m index 4b14ef4a..0af84e00 100644 --- a/src/cocoa_window.m +++ b/src/cocoa_window.m @@ -413,8 +413,6 @@ static int translateKey(unsigned int key) @interface GLFWContentView : NSView { _GLFWwindow* window; - char * fileNamesForDrag; - int fileNamesSize; NSTrackingArea* trackingArea; } @@ -446,9 +444,6 @@ static int translateKey(unsigned int key) window = initWindow; trackingArea = nil; - fileNamesForDrag = malloc(1024); - fileNamesSize = 1024; - [self updateTrackingAreas]; [self registerForDraggedTypes:[NSArray arrayWithObjects: NSFilenamesPboardType, nil]]; @@ -460,7 +455,6 @@ static int translateKey(unsigned int key) -(void)dealloc { [trackingArea release]; - free(fileNamesForDrag); [super dealloc]; } @@ -696,38 +690,27 @@ static int translateKey(unsigned int key) NSPasteboard* pasteboard = [sender draggingPasteboard]; NSArray* files = [pasteboard propertyListForType:NSFilenamesPboardType]; - // set the first char to 0 so strcat - // starts to add from the beginning - fileNamesForDrag[0] = 0; - - const int dragX = [sender draggingLocation].x; - const int dragY = [sender draggingLocation].y; - int dragSize = 1; - - if ([files count]) - { - NSEnumerator* filenameEnum = [files objectEnumerator]; - NSString* name; - - while (name = [filenameEnum nextObject]) - { - dragSize += [name length] + 1; - - if (dragSize > fileNamesSize) - { - fileNamesSize *= 2; - fileNamesForDrag = realloc(fileNamesForDrag, fileNamesSize); - } - - strcat(fileNamesForDrag, [name UTF8String]); - strcat(fileNamesForDrag, "\n"); - } - } - int height; _glfwPlatformGetWindowSize(window, NULL, &height); - _glfwInputCursorMotion(window, dragX, height - dragY); - _glfwInputDrop(window, fileNamesForDrag); + _glfwInputCursorMotion(window, + [sender draggingLocation].x, + height - [sender draggingLocation].y); + + const int count = [files count]; + if (count) + { + NSEnumerator* e = [files objectEnumerator]; + char** names = calloc(count, sizeof(char*)); + + for (int i = 0; i < count; i++) + names[i] = strdup([[e nextObject] UTF8String]); + + _glfwInputDrop(window, count, (const char**) names); + + for (int i = 0; i < count; i++) + free(names[i]); + free(names); + } return YES; } diff --git a/src/input.c b/src/input.c index 2fd23a12..642c37f8 100644 --- a/src/input.c +++ b/src/input.c @@ -211,10 +211,10 @@ void _glfwInputCursorEnter(_GLFWwindow* window, int entered) window->callbacks.cursorEnter((GLFWwindow*) window, entered); } -void _glfwInputDrop(_GLFWwindow* window, const char* dropString) +void _glfwInputDrop(_GLFWwindow* window, int count, const char** names) { if (window->callbacks.drop) - window->callbacks.drop((GLFWwindow*) window, dropString); + window->callbacks.drop((GLFWwindow*) window, count, names); } diff --git a/src/internal.h b/src/internal.h index 08f849de..a2970378 100644 --- a/src/internal.h +++ b/src/internal.h @@ -681,11 +681,11 @@ void _glfwInputError(int error, const char* format, ...); /*! @brief Notifies dropped object over window. * @param[in] window The window that received the event. - * @param[in] dropString The string descriptor of the dropped object - * description. + * @param[in] count The number of dropped objects. + * @param[in] names The names of the dropped objects. * @ingroup event */ -void _glfwInputDrop(_GLFWwindow* window, const char* dropString); +void _glfwInputDrop(_GLFWwindow* window, int count, const char** names); //======================================================================== diff --git a/src/win32_init.c b/src/win32_init.c index 2063000e..aae46a9b 100644 --- a/src/win32_init.c +++ b/src/win32_init.c @@ -215,9 +215,6 @@ int _glfwPlatformInit(void) if (!_glfwInitContextAPI()) return GL_FALSE; - _glfw.win32.dropString = malloc(1000); - _glfw.win32.dropStringSize = 1000; - _glfwInitTimer(); _glfwInitJoysticks(); diff --git a/src/win32_platform.h b/src/win32_platform.h index 723da793..11d774b7 100644 --- a/src/win32_platform.h +++ b/src/win32_platform.h @@ -169,8 +169,6 @@ typedef struct _GLFWlibraryWin32 ATOM classAtom; DWORD foregroundLockTimeout; char* clipboardString; - char* dropString; - int dropStringSize; // Timer data struct { diff --git a/src/win32_window.c b/src/win32_window.c index bcf82e1c..32e9376f 100644 --- a/src/win32_window.c +++ b/src/win32_window.c @@ -751,38 +751,34 @@ static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg, case WM_DROPFILES: { - WCHAR szName[MAX_PATH]; HDROP hDrop = (HDROP) wParam; POINT pt; - const int numFiles = DragQueryFile(hDrop, 0xffffffff, szName, MAX_PATH); - int i, currentSize = 1; - char* utf8str; + int i; + + const int count = DragQueryFile(hDrop, 0xffffffff, NULL, 0); + char** names = calloc(count, sizeof(char*)); // Move the mouse to the position of the drop DragQueryPoint(hDrop, &pt); _glfwInputCursorMotion(window, pt.x, pt.y); - memset(_glfw.win32.dropString, 0, _glfw.win32.dropStringSize); - - for (i = 0; i < numFiles; i++) + for (i = 0; i < count; i++) { - DragQueryFile(hDrop, i, szName, MAX_PATH); - utf8str = _glfwCreateUTF8FromWideString((const WCHAR*) szName); - currentSize += strlen(utf8str); + const UINT length = DragQueryFile(hDrop, i, NULL, 0); + WCHAR* buffer = calloc(length + 1, sizeof(WCHAR)); - if (_glfw.win32.dropStringSize < currentSize) - { - _glfw.win32.dropStringSize *= 2; - _glfw.win32.dropString = realloc(_glfw.win32.dropString, - _glfw.win32.dropStringSize); - } + DragQueryFile(hDrop, i, buffer, length + 1); + names[i] = _glfwCreateUTF8FromWideString(buffer); - strcat(_glfw.win32.dropString, utf8str); - strcat(_glfw.win32.dropString, "\n"); - free(utf8str); + free(buffer); } - _glfwInputDrop(window,_glfw.win32.dropString); + _glfwInputDrop(window, count, (const char**) names); + + for (i = 0; i < count; i++) + free(names[i]); + free(names); + DragFinish(hDrop); break; } diff --git a/src/x11_platform.h b/src/x11_platform.h index 95682cf0..603ad817 100644 --- a/src/x11_platform.h +++ b/src/x11_platform.h @@ -132,10 +132,6 @@ typedef struct _GLFWlibraryX11 struct { Window sourceWindow; - char* string; - char* type1; - char* type2; - char* type3; } xdnd; // Selection atoms diff --git a/src/x11_window.c b/src/x11_window.c index 31c13a8b..77419279 100644 --- a/src/x11_window.c +++ b/src/x11_window.c @@ -97,6 +97,51 @@ static int translateChar(XKeyEvent* event) return (int) _glfwKeySym2Unicode(keysym); } +// Splits a text/uri-list into separate file paths +// +static char** splitUriList(char* text, int* count) +{ + const char* prefix = "file://"; + char** names = NULL; + char* line; + + *count = 0; + + while ((line = strtok(text, "\r\n"))) + { + text = NULL; + + if (*line == '#') + continue; + + if (strncmp(line, prefix, strlen(prefix)) == 0) + line += strlen(prefix); + + (*count)++; + + char* name = calloc(strlen(line) + 1, 1); + names = realloc(names, *count * sizeof(char*)); + names[*count - 1] = name; + + while (*line) + { + if (line[0] == '%' && line[1] && line[2]) + { + const char digits[3] = { line[1], line[2], '\0' }; + *name = strtol(digits, NULL, 16); + line += 2; + } + else + *name = *line; + + name++; + line++; + } + } + + return names; +} + // Create the X11 window (and its colormap) // static GLboolean createWindow(_GLFWwindow* window, @@ -714,7 +759,6 @@ static void processEvent(XEvent *event) False, SubstructureNotifyMask | SubstructureRedirectMask, event); - } else if (event->xclient.message_type == _glfw.x11.XdndEnter) { @@ -733,9 +777,6 @@ static void processEvent(XEvent *event) _glfw.x11.XdndSelection, window->x11.handle, CurrentTime); } - else if (event->xclient.message_type == _glfw.x11.XdndLeave) - { - } else if (event->xclient.message_type == _glfw.x11.XdndPosition) { // Xdnd Position: get coordinates of the mouse inside the window @@ -771,15 +812,12 @@ static void processEvent(XEvent *event) case SelectionNotify: { - if (event->xselection.property != None) + if (event->xselection.property) { // Xdnd: got a selection notification from the conversion // we asked for, get the data and finish the d&d event char* data; - free(_glfw.x11.xdnd.string); - _glfw.x11.xdnd.string = NULL; - const int result = _glfwGetWindowProperty(event->xselection.requestor, event->xselection.property, event->xselection.target, @@ -787,30 +825,18 @@ static void processEvent(XEvent *event) if (result) { - // Nautilus seems to add a \r at the end of the paths - // remove it so paths can be directly used - _glfw.x11.xdnd.string = malloc(strlen(data) + 1); - char *to = _glfw.x11.xdnd.string; - const char *from = data; - const char *current = strchr(from, '\r'); + int i, count; + char** names = splitUriList(data, &count); - while (current) - { - const int charsToCopy = current - from; - memcpy(to, from, (size_t) charsToCopy); - to += charsToCopy; + _glfwInputDrop(window, count, (const char**) names); - from = current + 1; - current = strchr(from, '\r'); - } - - const size_t remaining = strlen(from); - - memcpy(to, from, remaining); - to += remaining; - *to = 0; + for (i = 0; i < count; i++) + free(names[i]); + free(names); } + XFree(data); + XEvent reply; memset(&reply, 0, sizeof(reply)); @@ -825,11 +851,7 @@ static void processEvent(XEvent *event) // Reply that all is well XSendEvent(_glfw.x11.display, _glfw.x11.xdnd.sourceWindow, False, NoEventMask, &reply); - XSync(_glfw.x11.display, False); - XFree(data); - - if (result) - _glfwInputDrop(window, _glfw.x11.xdnd.string); + XFlush(_glfw.x11.display); } break; diff --git a/tests/events.c b/tests/events.c index d5e2ce49..28c70c27 100644 --- a/tests/events.c +++ b/tests/events.c @@ -381,12 +381,14 @@ static void char_callback(GLFWwindow* window, unsigned int codepoint) get_character_string(codepoint)); } -static void drop_callback(GLFWwindow* window, const char* descriptor) +static void drop_callback(GLFWwindow* window, int count, const char** names) { - printf("%08x at %0.3f: Drop of \"%s\" input\n", - counter++, - glfwGetTime(), - descriptor); + int i; + + printf("%08x at %0.3f: Drop input\n", counter++, glfwGetTime()); + + for (i = 0; i < count; i++) + printf(" %i: \"%s\"\n", i, names[i]); } void monitor_callback(GLFWmonitor* monitor, int event)