[client] clipboard: add seamless file backends

This commit is contained in:
Geoffrey McRae
2026-08-14 16:07:30 +10:00
parent 9679c94b91
commit a91343be96
20 changed files with 7145 additions and 105 deletions

View File

@@ -19,6 +19,7 @@
*/
#include "clipboard.h"
#include "clipboard_files.h"
#include "main.h"
#include "common/debug.h"
@@ -83,6 +84,7 @@ static struct
LG_Lock requestLock;
LG_Lock writeLock;
LG_Lock callbackLock;
LG_Lock fileLock;
ClipboardBinding fallback;
ClipboardBinding transport;
@@ -91,10 +93,12 @@ static struct
bool localAvailable;
LG_ClipboardData localTypes[LG_CLIPBOARD_DATA_NONE];
size_t localTypeCount;
uint64_t localFileDataset;
bool remoteNotice;
LG_ClipboardData remoteType;
uint32_t remoteGeneration;
uint64_t remoteFileDataset;
bool remoteRequest;
ClipboardBinding remoteRequestBinding;
@@ -371,18 +375,22 @@ static void resetRemote(void)
bool release;
LG_ClipboardRequest transfer;
LG_LOCK(clipboard.fileLock);
LG_LOCK(clipboard.requestLock);
LG_LOCK(clipboard.stateLock);
release = clipboard.remoteNotice && clipboard.localAvailable &&
g_params.clipboardToLocal;
clipboard.remoteNotice = false;
clipboard.remoteType = LG_CLIPBOARD_DATA_NONE;
clipboard.remoteGeneration = nextGeneration(
clipboard.remoteNotice = false;
clipboard.remoteType = LG_CLIPBOARD_DATA_NONE;
clipboard.remoteFileDataset = 0;
clipboard.remoteGeneration = nextGeneration(
clipboard.remoteGeneration);
transfer = clearRemoteRequestNL();
LG_UNLOCK(clipboard.stateLock);
LG_UNLOCK(clipboard.requestLock);
lgClipboardFiles_providerUnavailable();
LG_LOCK(clipboard.callbackLock);
cancelRequestsNL(NULL, 0, true, LG_CLIPBOARD_CANCEL_UNAVAILABLE);
if (transfer != LG_CLIPBOARD_REQUEST_INVALID &&
@@ -396,6 +404,7 @@ static void resetRemote(void)
if (release)
g_state.ds->cbRelease();
LG_UNLOCK(clipboard.callbackLock);
LG_UNLOCK(clipboard.fileLock);
}
static bool validOps(const LG_ClipboardOps * ops)
@@ -435,11 +444,18 @@ static void publishLocal(const ClipboardBinding * binding)
LG_LOCK(clipboard.stateLock);
count = g_params.clipboardToVM ? clipboard.localTypeCount : 0;
memcpy(types, clipboard.localTypes, count * sizeof(*types));
const uint64_t fileDataset = clipboard.localFileDataset;
LG_UNLOCK(clipboard.stateLock);
const bool result = count ?
binding->ops->notifyTypes(binding->opaque, types, count) :
binding->ops->release(binding->opaque);
bool result;
if (count == 1 && types[0] == LG_CLIPBOARD_DATA_FILES)
result = fileDataset && binding->ops->offerFiles ?
binding->ops->offerFiles(binding->opaque, fileDataset) :
binding->ops->release(binding->opaque);
else
result = count ? binding->ops->notifyTypes(
binding->opaque, types, count) :
binding->ops->release(binding->opaque);
if (!result)
DEBUG_WARN("Failed to publish the local clipboard to %s",
binding->ops->name);
@@ -454,39 +470,60 @@ static void eventNotice(void * opaque,
return;
LG_ClipboardData type = LG_CLIPBOARD_DATA_NONE;
bool hasFiles = false;
for (size_t i = 0; i < count; ++i)
if (validType(types[i]))
{
type = types[i];
break;
if (types[i] == LG_CLIPBOARD_DATA_FILES)
{
type = LG_CLIPBOARD_DATA_FILES;
hasFiles = true;
}
else if (type == LG_CLIPBOARD_DATA_NONE)
type = types[i];
}
if (type == LG_CLIPBOARD_DATA_NONE)
return;
LG_LOCK(clipboard.fileLock);
LG_LOCK_SHARED(clipboard.activeLock);
if (!bindingActiveNL(binding))
{
LG_UNLOCK_SHARED(clipboard.activeLock);
LG_UNLOCK(clipboard.fileLock);
return;
}
const ClipboardBinding current = *binding;
LG_UNLOCK_SHARED(clipboard.activeLock);
bool notice;
bool release;
uint32_t generation;
uint64_t fileDataset;
LG_ClipboardRequest transfer;
LG_LOCK(clipboard.requestLock);
LG_LOCK(clipboard.stateLock);
clipboard.remoteNotice = true;
clipboard.remoteType = type;
const bool previousNotice = clipboard.remoteNotice;
const bool usable = !hasFiles || clipboard.remoteFileDataset;
clipboard.remoteNotice = usable;
clipboard.remoteType = usable ? type : LG_CLIPBOARD_DATA_NONE;
clipboard.remoteGeneration = nextGeneration(
clipboard.remoteGeneration);
generation = clipboard.remoteGeneration;
if (!hasFiles || !usable)
clipboard.remoteFileDataset = 0;
fileDataset = clipboard.remoteFileDataset;
transfer = clearRemoteRequestNL();
notice = clipboard.localAvailable && g_params.clipboardToLocal;
notice = usable && clipboard.localAvailable &&
g_params.clipboardToLocal;
release = !usable && previousNotice && clipboard.localAvailable &&
g_params.clipboardToLocal;
LG_UNLOCK(clipboard.stateLock);
LG_UNLOCK(clipboard.requestLock);
if (!hasFiles)
lgClipboardFiles_remoteClear();
LG_UNLOCK(clipboard.fileLock);
LG_LOCK(clipboard.callbackLock);
cancelRequestsNL(&current, generation, false,
@@ -498,12 +535,21 @@ static void eventNotice(void * opaque,
notice = notice && clipboard.localAvailable &&
clipboard.remoteNotice &&
clipboard.remoteGeneration == generation;
release = release && clipboard.localAvailable &&
!clipboard.remoteNotice &&
clipboard.remoteGeneration == generation;
LG_UNLOCK(clipboard.stateLock);
LG_LOCK_SHARED(clipboard.activeLock);
const bool active = bindingActiveNL(&current);
LG_UNLOCK_SHARED(clipboard.activeLock);
if (notice && active)
g_state.ds->cbNotice(type);
{
if (type != LG_CLIPBOARD_DATA_FILES ||
lgClipboardFiles_remoteReady(fileDataset))
g_state.ds->cbNotice(type);
}
else if (release && active && g_state.ds->cbRelease)
g_state.ds->cbRelease();
LG_UNLOCK(clipboard.callbackLock);
}
@@ -892,14 +938,19 @@ static void eventRelease(void * opaque)
LG_LOCK(clipboard.stateLock);
release = clipboard.remoteNotice && clipboard.localAvailable &&
g_params.clipboardToLocal;
clipboard.remoteNotice = false;
clipboard.remoteType = LG_CLIPBOARD_DATA_NONE;
clipboard.remoteGeneration = nextGeneration(
clipboard.remoteNotice = false;
clipboard.remoteType = LG_CLIPBOARD_DATA_NONE;
clipboard.remoteFileDataset = 0;
clipboard.remoteGeneration = nextGeneration(
clipboard.remoteGeneration);
transfer = clearRemoteRequestNL();
LG_UNLOCK(clipboard.stateLock);
LG_UNLOCK(clipboard.requestLock);
LG_LOCK(clipboard.fileLock);
lgClipboardFiles_remoteClear();
LG_UNLOCK(clipboard.fileLock);
LG_LOCK(clipboard.callbackLock);
cancelRequestsNL(NULL, 0, true, LG_CLIPBOARD_CANCEL_REPLACED);
if (transfer != LG_CLIPBOARD_REQUEST_INVALID &&
@@ -1024,6 +1075,163 @@ static void eventRequestCancel(void * opaque, LG_ClipboardRequest id,
LG_UNLOCK(clipboard.callbackLock);
}
static bool fileEventBindingActive(void * opaque)
{
ClipboardBinding * binding = opaque;
LG_LOCK_SHARED(clipboard.activeLock);
const bool active = bindingActiveNL(binding);
LG_UNLOCK_SHARED(clipboard.activeLock);
return active;
}
static void eventFileOffer(void * opaque, uint64_t dataset)
{
LG_LOCK(clipboard.fileLock);
const bool active = fileEventBindingActive(opaque);
if (!active || !dataset)
{
LG_UNLOCK(clipboard.fileLock);
return;
}
LG_LOCK(clipboard.stateLock);
clipboard.remoteFileDataset = dataset;
LG_UNLOCK(clipboard.stateLock);
if (!lgClipboardFiles_remoteOffer(dataset))
{
LG_LOCK(clipboard.stateLock);
if (clipboard.remoteFileDataset == dataset)
clipboard.remoteFileDataset = 0;
LG_UNLOCK(clipboard.stateLock);
lgClipboardFiles_remoteClear();
}
LG_UNLOCK(clipboard.fileLock);
}
static void eventFileAcquire(void * opaque, uint64_t dataset,
uint64_t acquisition)
{
LG_LOCK(clipboard.fileLock);
if (!fileEventBindingActive(opaque))
{
LG_UNLOCK(clipboard.fileLock);
return;
}
lgClipboardFiles_localAcquire(dataset, acquisition);
LG_UNLOCK(clipboard.fileLock);
}
static void eventFileAcquired(void * opaque, uint64_t dataset,
uint64_t acquisition, LG_ClipboardFileError error)
{
LG_LOCK(clipboard.fileLock);
if (!fileEventBindingActive(opaque))
{
LG_UNLOCK(clipboard.fileLock);
return;
}
lgClipboardFiles_remoteAcquired(dataset, acquisition, error);
LG_UNLOCK(clipboard.fileLock);
}
static void eventFileRelease(void * opaque, uint64_t dataset,
uint64_t acquisition)
{
LG_LOCK(clipboard.fileLock);
if (!fileEventBindingActive(opaque))
{
LG_UNLOCK(clipboard.fileLock);
return;
}
lgClipboardFiles_localRelease(dataset, acquisition);
LG_UNLOCK(clipboard.fileLock);
}
static void eventFileRequest(void * opaque,
const LG_ClipboardFileRequest * request)
{
LG_LOCK(clipboard.fileLock);
if (!fileEventBindingActive(opaque))
{
LG_UNLOCK(clipboard.fileLock);
return;
}
lgClipboardFiles_localRequest(request);
LG_UNLOCK(clipboard.fileLock);
}
static LG_ClipboardResult eventFileDataBegin(void * opaque,
const LG_ClipboardFileRequest * request, uint64_t sizeHint)
{
LG_LOCK(clipboard.fileLock);
if (!fileEventBindingActive(opaque))
{
LG_UNLOCK(clipboard.fileLock);
return LG_CLIPBOARD_RESULT_FAILED;
}
const LG_ClipboardResult result =
lgClipboardFiles_remoteDataBegin(request, sizeHint);
LG_UNLOCK(clipboard.fileLock);
return result;
}
static LG_ClipboardResult eventFileDataChunk(void * opaque,
const LG_ClipboardFileRequest * request, uint64_t responseOffset,
const void * data, size_t size)
{
LG_LOCK(clipboard.fileLock);
if (!fileEventBindingActive(opaque))
{
LG_UNLOCK(clipboard.fileLock);
return LG_CLIPBOARD_RESULT_FAILED;
}
const LG_ClipboardResult result = lgClipboardFiles_remoteDataChunk(
request, responseOffset, data, size);
LG_UNLOCK(clipboard.fileLock);
return result;
}
static LG_ClipboardResult eventFileDataEnd(void * opaque,
const LG_ClipboardFileRequest * request, uint64_t finalSize)
{
LG_LOCK(clipboard.fileLock);
if (!fileEventBindingActive(opaque))
{
LG_UNLOCK(clipboard.fileLock);
return LG_CLIPBOARD_RESULT_FAILED;
}
const LG_ClipboardResult result =
lgClipboardFiles_remoteDataEnd(request, finalSize);
LG_UNLOCK(clipboard.fileLock);
return result;
}
static void eventFileDataReady(void * opaque, uint64_t request)
{
LG_LOCK(clipboard.fileLock);
if (!fileEventBindingActive(opaque))
{
LG_UNLOCK(clipboard.fileLock);
return;
}
lgClipboardFiles_localReady(request);
LG_UNLOCK(clipboard.fileLock);
}
static void eventFileCancel(void * opaque, uint64_t dataset,
uint64_t request, LG_ClipboardFileError reason)
{
LG_LOCK(clipboard.fileLock);
if (!fileEventBindingActive(opaque))
{
LG_UNLOCK(clipboard.fileLock);
return;
}
lgClipboardFiles_remoteCancel(dataset, request, reason);
lgClipboardFiles_localCancel(dataset, request, reason);
LG_UNLOCK(clipboard.fileLock);
}
static const LG_ClipboardEventOps eventOps =
{
.notice = eventNotice,
@@ -1036,6 +1244,16 @@ static const LG_ClipboardEventOps eventOps =
.requestCancel = eventRequestCancel,
.release = eventRelease,
.request = eventRequest,
.fileOffer = eventFileOffer,
.fileAcquire = eventFileAcquire,
.fileAcquired = eventFileAcquired,
.fileRelease = eventFileRelease,
.fileRequest = eventFileRequest,
.fileDataBegin = eventFileDataBegin,
.fileDataChunk = eventFileDataChunk,
.fileDataEnd = eventFileDataEnd,
.fileDataReady = eventFileDataReady,
.fileCancel = eventFileCancel,
};
/* providerLock must be held. dropActive suppresses all calls into an endpoint
@@ -1168,7 +1386,7 @@ static void setBinding(ClipboardBinding * target,
LG_UNLOCK(clipboard.registrationLock);
}
void lgClipboard_init(void)
bool lgClipboard_init(void)
{
memset(&clipboard, 0, sizeof(clipboard));
LG_LOCK_INIT(clipboard.registrationLock);
@@ -1178,9 +1396,16 @@ void lgClipboard_init(void)
LG_LOCK_INIT(clipboard.requestLock);
LG_LOCK_INIT(clipboard.writeLock);
LG_LOCK_INIT(clipboard.callbackLock);
LG_LOCK_INIT(clipboard.fileLock);
clipboard.remoteType = LG_CLIPBOARD_DATA_NONE;
clipboard.remoteRequestType = LG_CLIPBOARD_DATA_NONE;
clipboard.requests = ll_new();
if (!lgClipboardFiles_init())
{
DEBUG_ERROR("Failed to initialize clipboard file transfer");
return false;
}
return true;
}
void lgClipboard_free(void)
@@ -1188,6 +1413,7 @@ void lgClipboard_free(void)
lgClipboard_setLocalAvailable(false);
lgClipboard_setTransport(NULL, NULL);
lgClipboard_setFallback(NULL, NULL);
lgClipboardFiles_free();
LG_LOCK(clipboard.callbackLock);
cancelRequestsNL(NULL, 0, true, LG_CLIPBOARD_CANCEL_UNAVAILABLE);
LG_UNLOCK(clipboard.callbackLock);
@@ -1201,6 +1427,7 @@ void lgClipboard_free(void)
LG_LOCK_FREE(clipboard.requestLock);
LG_LOCK_FREE(clipboard.stateLock);
LG_LOCK_FREE(clipboard.callbackLock);
LG_LOCK_FREE(clipboard.fileLock);
LG_RWLOCK_FREE(clipboard.activeLock);
LG_LOCK_FREE(clipboard.providerLock);
LG_LOCK_FREE(clipboard.registrationLock);
@@ -1211,6 +1438,7 @@ void lgClipboard_setLocalAvailable(bool available)
bool notice;
bool release;
LG_ClipboardData type;
uint64_t fileDataset;
LG_ClipboardRequest transfer = LG_CLIPBOARD_REQUEST_INVALID;
LG_LOCK(clipboard.callbackLock);
@@ -1222,13 +1450,18 @@ void lgClipboard_setLocalAvailable(bool available)
notice = available && clipboard.remoteNotice &&
g_params.clipboardToLocal;
type = clipboard.remoteType;
fileDataset = clipboard.remoteFileDataset;
if (!available)
transfer = clearRemoteRequestNL();
LG_UNLOCK(clipboard.stateLock);
LG_UNLOCK(clipboard.requestLock);
if (notice)
g_state.ds->cbNotice(type);
{
if (type != LG_CLIPBOARD_DATA_FILES ||
lgClipboardFiles_remoteReady(fileDataset))
g_state.ds->cbNotice(type);
}
if (!available)
{
cancelRequestsNL(NULL, 0, true, LG_CLIPBOARD_CANCEL_UNAVAILABLE);
@@ -1285,6 +1518,7 @@ void lgClipboard_release(void)
LG_LOCK(clipboard.writeLock);
LG_LOCK(clipboard.stateLock);
clipboard.localTypeCount = 0;
clipboard.localFileDataset = 0;
clearRemoteRequestNL();
LG_UNLOCK(clipboard.stateLock);
@@ -1310,13 +1544,14 @@ void lgClipboard_notifyTypes(
return;
for (size_t i = 0; i < count; ++i)
if (!validType(types[i]))
if (!validType(types[i]) || types[i] == LG_CLIPBOARD_DATA_FILES)
return;
LG_LOCK(clipboard.writeLock);
LG_LOCK(clipboard.stateLock);
memcpy(clipboard.localTypes, types, count * sizeof(*types));
clipboard.localTypeCount = count;
clipboard.localFileDataset = 0;
clearRemoteRequestNL();
LG_UNLOCK(clipboard.stateLock);
@@ -1329,6 +1564,30 @@ void lgClipboard_notifyTypes(
LG_UNLOCK(clipboard.writeLock);
}
void lgClipboard_notifyFiles(uint64_t dataset)
{
if (!g_params.clipboardToVM || !dataset)
return;
LG_LOCK(clipboard.writeLock);
LG_LOCK(clipboard.stateLock);
clipboard.localTypes[0] = LG_CLIPBOARD_DATA_FILES;
clipboard.localTypeCount = 1;
clipboard.localFileDataset = dataset;
clearRemoteRequestNL();
LG_UNLOCK(clipboard.stateLock);
LG_LOCK_SHARED(clipboard.activeLock);
const bool result = !clipboard.active.ops ||
(clipboard.active.ops->offerFiles ?
clipboard.active.ops->offerFiles(clipboard.active.opaque, dataset) :
clipboard.active.ops->release(clipboard.active.opaque));
if (!result)
DEBUG_WARN("Failed to publish the local clipboard file dataset");
LG_UNLOCK_SHARED(clipboard.activeLock);
LG_UNLOCK(clipboard.writeLock);
}
/* writeLock and activeLock must be held. */
static bool cancelRemoteNL(LG_ClipboardCancelReason reason)
{
@@ -1864,3 +2123,139 @@ bool lgClipboard_request(LG_ClipboardData type,
return false;
return requestClipboard(type, replyFn, NULL, opaque, NULL);
}
bool lgClipboard_fileAcquire(uint64_t dataset, uint64_t acquisition)
{
bool result = false;
LG_LOCK_SHARED(clipboard.activeLock);
if (clipboard.active.ops && clipboard.active.ops->fileAcquire)
result = clipboard.active.ops->fileAcquire(
clipboard.active.opaque, dataset, acquisition);
LG_UNLOCK_SHARED(clipboard.activeLock);
return result;
}
bool lgClipboard_fileAcquired(uint64_t dataset, uint64_t acquisition,
LG_ClipboardFileError error)
{
bool result = false;
LG_LOCK_SHARED(clipboard.activeLock);
if (clipboard.active.ops && clipboard.active.ops->fileAcquired)
result = clipboard.active.ops->fileAcquired(
clipboard.active.opaque, dataset, acquisition, error);
LG_UNLOCK_SHARED(clipboard.activeLock);
return result;
}
bool lgClipboard_fileRelease(uint64_t dataset, uint64_t acquisition)
{
bool result = false;
LG_LOCK_SHARED(clipboard.activeLock);
if (clipboard.active.ops && clipboard.active.ops->fileRelease)
result = clipboard.active.ops->fileRelease(
clipboard.active.opaque, dataset, acquisition);
LG_UNLOCK_SHARED(clipboard.activeLock);
return result;
}
bool lgClipboard_fileRequest(const LG_ClipboardFileRequest * request)
{
bool result = false;
LG_LOCK_SHARED(clipboard.activeLock);
if (clipboard.active.ops && clipboard.active.ops->fileRequest)
result = clipboard.active.ops->fileRequest(
clipboard.active.opaque, request);
LG_UNLOCK_SHARED(clipboard.activeLock);
return result;
}
LG_ClipboardResult lgClipboard_fileDataBegin(
const LG_ClipboardFileRequest * request, uint64_t sizeHint)
{
LG_ClipboardResult result = LG_CLIPBOARD_RESULT_FAILED;
LG_LOCK_SHARED(clipboard.activeLock);
if (clipboard.active.ops && clipboard.active.ops->fileDataBegin)
result = clipboard.active.ops->fileDataBegin(
clipboard.active.opaque, request, sizeHint);
LG_UNLOCK_SHARED(clipboard.activeLock);
return result;
}
LG_ClipboardResult lgClipboard_fileDataChunk(
const LG_ClipboardFileRequest * request, uint64_t responseOffset,
const void * data, size_t size)
{
LG_ClipboardResult result = LG_CLIPBOARD_RESULT_FAILED;
LG_LOCK_SHARED(clipboard.activeLock);
if (clipboard.active.ops && clipboard.active.ops->fileDataChunk)
result = clipboard.active.ops->fileDataChunk(
clipboard.active.opaque, request, responseOffset, data, size);
LG_UNLOCK_SHARED(clipboard.activeLock);
return result;
}
LG_ClipboardResult lgClipboard_fileDataEnd(
const LG_ClipboardFileRequest * request, uint64_t finalSize)
{
LG_ClipboardResult result = LG_CLIPBOARD_RESULT_FAILED;
LG_LOCK_SHARED(clipboard.activeLock);
if (clipboard.active.ops && clipboard.active.ops->fileDataEnd)
result = clipboard.active.ops->fileDataEnd(
clipboard.active.opaque, request, finalSize);
LG_UNLOCK_SHARED(clipboard.activeLock);
return result;
}
bool lgClipboard_fileCancel(uint64_t dataset, uint64_t request,
LG_ClipboardFileError reason)
{
bool result = false;
LG_LOCK_SHARED(clipboard.activeLock);
if (clipboard.active.ops && clipboard.active.ops->fileCancel)
result = clipboard.active.ops->fileCancel(
clipboard.active.opaque, dataset, request, reason);
LG_UNLOCK_SHARED(clipboard.activeLock);
return result;
}
void lgClipboard_fileRemoteReady(uint64_t dataset)
{
LG_LOCK(clipboard.callbackLock);
LG_LOCK(clipboard.stateLock);
const bool notice = clipboard.localAvailable &&
g_params.clipboardToLocal && clipboard.remoteNotice &&
clipboard.remoteType == LG_CLIPBOARD_DATA_FILES &&
clipboard.remoteFileDataset == dataset;
LG_UNLOCK(clipboard.stateLock);
if (notice && g_state.ds->cbNotice)
g_state.ds->cbNotice(LG_CLIPBOARD_DATA_FILES);
LG_UNLOCK(clipboard.callbackLock);
}
void lgClipboard_fileRemoteFailed(uint64_t dataset)
{
if (!dataset)
return;
LG_LOCK(clipboard.callbackLock);
LG_LOCK(clipboard.stateLock);
const bool matchingDataset =
clipboard.remoteFileDataset == dataset;
const bool matchingNotice = matchingDataset && clipboard.remoteNotice &&
clipboard.remoteType == LG_CLIPBOARD_DATA_FILES;
const bool release = matchingNotice && clipboard.localAvailable &&
g_params.clipboardToLocal;
if (matchingDataset)
clipboard.remoteFileDataset = 0;
if (matchingNotice)
{
clipboard.remoteNotice = false;
clipboard.remoteType = LG_CLIPBOARD_DATA_NONE;
clipboard.remoteGeneration = nextGeneration(
clipboard.remoteGeneration);
}
LG_UNLOCK(clipboard.stateLock);
if (release && g_state.ds->cbRelease)
g_state.ds->cbRelease();
LG_UNLOCK(clipboard.callbackLock);
}

View File

@@ -23,7 +23,7 @@
#include "interface/clipboard.h"
void lgClipboard_init(void);
bool lgClipboard_init(void);
void lgClipboard_free(void);
void lgClipboard_setLocalAvailable(bool available);
@@ -36,6 +36,7 @@ void lgClipboard_dropTransport(void);
void lgClipboard_release(void);
void lgClipboard_notifyTypes(
const LG_ClipboardData types[], size_t count);
void lgClipboard_notifyFiles(uint64_t dataset);
LG_ClipboardResult lgClipboard_dataBegin(LG_ClipboardRequest request,
LG_ClipboardData type, uint64_t sizeHint);
LG_ClipboardResult lgClipboard_dataChunk(LG_ClipboardRequest request,
@@ -55,4 +56,22 @@ bool lgClipboard_requestReady(LG_ClipboardRequest request);
bool lgClipboard_request(LG_ClipboardData type,
LG_ClipboardReplyFn replyFn, void * opaque);
/* File-dataset transport entry points used by clipboard_files.c. */
bool lgClipboard_fileAcquire(uint64_t dataset, uint64_t acquisition);
bool lgClipboard_fileAcquired(uint64_t dataset, uint64_t acquisition,
LG_ClipboardFileError error);
bool lgClipboard_fileRelease(uint64_t dataset, uint64_t acquisition);
bool lgClipboard_fileRequest(const LG_ClipboardFileRequest * request);
LG_ClipboardResult lgClipboard_fileDataBegin(
const LG_ClipboardFileRequest * request, uint64_t sizeHint);
LG_ClipboardResult lgClipboard_fileDataChunk(
const LG_ClipboardFileRequest * request, uint64_t responseOffset,
const void * data, size_t size);
LG_ClipboardResult lgClipboard_fileDataEnd(
const LG_ClipboardFileRequest * request, uint64_t finalSize);
bool lgClipboard_fileCancel(uint64_t dataset, uint64_t request,
LG_ClipboardFileError reason);
void lgClipboard_fileRemoteReady(uint64_t dataset);
void lgClipboard_fileRemoteFailed(uint64_t dataset);
#endif

3703
client/src/clipboard_files.c Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,86 @@
/**
* Looking Glass
* Copyright © 2017-2026 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc., 59
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef _H_LG_CLIENT_CLIPBOARD_FILES_
#define _H_LG_CLIENT_CLIPBOARD_FILES_
#include "interface/clipboard.h"
bool lgClipboardFiles_init(void);
void lgClipboardFiles_free(void);
/* Desktop backends install a local file selection after reading one of the
* standard URI clipboard representations. The payload is copied. */
bool lgClipboardFiles_setLocal(const char * mime,
const void * data, size_t size);
void lgClipboardFiles_clearLocal(void);
/* Returns an allocated desktop representation for the current guest offer. */
bool lgClipboardFiles_getRemote(const char * mime,
char ** data, size_t * size);
uint64_t lgClipboardFiles_remotePresentationAcquire(void);
bool lgClipboardFiles_getRemotePresentation(uint64_t presentation,
const char * mime, char ** data, size_t * size);
void lgClipboardFiles_remotePresentationDelivered(uint64_t presentation);
void lgClipboardFiles_remotePresentationRelease(uint64_t presentation);
bool lgClipboardFiles_remoteReady(uint64_t dataset);
/* Events delivered by the active clipboard transport. */
bool lgClipboardFiles_remoteOffer(uint64_t dataset);
void lgClipboardFiles_remoteClear(void);
void lgClipboardFiles_providerUnavailable(void);
void lgClipboardFiles_remoteAcquired(uint64_t dataset,
uint64_t acquisition, LG_ClipboardFileError error);
LG_ClipboardResult lgClipboardFiles_remoteDataBegin(
const LG_ClipboardFileRequest * request, uint64_t sizeHint);
LG_ClipboardResult lgClipboardFiles_remoteDataChunk(
const LG_ClipboardFileRequest * request, uint64_t responseOffset,
const void * data, size_t size);
LG_ClipboardResult lgClipboardFiles_remoteDataEnd(
const LG_ClipboardFileRequest * request, uint64_t finalSize);
void lgClipboardFiles_remoteCancel(uint64_t dataset,
uint64_t request, LG_ClipboardFileError reason);
void lgClipboardFiles_localAcquire(uint64_t dataset,
uint64_t acquisition);
void lgClipboardFiles_localRelease(uint64_t dataset,
uint64_t acquisition);
void lgClipboardFiles_localRequest(
const LG_ClipboardFileRequest * request);
void lgClipboardFiles_localCancel(uint64_t dataset,
uint64_t request, LG_ClipboardFileError reason);
void lgClipboardFiles_localReady(uint64_t request);
#ifdef ENABLE_TESTS
bool lgClipboardFiles_testInit(uint64_t nonce);
bool lgClipboardFiles_testFuseStopWake(void);
bool lgClipboardFiles_testUnsentRemoteOwnership(void);
size_t lgClipboardFiles_testRemoteDatasetCount(void);
void lgClipboardFiles_testExpireRemoteDeliveries(void);
bool lgClipboardFiles_testBeginRemoteLookup(uint64_t presentation);
void lgClipboardFiles_testEndRemoteLookup(uint64_t presentation);
bool lgClipboardFiles_testRemoteRead(uint64_t presentation,
uint64_t node, uint64_t offset, uint32_t length);
void lgClipboardFiles_testForceLocalEof(void);
bool lgClipboardFiles_testLocalRequest(
const LG_ClipboardFileRequest * request);
#endif
#endif

View File

@@ -3322,7 +3322,8 @@ static int lg_run(void)
frameTimingInit();
lgInput_init();
lgAudio_init();
lgClipboard_init();
if (!lgClipboard_init())
return -1;
#ifdef ENABLE_TESTS
memset(&l_testCapture, 0, sizeof(l_testCapture));
@@ -4018,8 +4019,6 @@ static void lg_shutdown(void)
e_startup = NULL;
}
lgClipboard_free();
app_releaseAllKeybinds();
ll_free(g_state.bindings);
@@ -4029,6 +4028,10 @@ static void lg_shutdown(void)
g_state.ds->free();
}
/* Clipboard sources own FUSE dataset references and are released by the
* display server before the filesystem is unmounted. */
lgClipboard_free();
// Input callbacks have stopped; the evdev state and overlays can go
evdev_free();