mirror of
https://github.com/gnif/LookingGlass.git
synced 2026-08-29 02:11:52 +00:00
[client] wayland: reuse remote clipboard text
Some checks failed
build / client (Debug, map[cc:clang cxx:clang++], libdecor) (push) Has been cancelled
build / client (Debug, map[cc:clang cxx:clang++], xdg-shell) (push) Has been cancelled
build / client (Debug, map[cc:gcc cxx:g++], libdecor) (push) Has been cancelled
build / client (Debug, map[cc:gcc cxx:g++], xdg-shell) (push) Has been cancelled
build / client (Release, map[cc:clang cxx:clang++], libdecor) (push) Has been cancelled
build / client (Release, map[cc:clang cxx:clang++], xdg-shell) (push) Has been cancelled
build / client (Release, map[cc:gcc cxx:g++], libdecor) (push) Has been cancelled
build / client (Release, map[cc:gcc cxx:g++], xdg-shell) (push) Has been cancelled
build / module (push) Has been cancelled
build / host-linux (push) Has been cancelled
build / host-windows-cross (push) Has been cancelled
build / host-windows-native (push) Has been cancelled
build / idd (push) Has been cancelled
build / obs (clang) (push) Has been cancelled
build / obs (gcc) (push) Has been cancelled
build / docs (push) Has been cancelled
Some checks failed
build / client (Debug, map[cc:clang cxx:clang++], libdecor) (push) Has been cancelled
build / client (Debug, map[cc:clang cxx:clang++], xdg-shell) (push) Has been cancelled
build / client (Debug, map[cc:gcc cxx:g++], libdecor) (push) Has been cancelled
build / client (Debug, map[cc:gcc cxx:g++], xdg-shell) (push) Has been cancelled
build / client (Release, map[cc:clang cxx:clang++], libdecor) (push) Has been cancelled
build / client (Release, map[cc:clang cxx:clang++], xdg-shell) (push) Has been cancelled
build / client (Release, map[cc:gcc cxx:g++], libdecor) (push) Has been cancelled
build / client (Release, map[cc:gcc cxx:g++], xdg-shell) (push) Has been cancelled
build / module (push) Has been cancelled
build / host-linux (push) Has been cancelled
build / host-windows-cross (push) Has been cancelled
build / host-windows-native (push) Has been cancelled
build / idd (push) Has been cancelled
build / obs (clang) (push) Has been cancelled
build / obs (gcc) (push) Has been cancelled
build / docs (push) Has been cancelled
This commit is contained in:
@@ -31,6 +31,7 @@
|
|||||||
|
|
||||||
#include "core/clipboard.h"
|
#include "core/clipboard.h"
|
||||||
#include "core/clipboard_files.h"
|
#include "core/clipboard_files.h"
|
||||||
|
#include "common/countedbuffer.h"
|
||||||
#include "common/debug.h"
|
#include "common/debug.h"
|
||||||
#include "common/KVMFRClipboard.h"
|
#include "common/KVMFRClipboard.h"
|
||||||
|
|
||||||
@@ -1053,6 +1054,26 @@ void waylandCBRequestCancel(LG_ClipboardRequest request,
|
|||||||
clipboardReadRetire(data);
|
clipboardReadRetire(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct WCBPending
|
||||||
|
{
|
||||||
|
int fd;
|
||||||
|
struct WCBPending * next;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct WCBTextCache
|
||||||
|
{
|
||||||
|
LG_Lock lock;
|
||||||
|
CountedBuffer * data;
|
||||||
|
struct WCBPending * pending;
|
||||||
|
bool request;
|
||||||
|
};
|
||||||
|
|
||||||
|
static void fileTransferRetain(struct WCBTransfer * transfer);
|
||||||
|
static void fileTransferRelease(struct WCBTransfer * transfer);
|
||||||
|
static void clipboardTextComplete(struct WCBTransfer * transfer,
|
||||||
|
CountedBuffer * buffer);
|
||||||
|
static void clipboardTextCancel(struct WCBTransfer * transfer);
|
||||||
|
|
||||||
struct ClipboardWrite
|
struct ClipboardWrite
|
||||||
{
|
{
|
||||||
LG_Lock lock;
|
LG_Lock lock;
|
||||||
@@ -1070,11 +1091,60 @@ struct ClipboardWrite
|
|||||||
bool pollRegistered;
|
bool pollRegistered;
|
||||||
bool streamActive;
|
bool streamActive;
|
||||||
bool requesting;
|
bool requesting;
|
||||||
|
bool cacheTerminal;
|
||||||
|
struct WCBTransfer * cacheTransfer;
|
||||||
|
uint8_t * cache;
|
||||||
|
size_t cacheSize;
|
||||||
|
size_t cacheCapacity;
|
||||||
uint8_t buffer[KVMFR_CLIPBOARD_REPRESENTATION_BYTES];
|
uint8_t buffer[KVMFR_CLIPBOARD_REPRESENTATION_BYTES];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static bool clipboardWriteCacheGrow(struct ClipboardWrite * data,
|
||||||
|
size_t wanted)
|
||||||
|
{
|
||||||
|
if (wanted <= data->cacheCapacity)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
size_t capacity = data->cacheCapacity ? data->cacheCapacity : 4096U;
|
||||||
|
while (capacity < wanted)
|
||||||
|
{
|
||||||
|
if (capacity > SIZE_MAX / 2U)
|
||||||
|
{
|
||||||
|
capacity = wanted;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
capacity *= 2U;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t * cache = realloc(data->cache, capacity);
|
||||||
|
if (!cache)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
data->cache = cache;
|
||||||
|
data->cacheCapacity = capacity;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void clipboardWriteCancelCache(struct ClipboardWrite * data)
|
||||||
|
{
|
||||||
|
struct WCBTransfer * transfer = NULL;
|
||||||
|
LG_LOCK(data->lock);
|
||||||
|
if (data->cacheTransfer && !data->cacheTerminal)
|
||||||
|
{
|
||||||
|
data->cacheTerminal = true;
|
||||||
|
transfer = data->cacheTransfer;
|
||||||
|
}
|
||||||
|
LG_UNLOCK(data->lock);
|
||||||
|
|
||||||
|
if (transfer)
|
||||||
|
clipboardTextCancel(transfer);
|
||||||
|
}
|
||||||
|
|
||||||
static void clipboardWriteDestroy(struct ClipboardWrite * data)
|
static void clipboardWriteDestroy(struct ClipboardWrite * data)
|
||||||
{
|
{
|
||||||
|
free(data->cache);
|
||||||
|
if (data->cacheTransfer)
|
||||||
|
fileTransferRelease(data->cacheTransfer);
|
||||||
LG_LOCK_FREE(data->lock);
|
LG_LOCK_FREE(data->lock);
|
||||||
free(data);
|
free(data);
|
||||||
}
|
}
|
||||||
@@ -1102,7 +1172,6 @@ static void clipboardWriteRetireStream(struct ClipboardWrite * data)
|
|||||||
static LG_ClipboardResult clipboardWriteBegin(void * opaque,
|
static LG_ClipboardResult clipboardWriteBegin(void * opaque,
|
||||||
LG_ClipboardData type, uint64_t sizeHint)
|
LG_ClipboardData type, uint64_t sizeHint)
|
||||||
{
|
{
|
||||||
(void)sizeHint;
|
|
||||||
struct ClipboardWrite * data = opaque;
|
struct ClipboardWrite * data = opaque;
|
||||||
LG_LOCK(data->lock);
|
LG_LOCK(data->lock);
|
||||||
if (data->begun || data->ended || !data->streamActive ||
|
if (data->begun || data->ended || !data->streamActive ||
|
||||||
@@ -1111,6 +1180,13 @@ static LG_ClipboardResult clipboardWriteBegin(void * opaque,
|
|||||||
LG_UNLOCK(data->lock);
|
LG_UNLOCK(data->lock);
|
||||||
return LG_CLIPBOARD_RESULT_FAILED;
|
return LG_CLIPBOARD_RESULT_FAILED;
|
||||||
}
|
}
|
||||||
|
if (data->cacheTransfer && sizeHint != LG_CLIPBOARD_SIZE_UNKNOWN &&
|
||||||
|
(sizeHint > SIZE_MAX ||
|
||||||
|
!clipboardWriteCacheGrow(data, (size_t)sizeHint)))
|
||||||
|
{
|
||||||
|
LG_UNLOCK(data->lock);
|
||||||
|
return LG_CLIPBOARD_RESULT_FAILED;
|
||||||
|
}
|
||||||
|
|
||||||
data->begun = true;
|
data->begun = true;
|
||||||
LG_UNLOCK(data->lock);
|
LG_UNLOCK(data->lock);
|
||||||
@@ -1139,6 +1215,17 @@ static LG_ClipboardResult clipboardWriteChunk(void * opaque,
|
|||||||
LG_UNLOCK(data->lock);
|
LG_UNLOCK(data->lock);
|
||||||
return LG_CLIPBOARD_RESULT_FAILED;
|
return LG_CLIPBOARD_RESULT_FAILED;
|
||||||
}
|
}
|
||||||
|
if (data->cacheTransfer)
|
||||||
|
{
|
||||||
|
if (offset != data->cacheSize || size > SIZE_MAX - data->cacheSize ||
|
||||||
|
!clipboardWriteCacheGrow(data, data->cacheSize + size))
|
||||||
|
{
|
||||||
|
LG_UNLOCK(data->lock);
|
||||||
|
return LG_CLIPBOARD_RESULT_FAILED;
|
||||||
|
}
|
||||||
|
memcpy(data->cache + data->cacheSize, buffer, size);
|
||||||
|
data->cacheSize += size;
|
||||||
|
}
|
||||||
if (data->discard)
|
if (data->discard)
|
||||||
{
|
{
|
||||||
data->offset += size;
|
data->offset += size;
|
||||||
@@ -1168,6 +1255,8 @@ static LG_ClipboardResult clipboardWriteEnd(void * opaque,
|
|||||||
uint64_t finalSize)
|
uint64_t finalSize)
|
||||||
{
|
{
|
||||||
struct ClipboardWrite * data = opaque;
|
struct ClipboardWrite * data = opaque;
|
||||||
|
CountedBuffer * cache = NULL;
|
||||||
|
struct WCBTransfer * transfer = NULL;
|
||||||
LG_LOCK(data->lock);
|
LG_LOCK(data->lock);
|
||||||
if (!data->begun || data->ended || !data->streamActive)
|
if (!data->begun || data->ended || !data->streamActive)
|
||||||
{
|
{
|
||||||
@@ -1185,9 +1274,28 @@ static LG_ClipboardResult clipboardWriteEnd(void * opaque,
|
|||||||
LG_UNLOCK(data->lock);
|
LG_UNLOCK(data->lock);
|
||||||
return LG_CLIPBOARD_RESULT_FAILED;
|
return LG_CLIPBOARD_RESULT_FAILED;
|
||||||
}
|
}
|
||||||
|
if (data->cacheTransfer)
|
||||||
|
{
|
||||||
|
if (finalSize != data->cacheSize ||
|
||||||
|
data->cacheSize > SIZE_MAX - sizeof(*cache) ||
|
||||||
|
!(cache = countedBufferNew(data->cacheSize)))
|
||||||
|
{
|
||||||
|
LG_UNLOCK(data->lock);
|
||||||
|
return LG_CLIPBOARD_RESULT_FAILED;
|
||||||
|
}
|
||||||
|
if (data->cacheSize)
|
||||||
|
memcpy(cache->data, data->cache, data->cacheSize);
|
||||||
|
data->cacheTerminal = true;
|
||||||
|
transfer = data->cacheTransfer;
|
||||||
|
}
|
||||||
|
|
||||||
data->ended = true;
|
data->ended = true;
|
||||||
LG_UNLOCK(data->lock);
|
LG_UNLOCK(data->lock);
|
||||||
|
if (transfer)
|
||||||
|
{
|
||||||
|
clipboardTextComplete(transfer, cache);
|
||||||
|
countedBufferRelease(&cache);
|
||||||
|
}
|
||||||
clipboardWriteRetireStream(data);
|
clipboardWriteRetireStream(data);
|
||||||
return LG_CLIPBOARD_RESULT_ACCEPTED;
|
return LG_CLIPBOARD_RESULT_ACCEPTED;
|
||||||
}
|
}
|
||||||
@@ -1197,6 +1305,7 @@ static void clipboardWriteCancel(void * opaque,
|
|||||||
{
|
{
|
||||||
(void)reason;
|
(void)reason;
|
||||||
struct ClipboardWrite * data = opaque;
|
struct ClipboardWrite * data = opaque;
|
||||||
|
clipboardWriteCancelCache(data);
|
||||||
clipboardWriteRetireStream(data);
|
clipboardWriteRetireStream(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1219,10 +1328,34 @@ struct ClipboardFileWrite
|
|||||||
bool complete;
|
bool complete;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static void clipboardPendingClose(struct WCBPending * pending)
|
||||||
|
{
|
||||||
|
while (pending)
|
||||||
|
{
|
||||||
|
struct WCBPending * next = pending->next;
|
||||||
|
close(pending->fd);
|
||||||
|
free(pending);
|
||||||
|
pending = next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void clipboardTextDestroy(struct WCBTextCache * cache)
|
||||||
|
{
|
||||||
|
if (!cache)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (cache->data)
|
||||||
|
countedBufferRelease(&cache->data);
|
||||||
|
clipboardPendingClose(cache->pending);
|
||||||
|
LG_LOCK_FREE(cache->lock);
|
||||||
|
free(cache);
|
||||||
|
}
|
||||||
|
|
||||||
static void fileTransferDestroy(struct WCBTransfer * transfer)
|
static void fileTransferDestroy(struct WCBTransfer * transfer)
|
||||||
{
|
{
|
||||||
if (transfer->filePresentation)
|
if (transfer->filePresentation)
|
||||||
clipboardFiles_remotePresentationRelease(transfer->filePresentation);
|
clipboardFiles_remotePresentationRelease(transfer->filePresentation);
|
||||||
|
clipboardTextDestroy(transfer->textCache);
|
||||||
free(transfer->fileUri);
|
free(transfer->fileUri);
|
||||||
free(transfer->fileGnome);
|
free(transfer->fileGnome);
|
||||||
free(transfer->fileKde);
|
free(transfer->fileKde);
|
||||||
@@ -1317,6 +1450,107 @@ static bool clipboardFileWriteStart(int fd, const void * data, size_t size,
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Wayland consumers may request one selection more than once. Retain the
|
||||||
|
* first text transfer and hold overlapping reads until that cache is ready. */
|
||||||
|
static void clipboardTextComplete(struct WCBTransfer * transfer,
|
||||||
|
CountedBuffer * buffer)
|
||||||
|
{
|
||||||
|
struct WCBTextCache * cache = transfer->textCache;
|
||||||
|
struct WCBPending * pending = NULL;
|
||||||
|
CountedBuffer * cached = NULL;
|
||||||
|
|
||||||
|
LG_LOCK(cache->lock);
|
||||||
|
if (cache->request)
|
||||||
|
{
|
||||||
|
if (!cache->data)
|
||||||
|
{
|
||||||
|
countedBufferAddRef(buffer);
|
||||||
|
cache->data = buffer;
|
||||||
|
}
|
||||||
|
cache->request = false;
|
||||||
|
pending = cache->pending;
|
||||||
|
cache->pending = NULL;
|
||||||
|
cached = cache->data;
|
||||||
|
}
|
||||||
|
LG_UNLOCK(cache->lock);
|
||||||
|
|
||||||
|
while (pending)
|
||||||
|
{
|
||||||
|
struct WCBPending * next = pending->next;
|
||||||
|
if (!cached ||
|
||||||
|
!clipboardFileWriteStart(pending->fd, cached->data,
|
||||||
|
cached->size, transfer, false))
|
||||||
|
close(pending->fd);
|
||||||
|
free(pending);
|
||||||
|
pending = next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void clipboardTextCancel(struct WCBTransfer * transfer)
|
||||||
|
{
|
||||||
|
struct WCBTextCache * cache = transfer->textCache;
|
||||||
|
struct WCBPending * pending = NULL;
|
||||||
|
|
||||||
|
LG_LOCK(cache->lock);
|
||||||
|
if (cache->request)
|
||||||
|
{
|
||||||
|
cache->request = false;
|
||||||
|
pending = cache->pending;
|
||||||
|
cache->pending = NULL;
|
||||||
|
}
|
||||||
|
LG_UNLOCK(cache->lock);
|
||||||
|
clipboardPendingClose(pending);
|
||||||
|
}
|
||||||
|
|
||||||
|
enum ClipboardTextSendResult
|
||||||
|
{
|
||||||
|
CLIPBOARD_TEXT_SEND_FAILED,
|
||||||
|
CLIPBOARD_TEXT_SEND_HANDLED,
|
||||||
|
CLIPBOARD_TEXT_SEND_STREAM,
|
||||||
|
};
|
||||||
|
|
||||||
|
static enum ClipboardTextSendResult clipboardTextSend(
|
||||||
|
struct WCBTransfer * transfer, int fd)
|
||||||
|
{
|
||||||
|
struct WCBPending * pending = malloc(sizeof(*pending));
|
||||||
|
if (!pending)
|
||||||
|
{
|
||||||
|
DEBUG_ERROR("Out of memory queuing clipboard request");
|
||||||
|
return CLIPBOARD_TEXT_SEND_FAILED;
|
||||||
|
}
|
||||||
|
pending->fd = fd;
|
||||||
|
pending->next = NULL;
|
||||||
|
|
||||||
|
struct WCBTextCache * cache = transfer->textCache;
|
||||||
|
CountedBuffer * buffer = NULL;
|
||||||
|
bool stream = false;
|
||||||
|
LG_LOCK(cache->lock);
|
||||||
|
if (cache->data)
|
||||||
|
buffer = cache->data;
|
||||||
|
else if (cache->request)
|
||||||
|
{
|
||||||
|
pending->next = cache->pending;
|
||||||
|
cache->pending = pending;
|
||||||
|
pending = NULL;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
cache->request = true;
|
||||||
|
stream = true;
|
||||||
|
}
|
||||||
|
LG_UNLOCK(cache->lock);
|
||||||
|
|
||||||
|
free(pending);
|
||||||
|
if (stream)
|
||||||
|
return CLIPBOARD_TEXT_SEND_STREAM;
|
||||||
|
if (!buffer)
|
||||||
|
return CLIPBOARD_TEXT_SEND_HANDLED;
|
||||||
|
if (clipboardFileWriteStart(fd, buffer->data,
|
||||||
|
buffer->size, transfer, false))
|
||||||
|
return CLIPBOARD_TEXT_SEND_HANDLED;
|
||||||
|
return CLIPBOARD_TEXT_SEND_FAILED;
|
||||||
|
}
|
||||||
|
|
||||||
static bool fileTransferPayload(const struct WCBTransfer * transfer,
|
static bool fileTransferPayload(const struct WCBTransfer * transfer,
|
||||||
const char * mime, const char ** data, size_t * size)
|
const char * mime, const char ** data, size_t * size)
|
||||||
{
|
{
|
||||||
@@ -1453,19 +1687,45 @@ static void dataSourceHandleSend(void * data, struct wl_data_source * source,
|
|||||||
close(fd);
|
close(fd);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (containsMimetype(transfer->mimetypes, mimetype))
|
if (!containsMimetype(transfer->mimetypes, mimetype))
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
bool cacheText = false;
|
||||||
|
if (transfer->textCache)
|
||||||
{
|
{
|
||||||
struct ClipboardWrite * data = calloc(1, sizeof(*data));
|
switch (clipboardTextSend(transfer, fd))
|
||||||
if (!data)
|
{
|
||||||
|
case CLIPBOARD_TEXT_SEND_HANDLED:
|
||||||
|
return;
|
||||||
|
|
||||||
|
case CLIPBOARD_TEXT_SEND_FAILED:
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
case CLIPBOARD_TEXT_SEND_STREAM:
|
||||||
|
cacheText = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ClipboardWrite * output = calloc(1, sizeof(*output));
|
||||||
|
if (!output)
|
||||||
{
|
{
|
||||||
DEBUG_ERROR("Out of memory trying to allocate ClipboardWrite");
|
DEBUG_ERROR("Out of memory trying to allocate ClipboardWrite");
|
||||||
|
if (cacheText)
|
||||||
|
clipboardTextCancel(transfer);
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
data->fd = fd;
|
output->fd = fd;
|
||||||
data->request = LG_CLIPBOARD_REQUEST_INVALID;
|
output->request = LG_CLIPBOARD_REQUEST_INVALID;
|
||||||
data->type = transfer->type;
|
output->type = transfer->type;
|
||||||
LG_LOCK_INIT(data->lock);
|
LG_LOCK_INIT(output->lock);
|
||||||
|
if (cacheText)
|
||||||
|
{
|
||||||
|
output->cacheTransfer = transfer;
|
||||||
|
fileTransferRetain(transfer);
|
||||||
|
}
|
||||||
|
|
||||||
const int flags = fcntl(fd, F_GETFL);
|
const int flags = fcntl(fd, F_GETFL);
|
||||||
const int fdFlags = fcntl(fd, F_GETFD);
|
const int fdFlags = fcntl(fd, F_GETFD);
|
||||||
if (flags < 0 || fdFlags < 0 ||
|
if (flags < 0 || fdFlags < 0 ||
|
||||||
@@ -1474,50 +1734,60 @@ static void dataSourceHandleSend(void * data, struct wl_data_source * source,
|
|||||||
{
|
{
|
||||||
DEBUG_ERROR("Failed to make clipboard pipe nonblocking: %s",
|
DEBUG_ERROR("Failed to make clipboard pipe nonblocking: %s",
|
||||||
strerror(errno));
|
strerror(errno));
|
||||||
clipboardWriteDestroy(data);
|
clipboardWriteCancelCache(output);
|
||||||
|
clipboardWriteDestroy(output);
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
data->pollOwned = true;
|
|
||||||
data->pollRegistered = true;
|
output->pollOwned = true;
|
||||||
data->streamActive = true;
|
output->pollRegistered = true;
|
||||||
|
output->streamActive = true;
|
||||||
if (waylandPollRegisterWithCleanup(fd,
|
if (waylandPollRegisterWithCleanup(fd,
|
||||||
clipboardWriteCallback, data, clipboardWriteCleanup, 0))
|
clipboardWriteCallback, output, clipboardWriteCleanup, 0))
|
||||||
{
|
{
|
||||||
LG_LOCK(data->lock);
|
LG_LOCK(output->lock);
|
||||||
data->requesting = true;
|
output->requesting = true;
|
||||||
LG_UNLOCK(data->lock);
|
LG_UNLOCK(output->lock);
|
||||||
const bool result = clipboard_requestStream(transfer->type,
|
const bool result = clipboard_requestStream(transfer->type,
|
||||||
&clipboardWriteStream, data, &data->request);
|
&clipboardWriteStream, output, &output->request);
|
||||||
|
|
||||||
int retireFd = -1;
|
int retireFd = -1;
|
||||||
bool destroy = false;
|
bool destroy = false;
|
||||||
LG_LOCK(data->lock);
|
bool cancelCache = false;
|
||||||
data->requesting = false;
|
LG_LOCK(output->lock);
|
||||||
if (!result || data->request == LG_CLIPBOARD_REQUEST_INVALID ||
|
output->requesting = false;
|
||||||
!data->streamActive)
|
if (!result || output->request == LG_CLIPBOARD_REQUEST_INVALID ||
|
||||||
|
!output->streamActive)
|
||||||
{
|
{
|
||||||
data->streamActive = false;
|
output->streamActive = false;
|
||||||
if (data->pollRegistered)
|
if (output->cacheTransfer && !output->cacheTerminal)
|
||||||
{
|
{
|
||||||
data->pollRegistered = false;
|
output->cacheTerminal = true;
|
||||||
retireFd = data->fd;
|
cancelCache = true;
|
||||||
}
|
}
|
||||||
destroy = !data->pollOwned;
|
if (output->pollRegistered)
|
||||||
|
{
|
||||||
|
output->pollRegistered = false;
|
||||||
|
retireFd = output->fd;
|
||||||
}
|
}
|
||||||
LG_UNLOCK(data->lock);
|
destroy = !output->pollOwned;
|
||||||
|
}
|
||||||
|
LG_UNLOCK(output->lock);
|
||||||
|
|
||||||
|
if (cancelCache)
|
||||||
|
clipboardTextCancel(output->cacheTransfer);
|
||||||
if (retireFd >= 0)
|
if (retireFd >= 0)
|
||||||
waylandPollUnregister(retireFd);
|
waylandPollUnregister(retireFd);
|
||||||
else if (destroy)
|
else if (destroy)
|
||||||
clipboardWriteDestroy(data);
|
clipboardWriteDestroy(output);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
data->pollOwned = false;
|
output->pollOwned = false;
|
||||||
data->pollRegistered = false;
|
output->pollRegistered = false;
|
||||||
data->streamActive = false;
|
output->streamActive = false;
|
||||||
clipboardWriteDestroy(data);
|
clipboardWriteCancelCache(output);
|
||||||
}
|
clipboardWriteDestroy(output);
|
||||||
|
|
||||||
error:
|
error:
|
||||||
close(fd);
|
close(fd);
|
||||||
@@ -1560,8 +1830,18 @@ static bool waylandCBPublish(LG_ClipboardData type)
|
|||||||
atomic_init(&transfer->references, 1);
|
atomic_init(&transfer->references, 1);
|
||||||
|
|
||||||
transfer->mimetypes = cbTypeToMimetypes(type);
|
transfer->mimetypes = cbTypeToMimetypes(type);
|
||||||
|
transfer->textCache = type == LG_CLIPBOARD_DATA_TEXT ?
|
||||||
|
calloc(1, sizeof(*transfer->textCache)) : NULL;
|
||||||
transfer->type = type;
|
transfer->type = type;
|
||||||
transfer->next = NULL;
|
transfer->next = NULL;
|
||||||
|
if (type == LG_CLIPBOARD_DATA_TEXT && !transfer->textCache)
|
||||||
|
{
|
||||||
|
DEBUG_ERROR("Out of memory when allocating WCBTextCache");
|
||||||
|
fileTransferRelease(transfer);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (transfer->textCache)
|
||||||
|
LG_LOCK_INIT(transfer->textCache->lock);
|
||||||
if (type == LG_CLIPBOARD_DATA_FILES)
|
if (type == LG_CLIPBOARD_DATA_FILES)
|
||||||
transfer->filePresentation =
|
transfer->filePresentation =
|
||||||
clipboardFiles_remotePresentationAcquire();
|
clipboardFiles_remotePresentationAcquire();
|
||||||
|
|||||||
@@ -286,11 +286,14 @@ struct WaylandDSState
|
|||||||
LG_DSEventSource eventSource;
|
LG_DSEventSource eventSource;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct WCBTextCache;
|
||||||
|
|
||||||
struct WCBTransfer
|
struct WCBTransfer
|
||||||
{
|
{
|
||||||
atomic_uint references;
|
atomic_uint references;
|
||||||
LG_ClipboardData type;
|
LG_ClipboardData type;
|
||||||
const char ** mimetypes;
|
const char ** mimetypes;
|
||||||
|
struct WCBTextCache * textCache;
|
||||||
struct wl_data_source * source;
|
struct wl_data_source * source;
|
||||||
struct WCBTransfer * next;
|
struct WCBTransfer * next;
|
||||||
uint64_t filePresentation;
|
uint64_t filePresentation;
|
||||||
|
|||||||
@@ -1311,11 +1311,10 @@ static void testSource(void)
|
|||||||
struct Proxy * source = &proto.source[0];
|
struct Proxy * source = &proto.source[0];
|
||||||
CHECK(proto.selection == source);
|
CHECK(proto.selection == source);
|
||||||
CHECK(proto.serial == 77);
|
CHECK(proto.serial == 77);
|
||||||
CHECK(source->mimeN == 6);
|
CHECK(source->mimeN == 3);
|
||||||
CHECK(strcmp(source->mime[0], "text/plain") == 0);
|
CHECK(strcmp(source->mime[0], "text/plain;charset=utf-8") == 0);
|
||||||
CHECK(strcmp(source->mime[1], "text/plain;charset=utf-8") == 0);
|
CHECK(strcmp(source->mime[1], "text/plain") == 0);
|
||||||
CHECK(strcmp(source->mime[4], "UTF8_STRING") == 0);
|
CHECK(strcmp(source->mime[2], wlCb.lgMimetype) == 0);
|
||||||
CHECK(strcmp(source->mime[5], wlCb.lgMimetype) == 0);
|
|
||||||
|
|
||||||
int bad[2];
|
int bad[2];
|
||||||
CHECK(pipe(bad) == 0);
|
CHECK(pipe(bad) == 0);
|
||||||
@@ -1338,7 +1337,15 @@ static void testSource(void)
|
|||||||
CHECK(rec.requestStream);
|
CHECK(rec.requestStream);
|
||||||
CHECK(rec.requestOpaque);
|
CHECK(rec.requestOpaque);
|
||||||
|
|
||||||
const uint8_t first[] = "source ";
|
int queued[2];
|
||||||
|
CHECK(pipe(queued) == 0);
|
||||||
|
sourceListener(source)->send(source->data,
|
||||||
|
(struct wl_data_source *)source,
|
||||||
|
"text/plain;charset=utf-8", queued[1]);
|
||||||
|
CHECK(rec.pollN == 1);
|
||||||
|
CHECK(rec.requestN == 1);
|
||||||
|
|
||||||
|
const uint8_t first [] = "source ";
|
||||||
const uint8_t second[] = "data";
|
const uint8_t second[] = "data";
|
||||||
CHECK(rec.requestStream->begin(rec.requestOpaque,
|
CHECK(rec.requestStream->begin(rec.requestOpaque,
|
||||||
LG_CLIPBOARD_DATA_TEXT, LG_CLIPBOARD_SIZE_UNKNOWN) ==
|
LG_CLIPBOARD_DATA_TEXT, LG_CLIPBOARD_SIZE_UNKNOWN) ==
|
||||||
@@ -1361,19 +1368,39 @@ static void testSource(void)
|
|||||||
CHECK(rec.requestStream->end(rec.requestOpaque,
|
CHECK(rec.requestStream->end(rec.requestOpaque,
|
||||||
sizeof(first) + sizeof(second) - 2) == LG_CLIPBOARD_RESULT_ACCEPTED);
|
sizeof(first) + sizeof(second) - 2) == LG_CLIPBOARD_RESULT_ACCEPTED);
|
||||||
CHECK(!rec.poll[0].active);
|
CHECK(!rec.poll[0].active);
|
||||||
|
CHECK(rec.pollN == 2);
|
||||||
|
CHECK(rec.poll[1].fd == queued[1]);
|
||||||
|
CHECK(rec.poll[1].events == EPOLLOUT);
|
||||||
|
pollFire(1, EPOLLOUT);
|
||||||
|
checkClosed(good[1]);
|
||||||
|
checkClosed(queued[1]);
|
||||||
|
|
||||||
|
int cached[2];
|
||||||
|
CHECK(pipe(cached) == 0);
|
||||||
|
sourceListener(source)->send(source->data,
|
||||||
|
(struct wl_data_source *)source, "text/plain", cached[1]);
|
||||||
|
CHECK(rec.pollN == 3);
|
||||||
|
CHECK(rec.poll[2].fd == cached[1]);
|
||||||
|
CHECK(rec.poll[2].events == EPOLLOUT);
|
||||||
|
CHECK(rec.requestN == 1);
|
||||||
|
pollFire(2, EPOLLOUT);
|
||||||
|
checkClosed(cached[1]);
|
||||||
|
|
||||||
sourceListener(source)->cancelled(source->data,
|
sourceListener(source)->cancelled(source->data,
|
||||||
(struct wl_data_source *)source);
|
(struct wl_data_source *)source);
|
||||||
CHECK(source->dead);
|
CHECK(source->dead);
|
||||||
CHECK(proto.sourceDestroyN == 1);
|
CHECK(proto.sourceDestroyN == 1);
|
||||||
CHECK(rec.pollCleanupN == 1);
|
CHECK(rec.pollCleanupN == 3);
|
||||||
uint8_t actual[sizeof(first) + sizeof(second)] = {};
|
|
||||||
const size_t expected = sizeof(first) + sizeof(second) - 2;
|
const size_t expected = sizeof(first) + sizeof(second) - 2;
|
||||||
CHECK(read(good[0], actual, sizeof(actual)) == (ssize_t)expected);
|
const int reads[] = { good[0], queued[0], cached[0] };
|
||||||
|
for (size_t i = 0; i < ARRAY_LENGTH(reads); ++i)
|
||||||
|
{
|
||||||
|
uint8_t actual[sizeof(first) + sizeof(second)] = {};
|
||||||
|
CHECK(read(reads[i], actual, sizeof(actual)) == (ssize_t)expected);
|
||||||
CHECK(memcmp(actual, "source data", expected) == 0);
|
CHECK(memcmp(actual, "source data", expected) == 0);
|
||||||
CHECK(read(good[0], &value, 1) == 0);
|
CHECK(read(reads[i], &value, 1) == 0);
|
||||||
CHECK(close(good[0]) == 0);
|
CHECK(close(reads[i]) == 0);
|
||||||
checkClosed(good[1]);
|
}
|
||||||
|
|
||||||
finish();
|
finish();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -62,6 +62,8 @@
|
|||||||
#define A_GNOME 106UL
|
#define A_GNOME 106UL
|
||||||
#define A_MATE 107UL
|
#define A_MATE 107UL
|
||||||
#define A_KDE 108UL
|
#define A_KDE 108UL
|
||||||
|
#define A_TEXT_PLAIN 109UL
|
||||||
|
#define A_TEXT_UTF8 110UL
|
||||||
|
|
||||||
struct WindowLog
|
struct WindowLog
|
||||||
{
|
{
|
||||||
@@ -212,6 +214,10 @@ Atom XInternAtom(Display * display, const char * name, Bool onlyIfExists)
|
|||||||
CHECK(!onlyIfExists);
|
CHECK(!onlyIfExists);
|
||||||
if (!strcmp(name, "UTF8_STRING"))
|
if (!strcmp(name, "UTF8_STRING"))
|
||||||
return A_TEXT;
|
return A_TEXT;
|
||||||
|
if (!strcmp(name, "text/plain"))
|
||||||
|
return A_TEXT_PLAIN;
|
||||||
|
if (!strcmp(name, "text/plain;charset=utf-8"))
|
||||||
|
return A_TEXT_UTF8;
|
||||||
if (!strcmp(name, "image/png"))
|
if (!strcmp(name, "image/png"))
|
||||||
return A_PNG;
|
return A_PNG;
|
||||||
if (!strcmp(name, "image/bmp"))
|
if (!strcmp(name, "image/bmp"))
|
||||||
|
|||||||
Reference in New Issue
Block a user