diff --git a/client/displayservers/Wayland/clipboard.c b/client/displayservers/Wayland/clipboard.c index 294bfc0e..af52b896 100644 --- a/client/displayservers/Wayland/clipboard.c +++ b/client/displayservers/Wayland/clipboard.c @@ -1040,7 +1040,7 @@ struct ClipboardWrite bool pollRegistered; bool streamActive; bool requesting; - uint8_t buffer[KVMFR_CLIPBOARD_DATA_BYTES]; + uint8_t buffer[KVMFR_CLIPBOARD_REPRESENTATION_BYTES]; }; static void clipboardWriteDestroy(struct ClipboardWrite * data) diff --git a/client/displayservers/Wayland/wayland.h b/client/displayservers/Wayland/wayland.h index ce0f8c46..ac1f3260 100644 --- a/client/displayservers/Wayland/wayland.h +++ b/client/displayservers/Wayland/wayland.h @@ -316,7 +316,7 @@ struct ClipboardRead bool calling; bool readyPending; bool eof; - uint8_t buffer[KVMFR_CLIPBOARD_DATA_BYTES]; + uint8_t buffer[KVMFR_CLIPBOARD_REPRESENTATION_BYTES]; }; struct WCBState diff --git a/client/displayservers/X11/clipboard.c b/client/displayservers/X11/clipboard.c index 10f54851..6ad0f1e5 100644 --- a/client/displayservers/X11/clipboard.c +++ b/client/displayservers/X11/clipboard.c @@ -206,8 +206,9 @@ bool x11CBEventThread(const XEvent * xe) const size_t remaining = write->fileSize - (size_t)write->offset; if (remaining) { - const size_t chunk = remaining > KVMFR_CLIPBOARD_DATA_BYTES ? - KVMFR_CLIPBOARD_DATA_BYTES : remaining; + const size_t chunk = + remaining > KVMFR_CLIPBOARD_REPRESENTATION_BYTES ? + KVMFR_CLIPBOARD_REPRESENTATION_BYTES : remaining; XChangeProperty(x11.display, write->event.xselection.requestor, write->event.xselection.property, @@ -357,7 +358,8 @@ static LG_ClipboardResult x11CBWriteChunk(void * opaque, LG_UNLOCK(x11cb.lock); return LG_CLIPBOARD_RESULT_BLOCKED; } - if (offset != write->offset || size > KVMFR_CLIPBOARD_DATA_BYTES) + if (offset != write->offset || + size > KVMFR_CLIPBOARD_REPRESENTATION_BYTES) { LG_UNLOCK(x11cb.lock); return LG_CLIPBOARD_RESULT_FAILED; @@ -978,7 +980,7 @@ static void x11CBFileImportIncr(const XPropertyEvent e) readProperty: if (XGetWindowProperty(e.display, e.window, e.atom, x11cb.fileImport.propertyOffset, - (KVMFR_CLIPBOARD_DATA_BYTES + 3U) / 4U, + (KVMFR_CLIPBOARD_REPRESENTATION_BYTES + 3U) / 4U, True, AnyPropertyType, &type, &format, &itemCount, &after, &data) != Success || (itemCount && !data) || (!itemCount && after) || type != x11cb.fileImport.target || format != 8 || @@ -1136,7 +1138,7 @@ readProperty: e.window, e.atom, x11cb.read.propertyOffset, - (KVMFR_CLIPBOARD_DATA_BYTES + 3U) / 4U, + (KVMFR_CLIPBOARD_REPRESENTATION_BYTES + 3U) / 4U, True, // delete the property AnyPropertyType, &type, @@ -1198,7 +1200,7 @@ readProperty: return; } - if (itemCount > KVMFR_CLIPBOARD_DATA_BYTES) + if (itemCount > KVMFR_CLIPBOARD_REPRESENTATION_BYTES) { const struct X11ClipboardRead read = clearReadNL(); LG_UNLOCK(x11cb.lock); diff --git a/client/tests/lgmp_clipboard_test.c b/client/tests/lgmp_clipboard_test.c index c87686ba..1d7110f4 100644 --- a/client/tests/lgmp_clipboard_test.c +++ b/client/tests/lgmp_clipboard_test.c @@ -35,12 +35,18 @@ #include #include -#define TEST_SHM_SIZE (8U * 1024U * 1024U) -#define TEST_WAIT_MS 2500U -#define TEST_QUIET_MS 50U -#define TEST_MEMORY_MAX 128U -#define SLOT_BYTES \ +#define TEST_WAIT_MS 2500U +#define TEST_QUIET_MS 50U +#define TEST_MEMORY_MAX 128U +#define SLOT_BYTES \ (sizeof(KVMFRClipboardSlotHeader) + KVMFR_CLIPBOARD_DATA_BYTES) +#define TEST_TRANSIENT_SLOTS 4U +#define TEST_SHM_OVERHEAD (1024U * 1024U) +/* Match the production inbound and outbound slot pools, retain several + * synthetic host DATA records at once, and leave room for LGMP metadata. */ +#define TEST_SHM_SIZE \ + ((2U * KVMFR_CLIPBOARD_SLOT_COUNT + TEST_TRANSIENT_SLOTS) * SLOT_BYTES + \ + TEST_SHM_OVERHEAD) #define CHECK(x) \ do \ @@ -1219,6 +1225,10 @@ static bool testMalformedFileData(TestState * state) static bool testFileStream(TestState * state) { + static uint8_t bytes[KVMFR_CLIPBOARD_FILE_READ_BYTES]; + for (size_t i = 0; i < sizeof(bytes); ++i) + bytes[i] = (uint8_t)(i * 131U + 17U); + KVMFRClipboardMessage claimRecord; CHECK(claim(state, 78, false, &claimRecord)); CHECK(own(state, 78, claimRecord.generation)); @@ -1251,7 +1261,7 @@ static bool testFileStream(TestState * state) request.offset = 123; request.size = 42; request.format = KVMFR_CLIPBOARD_FORMAT_FILES; - request.flags = 3; + request.flags = sizeof(bytes); request.token = KVMFR_CLIPBOARD_FILE_OP_READ; CHECK(postRecord(state, &request, KVMFR_CLIPBOARD_QUEUE_MESSAGE, NULL)); @@ -1262,14 +1272,13 @@ static bool testFileStream(TestState * state) CHECK(descriptor.request == request.transfer); CHECK(descriptor.node == 42); CHECK(descriptor.offset == 123); - CHECK(descriptor.length == 3); + CHECK(descriptor.length == sizeof(bytes)); CHECK(descriptor.operation == LG_CLIPBOARD_FILE_READ); CHECK(state->ops->fileDataBegin(state->clipboard, - &descriptor, 3) == LG_CLIPBOARD_RESULT_ACCEPTED); + &descriptor, sizeof(bytes)) == LG_CLIPBOARD_RESULT_ACCEPTED); CHECK(postGrant(state, claimRecord.generation, 1)); CHECK(waitMemory(state, state->grantMemory[0])); - static const uint8_t bytes[] = { 9, 8, 7 }; CHECK(state->ops->fileDataChunk(state->clipboard, &descriptor, 0, bytes, sizeof(bytes)) == LG_CLIPBOARD_RESULT_ACCEPTED); @@ -1278,16 +1287,19 @@ static bool testFileStream(TestState * state) const void * data; CHECK(checkFileCommit(state, 1, &commit, &slot, &data)); CHECK(slot.flags == KVMFR_CLIPBOARD_FLAG_BEGIN); - CHECK(slot.offset == 0 && slot.sequence == 0 && slot.size == 3); + CHECK(slot.offset == 0 && slot.sequence == 0 && + slot.size == sizeof(bytes)); + CHECK(slot.length == sizeof(bytes)); CHECK(memcmp(data, bytes, sizeof(bytes)) == 0); CHECK(postGrant(state, claimRecord.generation, 1)); CHECK(waitMemory(state, state->grantMemory[0])); CHECK(state->ops->fileDataEnd(state->clipboard, - &descriptor, 3) == LG_CLIPBOARD_RESULT_ACCEPTED); + &descriptor, sizeof(bytes)) == LG_CLIPBOARD_RESULT_ACCEPTED); CHECK(checkFileCommit(state, 1, &commit, &slot, &data)); CHECK(slot.flags == KVMFR_CLIPBOARD_FLAG_END); - CHECK(slot.offset == 3 && slot.sequence == 1 && slot.size == 3); + CHECK(slot.offset == sizeof(bytes) && slot.sequence == 1 && + slot.size == sizeof(bytes)); CHECK(slot.length == 0); return true; } diff --git a/common/include/common/KVMFR.h b/common/include/common/KVMFR.h index 78c6844c..915c4b2b 100644 --- a/common/include/common/KVMFR.h +++ b/common/include/common/KVMFR.h @@ -32,7 +32,7 @@ #include "KVMFRInput.h" #define KVMFR_MAGIC "KVMFR---" -#define KVMFR_VERSION 31 +#define KVMFR_VERSION 32 // Fallback used by producers that cannot report the source display's SDR // white level. IDD frames override this with IDDCX_METADATA2::SdrWhiteLevel. diff --git a/common/include/common/KVMFRClipboard.h b/common/include/common/KVMFRClipboard.h index aa87ae52..25f0bf35 100644 --- a/common/include/common/KVMFRClipboard.h +++ b/common/include/common/KVMFRClipboard.h @@ -26,9 +26,12 @@ #include #include -#define KVMFR_CLIPBOARD_VERSION 2U -#define KVMFR_CLIPBOARD_SLOT_COUNT 8U -#define KVMFR_CLIPBOARD_DATA_BYTES (64U * 1024U) +#define KVMFR_CLIPBOARD_VERSION 3U +#define KVMFR_CLIPBOARD_SLOT_COUNT 1U +#define KVMFR_CLIPBOARD_DATA_BYTES (1024U * 1024U) +/* Keep text and image chunks within the core X11 request limit. File data can + * use the full slot without passing through XChangeProperty. */ +#define KVMFR_CLIPBOARD_REPRESENTATION_BYTES (64U * 1024U) #define KVMFR_CLIPBOARD_SIZE_UNKNOWN UINT64_MAX #define KVMFR_CLIPBOARD_FILE_READ_BYTES (1024U * 1024U) #define KVMFR_CLIPBOARD_FILE_ROOT_NODE UINT64_C(0) diff --git a/idd/LGCommon/ClipboardRing.h b/idd/LGCommon/ClipboardRing.h index 634a5ef0..23f44c02 100644 --- a/idd/LGCommon/ClipboardRing.h +++ b/idd/LGCommon/ClipboardRing.h @@ -26,7 +26,7 @@ #include static constexpr uint32_t LG_CLIPBOARD_MAPPING_MAGIC = 0x4c474342U; -static constexpr uint32_t LG_CLIPBOARD_MAPPING_VERSION = 2U; +static constexpr uint32_t LG_CLIPBOARD_MAPPING_VERSION = 3U; struct ClipboardRingSlot { diff --git a/idd/LGIdd/transport/lgmp/CLGMPClipboardTransport.h b/idd/LGIdd/transport/lgmp/CLGMPClipboardTransport.h index 0c8f8d92..773e1286 100644 --- a/idd/LGIdd/transport/lgmp/CLGMPClipboardTransport.h +++ b/idd/LGIdd/transport/lgmp/CLGMPClipboardTransport.h @@ -40,7 +40,7 @@ class CLGMPHost; class CLGMPClipboardTransport final : public IClipboardSource { private: - static constexpr unsigned MEMORY_COUNT = 8; + static constexpr unsigned MEMORY_COUNT = KVMFR_CLIPBOARD_SLOT_COUNT; static constexpr unsigned INTERNAL_TARGET_COUNT = CLGMPClipboardFiles::MAX_ACQUISITIONS + CLGMPClipboardFiles::MAX_REQUESTS + 3; diff --git a/idd/LGIddHelper/CClipboardManager.cpp b/idd/LGIddHelper/CClipboardManager.cpp index 698e699a..568ec648 100644 --- a/idd/LGIddHelper/CClipboardManager.cpp +++ b/idd/LGIddHelper/CClipboardManager.cpp @@ -457,7 +457,7 @@ namespace std::vector buffer; try { - buffer.resize(KVMFR_CLIPBOARD_DATA_BYTES); + buffer.resize(KVMFR_CLIPBOARD_REPRESENTATION_BYTES); } catch (const std::bad_alloc&) { @@ -1653,7 +1653,8 @@ void CClipboardManager::ProcessSendData(Work&& work) return; } const size_t length = static_cast((std::min)( - KVMFR_CLIPBOARD_DATA_BYTES, total - work.record.offset)); + KVMFR_CLIPBOARD_REPRESENTATION_BYTES, + total - work.record.offset)); std::vector data; if (length) {