[gtk+] wayland: Implement reading the clipboard



commit 82002eabfed16b2a47d715ab0c7b3883f35c7ab7
Author: Benjamin Otte <otte redhat com>
Date:   Sun Dec 3 02:48:17 2017 +0100

    wayland: Implement reading the clipboard
    
    We now keep track of what's in the clipboard and allow people to read
    its contents.

 gdk/wayland/gdkclipboard-wayland.c |  139 ++++++++++++++++++++++++++++++++++-
 gdk/wayland/gdkclipboard-wayland.h |    4 +
 gdk/wayland/gdkdevice-wayland.c    |   38 ++--------
 gdk/wayland/gdkprivate-wayland.h   |    2 +
 gdk/wayland/gdkselection-wayland.c |   19 +++++
 5 files changed, 168 insertions(+), 34 deletions(-)
---
diff --git a/gdk/wayland/gdkclipboard-wayland.c b/gdk/wayland/gdkclipboard-wayland.c
index cddc37b..51d5ed2 100644
--- a/gdk/wayland/gdkclipboard-wayland.c
+++ b/gdk/wayland/gdkclipboard-wayland.c
@@ -20,12 +20,21 @@
 #include "gdkclipboardprivate.h"
 #include "gdkclipboard-wayland.h"
 
+#include "gdkcontentformats.h"
+#include "gdkintl.h"
+#include "gdk-private.h"
+
+#include <glib-unix.h>
+#include <gio/gunixinputstream.h>
 
 typedef struct _GdkWaylandClipboardClass GdkWaylandClipboardClass;
 
 struct _GdkWaylandClipboard
 {
   GdkClipboard parent;
+
+  struct wl_data_offer *offer;
+  GdkContentFormats *offer_formats;
 };
 
 struct _GdkWaylandClipboardClass
@@ -36,28 +45,130 @@ struct _GdkWaylandClipboardClass
 G_DEFINE_TYPE (GdkWaylandClipboard, gdk_wayland_clipboard, GDK_TYPE_CLIPBOARD)
 
 static void
+gdk_wayland_clipboard_discard_offer (GdkWaylandClipboard *cb)
+{
+  g_clear_pointer (&cb->offer_formats, gdk_content_formats_unref);
+  g_clear_pointer (&cb->offer, (GDestroyNotify) wl_data_offer_destroy);
+}
+
+static void
 gdk_wayland_clipboard_finalize (GObject *object)
 {
-  //GdkWaylandClipboard *cb = GDK_WAYLAND_CLIPBOARD (object);
+  GdkWaylandClipboard *cb = GDK_WAYLAND_CLIPBOARD (object);
 
+  gdk_wayland_clipboard_discard_offer (cb);
+  
   G_OBJECT_CLASS (gdk_wayland_clipboard_parent_class)->finalize (object);
 }
 
