Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
93 changes: 92 additions & 1 deletion src/video/cocoa/SDL_cocoaclipboard.m
Original file line number Diff line number Diff line change
Expand Up @@ -71,15 +71,69 @@ - (void)pasteboard:(NSPasteboard *)pasteboard

@end

static void *GetTextURIsFromURLs(NSPasteboard *pasteboard, size_t *size)
{
@autoreleasepool {
NSDictionary *options = @{NSPasteboardURLReadingFileURLsOnlyKey: @YES};
NSArray<NSURL *> *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<NSPasteboardItem *> *items = [[NSPasteboard generalPasteboard] pasteboardItems];
NSDictionary *options = @{NSPasteboardURLReadingFileURLsOnlyKey : @YES};
const bool has_urls = [pasteboard canReadObjectForClasses:@[ [NSURL class] ] options:options];
NSArray<NSPasteboardItem *> *items = [pasteboard pasteboardItems];
bool has_text_uri = false;
NSUInteger nitems = [items count];
if (nitems > 0) {
for (NSPasteboardItem *item in items) {
Expand All @@ -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;
Expand All @@ -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;
Expand All @@ -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;
}
Expand Down Expand Up @@ -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++) {
Expand All @@ -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) {
Expand All @@ -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];
}
Expand Down Expand Up @@ -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;
}
}
Expand All @@ -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;

Expand Down
Loading