Skip to content

Commit

Permalink
Synchronize clipboard mime types with external clipboard updates
Browse files Browse the repository at this point in the history
  • Loading branch information
slouken committed Jan 1, 2025
1 parent ab00ff2 commit f420258
Show file tree
Hide file tree
Showing 5 changed files with 103 additions and 26 deletions.
6 changes: 6 additions & 0 deletions src/events/SDL_clipboardevents.c
Original file line number Diff line number Diff line change
Expand Up @@ -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(mime_types, num_mime_types);
}

if (SDL_EventEnabled(SDL_EVENT_CLIPBOARD_UPDATE)) {
SDL_Event event;
event.type = SDL_EVENT_CLIPBOARD_UPDATE;
Expand Down
55 changes: 34 additions & 21 deletions src/video/SDL_clipboard.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand All @@ -58,10 +58,36 @@ void SDL_CancelClipboardData(Uint32 sequence)
_this->clipboard_userdata = NULL;
}

bool SDL_SaveClipboardMimeTypes(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();
Expand All @@ -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) {
Expand All @@ -88,23 +114,9 @@ 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) {
SDL_ClearClipboardData();
return false;
}
_this->num_clipboard_mime_types = num_mime_types;
if (!SDL_SaveClipboardMimeTypes(mime_types, num_mime_types)) {
SDL_ClearClipboardData();
return false;
}

if (_this->SetClipboardData) {
Expand All @@ -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);
Expand Down Expand Up @@ -225,6 +237,7 @@ bool SDL_HasInternalClipboardData(SDL_VideoDevice *_this, const char *mime_type)
bool SDL_HasClipboardData(const char *mime_type)
{
SDL_VideoDevice *_this = SDL_GetVideoDevice();
size_t unused;

if (!_this) {
SDL_UninitializedVideo();
Expand Down
5 changes: 3 additions & 2 deletions src/video/SDL_clipboard_c.h
Original file line number Diff line number Diff line change
Expand Up @@ -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(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_
2 changes: 1 addition & 1 deletion src/video/SDL_video.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
61 changes: 59 additions & 2 deletions src/video/cocoa/SDL_cocoaclipboard.m
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -72,6 +75,57 @@ - (void)pasteboard:(NSPasteboard *)pasteboard

@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];
int nitems = [items count];
if (nitems > 0) {
for (NSPasteboardItem *item in items) {
NSArray<NSString *> *types = [item types];
for (NSString *type in types) {
UTType *uttype = [UTType typeWithIdentifier:type];
NSString *mime_type = [uttype preferredMIMEType];
if (mime_type == nil) {
continue;
}
int len = [mime_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) {
UTType *uttype = [UTType typeWithIdentifier:type];
NSString *mime_type = [uttype preferredMIMEType];
if (mime_type == nil) {
continue;
}
int len = [mime_type lengthOfBytesUsingEncoding:NSUTF8StringEncoding] + 1;
SDL_memcpy(strPtr, [mime_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)
{
Expand All @@ -83,8 +137,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;
}
Expand Down

0 comments on commit f420258

Please sign in to comment.