mirror of
https://github.com/libsdl-org/SDL.git
synced 2025-10-15 06:16:00 +00:00
Synchronize clipboard mime types with external clipboard updates
Fixes https://github.com/libsdl-org/SDL/issues/8338 Fixes https://github.com/libsdl-org/SDL/issues/9587
This commit is contained in:
@@ -28,6 +28,12 @@
|
||||
|
||||
void SDL_SendClipboardUpdate(bool owner, char **mime_types, size_t num_mime_types)
|
||||
{
|
||||
if (!owner) {
|
||||
// Clear our internal clipboard contents when external clipboard is set
|
||||
SDL_CancelClipboardData(0);
|
||||
SDL_SaveClipboardMimeTypes((const char **)mime_types, num_mime_types);
|
||||
}
|
||||
|
||||
if (SDL_EventEnabled(SDL_EVENT_CLIPBOARD_UPDATE)) {
|
||||
SDL_Event event;
|
||||
event.type = SDL_EVENT_CLIPBOARD_UPDATE;
|
||||
|
@@ -42,7 +42,7 @@ void SDL_CancelClipboardData(Uint32 sequence)
|
||||
{
|
||||
SDL_VideoDevice *_this = SDL_GetVideoDevice();
|
||||
|
||||
if (sequence != _this->clipboard_sequence) {
|
||||
if (sequence && sequence != _this->clipboard_sequence) {
|
||||
// This clipboard data was already canceled
|
||||
return;
|
||||
}
|
||||
@@ -58,10 +58,36 @@ void SDL_CancelClipboardData(Uint32 sequence)
|
||||
_this->clipboard_userdata = NULL;
|
||||
}
|
||||
|
||||
bool SDL_SaveClipboardMimeTypes(const char **mime_types, size_t num_mime_types)
|
||||
{
|
||||
SDL_VideoDevice *_this = SDL_GetVideoDevice();
|
||||
|
||||
SDL_FreeClipboardMimeTypes(_this);
|
||||
|
||||
if (mime_types && num_mime_types > 0) {
|
||||
size_t num_allocated = 0;
|
||||
|
||||
_this->clipboard_mime_types = (char **)SDL_malloc(num_mime_types * sizeof(char *));
|
||||
if (_this->clipboard_mime_types) {
|
||||
for (size_t i = 0; i < num_mime_types; ++i) {
|
||||
_this->clipboard_mime_types[i] = SDL_strdup(mime_types[i]);
|
||||
if (_this->clipboard_mime_types[i]) {
|
||||
++num_allocated;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (num_allocated < num_mime_types) {
|
||||
SDL_FreeClipboardMimeTypes(_this);
|
||||
return false;
|
||||
}
|
||||
_this->num_clipboard_mime_types = num_mime_types;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SDL_SetClipboardData(SDL_ClipboardDataCallback callback, SDL_ClipboardCleanupCallback cleanup, void *userdata, const char **mime_types, size_t num_mime_types)
|
||||
{
|
||||
SDL_VideoDevice *_this = SDL_GetVideoDevice();
|
||||
size_t i;
|
||||
|
||||
if (!_this) {
|
||||
return SDL_UninitializedVideo();
|
||||
@@ -78,7 +104,7 @@ bool SDL_SetClipboardData(SDL_ClipboardDataCallback callback, SDL_ClipboardClean
|
||||
return true;
|
||||
}
|
||||
|
||||
SDL_CancelClipboardData(_this->clipboard_sequence);
|
||||
SDL_CancelClipboardData(0);
|
||||
|
||||
++_this->clipboard_sequence;
|
||||
if (!_this->clipboard_sequence) {
|
||||
@@ -88,24 +114,10 @@ bool SDL_SetClipboardData(SDL_ClipboardDataCallback callback, SDL_ClipboardClean
|
||||
_this->clipboard_cleanup = cleanup;
|
||||
_this->clipboard_userdata = userdata;
|
||||
|
||||
if (mime_types && num_mime_types > 0) {
|
||||
size_t num_allocated = 0;
|
||||
|
||||
_this->clipboard_mime_types = (char **)SDL_malloc(num_mime_types * sizeof(char *));
|
||||
if (_this->clipboard_mime_types) {
|
||||
for (i = 0; i < num_mime_types; ++i) {
|
||||
_this->clipboard_mime_types[i] = SDL_strdup(mime_types[i]);
|
||||
if (_this->clipboard_mime_types[i]) {
|
||||
++num_allocated;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (num_allocated < num_mime_types) {
|
||||
if (!SDL_SaveClipboardMimeTypes(mime_types, num_mime_types)) {
|
||||
SDL_ClearClipboardData();
|
||||
return false;
|
||||
}
|
||||
_this->num_clipboard_mime_types = num_mime_types;
|
||||
}
|
||||
|
||||
if (_this->SetClipboardData) {
|
||||
if (!_this->SetClipboardData(_this)) {
|
||||
@@ -115,7 +127,7 @@ bool SDL_SetClipboardData(SDL_ClipboardDataCallback callback, SDL_ClipboardClean
|
||||
char *text = NULL;
|
||||
size_t size;
|
||||
|
||||
for (i = 0; i < num_mime_types; ++i) {
|
||||
for (size_t i = 0; i < num_mime_types; ++i) {
|
||||
const char *mime_type = _this->clipboard_mime_types[i];
|
||||
if (SDL_IsTextMimeType(mime_type)) {
|
||||
const void *data = _this->clipboard_callback(_this->clipboard_userdata, mime_type, &size);
|
||||
|
@@ -39,7 +39,8 @@ extern bool SDL_HasInternalClipboardData(SDL_VideoDevice *_this, const char *mim
|
||||
// General purpose clipboard text callback
|
||||
const void * SDLCALL SDL_ClipboardTextCallback(void *userdata, const char *mime_type, size_t *size);
|
||||
|
||||
void SDLCALL SDL_FreeClipboardMimeTypes(SDL_VideoDevice *_this);
|
||||
char ** SDLCALL SDL_CopyClipboardMimeTypes(const char **clipboard_mime_types, size_t num_mime_types, bool temporary);
|
||||
bool SDL_SaveClipboardMimeTypes(const char **mime_types, size_t num_mime_types);
|
||||
void SDL_FreeClipboardMimeTypes(SDL_VideoDevice *_this);
|
||||
char **SDL_CopyClipboardMimeTypes(const char **clipboard_mime_types, size_t num_mime_types, bool temporary);
|
||||
|
||||
#endif // SDL_clipboard_c_h_
|
||||
|
@@ -4264,7 +4264,7 @@ void SDL_VideoQuit(void)
|
||||
SDL_free(_this->displays);
|
||||
_this->displays = NULL;
|
||||
|
||||
SDL_CancelClipboardData(_this->clipboard_sequence);
|
||||
SDL_CancelClipboardData(0);
|
||||
|
||||
if (_this->primary_selection_text) {
|
||||
SDL_free(_this->primary_selection_text);
|
||||
|
@@ -23,8 +23,11 @@
|
||||
#ifdef SDL_VIDEO_DRIVER_COCOA
|
||||
|
||||
#include "SDL_cocoavideo.h"
|
||||
#include "../../events/SDL_events_c.h"
|
||||
#include "../../events/SDL_clipboardevents_c.h"
|
||||
|
||||
#include <UniformTypeIdentifiers/UniformTypeIdentifiers.h>
|
||||
|
||||
#if MAC_OS_X_VERSION_MAX_ALLOWED < 101300
|
||||
typedef NSString *NSPasteboardType; // Defined in macOS 10.13+
|
||||
#endif
|
||||
@@ -72,6 +75,66 @@ provideDataForType:(NSPasteboardType)type
|
||||
|
||||
@end
|
||||
|
||||
static char **GetMimeTypes(int *pnformats)
|
||||
{
|
||||
char **new_mime_types = NULL;
|
||||
|
||||
*pnformats = 0;
|
||||
|
||||
int nformats = 0;
|
||||
int formatsSz = 0;
|
||||
NSArray<NSPasteboardItem *> *items = [[NSPasteboard generalPasteboard] pasteboardItems];
|
||||
NSUInteger nitems = [items count];
|
||||
if (nitems > 0) {
|
||||
for (NSPasteboardItem *item in items) {
|
||||
NSArray<NSString *> *types = [item types];
|
||||
for (NSString *type in types) {
|
||||
if (@available(macOS 11.0, *)) {
|
||||
UTType *uttype = [UTType typeWithIdentifier:type];
|
||||
NSString *mime_type = [uttype preferredMIMEType];
|
||||
if (mime_type) {
|
||||
NSUInteger len = [mime_type lengthOfBytesUsingEncoding:NSUTF8StringEncoding] + 1;
|
||||
formatsSz += len;
|
||||
++nformats;
|
||||
}
|
||||
}
|
||||
NSUInteger len = [type lengthOfBytesUsingEncoding:NSUTF8StringEncoding] + 1;
|
||||
formatsSz += len;
|
||||
++nformats;
|
||||
}
|
||||
}
|
||||
|
||||
new_mime_types = SDL_AllocateTemporaryMemory((nformats + 1) * sizeof(char *) + formatsSz);
|
||||
if (new_mime_types) {
|
||||
int i = 0;
|
||||
char *strPtr = (char *)(new_mime_types + nformats + 1);
|
||||
for (NSPasteboardItem *item in items) {
|
||||
NSArray<NSString *> *types = [item types];
|
||||
for (NSString *type in types) {
|
||||
if (@available(macOS 11.0, *)) {
|
||||
UTType *uttype = [UTType typeWithIdentifier:type];
|
||||
NSString *mime_type = [uttype preferredMIMEType];
|
||||
if (mime_type) {
|
||||
NSUInteger len = [mime_type lengthOfBytesUsingEncoding:NSUTF8StringEncoding] + 1;
|
||||
SDL_memcpy(strPtr, [mime_type UTF8String], len);
|
||||
new_mime_types[i++] = strPtr;
|
||||
strPtr += len;
|
||||
}
|
||||
}
|
||||
NSUInteger len = [type lengthOfBytesUsingEncoding:NSUTF8StringEncoding] + 1;
|
||||
SDL_memcpy(strPtr, [type UTF8String], len);
|
||||
new_mime_types[i++] = strPtr;
|
||||
strPtr += len;
|
||||
}
|
||||
}
|
||||
|
||||
new_mime_types[nformats] = NULL;
|
||||
*pnformats = nformats;
|
||||
}
|
||||
}
|
||||
return new_mime_types;
|
||||
}
|
||||
|
||||
|
||||
void Cocoa_CheckClipboardUpdate(SDL_CocoaVideoData *data)
|
||||
{
|
||||
@@ -83,8 +146,11 @@ void Cocoa_CheckClipboardUpdate(SDL_CocoaVideoData *data)
|
||||
count = [pasteboard changeCount];
|
||||
if (count != data.clipboard_count) {
|
||||
if (data.clipboard_count) {
|
||||
// TODO: compute mime types
|
||||
SDL_SendClipboardUpdate(false, NULL, 0);
|
||||
int nformats = 0;
|
||||
char **new_mime_types = GetMimeTypes(&nformats);
|
||||
if (new_mime_types) {
|
||||
SDL_SendClipboardUpdate(false, new_mime_types, nformats);
|
||||
}
|
||||
}
|
||||
data.clipboard_count = count;
|
||||
}
|
||||
@@ -129,6 +195,33 @@ bool Cocoa_SetClipboardData(SDL_VideoDevice *_this)
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool IsMimeType(const char *tag)
|
||||
{
|
||||
if (SDL_strchr(tag, '/')) {
|
||||
// MIME types have slashes
|
||||
return true;
|
||||
} else if (SDL_strchr(tag, '.')) {
|
||||
// UTI identifiers have periods
|
||||
return false;
|
||||
} else {
|
||||
// Not sure, but it's not a UTI identifier
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
static CFStringRef GetUTIType(const char *tag)
|
||||
{
|
||||
CFStringRef utiType;
|
||||
if (IsMimeType(tag)) {
|
||||
CFStringRef mimeType = CFStringCreateWithCString(NULL, tag, kCFStringEncodingUTF8);
|
||||
utiType = UTTypeCreatePreferredIdentifierForTag(kUTTagClassMIMEType, mimeType, NULL);
|
||||
CFRelease(mimeType);
|
||||
} else {
|
||||
utiType = CFStringCreateWithCString(NULL, tag, kCFStringEncodingUTF8);
|
||||
}
|
||||
return utiType;
|
||||
}
|
||||
|
||||
void *Cocoa_GetClipboardData(SDL_VideoDevice *_this, const char *mime_type, size_t *size)
|
||||
{
|
||||
@autoreleasepool {
|
||||
@@ -137,9 +230,7 @@ void *Cocoa_GetClipboardData(SDL_VideoDevice *_this, const char *mime_type, size
|
||||
*size = 0;
|
||||
for (NSPasteboardItem *item in [pasteboard pasteboardItems]) {
|
||||
NSData *itemData;
|
||||
CFStringRef mimeType = CFStringCreateWithCString(NULL, mime_type, kCFStringEncodingUTF8);
|
||||
CFStringRef utiType = UTTypeCreatePreferredIdentifierForTag(kUTTagClassMIMEType, mimeType, NULL);
|
||||
CFRelease(mimeType);
|
||||
CFStringRef utiType = GetUTIType(mime_type);
|
||||
itemData = [item dataForType: (__bridge NSString *)utiType];
|
||||
CFRelease(utiType);
|
||||
if (itemData != nil) {
|
||||
@@ -162,9 +253,7 @@ bool Cocoa_HasClipboardData(SDL_VideoDevice *_this, const char *mime_type)
|
||||
bool result = false;
|
||||
@autoreleasepool {
|
||||
NSPasteboard *pasteboard = [NSPasteboard generalPasteboard];
|
||||
CFStringRef mimeType = CFStringCreateWithCString(NULL, mime_type, kCFStringEncodingUTF8);
|
||||
CFStringRef utiType = UTTypeCreatePreferredIdentifierForTag(kUTTagClassMIMEType, mimeType, NULL);
|
||||
CFRelease(mimeType);
|
||||
CFStringRef utiType = GetUTIType(mime_type);
|
||||
if ([pasteboard canReadItemWithDataConformingToTypes: @[(__bridge NSString *)utiType]]) {
|
||||
result = true;
|
||||
}
|
||||
|
Reference in New Issue
Block a user