+static gboolean
+gdk_wayland_clipboard_claim (GdkClipboard       *clipboard,
+                             GdkContentFormats  *formats,
+                             gboolean            local,
+                             GdkContentProvider *content)
+{
+  GdkWaylandClipboard *cb = GDK_WAYLAND_CLIPBOARD (clipboard);
+
+  if (local)
+    {
+      /* not handled yet */
+      cb->offer = NULL;
+    }
+
+  return GDK_CLIPBOARD_CLASS (gdk_wayland_clipboard_parent_class)->claim (clipboard, formats, local, 
content);
+}
+
+static void
+gdk_wayland_clipboard_read_async (GdkClipboard        *clipboard,
+                                  GdkContentFormats   *formats,
+                                  int                  io_priority,
+                                  GCancellable        *cancellable,
+                                  GAsyncReadyCallback  callback,
+                                  gpointer             user_data)
+{
+  GdkWaylandClipboard *cb = GDK_WAYLAND_CLIPBOARD (clipboard);
+  GInputStream *stream;
+  const char *mime_type;
+  int pipe_fd[2];
+  GError *error = NULL;
+  GTask *task;
+
+  task = g_task_new (clipboard, cancellable, callback, user_data);
+  g_task_set_priority (task, io_priority);
+  g_task_set_source_tag (task, gdk_wayland_clipboard_read_async);
+
+  GDK_NOTE (CLIPBOARD, char *s = gdk_content_formats_to_string (formats);
+                       g_printerr ("%p: read for %s\n", cb, s);
+                       g_free (s); );
+  mime_type = gdk_content_formats_match_mime_type (formats, cb->offer_formats);
+  if (mime_type == NULL)
+    {
+      g_task_return_new_error (task, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
+                               _("No compatible transfer format found"));
+      return;
+    }
+  /* offer formats should be empty if we have no offer */
+  g_assert (cb->offer);
+
+  g_task_set_task_data (task, (gpointer) mime_type, NULL);
+
+  if (!g_unix_open_pipe (pipe_fd, FD_CLOEXEC, &error))
+    {
+      g_task_return_error (task, error);
+      return;
+    }
+
+  wl_data_offer_receive (cb->offer, mime_type, pipe_fd[1]);
+  stream = g_unix_input_stream_new (pipe_fd[0], TRUE);
+  close (pipe_fd[1]);
+  g_task_return_pointer (task, stream, g_object_unref);
+}
+
+static GInputStream *
+gdk_wayland_clipboard_read_finish (GdkClipboard  *clipboard,
+                                   const char   **out_mime_type,
+                                   GAsyncResult  *result,
+                                   GError       **error)
+{
+  GInputStream *stream;
+  GTask *task;
+
+  g_return_val_if_fail (g_task_is_valid (result, G_OBJECT (clipboard)), NULL);
+  task = G_TASK (result);
+  g_return_val_if_fail (g_task_get_source_tag (task) == gdk_wayland_clipboard_read_async, NULL);
+
+  stream = g_task_propagate_pointer (task, error);
+
+  if (stream)
+    {
+      if (out_mime_type)
+        *out_mime_type = g_task_get_task_data (task);
+      g_object_ref (stream);
+    }
+  else
+    {
+      if (out_mime_type)
+        *out_mime_type = NULL;
+    }
+
+  return stream;
+}
+
 static void
 gdk_wayland_clipboard_class_init (GdkWaylandClipboardClass *class)
 {
   GObjectClass *object_class = G_OBJECT_CLASS (class);
-  //GdkClipboardClass *clipboard_class = GDK_CLIPBOARD_CLASS (class);
+  GdkClipboardClass *clipboard_class = GDK_CLIPBOARD_CLASS (class);
 
   object_class->finalize = gdk_wayland_clipboard_finalize;
 
-#if 0
   clipboard_class->claim = gdk_wayland_clipboard_claim;
+#if 0
   clipboard_class->store_async = gdk_wayland_clipboard_store_async;
   clipboard_class->store_finish = gdk_wayland_clipboard_store_finish;
+#endif
   clipboard_class->read_async = gdk_wayland_clipboard_read_async;
   clipboard_class->read_finish = gdk_wayland_clipboard_read_finish;
-#endif
 }
 
 static void
@@ -76,3 +187,23 @@ gdk_wayland_clipboard_new (GdkDisplay *display)
 
   return GDK_CLIPBOARD (cb);
 }
+
+void
+gdk_wayland_clipboard_claim_remote (GdkWaylandClipboard  *cb,
+                                    struct wl_data_offer *offer,
+                                    GdkContentFormats    *formats)
+{
+  g_return_if_fail (GDK_IS_WAYLAND_CLIPBOARD (cb));
+
+  gdk_wayland_clipboard_discard_offer (cb);
+
+  GDK_NOTE (CLIPBOARD, char *s = gdk_content_formats_to_string (formats);
+                       g_printerr ("%p: remote clipboard claim for %s\n", cb, s);
+                       g_free (s); );
+  cb->offer_formats = formats;
+  cb->offer = offer;
+
+  gdk_clipboard_claim_remote (GDK_CLIPBOARD (cb),
+                              cb->offer_formats);
+}
+
diff --git a/gdk/wayland/gdkclipboard-wayland.h b/gdk/wayland/gdkclipboard-wayland.h
index cf8bd7e..5e5eba8 100644
--- a/gdk/wayland/gdkclipboard-wayland.h
+++ b/gdk/wayland/gdkclipboard-wayland.h
@@ -20,6 +20,7 @@
 
 #include "gdk/gdkclipboard.h"
 
+#include <wayland-client.h>
 
 G_BEGIN_DECLS
 
@@ -33,6 +34,9 @@ GType                   gdk_wayland_clipboard_get_type              (void) G_GNU
 
 GdkClipboard *          gdk_wayland_clipboard_new                   (GdkDisplay             *display);
 
+void                    gdk_wayland_clipboard_claim_remote          (GdkWaylandClipboard    *cb,
+                                                                     struct wl_data_offer   *offer,
+                                                                     GdkContentFormats      *formats);
 
 G_END_DECLS
 
diff --git a/gdk/wayland/gdkdevice-wayland.c b/gdk/wayland/gdkdevice-wayland.c
index 908d9bd..ca2ea3e 100644
--- a/gdk/wayland/gdkdevice-wayland.c
+++ b/gdk/wayland/gdkdevice-wayland.c
@@ -216,7 +216,6 @@ struct _GdkWaylandSeat
 
   GdkModifierType key_modifiers;
   GdkWindow *keyboard_focus;
