[gtk/wip/chergert/quartz4u: 122/142] macos: implement basic clipboard reading



commit 8daf9e8f0ced1a72d0fee5c12863397a9917f87e
Author: Christian Hergert <chergert redhat com>
Date:   Thu Jun 11 10:20:47 2020 -0700

    macos: implement basic clipboard reading
    
    Still need to finish writing here, and we won't be implementing
    primary_clipboard unless we find that is necessary later.

 gdk/macos/gdkmacosclipboard-private.h |  45 +++++
 gdk/macos/gdkmacosclipboard.c         | 319 ++++++++++++++++++++++++++++++++++
 gdk/macos/gdkmacosdisplay.c           |  12 ++
 gdk/macos/meson.build                 |   1 +
 4 files changed, 377 insertions(+)
---
diff --git a/gdk/macos/gdkmacosclipboard-private.h b/gdk/macos/gdkmacosclipboard-private.h
new file mode 100644
index 0000000000..aae2727120
--- /dev/null
+++ b/gdk/macos/gdkmacosclipboard-private.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright © 2020 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 <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ */
+
+#ifndef __GDK_MACOS_CLIPBOARD_PRIVATE_H__
+#define __GDK_MACOS_CLIPBOARD_PRIVATE_H__
+
+#include <AppKit/AppKit.h>
+
+#include "gdkclipboardprivate.h"
+#include "gdkmacosdisplay-private.h"
+
+G_BEGIN_DECLS
+
+#define GDK_TYPE_MACOS_CLIPBOARD (_gdk_macos_clipboard_get_type())
+
+G_DECLARE_FINAL_TYPE (GdkMacosClipboard, _gdk_macos_clipboard, GDK, MACOS_CLIPBOARD, GdkClipboard)
+
+GdkClipboard *_gdk_macos_clipboard_new (GdkMacosDisplay *display);
+
+@interface GdkMacosClipboardOwner : NSObject
+{
+  GdkClipboard *clipboard;
+}
+
+@end
+
+G_END_DECLS
+
+#endif /* __GDK_MACOS_CLIPBOARD_PRIVATE_H__ */
diff --git a/gdk/macos/gdkmacosclipboard.c b/gdk/macos/gdkmacosclipboard.c
new file mode 100644
index 0000000000..dd16222a59
--- /dev/null
+++ b/gdk/macos/gdkmacosclipboard.c
@@ -0,0 +1,319 @@
+/*
+ * Copyright © 2020 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 <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ */
+
+#include "config.h"
+
+#include <glib/gi18n.h>
+
+#include "gdkmacosclipboard-private.h"
+#include "gdkmacosutils-private.h"
+
+struct _GdkMacosClipboard
+{
+  GdkClipboard            parent_instance;
+  NSPasteboard           *pasteboard;
+  GdkMacosClipboardOwner *owner;
+};
+
+G_DEFINE_TYPE (GdkMacosClipboard, _gdk_macos_clipboard, GDK_TYPE_CLIPBOARD)
+
+static void
+populate_content_formats (GdkContentFormatsBuilder *builder,
+                          NSPasteboardType          type)
+{
+  G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
+
+  if ([type isEqualToString:NSPasteboardTypeString] ||
+      [type isEqualToString:NSStringPboardType])
+    gdk_content_formats_builder_add_mime_type (builder, "text/plain;charset=utf-8");
+  else if ([type isEqualToString:NSPasteboardTypeURL] ||
+           [type isEqualToString:NSPasteboardTypeFileURL])
+    gdk_content_formats_builder_add_mime_type (builder, "text/uri-list");
+  else if ([type isEqualToString:NSPasteboardTypeColor])
+    gdk_content_formats_builder_add_mime_type (builder, "application/x-color");
+  else if ([type isEqualToString:NSPasteboardTypeTIFF])
+    gdk_content_formats_builder_add_mime_type (builder, "image/tiff");
+  else if ([type isEqualToString:NSPasteboardTypePNG])
+    gdk_content_formats_builder_add_mime_type (builder, "image/png");
+
+  G_GNUC_END_IGNORE_DEPRECATIONS;
+}
+
+static GdkContentFormats *
+load_offer_formats (GdkMacosClipboard *self)
+{
+  GDK_BEGIN_MACOS_ALLOC_POOL;
+
+  GdkContentFormatsBuilder *builder;
+  GdkContentFormats *formats;
+
+  g_assert (GDK_IS_MACOS_CLIPBOARD (self));
+
+  builder = gdk_content_formats_builder_new ();
+  for (NSPasteboardType type in [self->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)
+{
+  GdkContentFormats *formats;
+
+  g_assert (GDK_IS_MACOS_CLIPBOARD (self));
+
+  formats = load_offer_formats (self);
+  gdk_clipboard_claim_remote (GDK_CLIPBOARD (self), formats);
+  gdk_content_formats_unref (formats);
+}
+
+static gboolean
+_gdk_macos_clipboard_claim (GdkClipboard       *clipboard,
+                            GdkContentFormats  *formats,
+                            gboolean            local,
+                            GdkContentProvider *provider)
+{
+  g_assert (GDK_IS_CLIPBOARD (clipboard));
+  g_assert (formats != NULL);
+  g_assert (!provider || GDK_IS_CONTENT_PROVIDER (provider));
+
+  return GDK_CLIPBOARD_CLASS (_gdk_macos_clipboard_parent_class)->claim (clipboard, formats, local, 
provider);
+}
+
+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_memdup (bytes, len), len, g_free);
+}
+
+static void
+_gdk_macos_clipboard_read_async (GdkClipboard        *clipboard,
+                                 GdkContentFormats   *formats,
+                                 int                  io_priority,
+                                 GCancellable        *cancellable,
+                                 GAsyncReadyCallback  callback,
+                                 gpointer             user_data)
+{
+  GDK_BEGIN_MACOS_ALLOC_POOL;
+
+  GdkContentFormats *offer_formats = NULL;
+  NSPasteboard *pasteboard;
+  const gchar *mime_type;
+  GInputStream *stream = NULL;
+  GTask *task = NULL;
+
+  g_assert (GDK_IS_MACOS_CLIPBOARD (clipboard));
+  g_assert (formats != NULL);
+
+  task = g_task_new (clipboard, cancellable, callback, user_data);
+  g_task_set_source_tag (task, _gdk_macos_clipboard_read_async);
+  g_task_set_priority (task, io_priority);
+
+  pasteboard = [NSPasteboard generalPasteboard];
+  offer_formats = load_offer_formats (GDK_MACOS_CLIPBOARD (clipboard));
+  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),
+                                                        g_free);
+        }
+    }
+  else if (strcmp (mime_type, "text/uri-list") == 0)
+    {
+#if 0
+      if ([[pasteboard types] containsObject:NSPasteboardTypeFileURL])
+        {
+          GString *str = g_string_new (NULL);
+          NSArray *files = [pasteboard propertyListForType:NSFilenamesPboardType];
+          gsize n_files = [files count];
+          gchar **uris;
+
+          for (gsize i = 0; i < n_files; ++i)
+            {
+              NSString* uriString = [files objectAtIndex:i];
+              uriString = [@"file://" stringByAppendingString:uriString];
+              uriString = [uriString stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
+              uris[i] = (gchar *) [uriString cStringUsingEncoding:NSUTF8StringEncoding];
+            }
+          uris[i] = NULL;
+          gtk_selection_data_set_uris (selection_data, uris);
+          g_free (uris);
+        }
+#endif
+    }
+  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_memdup (&color, sizeof color),
+                                                    sizeof color,
+                                                    g_free);
+    }
+  else if (strcmp (mime_type, "image/tiff") == 0)
+    {
+      NSData *data = [pasteboard dataForType:NSPasteboardTypeTIFF];
+      stream = create_stream_from_nsdata (data);
+    }
+  else if (strcmp (mime_type, "image/png") == 0)
+    {
+      NSData *data = [pasteboard dataForType:NSPasteboardTypePNG];
+      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;
+}
+
+static GInputStream *
+_gdk_macos_clipboard_read_finish (GdkClipboard  *clipboard,
+                                  GAsyncResult  *result,
+                                  const gchar  **out_mime_type,
+                                  GError       **error)
+{
+  GTask *task = (GTask *)result;
+
+  g_assert (GDK_IS_MACOS_CLIPBOARD (clipboard));
+  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);
+}
+
+static void
+_gdk_macos_clipboard_constructed (GObject *object)
+{
+  GdkMacosClipboard *self = (GdkMacosClipboard *)object;
+
+  if (self->pasteboard == nil)
+    self->pasteboard = [[NSPasteboard generalPasteboard] retain];
+
+  G_OBJECT_CLASS (_gdk_macos_clipboard_parent_class)->constructed (object);
+}
+
+static void
+_gdk_macos_clipboard_finalize (GObject *object)
+{
+  GdkMacosClipboard *self = (GdkMacosClipboard *)object;
+
+  if (self->pasteboard != nil)
+    {
+      [self->pasteboard release];
+      self->pasteboard = nil;
+    }
+
+  G_OBJECT_CLASS (_gdk_macos_clipboard_parent_class)->finalize (object);
+}
+
+static void
+_gdk_macos_clipboard_class_init (GdkMacosClipboardClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+  GdkClipboardClass *clipboard_class = GDK_CLIPBOARD_CLASS (klass);
+
+  object_class->constructed = _gdk_macos_clipboard_constructed;
+  object_class->finalize = _gdk_macos_clipboard_finalize;
+
+  clipboard_class->claim = _gdk_macos_clipboard_claim;
+  clipboard_class->read_async = _gdk_macos_clipboard_read_async;
+  clipboard_class->read_finish = _gdk_macos_clipboard_read_finish;
+}
+
+static void
+_gdk_macos_clipboard_init (GdkMacosClipboard *self)
+{
+}
+
+GdkClipboard *
+_gdk_macos_clipboard_new (GdkMacosDisplay *display)
+{
+  GdkMacosClipboard *self;
+
+  g_return_val_if_fail (GDK_IS_MACOS_DISPLAY (display), NULL);
+
+  self = g_object_new (GDK_TYPE_MACOS_CLIPBOARD,
+                       "display", display,
+                       NULL);
+
+  _gdk_macos_clipboard_load_contents (self);
+
+  return GDK_CLIPBOARD (self);
+}
+
+@implementation GdkMacosClipboardOwner
+
+-(void)pasteboard:(NSPasteboard *)pb provideDataForType:(NSString *)type
+{
+  //[pb setData:data forType:type];
+}
+
+@end
diff --git a/gdk/macos/gdkmacosdisplay.c b/gdk/macos/gdkmacosdisplay.c
index 65c2fa1967..3e44452f45 100644
--- a/gdk/macos/gdkmacosdisplay.c
+++ b/gdk/macos/gdkmacosdisplay.c
@@ -27,6 +27,7 @@
 #include "gdkeventsprivate.h"
 
 #include "gdkdisplaylinksource.h"
