summaryrefslogtreecommitdiff
path: root/contrib/SDL-3.2.8/src/video/cocoa/SDL_cocoaclipboard.m
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/SDL-3.2.8/src/video/cocoa/SDL_cocoaclipboard.m')
-rw-r--r--contrib/SDL-3.2.8/src/video/cocoa/SDL_cocoaclipboard.m262
1 files changed, 262 insertions, 0 deletions
diff --git a/contrib/SDL-3.2.8/src/video/cocoa/SDL_cocoaclipboard.m b/contrib/SDL-3.2.8/src/video/cocoa/SDL_cocoaclipboard.m
new file mode 100644
index 0000000..42c2ad6
--- /dev/null
+++ b/contrib/SDL-3.2.8/src/video/cocoa/SDL_cocoaclipboard.m
@@ -0,0 +1,262 @@
1/*
2 Simple DirectMedia Layer
3 Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
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 NSPasteboardItem *newItem = [NSPasteboardItem new];
162 NSMutableArray *utiTypes = [NSMutableArray new];
163 Cocoa_PasteboardDataProvider *provider = [[Cocoa_PasteboardDataProvider alloc] initWith: _this->clipboard_callback userData: _this->clipboard_userdata];
164 BOOL itemResult = FALSE;
165 BOOL writeResult = FALSE;
166
167 if (_this->clipboard_callback) {
168 for (int i = 0; i < _this->num_clipboard_mime_types; i++) {
169 CFStringRef mimeType = CFStringCreateWithCString(NULL, _this->clipboard_mime_types[i], kCFStringEncodingUTF8);
170 CFStringRef utiType = UTTypeCreatePreferredIdentifierForTag(kUTTagClassMIMEType, mimeType, NULL);
171 CFRelease(mimeType);
172
173 [utiTypes addObject: (__bridge NSString *)utiType];
174 CFRelease(utiType);
175 }
176 itemResult = [newItem setDataProvider: provider forTypes: utiTypes];
177 if (itemResult == FALSE) {
178 return SDL_SetError("Unable to set clipboard item data");
179 }
180
181 [pasteboard clearContents];
182 writeResult = [pasteboard writeObjects: @[newItem]];
183 if (writeResult == FALSE) {
184 return SDL_SetError("Unable to set clipboard data");
185 }
186 } else {
187 [pasteboard clearContents];
188 }
189 data.clipboard_count = [pasteboard changeCount];
190 }
191 return true;
192}
193
194static bool IsMimeType(const char *tag)
195{
196 if (SDL_strchr(tag, '/')) {
197 // MIME types have slashes
198 return true;
199 } else if (SDL_strchr(tag, '.')) {
200 // UTI identifiers have periods
201 return false;
202 } else {
203 // Not sure, but it's not a UTI identifier
204 return true;
205 }
206}
207
208static CFStringRef GetUTIType(const char *tag)
209{
210 CFStringRef utiType;
211 if (IsMimeType(tag)) {
212 CFStringRef mimeType = CFStringCreateWithCString(NULL, tag, kCFStringEncodingUTF8);
213 utiType = UTTypeCreatePreferredIdentifierForTag(kUTTagClassMIMEType, mimeType, NULL);
214 CFRelease(mimeType);
215 } else {
216 utiType = CFStringCreateWithCString(NULL, tag, kCFStringEncodingUTF8);
217 }
218 return utiType;
219}
220
221void *Cocoa_GetClipboardData(SDL_VideoDevice *_this, const char *mime_type, size_t *size)
222{
223 @autoreleasepool {
224 NSPasteboard *pasteboard = [NSPasteboard generalPasteboard];
225 void *data = NULL;
226 *size = 0;
227 for (NSPasteboardItem *item in [pasteboard pasteboardItems]) {
228 NSData *itemData;
229 CFStringRef utiType = GetUTIType(mime_type);
230 itemData = [item dataForType: (__bridge NSString *)utiType];
231 CFRelease(utiType);
232 if (itemData != nil) {
233 NSUInteger length = [itemData length];
234 *size = (size_t)length;
235 data = SDL_malloc(*size + sizeof(Uint32));
236 if (data) {
237 [itemData getBytes: data length: length];
238 SDL_memset((Uint8 *)data + length, 0, sizeof(Uint32));
239 }
240 break;
241 }
242 }
243 return data;
244 }
245}
246
247bool Cocoa_HasClipboardData(SDL_VideoDevice *_this, const char *mime_type)
248{
249 bool result = false;
250 @autoreleasepool {
251 NSPasteboard *pasteboard = [NSPasteboard generalPasteboard];
252 CFStringRef utiType = GetUTIType(mime_type);
253 if ([pasteboard canReadItemWithDataConformingToTypes: @[(__bridge NSString *)utiType]]) {
254 result = true;
255 }
256 CFRelease(utiType);
257 }
258 return result;
259
260}
261
262#endif // SDL_VIDEO_DRIVER_COCOA