aboutsummaryrefslogtreecommitdiff
path: root/ui/vdagent.c
diff options
context:
space:
mode:
authorMarc-André Lureau <marcandre.lureau@redhat.com>2021-07-19 00:33:31 +0400
committerMarc-André Lureau <marcandre.lureau@redhat.com>2021-12-21 10:50:21 +0400
commit835f69f4e64e2285253bae57f618a75a2756f27c (patch)
tree51413bf4743019cfe78eddf1bf5ca79d0c564c61 /ui/vdagent.c
parent1b17f1e9f962f5ae9cd559d7f23718ceed71b813 (diff)
ui/vdagent: add serial capability support
The Spice agent implements a simple serial mechanism to avoid clipboard races between client & guest. See: https://gitlab.freedesktop.org/spice/spice-protocol/-/commit/045a6978d6dbbf7046affc5c321fa8177c8cce56 Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com> Acked-by: Gerd Hoffmann <kraxel@redhat.com>
Diffstat (limited to 'ui/vdagent.c')
-rw-r--r--ui/vdagent.c39
1 files changed, 38 insertions, 1 deletions
diff --git a/ui/vdagent.c b/ui/vdagent.c
index de827aad27..b4fdae6917 100644
--- a/ui/vdagent.c
+++ b/ui/vdagent.c
@@ -59,6 +59,7 @@ struct VDAgentChardev {
/* clipboard */
QemuClipboardPeer cbpeer;
+ uint32_t last_serial[QEMU_CLIPBOARD_SELECTION__COUNT];
uint32_t cbpending[QEMU_CLIPBOARD_SELECTION__COUNT];
};
typedef struct VDAgentChardev VDAgentChardev;
@@ -203,6 +204,9 @@ static void vdagent_send_caps(VDAgentChardev *vd)
if (vd->clipboard) {
caps->caps[0] |= (1 << VD_AGENT_CAP_CLIPBOARD_BY_DEMAND);
caps->caps[0] |= (1 << VD_AGENT_CAP_CLIPBOARD_SELECTION);
+#if CHECK_SPICE_PROTOCOL_VERSION(0, 14, 1)
+ caps->caps[0] |= (1 << VD_AGENT_CAP_CLIPBOARD_GRAB_SERIAL);
+#endif
}
vdagent_send_msg(vd, msg);
@@ -333,7 +337,8 @@ static void vdagent_send_clipboard_grab(VDAgentChardev *vd,
{
g_autofree VDAgentMessage *msg =
g_malloc0(sizeof(VDAgentMessage) +
- sizeof(uint32_t) * (QEMU_CLIPBOARD_TYPE__COUNT + 1));
+ sizeof(uint32_t) * (QEMU_CLIPBOARD_TYPE__COUNT + 1) +
+ sizeof(uint32_t));
uint8_t *s = msg->data;
uint32_t *data = (uint32_t *)msg->data;
uint32_t q, type;
@@ -346,6 +351,19 @@ static void vdagent_send_clipboard_grab(VDAgentChardev *vd,
return;
}
+#if CHECK_SPICE_PROTOCOL_VERSION(0, 14, 1)
+ if (vd->caps & (1 << VD_AGENT_CAP_CLIPBOARD_GRAB_SERIAL)) {
+ if (!info->has_serial) {
+ /* client should win */
+ info->serial = vd->last_serial[info->selection]++;
+ info->has_serial = true;
+ }
+ *data = info->serial;
+ data++;
+ msg->size += sizeof(uint32_t);
+ }
+#endif
+
for (q = 0; q < QEMU_CLIPBOARD_TYPE__COUNT; q++) {
type = type_qemu_to_vdagent(q);
if (type != VD_AGENT_CLIPBOARD_NONE && info->types[q].available) {
@@ -494,6 +512,24 @@ static void vdagent_clipboard_recv_grab(VDAgentChardev *vd, uint8_t s, uint32_t
trace_vdagent_cb_grab_selection(GET_NAME(sel_name, s));
info = qemu_clipboard_info_new(&vd->cbpeer, s);
+#if CHECK_SPICE_PROTOCOL_VERSION(0, 14, 1)
+ if (vd->caps & (1 << VD_AGENT_CAP_CLIPBOARD_GRAB_SERIAL)) {
+ if (size < sizeof(uint32_t)) {
+ /* this shouldn't happen! */
+ return;
+ }
+
+ info->has_serial = true;
+ info->serial = *(uint32_t *)data;
+ if (info->serial < vd->last_serial[s]) {
+ /* discard lower-ordering guest grab */
+ return;
+ }
+ vd->last_serial[s] = info->serial;
+ data += sizeof(uint32_t);
+ size -= sizeof(uint32_t);
+ }
+#endif
if (size > sizeof(uint32_t) * 10) {
/*
* spice has 6 types as of 2021. Limiting to 10 entries
@@ -671,6 +707,7 @@ static void vdagent_chr_recv_caps(VDAgentChardev *vd, VDAgentMessage *msg)
qemu_input_handler_activate(vd->mouse_hs);
}
if (have_clipboard(vd) && vd->cbpeer.notifier.notify == NULL) {
+ memset(vd->last_serial, 0, sizeof(vd->last_serial));
vd->cbpeer.name = "vdagent";
vd->cbpeer.notifier.notify = vdagent_clipboard_notify;
vd->cbpeer.request = vdagent_clipboard_request;