qemu-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[PULL v2 04/36] ui/vdagent: add serial capability support


From: marcandre . lureau
Subject: [PULL v2 04/36] ui/vdagent: add serial capability support
Date: Tue, 21 Dec 2021 10:58:23 +0400

From: Marc-André Lureau <marcandre.lureau@redhat.com>

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>
---
 include/ui/clipboard.h |  4 ++++
 ui/vdagent.c           | 39 ++++++++++++++++++++++++++++++++++++++-
 ui/trace-events        |  1 +
 3 files changed, 43 insertions(+), 1 deletion(-)

diff --git a/include/ui/clipboard.h b/include/ui/clipboard.h
index d82cf314811c..e590b453c8b7 100644
--- a/include/ui/clipboard.h
+++ b/include/ui/clipboard.h
@@ -102,6 +102,8 @@ struct QemuClipboardNotify {
  * @owner: clipboard owner.
  * @selection: clipboard selection.
  * @types: clipboard data array (one entry per type).
+ * @has_serial: whether @serial is available.
+ * @serial: the grab serial counter.
  *
  * Clipboard content data and metadata.
  */
@@ -109,6 +111,8 @@ struct QemuClipboardInfo {
     uint32_t refcount;
     QemuClipboardPeer *owner;
     QemuClipboardSelection selection;
+    bool has_serial;
+    uint32_t serial;
     struct {
         bool available;
         bool requested;
diff --git a/ui/vdagent.c b/ui/vdagent.c
index de827aad27ab..b4fdae69177f 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;
diff --git a/ui/trace-events b/ui/trace-events
index b9c0dd0fa11d..e832c3e3659d 100644
--- a/ui/trace-events
+++ b/ui/trace-events
@@ -135,3 +135,4 @@ vdagent_recv_msg(const char *name, uint32_t size) "msg %s, 
size %d"
 vdagent_peer_cap(const char *name) "cap %s"
 vdagent_cb_grab_selection(const char *name) "selection %s"
 vdagent_cb_grab_type(const char *name) "type %s"
+vdagent_cb_serial_discard(uint32_t current, uint32_t received) "current=%u, 
received=%u"
-- 
2.34.1.8.g35151cf07204




reply via email to

[Prev in Thread] Current Thread [Next in Thread]