diff --git a/src/video/cocoa/SDL_cocoaclipboard.m b/src/video/cocoa/SDL_cocoaclipboard.m index 9f785c6be3a50..5c8f4ac110ab6 100644 --- a/src/video/cocoa/SDL_cocoaclipboard.m +++ b/src/video/cocoa/SDL_cocoaclipboard.m @@ -71,15 +71,69 @@ - (void)pasteboard:(NSPasteboard *)pasteboard @end +static void *GetTextURIsFromURLs(NSPasteboard *pasteboard, size_t *size) +{ + @autoreleasepool { + NSDictionary *options = @{NSPasteboardURLReadingFileURLsOnlyKey: @YES}; + NSArray *urls = [pasteboard readObjectsForClasses:@[[NSURL class]] options:options]; + NSMutableString *string = [NSMutableString string]; + + for (NSURL *url in urls) { + [string appendFormat:@"%@\r\n", [[url filePathURL] absoluteString]]; + } + + if ([string length]) { + // Copy to the destination, trimming the trailing '/r/n'. + *size = [string length] - 1; + char *buf = SDL_malloc(*size); + if (buf) { + SDL_strlcpy(buf, [string UTF8String], *size); + return buf; + } + } + + return NULL; + } +} + +static void SetURLsFromTextURIs(SDL_VideoDevice *_this, NSPasteboard *pasteboard) +{ + @autoreleasepool { + size_t size = 0; + const char *urls = _this->clipboard_callback(_this->clipboard_userdata, "text/uri-list", &size); + if (urls) { + char *str = SDL_strdup(urls); + if (str) { + NSMutableArray *array = [NSMutableArray new]; + char *saveptr = NULL; + char *token = SDL_strtok_r(str, "\r\n", &saveptr); + while (token) { + NSURL *url = [NSURL URLWithString:[NSString stringWithUTF8String:token]]; + if (url && [url isFileURL]) { + [array addObject:url]; + } + token = SDL_strtok_r(NULL, "\r\n", &saveptr); + } + [pasteboard writeObjects:array]; + SDL_free(str); + } + } + } +} + static char **GetMimeTypes(int *pnformats) { + NSPasteboard *pasteboard = [NSPasteboard generalPasteboard]; char **new_mime_types = NULL; *pnformats = 0; int nformats = 0; int formatsSz = 0; - NSArray *items = [[NSPasteboard generalPasteboard] pasteboardItems]; + NSDictionary *options = @{NSPasteboardURLReadingFileURLsOnlyKey : @YES}; + const bool has_urls = [pasteboard canReadObjectForClasses:@[ [NSURL class] ] options:options]; + NSArray *items = [pasteboard pasteboardItems]; + bool has_text_uri = false; NSUInteger nitems = [items count]; if (nitems > 0) { for (NSPasteboardItem *item in items) { @@ -92,6 +146,9 @@ - (void)pasteboard:(NSPasteboard *)pasteboard NSUInteger len = [mime_type lengthOfBytesUsingEncoding:NSUTF8StringEncoding] + 1; formatsSz += len; ++nformats; + if (!has_text_uri && [mime_type isEqualToString:@"text/uri-list"]) { + has_text_uri = true; + } } } NSUInteger len = [type lengthOfBytesUsingEncoding:NSUTF8StringEncoding] + 1; @@ -100,6 +157,13 @@ - (void)pasteboard:(NSPasteboard *)pasteboard } } + // Synthesize the "text/uri-list" type from URLs, if only the latter is present. + if (has_urls && !has_text_uri) { + NSUInteger len = [@"text/uri-list" lengthOfBytesUsingEncoding:NSUTF8StringEncoding] + 1; + formatsSz += len; + ++nformats; + } + new_mime_types = SDL_AllocateTemporaryMemory((nformats + 1) * sizeof(char *) + formatsSz); if (new_mime_types) { int i = 0; @@ -124,6 +188,14 @@ - (void)pasteboard:(NSPasteboard *)pasteboard } } + if (has_urls && !has_text_uri) { + NSString *text_uri_list = @"text/uri-list"; + NSUInteger len = [text_uri_list lengthOfBytesUsingEncoding:NSUTF8StringEncoding] + 1; + SDL_memcpy(strPtr, [text_uri_list UTF8String], len); + new_mime_types[i++] = strPtr; + strPtr += len; + } + new_mime_types[nformats] = NULL; *pnformats = nformats; } @@ -174,6 +246,7 @@ bool Cocoa_SetClipboardData(SDL_VideoDevice *_this) Cocoa_PasteboardDataProvider *provider = [[Cocoa_PasteboardDataProvider alloc] initWith: _this->clipboard_callback userData: _this->clipboard_userdata]; BOOL itemResult = FALSE; BOOL writeResult = FALSE; + BOOL appendURLs = FALSE; if (_this->clipboard_callback) { for (int i = 0; i < _this->num_clipboard_mime_types; i++) { @@ -183,6 +256,10 @@ bool Cocoa_SetClipboardData(SDL_VideoDevice *_this) [utiTypes addObject: (__bridge NSString *)utiType]; CFRelease(utiType); + + if (SDL_strcmp(_this->clipboard_mime_types[i], "text/uri-list") == 0) { + appendURLs = TRUE; + } } itemResult = [newItem setDataProvider: provider forTypes: utiTypes]; if (itemResult == FALSE) { @@ -194,6 +271,9 @@ bool Cocoa_SetClipboardData(SDL_VideoDevice *_this) if (writeResult == FALSE) { return SDL_SetError("Unable to set clipboard data"); } + if (appendURLs) { + SetURLsFromTextURIs(_this, pasteboard); + } } else { [pasteboard clearContents]; } @@ -252,6 +332,11 @@ static CFStringRef GetUTIType(const char *tag) break; } } + + if (!data && SDL_strcmp(mime_type, "text/uri-list") == 0) { + // Try to synthesize text/uri-list entries from public.file-url. + data = GetTextURIsFromURLs(pasteboard, size); + } return data; } } @@ -266,6 +351,12 @@ bool Cocoa_HasClipboardData(SDL_VideoDevice *_this, const char *mime_type) result = true; } CFRelease(utiType); + + // If there's no text/uri-list type, see if URLs are available. + if (!result && SDL_strcmp(mime_type, "text/uri-list") == 0) { + NSDictionary *options = @{NSPasteboardURLReadingFileURLsOnlyKey: @YES}; + result = [pasteboard canReadObjectForClasses:@[ [NSURL class] ] options:options]; + } } return result;