[mutter/wip/carlosg/clipboard-manager: 1/10] core: Add MetaSelection and MetaSelectionSource



commit 6c3c9f11e350ea85b8f156f3bbeec08308a79eae
Author: Carlos Garnacho <carlosg gnome org>
Date:   Mon Nov 19 16:45:20 2018 +0100

    core: Add MetaSelection and MetaSelectionSource
    
    MetaSelectionSource represents a primary/clipboard/dnd selection owner,
    it is an abstract type so wayland/x11/etc implementations can be provided.
    These 3 selections are managed by the MetaSelection object, the current
    selection owners will be set there, and signals will be emitted so the
    previous selection owner can clean itself up.
    
    The actual data transfer is done through the meta_selection_transfer_async()
    call, which will take a GOutputStream and create a corresponding
    GInputStream from the MetaSelectionSource in order to splice them.

 src/core/meta-selection-source.c | 143 +++++++++++++++++++
 src/core/meta-selection-source.h |  79 +++++++++++
 src/core/meta-selection.c        | 296 +++++++++++++++++++++++++++++++++++++++
 src/core/meta-selection.h        |  62 ++++++++
 src/meson.build                  |   4 +
 5 files changed, 584 insertions(+)
---
diff --git a/src/core/meta-selection-source.c b/src/core/meta-selection-source.c
new file mode 100644
index 000000000..5d85ac171
--- /dev/null
+++ b/src/core/meta-selection-source.c
@@ -0,0 +1,143 @@
+/*
+ * Copyright (C) 2018 Red Hat
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ *
+ * Author: Carlos Garnacho <carlosg gnome org>
+ */
+#include "config.h"
+
+#include "core/meta-selection.h"
+#include "core/meta-selection-source.h"
+
+typedef struct MetaSelectionSourcePrivate MetaSelectionSourcePrivate;
+
+struct MetaSelectionSourcePrivate
+{
+  MetaSelectionType type;
+  guint active : 1;
+};
+
+G_DEFINE_TYPE_WITH_PRIVATE (MetaSelectionSource,
+                            meta_selection_source,
+                            G_TYPE_OBJECT)
+
+enum
+{
+  ACTIVE,
+  INACTIVE,
+  N_SIGNALS
+};
+
+static guint signals[N_SIGNALS] = { 0 };
+
+static void
+meta_selection_source_active (MetaSelectionSource *source,
+                              MetaSelectionType    selection_type)
+{
+  MetaSelectionSourcePrivate *priv =
+    meta_selection_source_get_instance_private (source);
+
+  g_assert (priv->type == -1);
+
+  priv->type = selection_type;
+  priv->active = TRUE;
+}
+
+static void
+meta_selection_source_inactive (MetaSelectionSource *source)
+{
+  MetaSelectionSourcePrivate *priv =
+    meta_selection_source_get_instance_private (source);
+
+  priv->active = FALSE;
+}
+
+static void
+meta_selection_source_class_init (MetaSelectionSourceClass *klass)
+{
+  klass->active = meta_selection_source_active;
+  klass->inactive = meta_selection_source_inactive;
+
+  signals[ACTIVE] =
+    g_signal_new ("active",
+                  G_TYPE_FROM_CLASS (klass),
+                  G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+                  G_STRUCT_OFFSET (MetaSelectionSourceClass, active),
+                  NULL, NULL, NULL,
+                  G_TYPE_NONE, 1,
+                  G_TYPE_UINT);
+  signals[INACTIVE] =
+    g_signal_new ("inactive",
+                  G_TYPE_FROM_CLASS (klass),
+                  G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+                  G_STRUCT_OFFSET (MetaSelectionSourceClass, inactive),
+                  NULL, NULL, NULL,
+                  G_TYPE_NONE, 0);
+}
+
+static void
+meta_selection_source_init (MetaSelectionSource *source)
+{
+  MetaSelectionSourcePrivate *priv =
+    meta_selection_source_get_instance_private (source);
+
+  priv->type = -1;
+}
+
+void
+meta_selection_source_read_async (MetaSelectionSource *source,
+                                  const gchar         *mimetype,
+                                  GCancellable        *cancellable,
+                                  GAsyncReadyCallback  callback,
+                                  gpointer             user_data)
+{
+  META_SELECTION_SOURCE_GET_CLASS (source)->read_async (source,
+                                                        mimetype,
+                                                        cancellable,
+                                                        callback,
+                                                        user_data);
+}
+
+GInputStream *
+meta_selection_source_read_finish (MetaSelectionSource  *source,
+                                   GAsyncResult         *result,
+                                   GError              **error)
+{
+  return META_SELECTION_SOURCE_GET_CLASS (source)->read_finish (source,
+                                                                result,
+                                                                error);
+}
+
+GList *
+meta_selection_source_get_mimetypes (MetaSelectionSource  *source)
+{
+  return META_SELECTION_SOURCE_GET_CLASS (source)->get_mimetypes (source);
+}
+
+gboolean
+meta_selection_source_get_selection_type (MetaSelectionSource *source,
+                                          MetaSelectionType   *selection_type)
+{
+  MetaSelectionSourcePrivate *priv =
+    meta_selection_source_get_instance_private (source);
+
+  if (!priv->active)
+    return FALSE;
+
+  *selection_type = priv->type;
+  return TRUE;
+}
diff --git a/src/core/meta-selection-source.h b/src/core/meta-selection-source.h
new file mode 100644
index 000000000..86c115130
--- /dev/null
+++ b/src/core/meta-selection-source.h
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2018 Red Hat
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ *
+ * Author: Carlos Garnacho <carlosg gnome org>
+ */
+#ifndef META_SELECTION_SOURCE_H
+#define META_SELECTION_SOURCE_H
+
+#include <gio/gio.h>
+
+typedef enum
+{
+  META_SELECTION_PRIMARY,
+  META_SELECTION_CLIPBOARD,
+  META_SELECTION_DND,
+  META_N_SELECTION_TYPES,
+} MetaSelectionType;
+
+typedef struct _MetaSelectionSourceClass MetaSelectionSourceClass;
+typedef struct _MetaSelectionSource MetaSelectionSource;
+
+#define META_TYPE_SELECTION_SOURCE (meta_selection_source_get_type ())
+
+G_DECLARE_DERIVABLE_TYPE (MetaSelectionSource,
+                          meta_selection_source,
+                          META, SELECTION_SOURCE,
+                          GObject)
+
+struct _MetaSelectionSourceClass
+{
+  GObjectClass parent_class;
+
+  void           (* active)        (MetaSelectionSource *source,
+                                    MetaSelectionType    selection_type);
+  void           (* inactive)      (MetaSelectionSource *source);
+
+  GList *        (* get_mimetypes) (MetaSelectionSource  *source);
+
+  void           (* read_async)    (MetaSelectionSource  *source,
+                                    const gchar          *mimetype,
+                                    GCancellable         *cancellable,
+                                    GAsyncReadyCallback   callback,
+                                    gpointer              user_data);
+  GInputStream * (* read_finish)   (MetaSelectionSource  *source,
+                                    GAsyncResult         *result,
+                                    GError              **error);
+};
+
+void           meta_selection_source_read_async  (MetaSelectionSource  *source,
+                                                  const gchar          *mimetype,
+                                                  GCancellable         *cancellable,
+                                                  GAsyncReadyCallback   callback,
+                                                  gpointer              user_data);
+
+GInputStream * meta_selection_source_read_finish (MetaSelectionSource  *source,
+                                                  GAsyncResult         *result,
+                                                  GError              **error);
+
+GList *  meta_selection_source_get_mimetypes     (MetaSelectionSource  *source);
+
+gboolean meta_selection_source_get_selection_type (MetaSelectionSource  *source,
+                                                   MetaSelectionType    *selection);
+
+#endif /* META_SELECTION_SOURCE_H */
diff --git a/src/core/meta-selection.c b/src/core/meta-selection.c
new file mode 100644
index 000000000..87ac677f7
--- /dev/null
+++ b/src/core/meta-selection.c
@@ -0,0 +1,296 @@
+/*
+ * Copyright (C) 2018 Red Hat
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ *
+ * Author: Carlos Garnacho <carlosg gnome org>
+ */
+
+#include "config.h"
+
+#include "core/meta-selection.h"
+
+typedef struct TransferRequest TransferRequest;
+
+struct _MetaSelection
+{
+  GObject parent_instance;
+  MetaDisplay *display;
+  MetaSelectionSource *owners[META_N_SELECTION_TYPES];
+};
+
+struct TransferRequest
+{
+  MetaSelectionType selection_type;
+  GInputStream  *istream;
+  GOutputStream *ostream;
+  gssize len;
+};
+
+enum
+{
+  OWNER_CHANGED,
+  N_SIGNALS
+};
+
+static guint signals[N_SIGNALS] = { 0 };
+
+G_DEFINE_TYPE (MetaSelection, meta_selection, G_TYPE_OBJECT)
+
+static void
+meta_selection_class_init (MetaSelectionClass *klass)
+{
+  signals[OWNER_CHANGED] =
+    g_signal_new ("owner-changed",
+                  G_TYPE_FROM_CLASS (klass),
+                  G_SIGNAL_RUN_LAST,
+                  0,
+                  NULL, NULL, NULL,
+                  G_TYPE_NONE, 2,
+                  G_TYPE_UINT,
+                  META_TYPE_SELECTION_SOURCE);
+}
+
+static void
+meta_selection_init (MetaSelection *selection)
+{
+}
+
+MetaSelection *
+meta_selection_new (MetaDisplay *display)
+{
+  return g_object_new (META_TYPE_SELECTION,
+                       NULL);
+}
+
+void
+meta_selection_set_owner (MetaSelection       *selection,
+                          MetaSelectionType    selection_type,
+                          MetaSelectionSource *owner)
+{
+  g_return_if_fail (META_IS_SELECTION (selection));
+  g_return_if_fail (selection_type < META_N_SELECTION_TYPES);
+
+  if (selection->owners[selection_type] == owner)
+    return;
+
+  if (selection->owners[selection_type])
+    g_signal_emit_by_name (selection->owners[selection_type], "inactive");
+
+  g_set_object (&selection->owners[selection_type], owner);
+  g_signal_emit_by_name (owner, "active", selection_type);
+  g_signal_emit (selection, signals[OWNER_CHANGED], 0, selection_type, owner);
+}
+
+void
+meta_selection_unset_owner (MetaSelection       *selection,
+                            MetaSelectionType    selection_type,
+                            MetaSelectionSource *owner)
+{
+  g_return_if_fail (META_IS_SELECTION (selection));
+  g_return_if_fail (selection_type < META_N_SELECTION_TYPES);
+
+  if (selection->owners[selection_type] == owner)
+    {
+      g_signal_emit_by_name (owner, "inactive");
+      g_clear_object (&selection->owners[selection_type]);
+      g_signal_emit (selection, signals[OWNER_CHANGED], 0,
+                     selection_type, NULL);
+    }
+}
+
+GList *
+meta_selection_get_mimetypes (MetaSelection     *selection,
+                              MetaSelectionType  selection_type)
+{
+  g_return_val_if_fail (META_IS_SELECTION (selection), NULL);
+  g_return_val_if_fail (selection_type < META_N_SELECTION_TYPES, NULL);
+
+  if (!selection->owners[selection_type])
+    return NULL;
+
+  return meta_selection_source_get_mimetypes (selection->owners[selection_type]);
+}
+
+static TransferRequest *
+transfer_request_new (GOutputStream     *ostream,
+                      MetaSelectionType  selection_type,
+                      gssize             len)
+{
+  TransferRequest *request;
+
+  request = g_new0 (TransferRequest, 1);
+  request->ostream = g_object_ref (ostream);
+  request->selection_type = selection_type;
+  request->len = len;
+  return request;
+}
+
+static void
+transfer_request_free (TransferRequest *request)
+{
+  g_clear_object (&request->istream);
+  g_clear_object (&request->ostream);
+  g_free (request);
+}
+
+static void
+splice_cb (GOutputStream *stream,
+           GAsyncResult  *result,
+           GTask         *task)
+{
+  GError *error = NULL;
+
+  g_output_stream_splice_finish (stream, result, &error);
+  if (error)
+    {
+      g_task_return_error (task, error);
+      g_object_unref (task);
+      return;
+    }
+
+  g_task_return_boolean (task, TRUE);
+  g_object_unref (task);
+}
+
+static void
+write_cb (GOutputStream *stream,
+          GAsyncResult  *result,
+          GTask         *task)
+{
+  GError *error = NULL;
+
+  g_output_stream_write_bytes_finish (stream, result, &error);
+  if (error)
+    {
+      g_task_return_error (task, error);
+      g_object_unref (task);
+      return;
+    }
+
+  g_task_return_boolean (task, TRUE);
+  g_object_unref (task);
+}
+
+static void
+read_cb (GInputStream *stream,
+         GAsyncResult *result,
+         GTask        *task)
+{
+  TransferRequest *request;
+  GError *error = NULL;
+  GBytes *bytes;
+
+  bytes = g_input_stream_read_bytes_finish (stream, result, &error);
+  if (error)
+    {
+      g_task_return_error (task, error);
+      g_object_unref (task);
+      return;
+    }
+
+  request = g_task_get_task_data (task);
+  g_output_stream_write_bytes_async (request->ostream,
+                                     bytes,
+                                     G_PRIORITY_DEFAULT,
+                                     g_task_get_cancellable (task),
+                                     (GAsyncReadyCallback) write_cb,
+                                     task);
+  g_bytes_unref (bytes);
+}
+
+static void
+source_read_cb (MetaSelectionSource *source,
+                GAsyncResult        *result,
+                GTask               *task)
+{
+  TransferRequest *request;
+  GInputStream *stream;
+  GError *error = NULL;
+
+  stream = meta_selection_source_read_finish (source, result, &error);
+  if (!stream)
+    {
+      g_task_return_error (task, error);
+      return;
+    }
+
+  request = g_task_get_task_data (task);
+  request->istream = stream;
+
+  if (request->len < 0)
+    {
+      g_output_stream_splice_async (request->ostream,
+                                    request->istream,
+                                    G_OUTPUT_STREAM_SPLICE_CLOSE_SOURCE |
+                                    G_OUTPUT_STREAM_SPLICE_CLOSE_TARGET,
+                                    G_PRIORITY_DEFAULT,
+                                    g_task_get_cancellable (task),
+                                    (GAsyncReadyCallback) splice_cb,
+                                    task);
+    }
+  else
+    {
+      g_input_stream_read_bytes_async (request->istream,
+                                       (gsize) request->len,
+                                       G_PRIORITY_DEFAULT,
+                                       g_task_get_cancellable (task),
+                                       (GAsyncReadyCallback) read_cb,
+                                       task);
+    }
+}
+
+void
+meta_selection_transfer_async (MetaSelection        *selection,
+                               MetaSelectionType     selection_type,
+                               const char           *mimetype,
+                               gssize                size,
+                               GOutputStream        *output,
+                               GCancellable         *cancellable,
+                               GAsyncReadyCallback   callback,
+                               gpointer              user_data)
+{
+  GTask *task;
+
+  g_return_if_fail (META_IS_SELECTION (selection));
+  g_return_if_fail (selection_type < META_N_SELECTION_TYPES);
+  g_return_if_fail (G_IS_OUTPUT_STREAM (output));
+  g_return_if_fail (mimetype != NULL);
+
+  task = g_task_new (selection, cancellable, callback, user_data);
+  g_task_set_source_tag (task, meta_selection_transfer_async);
+
+  g_task_set_task_data (task,
+                        transfer_request_new (output, selection_type, size),
+                        (GDestroyNotify) transfer_request_free);
+  meta_selection_source_read_async (selection->owners[selection_type],
+                                    mimetype,
+                                    cancellable,
+                                    (GAsyncReadyCallback) source_read_cb,
+                                    task);
+}
+
+gboolean
+meta_selection_transfer_finish (MetaSelection  *selection,
+                                GAsyncResult   *result,
+                                GError        **error)
+{
+  g_return_val_if_fail (g_task_is_valid (result, selection), FALSE);
+  g_return_val_if_fail (g_task_get_source_tag (G_TASK (result)) ==
+                        meta_selection_transfer_async, FALSE);
+
+  return g_task_propagate_boolean (G_TASK (result), error);
+}
diff --git a/src/core/meta-selection.h b/src/core/meta-selection.h
new file mode 100644
index 000000000..275ca544d
--- /dev/null
+++ b/src/core/meta-selection.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2018 Red Hat
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ *
+ * Author: Carlos Garnacho <carlosg gnome org>
+ */
+
+#ifndef META_SELECTION_H
+#define META_SELECTION_H
+
+#include <gio/gio.h>
+
+#include "core/meta-selection-source.h"
+#include "meta/display.h"
+
+#define META_TYPE_SELECTION (meta_selection_get_type ())
+
+G_DECLARE_FINAL_TYPE (MetaSelection,
+                      meta_selection,
+                      META, SELECTION,
+                      GObject)
+
+MetaSelection *
+         meta_selection_new                  (MetaDisplay *display);
+
+void     meta_selection_set_owner            (MetaSelection        *selection,
+                                              MetaSelectionType     selection_type,
+                                              MetaSelectionSource  *owner);
+void     meta_selection_unset_owner          (MetaSelection        *selection,
+                                              MetaSelectionType     selection_type,
+                                              MetaSelectionSource  *owner);
+
+GList *  meta_selection_get_mimetypes        (MetaSelection        *selection,
+                                              MetaSelectionType     selection_type);
+
+void     meta_selection_transfer_async       (MetaSelection        *selection,
+                                              MetaSelectionType     selection_type,
+                                              const gchar          *mimetype,
+                                              gssize                size,
+                                              GOutputStream        *output,
+                                              GCancellable         *cancellable,
+                                              GAsyncReadyCallback   callback,
+                                              gpointer              user_data);
+gboolean meta_selection_transfer_finish      (MetaSelection        *selection,
+                                              GAsyncResult         *result,
+                                              GError              **error);
+
+#endif /* META_SELECTION_H */
diff --git a/src/meson.build b/src/meson.build
index ca9698156..f5326ec2f 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -341,6 +341,10 @@ mutter_sources = [
   'core/meta-inhibit-shortcuts-dialog-default.c',
   'core/meta-inhibit-shortcuts-dialog-default-private.h',
   'core/meta-launch-context.c',
+  'core/meta-selection.c',
+  'core/meta-selection.h',
+  'core/meta-selection-source.c',
+  'core/meta-selection-source.h',
   'core/meta-sound-player.c',
   'core/meta-workspace-manager.c',
   'core/meta-workspace-manager-private.h',


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