[gtk+/gtk-3-22] mir: paste clipboard data from content-hub



commit 41732391d8b93478d4b63d63f9c3cc5bbf124a2a
Author: William Hua <william hua canonical com>
Date:   Sat Oct 15 22:19:59 2016 +0200

    mir: paste clipboard data from content-hub
    
    https://bugzilla.gnome.org/show_bug.cgi?id=775732

 gdk/mir/gdkmirdisplay.c |  339 ++++++++++++++++++++++++++++++++++++++++++++++-
 1 files changed, 335 insertions(+), 4 deletions(-)
---
diff --git a/gdk/mir/gdkmirdisplay.c b/gdk/mir/gdkmirdisplay.c
index 3dd3798..21967ac 100644
--- a/gdk/mir/gdkmirdisplay.c
+++ b/gdk/mir/gdkmirdisplay.c
@@ -23,6 +23,8 @@
 #include "gdkmir.h"
 #include "gdkmir-private.h"
 
+#include <string.h>
+
 #include <com/ubuntu/content/glib/content-hub-glib.h>
 
 #define GDK_TYPE_DISPLAY_MIR              (gdk_mir_display_get_type ())
@@ -62,6 +64,7 @@ typedef struct GdkMirDisplay
 
   ContentHubService *content_service;
   ContentHubHandler *content_handler;
+  GVariant *paste_data;
 } GdkMirDisplay;
 
 typedef struct GdkMirDisplayClass
