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)
|
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)) {
|
if (SDL_EventEnabled(SDL_EVENT_CLIPBOARD_UPDATE)) {
|
||||||
SDL_Event event;
|
SDL_Event event;
|
||||||
event.type = SDL_EVENT_CLIPBOARD_UPDATE;
|
event.type = SDL_EVENT_CLIPBOARD_UPDATE;
|
||||||
|
@@ -42,7 +42,7 @@ void SDL_CancelClipboardData(Uint32 sequence)
|
|||||||
{
|
{
|
||||||
SDL_VideoDevice *_this = SDL_GetVideoDevice();
|
SDL_VideoDevice *_this = SDL_GetVideoDevice();
|
||||||
|
|
||||||
if (sequence != _this->clipboard_sequence) {
|
if (sequence && sequence != _this->clipboard_sequence) {
|
||||||
// This clipboard data was already canceled
|
// This clipboard data was already canceled
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -58,10 +58,36 @@ void SDL_CancelClipboardData(Uint32 sequence)
|
|||||||
_this->clipboard_userdata = NULL;
|
_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)
|
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();
|
SDL_VideoDevice *_this = SDL_GetVideoDevice();
|
||||||
size_t i;
|
|
||||||
|
|
||||||
if (!_this) {
|
if (!_this) {
|
||||||
return SDL_UninitializedVideo();
|
return SDL_UninitializedVideo();
|
||||||
@@ -78,7 +104,7 @@ bool SDL_SetClipboardData(SDL_ClipboardDataCallback callback, SDL_ClipboardClean
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
SDL_CancelClipboardData(_this->clipboard_sequence);
|
SDL_CancelClipboardData(0);
|
||||||
|
|
||||||
++_this->clipboard_sequence;
|
++_this->clipboard_sequence;
|
||||||
if (!_this->clipboard_sequence) {
|
if (!_this->clipboard_sequence) {
|
||||||
@@ -88,23 +114,9 @@ bool SDL_SetClipboardData(SDL_ClipboardDataCallback callback, SDL_ClipboardClean
|
|||||||
_this->clipboard_cleanup = cleanup;
|
_this->clipboard_cleanup = cleanup;
|
||||||
_this->clipboard_userdata = userdata;
|
_this->clipboard_userdata = userdata;
|
||||||
|
|
||||||
if (mime_types && num_mime_types > 0) {
|
if (!SDL_SaveClipboardMimeTypes(mime_types, num_mime_types)) {
|
||||||
size_t num_allocated = 0;
|
SDL_ClearClipboardData();
|
||||||
|
return false;
|
||||||
_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) {
|
|
||||||
SDL_ClearClipboardData();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
_this->num_clipboard_mime_types = num_mime_types;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_this->SetClipboardData) {
|
if (_this->SetClipboardData) {
|
||||||
@@ -115,7 +127,7 @@ bool SDL_SetClipboardData(SDL_ClipboardDataCallback callback, SDL_ClipboardClean
|
|||||||
char *text = NULL;
|
char *text = NULL;
|
||||||
size_t size;
|
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];
|
const char *mime_type = _this->clipboard_mime_types[i];
|
||||||
if (SDL_IsTextMimeType(mime_type)) {
|
if (SDL_IsTextMimeType(mime_type)) {
|
||||||
const void *data = _this->clipboard_callback(_this->clipboard_userdata, mime_type, &size);
|
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
|
// General purpose clipboard text callback
|
||||||
const void * SDLCALL SDL_ClipboardTextCallback(void *userdata, const char *mime_type, size_t *size);
|
const void * SDLCALL SDL_ClipboardTextCallback(void *userdata, const char *mime_type, size_t *size);
|
||||||
|
|
||||||
void SDLCALL SDL_FreeClipboardMimeTypes(SDL_VideoDevice *_this);
|
bool SDL_SaveClipboardMimeTypes(const char **mime_types, size_t num_mime_types);
|
||||||
char ** SDLCALL SDL_CopyClipboardMimeTypes(const char **clipboard_mime_types, size_t num_mime_types, bool temporary);
|
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_
|
#endif // SDL_clipboard_c_h_
|
||||||
|
@@ -4264,7 +4264,7 @@ void SDL_VideoQuit(void)
|
|||||||
SDL_free(_this->displays);
|
SDL_free(_this->displays);
|
||||||
_this->displays = NULL;
|
_this->displays = NULL;
|
||||||
|
|
||||||
SDL_CancelClipboardData(_this->clipboard_sequence);
|
SDL_CancelClipboardData(0);
|
||||||
|
|
||||||
if (_this->primary_selection_text) {
|
if (_this->primary_selection_text) {
|
||||||
SDL_free(_this->primary_selection_text);
|
SDL_free(_this->primary_selection_text);
|
||||||
|
@@ -23,8 +23,11 @@
|
|||||||
#ifdef SDL_VIDEO_DRIVER_COCOA
|
#ifdef SDL_VIDEO_DRIVER_COCOA
|
||||||
|
|
||||||
#include "SDL_cocoavideo.h"
|
#include "SDL_cocoavideo.h"
|
||||||
|
#include "../../events/SDL_events_c.h"
|
||||||
#include "../../events/SDL_clipboardevents_c.h"
|
#include "../../events/SDL_clipboardevents_c.h"
|
||||||
|
|
||||||
|
#include <UniformTypeIdentifiers/UniformTypeIdentifiers.h>
|
||||||
|
|
||||||
#if MAC_OS_X_VERSION_MAX_ALLOWED < 101300
|
#if MAC_OS_X_VERSION_MAX_ALLOWED < 101300
|
||||||
typedef NSString *NSPasteboardType; // Defined in macOS 10.13+
|
typedef NSString *NSPasteboardType; // Defined in macOS 10.13+
|
||||||
#endif
|
#endif
|
||||||
@@ -72,6 +75,66 @@ provideDataForType:(NSPasteboardType)type
|
|||||||
|
|
||||||
@end
|
@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)
|
void Cocoa_CheckClipboardUpdate(SDL_CocoaVideoData *data)
|
||||||
{
|
{
|
||||||
@@ -83,8 +146,11 @@ void Cocoa_CheckClipboardUpdate(SDL_CocoaVideoData *data)
|
|||||||
count = [pasteboard changeCount];
|
count = [pasteboard changeCount];
|
||||||
if (count != data.clipboard_count) {
|
if (count != data.clipboard_count) {
|
||||||
if (data.clipboard_count) {
|
if (data.clipboard_count) {
|
||||||
// TODO: compute mime types
|
int nformats = 0;
|
||||||
SDL_SendClipboardUpdate(false, NULL, 0);
|
char **new_mime_types = GetMimeTypes(&nformats);
|
||||||
|
if (new_mime_types) {
|
||||||
|
SDL_SendClipboardUpdate(false, new_mime_types, nformats);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
data.clipboard_count = count;
|
data.clipboard_count = count;
|
||||||
}
|
}
|
||||||
@@ -129,6 +195,33 @@ bool Cocoa_SetClipboardData(SDL_VideoDevice *_this)
|
|||||||
return true;
|
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)
|
void *Cocoa_GetClipboardData(SDL_VideoDevice *_this, const char *mime_type, size_t *size)
|
||||||
{
|
{
|
||||||
@autoreleasepool {
|
@autoreleasepool {
|
||||||
@@ -137,9 +230,7 @@ void *Cocoa_GetClipboardData(SDL_VideoDevice *_this, const char *mime_type, size
|
|||||||
*size = 0;
|
*size = 0;
|
||||||
for (NSPasteboardItem *item in [pasteboard pasteboardItems]) {
|
for (NSPasteboardItem *item in [pasteboard pasteboardItems]) {
|
||||||
NSData *itemData;
|
NSData *itemData;
|
||||||
CFStringRef mimeType = CFStringCreateWithCString(NULL, mime_type, kCFStringEncodingUTF8);
|
CFStringRef utiType = GetUTIType(mime_type);
|
||||||
CFStringRef utiType = UTTypeCreatePreferredIdentifierForTag(kUTTagClassMIMEType, mimeType, NULL);
|
|
||||||
CFRelease(mimeType);
|
|
||||||
itemData = [item dataForType: (__bridge NSString *)utiType];
|
itemData = [item dataForType: (__bridge NSString *)utiType];
|
||||||
CFRelease(utiType);
|
CFRelease(utiType);
|
||||||
if (itemData != nil) {
|
if (itemData != nil) {
|
||||||
@@ -162,9 +253,7 @@ bool Cocoa_HasClipboardData(SDL_VideoDevice *_this, const char *mime_type)
|
|||||||
bool result = false;
|
bool result = false;
|
||||||
@autoreleasepool {
|
@autoreleasepool {
|
||||||
NSPasteboard *pasteboard = [NSPasteboard generalPasteboard];
|
NSPasteboard *pasteboard = [NSPasteboard generalPasteboard];
|
||||||
CFStringRef mimeType = CFStringCreateWithCString(NULL, mime_type, kCFStringEncodingUTF8);
|
CFStringRef utiType = GetUTIType(mime_type);
|
||||||
CFStringRef utiType = UTTypeCreatePreferredIdentifierForTag(kUTTagClassMIMEType, mimeType, NULL);
|
|
||||||
CFRelease(mimeType);
|
|
||||||
if ([pasteboard canReadItemWithDataConformingToTypes: @[(__bridge NSString *)utiType]]) {
|
if ([pasteboard canReadItemWithDataConformingToTypes: @[(__bridge NSString *)utiType]]) {
|
||||||
result = true;
|
result = true;
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user