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.

https://gitlab.gnome.org/GNOME/mutter/merge_requests/320
This commit is contained in:
Carlos Garnacho 2018-11-19 16:45:20 +01:00
parent 156980eff9
commit a984622cd1
5 changed files with 580 additions and 0 deletions

View File

@ -0,0 +1,140 @@
/*
* 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
{
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_activated (MetaSelectionSource *source)
{
MetaSelectionSourcePrivate *priv =
meta_selection_source_get_instance_private (source);
priv->active = TRUE;
}
static void
meta_selection_source_deactivated (MetaSelectionSource *source)
{
MetaSelectionSourcePrivate *priv =
meta_selection_source_get_instance_private (source);
priv->active = FALSE;
}
static void
meta_selection_source_class_init (MetaSelectionSourceClass *klass)
{
klass->activated = meta_selection_source_activated;
klass->deactivated = meta_selection_source_deactivated;
signals[ACTIVE] =
g_signal_new ("activated",
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
G_STRUCT_OFFSET (MetaSelectionSourceClass, activated),
NULL, NULL, NULL,
G_TYPE_NONE, 0);
signals[INACTIVE] =
g_signal_new ("deactivated",
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
G_STRUCT_OFFSET (MetaSelectionSourceClass, deactivated),
NULL, NULL, NULL,
G_TYPE_NONE, 0);
}
static void
meta_selection_source_init (MetaSelectionSource *source)
{
}
void
meta_selection_source_read_async (MetaSelectionSource *source,
const gchar *mimetype,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
g_return_if_fail (META_IS_SELECTION_SOURCE (source));
g_return_if_fail (mimetype != NULL);
g_return_if_fail (callback != NULL);
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)
{
g_return_val_if_fail (META_IS_SELECTION_SOURCE (source), NULL);
g_return_val_if_fail (g_task_is_valid (result, source), NULL);
return META_SELECTION_SOURCE_GET_CLASS (source)->read_finish (source,
result,
error);
}
GList *
meta_selection_source_get_mimetypes (MetaSelectionSource *source)
{
g_return_val_if_fail (META_IS_SELECTION_SOURCE (source), NULL);
return META_SELECTION_SOURCE_GET_CLASS (source)->get_mimetypes (source);
}
gboolean
meta_selection_source_is_active (MetaSelectionSource *source)
{
MetaSelectionSourcePrivate *priv =
meta_selection_source_get_instance_private (source);
g_return_val_if_fail (META_IS_SELECTION_SOURCE (source), FALSE);
return priv->active;
}

View File

@ -0,0 +1,78 @@
/*
* 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 (* activated) (MetaSelectionSource *source);
void (* deactivated) (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_is_active (MetaSelectionSource *source);
#endif /* META_SELECTION_SOURCE_H */

296
src/core/meta-selection.c Normal file
View File

@ -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], "deactivated");
g_set_object (&selection->owners[selection_type], owner);
g_signal_emit_by_name (owner, "activated");
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, "deactivated");
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);
}

62
src/core/meta-selection.h Normal file
View File

@ -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 */

View File

@ -340,6 +340,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',