From e73a3dec6079434de10b9a865d9dde73af24e19d Mon Sep 17 00:00:00 2001 From: Geoffrey McRae Date: Tue, 28 Jul 2026 13:05:40 +1000 Subject: [PATCH] [client] spice: handle clipboard request failures --- client/displayservers/Wayland/clipboard.c | 3 ++- client/displayservers/X11/clipboard.c | 5 ++-- client/include/app.h | 2 +- client/src/app.c | 33 ++++++++++++++++++----- 4 files changed, 32 insertions(+), 11 deletions(-) diff --git a/client/displayservers/Wayland/clipboard.c b/client/displayservers/Wayland/clipboard.c index c33da445..4b1330f3 100644 --- a/client/displayservers/Wayland/clipboard.c +++ b/client/displayservers/Wayland/clipboard.c @@ -595,7 +595,8 @@ void waylandCBNotice(LG_ClipboardData type) { wlCb.haveRequest = true; wlCb.type = type; - app_clipboardRequest(waylandCBReplyFn, NULL); + if (!app_clipboardRequest(waylandCBReplyFn, NULL)) + DEBUG_ERROR("Failed to request SPICE clipboard data"); } void waylandCBRelease(void) diff --git a/client/displayservers/X11/clipboard.c b/client/displayservers/X11/clipboard.c index 2932a75a..068b830d 100644 --- a/client/displayservers/X11/clipboard.c +++ b/client/displayservers/X11/clipboard.c @@ -195,8 +195,9 @@ static void x11CBSelectionRequest(const XSelectionRequestEvent e) if (x11cb.aTypes[i] == e.target && x11cb.type == i) { // request the data - app_clipboardRequest(x11CBReplyFn, s); - return; + if (app_clipboardRequest(x11CBReplyFn, s)) + return; + goto nodata; } nodata: diff --git a/client/include/app.h b/client/include/app.h index 8dabf784..6f663ce0 100644 --- a/client/include/app.h +++ b/client/include/app.h @@ -151,7 +151,7 @@ void app_clipboardRelease(void); void app_clipboardNotifyTypes(const LG_ClipboardData types[], int count); void app_clipboardNotifySize(const LG_ClipboardData type, size_t size); void app_clipboardData(const LG_ClipboardData type, uint8_t * data, size_t size); -void app_clipboardRequest(const LG_ClipboardReplyFn replyFn, void * opaque); +bool app_clipboardRequest(const LG_ClipboardReplyFn replyFn, void * opaque); /** * Show an alert on screen diff --git a/client/src/app.c b/client/src/app.c index b4d7a7f4..adae5bab 100644 --- a/client/src/app.c +++ b/client/src/app.c @@ -348,24 +348,43 @@ void app_clipboardData(const LG_ClipboardData type, uint8_t * data, size_t size) DEBUG_ERROR("Failed to send SPICE clipboard data"); } -void app_clipboardRequest(const LG_ClipboardReplyFn replyFn, void * opaque) +bool app_clipboardRequest(const LG_ClipboardReplyFn replyFn, void * opaque) { - if (!g_params.clipboardToLocal) - return; + if (!g_params.clipboardToLocal || !replyFn || !g_state.cbRequestList) + return false; + const PSDataType type = g_state.cbType; struct CBRequest * cbr = malloc(sizeof(*cbr)); if (!cbr) { DEBUG_ERROR("out of memory"); - return; + return false; } - cbr->type = g_state.cbType; + cbr->type = type; cbr->replyFn = replyFn; cbr->opaque = opaque; - ll_push(g_state.cbRequestList, cbr); - purespice_clipboardRequest(g_state.cbType); + if (!ll_push(g_state.cbRequestList, cbr)) + { + free(cbr); + return false; + } + + if (purespice_clipboardRequest(type)) + return true; + + /* + * Queue the callback before sending so a fast response cannot arrive before + * its request is visible. If another thread has already consumed it, the + * callback owns both cbr and opaque and the request completed successfully + * from the caller's perspective. + */ + if (!ll_removeData(g_state.cbRequestList, cbr)) + return true; + + free(cbr); + return false; } static int mapSpiceToImGuiButton(uint32_t button)