@@ -107,6 +110,13 @@ static void get_pixel_formats (MirConnection *, MirPixelFormat *sw, MirPixelForm
 
 G_DEFINE_TYPE (GdkMirDisplay, gdk_mir_display, GDK_TYPE_DISPLAY)
 
+static void
+pasteboard_changed_cb (GdkMirDisplay *display,
+                       gpointer       user_data)
+{
+  g_clear_pointer (&display->paste_data, g_variant_unref);
+}
+
 GdkDisplay *
 _gdk_mir_display_open (const gchar *display_name)
 {
@@ -156,6 +166,12 @@ _gdk_mir_display_open (const gchar *display_name)
     NULL,
     NULL);
 
+  g_signal_connect_swapped (
+    display->content_service,
+    "pasteboard-changed",
+    G_CALLBACK (pasteboard_changed_cb),
+    display);
+
   display->content_handler = content_hub_handler_skeleton_new ();
 
   g_dbus_interface_skeleton_export (
@@ -214,6 +230,7 @@ gdk_mir_display_dispose (GObject *object)
 {
   GdkMirDisplay *display = GDK_MIR_DISPLAY (object);
 
+  g_clear_pointer (&display->paste_data, g_variant_unref);
   g_dbus_interface_skeleton_unexport (G_DBUS_INTERFACE_SKELETON (display->content_handler));
   g_clear_object (&display->content_handler);
   g_clear_object (&display->content_service);
@@ -547,10 +564,255 @@ gdk_mir_display_get_selection_property (GdkDisplay  *display,
                                         GdkAtom     *ret_type,
                                         gint        *ret_format)
 {
-  //g_printerr ("gdk_mir_display_get_selection_property\n");
+  gint length;
+
+  gdk_property_get (requestor,
+                    gdk_atom_intern_static_string ("GDK_SELECTION"),
+                    GDK_NONE,
+                    0,
+                    G_MAXULONG,
+                    FALSE,
+                    ret_type,
+                    ret_format,
+                    &length,
+                    data);
+
+  return length;
+}
+
+static gint
+get_format_score (const gchar *format,
+                  GdkAtom      target,
+                  GdkAtom     *out_type,
+                  gint        *out_size)
+{
+  const gchar *target_string;
+  GdkAtom dummy_type;
+  gint dummy_size;
+
+  target_string = _gdk_atom_name_const (target);
+
+  if (!out_type)
+    out_type = &dummy_type;
+
+  if (!out_size)
+    out_size = &dummy_size;
+
+  if (!g_ascii_strcasecmp (format, target_string))
+    {
+      *out_type = GDK_SELECTION_TYPE_STRING;
+      *out_size = sizeof (guchar);
+
+      return G_MAXINT;
+    }
+
+  if (target == gdk_atom_intern_static_string ("UTF8_STRING"))
+    return get_format_score (format, gdk_atom_intern_static_string ("text/plain;charset=utf-8"), out_type, 
out_size);
+
+  /* TODO: use best media type for COMPOUND_TEXT target */
+  if (target == gdk_atom_intern_static_string ("COMPOUND_TEXT"))
+    return get_format_score (format, gdk_atom_intern_static_string ("text/plain;charset=utf-8"), out_type, 
out_size);
+
+  if (target == GDK_TARGET_STRING)
+    return get_format_score (format, gdk_atom_intern_static_string ("text/plain;charset=iso-8859-1"), 
out_type, out_size);
+
+  if (target == gdk_atom_intern_static_string ("GTK_TEXT_BUFFER_CONTENTS"))
+    return get_format_score (format, gdk_atom_intern_static_string ("text/plain;charset=utf-8"), out_type, 
out_size);
+
+  if (g_content_type_is_a (format, target_string))
+    {
+      *out_type = GDK_SELECTION_TYPE_STRING;
+      *out_size = sizeof (guchar);
+
+      return 2;
+    }
+
+  if (g_content_type_is_a (target_string, format))
+    {
+      *out_type = GDK_SELECTION_TYPE_STRING;
+      *out_size = sizeof (guchar);
+
+      return 1;
+    }
+
   return 0;
 }
 
+static gint
+get_best_format_index (const gchar * const *formats,
+                       guint                n_formats,
+                       GdkAtom              target,
+                       GdkAtom             *out_type,
+                       gint                *out_size)
+{
+  gint best_i = -1;
+  gint best_score = 0;
+  GdkAtom best_type;
+  gint best_size;
+  gint score;
+  GdkAtom type;
+  gint size;
+  gint i;
+
+  if (!out_type)
+    out_type = &best_type;
+
+  if (!out_size)
+    out_size = &best_size;
+
+  *out_type = GDK_NONE;
+  *out_size = 0;
+
+  for (i = 0; i < n_formats; i++)
+    {
+      score = get_format_score (formats[i], target, &type, &size);
+
+      if (score > best_score)
+        {
+          best_i = i;
+          best_score = score;
+          *out_type = type;
+          *out_size = size;
+        }
+    }
+
+  return best_i;
+}
+
+static void
+gdk_mir_display_real_convert_selection (GdkDisplay *display,
+                                        GdkWindow  *requestor,
+                                        GdkAtom     selection,
+                                        GdkAtom     target,
+                                        guint32     time)
+{
+  GdkMirDisplay *mir_display = GDK_MIR_DISPLAY (display);
+  const gchar *paste_data;
+  gsize paste_size;
+  const gint *paste_header;
+  GPtrArray *paste_formats;
+  GArray *paste_targets;
+  GdkAtom paste_target;
+  GdkEvent *event;
+  gint best_i;
+  GdkAtom best_type;
+  gint best_size;
+  gint i;
+
+  g_return_if_fail (mir_display->paste_data);
+
+  paste_data = g_variant_get_fixed_array (mir_display->paste_data, &paste_size, sizeof (guchar));
+  paste_header = (const gint *) paste_data;
+  paste_formats = g_ptr_array_new_full (paste_header[0], g_free);
+
+  for (i = 0; i < paste_header[0]; i++)
+    g_ptr_array_add (paste_formats, g_strndup (paste_data + paste_header[1 + 4 * i], paste_header[2 + 4 * 
i]));
+
+  if (target == gdk_atom_intern_static_string ("TARGETS"))
+    {
+      paste_targets = g_array_sized_new (TRUE, FALSE, sizeof (GdkAtom), paste_formats->len);
+
+      for (i = 0; i < paste_formats->len; i++)
+        {
+          paste_target = gdk_atom_intern (g_ptr_array_index (paste_formats, i), FALSE);
+          g_array_append_val (paste_targets, paste_target);
+        }
+
+      gdk_property_change (requestor,
+                           gdk_atom_intern_static_string ("GDK_SELECTION"),
+                           GDK_SELECTION_TYPE_ATOM,
+                           8 * sizeof (GdkAtom),
+                           GDK_PROP_MODE_REPLACE,
+                           (const guchar *) paste_targets->data,
+                           paste_targets->len);
+
+      g_array_unref (paste_targets);
+
+      event = gdk_event_new (GDK_SELECTION_NOTIFY);
+      event->selection.window = g_object_ref (requestor);
+      event->selection.send_event = FALSE;
+      event->selection.selection = selection;
+      event->selection.target = target;
+      event->selection.property = gdk_atom_intern_static_string ("GDK_SELECTION");
+      event->selection.time = time;
+      event->selection.requestor = g_object_ref (requestor);
+
+      gdk_event_put (event);
+      gdk_event_free (event);
+    }
+  else
+    {
+      best_i = get_best_format_index ((const gchar * const *) paste_formats->pdata,
+                                      paste_formats->len,
+                                      target,
+                                      &best_type,
+                                      &best_size);
+
+      if (best_i >= 0)
+        {
+          gdk_property_change (requestor,
+                               gdk_atom_intern_static_string ("GDK_SELECTION"),
+                               best_type,
+                               8 * best_size,
+                               GDK_PROP_MODE_REPLACE,
+                               (const guchar *) paste_data + paste_header[3 + 4 * best_i],
+                               paste_header[4 + 4 * best_i] / best_size);
+
+          event = gdk_event_new (GDK_SELECTION_NOTIFY);
+          event->selection.window = g_object_ref (requestor);
+          event->selection.send_event = FALSE;
+          event->selection.selection = selection;
+          event->selection.target = target;
+          event->selection.property = gdk_atom_intern_static_string ("GDK_SELECTION");
+          event->selection.time = time;
+          event->selection.requestor = g_object_ref (requestor);
+
+          gdk_event_put (event);
+          gdk_event_free (event);
+        }
+    }
+
+  g_ptr_array_unref (paste_formats);
+}
+
+typedef struct
+{
+  GdkDisplay *display;
+  GdkWindow  *requestor;
+  GdkAtom     selection;
+  GdkAtom     target;
+  guint32     time;
+} ConvertInfo;
+
+static void
+paste_data_ready_cb (GObject      *source_object,
+                     GAsyncResult *res,
+                     gpointer      user_data)
+{
+  ContentHubService *content_service = CONTENT_HUB_SERVICE (source_object);
+  ConvertInfo *info = user_data;
+  GdkMirDisplay *mir_display = GDK_MIR_DISPLAY (info->display);
+  gboolean result;
+
+  g_clear_pointer (&mir_display->paste_data, g_variant_unref);
+
+  result = content_hub_service_call_get_latest_paste_data_finish (content_service,
+                                                                  &mir_display->paste_data,
+                                                                  res,
+                                                                  NULL);
+
+  if (result)
+    gdk_mir_display_real_convert_selection (info->display,
+                                            info->requestor,
+                                            info->selection,
+                                            info->target,
+                                            info->time);
+
+  g_object_unref (info->requestor);
+  g_object_unref (info->display);
+  g_free (info);
+}
+
 static void
 gdk_mir_display_convert_selection (GdkDisplay *display,
                                    GdkWindow  *requestor,
@@ -558,7 +820,46 @@ gdk_mir_display_convert_selection (GdkDisplay *display,
                                    GdkAtom     target,
                                    guint32     time)
 {
-  //g_printerr ("gdk_mir_display_convert_selection\n");
+  GdkMirDisplay *mir_display = GDK_MIR_DISPLAY (display);
+  MirSurface *surface;
+  MirPersistentId *persistent_id;
+  ConvertInfo *info;
+
+  if (selection != GDK_SELECTION_CLIPBOARD)
+    return;
+  else if (mir_display->paste_data)
+    gdk_mir_display_real_convert_selection (display, requestor, selection, target, time);
+  else if (mir_display->focused_window)
+    {
+      surface = gdk_mir_window_get_mir_surface (mir_display->focused_window);
+
+      if (!surface)
+        return;
+
+      persistent_id = mir_surface_request_persistent_id_sync (surface);
+
+      if (!persistent_id)
+        return;
+
+      if (mir_persistent_id_is_valid (persistent_id))
+        {
+          info = g_new (ConvertInfo, 1);
+          info->display = g_object_ref (display);
+          info->requestor = g_object_ref (requestor);
+          info->selection = selection;
+          info->target = target;
+          info->time = time;
+
+          content_hub_service_call_get_latest_paste_data (
+            mir_display->content_service,
+            mir_persistent_id_as_string (persistent_id),
+            NULL,
+            paste_data_ready_cb,
+            info);
+        }
+
+      mir_persistent_id_release (persistent_id);
+    }
 }
 
 static gint
@@ -569,8 +870,38 @@ gdk_mir_display_text_property_to_utf8_list (GdkDisplay    *display,
                                             gint           length,
                                             gchar       ***list)
 {
-  //g_printerr ("gdk_mir_display_text_property_to_utf8_list\n");
-  return 0;
+  GPtrArray *array;
+  const gchar *ptr;
+  gsize chunk_len;
+  gchar *copy;
+  guint nitems;
+
+  ptr = (const gchar *) text;
+  array = g_ptr_array_new ();
+
+  /* split text into utf-8 strings */
+  while (ptr < (const gchar *) &text[length])
+    {
+      chunk_len = strlen (ptr);
+
+      if (g_utf8_validate (ptr, chunk_len, NULL))
+        {
+          copy = g_strndup (ptr, chunk_len);
+          g_ptr_array_add (array, copy);
+        }
+
+      ptr = &ptr[chunk_len + 1];
+    }
+
+  nitems = array->len;
+  g_ptr_array_add (array, NULL);
+
+  if (list)
+    *list = (gchar **) g_ptr_array_free (array, FALSE);
+  else
+    g_ptr_array_free (array, TRUE);
+
+  return nitems;
 }
 
 static gchar *


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