[gnome-remote-desktop] session: Add APIs and callbacks for clipboard handling



commit 45d56c8a4a70a9f21a3ca2449176a876344eb2b4
Author: Pascal Nowack <Pascal Nowack gmx de>
Date:   Tue Sep 22 13:45:40 2020 +0200

    session: Add APIs and callbacks for clipboard handling
    
    Add APIs to enable or disable a clipboard.
    Also add APIs to submit mime type lists, requested content for a mime
    type and to read the content for a mime type from mutter.
    Additionally, handle SelectionOwnerChanged- and SelectionTransfer-
    signals with the previously implemented clipboard APIs in
    grd-clipboard.

 src/grd-session.c | 304 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 src/grd-session.h |  15 +++
 2 files changed, 319 insertions(+)
---
diff --git a/src/grd-session.c b/src/grd-session.c
index bbaee00..2c54c30 100644
--- a/src/grd-session.c
+++ b/src/grd-session.c
@@ -1,5 +1,6 @@
 /*
  * Copyright (C) 2015 Red Hat Inc.
+ * Copyright (C) 2020 Pascal Nowack
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License as
@@ -24,8 +25,12 @@
 
 #include "grd-session.h"
 
+#include <gio/gunixfdlist.h>
 #include <glib-object.h>
+#include <glib-unix.h>
+#include <sys/mman.h>
 
+#include "grd-clipboard.h"
 #include "grd-dbus-remote-desktop.h"
 #include "grd-context.h"
 #include "grd-private.h"
@@ -63,6 +68,8 @@ typedef struct _GrdSessionPrivate
 
   GrdStream *stream;
 
+  GrdClipboard *clipboard;
+
   GCancellable *cancellable;
 
   gboolean started;
@@ -174,6 +181,194 @@ grd_session_notify_pointer_motion_absolute (GrdSession *session,
     priv->remote_desktop_session, stream_path, x, y, NULL, NULL, NULL);
 }
 
+static GVariant *
+serialize_mime_type_tables (GList *mime_type_tables)
+{
+  GVariantBuilder builder;
+  GList *l;
+
+  g_variant_builder_init (&builder, G_VARIANT_TYPE ("as"));
+  for (l = mime_type_tables; l; l = l->next)
+    {
+      GrdMimeTypeTable *mime_type_table = l->data;
+      GrdMimeType mime_type;
+      const char *mime_type_string;
+      
+      mime_type = mime_type_table->mime_type;
+      mime_type_string = grd_mime_type_to_string (mime_type);
+      g_variant_builder_add (&builder, "s", mime_type_string);
+    }
+
+  return g_variant_builder_end (&builder);
+}
+
+static GVariant *
+serialize_clipboard_options (GList *mime_type_tables)
+{
+  GVariantBuilder builder;
+
+  g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{sv}"));
+  if (mime_type_tables)
+    {
+      g_variant_builder_add (&builder, "{sv}", "mime-types",
+                             serialize_mime_type_tables (mime_type_tables));
+    }
+
+  return g_variant_builder_end (&builder);
+}
+
+gboolean
+grd_session_enable_clipboard (GrdSession   *session,
+                              GrdClipboard *clipboard,
+                              GList        *mime_type_tables)
+{
+  GrdSessionPrivate *priv = grd_session_get_instance_private (session);
+  GVariant *options_variant;
+  g_autoptr (GError) error = NULL;
+
+  if (!priv->remote_desktop_session)
+    return FALSE;
+
+  priv->clipboard = clipboard;
+
+  options_variant = serialize_clipboard_options (mime_type_tables);
+  if (!grd_dbus_remote_desktop_session_call_enable_clipboard_sync (
+         priv->remote_desktop_session, options_variant, NULL, &error))
+    {
+      g_warning ("Failed to enable clipboard: %s", error->message);
+      return FALSE;
+    }
+
+  return TRUE;
+}
+
+void
+grd_session_disable_clipboard (GrdSession *session)
+{
+  GrdSessionPrivate *priv = grd_session_get_instance_private (session);
+
+  if (!priv->remote_desktop_session)
+    return;
+
+  grd_dbus_remote_desktop_session_call_disable_clipboard (
+    priv->remote_desktop_session, NULL, NULL, NULL);
+}
+
+void
+grd_session_set_selection (GrdSession *session,
+                           GList      *mime_type_tables)
+{
+  GrdSessionPrivate *priv = grd_session_get_instance_private (session);
+  GVariant *options_variant;
+  g_autoptr (GError) error = NULL;
+  
+  options_variant = serialize_clipboard_options (mime_type_tables);
+
+  if (!grd_dbus_remote_desktop_session_call_set_selection_sync (
+         priv->remote_desktop_session, options_variant, NULL, &error))
+    g_warning ("Failed to set selection: %s", error->message);
+}
+
+static int
+acquire_fd_from_list (GUnixFDList  *fd_list,
+                      int           fd_idx,
+                      GError      **error)
+{
+  int fd;
+  int fd_flags;
+
+  fd = g_unix_fd_list_get (fd_list, fd_idx, error);
+  if (fd == -1)
+    {
+      g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errno),
+                   "fcntl: %s", g_strerror (errno));
+      return -1;
+    }
+
+  fd_flags = fcntl (fd, F_GETFD);
+  if (fd_flags == -1)
+    {
+      close (fd);
+      g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errno),
+                   "fcntl: %s", g_strerror (errno));
+      return -1;
+    }
+
+  if (fcntl (fd, F_SETFD, fd_flags | FD_CLOEXEC) == -1)
+    {
+      close (fd);
+      g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errno),
+                   "fcntl: %s", g_strerror (errno));
+      return -1;
+    }
+
+  return fd;
+}
+
+uint8_t *
+grd_session_selection_read (GrdSession  *session,
+                            GrdMimeType  mime_type,
+                            uint32_t    *size)
+{
+  GrdSessionPrivate *priv = grd_session_get_instance_private (session);
+  g_autoptr (GError) error = NULL;
+  g_autoptr (GVariant) fd_variant = NULL;
+  g_autoptr (GUnixFDList) fd_list = NULL;
+  int fd_idx;
+  int fd;
+  const char *mime_type_string;
+  GArray *data;
+
+  mime_type_string = grd_mime_type_to_string (mime_type);
+  if (!grd_dbus_remote_desktop_session_call_selection_read_sync (
+         priv->remote_desktop_session, mime_type_string, NULL, &fd_variant,
+         &fd_list, NULL, &error))
+    {
+      g_warning ("Failed to read selection: %s", error->message);
+      return NULL;
+    }
+
+  g_variant_get (fd_variant, "h", &fd_idx);
+  fd = acquire_fd_from_list (fd_list, fd_idx, &error);
+  if (fd == -1)
+    {
+      g_warning ("Failed to acquire file descriptor: %s", error->message);
+      return NULL;
+    }
+
+  data = g_array_new (FALSE, TRUE, sizeof (uint8_t));
+  while (TRUE)
+    {
+      int len;
+      uint8_t buffer[1024];
+
+      len = read (fd, buffer, G_N_ELEMENTS (buffer));
+      if (len < 0)
+        {
+          if (errno == EAGAIN)
+            continue;
+
+          g_warning ("read() failed: %s", g_strerror (errno));
+          break;
+        }
+      else if (len == 0)
+        {
+          break;
+        }
+      else
+        {
+          g_array_append_vals (data, buffer, len);
+        }
+    }
+
+  if (data->len >= 0)
+    *size = data->len;
+
+  close (fd);
+
+  return (uint8_t *) g_array_free (data, FALSE);
+}
+
 static void
 on_stream_ready (GrdStream  *stream,
                  GrdSession *session)
@@ -380,6 +575,109 @@ on_remote_desktop_session_closed (GrdDBusRemoteDesktopSession *session_proxy,
   grd_session_stop (session);
 }
 
+static void
+on_remote_desktop_session_selection_owner_changed (GrdDBusRemoteDesktopSession *session_proxy,
+                                                   GVariant                    *options_variant,
+                                                   GrdSession                  *session)
+{
+  GrdSessionPrivate *priv = grd_session_get_instance_private (session);
+  GVariant *is_owner_variant, *mime_types_variant;
+  GVariantIter iter;
+  const char *mime_string;
+  GrdMimeType mime_type;
+  GList *mime_type_list = NULL;
+
+  is_owner_variant = g_variant_lookup_value (options_variant, "session-is-owner",
+                                             G_VARIANT_TYPE ("b"));
+  if (is_owner_variant && g_variant_get_boolean (is_owner_variant))
+    return;
+
+  mime_types_variant = g_variant_lookup_value (options_variant, "mime-types",
+                                               G_VARIANT_TYPE ("(as)"));
+  if (!mime_types_variant)
+    return;
+
+  g_variant_iter_init (&iter, g_variant_get_child_value (mime_types_variant, 0));
+  while (g_variant_iter_loop (&iter, "s", &mime_string))
+    {
+      mime_type = grd_mime_type_from_string (mime_string);
+      if (mime_type != GRD_MIME_TYPE_NONE)
+        {
+          mime_type_list = g_list_append (mime_type_list,
+                                          GUINT_TO_POINTER (mime_type));
+          g_debug ("Clipboard[SelectionOwnerChanged]: Server advertises mime "
+                   "type %s", mime_string);
+        }
+      else
+        {
+          g_debug ("Clipboard[SelectionOwnerChanged]: Server advertised unknown "
+                   "mime type: %s", mime_string);
+        }
+    }
+
+  if (mime_type_list)
+    grd_clipboard_update_client_mime_type_list (priv->clipboard, mime_type_list);
+}
+
+static void
+on_remote_desktop_session_selection_transfer (GrdDBusRemoteDesktopSession *session_proxy,
+                                              char                        *mime_type_string,
+                                              unsigned int                 serial,
+                                              GrdSession                  *session)
+{
+  GrdSessionPrivate *priv = grd_session_get_instance_private (session);
+  uint8_t *data;
+  uint32_t size;
+  g_autoptr (GError) error = NULL;
+  g_autoptr (GVariant) fd_variant = NULL;
+  g_autoptr (GUnixFDList) fd_list = NULL;
+  int fd_idx;
+  int fd;
+  GrdMimeType mime_type;
+
+  mime_type = grd_mime_type_from_string (mime_type_string);
+  if (mime_type == GRD_MIME_TYPE_NONE)
+    {
+      grd_dbus_remote_desktop_session_call_selection_write_done (
+        priv->remote_desktop_session, serial, FALSE, NULL, NULL, NULL);
+      return;
+    }
+
+  if (!grd_dbus_remote_desktop_session_call_selection_write_sync (
+         priv->remote_desktop_session, serial, NULL, &fd_variant, &fd_list,
+         NULL, &error))
+    {
+      g_warning ("Failed to write selection: %s", error->message);
+      return;
+    }
+
+  g_variant_get (fd_variant, "h", &fd_idx);
+  fd = acquire_fd_from_list (fd_list, fd_idx, &error);
+  if (fd == -1)
+    {
+      g_warning ("Failed to acquire file descriptor: %s", error->message);
+      return;
+    }
+
+  data = grd_clipboard_request_client_content_for_mime_type (priv->clipboard,
+                                                             mime_type, &size);
+  if (!size || write (fd, data, size) < 0)
+    {
+      grd_dbus_remote_desktop_session_call_selection_write_done (
+        priv->remote_desktop_session, serial, FALSE, NULL, NULL, NULL);
+
+      close (fd);
+      g_free (data);
+      return;
+    }
+
+  grd_dbus_remote_desktop_session_call_selection_write_done (
+    priv->remote_desktop_session, serial, TRUE, NULL, NULL, NULL);
+
+  close (fd);
+  g_free (data);
+}
+
 static void
 on_remote_desktop_session_proxy_acquired (GObject      *object,
                                           GAsyncResult *result,
@@ -410,6 +708,12 @@ on_remote_desktop_session_proxy_acquired (GObject      *object,
   g_signal_connect (session_proxy, "closed",
                     G_CALLBACK (on_remote_desktop_session_closed),
                     session);
+  g_signal_connect (session_proxy, "selection-owner-changed",
+                    G_CALLBACK (on_remote_desktop_session_selection_owner_changed),
+                    session);
+  g_signal_connect (session_proxy, "selection-transfer",
+                    G_CALLBACK (on_remote_desktop_session_selection_transfer),
+                    session);
 
   priv->remote_desktop_session = session_proxy;
 
diff --git a/src/grd-session.h b/src/grd-session.h
index 2dc6f48..937c068 100644
--- a/src/grd-session.h
+++ b/src/grd-session.h
@@ -1,5 +1,6 @@
 /*
  * Copyright (C) 2015 Red Hat Inc.
+ * Copyright (C) 2020 Pascal Nowack
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License as
@@ -26,6 +27,7 @@
 #include <glib-object.h>
 #include <stdint.h>
 
+#include "grd-mime-type.h"
 #include "grd-types.h"
 
 #define GRD_TYPE_SESSION (grd_session_get_type ())
@@ -89,6 +91,19 @@ void grd_session_notify_pointer_motion_absolute (GrdSession *session,
                                                  double      x,
                                                  double      y);
 
+gboolean grd_session_enable_clipboard (GrdSession   *session,
+                                       GrdClipboard *clipboard,
+                                       GList        *mime_type_tables);
+
+void grd_session_disable_clipboard (GrdSession *session);
+
+void grd_session_set_selection (GrdSession *session,
+                                GList      *mime_type_tables);
+
+uint8_t *grd_session_selection_read (GrdSession  *session,
+                                     GrdMimeType  mime_type,
+                                     uint32_t    *size);
+
 void grd_session_start (GrdSession *session);
 
 void grd_session_stop (GrdSession *session);


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