[Spice-devel] [RFC spice-gtk 2/8] main: support VD_AGENT_SELECTION_* messages
Marc-André Lureau
marcandre.lureau at gmail.com
Tue Jun 5 17:44:27 UTC 2018
Hi
On Thu, May 31, 2018 at 10:52 PM, Jakub Janků <janku.jakub.jj at gmail.com> wrote:
> These enable transferring arbitrary type of data
> unlike the VDAgentClipboard* messages that are
> restricted to types defined in spice protocol
> (and atom2agent[] in spice-gtk-session.c).
>
> This will later be used for clipboard and DND data.
> ---
> doc/reference/spice-gtk-sections.txt | 4 +
> src/channel-main.c | 275 +++++++++++++++++++++++++++
> src/channel-main.h | 9 +
> src/map-file | 4 +
> src/spice-glib-sym-file | 4 +
> src/spice-marshal.txt | 3 +
> 6 files changed, 299 insertions(+)
>
> diff --git a/doc/reference/spice-gtk-sections.txt b/doc/reference/spice-gtk-sections.txt
> index a85df7a..3f64a81 100644
> --- a/doc/reference/spice-gtk-sections.txt
> +++ b/doc/reference/spice-gtk-sections.txt
> @@ -94,6 +94,10 @@ spice_main_file_copy_async
> spice_main_channel_file_copy_async
> spice_main_file_copy_finish
> spice_main_channel_file_copy_finish
> +spice_main_channel_selection_grab
> +spice_main_channel_selection_send_data
> +spice_main_channel_selection_release
> +spice_main_channel_selection_request
> <SUBSECTION Standard>
> SPICE_MAIN_CHANNEL
> SPICE_IS_MAIN_CHANNEL
> diff --git a/src/channel-main.c b/src/channel-main.c
> index 3d682d6..047b74e 100644
> --- a/src/channel-main.c
> +++ b/src/channel-main.c
> @@ -167,6 +167,10 @@ enum {
> SPICE_MAIN_CLIPBOARD_SELECTION_RELEASE,
> SPICE_MIGRATION_STARTED,
> SPICE_MAIN_NEW_FILE_TRANSFER,
> + SPICE_MAIN_SELECTION_GRAB,
> + SPICE_MAIN_SELECTION_DATA,
> + SPICE_MAIN_SELECTION_RELEASE,
> + SPICE_MAIN_SELECTION_REQUEST,
> SPICE_MAIN_LAST_SIGNAL,
> };
>
> @@ -851,6 +855,86 @@ static void spice_main_channel_class_init(SpiceMainChannelClass *klass)
> 1,
> G_TYPE_OBJECT);
>
> + /**
> + * SpiceMainChannel::main-selection-grab:
> + * @main: the #SpiceMainChannel that emitted the signal
> + * @selection: VD_AGENT_CLIPBOARD_SELECTION_*
> + * @targets: advertised MIME types
> + *
> + * Inform that selection data is available from the guest
> + * and in which formats it can be provided.
> + **/
> + signals[SPICE_MAIN_SELECTION_GRAB] =
> + g_signal_new("main-selection-grab",
> + G_OBJECT_CLASS_TYPE(gobject_class),
> + G_SIGNAL_RUN_LAST,
> + 0,
> + NULL, NULL,
> + g_cclosure_user_marshal_VOID__UINT_BOXED,
> + G_TYPE_NONE,
> + 2,
> + G_TYPE_UINT, G_TYPE_STRV);
> +
> + /**
> + * SpiceMainChannel::main-selection-data:
> + * @main: the #SpiceMainChannel that emitted the signal
> + * @selection: VD_AGENT_CLIPBOARD_SELECTION_*
> + * @format: number of bits per unit of @data
> + * @type: MIME type of @data
> + * @data: selection data
> + * @size: size of @data in bytes
> + *
> + * Inform that selection data has been retrieved.
> + **/
> + signals[SPICE_MAIN_SELECTION_DATA] =
> + g_signal_new("main-selection-data",
> + G_OBJECT_CLASS_TYPE(gobject_class),
> + G_SIGNAL_RUN_LAST,
> + 0,
> + NULL, NULL,
> + g_cclosure_user_marshal_VOID__UINT_INT_STRING_POINTER_UINT,
> + G_TYPE_NONE,
> + 5,
> + G_TYPE_UINT, G_TYPE_INT, G_TYPE_STRING, G_TYPE_POINTER, G_TYPE_UINT);
> +
> + /**
> + * SpiceMainChannel::main-selection-release:
> + * @main: the #SpiceMainChannel that emitted the signal
> + * @selection: VD_AGENT_CLIPBOARD_SELECTION_*
> + *
> + * Inform that the selection in the guest has been released
> + * and selection data is not available anymore.
> + **/
> + signals[SPICE_MAIN_SELECTION_RELEASE] =
> + g_signal_new("main-selection-release",
> + G_OBJECT_CLASS_TYPE(gobject_class),
> + G_SIGNAL_RUN_LAST,
> + 0,
> + NULL, NULL,
> + g_cclosure_marshal_VOID__UINT,
> + G_TYPE_NONE,
> + 1,
> + G_TYPE_UINT);
> +
> + /**
> + * SpiceMainChannel::main-selection-request:
> + * @main: the #SpiceMainChannel that emitted the signal
> + * @selection: VD_AGENT_CLIPBOARD_SELECTION_*
> + * @target: requested MIME type of selection data
> + *
> + * Request selection data from the client in @target format.
> + **/
> + signals[SPICE_MAIN_SELECTION_REQUEST] =
> + g_signal_new("main-selection-request",
> + G_OBJECT_CLASS_TYPE(gobject_class),
> + G_SIGNAL_RUN_LAST,
> + 0,
> + NULL, NULL,
> + g_cclosure_user_marshal_VOID__UINT_STRING,
> + G_TYPE_NONE,
> + 2,
> + G_TYPE_UINT, G_TYPE_STRING);
> +
> g_type_class_add_private(klass, sizeof(SpiceMainChannelPrivate));
> channel_set_handlers(SPICE_CHANNEL_CLASS(klass));
> }
> @@ -1339,6 +1423,7 @@ static void agent_announce_caps(SpiceMainChannel *channel)
> VD_AGENT_SET_CAPABILITY(caps->caps, VD_AGENT_CAP_CLIPBOARD_SELECTION);
> VD_AGENT_SET_CAPABILITY(caps->caps, VD_AGENT_CAP_MONITORS_CONFIG_POSITION);
> VD_AGENT_SET_CAPABILITY(caps->caps, VD_AGENT_CAP_FILE_XFER_DETAILED_ERRORS);
> + VD_AGENT_SET_CAPABILITY(caps->caps, VD_AGENT_CAP_SELECTION_DATA);
>
> agent_msg_queue(channel, VD_AGENT_ANNOUNCE_CAPABILITIES, size, caps);
> g_free(caps);
> @@ -2085,6 +2170,76 @@ static void main_agent_handle_msg(SpiceChannel *channel,
> case VD_AGENT_FILE_XFER_STATUS:
> main_agent_handle_xfer_status(self, payload);
> break;
> + case VD_AGENT_SELECTION_GRAB:
> + {
> + VDAgentSelectionGrab *s = payload;
> + GStrv targets;
> + guint i, n, len;
> +
> + len = msg->size - sizeof(VDAgentSelectionGrab);
> + /* make sure data is properly formatted */
> + g_return_if_fail(len >= 2);
> + g_return_if_fail(s->targets[0] != 0);
> + g_return_if_fail(s->targets[len-1] == 0);
> +
> + for (i = 1, n = 0; i < len; i++)
> + if (s->targets[i] == 0) {
> + g_return_if_fail(s->targets[i-1] != 0);
> + n++;
> + }
> +
> + targets = g_new0(gchar *, n + 1);
> + for (i = 0, n = 0; i < len; i++)
> + if (s->targets[i]) {
> + if (targets[n] == NULL)
> + targets[n] = (gchar *)(s->targets + i);
> + } else
> + n++;
> +
> +
It looks like you use the same or similar list of targets format as I
did in my PoC (list of strings seperated by \0).
However, I had utility functions to do the job, with some tests. I
suggest you do the same to clarify and factorize the code.
See:
https://github.com/elmarco/spice-gtk/commit/875df3351080c8766f80d5d3f6c8378ce0d70e22
https://github.com/elmarco/spice-gtk/commit/886d9f4d0a37018dc6d4b4ea21f659fb8718b888
> + s->selection, targets);
> + g_free(targets);
> + break;
> + }
> + case VD_AGENT_SELECTION_DATA:
> + {
> + VDAgentSelectionData *s = payload;
> + guint offset, len;
> +
> + len = msg->size - sizeof(VDAgentSelectionData);
> + for (offset = 0; offset < len; offset++)
> + if (s->data[offset] == 0)
> + break;
> + offset++;
> + g_return_if_fail(offset >= 2 && offset <= len);
> +
> + g_coroutine_signal_emit(self, signals[SPICE_MAIN_SELECTION_DATA], 0,
> + s->selection, s->format, s->data,
> + s->data + offset, len - offset);
> + break;
> + }
> + case VD_AGENT_SELECTION_RELEASE:
> + {
> + VDAgentSelectionRelease *s = payload;
> + g_coroutine_signal_emit(self, signals[SPICE_MAIN_SELECTION_RELEASE], 0,
> + s->selection);
> + break;
> + }
> + case VD_AGENT_SELECTION_REQUEST:
> + {
> + VDAgentSelectionRequest *s = payload;
> + guint i, len;
> +
> + len = msg->size - sizeof(VDAgentSelectionRequest);
> + for (i = 0; i < len; i++)
> + if (s->target[i] == 0)
> + break;
> + g_return_if_fail(i > 0 && i == len - 1);
> +
> + g_coroutine_signal_emit(self, signals[SPICE_MAIN_SELECTION_REQUEST], 0,
> + s->selection, s->target);
> + break;
> + }
> default:
> g_warning("unhandled agent message type: %u (%s), size %u",
> msg->type, NAME(agent_msg_types, msg->type), msg->size);
> @@ -3408,3 +3563,123 @@ gboolean spice_main_channel_file_copy_finish(SpiceMainChannel *channel,
>
> return g_task_propagate_boolean(task, error);
> }
> +
> +static gboolean main_selection_params_valid(SpiceMainChannel *channel,
> + guint selection)
> +{
> + g_return_val_if_fail(channel != NULL, FALSE);
> + g_return_val_if_fail(SPICE_IS_MAIN_CHANNEL(channel), FALSE);
> + g_return_val_if_fail(test_agent_cap(channel, VD_AGENT_CAP_SELECTION_DATA), FALSE);
> + g_return_val_if_fail(selection <= VD_AGENT_CLIPBOARD_SELECTION_SECONDARY, FALSE);
> +
> + return channel->priv->agent_connected;
> +}
> +
> +/**
> + * spice_main_channel_selection_grab:
> + * @channel: a #SpiceMainChannel
> + * @selection: #VD_AGENT_CLIPBOARD_SELECTION_*
> + * @targets: NULL-terminated array of MIME types to advertise
> + *
> + * Grab the guest selection.
> + **/
> +void spice_main_channel_selection_grab(SpiceMainChannel *channel,
> + guint selection,
> + const gchar **targets)
> +{
> + if (!main_selection_params_valid(channel, selection))
> + return;
> +
> + VDAgentSelectionGrab *msg;
> + guint i, size;
> + gchar *ptr;
> +
> + size = sizeof(VDAgentSelectionGrab);
> + for (i = 0; targets[i]; i++)
> + size += strlen(targets[i]) + 1;
> +
> + msg = g_malloc(size);
> + msg->selection = selection;
> + for (i = 0, ptr = (gchar *)msg->targets; targets[i]; i++)
> + ptr = g_stpcpy(ptr, targets[i]) + 1;
> +
> + agent_msg_queue(channel, VD_AGENT_SELECTION_GRAB, size, msg);
> + g_free(msg);
> + spice_channel_wakeup(SPICE_CHANNEL(channel), FALSE);
> +}
> +
> +/**
> + * spice_main_channel_selection_send_data:
> + * @channel: a #SpiceMainChannel
> + * @selection: #VD_AGENT_CLIPBOARD_SELECTION_*
> + * @format: number of bits per unit of @data
> + * @type: MIME type of @data
> + * @data: selection data
> + * @size: length of @data in bytes
> + *
> + * Send the selection data to the guest.
> + **/
> +void spice_main_channel_selection_send_data(SpiceMainChannel *channel,
> + guint selection,
> + gint format,
> + const gchar *type,
> + const guchar *data,
> + guint size)
> +{
> + if (!main_selection_params_valid(channel, selection))
> + return;
> +
> + VDAgentSelectionData msg;
> + msg.selection = selection;
> + msg.format = format;
> + agent_msg_queue_many(channel, VD_AGENT_SELECTION_DATA,
> + &msg, sizeof(msg),
> + type, strlen(type) + 1,
> + data, size, NULL);
> + spice_channel_wakeup(SPICE_CHANNEL(channel), FALSE);
> +}
> +
> +/**
> + * spice_main_channel_selection_release:
> + * @channel: a #SpiceMainChannel
> + * @selection: #VD_AGENT_CLIPBOARD_SELECTION_*
> + *
> + * Release grab of the selection,
> + * inform the guest data is not available anymore.
> + **/
> +void spice_main_channel_selection_release(SpiceMainChannel *channel,
> + guint selection)
> +{
> + if (!main_selection_params_valid(channel, selection))
> + return;
> +
> + VDAgentSelectionRelease msg;
> + msg.selection = selection;
> + agent_msg_queue(channel, VD_AGENT_SELECTION_RELEASE, sizeof(msg), &msg);
> + spice_channel_wakeup(SPICE_CHANNEL(channel), FALSE);
> +}
> +
> +/**
> + * spice_main_channel_selection_request:
> + * @channel: a #SpiceMainChannel
> + * @selection: #VD_AGENT_CLIPBOARD_SELECTION_*
> + * @target: MIME type of selection data to retrieve
> + *
> + * Request selection data from the guest in @target type.
> + * The reply is sent through the #SpiceMainChannel::main-selection-data signal.
> + **/
> +gboolean spice_main_channel_selection_request(SpiceMainChannel *channel,
> + guint selection,
> + const gchar *target)
> +{
> + if (!main_selection_params_valid(channel, selection))
> + return FALSE;
> +
> + VDAgentSelectionRequest msg;
> + msg.selection = selection;
> + agent_msg_queue_many(channel, VD_AGENT_SELECTION_REQUEST,
> + &msg, sizeof(msg),
> + target, strlen(target) + 1, NULL);
> + spice_channel_wakeup(SPICE_CHANNEL(channel), FALSE);
> + return TRUE;
> +}
> diff --git a/src/channel-main.h b/src/channel-main.h
> index 530378d..1c915ad 100644
> --- a/src/channel-main.h
> +++ b/src/channel-main.h
> @@ -85,6 +85,15 @@ void spice_main_channel_clipboard_selection_notify(SpiceMainChannel *channel, gu
> void spice_main_channel_clipboard_selection_request(SpiceMainChannel *channel, guint selection,
> guint32 type);
>
> +void spice_main_channel_selection_grab(SpiceMainChannel *channel, guint selection,
> + const gchar **targets);
> +void spice_main_channel_selection_send_data(SpiceMainChannel *channel, guint selection,
> + gint format, const gchar *type,
> + const guchar *data, guint size);
> +void spice_main_channel_selection_release(SpiceMainChannel *channel, guint selection);
> +gboolean spice_main_channel_selection_request(SpiceMainChannel *channel, guint selection,
> + const gchar *target);
> +
> gboolean spice_main_channel_agent_test_capability(SpiceMainChannel *channel, guint32 cap);
> void spice_main_channel_file_copy_async(SpiceMainChannel *channel,
> GFile **sources,
> diff --git a/src/map-file b/src/map-file
> index cdb81c3..4b20454 100644
> --- a/src/map-file
> +++ b/src/map-file
> @@ -108,6 +108,10 @@ spice_main_set_display;
> spice_main_set_display_enabled;
> spice_main_update_display;
> spice_main_update_display_enabled;
> +spice_main_channel_selection_grab;
> +spice_main_channel_selection_send_data;
> +spice_main_channel_selection_release;
> +spice_main_channel_selection_request;
> spice_playback_channel_get_type;
> spice_playback_channel_set_delay;
> spice_port_channel_event;
> diff --git a/src/spice-glib-sym-file b/src/spice-glib-sym-file
> index b19844c..2cb7738 100644
> --- a/src/spice-glib-sym-file
> +++ b/src/spice-glib-sym-file
> @@ -87,6 +87,10 @@ spice_main_set_display
> spice_main_set_display_enabled
> spice_main_update_display
> spice_main_update_display_enabled
> +spice_main_channel_selection_grab
> +spice_main_channel_selection_send_data
> +spice_main_channel_selection_release
> +spice_main_channel_selection_request
> spice_playback_channel_get_type
> spice_playback_channel_set_delay
> spice_port_channel_event
> diff --git a/src/spice-marshal.txt b/src/spice-marshal.txt
> index b3a8e69..18b8d4c 100644
> --- a/src/spice-marshal.txt
> +++ b/src/spice-marshal.txt
> @@ -14,3 +14,6 @@ BOOLEAN:UINT,UINT
> VOID:OBJECT,OBJECT
> VOID:BOXED,BOXED
> POINTER:BOOLEAN
> +VOID:UINT,BOXED
> +VOID:UINT,STRING
> +VOID:UINT,INT,STRING,POINTER,UINT
> --
> 2.17.0
>
> _______________________________________________
> Spice-devel mailing list
> Spice-devel at lists.freedesktop.org
> https://lists.freedesktop.org/mailman/listinfo/spice-devel
--
Marc-André Lureau
More information about the Spice-devel
mailing list