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++; + + g_coroutine_signal_emit(channel, signals[SPICE_MAIN_SELECTION_GRAB], 0, + 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@xxxxxxxxxxxxxxxxxxxxx https://lists.freedesktop.org/mailman/listinfo/spice-devel