-  GdkAtom pending_selection;
   GdkWindow *grab_window;
   uint32_t grab_time;
   gboolean have_server_repeat;
@@ -1191,26 +1190,16 @@ data_device_selection (void                  *data,
                        struct wl_data_offer  *offer)
 {
   GdkWaylandSeat *seat = data;
-  GdkAtom selection;
-
-  GDK_NOTE (EVENTS,
-            g_message ("data device selection, data device %p, data offer %p",
-                       wl_data_device, offer));
+  GdkContentFormats *formats;
 
-  selection = gdk_atom_intern_static_string ("CLIPBOARD");
-  gdk_wayland_selection_set_offer (seat->display, selection, offer);
-
-#if 0
-  /* If we already have keyboard focus, the selection was targeted at the
-   * focused surface. If we don't we will receive keyboard focus directly after
-   * this, so lets wait and find out what window will get the focus before
-   * emitting the owner-changed event.
-   */
-  if (seat->keyboard_focus)
-    emit_selection_owner_change (seat->keyboard_focus, selection);
+  if (offer)
+    formats = gdk_wayland_selection_steal_offer (seat->display, offer);
   else
-    seat->pending_selection = selection;
-#endif
+    formats = gdk_content_formats_new (NULL, 0);
+
+  gdk_wayland_clipboard_claim_remote (GDK_WAYLAND_CLIPBOARD (seat->clipboard),
+                                      offer,
+                                      formats);
 }
 
 static const struct wl_data_device_listener data_device_listener = {
@@ -1860,15 +1849,6 @@ keyboard_handle_enter (void               *data,
                        seat, seat->keyboard_focus));
 
   _gdk_wayland_display_deliver_event (seat->display, event);
-
-  /*
-  if (seat->pending_selection != NULL)
-    {
-      emit_selection_owner_change (seat->keyboard_focus,
-                                   seat->pending_selection);
-      seat->pending_selection = NULL;
-    }
-  */
 }
 
 static void stop_key_repeat (GdkWaylandSeat *seat);
@@ -4811,8 +4791,6 @@ _gdk_wayland_display_create_seat (GdkWaylandDisplay *display_wayland,
   seat->foreign_dnd_window = create_foreign_dnd_window (display);
   seat->wl_seat = wl_seat;
 
-  seat->pending_selection = NULL;
-
   wl_seat_add_listener (seat->wl_seat, &seat_listener, seat);
   wl_seat_set_user_data (seat->wl_seat, seat);
 
diff --git a/gdk/wayland/gdkprivate-wayland.h b/gdk/wayland/gdkprivate-wayland.h
index 0a21c89..2f99104 100644
--- a/gdk/wayland/gdkprivate-wayland.h
+++ b/gdk/wayland/gdkprivate-wayland.h
@@ -227,10 +227,12 @@ GdkWaylandSelection * gdk_wayland_display_get_selection (GdkDisplay *display);
 GdkWaylandSelection * gdk_wayland_selection_new (void);
 void gdk_wayland_selection_free (GdkWaylandSelection *selection);
 
+
 void gdk_wayland_selection_ensure_offer (GdkDisplay           *display,
                                          struct wl_data_offer *wl_offer);
 void gdk_wayland_selection_ensure_primary_offer (GdkDisplay                         *display,
                                                  struct gtk_primary_selection_offer *wp_offer);
+GdkContentFormats *gdk_wayland_selection_steal_offer (GdkDisplay *display, gpointer wl_offer);
 
 void gdk_wayland_selection_set_offer (GdkDisplay           *display,
                                       GdkAtom               selection,
diff --git a/gdk/wayland/gdkselection-wayland.c b/gdk/wayland/gdkselection-wayland.c
index 846972a..f2d76fc 100644
--- a/gdk/wayland/gdkselection-wayland.c
+++ b/gdk/wayland/gdkselection-wayland.c
@@ -537,6 +537,25 @@ gdk_wayland_selection_ensure_primary_offer (GdkDisplay                         *
     }
 }
 
+GdkContentFormats *
+gdk_wayland_selection_steal_offer (GdkDisplay *display,
+                                   gpointer    wl_offer)
+{
+  GdkWaylandSelection *selection = gdk_wayland_display_get_selection (display);
+  GdkContentFormats *formats;
+  DataOfferData *info;
+
+  info = g_hash_table_lookup (selection->offers, wl_offer);
+  if (info == NULL)
+    return NULL;
+
+  g_hash_table_steal (selection->offers, wl_offer);
+  formats = info->targets;
+  g_slice_free (DataOfferData, info);
+
+  return formats;
+}
+
 void
 gdk_wayland_selection_set_offer (GdkDisplay *display,
                                  GdkAtom     selection_atom,


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