From eb5cf831b1a46a46ca88a9f7ee5e1cf267ff7aac Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Fri, 18 Jun 2021 11:27:54 -0700 Subject: [PATCH 01/14] 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. --- gdk/macos/GdkMacosWindow.c | 4 +- gdk/macos/gdkmacosclipboard-private.h | 22 +- gdk/macos/gdkmacosclipboard.c | 373 +--------------------- gdk/macos/gdkmacospasteboard-private.h | 64 ++++ gdk/macos/gdkmacospasteboard.c | 416 +++++++++++++++++++++++++ gdk/macos/meson.build | 1 + 6 files changed, 499 insertions(+), 381 deletions(-) create mode 100644 gdk/macos/gdkmacospasteboard-private.h create mode 100644 gdk/macos/gdkmacospasteboard.c 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', From 9c3629653f87d8535de4d473694ab77e30720b00 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Fri, 18 Jun 2021 19:51:13 -0700 Subject: [PATCH 02/14] macos: abstract pasteboard for use in clipboard and drag This will allow us to share a single NSPasteboardItem and data provider implementation for both GdkClipboard and GdkDrag. --- gdk/macos/gdkmacosclipboard-private.h | 9 - gdk/macos/gdkmacosclipboard.c | 150 +---------- gdk/macos/gdkmacospasteboard-private.h | 28 ++- gdk/macos/gdkmacospasteboard.c | 329 +++++++++++++++++++------ 4 files changed, 280 insertions(+), 236 deletions(-) diff --git a/gdk/macos/gdkmacosclipboard-private.h b/gdk/macos/gdkmacosclipboard-private.h index a1fa83f7ca..ef70ee3e33 100644 --- a/gdk/macos/gdkmacosclipboard-private.h +++ b/gdk/macos/gdkmacosclipboard-private.h @@ -43,15 +43,6 @@ NSPasteboardType _gdk_macos_clipboard_to_ns_type (const char const char *_gdk_macos_clipboard_from_ns_type (NSPasteboardType ns_type); void _gdk_macos_clipboard_register_drag_types (NSWindow *window); -@interface GdkMacosClipboardDataProvider : GdkMacosPasteboardDataProvider -{ - GdkClipboard *clipboard; -} - --(id)initClipboard:(GdkMacosClipboard *)gdkClipboard mimetypes:(const char * const *)mime_types; - -@end - G_END_DECLS #endif /* __GDK_MACOS_CLIPBOARD_PRIVATE_H__ */ diff --git a/gdk/macos/gdkmacosclipboard.c b/gdk/macos/gdkmacosclipboard.c index e647950364..4725784dfe 100644 --- a/gdk/macos/gdkmacosclipboard.c +++ b/gdk/macos/gdkmacosclipboard.c @@ -33,26 +33,8 @@ struct _GdkMacosClipboard NSInteger last_change_count; }; -typedef struct -{ - GMemoryOutputStream *stream; - NSPasteboardItem *item; - NSPasteboardType type; - GMainContext *main_context; - guint done : 1; -} WriteRequest; - G_DEFINE_TYPE (GdkMacosClipboard, _gdk_macos_clipboard, GDK_TYPE_CLIPBOARD) -static void -write_request_free (WriteRequest *wr) -{ - g_clear_pointer (&wr->main_context, g_main_context_unref); - g_clear_object (&wr->stream); - [wr->item release]; - g_slice_free (WriteRequest, wr); -} - static void _gdk_macos_clipboard_load_contents (GdkMacosClipboard *self) { @@ -100,25 +82,27 @@ static void _gdk_macos_clipboard_send_to_pasteboard (GdkMacosClipboard *self, GdkContentProvider *content) { - GdkMacosClipboardDataProvider *dataProvider; - GdkContentFormats *serializable; - const char * const *mime_types; - gsize n_mime_types; + GdkMacosPasteboardItem *item; + NSArray *items; 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); + if (self->pasteboard == NULL) + return; - dataProvider = [[GdkMacosClipboardDataProvider alloc] initClipboard:self mimetypes:mime_types]; - _gdk_macos_pasteboard_send_content (self->pasteboard, content, dataProvider); - [dataProvider release]; + GDK_BEGIN_MACOS_ALLOC_POOL; + + item = [[GdkMacosPasteboardItem alloc] initForClipboard:GDK_CLIPBOARD (self) withContentProvider:content]; + items = [NSArray arrayWithObject:item]; + + [self->pasteboard clearContents]; + if ([self->pasteboard writeObjects:items] == NO) + g_warning ("Failed to send clipboard to pasteboard"); self->last_change_count = [self->pasteboard changeCount]; - g_clear_pointer (&serializable, gdk_content_formats_unref); + GDK_END_MACOS_ALLOC_POOL; } static gboolean @@ -210,111 +194,3 @@ _gdk_macos_clipboard_check_externally_modified (GdkMacosClipboard *self) if ([self->pasteboard changeCount] != self->last_change_count) _gdk_macos_clipboard_load_contents (self); } - -@implementation GdkMacosClipboardDataProvider - --(id)initClipboard:(GdkMacosClipboard *)gdkClipboard mimetypes:(const char * const *)mime_types; -{ - [super initPasteboard:gdkClipboard->pasteboard mimetypes:mime_types]; - - self->clipboard = g_object_ref (GDK_CLIPBOARD (gdkClipboard)); - - return self; -} - --(void)dealloc -{ - g_clear_object (&self->clipboard); - - [super dealloc]; -} - --(void)pasteboardFinishedWithDataProvider:(NSPasteboard *)pasteboard -{ - g_clear_object (&self->clipboard); -} - -static void -on_data_ready_cb (GObject *object, - GAsyncResult *result, - gpointer user_data) -{ - GDK_BEGIN_MACOS_ALLOC_POOL; - - GdkClipboard *clipboard = (GdkClipboard *)object; - WriteRequest *wr = user_data; - GError *error = NULL; - NSData *data = nil; - - g_assert (GDK_IS_CLIPBOARD (clipboard)); - g_assert (G_IS_ASYNC_RESULT (result)); - g_assert (wr != NULL); - g_assert (G_IS_MEMORY_OUTPUT_STREAM (wr->stream)); - g_assert ([wr->item isKindOfClass:[NSPasteboardItem class]]); - - if (gdk_clipboard_write_finish (clipboard, result, &error)) - { - gsize size; - gpointer bytes; - - g_output_stream_close (G_OUTPUT_STREAM (wr->stream), NULL, NULL); - - size = g_memory_output_stream_get_data_size (wr->stream); - bytes = g_memory_output_stream_steal_data (wr->stream); - data = [[NSData alloc] initWithBytesNoCopy:bytes - length:size - deallocator:^(void *alloc, NSUInteger length) { g_free (alloc); }]; - } - else - { - g_warning ("Failed to serialize clipboard contents: %s", - error->message); - g_clear_error (&error); - } - - [wr->item setData:data forType:wr->type]; - - wr->done = TRUE; - - GDK_END_MACOS_ALLOC_POOL; -} - --(void)pasteboard:(NSPasteboard *)pasteboard item:(NSPasteboardItem *)item provideDataForType:(NSPasteboardType)type -{ - const char *mime_type = _gdk_macos_pasteboard_from_ns_type (type); - GMainContext *main_context = g_main_context_default (); - WriteRequest *wr; - - if (self->clipboard == NULL || mime_type == NULL) - { - [item setData:[NSData data] forType:type]; - return; - } - - wr = g_slice_new0 (WriteRequest); - wr->item = [item retain]; - wr->stream = G_MEMORY_OUTPUT_STREAM (g_memory_output_stream_new_resizable ()); - wr->type = type; - wr->main_context = g_main_context_ref (main_context); - wr->done = FALSE; - - gdk_clipboard_write_async (self->clipboard, - mime_type, - G_OUTPUT_STREAM (wr->stream), - G_PRIORITY_DEFAULT, - self->cancellable, - on_data_ready_cb, - wr); - - /* We're forced to provide data synchronously via this API - * so we must block on the main loop. Using another main loop - * than the default tends to get us locked up here, so that is - * what we'll do for now. - */ - while (!wr->done) - g_main_context_iteration (wr->main_context, TRUE); - - write_request_free (wr); -} - -@end diff --git a/gdk/macos/gdkmacospasteboard-private.h b/gdk/macos/gdkmacospasteboard-private.h index c135d82d58..fdeb936535 100644 --- a/gdk/macos/gdkmacospasteboard-private.h +++ b/gdk/macos/gdkmacospasteboard-private.h @@ -27,15 +27,28 @@ G_BEGIN_DECLS -@interface GdkMacosPasteboardDataProvider : NSObject +@interface GdkMacosPasteboardItemDataProvider : NSObject { - NSPasteboard *pasteboard; - GCancellable *cancellable; - char **mimeTypes; + GdkContentProvider *_contentProvider; + GdkClipboard *_clipboard; + GdkDrag *_drag; } --(id)initPasteboard:(NSPasteboard *)pasteBoard mimetypes:(const char * const *)mime_types; --(NSArray *)types; +-(id)initForClipboard:(GdkClipboard *)clipboard withContentProvider:(GdkContentProvider *)contentProvider; +-(id)initForDrag:(GdkDrag *)drag withContentProvider:(GdkContentProvider *)contentProvider; + +@end + +@interface GdkMacosPasteboardItem : NSPasteboardItem +{ + GdkContentProvider *_contentProvider; + GdkClipboard *_clipboard; + GdkDrag *_drag; + NSRect _draggingFrame; +} + +-(id)initForClipboard:(GdkClipboard *)clipboard withContentProvider:(GdkContentProvider *)contentProvider; +-(id)initForDrag:(GdkDrag *)drag withContentProvider:(GdkContentProvider *)contentProvider; @end @@ -55,9 +68,6 @@ GInputStream *_gdk_macos_pasteboard_read_finish (GObject GAsyncResult *result, const char **out_mime_type, GError **error); -void _gdk_macos_pasteboard_send_content (NSPasteboard *pasteboard, - GdkContentProvider *content, - GdkMacosPasteboardDataProvider *dataProvider); G_END_DECLS diff --git a/gdk/macos/gdkmacospasteboard.c b/gdk/macos/gdkmacospasteboard.c index 73e6af07cd..f74f9818cd 100644 --- a/gdk/macos/gdkmacospasteboard.c +++ b/gdk/macos/gdkmacospasteboard.c @@ -21,6 +21,7 @@ #include +#include "gdkdragprivate.h" #include "gdkmacospasteboard-private.h" #include "gdkmacosutils-private.h" @@ -165,87 +166,6 @@ _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) { @@ -414,3 +334,250 @@ _gdk_macos_pasteboard_register_drag_types (NSWindow *window) PTYPE(PNG), nil]]; } + +@implementation GdkMacosPasteboardItemDataProvider + +-(id)initForClipboard:(GdkClipboard*)clipboard withContentProvider:(GdkContentProvider*)contentProvider +{ + [super init]; + g_set_object (&self->_clipboard, clipboard); + g_set_object (&self->_contentProvider, contentProvider); + return self; +} + +-(id)initForDrag:(GdkDrag*)drag withContentProvider:(GdkContentProvider*)contentProvider +{ + [super init]; + g_set_object (&self->_drag, drag); + g_set_object (&self->_contentProvider, contentProvider); + return self; +} + +-(void)dealloc +{ + g_clear_object (&self->_contentProvider); + g_clear_object (&self->_clipboard); + g_clear_object (&self->_drag); + [super dealloc]; +} + +-(NSArray *)types +{ + NSMutableArray *ret = [[NSMutableArray alloc] init]; + GdkContentFormats *serializable; + const char * const *mime_types; + gsize n_mime_types; + + serializable = gdk_content_provider_ref_storable_formats (self->_contentProvider); + serializable = gdk_content_formats_union_serialize_mime_types (serializable); + mime_types = gdk_content_formats_get_mime_types (serializable, &n_mime_types); + + for (guint i = 0; mime_types[i]; i++) + { + const char *mime_type = mime_types[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); +} + +typedef struct +{ + GMemoryOutputStream *stream; + NSPasteboardItem *item; + NSPasteboardType type; + GMainContext *main_context; + guint done : 1; +} WriteRequest; + +static void +write_request_free (WriteRequest *wr) +{ + g_clear_pointer (&wr->main_context, g_main_context_unref); + g_clear_object (&wr->stream); + [wr->item release]; + g_slice_free (WriteRequest, wr); +} + +static void +on_data_ready_cb (GObject *object, + GAsyncResult *result, + gpointer user_data) +{ + GDK_BEGIN_MACOS_ALLOC_POOL; + + WriteRequest *wr = user_data; + GError *error = NULL; + NSData *data = nil; + gboolean ret; + + g_assert (G_IS_OBJECT (object)); + g_assert (GDK_IS_CLIPBOARD (object) || GDK_IS_DRAG (object)); + g_assert (G_IS_ASYNC_RESULT (result)); + g_assert (wr != NULL); + g_assert (G_IS_MEMORY_OUTPUT_STREAM (wr->stream)); + g_assert ([wr->item isKindOfClass:[NSPasteboardItem class]]); + + if (GDK_IS_CLIPBOARD (object)) + ret = gdk_clipboard_write_finish (GDK_CLIPBOARD (object), result, &error); + else if (GDK_IS_DRAG (object)) + ret = gdk_drag_write_finish (GDK_DRAG (object), result, &error); + else + g_return_if_reached (); + + if (ret) + { + gsize size; + gpointer bytes; + + g_output_stream_close (G_OUTPUT_STREAM (wr->stream), NULL, NULL); + + size = g_memory_output_stream_get_data_size (wr->stream); + bytes = g_memory_output_stream_steal_data (wr->stream); + data = [[NSData alloc] initWithBytesNoCopy:bytes + length:size + deallocator:^(void *alloc, NSUInteger length) { g_free (alloc); }]; + } + else + { + g_warning ("Failed to serialize pasteboard contents: %s", + error->message); + g_clear_error (&error); + } + + [wr->item setData:data forType:wr->type]; + + wr->done = TRUE; + + GDK_END_MACOS_ALLOC_POOL; +} + +-(void)pasteboard:(NSPasteboard *)pasteboard item:(NSPasteboardItem *)item provideDataForType:(NSPasteboardType)type +{ + const char *mime_type = _gdk_macos_pasteboard_from_ns_type (type); + GMainContext *main_context = g_main_context_default (); + WriteRequest *wr; + + if (self->_contentProvider == NULL || mime_type == NULL) + { + [item setData:[NSData data] forType:type]; + return; + } + + wr = g_slice_new0 (WriteRequest); + wr->item = [item retain]; + wr->stream = G_MEMORY_OUTPUT_STREAM (g_memory_output_stream_new_resizable ()); + wr->type = type; + wr->main_context = g_main_context_ref (main_context); + wr->done = FALSE; + + if (GDK_IS_CLIPBOARD (self->_clipboard)) + gdk_clipboard_write_async (self->_clipboard, + mime_type, + G_OUTPUT_STREAM (wr->stream), + G_PRIORITY_DEFAULT, + NULL, + on_data_ready_cb, + wr); + else if (GDK_IS_DRAG (self->_drag)) + gdk_drag_write_async (self->_drag, + mime_type, + G_OUTPUT_STREAM (wr->stream), + G_PRIORITY_DEFAULT, + NULL, + on_data_ready_cb, + wr); + else + g_return_if_reached (); + + /* We're forced to provide data synchronously via this API + * so we must block on the main loop. Using another main loop + * than the default tends to get us locked up here, so that is + * what we'll do for now. + */ + while (!wr->done) + g_main_context_iteration (wr->main_context, TRUE); + + write_request_free (wr); +} + +-(void)pasteboardFinishedWithDataProvider:(NSPasteboard *)pasteboard +{ + g_clear_object (&self->_clipboard); + g_clear_object (&self->_drag); + g_clear_object (&self->_contentProvider); +} + +@end + +@implementation GdkMacosPasteboardItem + +-(id)initForClipboard:(GdkClipboard*)clipboard withContentProvider:(GdkContentProvider*)contentProvider +{ + GdkMacosPasteboardItemDataProvider *dataProvider; + + dataProvider = [[GdkMacosPasteboardItemDataProvider alloc] initForClipboard:clipboard withContentProvider:contentProvider]; + + [super init]; + g_set_object (&self->_clipboard, clipboard); + g_set_object (&self->_contentProvider, contentProvider); + [self setDataProvider:dataProvider forTypes:[dataProvider types]]; + + [dataProvider release]; + + return self; +} + +-(id)initForDrag:(GdkDrag*)drag withContentProvider:(GdkContentProvider*)contentProvider +{ + GdkMacosPasteboardItemDataProvider *dataProvider; + + dataProvider = [[GdkMacosPasteboardItemDataProvider alloc] initForDrag:drag withContentProvider:contentProvider]; + + [super init]; + g_set_object (&self->_drag, drag); + g_set_object (&self->_contentProvider, contentProvider); + [self setDataProvider:dataProvider forTypes:[dataProvider types]]; + + [dataProvider release]; + + return self; +} + +-(void)dealloc +{ + g_clear_object (&self->_contentProvider); + g_clear_object (&self->_clipboard); + g_clear_object (&self->_drag); + [super dealloc]; +} + +-(NSRect)draggingFrame +{ + return self->_draggingFrame; +} + +-(void)setDraggingFrame:(NSRect)draggingFrame; +{ + self->_draggingFrame = draggingFrame; +} + +-(id)item +{ + return self; +} + +-(NSArray* (^) (void))imageComponentsProvider +{ + return nil; +} + +@end From 420be8fb0f008959d4d5a0be0c1e98cba5a54eb7 Mon Sep 17 00:00:00 2001 From: Arjan Molenaar Date: Wed, 11 Jan 2023 16:29:24 +0100 Subject: [PATCH 03/14] macos: run glib idle functions when in nested run loop It looks like DnD starts a nested run loop. We still have to run our GLib handlers, so animations work. --- gdk/macos/gdkmacoseventsource.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/gdk/macos/gdkmacoseventsource.c b/gdk/macos/gdkmacoseventsource.c index a60fb558c9..725cc57476 100644 --- a/gdk/macos/gdkmacoseventsource.c +++ b/gdk/macos/gdkmacoseventsource.c @@ -1018,7 +1018,10 @@ run_loop_observer_callback (CFRunLoopObserverRef observer, break; } - if (getting_events > 0) /* Activity we triggered */ + /* DnD starts a nested runloop, or so it seems. + If we have such a loop, we still want to run + our idle handlers. */ + if (getting_events > 0 && current_loop_level < 2) return; switch (activity) From b9847795a7d060d3f2d97000c4a6d738d7fece97 Mon Sep 17 00:00:00 2001 From: Arjan Molenaar Date: Mon, 9 Jan 2023 23:01:12 +0100 Subject: [PATCH 04/14] macos: Support dragging from GdkMacosWindow The handling is done similar to drag targets. Note that dragging is a modal action on macos: no events are sent to the main window. This could cause trouble when we finish the drag, and not finish the gesture in GTK. --- gdk/macos/GdkMacosWindow.c | 52 +++++++++++++++- gdk/macos/GdkMacosWindow.h | 2 +- gdk/macos/gdkmacosdisplay-private.h | 1 + gdk/macos/gdkmacosdisplay.c | 10 +++ gdk/macos/gdkmacosdrag-private.h | 16 ++++- gdk/macos/gdkmacosdrag.c | 97 ++++++++++++++++++++++++++++- gdk/macos/gdkmacossurface.c | 2 +- 7 files changed, 172 insertions(+), 8 deletions(-) diff --git a/gdk/macos/GdkMacosWindow.c b/gdk/macos/GdkMacosWindow.c index 7680e6d8b0..79e2ec8001 100644 --- a/gdk/macos/GdkMacosWindow.c +++ b/gdk/macos/GdkMacosWindow.c @@ -28,6 +28,7 @@ #import "GdkMacosWindow.h" #include "gdkmacosdisplay-private.h" +#include "gdkmacosdrag-private.h" #include "gdkmacosdrop-private.h" #include "gdkmacosmonitor-private.h" #include "gdkmacospasteboard-private.h" @@ -668,7 +669,56 @@ typedef NSString *CALayerContentsGravity; } // NSDraggingSource protocol -// ... + +- (NSDragOperation)draggingSession:(NSDraggingSession *)session sourceOperationMaskForDraggingContext:(NSDraggingContext)context +{ + NSInteger sequence_number = [session draggingSequenceNumber]; + GdkDisplay *display = gdk_surface_get_display (GDK_SURFACE (gdk_surface)); + GdkDrag *drag = _gdk_macos_display_find_drag (GDK_MACOS_DISPLAY (display), sequence_number); + GdkModifierType state = _gdk_macos_display_get_current_keyboard_modifiers (GDK_MACOS_DISPLAY (display)); + + _gdk_macos_drag_set_actions (GDK_MACOS_DRAG (drag), state); + + return _gdk_macos_drag_operation (GDK_MACOS_DRAG (drag)); +} + +- (void)draggingSession:(NSDraggingSession *)session willBeginAtPoint:(NSPoint)screenPoint +{ + NSInteger sequence_number = [session draggingSequenceNumber]; + GdkDisplay *display = gdk_surface_get_display (GDK_SURFACE (gdk_surface)); + GdkDrag *drag = _gdk_macos_display_find_drag (GDK_MACOS_DISPLAY (display), sequence_number); + int x, y; + + _gdk_macos_display_from_display_coords (GDK_MACOS_DISPLAY (display), screenPoint.x, screenPoint.y, &x, &y); + _gdk_macos_drag_set_start_position (GDK_MACOS_DRAG (drag), x, y); + _gdk_macos_drag_surface_move (GDK_MACOS_DRAG (drag), x, y); +} + +- (void)draggingSession:(NSDraggingSession *)session movedToPoint:(NSPoint)screenPoint +{ + NSInteger sequence_number = [session draggingSequenceNumber]; + GdkDisplay *display = gdk_surface_get_display (GDK_SURFACE (gdk_surface)); + GdkDrag *drag = _gdk_macos_display_find_drag (GDK_MACOS_DISPLAY (display), sequence_number); + int x, y; + + _gdk_macos_display_from_display_coords (GDK_MACOS_DISPLAY (display), screenPoint.x, screenPoint.y, &x, &y); + _gdk_macos_drag_surface_move (GDK_MACOS_DRAG (drag), x, y); +} + +- (void)draggingSession:(NSDraggingSession *)session endedAtPoint:(NSPoint)screenPoint operation:(NSDragOperation)operation +{ + NSInteger sequence_number = [session draggingSequenceNumber]; + GdkDisplay *display = gdk_surface_get_display (GDK_SURFACE (gdk_surface)); + GdkDrag *drag = _gdk_macos_display_find_drag (GDK_MACOS_DISPLAY (display), sequence_number); + + if (gdk_drag_get_selected_action (drag) != 0) + g_signal_emit_by_name (drag, "drop-performed"); + else + gdk_drag_cancel (drag, GDK_DRAG_CANCEL_NO_TARGET); + + _gdk_macos_display_set_drag (GDK_MACOS_DISPLAY (display), [session draggingSequenceNumber], NULL); +} + // end -(void)setStyleMask:(NSWindowStyleMask)styleMask diff --git a/gdk/macos/GdkMacosWindow.h b/gdk/macos/GdkMacosWindow.h index 1cf9ec805c..66fcd45717 100644 --- a/gdk/macos/GdkMacosWindow.h +++ b/gdk/macos/GdkMacosWindow.h @@ -32,7 +32,7 @@ #define GDK_IS_MACOS_WINDOW(obj) ([obj isKindOfClass:[GdkMacosWindow class]]) -@interface GdkMacosWindow : NSWindow { +@interface GdkMacosWindow : NSWindow { GdkMacosSurface *gdk_surface; BOOL inMove; diff --git a/gdk/macos/gdkmacosdisplay-private.h b/gdk/macos/gdkmacosdisplay-private.h index 83ae435e49..122d004cec 100644 --- a/gdk/macos/gdkmacosdisplay-private.h +++ b/gdk/macos/gdkmacosdisplay-private.h @@ -161,6 +161,7 @@ void _gdk_macos_display_warp_pointer (GdkMacosDisp int x, int y); NSEvent *_gdk_macos_display_get_nsevent (GdkEvent *event); +NSEvent *_gdk_macos_display_get_last_nsevent (void); GdkDrag *_gdk_macos_display_find_drag (GdkMacosDisplay *self, NSInteger sequence_number); GdkDrop *_gdk_macos_display_find_drop (GdkMacosDisplay *self, diff --git a/gdk/macos/gdkmacosdisplay.c b/gdk/macos/gdkmacosdisplay.c index 0e5a9b8eb6..b9869fae7c 100644 --- a/gdk/macos/gdkmacosdisplay.c +++ b/gdk/macos/gdkmacosdisplay.c @@ -1024,6 +1024,16 @@ _gdk_macos_display_get_nsevent (GdkEvent *event) return NULL; } +NSEvent * +_gdk_macos_display_get_last_nsevent () +{ + const GdkToNSEventMap *map = g_queue_peek_tail (&event_map); + if (map) + return map->nsevent; + + return NULL; +} + GdkDrag * _gdk_macos_display_find_drag (GdkMacosDisplay *self, NSInteger sequence_number) diff --git a/gdk/macos/gdkmacosdrag-private.h b/gdk/macos/gdkmacosdrag-private.h index 98075f27ef..fbc525d247 100644 --- a/gdk/macos/gdkmacosdrag-private.h +++ b/gdk/macos/gdkmacosdrag-private.h @@ -62,8 +62,20 @@ struct _GdkMacosDragClass GdkDragClass parent_class; }; -GType gdk_macos_drag_get_type (void) G_GNUC_CONST; -gboolean _gdk_macos_drag_begin (GdkMacosDrag *self); +GType gdk_macos_drag_get_type (void) G_GNUC_CONST; +gboolean _gdk_macos_drag_begin (GdkMacosDrag *self, + GdkContentProvider *content, + GdkMacosWindow *window); +NSDragOperation _gdk_macos_drag_operation (GdkMacosDrag *self); +void _gdk_macos_drag_surface_move (GdkMacosDrag *self, + int x_root, + int y_root); +void _gdk_macos_drag_set_start_position (GdkMacosDrag *self, + int start_x, + int start_y); +void _gdk_macos_drag_set_actions (GdkMacosDrag *self, + GdkModifierType mods); + G_END_DECLS diff --git a/gdk/macos/gdkmacosdrag.c b/gdk/macos/gdkmacosdrag.c index 10a756998d..50c1ac814d 100644 --- a/gdk/macos/gdkmacosdrag.c +++ b/gdk/macos/gdkmacosdrag.c @@ -25,6 +25,7 @@ #include "gdkmacoscursor-private.h" #include "gdkmacosdisplay-private.h" #include "gdkmacosdragsurface-private.h" +#include "gdkmacospasteboard-private.h" #include "gdk/gdkdeviceprivate.h" #include "gdk/gdkeventsprivate.h" @@ -624,11 +625,101 @@ gdk_macos_drag_init (GdkMacosDrag *self) } gboolean -_gdk_macos_drag_begin (GdkMacosDrag *self) +_gdk_macos_drag_begin (GdkMacosDrag *self, + GdkContentProvider *content, + GdkMacosWindow *window) { + NSArray *items; + NSDraggingSession *session; + NSPasteboardItem *item; + NSEvent *nsevent; + g_return_val_if_fail (GDK_IS_MACOS_DRAG (self), FALSE); + g_return_val_if_fail (GDK_IS_MACOS_WINDOW (window), FALSE); - _gdk_macos_surface_show (GDK_MACOS_SURFACE (self->drag_surface)); + GDK_BEGIN_MACOS_ALLOC_POOL; - return drag_grab (self); + item = [[GdkMacosPasteboardItem alloc] initForDrag:GDK_DRAG (self) withContentProvider:content]; + items = [NSArray arrayWithObject:item]; + nsevent = _gdk_macos_display_get_last_nsevent (); + + session = [[window contentView] beginDraggingSessionWithItems:items + event:nsevent + source:window]; + + GDK_END_MACOS_ALLOC_POOL; + + _gdk_macos_display_set_drag (GDK_MACOS_DISPLAY (gdk_drag_get_display (GDK_DRAG (self))), + [session draggingSequenceNumber], + GDK_DRAG (self)); + + return TRUE; +} + +NSDragOperation +_gdk_macos_drag_operation (GdkMacosDrag *self) +{ + NSDragOperation operation = NSDragOperationNone; + GdkDragAction actions; + + g_return_val_if_fail (GDK_IS_MACOS_DRAG (self), NSDragOperationNone); + + actions = gdk_drag_get_actions (GDK_DRAG (self)); + + if (actions & GDK_ACTION_LINK) + operation |= NSDragOperationLink; + + if (actions & GDK_ACTION_MOVE) + operation |= NSDragOperationMove; + + if (actions & GDK_ACTION_COPY) + operation |= NSDragOperationCopy; + + return operation; +} + +void +_gdk_macos_drag_surface_move (GdkMacosDrag *self, + int x_root, + int y_root) +{ + g_return_if_fail (GDK_IS_MACOS_DRAG (self)); + + self->last_x = x_root; + self->last_y = y_root; + + if (GDK_IS_MACOS_SURFACE (self->drag_surface)) + _gdk_macos_surface_move (GDK_MACOS_SURFACE (self->drag_surface), + x_root - self->hot_x, + y_root - self->hot_y); +} + +void +_gdk_macos_drag_set_start_position (GdkMacosDrag *self, + int start_x, + int start_y) +{ + g_return_if_fail (GDK_IS_MACOS_DRAG (self)); + + self->start_x = start_x; + self->start_y = start_y; +} + +void +_gdk_macos_drag_set_actions (GdkMacosDrag *self, + GdkModifierType mods) +{ + GdkDragAction suggested_action; + GdkDragAction possible_actions; + + g_assert (GDK_IS_MACOS_DRAG (self)); + + gdk_drag_get_current_actions (mods, + GDK_BUTTON_PRIMARY, + gdk_drag_get_actions (GDK_DRAG (self)), + &suggested_action, + &possible_actions); + + gdk_drag_set_selected_action (GDK_DRAG (self), suggested_action); + gdk_drag_set_actions (GDK_DRAG (self), possible_actions); } diff --git a/gdk/macos/gdkmacossurface.c b/gdk/macos/gdkmacossurface.c index 37400d1892..a002dc7db3 100644 --- a/gdk/macos/gdkmacossurface.c +++ b/gdk/macos/gdkmacossurface.c @@ -446,7 +446,7 @@ gdk_macos_surface_drag_begin (GdkSurface *surface, gdk_drag_get_selected_action (GDK_DRAG (drag))); gdk_drag_set_cursor (GDK_DRAG (drag), cursor); - if (!_gdk_macos_drag_begin (drag)) + if (!_gdk_macos_drag_begin (drag, content, self->window)) { g_object_unref (drag); return NULL; From 27ee8b23fde000b481acb88662ec6a9cff54b5c8 Mon Sep 17 00:00:00 2001 From: Arjan Molenaar Date: Wed, 11 Jan 2023 18:56:01 +0100 Subject: [PATCH 05/14] macos: No need to grab a seat The macOS DnD logic will take over anyway. --- gdk/macos/gdkmacosdrag-private.h | 1 - gdk/macos/gdkmacosdrag.c | 48 -------------------------------- 2 files changed, 49 deletions(-) diff --git a/gdk/macos/gdkmacosdrag-private.h b/gdk/macos/gdkmacosdrag-private.h index fbc525d247..ab46828d5e 100644 --- a/gdk/macos/gdkmacosdrag-private.h +++ b/gdk/macos/gdkmacosdrag-private.h @@ -41,7 +41,6 @@ struct _GdkMacosDrag GdkDrag parent_instance; GdkMacosDragSurface *drag_surface; - GdkSeat *drag_seat; GdkCursor *cursor; int hot_x; diff --git a/gdk/macos/gdkmacosdrag.c b/gdk/macos/gdkmacosdrag.c index 50c1ac814d..5c11da851b 100644 --- a/gdk/macos/gdkmacosdrag.c +++ b/gdk/macos/gdkmacosdrag.c @@ -188,47 +188,6 @@ gdk_macos_drag_set_cursor (GdkDrag *drag, [nscursor set]; } -static gboolean -drag_grab (GdkMacosDrag *self) -{ - GdkSeat *seat; - - g_assert (GDK_IS_MACOS_DRAG (self)); - - seat = gdk_device_get_seat (gdk_drag_get_device (GDK_DRAG (self))); - - if (gdk_seat_grab (seat, - GDK_SURFACE (self->drag_surface), - GDK_SEAT_CAPABILITY_ALL_POINTING, - FALSE, - self->cursor, - NULL, - NULL, - NULL) != GDK_GRAB_SUCCESS) - return FALSE; - - g_set_object (&self->drag_seat, seat); - - return TRUE; -} - -static void -drag_ungrab (GdkMacosDrag *self) -{ - GdkDisplay *display; - - g_assert (GDK_IS_MACOS_DRAG (self)); - - if (self->drag_seat) - { - gdk_seat_ungrab (self->drag_seat); - g_clear_object (&self->drag_seat); - } - - display = gdk_drag_get_display (GDK_DRAG (self)); - _gdk_macos_display_break_all_grabs (GDK_MACOS_DISPLAY (display), GDK_CURRENT_TIME); -} - static void gdk_macos_drag_cancel (GdkDrag *drag, GdkDragCancelReason reason) @@ -241,7 +200,6 @@ gdk_macos_drag_cancel (GdkDrag *drag, return; self->cancelled = TRUE; - drag_ungrab (self); gdk_drag_drop_done (drag, FALSE); } @@ -254,7 +212,6 @@ gdk_macos_drag_drop_performed (GdkDrag *drag, g_assert (GDK_IS_MACOS_DRAG (self)); g_object_ref (self); - drag_ungrab (self); g_signal_emit_by_name (drag, "dnd-finished"); gdk_drag_drop_done (drag, TRUE); g_object_unref (self); @@ -543,11 +500,6 @@ gdk_macos_drag_finalize (GObject *object) GdkMacosDragSurface *drag_surface = g_steal_pointer (&self->drag_surface); g_clear_object (&self->cursor); - if (self->drag_seat) - { - gdk_seat_ungrab (self->drag_seat); - g_clear_object (&self->drag_seat); - } G_OBJECT_CLASS (gdk_macos_drag_parent_class)->finalize (object); From 422b4b6561b0a258b0ea58ca8d5c0295fb8bff26 Mon Sep 17 00:00:00 2001 From: Arjan Molenaar Date: Wed, 11 Jan 2023 19:01:05 +0100 Subject: [PATCH 06/14] macos: No need to drag handle events This is handled by the DraggingSource object. --- gdk/macos/gdkmacosdrag.c | 220 --------------------------------------- 1 file changed, 220 deletions(-) diff --git a/gdk/macos/gdkmacosdrag.c b/gdk/macos/gdkmacosdrag.c index 5c11da851b..b7549e5b9e 100644 --- a/gdk/macos/gdkmacosdrag.c +++ b/gdk/macos/gdkmacosdrag.c @@ -274,225 +274,6 @@ gdk_drag_get_current_actions (GdkModifierType state, } } -static void -gdk_drag_update (GdkDrag *drag, - double x_root, - double y_root, - GdkModifierType mods, - guint32 evtime) -{ - GdkMacosDrag *self = (GdkMacosDrag *)drag; - GdkDragAction suggested_action; - GdkDragAction possible_actions; - - g_assert (GDK_IS_MACOS_DRAG (self)); - - self->last_x = x_root; - self->last_y = y_root; - - gdk_drag_get_current_actions (mods, - GDK_BUTTON_PRIMARY, - gdk_drag_get_actions (drag), - &suggested_action, - &possible_actions); - - if (GDK_IS_MACOS_SURFACE (self->drag_surface)) - _gdk_macos_surface_move (GDK_MACOS_SURFACE (self->drag_surface), - x_root - self->hot_x, - y_root - self->hot_y); - - if (!self->did_update) - { - self->start_x = self->last_x; - self->start_y = self->last_y; - self->did_update = TRUE; - } - - gdk_drag_set_actions (drag, possible_actions); -} - -static gboolean -gdk_dnd_handle_motion_event (GdkDrag *drag, - GdkEvent *event) -{ - double x, y; - int x_root, y_root; - - g_assert (GDK_IS_MACOS_DRAG (drag)); - g_assert (event != NULL); - - /* Ignore motion while doing zoomback */ - if (GDK_MACOS_DRAG (drag)->cancelled) - return FALSE; - - gdk_event_get_position (event, &x, &y); - x_root = event->surface->x + x; - y_root = event->surface->y + y; - gdk_drag_update (drag, x_root, y_root, - gdk_event_get_modifier_state (event), - gdk_event_get_time (event)); - - return TRUE; -} - -static gboolean -gdk_dnd_handle_grab_broken_event (GdkDrag *drag, - GdkEvent *event) -{ - GdkMacosDrag *self = GDK_MACOS_DRAG (drag); - gboolean is_implicit = gdk_grab_broken_event_get_implicit (event); - GdkSurface *grab_surface = gdk_grab_broken_event_get_grab_surface (event); - - /* Don't cancel if we break the implicit grab from the initial button_press. */ - if (is_implicit || grab_surface == (GdkSurface *)self->drag_surface) - return FALSE; - - if (gdk_event_get_device (event) != gdk_drag_get_device (drag)) - return FALSE; - - gdk_drag_cancel (drag, GDK_DRAG_CANCEL_ERROR); - - return TRUE; -} - -static gboolean -gdk_dnd_handle_button_event (GdkDrag *drag, - GdkEvent *event) -{ - GdkMacosDrag *self = GDK_MACOS_DRAG (drag); - - g_assert (GDK_IS_MACOS_DRAG (self)); - g_assert (event != NULL); - -#if 0 - /* FIXME: Check the button matches */ - if (event->button != self->button) - return FALSE; -#endif - - if (gdk_drag_get_selected_action (drag) != 0) - g_signal_emit_by_name (drag, "drop-performed"); - else - gdk_drag_cancel (drag, GDK_DRAG_CANCEL_NO_TARGET); - - return TRUE; -} - -static gboolean -gdk_dnd_handle_key_event (GdkDrag *drag, - GdkEvent *event) -{ - GdkMacosDrag *self = GDK_MACOS_DRAG (drag); - GdkModifierType state; - GdkDevice *pointer; - GdkSeat *seat; - int dx, dy; - - dx = dy = 0; - state = gdk_event_get_modifier_state (event); - seat = gdk_event_get_seat (event); - pointer = gdk_seat_get_pointer (seat); - - if (event->event_type == GDK_KEY_PRESS) - { - guint keyval = gdk_key_event_get_keyval (event); - - switch (keyval) - { - case GDK_KEY_Escape: - gdk_drag_cancel (drag, GDK_DRAG_CANCEL_USER_CANCELLED); - return TRUE; - - case GDK_KEY_space: - case GDK_KEY_Return: - case GDK_KEY_ISO_Enter: - case GDK_KEY_KP_Enter: - case GDK_KEY_KP_Space: - if (gdk_drag_get_selected_action (drag) != 0) - g_signal_emit_by_name (drag, "drop-performed"); - else - gdk_drag_cancel (drag, GDK_DRAG_CANCEL_NO_TARGET); - - return TRUE; - - case GDK_KEY_Up: - case GDK_KEY_KP_Up: - dy = (state & GDK_ALT_MASK) ? -BIG_STEP : -SMALL_STEP; - break; - - case GDK_KEY_Down: - case GDK_KEY_KP_Down: - dy = (state & GDK_ALT_MASK) ? BIG_STEP : SMALL_STEP; - break; - - case GDK_KEY_Left: - case GDK_KEY_KP_Left: - dx = (state & GDK_ALT_MASK) ? -BIG_STEP : -SMALL_STEP; - break; - - case GDK_KEY_Right: - case GDK_KEY_KP_Right: - dx = (state & GDK_ALT_MASK) ? BIG_STEP : SMALL_STEP; - break; - - default: - break; - } - } - - /* The state is not yet updated in the event, so we need - * to query it here. We could use XGetModifierMapping, but - * that would be overkill. - */ - gdk_macos_device_query_state (pointer, NULL, NULL, NULL, NULL, &state); - - if (dx != 0 || dy != 0) - { - GdkDisplay *display = gdk_event_get_display ((GdkEvent *)event); - - self->last_x += dx; - self->last_y += dy; - - _gdk_macos_display_warp_pointer (GDK_MACOS_DISPLAY (display), - self->last_x, - self->last_y); - } - - gdk_drag_update (drag, - self->last_x, self->last_y, - state, - gdk_event_get_time (event)); - - return TRUE; -} - -static gboolean -gdk_macos_drag_handle_event (GdkDrag *drag, - GdkEvent *event) -{ - g_assert (GDK_IS_MACOS_DRAG (drag)); - g_assert (event != NULL); - - switch ((guint) event->event_type) - { - case GDK_MOTION_NOTIFY: - return gdk_dnd_handle_motion_event (drag, event); - - case GDK_BUTTON_RELEASE: - return gdk_dnd_handle_button_event (drag, event); - - case GDK_KEY_PRESS: - case GDK_KEY_RELEASE: - return gdk_dnd_handle_key_event (drag, event); - - case GDK_GRAB_BROKEN: - return gdk_dnd_handle_grab_broken_event (drag, event); - - default: - return FALSE; - } -} - static void gdk_macos_drag_finalize (GObject *object) { @@ -561,7 +342,6 @@ gdk_macos_drag_class_init (GdkMacosDragClass *klass) drag_class->set_cursor = gdk_macos_drag_set_cursor; drag_class->cancel = gdk_macos_drag_cancel; drag_class->drop_performed = gdk_macos_drag_drop_performed; - drag_class->handle_event = gdk_macos_drag_handle_event; properties [PROP_DRAG_SURFACE] = g_param_spec_object ("drag-surface", NULL, NULL, From 1d8a23e97bb1e34c6e99fe77e7b3fb5a33d720a7 Mon Sep 17 00:00:00 2001 From: Arjan Molenaar Date: Wed, 11 Jan 2023 23:17:24 +0100 Subject: [PATCH 07/14] macos: Provide fallback pasteboard for gtype Dnd The Drag data should pass through the macos pasteboard system. We need to provide some pasteboard type. Let's make it a "URL", which is a pretty generic type anyway. --- gdk/macos/gdkmacospasteboard.c | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/gdk/macos/gdkmacospasteboard.c b/gdk/macos/gdkmacospasteboard.c index f74f9818cd..66b3c9f03b 100644 --- a/gdk/macos/gdkmacospasteboard.c +++ b/gdk/macos/gdkmacospasteboard.c @@ -372,7 +372,7 @@ _gdk_macos_pasteboard_register_drag_types (NSWindow *window) serializable = gdk_content_formats_union_serialize_mime_types (serializable); mime_types = gdk_content_formats_get_mime_types (serializable, &n_mime_types); - for (guint i = 0; mime_types[i]; i++) + for (gsize i = 0; i < n_mime_types; i++) { const char *mime_type = mime_types[i]; NSPasteboardType type; @@ -386,6 +386,25 @@ _gdk_macos_pasteboard_register_drag_types (NSWindow *window) } } + gdk_content_formats_unref (serializable); + + /* Default to an url type (think gobject://internal) + * to support internal, GType-based DnD. + */ + if (n_mime_types == 0) + { + GdkContentFormats *formats; + gsize n_gtypes; + + formats = gdk_content_provider_ref_formats (self->_contentProvider); + gdk_content_formats_get_gtypes (formats, &n_gtypes); + + if (n_gtypes) + [ret addObject:NSPasteboardTypeURL]; + + gdk_content_formats_unref (formats); + } + return g_steal_pointer (&ret); } From 1bc63eeadb911f0f6ba9358f1443e613b98bbbc5 Mon Sep 17 00:00:00 2001 From: Arjan Molenaar Date: Mon, 16 Jan 2023 14:47:49 +0100 Subject: [PATCH 08/14] macos: Queue events during drag By passing the events during a (midal-ish) drag operation to the main loop, we're able to keep up with what's happening. This allows the internal drag state (GtkDragSource) to be updated and be done when the drag is done. --- gdk/macos/GdkMacosWindow.c | 5 ++++ gdk/macos/gdkmacoseventsource-private.h | 1 + gdk/macos/gdkmacoseventsource.c | 35 +++++++++++++------------ 3 files changed, 24 insertions(+), 17 deletions(-) diff --git a/gdk/macos/GdkMacosWindow.c b/gdk/macos/GdkMacosWindow.c index 79e2ec8001..1005d0c837 100644 --- a/gdk/macos/GdkMacosWindow.c +++ b/gdk/macos/GdkMacosWindow.c @@ -30,6 +30,7 @@ #include "gdkmacosdisplay-private.h" #include "gdkmacosdrag-private.h" #include "gdkmacosdrop-private.h" +#include "gdkmacoseventsource-private.h" #include "gdkmacosmonitor-private.h" #include "gdkmacospasteboard-private.h" #include "gdkmacossurface-private.h" @@ -701,6 +702,8 @@ typedef NSString *CALayerContentsGravity; GdkDrag *drag = _gdk_macos_display_find_drag (GDK_MACOS_DISPLAY (display), sequence_number); int x, y; + _gdk_macos_event_source_queue_event ([NSApp currentEvent]); + _gdk_macos_display_from_display_coords (GDK_MACOS_DISPLAY (display), screenPoint.x, screenPoint.y, &x, &y); _gdk_macos_drag_surface_move (GDK_MACOS_DRAG (drag), x, y); } @@ -711,6 +714,8 @@ typedef NSString *CALayerContentsGravity; GdkDisplay *display = gdk_surface_get_display (GDK_SURFACE (gdk_surface)); GdkDrag *drag = _gdk_macos_display_find_drag (GDK_MACOS_DISPLAY (display), sequence_number); + _gdk_macos_event_source_queue_event ([NSApp currentEvent]); + if (gdk_drag_get_selected_action (drag) != 0) g_signal_emit_by_name (drag, "drop-performed"); else diff --git a/gdk/macos/gdkmacoseventsource-private.h b/gdk/macos/gdkmacoseventsource-private.h index 09853a18ac..66579546d2 100644 --- a/gdk/macos/gdkmacoseventsource-private.h +++ b/gdk/macos/gdkmacoseventsource-private.h @@ -34,6 +34,7 @@ typedef enum GSource *_gdk_macos_event_source_new (GdkMacosDisplay *display); NSEvent *_gdk_macos_event_source_get_pending (void); gboolean _gdk_macos_event_source_check_pending (void); +void _gdk_macos_event_source_queue_event (NSEvent *event); G_END_DECLS diff --git a/gdk/macos/gdkmacoseventsource.c b/gdk/macos/gdkmacoseventsource.c index 725cc57476..5725e2850f 100644 --- a/gdk/macos/gdkmacoseventsource.c +++ b/gdk/macos/gdkmacoseventsource.c @@ -640,6 +640,23 @@ _gdk_macos_event_source_get_pending (void) return event; } +void +_gdk_macos_event_source_queue_event (NSEvent *event) +{ + /* Just used to wake us up; if an event and a FD arrived at the same + * time; could have come from a previous iteration in some cases, + * but the spurious wake up is harmless if a little inefficient. + */ + if (!event || + ([event type] == NSEventTypeApplicationDefined && + [event subtype] == GDK_MACOS_EVENT_SUBTYPE_EVENTLOOP)) + return; + + if (!current_events) + current_events = g_queue_new (); + g_queue_push_head (current_events, [event retain]); +} + static gboolean gdk_macos_event_source_prepare (GSource *source, int *timeout) @@ -782,23 +799,7 @@ poll_func (GPollFD *ufds, if (last_ufds == ufds && n_ready < 0) n_ready = select_thread_collect_poll (ufds, nfds); - if (event && - [event type] == NSEventTypeApplicationDefined && - [event subtype] == GDK_MACOS_EVENT_SUBTYPE_EVENTLOOP) - { - /* Just used to wake us up; if an event and a FD arrived at the same - * time; could have come from a previous iteration in some cases, - * but the spurious wake up is harmless if a little inefficient. - */ - event = NULL; - } - - if (event) - { - if (!current_events) - current_events = g_queue_new (); - g_queue_push_head (current_events, [event retain]); - } + _gdk_macos_event_source_queue_event (event); return n_ready; } From 2669dc269b8eb7914fb93221a74907df5cada508 Mon Sep 17 00:00:00 2001 From: Arjan Molenaar Date: Mon, 16 Jan 2023 20:28:38 +0100 Subject: [PATCH 09/14] macos: Default kCFRunLoopAllActivities It's actually a mask, but we'll keep it in to avoid compiler warnings. --- gdk/macos/gdkmacoseventsource.c | 1 - 1 file changed, 1 deletion(-) diff --git a/gdk/macos/gdkmacoseventsource.c b/gdk/macos/gdkmacoseventsource.c index 5725e2850f..c0311fc745 100644 --- a/gdk/macos/gdkmacoseventsource.c +++ b/gdk/macos/gdkmacoseventsource.c @@ -1046,7 +1046,6 @@ run_loop_observer_callback (CFRunLoopObserverRef observer, run_loop_exit (); break; case kCFRunLoopAllActivities: - /* TODO: Do most of the above? */ default: break; } From 628aeda7ee9f6cd3221c3293b636678db4c5e5a2 Mon Sep 17 00:00:00 2001 From: Arjan Molenaar Date: Mon, 16 Jan 2023 23:15:46 +0100 Subject: [PATCH 10/14] macos: Provide the right drag action We should use the drag action/operation provided by the drag handlers, not rely on our internal bookkeeping. --- gdk/macos/GdkMacosWindow.c | 1 + gdk/macos/gdkmacosdrag-private.h | 1 + gdk/macos/gdkmacosdrag.c | 12 ++++++++++++ 3 files changed, 14 insertions(+) diff --git a/gdk/macos/GdkMacosWindow.c b/gdk/macos/GdkMacosWindow.c index 1005d0c837..79a25c04d3 100644 --- a/gdk/macos/GdkMacosWindow.c +++ b/gdk/macos/GdkMacosWindow.c @@ -715,6 +715,7 @@ typedef NSString *CALayerContentsGravity; GdkDrag *drag = _gdk_macos_display_find_drag (GDK_MACOS_DISPLAY (display), sequence_number); _gdk_macos_event_source_queue_event ([NSApp currentEvent]); + gdk_drag_set_selected_action (drag, _gdk_macos_drag_ns_operation_to_action (operation)); if (gdk_drag_get_selected_action (drag) != 0) g_signal_emit_by_name (drag, "drop-performed"); diff --git a/gdk/macos/gdkmacosdrag-private.h b/gdk/macos/gdkmacosdrag-private.h index ab46828d5e..64db6ba770 100644 --- a/gdk/macos/gdkmacosdrag-private.h +++ b/gdk/macos/gdkmacosdrag-private.h @@ -66,6 +66,7 @@ gboolean _gdk_macos_drag_begin (GdkMacosDrag *self, GdkContentProvider *content, GdkMacosWindow *window); NSDragOperation _gdk_macos_drag_operation (GdkMacosDrag *self); +GdkDragAction _gdk_macos_drag_ns_operation_to_action (NSDragOperation operation); void _gdk_macos_drag_surface_move (GdkMacosDrag *self, int x_root, int y_root); diff --git a/gdk/macos/gdkmacosdrag.c b/gdk/macos/gdkmacosdrag.c index b7549e5b9e..c0201df067 100644 --- a/gdk/macos/gdkmacosdrag.c +++ b/gdk/macos/gdkmacosdrag.c @@ -410,6 +410,18 @@ _gdk_macos_drag_operation (GdkMacosDrag *self) return operation; } +GdkDragAction +_gdk_macos_drag_ns_operation_to_action (NSDragOperation operation) +{ + if (operation & NSDragOperationCopy) + return GDK_ACTION_COPY; + if (operation & NSDragOperationMove) + return GDK_ACTION_MOVE; + if (operation & NSDragOperationLink) + return GDK_ACTION_LINK; + return 0; +} + void _gdk_macos_drag_surface_move (GdkMacosDrag *self, int x_root, From 16e23b3f9ed391c9379b2e3c5f3ae2a7aeffd859 Mon Sep 17 00:00:00 2001 From: Arjan Molenaar Date: Tue, 17 Jan 2023 16:12:57 +0100 Subject: [PATCH 11/14] macos: align functions in gdkmacosdrag-private.h --- gdk/macos/gdkmacosdrag-private.h | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/gdk/macos/gdkmacosdrag-private.h b/gdk/macos/gdkmacosdrag-private.h index 64db6ba770..bdcbbbe23d 100644 --- a/gdk/macos/gdkmacosdrag-private.h +++ b/gdk/macos/gdkmacosdrag-private.h @@ -61,20 +61,21 @@ struct _GdkMacosDragClass GdkDragClass parent_class; }; -GType gdk_macos_drag_get_type (void) G_GNUC_CONST; -gboolean _gdk_macos_drag_begin (GdkMacosDrag *self, - GdkContentProvider *content, - GdkMacosWindow *window); -NSDragOperation _gdk_macos_drag_operation (GdkMacosDrag *self); -GdkDragAction _gdk_macos_drag_ns_operation_to_action (NSDragOperation operation); -void _gdk_macos_drag_surface_move (GdkMacosDrag *self, - int x_root, - int y_root); -void _gdk_macos_drag_set_start_position (GdkMacosDrag *self, - int start_x, - int start_y); -void _gdk_macos_drag_set_actions (GdkMacosDrag *self, - GdkModifierType mods); +GType gdk_macos_drag_get_type (void) G_GNUC_CONST; +gboolean _gdk_macos_drag_begin (GdkMacosDrag *self, + GdkContentProvider *content, + GdkMacosWindow *window); +NSDragOperation _gdk_macos_drag_operation (GdkMacosDrag *self); +GdkDragAction _gdk_macos_drag_ns_operation_to_action + (NSDragOperation operation); +void _gdk_macos_drag_surface_move (GdkMacosDrag *self, + int x_root, + int y_root); +void _gdk_macos_drag_set_start_position (GdkMacosDrag *self, + int start_x, + int start_y); +void _gdk_macos_drag_set_actions (GdkMacosDrag *self, + GdkModifierType mods); G_END_DECLS From 136c1ccd2c4646f401e356f80ee88b2762767259 Mon Sep 17 00:00:00 2001 From: Arjan Molenaar Date: Tue, 17 Jan 2023 16:24:47 +0100 Subject: [PATCH 12/14] macos: Update event source doc Update the documentation with the interaction pattern we need to support DnD. --- gdk/macos/gdkmacoseventsource.c | 27 ++++++++++++++++++++------- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/gdk/macos/gdkmacoseventsource.c b/gdk/macos/gdkmacoseventsource.c index c0311fc745..6c4a962ae0 100644 --- a/gdk/macos/gdkmacoseventsource.c +++ b/gdk/macos/gdkmacoseventsource.c @@ -37,11 +37,15 @@ /* * This file implementations integration between the GLib main loop and * the native system of the Core Foundation run loop and Cocoa event - * handling. There are basically two different cases that we need to - * handle: either the GLib main loop is in control (the application - * has called gtk_main(), or is otherwise iterating the main loop), or - * CFRunLoop is in control (we are in a modal operation such as window - * resizing or drag-and-drop.) + * handling. There are basically three different cases that we need to + * handle: + * + * - the GLib main loop is in control. The application has called + * gtk_main(), or is otherwise iterating the main loop. + * - CFRunLoop is in control. We are in a modal operation such as window + * resizing. + * - CFRunLoop is running a nested loop. This happens when a drag-and-drop + * operation has been initiated. * * When the GLib main loop is in control we integrate in native event * handling in two ways: first we add a GSource that handles checking @@ -57,14 +61,23 @@ * stages of the GLib main loop (prepare, check, dispatch), and make the * appropriate calls into GLib. * - * Both cases share a single problem: the OS X API’s don’t allow us to + * When initiating a drag operation, a nested CFRunLoop is executed. + * The nested run loop is started when fetching a native event in our GLib + * main loop. The application does not receive any events until the nested loop + * is finished. We work around this by forwarding the + * events that trigger the callbacks of the NSDraggingSource protocol. + * The "run loop observer" is executing the GLib main loop stages as long as we're + * in the nested run loop, as if CFRunLoop were in control. + * See also GdkMacosWindow. + * + * All cases share a single problem: the macOS API’s don’t allow us to * wait simultaneously for file descriptors and for events. So when we * need to do a blocking wait that includes file descriptor activity, we * push the actual work of calling select() to a helper thread (the * "select thread") and wait for native events in the main thread. * * The main known limitation of this code is that if a callback is triggered - * via the OS X run loop while we are "polling" (in either case described + * via the macOS run loop while we are "polling" (in either case described * above), iteration of the GLib main loop is not possible from within * that callback. If the programmer tries to do so explicitly, then they * will get a warning from GLib "main loop already active in another thread". From 52f6acd398b7a83f53f3605a9d50d51c3b48286c Mon Sep 17 00:00:00 2001 From: Arjan Molenaar Date: Sat, 21 Jan 2023 14:54:12 +0100 Subject: [PATCH 13/14] macos: Send dragging events directly to the display Instead of adding events to the application event queue, dispatch them directly to the right display. We know this when the event is to be dispatched. This is the same as used for the `sendEvent` method in `GdkMacosWindow`. To achieve this I factored out the generic NSEvent to GdkEvent translation. We can send an event directly, when we receive it in the GdkMacosWindow directly from the OS. --- gdk/macos/GdkMacosWindow.c | 16 +-- gdk/macos/gdkmacosdisplay-private.h | 2 +- gdk/macos/gdkmacosdisplay-translate.c | 155 +++++++++++++----------- gdk/macos/gdkmacoseventsource-private.h | 1 - gdk/macos/gdkmacoseventsource.c | 2 +- 5 files changed, 95 insertions(+), 81 deletions(-) diff --git a/gdk/macos/GdkMacosWindow.c b/gdk/macos/GdkMacosWindow.c index 79a25c04d3..0636018c93 100644 --- a/gdk/macos/GdkMacosWindow.c +++ b/gdk/macos/GdkMacosWindow.c @@ -146,7 +146,7 @@ typedef NSString *CALayerContentsGravity; * * TODO: Can we improve grab breaking to fix this? */ - _gdk_macos_display_send_button_event ([self gdkDisplay], event); + _gdk_macos_display_send_event ([self gdkDisplay], event); _gdk_macos_display_break_all_grabs (GDK_MACOS_DISPLAY (display), time); @@ -698,23 +698,23 @@ typedef NSString *CALayerContentsGravity; - (void)draggingSession:(NSDraggingSession *)session movedToPoint:(NSPoint)screenPoint { NSInteger sequence_number = [session draggingSequenceNumber]; - GdkDisplay *display = gdk_surface_get_display (GDK_SURFACE (gdk_surface)); + GdkMacosDisplay *display = GDK_MACOS_DISPLAY (gdk_surface_get_display (GDK_SURFACE (gdk_surface))); GdkDrag *drag = _gdk_macos_display_find_drag (GDK_MACOS_DISPLAY (display), sequence_number); int x, y; - _gdk_macos_event_source_queue_event ([NSApp currentEvent]); + _gdk_macos_display_send_event (display, [NSApp currentEvent]); - _gdk_macos_display_from_display_coords (GDK_MACOS_DISPLAY (display), screenPoint.x, screenPoint.y, &x, &y); + _gdk_macos_display_from_display_coords (display, screenPoint.x, screenPoint.y, &x, &y); _gdk_macos_drag_surface_move (GDK_MACOS_DRAG (drag), x, y); } - (void)draggingSession:(NSDraggingSession *)session endedAtPoint:(NSPoint)screenPoint operation:(NSDragOperation)operation { NSInteger sequence_number = [session draggingSequenceNumber]; - GdkDisplay *display = gdk_surface_get_display (GDK_SURFACE (gdk_surface)); - GdkDrag *drag = _gdk_macos_display_find_drag (GDK_MACOS_DISPLAY (display), sequence_number); + GdkMacosDisplay *display = GDK_MACOS_DISPLAY (gdk_surface_get_display (GDK_SURFACE (gdk_surface))); + GdkDrag *drag = _gdk_macos_display_find_drag (display, sequence_number); - _gdk_macos_event_source_queue_event ([NSApp currentEvent]); + _gdk_macos_display_send_event (display, [NSApp currentEvent]); gdk_drag_set_selected_action (drag, _gdk_macos_drag_ns_operation_to_action (operation)); if (gdk_drag_get_selected_action (drag) != 0) @@ -722,7 +722,7 @@ typedef NSString *CALayerContentsGravity; else gdk_drag_cancel (drag, GDK_DRAG_CANCEL_NO_TARGET); - _gdk_macos_display_set_drag (GDK_MACOS_DISPLAY (display), [session draggingSequenceNumber], NULL); + _gdk_macos_display_set_drag (display, [session draggingSequenceNumber], NULL); } // end diff --git a/gdk/macos/gdkmacosdisplay-private.h b/gdk/macos/gdkmacosdisplay-private.h index 122d004cec..9fc910ec2e 100644 --- a/gdk/macos/gdkmacosdisplay-private.h +++ b/gdk/macos/gdkmacosdisplay-private.h @@ -155,7 +155,7 @@ void _gdk_macos_display_surface_became_key (GdkMacosDisp GdkMacosSurface *surface); void _gdk_macos_display_clear_sorting (GdkMacosDisplay *self); const GList *_gdk_macos_display_get_surfaces (GdkMacosDisplay *self); -void _gdk_macos_display_send_button_event (GdkMacosDisplay *self, +void _gdk_macos_display_send_event (GdkMacosDisplay *self, NSEvent *nsevent); void _gdk_macos_display_warp_pointer (GdkMacosDisplay *self, int x, diff --git a/gdk/macos/gdkmacosdisplay-translate.c b/gdk/macos/gdkmacosdisplay-translate.c index b06dc83dbe..f6d4a23991 100644 --- a/gdk/macos/gdkmacosdisplay-translate.c +++ b/gdk/macos/gdkmacosdisplay-translate.c @@ -723,6 +723,85 @@ fill_scroll_event (GdkMacosDisplay *self, return g_steal_pointer (&ret); } + +static GdkEvent * +fill_event (GdkMacosDisplay *self, + GdkMacosWindow *window, + NSEvent *nsevent, + int x, + int y) +{ + GdkMacosSurface *surface = [window gdkSurface]; + NSEventType event_type = [nsevent type]; + GdkEvent *ret = NULL; + + switch ((int)event_type) + { + case NSEventTypeLeftMouseDown: + case NSEventTypeRightMouseDown: + case NSEventTypeOtherMouseDown: + case NSEventTypeLeftMouseUp: + case NSEventTypeRightMouseUp: + case NSEventTypeOtherMouseUp: + ret = fill_button_event (self, surface, nsevent, x, y); + break; + + case NSEventTypeLeftMouseDragged: + case NSEventTypeRightMouseDragged: + case NSEventTypeOtherMouseDragged: + case NSEventTypeMouseMoved: + ret = fill_motion_event (self, surface, nsevent, x, y); + break; + + case NSEventTypeMagnify: + case NSEventTypeRotate: + ret = fill_pinch_event (self, surface, nsevent, x, y); + break; + + case NSEventTypeMouseExited: + case NSEventTypeMouseEntered: + { + GdkSeat *seat = gdk_display_get_default_seat (GDK_DISPLAY (self)); + GdkDevice *pointer = gdk_seat_get_pointer (seat); + GdkDeviceGrabInfo *grab = _gdk_display_get_last_device_grab (GDK_DISPLAY (self), pointer); + + if ([(GdkMacosWindow *)window isInManualResizeOrMove]) + { + ret = GDK_MACOS_EVENT_DROP; + } + else if (grab == NULL) + { + if (event_type == NSEventTypeMouseExited) + [[NSCursor arrowCursor] set]; + + ret = synthesize_crossing_event (self, surface, nsevent, x, y); + } + } + + break; + + case NSEventTypeKeyDown: + case NSEventTypeKeyUp: + case NSEventTypeFlagsChanged: { + GdkEventType type = _gdk_macos_keymap_get_event_type (nsevent); + + if (type) + ret = fill_key_event (self, surface, nsevent, type); + + break; + } + + case NSEventTypeScrollWheel: + ret = fill_scroll_event (self, surface, nsevent, x, y); + break; + + default: + break; + } + + return ret; +} + static gboolean is_mouse_button_press_event (NSEventType type) { @@ -1083,7 +1162,6 @@ _gdk_macos_display_translate (GdkMacosDisplay *self, GdkMacosWindow *window; NSEventType event_type; NSWindow *event_window; - GdkEvent *ret = NULL; int x; int y; @@ -1191,79 +1269,15 @@ _gdk_macos_display_translate (GdkMacosDisplay *self, _gdk_macos_display_clear_sorting (self); } } - - switch ((int)event_type) - { - case NSEventTypeLeftMouseDown: - case NSEventTypeRightMouseDown: - case NSEventTypeOtherMouseDown: - case NSEventTypeLeftMouseUp: - case NSEventTypeRightMouseUp: - case NSEventTypeOtherMouseUp: - ret = fill_button_event (self, surface, nsevent, x, y); - break; - - case NSEventTypeLeftMouseDragged: - case NSEventTypeRightMouseDragged: - case NSEventTypeOtherMouseDragged: - case NSEventTypeMouseMoved: - ret = fill_motion_event (self, surface, nsevent, x, y); - break; - - case NSEventTypeMagnify: - case NSEventTypeRotate: - ret = fill_pinch_event (self, surface, nsevent, x, y); - break; - - case NSEventTypeMouseExited: - case NSEventTypeMouseEntered: - { - GdkSeat *seat = gdk_display_get_default_seat (GDK_DISPLAY (self)); - GdkDevice *pointer = gdk_seat_get_pointer (seat); - GdkDeviceGrabInfo *grab = _gdk_display_get_last_device_grab (GDK_DISPLAY (self), pointer); - - if ([(GdkMacosWindow *)window isInManualResizeOrMove]) - { - ret = GDK_MACOS_EVENT_DROP; - } - else if (grab == NULL) - { - if (event_type == NSEventTypeMouseExited) - [[NSCursor arrowCursor] set]; - - ret = synthesize_crossing_event (self, surface, nsevent, x, y); - } - } - - break; - - case NSEventTypeKeyDown: - case NSEventTypeKeyUp: - case NSEventTypeFlagsChanged: { - GdkEventType type = _gdk_macos_keymap_get_event_type (nsevent); - - if (type) - ret = fill_key_event (self, surface, nsevent, type); - - break; - } - - case NSEventTypeScrollWheel: - ret = fill_scroll_event (self, surface, nsevent, x, y); - break; - - default: - break; - } - - return ret; + return fill_event (self, window, nsevent, x, y); } void -_gdk_macos_display_send_button_event (GdkMacosDisplay *self, - NSEvent *nsevent) +_gdk_macos_display_send_event (GdkMacosDisplay *self, + NSEvent *nsevent) { GdkMacosSurface *surface; + GdkMacosWindow *window; GdkEvent *event; int x; int y; @@ -1272,7 +1286,8 @@ _gdk_macos_display_send_button_event (GdkMacosDisplay *self, g_return_if_fail (nsevent != NULL); if ((surface = find_surface_for_ns_event (self, nsevent, &x, &y)) && - (event = fill_button_event (self, surface, nsevent, x, y))) + (window = (GdkMacosWindow *)_gdk_macos_surface_get_native (surface)) && + (event = fill_event (self, window, nsevent, x, y))) _gdk_windowing_got_event (GDK_DISPLAY (self), _gdk_event_queue_append (GDK_DISPLAY (self), event), event, diff --git a/gdk/macos/gdkmacoseventsource-private.h b/gdk/macos/gdkmacoseventsource-private.h index 66579546d2..09853a18ac 100644 --- a/gdk/macos/gdkmacoseventsource-private.h +++ b/gdk/macos/gdkmacoseventsource-private.h @@ -34,7 +34,6 @@ typedef enum GSource *_gdk_macos_event_source_new (GdkMacosDisplay *display); NSEvent *_gdk_macos_event_source_get_pending (void); gboolean _gdk_macos_event_source_check_pending (void); -void _gdk_macos_event_source_queue_event (NSEvent *event); G_END_DECLS diff --git a/gdk/macos/gdkmacoseventsource.c b/gdk/macos/gdkmacoseventsource.c index 6c4a962ae0..121efc679b 100644 --- a/gdk/macos/gdkmacoseventsource.c +++ b/gdk/macos/gdkmacoseventsource.c @@ -653,7 +653,7 @@ _gdk_macos_event_source_get_pending (void) return event; } -void +static void _gdk_macos_event_source_queue_event (NSEvent *event) { /* Just used to wake us up; if an event and a FD arrived at the same From d6ef39b9a23a3c7c07c12af05cc7a534e52f4eb2 Mon Sep 17 00:00:00 2001 From: Arjan Molenaar Date: Sat, 21 Jan 2023 15:22:13 +0100 Subject: [PATCH 14/14] macos: remove dead code --- gdk/macos/gdkmacosdisplay-translate.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/gdk/macos/gdkmacosdisplay-translate.c b/gdk/macos/gdkmacosdisplay-translate.c index f6d4a23991..b88ed281ac 100644 --- a/gdk/macos/gdkmacosdisplay-translate.c +++ b/gdk/macos/gdkmacosdisplay-translate.c @@ -1104,16 +1104,12 @@ find_surface_for_ns_event (GdkMacosDisplay *self, GdkMacosBaseView *view; GdkSurface *surface; NSPoint point; - int x_tmp; - int y_tmp; g_assert (GDK_IS_MACOS_DISPLAY (self)); g_assert (nsevent != NULL); g_assert (x != NULL); g_assert (y != NULL); - _gdk_macos_display_from_display_coords (self, point.x, point.y, &x_tmp, &y_tmp); - switch ((int)[nsevent type]) { case NSEventTypeLeftMouseDown: