diff options
author | Marc-André Lureau <marcandre.lureau@redhat.com> | 2011-02-18 19:42:04 +0100 |
---|---|---|
committer | Marc-André Lureau <marcandre.lureau@redhat.com> | 2011-03-01 18:49:38 +0100 |
commit | 7198a37b4dcf66411df176a7c7a340262745a921 (patch) | |
tree | 8634efbbe9307a079ee5cbd2e65edeb46033bc77 | |
parent | cc9213aad878f0c40af040af0514cf38c6009bd2 (diff) |
gtk: add multiple selection clipboard sharing
-rw-r--r-- | gtk/Makefile.am | 4 | ||||
-rw-r--r-- | gtk/channel-main.c | 358 | ||||
-rw-r--r-- | gtk/channel-main.h | 5 | ||||
-rw-r--r-- | gtk/map-file | 4 | ||||
-rw-r--r-- | gtk/spice-client-gtk.defs | 42 | ||||
-rw-r--r-- | gtk/spice-marshal.txt | 3 | ||||
-rw-r--r-- | gtk/spice-widget-priv.h | 1 | ||||
-rw-r--r-- | gtk/spice-widget.c | 74 |
8 files changed, 449 insertions, 42 deletions
diff --git a/gtk/Makefile.am b/gtk/Makefile.am index bf90621..7ce8a65 100644 --- a/gtk/Makefile.am +++ b/gtk/Makefile.am @@ -68,7 +68,7 @@ AM_CPPFLAGS = \ # http://www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html SPICE_GTK_LDFLAGS_COMMON = \ - -version-number 1:0:0 \ + -version-number 1:1:0 \ -no-undefined \ $(VERSION_LDFLAGS) \ $(NULL) @@ -120,7 +120,7 @@ libspice_client_gtkinclude_HEADERS = \ $(NULL) libspice_client_glib_2_0_la_LDFLAGS = \ - -version-number 2:0:1 \ + -version-number 3:0:2 \ -no-undefined \ $(VERSION_LDFLAGS) \ $(NULL) diff --git a/gtk/channel-main.c b/gtk/channel-main.c index 6121f20..ff784b9 100644 --- a/gtk/channel-main.c +++ b/gtk/channel-main.c @@ -106,6 +106,10 @@ enum { SPICE_MAIN_CLIPBOARD_GRAB, SPICE_MAIN_CLIPBOARD_REQUEST, SPICE_MAIN_CLIPBOARD_RELEASE, + SPICE_MAIN_CLIPBOARD_SELECTION, + SPICE_MAIN_CLIPBOARD_SELECTION_GRAB, + SPICE_MAIN_CLIPBOARD_SELECTION_REQUEST, + SPICE_MAIN_CLIPBOARD_SELECTION_RELEASE, SPICE_MIGRATION_STARTED, SPICE_MAIN_LAST_SIGNAL, }; @@ -379,7 +383,7 @@ static void spice_main_channel_class_init(SpiceMainChannelClass *klass) /* TODO use notify instead */ /** - * SpiceMainChannel::main-clipboard: + * SpiceMainChannel::main-agent-update: * @main: the #SpiceMainChannel that emitted the signal * * Notify when the %SpiceMainChannel:agent-connected or @@ -402,6 +406,8 @@ static void spice_main_channel_class_init(SpiceMainChannelClass *klass) * @size: size of @data in bytes * * Provides guest clipboard data requested by spice_main_clipboard_request(). + * + * Deprecated: 0.6: use SpiceMainChannel::main-clipboard-selection instead. **/ signals[SPICE_MAIN_CLIPBOARD] = g_signal_new("main-clipboard", @@ -415,6 +421,23 @@ static void spice_main_channel_class_init(SpiceMainChannelClass *klass) G_TYPE_UINT, G_TYPE_POINTER, G_TYPE_UINT); /** + * SpiceMainChannel::main-clipboard-selection: + * @main: the #SpiceMainChannel that emitted the signal + * + * Since: 0.6 + **/ + signals[SPICE_MAIN_CLIPBOARD_SELECTION] = + g_signal_new("main-clipboard-selection", + G_OBJECT_CLASS_TYPE(gobject_class), + G_SIGNAL_RUN_LAST, + 0, + NULL, NULL, + g_cclosure_user_marshal_VOID__UINT_UINT_POINTER_UINT, + G_TYPE_NONE, + 4, + G_TYPE_UINT, G_TYPE_UINT, G_TYPE_POINTER, G_TYPE_UINT); + + /** * SpiceMainChannel::main-clipboard-grab: * @main: the #SpiceMainChannel that emitted the signal * @types: the VD_AGENT_CLIPBOARD data types @@ -422,6 +445,8 @@ static void spice_main_channel_class_init(SpiceMainChannelClass *klass) * * Inform when clipboard data is available from the guest, and for * which @types. + * + * Deprecated: 0.6: use SpiceMainChannel::main-clipboard-selection-grab instead. **/ signals[SPICE_MAIN_CLIPBOARD_GRAB] = g_signal_new("main-clipboard-grab", @@ -435,6 +460,28 @@ static void spice_main_channel_class_init(SpiceMainChannelClass *klass) G_TYPE_POINTER, G_TYPE_UINT); /** + * SpiceMainChannel::main-clipboard-selection-grab: + * @main: the #SpiceMainChannel that emitted the signal + * @types: the VD_AGENT_CLIPBOARD data types + * @ntypes: the number of @types + * + * Inform when clipboard data is available from the guest, and for + * which @types. + * + * Since: 0.6 + **/ + signals[SPICE_MAIN_CLIPBOARD_SELECTION_GRAB] = + g_signal_new("main-clipboard-selection-grab", + G_OBJECT_CLASS_TYPE(gobject_class), + G_SIGNAL_RUN_LAST, + 0, + NULL, NULL, + g_cclosure_user_marshal_BOOLEAN__UINT_POINTER_UINT, + G_TYPE_BOOLEAN, + 3, + G_TYPE_UINT, G_TYPE_POINTER, G_TYPE_UINT); + + /** * SpiceMainChannel::main-clipboard-request: * @main: the #SpiceMainChannel that emitted the signal * @types: the VD_AGENT_CLIPBOARD request type @@ -442,6 +489,7 @@ static void spice_main_channel_class_init(SpiceMainChannelClass *klass) * * Request clipbard data from the client. * + * Deprecated: 0.6: use SpiceMainChannel::main-clipboard-selection-request instead. **/ signals[SPICE_MAIN_CLIPBOARD_REQUEST] = g_signal_new("main-clipboard-request", @@ -455,12 +503,34 @@ static void spice_main_channel_class_init(SpiceMainChannelClass *klass) G_TYPE_UINT); /** + * SpiceMainChannel::main-clipboard-selection-request: + * @main: the #SpiceMainChannel that emitted the signal + * @types: the VD_AGENT_CLIPBOARD request type + * Returns: %TRUE if the request is successful + * + * Request clipbard data from the client. + * + * Since: 0.6 + **/ + signals[SPICE_MAIN_CLIPBOARD_SELECTION_REQUEST] = + g_signal_new("main-clipboard-selection-request", + G_OBJECT_CLASS_TYPE(gobject_class), + G_SIGNAL_RUN_LAST, + 0, + NULL, NULL, + g_cclosure_user_marshal_BOOLEAN__UINT_UINT, + G_TYPE_BOOLEAN, + 2, + G_TYPE_UINT, G_TYPE_UINT); + + /** * SpiceMainChannel::main-clipboard-release: * @main: the #SpiceMainChannel that emitted the signal * * Inform when the clipboard is released from the guest, when no * clipboard data is available from the guest. * + * Deprecated: 0.6: use SpiceMainChannel::main-clipboard-selection-release instead. **/ signals[SPICE_MAIN_CLIPBOARD_RELEASE] = g_signal_new("main-clipboard-release", @@ -473,6 +543,26 @@ static void spice_main_channel_class_init(SpiceMainChannelClass *klass) 0); /** + * SpiceMainChannel::main-clipboard-selection-release: + * @main: the #SpiceMainChannel that emitted the signal + * + * Inform when the clipboard is released from the guest, when no + * clipboard data is available from the guest. + * + * Since: 0.6 + **/ + signals[SPICE_MAIN_CLIPBOARD_SELECTION_RELEASE] = + g_signal_new("main-clipboard-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::migration-started: * @main: the #SpiceMainChannel that emitted the signal * @session: a migration #SpiceSession @@ -526,6 +616,30 @@ struct SPICE_MAIN_CLIPBOARD_REQUEST { gboolean *ret; }; +struct SPICE_MAIN_CLIPBOARD_SELECTION { + guint8 selection; + guint type; + gpointer data; + gsize size; +}; + +struct SPICE_MAIN_CLIPBOARD_SELECTION_GRAB { + guint8 selection; + gpointer types; + gsize ntypes; + gboolean *ret; +}; + +struct SPICE_MAIN_CLIPBOARD_SELECTION_REQUEST { + guint8 selection; + guint type; + gboolean *ret; +}; + +struct SPICE_MAIN_CLIPBOARD_SELECTION_RELEASE { + guint8 selection; +}; + /* main context */ static void do_emit_main_context(GObject *object, int signum, gpointer params) { @@ -554,6 +668,30 @@ static void do_emit_main_context(GObject *object, int signum, gpointer params) p->type, p->ret); break; } + case SPICE_MAIN_CLIPBOARD_SELECTION: { + struct SPICE_MAIN_CLIPBOARD_SELECTION *p = params; + g_signal_emit(object, signals[signum], 0, + p->selection, p->type, p->data, p->size); + break; + } + case SPICE_MAIN_CLIPBOARD_SELECTION_GRAB: { + struct SPICE_MAIN_CLIPBOARD_SELECTION_GRAB *p = params; + g_signal_emit(object, signals[signum], 0, + p->selection, p->types, p->ntypes, p->ret); + break; + } + case SPICE_MAIN_CLIPBOARD_SELECTION_REQUEST: { + struct SPICE_MAIN_CLIPBOARD_SELECTION_REQUEST *p = params; + g_signal_emit(object, signals[signum], 0, + p->selection, p->type, p->ret); + break; + } + case SPICE_MAIN_CLIPBOARD_SELECTION_RELEASE: { + struct SPICE_MAIN_CLIPBOARD_SELECTION_RELEASE *p = params; + g_signal_emit(object, signals[signum], 0, + p->selection); + break; + } default: g_warn_if_reached(); } @@ -706,16 +844,22 @@ static void agent_announce_caps(SpiceMainChannel *channel) VD_AGENT_SET_CAPABILITY(caps->caps, VD_AGENT_CAP_REPLY); VD_AGENT_SET_CAPABILITY(caps->caps, VD_AGENT_CAP_DISPLAY_CONFIG); VD_AGENT_SET_CAPABILITY(caps->caps, VD_AGENT_CAP_CLIPBOARD_BY_DEMAND); + VD_AGENT_SET_CAPABILITY(caps->caps, VD_AGENT_CAP_CLIPBOARD_SELECTION); agent_msg_queue(channel, VD_AGENT_ANNOUNCE_CAPABILITIES, size, caps); free(caps); } +#define HAS_CLIPBOARD_SELECTION(c) \ + VD_AGENT_HAS_CAPABILITY((c)->agent_caps, sizeof((c)->agent_caps), VD_AGENT_CAP_CLIPBOARD_SELECTION) + /* any context: the message is not flushed immediately, you can wakeup() the channel coroutine or send_msg_queue() */ -static void agent_clipboard_grab(SpiceMainChannel *channel, guint32 *types, int ntypes) +static void agent_clipboard_grab(SpiceMainChannel *channel, guint selection, + guint32 *types, int ntypes) { spice_main_channel *c = channel->priv; + guint8 *msg; VDAgentClipboardGrab *grab; size_t size; int i; @@ -727,22 +871,38 @@ static void agent_clipboard_grab(SpiceMainChannel *channel, guint32 *types, int sizeof(c->agent_caps), VD_AGENT_CAP_CLIPBOARD_BY_DEMAND)); size = sizeof(VDAgentClipboardGrab) + sizeof(uint32_t) * ntypes; - grab = spice_malloc0(size); + if (HAS_CLIPBOARD_SELECTION(c)) + size += 4; + else if (selection != VD_AGENT_CLIPBOARD_SELECTION_CLIPBOARD) { + SPICE_DEBUG("Ignoring clipboard grab"); + return; + } + + msg = g_alloca(size); + memset(msg, 0, size); + + grab = (VDAgentClipboardGrab *)msg; + + if (HAS_CLIPBOARD_SELECTION(c)) { + msg[0] = selection; + grab = (VDAgentClipboardGrab *)(msg + 4); + } + for (i = 0; i < ntypes; i++) { grab->types[i] = types[i]; } - agent_msg_queue(channel, VD_AGENT_CLIPBOARD_GRAB, size, grab); - free(grab); + agent_msg_queue(channel, VD_AGENT_CLIPBOARD_GRAB, size, msg); } /* any context: the message is not flushed immediately, you can wakeup() the channel coroutine or send_msg_queue() */ -static void agent_clipboard_notify(SpiceMainChannel *channel, +static void agent_clipboard_notify(SpiceMainChannel *channel, guint selection, guint32 type, const guchar *data, size_t size) { spice_main_channel *c = channel->priv; VDAgentClipboard *cb; + guint8 *msg; size_t msgsize; g_return_if_fail(c->agent_connected); @@ -751,43 +911,88 @@ static void agent_clipboard_notify(SpiceMainChannel *channel, sizeof(c->agent_caps), VD_AGENT_CAP_CLIPBOARD_BY_DEMAND)); msgsize = sizeof(VDAgentClipboard) + size; - cb = spice_malloc0(msgsize); + if (HAS_CLIPBOARD_SELECTION(c)) + msgsize += 4; + else if (selection != VD_AGENT_CLIPBOARD_SELECTION_CLIPBOARD) { + SPICE_DEBUG("Ignoring clipboard notify"); + return; + } + + msg = g_alloca(msgsize); + memset(msg, 0, msgsize); + + cb = (VDAgentClipboard *)msg; + + if (HAS_CLIPBOARD_SELECTION(c)) { + msg[0] = selection; + cb = (VDAgentClipboard *)(msg + 4); + } + cb->type = type; memcpy(cb->data, data, size); - agent_msg_queue(channel, VD_AGENT_CLIPBOARD, msgsize, cb); - free(cb); + agent_msg_queue(channel, VD_AGENT_CLIPBOARD, msgsize, msg); } /* any context: the message is not flushed immediately, you can wakeup() the channel coroutine or send_msg_queue() */ -static void agent_clipboard_request(SpiceMainChannel *channel, guint32 type) +static void agent_clipboard_request(SpiceMainChannel *channel, guint selection, guint32 type) { spice_main_channel *c = channel->priv; - VDAgentClipboardRequest request = { 0, }; + VDAgentClipboardRequest *request; + guint8 *msg; + size_t msgsize; g_return_if_fail(c->agent_connected); g_return_if_fail(VD_AGENT_HAS_CAPABILITY(c->agent_caps, sizeof(c->agent_caps), VD_AGENT_CAP_CLIPBOARD_BY_DEMAND)); - request.type = type; + msgsize = sizeof(VDAgentClipboardRequest); + if (HAS_CLIPBOARD_SELECTION(c)) + msgsize += 4; + else if (selection != VD_AGENT_CLIPBOARD_SELECTION_CLIPBOARD) { + SPICE_DEBUG("Ignoring clipboard request"); + return; + } + + msg = g_alloca(msgsize); + memset(msg, 0, msgsize); + + request = (VDAgentClipboardRequest *)msg; + + if (HAS_CLIPBOARD_SELECTION(c)) { + msg[0] = selection; + request = (VDAgentClipboardRequest *)(msg + 4); + } + + request->type = type; - agent_msg_queue(channel, VD_AGENT_CLIPBOARD_REQUEST, sizeof(VDAgentClipboardRequest), &request); + agent_msg_queue(channel, VD_AGENT_CLIPBOARD_REQUEST, msgsize, msg); } /* any context: the message is not flushed immediately, you can wakeup() the channel coroutine or send_msg_queue() */ -static void agent_clipboard_release(SpiceMainChannel *channel) +static void agent_clipboard_release(SpiceMainChannel *channel, guint selection) { spice_main_channel *c = channel->priv; + guint8 msg[4] = { 0, }; + guint8 msgsize = 0; g_return_if_fail(c->agent_connected); g_return_if_fail(VD_AGENT_HAS_CAPABILITY(c->agent_caps, sizeof(c->agent_caps), VD_AGENT_CAP_CLIPBOARD_BY_DEMAND)); - agent_msg_queue(channel, VD_AGENT_CLIPBOARD_REQUEST, 0, NULL); + if (HAS_CLIPBOARD_SELECTION(c)) { + msg[0] = selection; + msgsize += 4; + } else if (selection != VD_AGENT_CLIPBOARD_SELECTION_CLIPBOARD) { + SPICE_DEBUG("Ignoring clipboard release"); + return; + } + + agent_msg_queue(channel, VD_AGENT_CLIPBOARD_RELEASE, msgsize, msg); } /* coroutine context */ @@ -952,6 +1157,22 @@ static void main_agent_handle_msg(SpiceChannel *channel, VDAgentMessage *msg, gpointer payload) { spice_main_channel *c = SPICE_MAIN_CHANNEL(channel)->priv; + guint8 selection = VD_AGENT_CLIPBOARD_SELECTION_CLIPBOARD; + + switch (msg->type) { + case VD_AGENT_CLIPBOARD_RELEASE: + case VD_AGENT_CLIPBOARD_REQUEST: + case VD_AGENT_CLIPBOARD_GRAB: + case VD_AGENT_CLIPBOARD: + if (VD_AGENT_HAS_CAPABILITY(c->agent_caps, sizeof(c->agent_caps), VD_AGENT_CAP_CLIPBOARD_SELECTION)) { + selection = *((guint8*)payload); + payload = ((guint8*)payload) + 4; + msg->size -= 4; + } + break; + default: + break; + } switch (msg->type) { case VD_AGENT_ANNOUNCE_CAPABILITIES: @@ -987,28 +1208,42 @@ static void main_agent_handle_msg(SpiceChannel *channel, case VD_AGENT_CLIPBOARD: { VDAgentClipboard *cb = payload; - emit_main_context(channel, SPICE_MAIN_CLIPBOARD, + emit_main_context(channel, SPICE_MAIN_CLIPBOARD_SELECTION, selection, cb->type, cb->data, msg->size - sizeof(VDAgentClipboard)); + + if (selection == VD_AGENT_CLIPBOARD_SELECTION_CLIPBOARD) + emit_main_context(channel, SPICE_MAIN_CLIPBOARD, + cb->type, cb->data, msg->size - sizeof(VDAgentClipboard)); break; } case VD_AGENT_CLIPBOARD_GRAB: { gboolean ret; - emit_main_context(channel, SPICE_MAIN_CLIPBOARD_GRAB, - payload, msg->size / sizeof(uint32_t), &ret); + emit_main_context(channel, SPICE_MAIN_CLIPBOARD_SELECTION_GRAB, selection, + (guint8*)payload, msg->size / sizeof(uint32_t), &ret); + if (selection == VD_AGENT_CLIPBOARD_SELECTION_CLIPBOARD) + emit_main_context(channel, SPICE_MAIN_CLIPBOARD_GRAB, + payload, msg->size / sizeof(uint32_t), &ret); break; } case VD_AGENT_CLIPBOARD_REQUEST: { gboolean ret; VDAgentClipboardRequest *req = payload; - emit_main_context(channel, SPICE_MAIN_CLIPBOARD_REQUEST, + emit_main_context(channel, SPICE_MAIN_CLIPBOARD_SELECTION_REQUEST, selection, req->type, &ret); + + if (selection == VD_AGENT_CLIPBOARD_SELECTION_CLIPBOARD) + emit_main_context(channel, SPICE_MAIN_CLIPBOARD_REQUEST, + req->type, &ret); break; } case VD_AGENT_CLIPBOARD_RELEASE: { - emit_main_context(channel, SPICE_MAIN_CLIPBOARD_RELEASE); + emit_main_context(channel, SPICE_MAIN_CLIPBOARD_SELECTION_RELEASE, selection); + + if (selection == VD_AGENT_CLIPBOARD_SELECTION_CLIPBOARD) + emit_main_context(channel, SPICE_MAIN_CLIPBOARD_RELEASE); break; } case VD_AGENT_REPLY: @@ -1414,13 +1649,32 @@ void spice_main_set_display(SpiceMainChannel *channel, int id, * @ntypes: the number of @types * * Grab the guest clipboard, with #VD_AGENT_CLIPBOARD @types. + * + * Deprecated: 0.6: use spice_main_clipboard_selection_grab() instead. **/ void spice_main_clipboard_grab(SpiceMainChannel *channel, guint32 *types, int ntypes) { + spice_main_clipboard_selection_grab(channel, VD_AGENT_CLIPBOARD_SELECTION_CLIPBOARD, types, ntypes); +} + +/** + * spice_main_clipboard_selection_grab: + * @channel: + * @selection: one of the clipboard #VD_AGENT_CLIPBOARD_SELECTION_* + * @types: an array of #VD_AGENT_CLIPBOARD types available in the clipboard + * @ntypes: the number of @types + * + * Grab the guest clipboard, with #VD_AGENT_CLIPBOARD @types. + * + * Since: 0.6 + **/ +void spice_main_clipboard_selection_grab(SpiceMainChannel *channel, guint selection, + guint32 *types, int ntypes) +{ g_return_if_fail(channel != NULL); g_return_if_fail(SPICE_IS_MAIN_CHANNEL(channel)); - agent_clipboard_grab(channel, types, ntypes); + agent_clipboard_grab(channel, selection, types, ntypes); spice_channel_wakeup(SPICE_CHANNEL(channel)); } @@ -1430,9 +1684,26 @@ void spice_main_clipboard_grab(SpiceMainChannel *channel, guint32 *types, int nt * * Release the clipboard (for example, when the client looses the * clipboard grab): Inform the guest no clipboard data is available. + * + * Deprecated: 0.6: use spice_main_clipboard_selection_release() instead. **/ void spice_main_clipboard_release(SpiceMainChannel *channel) { + spice_main_clipboard_selection_release(channel, VD_AGENT_CLIPBOARD_SELECTION_CLIPBOARD); +} + +/** + * spice_main_clipboard_selection_release: + * @channel: + * @selection: one of the clipboard #VD_AGENT_CLIPBOARD_SELECTION_* + * + * Release the clipboard (for example, when the client looses the + * clipboard grab): Inform the guest no clipboard data is available. + * + * Since: 0.6 + **/ +void spice_main_clipboard_selection_release(SpiceMainChannel *channel, guint selection) +{ g_return_if_fail(channel != NULL); g_return_if_fail(SPICE_IS_MAIN_CHANNEL(channel)); @@ -1441,7 +1712,7 @@ void spice_main_clipboard_release(SpiceMainChannel *channel) if (!c->agent_connected) return; - agent_clipboard_release(channel); + agent_clipboard_release(channel, selection); spice_channel_wakeup(SPICE_CHANNEL(channel)); } @@ -1453,14 +1724,35 @@ void spice_main_clipboard_release(SpiceMainChannel *channel) * @size: data length in bytes * * Send the clipboard data to the guest. + * + * Deprecated: 0.6: use spice_main_clipboard_selection_notify() instead. **/ void spice_main_clipboard_notify(SpiceMainChannel *channel, guint32 type, const guchar *data, size_t size) { + spice_main_clipboard_selection_notify(channel, VD_AGENT_CLIPBOARD_SELECTION_CLIPBOARD, + type, data, size); +} + +/** + * spice_main_clipboard_selection_notify: + * @channel: + * @selection: one of the clipboard #VD_AGENT_CLIPBOARD_SELECTION_* + * @type: a #VD_AGENT_CLIPBOARD type + * @data: clipboard data + * @size: data length in bytes + * + * Send the clipboard data to the guest. + * + * Since: 0.6 + **/ +void spice_main_clipboard_selection_notify(SpiceMainChannel *channel, guint selection, + guint32 type, const guchar *data, size_t size) +{ g_return_if_fail(channel != NULL); g_return_if_fail(SPICE_IS_MAIN_CHANNEL(channel)); - agent_clipboard_notify(channel, type, data, size); + agent_clipboard_notify(channel, selection, type, data, size); spice_channel_wakeup(SPICE_CHANNEL(channel)); } @@ -1471,12 +1763,30 @@ void spice_main_clipboard_notify(SpiceMainChannel *channel, * * Request clipboard data of @type from the guest. The reply is sent * through the #SpiceMainChannel::main-clipboard signal. + * + * Deprecated: 0.6: use spice_main_clipboard_selection_request() instead. **/ void spice_main_clipboard_request(SpiceMainChannel *channel, guint32 type) { + spice_main_clipboard_selection_request(channel, VD_AGENT_CLIPBOARD_SELECTION_CLIPBOARD, type); +} + +/** + * spice_main_clipboard_selection_request: + * @channel: + * @selection: one of the clipboard #VD_AGENT_CLIPBOARD_SELECTION_* + * @type: a #VD_AGENT_CLIPBOARD type + * + * Request clipboard data of @type from the guest. The reply is sent + * through the #SpiceMainChannel::main-clipboard signal. + * + * Since: 0.6 + **/ +void spice_main_clipboard_selection_request(SpiceMainChannel *channel, guint selection, guint32 type) +{ g_return_if_fail(channel != NULL); g_return_if_fail(SPICE_IS_MAIN_CHANNEL(channel)); - agent_clipboard_request(channel, type); + agent_clipboard_request(channel, selection, type); spice_channel_wakeup(SPICE_CHANNEL(channel)); } diff --git a/gtk/channel-main.h b/gtk/channel-main.h index 9a4f884..645de5d 100644 --- a/gtk/channel-main.h +++ b/gtk/channel-main.h @@ -60,6 +60,11 @@ void spice_main_clipboard_release(SpiceMainChannel *channel); void spice_main_clipboard_notify(SpiceMainChannel *channel, guint32 type, const guchar *data, size_t size); void spice_main_clipboard_request(SpiceMainChannel *channel, guint32 type); +void spice_main_clipboard_selection_grab(SpiceMainChannel *channel, guint selection, guint32 *types, int ntypes); +void spice_main_clipboard_selection_release(SpiceMainChannel *channel, guint selection); +void spice_main_clipboard_selection_notify(SpiceMainChannel *channel, guint selection, guint32 type, const guchar *data, size_t size); +void spice_main_clipboard_selection_request(SpiceMainChannel *channel, guint selection, guint32 type); + G_END_DECLS #endif /* __SPICE_CLIENT_MAIN_CHANNEL_H__ */ diff --git a/gtk/map-file b/gtk/map-file index a17ab16..0f52a53 100644 --- a/gtk/map-file +++ b/gtk/map-file @@ -44,6 +44,10 @@ spice_main_clipboard_grab; spice_main_clipboard_notify; spice_main_clipboard_release; spice_main_clipboard_request; +spice_main_clipboard_selection_grab; +spice_main_clipboard_selection_notify; +spice_main_clipboard_selection_release; +spice_main_clipboard_selection_request; spice_main_set_display; spice_playback_channel_get_type; spice_playback_channel_set_delay; diff --git a/gtk/spice-client-gtk.defs b/gtk/spice-client-gtk.defs index 7930318..fddb641 100644 --- a/gtk/spice-client-gtk.defs +++ b/gtk/spice-client-gtk.defs @@ -481,6 +481,48 @@ ) ) +(define-function spice_main_clipboard_selection_grab + (c-name "spice_main_clipboard_selection_grab") + (return-type "none") + (parameters + '("SpiceMainChannel*" "channel") + '("guint" "selection") + '("guint32*" "types") + '("int" "ntypes") + ) +) + +(define-function spice_main_clipboard_selection_release + (c-name "spice_main_clipboard_selection_release") + (return-type "none") + (parameters + '("SpiceMainChannel*" "channel") + '("guint" "selection") + ) +) + +(define-function spice_main_clipboard_selection_notify + (c-name "spice_main_clipboard_selection_notify") + (return-type "none") + (parameters + '("SpiceMainChannel*" "channel") + '("guint" "selection") + '("guint32" "type") + '("const-guchar*" "data") + '("size_t" "size") + ) +) + +(define-function spice_main_clipboard_selection_request + (c-name "spice_main_clipboard_selection_request") + (return-type "none") + (parameters + '("SpiceMainChannel*" "channel") + '("guint" "selection") + '("guint32" "type") + ) +) + ;; From channel-display.h diff --git a/gtk/spice-marshal.txt b/gtk/spice-marshal.txt index 9884804..b99a5f5 100644 --- a/gtk/spice-marshal.txt +++ b/gtk/spice-marshal.txt @@ -7,3 +7,6 @@ VOID:POINTER,INT BOOLEAN:POINTER,UINT BOOLEAN:UINT VOID:UINT,POINTER,UINT +VOID:UINT,UINT,POINTER,UINT +BOOLEAN:UINT,POINTER,UINT +BOOLEAN:UINT,UINT diff --git a/gtk/spice-widget-priv.h b/gtk/spice-widget-priv.h index e90603a..325e897 100644 --- a/gtk/spice-widget-priv.h +++ b/gtk/spice-widget-priv.h @@ -73,6 +73,7 @@ struct spice_display { #endif GtkClipboard *clipboard; + GtkClipboard *clipboard_primary; GtkTargetEntry *clip_targets; guint nclip_targets; bool clip_hasdata; diff --git a/gtk/spice-widget.c b/gtk/spice-widget.c index 72cbd9c..98920b9 100644 --- a/gtk/spice-widget.c +++ b/gtk/spice-widget.c @@ -254,6 +254,9 @@ static void spice_display_init(SpiceDisplay *display) d->clipboard = gtk_clipboard_get(GDK_SELECTION_CLIPBOARD); g_signal_connect(G_OBJECT(d->clipboard), "owner-change", G_CALLBACK(clipboard_owner_change), display); + d->clipboard_primary = gtk_clipboard_get(GDK_SELECTION_PRIMARY); + g_signal_connect(G_OBJECT(d->clipboard_primary), "owner-change", + G_CALLBACK(clipboard_owner_change), display); if (g_getenv("SPICE_DEBUG_CURSOR")) d->mouse_cursor = gdk_cursor_new(GDK_DOT); @@ -976,6 +979,30 @@ static gboolean configure_event(GtkWidget *widget, GdkEventConfigure *conf) /* ---------------------------------------------------------------- */ +static GtkClipboard* get_clipboard_from_selection(spice_display *d, guint selection) +{ + if (selection == VD_AGENT_CLIPBOARD_SELECTION_CLIPBOARD) { + return d->clipboard; + } else if (selection == VD_AGENT_CLIPBOARD_SELECTION_PRIMARY) { + return d->clipboard_primary; + } else { + g_warning("Unhandled clipboard selection: %d", selection); + return NULL; + } +} + +static gint get_selection_from_clipboard(spice_display *d, GtkClipboard* cb) +{ + if (cb == d->clipboard) { + return VD_AGENT_CLIPBOARD_SELECTION_CLIPBOARD; + } else if (cb == d->clipboard_primary) { + return VD_AGENT_CLIPBOARD_SELECTION_PRIMARY; + } else { + g_warning("Unhandled clipboard"); + return -1; + } +} + static const struct { const char *xatom; uint32_t vdagent; @@ -1031,6 +1058,8 @@ static void clipboard_get_targets(GtkClipboard *clipboard, char *name; int a, m, t; + g_return_if_fail(get_selection_from_clipboard(d, clipboard) != -1); + SPICE_DEBUG("%s:", __FUNCTION__); if (spice_util_get_debug()) { for (a = 0; a < n_atoms; a++) { @@ -1070,7 +1099,8 @@ static void clipboard_get_targets(GtkClipboard *clipboard, } if (!d->clip_grabbed && t > 0) { d->clip_grabbed = true; - spice_main_clipboard_grab(d->main, types, t); + spice_main_clipboard_selection_grab(d->main, + get_selection_from_clipboard(d, clipboard), types, t); } } @@ -1081,12 +1111,15 @@ static void clipboard_owner_change(GtkClipboard *clipboard, SpiceDisplay *display = data; spice_display *d = SPICE_DISPLAY_GET_PRIVATE(display); + g_return_if_fail(get_selection_from_clipboard(d, clipboard) != -1); + if (d->main == NULL) return; if (d->clip_grabbed) { d->clip_grabbed = false; - spice_main_clipboard_release(d->main); + spice_main_clipboard_selection_release(d->main, + get_selection_from_clipboard(d, clipboard)); } switch (event->reason) { @@ -1435,7 +1468,6 @@ static void disconnect_cursor(SpiceDisplay *display) d->cursor = NULL; } - typedef struct { GMainLoop *loop; @@ -1443,14 +1475,17 @@ typedef struct GtkSelectionData *selection_data; guint info; gulong timeout_handler; + guint selection; } RunInfo; -static void clipboard_got_from_guest(SpiceMainChannel *main, +static void clipboard_got_from_guest(SpiceMainChannel *main, guint selection, guint type, guchar *data, guint size, gpointer userdata) { RunInfo *ri = userdata; + g_return_if_fail(selection == ri->selection); + SPICE_DEBUG("clipboard got data"); gtk_selection_data_set(ri->selection_data, @@ -1483,16 +1518,18 @@ static void clipboard_get(GtkClipboard *clipboard, GtkSelectionData *selection_d SPICE_DEBUG("clipboard get"); g_return_if_fail(info < SPICE_N_ELEMENTS(atom2agent)); + g_return_if_fail(get_selection_from_clipboard(d, clipboard) != -1); ri.display = display; ri.selection_data = selection_data; ri.info = info; ri.loop = g_main_loop_new(NULL, FALSE); + ri.selection = get_selection_from_clipboard(d, clipboard); - clipboard_handler = g_signal_connect(d->main, "main-clipboard", + clipboard_handler = g_signal_connect(d->main, "main-clipboard-selection", G_CALLBACK(clipboard_got_from_guest), &ri); ri.timeout_handler = g_timeout_add_seconds(7, clipboard_timeout, &ri); - spice_main_clipboard_request(d->main, atom2agent[info].vdagent); + spice_main_clipboard_selection_request(d->main, ri.selection, atom2agent[info].vdagent); /* apparently, this is needed to avoid dead-lock, from gtk_dialog_run */ @@ -1513,7 +1550,7 @@ static void clipboard_clear(GtkClipboard *clipboard, gpointer display) // clipboard release ? } -static gboolean clipboard_grab(SpiceMainChannel *main, +static gboolean clipboard_grab(SpiceMainChannel *main, guint selection, guint32* types, guint32 ntypes, gpointer display) { int m, n, i; @@ -1521,6 +1558,10 @@ static gboolean clipboard_grab(SpiceMainChannel *main, GtkTargetEntry targets[SPICE_N_ELEMENTS(atom2agent)]; gboolean target_selected[SPICE_N_ELEMENTS(atom2agent)] = { FALSE, }; gboolean found; + GtkClipboard* cb; + + cb = get_clipboard_from_selection(d, selection); + g_return_val_if_fail(cb != NULL, FALSE); i = 0; for (n = 0; n < ntypes; ++n) { @@ -1548,7 +1589,7 @@ static gboolean clipboard_grab(SpiceMainChannel *main, if (!d->auto_clipboard_enable) goto skip_grab_clipboard; - if (!gtk_clipboard_set_with_data(d->clipboard, targets, i, + if (!gtk_clipboard_set_with_data(cb, targets, i, clipboard_get, clipboard_clear, display)) { g_warning("clipboard grab failed"); return FALSE; @@ -1569,6 +1610,7 @@ static void clipboard_received_cb(GtkClipboard *clipboard, gchar* name; GdkAtom atom; + g_return_if_fail(get_selection_from_clipboard(d, clipboard) != -1); len = gtk_selection_data_get_length(selection_data); if (len == -1) { @@ -1594,12 +1636,12 @@ static void clipboard_received_cb(GtkClipboard *clipboard, g_free(name); } - spice_main_clipboard_notify(d->main, + spice_main_clipboard_selection_notify(d->main, get_selection_from_clipboard(d, clipboard), type, gtk_selection_data_get_data(selection_data), len); } -static gboolean clipboard_request(SpiceMainChannel *main, - guint32 type, gpointer display) +static gboolean clipboard_request(SpiceMainChannel *main, guint selection, + guint type, gpointer display) { spice_display *d = SPICE_DISPLAY_GET_PRIVATE(display); int m; @@ -1613,13 +1655,13 @@ static gboolean clipboard_request(SpiceMainChannel *main, g_return_val_if_fail(m < SPICE_N_ELEMENTS(atom2agent), FALSE); atom = gdk_atom_intern_static_string(atom2agent[m].xatom); - gtk_clipboard_request_contents(d->clipboard, atom, + gtk_clipboard_request_contents(get_clipboard_from_selection(d, selection), atom, clipboard_received_cb, display); return TRUE; } -static void clipboard_release(SpiceMainChannel *main, gpointer data) +static void clipboard_release(SpiceMainChannel *main, guint selection, gpointer data) { spice_display *d = SPICE_DISPLAY_GET_PRIVATE(data); @@ -1637,11 +1679,11 @@ static void channel_new(SpiceSession *s, SpiceChannel *channel, gpointer data) d->main = SPICE_MAIN_CHANNEL(channel); g_signal_connect(channel, "main-mouse-update", G_CALLBACK(mouse_update), display); - g_signal_connect(channel, "main-clipboard-grab", + g_signal_connect(channel, "main-clipboard-selection-grab", G_CALLBACK(clipboard_grab), display); - g_signal_connect(channel, "main-clipboard-request", + g_signal_connect(channel, "main-clipboard-selection-request", G_CALLBACK(clipboard_request), display); - g_signal_connect(channel, "main-clipboard-release", + g_signal_connect(channel, "main-clipboard-selection-release", G_CALLBACK(clipboard_release), display); mouse_update(channel, display); return; |