From aea8a5a6273f68655fe369ea8b152de9a0e9b36d Mon Sep 17 00:00:00 2001 From: Frank Praznik Date: Fri, 16 Oct 2026 00:39:55 -0400 Subject: [PATCH 1/2] cocoa: Synthesize text/uri-list from pasteboard URLs If there is no explicit text/uri-list MIME type, and the pasteboard contains URLs, synthesize the text/uri-list contents from the URLs. --- src/video/cocoa/SDL_cocoaclipboard.m | 60 +++++++++++++++++++++++++++- 1 file changed, 59 insertions(+), 1 deletion(-) diff --git a/src/video/cocoa/SDL_cocoaclipboard.m b/src/video/cocoa/SDL_cocoaclipboard.m index 9f785c6be3a50..54bcd68f78295 100644 --- a/src/video/cocoa/SDL_cocoaclipboard.m +++ b/src/video/cocoa/SDL_cocoaclipboard.m @@ -71,15 +71,44 @@ - (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 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 +121,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 +132,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 +163,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; } @@ -252,6 +299,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 +318,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; From d56c568d89288f0fe4f03efb52495d360b2a27ee Mon Sep 17 00:00:00 2001 From: Frank Praznik Date: Fri, 16 Oct 2026 16:28:40 -0400 Subject: [PATCH 2/2] cocoa: Add text/uri-list entries to the pasteboard as URLs Add the file URLs from text/uri-list to the pasteboard as URLs. This allows pasting file paths into Finder. --- src/video/cocoa/SDL_cocoaclipboard.m | 33 ++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/src/video/cocoa/SDL_cocoaclipboard.m b/src/video/cocoa/SDL_cocoaclipboard.m index 54bcd68f78295..5c8f4ac110ab6 100644 --- a/src/video/cocoa/SDL_cocoaclipboard.m +++ b/src/video/cocoa/SDL_cocoaclipboard.m @@ -96,6 +96,31 @@ - (void)pasteboard:(NSPasteboard *)pasteboard } } +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]; @@ -221,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++) { @@ -230,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) { @@ -241,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]; }