[client] clipboard: preserve newer Wayland selections

This commit is contained in:
Geoffrey McRae
2026-08-14 12:02:40 +10:00
parent dc2ab76c36
commit 608e85fe5d
3 changed files with 49 additions and 4 deletions

View File

@@ -339,12 +339,10 @@ static void dataDeviceHandleSelection(void * opaque,
for (enum LG_ClipboardData i = 0; i < LG_CLIPBOARD_DATA_NONE; ++i) for (enum LG_ClipboardData i = 0; i < LG_CLIPBOARD_DATA_NONE; ++i)
free(extra->mimetypes[i]); free(extra->mimetypes[i]);
free(extra); free(extra);
/* Publishing already retired the old offer, so a delayed self-copy echo
* must not invalidate a newer local selection. */
if (!selfCopy) if (!selfCopy)
waylandCBInvalidateLocal(CLIPBOARD_INVALIDATE_FORCE); waylandCBInvalidateLocal(CLIPBOARD_INVALIDATE_FORCE);
else
/* This is the compositor echoing the source we just installed. It is
* not a new local clipboard owner and must not send a provider CLEAR. */
waylandCBInvalidateLocal(CLIPBOARD_INVALIDATE_SILENT);
wl_data_offer_destroy(offer); wl_data_offer_destroy(offer);
return; return;
} }

View File

@@ -211,6 +211,7 @@ if(ENABLE_WAYLAND)
teardown teardown
teardown-external teardown-external
self-copy self-copy
self-copy-late
ignored ignored
invalidate-lock invalidate-lock
dnd dnd

View File

@@ -759,6 +759,51 @@ static void testSelfCopy(void)
finish(); finish();
} }
static void testLateSelfCopy(void)
{
start();
waylandCBNotice(LG_CLIPBOARD_DATA_TEXT);
struct Proxy * source = &proto.source[0];
CHECK(wlCb.selectionSource == (struct wl_data_source *)source);
struct Proxy * local = textOffer();
CHECK(rec.noticeN == 1);
CHECK(!wlCb.selectionSource);
CHECK(wlCb.offer == (struct wl_data_offer *)local);
CHECK(!local->dead);
/* The compositor may deliver our marked echo after a newer external
* selection. The stale echo must not discard that local selection. */
struct Proxy * echo = newOffer();
offerMime(echo, "text/plain");
offerMime(echo, wlCb.lgMimetype);
selectOffer(echo);
CHECK(rec.noticeN == 1);
CHECK(rec.releaseN == 0);
CHECK(!wlCb.selectionSource);
CHECK(wlCb.offer == (struct wl_data_offer *)local);
CHECK(wlCb.mimetypes[LG_CLIPBOARD_DATA_TEXT]);
CHECK(!local->dead);
CHECK(echo->dead);
const uint8_t data[] = "new local selection";
proto.receiveData = data;
proto.receiveSize = sizeof(data) - 1;
waylandCBRequest(102, LG_CLIPBOARD_DATA_TEXT);
CHECK(proto.receiveN == 1);
CHECK(rec.pollN == 1);
pollFire(0, EPOLLIN);
pollFire(0, EPOLLIN);
CHECK(rec.streamData.request == 102);
CHECK(rec.streamData.size == sizeof(data) - 1);
CHECK(rec.streamData.endN == 1);
CHECK(memcmp(rec.streamData.data, data, sizeof(data) - 1) == 0);
CHECK(proto.dirtyDestroyN == 0);
finish();
}
static void testIgnoredOffer(void) static void testIgnoredOffer(void)
{ {
start(); start();
@@ -1329,6 +1374,7 @@ static const struct Test tests[] =
{ "teardown" , testTeardown }, { "teardown" , testTeardown },
{ "teardown-external", testTeardownExternalOwner }, { "teardown-external", testTeardownExternalOwner },
{ "self-copy" , testSelfCopy }, { "self-copy" , testSelfCopy },
{ "self-copy-late", testLateSelfCopy },
{ "ignored" , testIgnoredOffer }, { "ignored" , testIgnoredOffer },
{ "invalidate-lock", testInvalidateSerialization }, { "invalidate-lock", testInvalidateSerialization },
{ "dnd" , testDnd }, { "dnd" , testDnd },