+#include "gdkmacosclipboard-private.h"
 #include "gdkmacoscairocontext-private.h"
 #include "gdkmacoseventsource-private.h"
 #include "gdkmacosdisplay-private.h"
@@ -597,6 +598,14 @@ gdk_macos_display_get_keymap (GdkDisplay *display)
   return GDK_KEYMAP (self->keymap);
 }
 
+static void
+gdk_macos_display_load_clipboard (GdkMacosDisplay *self)
+{
+  g_assert (GDK_IS_MACOS_DISPLAY (self));
+
+  GDK_DISPLAY (self)->clipboard = _gdk_macos_clipboard_new (self);
+}
+
 static void
 gdk_macos_display_finalize (GObject *object)
 {
@@ -612,6 +621,7 @@ gdk_macos_display_finalize (GObject *object)
                                       CFSTR ("NSUserDefaultsDidChangeNotification"),
                                       NULL);
 
+  g_clear_object (&GDK_DISPLAY (self)->clipboard);
   g_clear_pointer (&self->frame_source, g_source_unref);
   g_clear_object (&self->monitors);
   g_clear_pointer (&self->name, g_free);
@@ -682,6 +692,8 @@ _gdk_macos_display_open (const gchar *display_name)
   self->keymap = _gdk_macos_keymap_new (self);
 
   gdk_macos_display_load_seat (self);
+  gdk_macos_display_load_clipboard (self);
+
   /* Load CVDisplayLink before monitors to access refresh rates */
   gdk_macos_display_load_display_link (self);
   _gdk_macos_display_reload_monitors (self);
diff --git a/gdk/macos/meson.build b/gdk/macos/meson.build
index e534e1f581..56a1604f52 100644
--- a/gdk/macos/meson.build
+++ b/gdk/macos/meson.build
@@ -4,6 +4,7 @@ gdk_macos_sources = files([
   'gdkdisplaylinksource.c',
   'GdkMacosWindow.c',
   'gdkmacoscairocontext.c',
+  'gdkmacosclipboard.c',
   'gdkmacoscursor.c',
   'gdkmacosdevice.c',
   'gdkmacosdisplay.c',


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]