summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJonas Ã…dahl <jadahl@gmail.com>2020-11-04 16:09:42 +0100
committerMarge Bot <marge-bot@gnome.org>2021-02-05 16:44:27 +0000
commit5104a9b2ceea8635178639e719d5c9e0243c25ce (patch)
tree78c105e4308a97488f8de58cdd9d72c362437e5e
parentd7c8535ac6ade82d99830489c3a0ba2c64dade49 (diff)
downloadmutter-5104a9b2ceea8635178639e719d5c9e0243c25ce.tar.gz
remote-desktop/session: Add support for SelectionTransfer/Write
When a transfer request is done to the MetaSelectionSourceRemote source, it's translated to a SelectionTransfer signal, which the remote desktop server is supposed to respond to with SelectionWrite. A timeout (set to 15 seconds) is added to handle too long timeouts, which cancels the transfer request. Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1552>
-rw-r--r--clutter/clutter/clutter-private.h6
-rw-r--r--src/backends/meta-remote-desktop-session.c313
-rw-r--r--src/backends/meta-remote-desktop-session.h4
-rw-r--r--src/core/meta-selection-source-remote.c33
-rw-r--r--src/core/meta-selection-source-remote.h7
5 files changed, 340 insertions, 23 deletions
diff --git a/clutter/clutter/clutter-private.h b/clutter/clutter/clutter-private.h
index e1c0d8388..f596ded9c 100644
--- a/clutter/clutter/clutter-private.h
+++ b/clutter/clutter/clutter-private.h
@@ -312,6 +312,12 @@ s2us (int64_t s)
return ms2us (s * 1000);
}
+static inline int64_t
+s2ms (int64_t s)
+{
+ return (int64_t) ms (s * 1000);
+}
+
G_END_DECLS
#endif /* __CLUTTER_PRIVATE_H__ */
diff --git a/src/backends/meta-remote-desktop-session.c b/src/backends/meta-remote-desktop-session.c
index fcdf8d9fa..ba6e84ae3 100644
--- a/src/backends/meta-remote-desktop-session.c
+++ b/src/backends/meta-remote-desktop-session.c
@@ -40,12 +40,15 @@
#include "cogl/cogl.h"
#include "core/display-private.h"
#include "core/meta-selection-private.h"
+#include "core/meta-selection-source-remote.h"
#include "meta/meta-backend.h"
#include "meta-dbus-remote-desktop.h"
#define META_REMOTE_DESKTOP_SESSION_DBUS_PATH "/org/gnome/Mutter/RemoteDesktop/Session"
+#define TRANSFER_REQUEST_CLEANUP_TIMEOUT_MS (s2ms (15))
+
typedef enum _MetaRemoteDesktopNotifyAxisFlags
{
META_REMOTE_DESKTOP_NOTIFY_AXIS_FLAGS_NONE = 0,
@@ -86,6 +89,9 @@ struct _MetaRemoteDesktopSession
gulong owner_changed_handler_id;
SelectionReadData *read_data;
unsigned int transfer_serial;
+ MetaSelectionSourceRemote *current_source;
+ GHashTable *transfer_requests;
+ guint transfer_request_timeout_id;
};
static void
@@ -810,6 +816,31 @@ handle_notify_touch_up (MetaDBusRemoteDesktopSession *skeleton,
return TRUE;
}
+static MetaSelectionSourceRemote *
+create_remote_desktop_source (MetaRemoteDesktopSession *session,
+ GVariant *mime_types_variant,
+ GError **error)
+{
+ GVariantIter iter;
+ char *mime_type;
+ GList *mime_types = NULL;
+
+ g_variant_iter_init (&iter, mime_types_variant);
+ if (g_variant_iter_n_children (&iter) == 0)
+ {
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_DATA,
+ "No mime types in mime types list");
+ return NULL;
+ }
+
+ while (g_variant_iter_next (&iter, "s", &mime_type))
+ mime_types = g_list_prepend (mime_types, mime_type);
+
+ mime_types = g_list_reverse (mime_types);
+
+ return meta_selection_source_remote_new (session, mime_types);
+}
+
static const char *
mime_types_to_string (char **formats,
char *buf,
@@ -831,8 +862,16 @@ mime_types_to_string (char **formats,
return buf;
}
+static gboolean
+is_own_source (MetaRemoteDesktopSession *session,
+ MetaSelectionSource *source)
+{
+ return source && source == META_SELECTION_SOURCE (session->current_source);
+}
+
static GVariant *
-generate_owner_changed_variant (char **mime_types_array)
+generate_owner_changed_variant (char **mime_types_array,
+ gboolean is_own_source)
{
GVariantBuilder builder;
@@ -841,6 +880,8 @@ generate_owner_changed_variant (char **mime_types_array)
{
g_variant_builder_add (&builder, "{sv}", "mime-types",
g_variant_new ("(^as)", mime_types_array));
+ g_variant_builder_add (&builder, "{sv}", "session-is-owner",
+ g_variant_new_boolean (is_own_source));
}
return g_variant_builder_end (&builder);
@@ -875,16 +916,19 @@ on_selection_owner_changed (MetaSelection *selection,
}
meta_topic (META_DEBUG_REMOTE_DESKTOP,
- "Clipboard owner changed, owner: %p (%s), mime types: [%s], "
+ "Clipboard owner changed, owner: %p (%s, is own? %s), mime types: [%s], "
"notifying %s",
owner,
owner ? g_type_name_from_instance ((GTypeInstance *) owner)
: "NULL",
+ is_own_source (session, owner) ? "yes" : "no",
mime_types_to_string (mime_types_array, log_buf,
G_N_ELEMENTS (log_buf)),
session->peer_name);
- options_variant = generate_owner_changed_variant (mime_types_array);
+ options_variant =
+ generate_owner_changed_variant (mime_types_array,
+ is_own_source (session, owner));
object_path = g_dbus_interface_skeleton_get_object_path (
G_DBUS_INTERFACE_SKELETON (session));
@@ -895,8 +939,6 @@ on_selection_owner_changed (MetaSelection *selection,
"SelectionOwnerChanged",
g_variant_new ("(@a{sv})", options_variant),
NULL);
-
- session->transfer_serial++;
}
static gboolean
@@ -905,6 +947,8 @@ handle_enable_clipboard (MetaDBusRemoteDesktopSession *skeleton,
GVariant *arg_options)
{
MetaRemoteDesktopSession *session = META_REMOTE_DESKTOP_SESSION (skeleton);
+ GVariant *mime_types_variant;
+ g_autoptr (GError) error = NULL;
MetaDisplay *display = meta_get_display ();
MetaSelection *selection = meta_display_get_selection (display);
@@ -920,6 +964,35 @@ handle_enable_clipboard (MetaDBusRemoteDesktopSession *skeleton,
return TRUE;
}
+ mime_types_variant = g_variant_lookup_value (arg_options,
+ "mime-types",
+ G_VARIANT_TYPE_STRING_ARRAY);
+ if (mime_types_variant)
+ {
+ g_autoptr (MetaSelectionSourceRemote) source_remote = NULL;
+
+ source_remote = create_remote_desktop_source (session,
+ mime_types_variant,
+ &error);
+ if (!source_remote)
+ {
+ g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR,
+ G_DBUS_ERROR_FAILED,
+ "Invalid mime type list: %s",
+ error->message);
+ return TRUE;
+ }
+
+ meta_topic (META_DEBUG_REMOTE_DESKTOP,
+ "Setting remote desktop clipboard source: %p from %s",
+ source_remote, session->peer_name);
+
+ g_set_object (&session->current_source, source_remote);
+ meta_selection_set_owner (selection,
+ META_SELECTION_CLIPBOARD,
+ META_SELECTION_SOURCE (source_remote));
+ }
+
session->is_clipboard_enabled = TRUE;
session->owner_changed_handler_id =
g_signal_connect (selection, "owner-changed",
@@ -932,6 +1005,64 @@ handle_enable_clipboard (MetaDBusRemoteDesktopSession *skeleton,
return TRUE;
}
+static gboolean
+cancel_transfer_request (gpointer key,
+ gpointer value,
+ gpointer user_data)
+{
+ GTask *task = G_TASK (value);
+ MetaRemoteDesktopSession *session = user_data;
+
+ meta_selection_source_remote_cancel_transfer (session->current_source,
+ task);
+
+ return TRUE;
+}
+
+static void
+meta_remote_desktop_session_cancel_transfer_requests (MetaRemoteDesktopSession *session)
+{
+ g_return_if_fail (session->current_source);
+
+ g_hash_table_foreach_remove (session->transfer_requests,
+ cancel_transfer_request,
+ session);
+}
+
+static gboolean
+transfer_request_cleanup_timout (gpointer user_data)
+{
+ MetaRemoteDesktopSession *session = user_data;
+
+ meta_topic (META_DEBUG_REMOTE_DESKTOP,
+ "Cancel unanswered SelectionTransfer requests for %s, "
+ "waited for %.02f seconds already",
+ session->peer_name,
+ TRANSFER_REQUEST_CLEANUP_TIMEOUT_MS / 1000.0);
+
+ meta_remote_desktop_session_cancel_transfer_requests (session);
+
+ session->transfer_request_timeout_id = 0;
+ return G_SOURCE_REMOVE;
+}
+
+static void
+reset_current_selection_source (MetaRemoteDesktopSession *session)
+{
+ MetaDisplay *display = meta_get_display ();
+ MetaSelection *selection = meta_display_get_selection (display);
+
+ if (!session->current_source)
+ return;
+
+ meta_selection_unset_owner (selection,
+ META_SELECTION_CLIPBOARD,
+ META_SELECTION_SOURCE (session->current_source));
+ meta_remote_desktop_session_cancel_transfer_requests (session);
+ g_clear_handle_id (&session->transfer_request_timeout_id, g_source_remove);
+ g_clear_object (&session->current_source);
+}
+
static void
cancel_selection_read (MetaRemoteDesktopSession *session)
{
@@ -964,6 +1095,7 @@ handle_disable_clipboard (MetaDBusRemoteDesktopSession *skeleton,
}
g_clear_signal_handler (&session->owner_changed_handler_id, selection);
+ reset_current_selection_source (session);
cancel_selection_read (session);
meta_dbus_remote_desktop_session_complete_disable_clipboard (skeleton,
@@ -978,10 +1110,8 @@ handle_set_selection (MetaDBusRemoteDesktopSession *skeleton,
GVariant *arg_options)
{
MetaRemoteDesktopSession *session = META_REMOTE_DESKTOP_SESSION (skeleton);
-
- meta_topic (META_DEBUG_REMOTE_DESKTOP,
- "Set selection for %s",
- g_dbus_method_invocation_get_sender (invocation));
+ g_autoptr (GVariant) mime_types_variant = NULL;
+ g_autoptr (GError) error = NULL;
if (!session->is_clipboard_enabled)
{
@@ -991,19 +1121,114 @@ handle_set_selection (MetaDBusRemoteDesktopSession *skeleton,
return TRUE;
}
+ if (session->current_source)
+ {
+ meta_remote_desktop_session_cancel_transfer_requests (session);
+ g_clear_handle_id (&session->transfer_request_timeout_id,
+ g_source_remove);
+ }
+
+ mime_types_variant = g_variant_lookup_value (arg_options,
+ "mime-types",
+ G_VARIANT_TYPE_STRING_ARRAY);
+ if (mime_types_variant)
+ {
+ g_autoptr (MetaSelectionSourceRemote) source_remote = NULL;
+ MetaDisplay *display = meta_get_display ();
+
+ source_remote = create_remote_desktop_source (session,
+ mime_types_variant,
+ &error);
+ if (!source_remote)
+ {
+ g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR,
+ G_DBUS_ERROR_FAILED,
+ "Invalid format list: %s",
+ error->message);
+ return TRUE;
+ }
+
+ meta_topic (META_DEBUG_REMOTE_DESKTOP,
+ "Set selection for %s to %p",
+ g_dbus_method_invocation_get_sender (invocation),
+ source_remote);
+
+ g_set_object (&session->current_source, source_remote);
+ meta_selection_set_owner (meta_display_get_selection (display),
+ META_SELECTION_CLIPBOARD,
+ META_SELECTION_SOURCE (source_remote));
+ }
+ else
+ {
+ meta_topic (META_DEBUG_REMOTE_DESKTOP,
+ "Unset selection for %s",
+ g_dbus_method_invocation_get_sender (invocation));
+
+ reset_current_selection_source (session);
+ }
+
meta_dbus_remote_desktop_session_complete_set_selection (skeleton,
invocation);
return TRUE;
}
+static void
+reset_transfer_cleanup_timeout (MetaRemoteDesktopSession *session)
+{
+ g_clear_handle_id (&session->transfer_request_timeout_id, g_source_remove);
+ session->transfer_request_timeout_id =
+ g_timeout_add (TRANSFER_REQUEST_CLEANUP_TIMEOUT_MS,
+ transfer_request_cleanup_timout,
+ session);
+}
+
+void
+meta_remote_desktop_session_request_transfer (MetaRemoteDesktopSession *session,
+ const char *mime_type,
+ GTask *task)
+{
+ const char *object_path;
+
+ session->transfer_serial++;
+
+ meta_topic (META_DEBUG_REMOTE_DESKTOP,
+ "Emit SelectionTransfer ('%s', %u) for %s",
+ mime_type,
+ session->transfer_serial,
+ session->peer_name);
+
+ g_hash_table_insert (session->transfer_requests,
+ GUINT_TO_POINTER (session->transfer_serial),
+ task);
+ reset_transfer_cleanup_timeout (session);
+
+ object_path = g_dbus_interface_skeleton_get_object_path (
+ G_DBUS_INTERFACE_SKELETON (session));
+ g_dbus_connection_emit_signal (session->connection,
+ NULL,
+ object_path,
+ "org.gnome.Mutter.RemoteDesktop.Session",
+ "SelectionTransfer",
+ g_variant_new ("(su)",
+ mime_type,
+ session->transfer_serial),
+ NULL);
+}
+
static gboolean
handle_selection_write (MetaDBusRemoteDesktopSession *skeleton,
GDBusMethodInvocation *invocation,
- GUnixFDList *fd_list,
+ GUnixFDList *fd_list_in,
unsigned int serial)
{
MetaRemoteDesktopSession *session = META_REMOTE_DESKTOP_SESSION (skeleton);
+ g_autoptr (GError) error = NULL;
+ int pipe_fds[2];
+ g_autoptr (GUnixFDList) fd_list = NULL;
+ int fd_idx;
+ GVariant *fd_variant;
+ GTask *task;
meta_topic (META_DEBUG_REMOTE_DESKTOP,
"Write selection for %s",
@@ -1017,22 +1242,62 @@ handle_selection_write (MetaDBusRemoteDesktopSession *skeleton,
return TRUE;
}
- if (session->transfer_serial != serial)
+ if (!session->current_source)
+ {
+ g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR,
+ G_DBUS_ERROR_FAILED,
+ "No current selection owned");
+ return TRUE;
+ }
+
+ if (!g_hash_table_steal_extended (session->transfer_requests,
+ GUINT_TO_POINTER (serial),
+ NULL,
+ (gpointer *) &task))
{
g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR,
G_DBUS_ERROR_FAILED,
- "Provided transfer serial %u "
- "doesn't match current current "
- "transfer serial %u",
- serial,
- session->transfer_serial);
+ "Transfer serial %u doesn't match "
+ "any transfer request",
+ serial);
return TRUE;
}
+ if (!g_unix_open_pipe (pipe_fds, FD_CLOEXEC, &error))
+ {
+ g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR,
+ G_DBUS_ERROR_FAILED,
+ "Failed open pipe: %s",
+ error->message);
+ return TRUE;
+ }
+
+ if (!g_unix_set_fd_nonblocking (pipe_fds[0], TRUE, &error))
+ {
+ close (pipe_fds[0]);
+ close (pipe_fds[1]);
+
+ g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR,
+ G_DBUS_ERROR_FAILED,
+ "Failed to make pipe non-blocking: %s",
+ error->message);
+ return TRUE;
+ }
+
+ fd_list = g_unix_fd_list_new ();
+
+ fd_idx = g_unix_fd_list_append (fd_list, pipe_fds[1], NULL);
+ close (pipe_fds[1]);
+ fd_variant = g_variant_new_handle (fd_idx);
+
+ meta_selection_source_remote_complete_transfer (session->current_source,
+ pipe_fds[0],
+ task);
+
meta_dbus_remote_desktop_session_complete_selection_write (skeleton,
invocation,
- NULL,
- NULL);
+ fd_list,
+ fd_variant);
return TRUE;
}
@@ -1132,6 +1397,14 @@ handle_selection_read (MetaDBusRemoteDesktopSession *skeleton,
return TRUE;
}
+ if (is_own_source (session, source))
+ {
+ g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR,
+ G_DBUS_ERROR_FAILED,
+ "Tried to read own selection");
+ return TRUE;
+ }
+
if (session->read_data)
{
g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR,
@@ -1233,7 +1506,9 @@ meta_remote_desktop_session_finalize (GObject *object)
g_assert (!meta_remote_desktop_session_is_running (session));
g_clear_signal_handler (&session->owner_changed_handler_id, selection);
+ reset_current_selection_source (session);
cancel_selection_read (session);
+ g_hash_table_unref (session->transfer_requests);
g_clear_object (&session->handle);
g_free (session->peer_name);
@@ -1260,6 +1535,8 @@ meta_remote_desktop_session_init (MetaRemoteDesktopSession *session)
session->object_path =
g_strdup_printf (META_REMOTE_DESKTOP_SESSION_DBUS_PATH "/u%u",
++global_session_number);
+
+ session->transfer_requests = g_hash_table_new (NULL, NULL);
}
static void
diff --git a/src/backends/meta-remote-desktop-session.h b/src/backends/meta-remote-desktop-session.h
index 1edb3739b..7af9c4897 100644
--- a/src/backends/meta-remote-desktop-session.h
+++ b/src/backends/meta-remote-desktop-session.h
@@ -47,6 +47,10 @@ gboolean meta_remote_desktop_session_register_screen_cast (MetaRemoteDesktopSess
MetaScreenCastSession *screen_cast_session,
GError **error);
+void meta_remote_desktop_session_request_transfer (MetaRemoteDesktopSession *session,
+ const char *mime_type,
+ GTask *task);
+
void meta_remote_desktop_session_close (MetaRemoteDesktopSession *session);
MetaRemoteDesktopSession * meta_remote_desktop_session_new (MetaRemoteDesktop *remote_desktop,
diff --git a/src/core/meta-selection-source-remote.c b/src/core/meta-selection-source-remote.c
index 828c8c8c8..aa9f9e60a 100644
--- a/src/core/meta-selection-source-remote.c
+++ b/src/core/meta-selection-source-remote.c
@@ -22,6 +22,8 @@
#include "core/meta-selection-source-remote.h"
+#include <gio/gunixinputstream.h>
+
#include "backends/meta-remote-desktop-session.h"
struct _MetaSelectionSourceRemote
@@ -56,16 +58,16 @@ meta_selection_source_remote_read_async (MetaSelectionSource *source,
GAsyncReadyCallback callback,
gpointer user_data)
{
+ MetaSelectionSourceRemote *source_remote =
+ META_SELECTION_SOURCE_REMOTE (source);
GTask *task;
- GInputStream *stream;
task = g_task_new (source, cancellable, callback, user_data);
g_task_set_source_tag (task, meta_selection_source_remote_read_async);
- stream = g_memory_input_stream_new_from_data ("place holder text", -1, NULL);
- g_task_return_pointer (task, stream, g_object_unref);
-
- g_object_unref (task);
+ meta_remote_desktop_session_request_transfer (source_remote->session,
+ mimetype,
+ task);
}
static GInputStream *
@@ -80,6 +82,27 @@ meta_selection_source_remote_read_finish (MetaSelectionSource *source,
return g_task_propagate_pointer (G_TASK (result), error);
}
+void
+meta_selection_source_remote_complete_transfer (MetaSelectionSourceRemote *source_remote,
+ int fd,
+ GTask *task)
+{
+ GInputStream *stream;
+
+ stream = g_unix_input_stream_new (fd, TRUE);
+ g_task_return_pointer (task, stream, g_object_unref);
+ g_object_unref (task);
+}
+
+void
+meta_selection_source_remote_cancel_transfer (MetaSelectionSourceRemote *source_remote,
+ GTask *task)
+{
+ g_task_return_new_error (task, G_IO_ERROR, G_IO_ERROR_CANCELLED,
+ "Remote selection transfer was cancelled");
+ g_object_unref (task);
+}
+
static GList *
meta_selection_source_remote_get_mimetypes (MetaSelectionSource *source)
{
diff --git a/src/core/meta-selection-source-remote.h b/src/core/meta-selection-source-remote.h
index 28a112758..51e66957b 100644
--- a/src/core/meta-selection-source-remote.h
+++ b/src/core/meta-selection-source-remote.h
@@ -30,6 +30,13 @@ G_DECLARE_FINAL_TYPE (MetaSelectionSourceRemote,
META, SELECTION_SOURCE_REMOTE,
MetaSelectionSource)
+void meta_selection_source_remote_complete_transfer (MetaSelectionSourceRemote *source_remote,
+ int fd,
+ GTask *task);
+
+void meta_selection_source_remote_cancel_transfer (MetaSelectionSourceRemote *source_remote,
+ GTask *task);
+
MetaSelectionSourceRemote * meta_selection_source_remote_new (MetaRemoteDesktopSession *session,
GList *mime_types);