From: Christian Hergert Date: Fri, 18 Jun 2021 18:27:54 +0000 (-0700) Subject: macos: refactor pasteboard components into separate module X-Git-Tag: archive/raspbian/4.12.3+ds-1+rpi1~1^2^2^2~22^2~8^2~50^2~13 X-Git-Url: https://dgit.raspbian.org/?a=commitdiff_plain;h=eb5cf831b1a46a46ca88a9f7ee5e1cf267ff7aac;p=gtk4.git macos: refactor pasteboard components into separate module This will make it easier to reuse from drag integration so that we don't require clipboards for everything. We will need to subclass the pasteboard provider twice, however, both for clipboard and dragging. --- diff --git a/gdk/macos/GdkMacosWindow.c b/gdk/macos/GdkMacosWindow.c index 36964391b3..7680e6d8b0 100644 --- a/gdk/macos/GdkMacosWindow.c +++ b/gdk/macos/GdkMacosWindow.c @@ -27,10 +27,10 @@ #import "GdkMacosView.h" #import "GdkMacosWindow.h" -#include "gdkmacosclipboard-private.h" #include "gdkmacosdisplay-private.h" #include "gdkmacosdrop-private.h" #include "gdkmacosmonitor-private.h" +#include "gdkmacospasteboard-private.h" #include "gdkmacossurface-private.h" #include "gdkmacospopupsurface-private.h" #include "gdkmacostoplevelsurface-private.h" @@ -246,7 +246,7 @@ typedef NSString *CALayerContentsGravity; [view release]; /* TODO: We might want to make this more extensible at some point */ - _gdk_macos_clipboard_register_drag_types (self); + _gdk_macos_pasteboard_register_drag_types (self); return self; } diff --git a/gdk/macos/gdkmacosclipboard-private.h b/gdk/macos/gdkmacosclipboard-private.h index ba0b52bf0a..a1fa83f7ca 100644 --- a/gdk/macos/gdkmacosclipboard-private.h +++ b/gdk/macos/gdkmacosclipboard-private.h @@ -24,6 +24,7 @@ #include "gdkclipboardprivate.h" #include "gdkmacosdisplay-private.h" +#include "gdkmacospasteboard-private.h" G_BEGIN_DECLS @@ -41,28 +42,13 @@ NSPasteboardType _gdk_macos_clipboard_to_ns_type (const char NSPasteboardType *alternate); const char *_gdk_macos_clipboard_from_ns_type (NSPasteboardType ns_type); void _gdk_macos_clipboard_register_drag_types (NSWindow *window); -GdkContentFormats *_gdk_macos_pasteboard_load_formats (NSPasteboard *pasteboard); -void _gdk_macos_pasteboard_read_async (GObject *object, - NSPasteboard *pasteboard, - GdkContentFormats *formats, - int io_priority, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data); -GInputStream *_gdk_macos_pasteboard_read_finish (GObject *object, - GAsyncResult *result, - const char **out_mime_type, - GError **error); -@interface GdkMacosClipboardDataProvider : NSObject +@interface GdkMacosClipboardDataProvider : GdkMacosPasteboardDataProvider { - GCancellable *cancellable; - GdkClipboard *clipboard; - char **mimeTypes; + GdkClipboard *clipboard; } --(id)initClipboard:(GdkClipboard *)gdkClipboard mimetypes:(const char * const *)mime_types; --(NSArray *)types; +-(id)initClipboard:(GdkMacosClipboard *)gdkClipboard mimetypes:(const char * const *)mime_types; @end diff --git a/gdk/macos/gdkmacosclipboard.c b/gdk/macos/gdkmacosclipboard.c index 3a2667c0df..e647950364 100644 --- a/gdk/macos/gdkmacosclipboard.c +++ b/gdk/macos/gdkmacosclipboard.c @@ -22,6 +22,7 @@ #include #include "gdkmacosclipboard-private.h" +#include "gdkmacospasteboard-private.h" #include "gdkmacosutils-private.h" #include "gdkprivate.h" @@ -41,56 +42,8 @@ typedef struct guint done : 1; } WriteRequest; -enum { - TYPE_STRING, - TYPE_PBOARD, - TYPE_URL, - TYPE_FILE_URL, - TYPE_COLOR, - TYPE_TIFF, - TYPE_PNG, - TYPE_LAST -}; - -#define PTYPE(k) (get_pasteboard_type(TYPE_##k)) - -static NSPasteboardType pasteboard_types[TYPE_LAST]; - G_DEFINE_TYPE (GdkMacosClipboard, _gdk_macos_clipboard, GDK_TYPE_CLIPBOARD) -static NSPasteboardType -get_pasteboard_type (int type) -{ - static gsize initialized = FALSE; - - g_assert (type >= 0); - g_assert (type < TYPE_LAST); - - if (g_once_init_enter (&initialized)) - { - pasteboard_types[TYPE_PNG] = NSPasteboardTypePNG; - pasteboard_types[TYPE_STRING] = NSPasteboardTypeString; - pasteboard_types[TYPE_TIFF] = NSPasteboardTypeTIFF; - pasteboard_types[TYPE_COLOR] = NSPasteboardTypeColor; - - G_GNUC_BEGIN_IGNORE_DEPRECATIONS - pasteboard_types[TYPE_PBOARD] = NSStringPboardType; - G_GNUC_END_IGNORE_DEPRECATIONS - -#ifdef AVAILABLE_MAC_OS_X_VERSION_10_13_AND_LATER - pasteboard_types[TYPE_URL] = NSPasteboardTypeURL; - pasteboard_types[TYPE_FILE_URL] = NSPasteboardTypeFileURL; -#else - pasteboard_types[TYPE_URL] = [[NSString alloc] initWithUTF8String:"public.url"]; - pasteboard_types[TYPE_FILE_URL] = [[NSString alloc] initWithUTF8String:"public.file-url"]; -#endif - - g_once_init_leave (&initialized, TRUE); - } - - return pasteboard_types[type]; -} - static void write_request_free (WriteRequest *wr) { @@ -100,95 +53,6 @@ write_request_free (WriteRequest *wr) g_slice_free (WriteRequest, wr); } -const char * -_gdk_macos_clipboard_from_ns_type (NSPasteboardType type) -{ - G_GNUC_BEGIN_IGNORE_DEPRECATIONS; - - if ([type isEqualToString:PTYPE(STRING)] || - [type isEqualToString:PTYPE(PBOARD)]) - return g_intern_string ("text/plain;charset=utf-8"); - else if ([type isEqualToString:PTYPE(URL)] || - [type isEqualToString:PTYPE(FILE_URL)]) - return g_intern_string ("text/uri-list"); - else if ([type isEqualToString:PTYPE(COLOR)]) - return g_intern_string ("application/x-color"); - else if ([type isEqualToString:PTYPE(TIFF)]) - return g_intern_string ("image/tiff"); - else if ([type isEqualToString:PTYPE(PNG)]) - return g_intern_string ("image/png"); - - G_GNUC_END_IGNORE_DEPRECATIONS; - - return NULL; -} - -NSPasteboardType -_gdk_macos_clipboard_to_ns_type (const char *mime_type, - NSPasteboardType *alternate) -{ - if (alternate) - *alternate = NULL; - - if (g_strcmp0 (mime_type, "text/plain;charset=utf-8") == 0) - { - return PTYPE(STRING); - } - else if (g_strcmp0 (mime_type, "text/uri-list") == 0) - { - if (alternate) - *alternate = PTYPE(URL); - return PTYPE(FILE_URL); - } - else if (g_strcmp0 (mime_type, "application/x-color") == 0) - { - return PTYPE(COLOR); - } - else if (g_strcmp0 (mime_type, "image/tiff") == 0) - { - return PTYPE(TIFF); - } - else if (g_strcmp0 (mime_type, "image/png") == 0) - { - return PTYPE(PNG); - } - - return nil; -} - -static void -populate_content_formats (GdkContentFormatsBuilder *builder, - NSPasteboardType type) -{ - const char *mime_type; - - g_return_if_fail (builder != NULL); - g_return_if_fail (type != NULL); - - mime_type = _gdk_macos_clipboard_from_ns_type (type); - - if (mime_type != NULL) - gdk_content_formats_builder_add_mime_type (builder, mime_type); -} - -static GdkContentFormats * -load_offer_formats (NSPasteboard *pasteboard) -{ - GDK_BEGIN_MACOS_ALLOC_POOL; - - GdkContentFormatsBuilder *builder; - GdkContentFormats *formats; - - builder = gdk_content_formats_builder_new (); - for (NSPasteboardType type in [pasteboard types]) - populate_content_formats (builder, type); - formats = gdk_content_formats_builder_free_to_formats (builder); - - GDK_END_MACOS_ALLOC_POOL; - - return g_steal_pointer (&formats); -} - static void _gdk_macos_clipboard_load_contents (GdkMacosClipboard *self) { @@ -199,22 +63,13 @@ _gdk_macos_clipboard_load_contents (GdkMacosClipboard *self) change_count = [self->pasteboard changeCount]; - formats = load_offer_formats (self->pasteboard); + formats = _gdk_macos_pasteboard_load_formats (self->pasteboard); gdk_clipboard_claim_remote (GDK_CLIPBOARD (self), formats); gdk_content_formats_unref (formats); self->last_change_count = change_count; } -static GInputStream * -create_stream_from_nsdata (NSData *data) -{ - const guint8 *bytes = [data bytes]; - gsize len = [data length]; - - return g_memory_input_stream_new_from_data (g_memdup2 (bytes, len), len, g_free); -} - static void _gdk_macos_clipboard_read_async (GdkClipboard *clipboard, GdkContentFormats *formats, @@ -245,35 +100,25 @@ static void _gdk_macos_clipboard_send_to_pasteboard (GdkMacosClipboard *self, GdkContentProvider *content) { - GDK_BEGIN_MACOS_ALLOC_POOL; - GdkMacosClipboardDataProvider *dataProvider; GdkContentFormats *serializable; - NSPasteboardItem *item; const char * const *mime_types; gsize n_mime_types; - g_return_if_fail (GDK_IS_MACOS_CLIPBOARD (self)); - g_return_if_fail (GDK_IS_CONTENT_PROVIDER (content)); + g_assert (GDK_IS_MACOS_CLIPBOARD (self)); + g_assert (GDK_IS_CONTENT_PROVIDER (content)); serializable = gdk_content_provider_ref_storable_formats (content); serializable = gdk_content_formats_union_serialize_mime_types (serializable); mime_types = gdk_content_formats_get_mime_types (serializable, &n_mime_types); - dataProvider = [[GdkMacosClipboardDataProvider alloc] initClipboard:GDK_CLIPBOARD (self) - mimetypes:mime_types]; - item = [[NSPasteboardItem alloc] init]; - [item setDataProvider:dataProvider forTypes:[dataProvider types]]; - - [self->pasteboard clearContents]; - if ([self->pasteboard writeObjects:[NSArray arrayWithObject:item]] == NO) - g_warning ("Failed to write object to pasteboard"); + dataProvider = [[GdkMacosClipboardDataProvider alloc] initClipboard:self mimetypes:mime_types]; + _gdk_macos_pasteboard_send_content (self->pasteboard, content, dataProvider); + [dataProvider release]; self->last_change_count = [self->pasteboard changeCount]; g_clear_pointer (&serializable, gdk_content_formats_unref); - - GDK_END_MACOS_ALLOC_POOL; } static gboolean @@ -368,23 +213,18 @@ _gdk_macos_clipboard_check_externally_modified (GdkMacosClipboard *self) @implementation GdkMacosClipboardDataProvider --(id)initClipboard:(GdkClipboard *)gdkClipboard mimetypes:(const char * const *)mime_types; +-(id)initClipboard:(GdkMacosClipboard *)gdkClipboard mimetypes:(const char * const *)mime_types; { - [super init]; + [super initPasteboard:gdkClipboard->pasteboard mimetypes:mime_types]; - self->mimeTypes = g_strdupv ((char **)mime_types); - self->clipboard = g_object_ref (gdkClipboard); + self->clipboard = g_object_ref (GDK_CLIPBOARD (gdkClipboard)); return self; } -(void)dealloc { - g_cancellable_cancel (self->cancellable); - - g_clear_pointer (&self->mimeTypes, g_strfreev); g_clear_object (&self->clipboard); - g_clear_object (&self->cancellable); [super dealloc]; } @@ -394,27 +234,6 @@ _gdk_macos_clipboard_check_externally_modified (GdkMacosClipboard *self) g_clear_object (&self->clipboard); } --(NSArray *)types -{ - NSMutableArray *ret = [[NSMutableArray alloc] init]; - - for (guint i = 0; self->mimeTypes[i]; i++) - { - const char *mime_type = self->mimeTypes[i]; - NSPasteboardType type; - NSPasteboardType alternate = nil; - - if ((type = _gdk_macos_clipboard_to_ns_type (mime_type, &alternate))) - { - [ret addObject:type]; - if (alternate) - [ret addObject:alternate]; - } - } - - return g_steal_pointer (&ret); -} - static void on_data_ready_cb (GObject *object, GAsyncResult *result, @@ -460,11 +279,9 @@ on_data_ready_cb (GObject *object, GDK_END_MACOS_ALLOC_POOL; } --(void) pasteboard:(NSPasteboard *)pasteboard - item:(NSPasteboardItem *)item - provideDataForType:(NSPasteboardType)type +-(void)pasteboard:(NSPasteboard *)pasteboard item:(NSPasteboardItem *)item provideDataForType:(NSPasteboardType)type { - const char *mime_type = _gdk_macos_clipboard_from_ns_type (type); + const char *mime_type = _gdk_macos_pasteboard_from_ns_type (type); GMainContext *main_context = g_main_context_default (); WriteRequest *wr; @@ -500,170 +317,4 @@ on_data_ready_cb (GObject *object, write_request_free (wr); } -void -_gdk_macos_clipboard_register_drag_types (NSWindow *window) -{ - [window registerForDraggedTypes:[NSArray arrayWithObjects:PTYPE(STRING), - PTYPE(PBOARD), - PTYPE(URL), - PTYPE(FILE_URL), - PTYPE(COLOR), - PTYPE(TIFF), - PTYPE(PNG), - nil]]; -} - @end - -GdkContentFormats * -_gdk_macos_pasteboard_load_formats (NSPasteboard *pasteboard) -{ - return load_offer_formats (pasteboard); -} - -void -_gdk_macos_pasteboard_read_async (GObject *object, - NSPasteboard *pasteboard, - GdkContentFormats *formats, - int io_priority, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) -{ - GDK_BEGIN_MACOS_ALLOC_POOL; - - GdkContentFormats *offer_formats = NULL; - const char *mime_type; - GInputStream *stream = NULL; - GTask *task = NULL; - - g_assert (G_IS_OBJECT (object)); - g_assert (pasteboard != NULL); - g_assert (formats != NULL); - - task = g_task_new (object, cancellable, callback, user_data); - g_task_set_source_tag (task, _gdk_macos_pasteboard_read_async); - g_task_set_priority (task, io_priority); - - offer_formats = load_offer_formats (pasteboard); - mime_type = gdk_content_formats_match_mime_type (formats, offer_formats); - - if (mime_type == NULL) - { - g_task_return_new_error (task, - G_IO_ERROR, - G_IO_ERROR_NOT_SUPPORTED, - "%s", - _("No compatible transfer format found")); - goto cleanup; - } - - if (strcmp (mime_type, "text/plain;charset=utf-8") == 0) - { - NSString *nsstr = [pasteboard stringForType:NSPasteboardTypeString]; - - if (nsstr != NULL) - { - const char *str = [nsstr UTF8String]; - stream = g_memory_input_stream_new_from_data (g_strdup (str), - strlen (str) + 1, - g_free); - } - } - else if (strcmp (mime_type, "text/uri-list") == 0) - { - G_GNUC_BEGIN_IGNORE_DEPRECATIONS; - - if ([[pasteboard types] containsObject:PTYPE(FILE_URL)]) - { - GString *str = g_string_new (NULL); - NSArray *files = [pasteboard propertyListForType:NSFilenamesPboardType]; - gsize n_files = [files count]; - char *data; - guint len; - - for (gsize i = 0; i < n_files; ++i) - { - NSString* uriString = [files objectAtIndex:i]; - uriString = [@"file://" stringByAppendingString:uriString]; - uriString = [uriString stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]; - - g_string_append_printf (str, - "%s\r\n", - [uriString cStringUsingEncoding:NSUTF8StringEncoding]); - } - - len = str->len; - data = g_string_free (str, FALSE); - stream = g_memory_input_stream_new_from_data (data, len, g_free); - } - - G_GNUC_END_IGNORE_DEPRECATIONS; - } - else if (strcmp (mime_type, "application/x-color") == 0) - { - NSColorSpace *colorspace; - NSColor *nscolor; - guint16 color[4]; - - colorspace = [NSColorSpace genericRGBColorSpace]; - nscolor = [[NSColor colorFromPasteboard:pasteboard] - colorUsingColorSpace:colorspace]; - - color[0] = 0xffff * [nscolor redComponent]; - color[1] = 0xffff * [nscolor greenComponent]; - color[2] = 0xffff * [nscolor blueComponent]; - color[3] = 0xffff * [nscolor alphaComponent]; - - stream = g_memory_input_stream_new_from_data (g_memdup2 (&color, sizeof color), - sizeof color, - g_free); - } - else if (strcmp (mime_type, "image/tiff") == 0) - { - NSData *data = [pasteboard dataForType:PTYPE(TIFF)]; - stream = create_stream_from_nsdata (data); - } - else if (strcmp (mime_type, "image/png") == 0) - { - NSData *data = [pasteboard dataForType:PTYPE(PNG)]; - stream = create_stream_from_nsdata (data); - } - - if (stream != NULL) - { - g_task_set_task_data (task, g_strdup (mime_type), g_free); - g_task_return_pointer (task, g_steal_pointer (&stream), g_object_unref); - } - else - { - g_task_return_new_error (task, - G_IO_ERROR, - G_IO_ERROR_FAILED, - _("Failed to decode contents with mime-type of '%s'"), - mime_type); - } - -cleanup: - g_clear_object (&task); - g_clear_pointer (&offer_formats, gdk_content_formats_unref); - - GDK_END_MACOS_ALLOC_POOL; -} - -GInputStream * -_gdk_macos_pasteboard_read_finish (GObject *object, - GAsyncResult *result, - const char **out_mime_type, - GError **error) -{ - GTask *task = (GTask *)result; - - g_assert (G_IS_OBJECT (object)); - g_assert (G_IS_TASK (task)); - - if (out_mime_type != NULL) - *out_mime_type = g_strdup (g_task_get_task_data (task)); - - return g_task_propagate_pointer (task, error); -} diff --git a/gdk/macos/gdkmacospasteboard-private.h b/gdk/macos/gdkmacospasteboard-private.h new file mode 100644 index 0000000000..c135d82d58 --- /dev/null +++ b/gdk/macos/gdkmacospasteboard-private.h @@ -0,0 +1,64 @@ +/* + * Copyright © 2021 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see . + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#ifndef __GDK_MACOS_PASTEBOARD_PRIVATE_H__ +#define __GDK_MACOS_PASTEBOARD_PRIVATE_H__ + +#include +#include + +#include "gdkclipboardprivate.h" + +G_BEGIN_DECLS + +@interface GdkMacosPasteboardDataProvider : NSObject +{ + NSPasteboard *pasteboard; + GCancellable *cancellable; + char **mimeTypes; +} + +-(id)initPasteboard:(NSPasteboard *)pasteBoard mimetypes:(const char * const *)mime_types; +-(NSArray *)types; + +@end + +NSPasteboardType _gdk_macos_pasteboard_to_ns_type (const char *mime_type, + NSPasteboardType *alternate); +const char *_gdk_macos_pasteboard_from_ns_type (NSPasteboardType type); +GdkContentFormats *_gdk_macos_pasteboard_load_formats (NSPasteboard *pasteboard); +void _gdk_macos_pasteboard_register_drag_types (NSWindow *window); +void _gdk_macos_pasteboard_read_async (GObject *object, + NSPasteboard *pasteboard, + GdkContentFormats *formats, + int io_priority, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); +GInputStream *_gdk_macos_pasteboard_read_finish (GObject *object, + GAsyncResult *result, + const char **out_mime_type, + GError **error); +void _gdk_macos_pasteboard_send_content (NSPasteboard *pasteboard, + GdkContentProvider *content, + GdkMacosPasteboardDataProvider *dataProvider); + +G_END_DECLS + +#endif /* __GDK_MACOS_PASTEBOARD_PRIVATE_H__ */ diff --git a/gdk/macos/gdkmacospasteboard.c b/gdk/macos/gdkmacospasteboard.c new file mode 100644 index 0000000000..73e6af07cd --- /dev/null +++ b/gdk/macos/gdkmacospasteboard.c @@ -0,0 +1,416 @@ +/* + * Copyright © 2021 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see . + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#include "config.h" + +#include + +#include "gdkmacospasteboard-private.h" +#include "gdkmacosutils-private.h" + +enum { + TYPE_STRING, + TYPE_PBOARD, + TYPE_URL, + TYPE_FILE_URL, + TYPE_COLOR, + TYPE_TIFF, + TYPE_PNG, + TYPE_LAST +}; + +#define PTYPE(k) (get_pasteboard_type(TYPE_##k)) + +static NSPasteboardType pasteboard_types[TYPE_LAST]; + +static NSPasteboardType +get_pasteboard_type (int type) +{ + static gsize initialized = FALSE; + + g_assert (type >= 0); + g_assert (type < TYPE_LAST); + + if (g_once_init_enter (&initialized)) + { + pasteboard_types[TYPE_PNG] = NSPasteboardTypePNG; + pasteboard_types[TYPE_STRING] = NSPasteboardTypeString; + pasteboard_types[TYPE_TIFF] = NSPasteboardTypeTIFF; + pasteboard_types[TYPE_COLOR] = NSPasteboardTypeColor; + + G_GNUC_BEGIN_IGNORE_DEPRECATIONS + pasteboard_types[TYPE_PBOARD] = NSStringPboardType; + G_GNUC_END_IGNORE_DEPRECATIONS + +#ifdef AVAILABLE_MAC_OS_X_VERSION_10_13_AND_LATER + pasteboard_types[TYPE_URL] = NSPasteboardTypeURL; + pasteboard_types[TYPE_FILE_URL] = NSPasteboardTypeFileURL; +#else + pasteboard_types[TYPE_URL] = [[NSString alloc] initWithUTF8String:"public.url"]; + pasteboard_types[TYPE_FILE_URL] = [[NSString alloc] initWithUTF8String:"public.file-url"]; +#endif + + g_once_init_leave (&initialized, TRUE); + } + + return pasteboard_types[type]; +} + +const char * +_gdk_macos_pasteboard_from_ns_type (NSPasteboardType type) +{ + G_GNUC_BEGIN_IGNORE_DEPRECATIONS; + + if ([type isEqualToString:PTYPE(STRING)] || + [type isEqualToString:PTYPE(PBOARD)]) + return g_intern_string ("text/plain;charset=utf-8"); + else if ([type isEqualToString:PTYPE(URL)] || + [type isEqualToString:PTYPE(FILE_URL)]) + return g_intern_string ("text/uri-list"); + else if ([type isEqualToString:PTYPE(COLOR)]) + return g_intern_string ("application/x-color"); + else if ([type isEqualToString:PTYPE(TIFF)]) + return g_intern_string ("image/tiff"); + else if ([type isEqualToString:PTYPE(PNG)]) + return g_intern_string ("image/png"); + + G_GNUC_END_IGNORE_DEPRECATIONS; + + return NULL; +} + +NSPasteboardType +_gdk_macos_pasteboard_to_ns_type (const char *mime_type, + NSPasteboardType *alternate) +{ + if (alternate) + *alternate = NULL; + + if (g_strcmp0 (mime_type, "text/plain;charset=utf-8") == 0) + { + return PTYPE(STRING); + } + else if (g_strcmp0 (mime_type, "text/uri-list") == 0) + { + if (alternate) + *alternate = PTYPE(URL); + return PTYPE(FILE_URL); + } + else if (g_strcmp0 (mime_type, "application/x-color") == 0) + { + return PTYPE(COLOR); + } + else if (g_strcmp0 (mime_type, "image/tiff") == 0) + { + return PTYPE(TIFF); + } + else if (g_strcmp0 (mime_type, "image/png") == 0) + { + return PTYPE(PNG); + } + + return nil; +} + +static void +populate_content_formats (GdkContentFormatsBuilder *builder, + NSPasteboardType type) +{ + const char *mime_type; + + g_assert (builder != NULL); + g_assert (type != NULL); + + if ((mime_type = _gdk_macos_pasteboard_from_ns_type (type))) + gdk_content_formats_builder_add_mime_type (builder, mime_type); +} + +static GdkContentFormats * +load_offer_formats (NSPasteboard *pasteboard) +{ + GDK_BEGIN_MACOS_ALLOC_POOL; + + GdkContentFormatsBuilder *builder; + GdkContentFormats *formats; + + builder = gdk_content_formats_builder_new (); + for (NSPasteboardType type in [pasteboard types]) + populate_content_formats (builder, type); + formats = gdk_content_formats_builder_free_to_formats (builder); + + GDK_END_MACOS_ALLOC_POOL; + + return g_steal_pointer (&formats); +} + +GdkContentFormats * +_gdk_macos_pasteboard_load_formats (NSPasteboard *pasteboard) +{ + return load_offer_formats (pasteboard); +} + +void +_gdk_macos_pasteboard_send_content (NSPasteboard *pasteboard, + GdkContentProvider *content, + GdkMacosPasteboardDataProvider *dataProvider) +{ + GDK_BEGIN_MACOS_ALLOC_POOL; + + NSPasteboardItem *item; + + g_return_if_fail (pasteboard != NULL); + g_return_if_fail (GDK_IS_CONTENT_PROVIDER (content)); + + item = [[NSPasteboardItem alloc] init]; + [item setDataProvider:dataProvider forTypes:[dataProvider types]]; + + [pasteboard clearContents]; + if ([pasteboard writeObjects:[NSArray arrayWithObject:item]] == NO) + g_warning ("Failed to write object to pasteboard"); + + GDK_END_MACOS_ALLOC_POOL; +} + +@implementation GdkMacosPasteboardDataProvider + +-(id)initPasteboard:(NSPasteboard *)pasteBoard mimetypes:(const char * const *)mime_types; +{ + [super init]; + + self->mimeTypes = g_strdupv ((char **)mime_types); + self->pasteboard = [pasteBoard retain]; + + return self; +} + +-(void)dealloc +{ + g_cancellable_cancel (self->cancellable); + + if (self->pasteboard) + { + [self->pasteboard release]; + self->pasteboard = nil; + } + + g_clear_pointer (&self->mimeTypes, g_strfreev); + g_clear_object (&self->cancellable); + + [super dealloc]; +} + +-(void)pasteboardFinishedWithDataProvider:(NSPasteboard *)pasteboard +{ +} + +-(void)pasteboard:(NSPasteboard *)pasteboard item:(NSPasteboardItem *)item provideDataForType:(NSPasteboardType)type +{ +} + +-(NSArray *)types +{ + NSMutableArray *ret = [[NSMutableArray alloc] init]; + + for (guint i = 0; self->mimeTypes[i]; i++) + { + const char *mime_type = self->mimeTypes[i]; + NSPasteboardType type; + NSPasteboardType alternate = nil; + + if ((type = _gdk_macos_pasteboard_to_ns_type (mime_type, &alternate))) + { + [ret addObject:type]; + if (alternate) + [ret addObject:alternate]; + } + } + + return g_steal_pointer (&ret); +} + +@end + +static GInputStream * +create_stream_from_nsdata (NSData *data) +{ + const guint8 *bytes = [data bytes]; + gsize len = [data length]; + + return g_memory_input_stream_new_from_data (g_memdup2 (bytes, len), len, g_free); +} + +void +_gdk_macos_pasteboard_read_async (GObject *object, + NSPasteboard *pasteboard, + GdkContentFormats *formats, + int io_priority, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GDK_BEGIN_MACOS_ALLOC_POOL; + + GdkContentFormats *offer_formats = NULL; + const char *mime_type; + GInputStream *stream = NULL; + GTask *task = NULL; + + g_assert (G_IS_OBJECT (object)); + g_assert (pasteboard != NULL); + g_assert (formats != NULL); + + task = g_task_new (object, cancellable, callback, user_data); + g_task_set_source_tag (task, _gdk_macos_pasteboard_read_async); + g_task_set_priority (task, io_priority); + + offer_formats = load_offer_formats (pasteboard); + mime_type = gdk_content_formats_match_mime_type (formats, offer_formats); + + if (mime_type == NULL) + { + g_task_return_new_error (task, + G_IO_ERROR, + G_IO_ERROR_NOT_SUPPORTED, + "%s", + _("No compatible transfer format found")); + goto cleanup; + } + + if (strcmp (mime_type, "text/plain;charset=utf-8") == 0) + { + NSString *nsstr = [pasteboard stringForType:NSPasteboardTypeString]; + + if (nsstr != NULL) + { + const char *str = [nsstr UTF8String]; + stream = g_memory_input_stream_new_from_data (g_strdup (str), + strlen (str) + 1, + g_free); + } + } + else if (strcmp (mime_type, "text/uri-list") == 0) + { + G_GNUC_BEGIN_IGNORE_DEPRECATIONS; + + if ([[pasteboard types] containsObject:PTYPE(FILE_URL)]) + { + GString *str = g_string_new (NULL); + NSArray *files = [pasteboard propertyListForType:NSFilenamesPboardType]; + gsize n_files = [files count]; + char *data; + guint len; + + for (gsize i = 0; i < n_files; ++i) + { + NSString* uriString = [files objectAtIndex:i]; + uriString = [@"file://" stringByAppendingString:uriString]; + uriString = [uriString stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]; + + g_string_append_printf (str, + "%s\r\n", + [uriString cStringUsingEncoding:NSUTF8StringEncoding]); + } + + len = str->len; + data = g_string_free (str, FALSE); + stream = g_memory_input_stream_new_from_data (data, len, g_free); + } + + G_GNUC_END_IGNORE_DEPRECATIONS; + } + else if (strcmp (mime_type, "application/x-color") == 0) + { + NSColorSpace *colorspace; + NSColor *nscolor; + guint16 color[4]; + + colorspace = [NSColorSpace genericRGBColorSpace]; + nscolor = [[NSColor colorFromPasteboard:pasteboard] + colorUsingColorSpace:colorspace]; + + color[0] = 0xffff * [nscolor redComponent]; + color[1] = 0xffff * [nscolor greenComponent]; + color[2] = 0xffff * [nscolor blueComponent]; + color[3] = 0xffff * [nscolor alphaComponent]; + + stream = g_memory_input_stream_new_from_data (g_memdup2 (&color, sizeof color), + sizeof color, + g_free); + } + else if (strcmp (mime_type, "image/tiff") == 0) + { + NSData *data = [pasteboard dataForType:PTYPE(TIFF)]; + stream = create_stream_from_nsdata (data); + } + else if (strcmp (mime_type, "image/png") == 0) + { + NSData *data = [pasteboard dataForType:PTYPE(PNG)]; + stream = create_stream_from_nsdata (data); + } + + if (stream != NULL) + { + g_task_set_task_data (task, g_strdup (mime_type), g_free); + g_task_return_pointer (task, g_steal_pointer (&stream), g_object_unref); + } + else + { + g_task_return_new_error (task, + G_IO_ERROR, + G_IO_ERROR_FAILED, + _("Failed to decode contents with mime-type of '%s'"), + mime_type); + } + +cleanup: + g_clear_object (&task); + g_clear_pointer (&offer_formats, gdk_content_formats_unref); + + GDK_END_MACOS_ALLOC_POOL; +} + +GInputStream * +_gdk_macos_pasteboard_read_finish (GObject *object, + GAsyncResult *result, + const char **out_mime_type, + GError **error) +{ + GTask *task = (GTask *)result; + + g_assert (G_IS_OBJECT (object)); + g_assert (G_IS_TASK (task)); + + if (out_mime_type != NULL) + *out_mime_type = g_strdup (g_task_get_task_data (task)); + + return g_task_propagate_pointer (task, error); +} + +void +_gdk_macos_pasteboard_register_drag_types (NSWindow *window) +{ + [window registerForDraggedTypes:[NSArray arrayWithObjects:PTYPE(STRING), + PTYPE(PBOARD), + PTYPE(URL), + PTYPE(FILE_URL), + PTYPE(COLOR), + PTYPE(TIFF), + PTYPE(PNG), + nil]]; +} diff --git a/gdk/macos/meson.build b/gdk/macos/meson.build index bd7bbb5324..b3baefb898 100644 --- a/gdk/macos/meson.build +++ b/gdk/macos/meson.build @@ -19,6 +19,7 @@ gdk_macos_sources = files([ 'gdkmacoseventsource.c', 'gdkmacoskeymap.c', 'gdkmacosmonitor.c', + 'gdkmacospasteboard.c', 'gdkmacospopupsurface.c', 'gdkmacosseat.c', 'gdkmacossurface.c',