Atlas - SDL_cocoaclipboard.m
Home / ext / SDL / src / video / cocoa Lines: 1 | Size: 9822 bytes [Download] [Show on GitHub] [Search similar files] [Raw] [Raw (proxy)][FILE BEGIN]1/* 2 Simple DirectMedia Layer 3 Copyright (C) 1997-2025 Sam Lantinga <[email protected]> 4 5 This software is provided 'as-is', without any express or implied 6 warranty. In no event will the authors be held liable for any damages 7 arising from the use of this software. 8 9 Permission is granted to anyone to use this software for any purpose, 10 including commercial applications, and to alter it and redistribute it 11 freely, subject to the following restrictions: 12 13 1. The origin of this software must not be misrepresented; you must not 14 claim that you wrote the original software. If you use this software 15 in a product, an acknowledgment in the product documentation would be 16 appreciated but is not required. 17 2. Altered source versions must be plainly marked as such, and must not be 18 misrepresented as being the original software. 19 3. This notice may not be removed or altered from any source distribution. 20*/ 21#include "SDL_internal.h" 22 23#ifdef SDL_VIDEO_DRIVER_COCOA 24 25#include "SDL_cocoavideo.h" 26#include "../../events/SDL_events_c.h" 27#include "../../events/SDL_clipboardevents_c.h" 28 29#include <UniformTypeIdentifiers/UniformTypeIdentifiers.h> 30 31@interface Cocoa_PasteboardDataProvider : NSObject<NSPasteboardItemDataProvider> 32{ 33 SDL_ClipboardDataCallback m_callback; 34 void *m_userdata; 35} 36@end 37 38@implementation Cocoa_PasteboardDataProvider 39 40- (nullable instancetype)initWith:(SDL_ClipboardDataCallback)callback 41 userData:(void *)userdata 42{ 43 self = [super init]; 44 if (!self) { 45 return self; 46 } 47 m_callback = callback; 48 m_userdata = userdata; 49 return self; 50} 51 52- (void)pasteboard:(NSPasteboard *)pasteboard 53 item:(NSPasteboardItem *)item 54provideDataForType:(NSPasteboardType)type 55{ 56 @autoreleasepool { 57 size_t size = 0; 58 CFStringRef mimeType; 59 const void *callbackData; 60 NSData *data; 61 mimeType = UTTypeCopyPreferredTagWithClass((__bridge CFStringRef)type, kUTTagClassMIMEType); 62 callbackData = m_callback(m_userdata, [(__bridge NSString *)mimeType UTF8String], &size); 63 CFRelease(mimeType); 64 if (callbackData == NULL || size == 0) { 65 return; 66 } 67 data = [NSData dataWithBytes: callbackData length: size]; 68 [item setData: data forType: type]; 69 } 70} 71 72@end 73 74static char **GetMimeTypes(int *pnformats) 75{ 76 char **new_mime_types = NULL; 77 78 *pnformats = 0; 79 80 int nformats = 0; 81 int formatsSz = 0; 82 NSArray<NSPasteboardItem *> *items = [[NSPasteboard generalPasteboard] pasteboardItems]; 83 NSUInteger nitems = [items count]; 84 if (nitems > 0) { 85 for (NSPasteboardItem *item in items) { 86 NSArray<NSString *> *types = [item types]; 87 for (NSString *type in types) { 88 if (@available(macOS 11.0, *)) { 89 UTType *uttype = [UTType typeWithIdentifier:type]; 90 NSString *mime_type = [uttype preferredMIMEType]; 91 if (mime_type) { 92 NSUInteger len = [mime_type lengthOfBytesUsingEncoding:NSUTF8StringEncoding] + 1; 93 formatsSz += len; 94 ++nformats; 95 } 96 } 97 NSUInteger len = [type lengthOfBytesUsingEncoding:NSUTF8StringEncoding] + 1; 98 formatsSz += len; 99 ++nformats; 100 } 101 } 102 103 new_mime_types = SDL_AllocateTemporaryMemory((nformats + 1) * sizeof(char *) + formatsSz); 104 if (new_mime_types) { 105 int i = 0; 106 char *strPtr = (char *)(new_mime_types + nformats + 1); 107 for (NSPasteboardItem *item in items) { 108 NSArray<NSString *> *types = [item types]; 109 for (NSString *type in types) { 110 if (@available(macOS 11.0, *)) { 111 UTType *uttype = [UTType typeWithIdentifier:type]; 112 NSString *mime_type = [uttype preferredMIMEType]; 113 if (mime_type) { 114 NSUInteger len = [mime_type lengthOfBytesUsingEncoding:NSUTF8StringEncoding] + 1; 115 SDL_memcpy(strPtr, [mime_type UTF8String], len); 116 new_mime_types[i++] = strPtr; 117 strPtr += len; 118 } 119 } 120 NSUInteger len = [type lengthOfBytesUsingEncoding:NSUTF8StringEncoding] + 1; 121 SDL_memcpy(strPtr, [type UTF8String], len); 122 new_mime_types[i++] = strPtr; 123 strPtr += len; 124 } 125 } 126 127 new_mime_types[nformats] = NULL; 128 *pnformats = nformats; 129 } 130 } 131 return new_mime_types; 132} 133 134 135void Cocoa_CheckClipboardUpdate(SDL_CocoaVideoData *data) 136{ 137 @autoreleasepool { 138 NSPasteboard *pasteboard; 139 NSInteger count; 140 141 pasteboard = [NSPasteboard generalPasteboard]; 142 count = [pasteboard changeCount]; 143 if (count != data.clipboard_count) { 144 if (count) { 145 int nformats = 0; 146 char **new_mime_types = GetMimeTypes(&nformats); 147 if (new_mime_types) { 148 SDL_SendClipboardUpdate(false, new_mime_types, nformats); 149 } 150 } 151 data.clipboard_count = count; 152 } 153 } 154} 155 156bool Cocoa_SetClipboardData(SDL_VideoDevice *_this) 157{ 158 @autoreleasepool { 159 SDL_CocoaVideoData *data = (__bridge SDL_CocoaVideoData *)_this->internal; 160 NSPasteboard *pasteboard = [NSPasteboard generalPasteboard]; 161 162 // SetClipboardText specialization so text is available after the app quits 163 if (_this->clipboard_callback && _this->num_clipboard_mime_types == 1) { 164 if (SDL_strncmp(_this->clipboard_mime_types[0], "text/plain;charset=utf-8", 24) == 0) { 165 [pasteboard declareTypes:@[ NSPasteboardTypeString ] owner:nil]; 166 [pasteboard setString:@((char *)_this->clipboard_userdata) forType:NSPasteboardTypeString]; 167 data.clipboard_count = [pasteboard changeCount]; 168 return true; 169 } 170 } 171 172 NSPasteboardItem *newItem = [NSPasteboardItem new]; 173 NSMutableArray *utiTypes = [NSMutableArray new]; 174 Cocoa_PasteboardDataProvider *provider = [[Cocoa_PasteboardDataProvider alloc] initWith: _this->clipboard_callback userData: _this->clipboard_userdata]; 175 BOOL itemResult = FALSE; 176 BOOL writeResult = FALSE; 177 178 if (_this->clipboard_callback) { 179 for (int i = 0; i < _this->num_clipboard_mime_types; i++) { 180 CFStringRef mimeType = CFStringCreateWithCString(NULL, _this->clipboard_mime_types[i], kCFStringEncodingUTF8); 181 CFStringRef utiType = UTTypeCreatePreferredIdentifierForTag(kUTTagClassMIMEType, mimeType, NULL); 182 CFRelease(mimeType); 183 184 [utiTypes addObject: (__bridge NSString *)utiType]; 185 CFRelease(utiType); 186 } 187 itemResult = [newItem setDataProvider: provider forTypes: utiTypes]; 188 if (itemResult == FALSE) { 189 return SDL_SetError("Unable to set clipboard item data"); 190 } 191 192 [pasteboard clearContents]; 193 writeResult = [pasteboard writeObjects: @[newItem]]; 194 if (writeResult == FALSE) { 195 return SDL_SetError("Unable to set clipboard data"); 196 } 197 } else { 198 [pasteboard clearContents]; 199 } 200 data.clipboard_count = [pasteboard changeCount]; 201 } 202 return true; 203} 204 205static bool IsMimeType(const char *tag) 206{ 207 if (SDL_strchr(tag, '/')) { 208 // MIME types have slashes 209 return true; 210 } else if (SDL_strchr(tag, '.')) { 211 // UTI identifiers have periods 212 return false; 213 } else { 214 // Not sure, but it's not a UTI identifier 215 return true; 216 } 217} 218 219static CFStringRef GetUTIType(const char *tag) 220{ 221 CFStringRef utiType; 222 if (IsMimeType(tag)) { 223 CFStringRef mimeType = CFStringCreateWithCString(NULL, tag, kCFStringEncodingUTF8); 224 utiType = UTTypeCreatePreferredIdentifierForTag(kUTTagClassMIMEType, mimeType, NULL); 225 CFRelease(mimeType); 226 } else { 227 utiType = CFStringCreateWithCString(NULL, tag, kCFStringEncodingUTF8); 228 } 229 return utiType; 230} 231 232void *Cocoa_GetClipboardData(SDL_VideoDevice *_this, const char *mime_type, size_t *size) 233{ 234 @autoreleasepool { 235 NSPasteboard *pasteboard = [NSPasteboard generalPasteboard]; 236 void *data = NULL; 237 *size = 0; 238 for (NSPasteboardItem *item in [pasteboard pasteboardItems]) { 239 NSData *itemData; 240 CFStringRef utiType = GetUTIType(mime_type); 241 itemData = [item dataForType: (__bridge NSString *)utiType]; 242 CFRelease(utiType); 243 if (itemData != nil) { 244 NSUInteger length = [itemData length]; 245 *size = (size_t)length; 246 data = SDL_malloc(*size + sizeof(Uint32)); 247 if (data) { 248 [itemData getBytes: data length: length]; 249 SDL_memset((Uint8 *)data + length, 0, sizeof(Uint32)); 250 } 251 break; 252 } 253 } 254 return data; 255 } 256} 257 258bool Cocoa_HasClipboardData(SDL_VideoDevice *_this, const char *mime_type) 259{ 260 bool result = false; 261 @autoreleasepool { 262 NSPasteboard *pasteboard = [NSPasteboard generalPasteboard]; 263 CFStringRef utiType = GetUTIType(mime_type); 264 if ([pasteboard canReadItemWithDataConformingToTypes: @[(__bridge NSString *)utiType]]) { 265 result = true; 266 } 267 CFRelease(utiType); 268 } 269 return result; 270 271} 272 273#endif // SDL_VIDEO_DRIVER_COCOA 274[FILE END](C) 2025 0x4248 (C) 2025 4248 Media and 4248 Systems, All part of 0x4248 See LICENCE files for more information. Not all files are by 0x4248 always check Licencing.