mirror of
https://github.com/gnif/LookingGlass.git
synced 2026-11-12 14:57:20 +00:00
[client] windows: implement file clipboard support
This commit is contained in:
@@ -27,6 +27,7 @@ target_link_libraries(displayserver_Windows
|
||||
lg_common
|
||||
user32
|
||||
gdi32
|
||||
ole32
|
||||
)
|
||||
|
||||
if(ENABLE_EGL)
|
||||
|
||||
@@ -29,9 +29,14 @@
|
||||
#include "common/debug.h"
|
||||
#include "common/windebug.h"
|
||||
#include "core/clipboard.h"
|
||||
#include "core/clipboard_files.h"
|
||||
#include "platform.h"
|
||||
#include "platform/windows/clipboard_files.h"
|
||||
|
||||
#define WINDOWS_CB_OPEN_ATTEMPTS 8
|
||||
#define WINDOWS_CB_OPEN_ATTEMPTS 8
|
||||
#define WINDOWS_CB_RETRY_INTERVAL_MS 50
|
||||
#define WINDOWS_CB_RETRY_TIMEOUT_MS 5000
|
||||
#define WINDOWS_CB_RETRY_TIMER_ID ((UINT_PTR)0x4C47)
|
||||
|
||||
enum WindowsClipboardMessage
|
||||
{
|
||||
@@ -47,6 +52,13 @@ enum WindowsClipboardOperationType
|
||||
WINDOWS_CB_OPERATION_DATA,
|
||||
};
|
||||
|
||||
enum WindowsClipboardCaptureResult
|
||||
{
|
||||
WINDOWS_CB_CAPTURE_RETRY,
|
||||
WINDOWS_CB_CAPTURE_TYPES,
|
||||
WINDOWS_CB_CAPTURE_FILES,
|
||||
};
|
||||
|
||||
struct WindowsClipboardOperation
|
||||
{
|
||||
struct WindowsClipboardOperation * next;
|
||||
@@ -79,15 +91,23 @@ struct WindowsClipboardState
|
||||
UINT pngFormat;
|
||||
UINT jfifFormat;
|
||||
UINT jpegFormat;
|
||||
IDataObject * remoteFiles;
|
||||
LG_ClipboardRequest canceledRequest;
|
||||
ULONGLONG retryDeadline;
|
||||
DWORD retrySequence;
|
||||
bool listener;
|
||||
bool accepting;
|
||||
bool oleInitialized;
|
||||
bool applyingRemote;
|
||||
bool pendingUpdate;
|
||||
};
|
||||
|
||||
static SRWLOCK windowsCBQueueLock = SRWLOCK_INIT;
|
||||
static struct WindowsClipboardState windowsCB;
|
||||
static _Atomic(uint64_t) windowsCBGeneration;
|
||||
|
||||
static void updateLocalClipboard(void);
|
||||
|
||||
static uint16_t readLE16(const uint8_t * data)
|
||||
{
|
||||
return (uint16_t)data[0] |
|
||||
@@ -557,6 +577,8 @@ static bool localTypeAvailable(LG_ClipboardData type)
|
||||
IsClipboardFormatAvailable(windowsCB.jpegFormat));
|
||||
|
||||
case LG_CLIPBOARD_DATA_FILES:
|
||||
return IsClipboardFormatAvailable(CF_HDROP);
|
||||
|
||||
case LG_CLIPBOARD_DATA_NONE:
|
||||
return false;
|
||||
}
|
||||
@@ -649,36 +671,294 @@ static bool readLocalClipboard(LG_ClipboardData type,
|
||||
return true;
|
||||
}
|
||||
|
||||
static void releaseRemoteFilesReference(void)
|
||||
{
|
||||
if (!windowsCB.remoteFiles)
|
||||
return;
|
||||
|
||||
windowsCB.remoteFiles->lpVtbl->Release(windowsCB.remoteFiles);
|
||||
windowsCB.remoteFiles = NULL;
|
||||
}
|
||||
|
||||
static bool remoteFilesAreCurrent(void)
|
||||
{
|
||||
return windowsCB.oleInitialized && windowsCB.remoteFiles &&
|
||||
OleIsCurrentClipboard(windowsCB.remoteFiles) == S_OK;
|
||||
}
|
||||
|
||||
static HRESULT setOleClipboard(IDataObject * object)
|
||||
{
|
||||
HRESULT status = E_FAIL;
|
||||
DWORD delay = 1;
|
||||
// OleSetClipboard can synchronously dispatch a clipboard notification.
|
||||
windowsCB.applyingRemote = true;
|
||||
for (unsigned int attempt = 0;
|
||||
attempt < WINDOWS_CB_OPEN_ATTEMPTS; ++attempt)
|
||||
{
|
||||
status = OleSetClipboard(object);
|
||||
if (SUCCEEDED(status))
|
||||
break;
|
||||
if (attempt + 1U < WINDOWS_CB_OPEN_ATTEMPTS)
|
||||
{
|
||||
Sleep(delay);
|
||||
delay *= 2U;
|
||||
}
|
||||
}
|
||||
windowsCB.applyingRemote = false;
|
||||
if (windowsCB.pendingUpdate && windowsCB.accepting && windowsCB.window)
|
||||
{
|
||||
windowsCB.pendingUpdate = false;
|
||||
if (!PostMessageW(windowsCB.window, WM_CLIPBOARDUPDATE, 0, 0))
|
||||
DEBUG_WINERROR("Failed to replay a Windows clipboard update",
|
||||
GetLastError());
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
static bool clearRemoteFilesClipboard(void)
|
||||
{
|
||||
if (!windowsCB.remoteFiles)
|
||||
return true;
|
||||
|
||||
if (remoteFilesAreCurrent())
|
||||
{
|
||||
const HRESULT status = setOleClipboard(NULL);
|
||||
if (FAILED(status))
|
||||
{
|
||||
DEBUG_WINERROR("Failed to clear the Windows file clipboard", status);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
releaseRemoteFilesReference();
|
||||
return true;
|
||||
}
|
||||
|
||||
struct WindowsClipboardCapture
|
||||
{
|
||||
LG_ClipboardData types[LG_CLIPBOARD_DATA_NONE];
|
||||
size_t count;
|
||||
uint32_t mask;
|
||||
uint8_t * fileData;
|
||||
size_t fileSize;
|
||||
HWND owner;
|
||||
DWORD sequence;
|
||||
};
|
||||
|
||||
static enum WindowsClipboardCaptureResult captureLocalClipboard(
|
||||
HWND owner, DWORD sequence, struct WindowsClipboardCapture * capture)
|
||||
{
|
||||
memset(capture, 0, sizeof(*capture));
|
||||
// Explorer can still own the clipboard when WM_CLIPBOARDUPDATE arrives.
|
||||
// Acquire it before probing formats so that generation is retried instead
|
||||
// of being recorded as an empty clipboard.
|
||||
if (!openClipboard())
|
||||
return WINDOWS_CB_CAPTURE_RETRY;
|
||||
|
||||
if (owner != GetClipboardOwner() ||
|
||||
sequence != GetClipboardSequenceNumber())
|
||||
{
|
||||
CloseClipboard();
|
||||
return WINDOWS_CB_CAPTURE_RETRY;
|
||||
}
|
||||
|
||||
const bool files = localTypeAvailable(LG_CLIPBOARD_DATA_FILES);
|
||||
bool copied = false;
|
||||
if (files)
|
||||
{
|
||||
HANDLE handle = GetClipboardData(CF_HDROP);
|
||||
copied = handle && copyGlobalData(handle,
|
||||
&capture->fileData, &capture->fileSize);
|
||||
}
|
||||
else
|
||||
{
|
||||
for (LG_ClipboardData type = LG_CLIPBOARD_DATA_TEXT;
|
||||
type < LG_CLIPBOARD_DATA_FILES; ++type)
|
||||
{
|
||||
if (!localTypeAvailable(type))
|
||||
continue;
|
||||
|
||||
capture->types[capture->count++] = type;
|
||||
capture->mask |= UINT32_C(1) << type;
|
||||
}
|
||||
}
|
||||
|
||||
capture->owner = GetClipboardOwner();
|
||||
capture->sequence = GetClipboardSequenceNumber();
|
||||
const bool sequenceChanged = capture->sequence != sequence;
|
||||
const BOOL closed = CloseClipboard();
|
||||
const DWORD closeError = closed ? ERROR_SUCCESS : GetLastError();
|
||||
const HWND currentOwner = GetClipboardOwner();
|
||||
const DWORD currentSequence = GetClipboardSequenceNumber();
|
||||
const bool stable = closed && capture->owner == currentOwner &&
|
||||
capture->sequence == currentSequence && capture->owner == owner &&
|
||||
(!sequenceChanged || (files && owner));
|
||||
if (!stable || (files && !copied))
|
||||
{
|
||||
if (!closed)
|
||||
DEBUG_WINERROR("Failed to close the Windows clipboard", closeError);
|
||||
free(capture->fileData);
|
||||
capture->fileData = NULL;
|
||||
return WINDOWS_CB_CAPTURE_RETRY;
|
||||
}
|
||||
|
||||
return files ? WINDOWS_CB_CAPTURE_FILES : WINDOWS_CB_CAPTURE_TYPES;
|
||||
}
|
||||
|
||||
static void clearLocalRetry(void)
|
||||
{
|
||||
if (windowsCB.window && windowsCB.retryDeadline)
|
||||
KillTimer(windowsCB.window, WINDOWS_CB_RETRY_TIMER_ID);
|
||||
windowsCB.retryDeadline = 0;
|
||||
windowsCB.retrySequence = 0;
|
||||
}
|
||||
|
||||
static void expireLocalRetry(DWORD sequence)
|
||||
{
|
||||
clearLocalRetry();
|
||||
if (!windowsCB.accepting || remoteFilesAreCurrent())
|
||||
return;
|
||||
if (sequence != GetClipboardSequenceNumber())
|
||||
{
|
||||
PostMessageW(windowsCB.window, WM_CLIPBOARDUPDATE, 0, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
windowsCB.localOwner = GetClipboardOwner();
|
||||
windowsCB.localSequence = sequence;
|
||||
windowsCB.localTypes = 0;
|
||||
clipboardFiles_clearLocal();
|
||||
clipboard_release();
|
||||
}
|
||||
|
||||
static void deferLocalClipboard(DWORD sequence)
|
||||
{
|
||||
const ULONGLONG now = GetTickCount64();
|
||||
if (!windowsCB.retryDeadline || windowsCB.retrySequence != sequence)
|
||||
{
|
||||
clearLocalRetry();
|
||||
windowsCB.retrySequence = sequence;
|
||||
windowsCB.retryDeadline = now + WINDOWS_CB_RETRY_TIMEOUT_MS;
|
||||
}
|
||||
|
||||
if (now < windowsCB.retryDeadline &&
|
||||
SetTimer(windowsCB.window, WINDOWS_CB_RETRY_TIMER_ID,
|
||||
WINDOWS_CB_RETRY_INTERVAL_MS, NULL))
|
||||
return;
|
||||
|
||||
if (now >= windowsCB.retryDeadline)
|
||||
DEBUG_WARN("Timed out waiting for the Windows clipboard");
|
||||
else
|
||||
DEBUG_WINERROR("Failed to schedule a Windows clipboard retry",
|
||||
GetLastError());
|
||||
expireLocalRetry(sequence);
|
||||
}
|
||||
|
||||
static void retryLocalClipboard(void)
|
||||
{
|
||||
if (windowsCB.window)
|
||||
KillTimer(windowsCB.window, WINDOWS_CB_RETRY_TIMER_ID);
|
||||
if (!windowsCB.retryDeadline)
|
||||
return;
|
||||
if (!windowsCB.accepting || remoteFilesAreCurrent())
|
||||
{
|
||||
clearLocalRetry();
|
||||
return;
|
||||
}
|
||||
|
||||
const DWORD sequence = GetClipboardSequenceNumber();
|
||||
if (sequence != windowsCB.retrySequence)
|
||||
{
|
||||
clearLocalRetry();
|
||||
PostMessageW(windowsCB.window, WM_CLIPBOARDUPDATE, 0, 0);
|
||||
return;
|
||||
}
|
||||
if (GetTickCount64() >= windowsCB.retryDeadline)
|
||||
{
|
||||
DEBUG_WARN("Timed out waiting for the Windows clipboard");
|
||||
expireLocalRetry(sequence);
|
||||
return;
|
||||
}
|
||||
|
||||
updateLocalClipboard();
|
||||
}
|
||||
|
||||
static void updateLocalClipboard(void)
|
||||
{
|
||||
if (windowsCB.applyingRemote)
|
||||
{
|
||||
windowsCB.pendingUpdate = true;
|
||||
return;
|
||||
}
|
||||
|
||||
const HWND owner = GetClipboardOwner();
|
||||
const DWORD sequence = GetClipboardSequenceNumber();
|
||||
if (remoteFilesAreCurrent())
|
||||
{
|
||||
clearLocalRetry();
|
||||
return;
|
||||
}
|
||||
releaseRemoteFilesReference();
|
||||
if (owner == windowsCB.window)
|
||||
return;
|
||||
if (owner == windowsCB.localOwner &&
|
||||
sequence == windowsCB.localSequence)
|
||||
return;
|
||||
|
||||
LG_ClipboardData types[LG_CLIPBOARD_DATA_NONE];
|
||||
size_t count = 0;
|
||||
uint32_t mask = 0;
|
||||
for (LG_ClipboardData type = LG_CLIPBOARD_DATA_TEXT;
|
||||
type < LG_CLIPBOARD_DATA_FILES; ++type)
|
||||
{
|
||||
if (!localTypeAvailable(type))
|
||||
continue;
|
||||
if (!windowsCB.retryDeadline || windowsCB.retrySequence != sequence)
|
||||
atomic_fetch_add_explicit(
|
||||
&windowsCBGeneration, 1, memory_order_acq_rel);
|
||||
|
||||
types[count++] = type;
|
||||
mask |= UINT32_C(1) << type;
|
||||
struct WindowsClipboardCapture capture;
|
||||
const enum WindowsClipboardCaptureResult result =
|
||||
captureLocalClipboard(owner, sequence, &capture);
|
||||
if (result == WINDOWS_CB_CAPTURE_RETRY)
|
||||
{
|
||||
if (owner != GetClipboardOwner() ||
|
||||
sequence != GetClipboardSequenceNumber())
|
||||
{
|
||||
clearLocalRetry();
|
||||
PostMessageW(windowsCB.window, WM_CLIPBOARDUPDATE, 0, 0);
|
||||
}
|
||||
else
|
||||
deferLocalClipboard(sequence);
|
||||
return;
|
||||
}
|
||||
|
||||
windowsCB.localOwner = owner;
|
||||
windowsCB.localSequence = sequence;
|
||||
windowsCB.localTypes = mask;
|
||||
atomic_fetch_add_explicit(
|
||||
&windowsCBGeneration, 1, memory_order_acq_rel);
|
||||
if (count)
|
||||
clipboard_notifyTypes(types, count);
|
||||
if (result == WINDOWS_CB_CAPTURE_FILES)
|
||||
{
|
||||
HGLOBAL duplicate = makeGlobalData(capture.fileData, capture.fileSize);
|
||||
free(capture.fileData);
|
||||
if (!duplicate)
|
||||
{
|
||||
deferLocalClipboard(capture.sequence);
|
||||
return;
|
||||
}
|
||||
|
||||
const bool published = windowsClipboardFiles_setLocalDrop(
|
||||
(HDROP)duplicate);
|
||||
GlobalFree(duplicate);
|
||||
if (!published)
|
||||
{
|
||||
DEBUG_WARN("Failed to capture the Windows file clipboard");
|
||||
deferLocalClipboard(capture.sequence);
|
||||
return;
|
||||
}
|
||||
|
||||
clearLocalRetry();
|
||||
windowsCB.localOwner = capture.owner;
|
||||
windowsCB.localSequence = capture.sequence;
|
||||
windowsCB.localTypes = UINT32_C(1) << LG_CLIPBOARD_DATA_FILES;
|
||||
return;
|
||||
}
|
||||
|
||||
clearLocalRetry();
|
||||
windowsCB.localOwner = capture.owner;
|
||||
windowsCB.localSequence = capture.sequence;
|
||||
windowsCB.localTypes = capture.mask;
|
||||
clipboardFiles_clearLocal();
|
||||
if (capture.count)
|
||||
clipboard_notifyTypes(capture.types, capture.count);
|
||||
else
|
||||
clipboard_release();
|
||||
}
|
||||
@@ -827,7 +1107,8 @@ static void handleRemoteData(
|
||||
}
|
||||
|
||||
if (operation->generation != atomic_load_explicit(
|
||||
&windowsCBGeneration, memory_order_acquire) || !openClipboard())
|
||||
&windowsCBGeneration, memory_order_acquire) ||
|
||||
!clearRemoteFilesClipboard() || !openClipboard())
|
||||
goto fail;
|
||||
if (operation->generation != atomic_load_explicit(
|
||||
&windowsCBGeneration, memory_order_acquire) || !EmptyClipboard())
|
||||
@@ -835,7 +1116,6 @@ static void handleRemoteData(
|
||||
CloseClipboard();
|
||||
goto fail;
|
||||
}
|
||||
|
||||
bool published = false;
|
||||
for (size_t i = 0; i < count; ++i)
|
||||
{
|
||||
@@ -853,6 +1133,7 @@ static void handleRemoteData(
|
||||
GetLastError());
|
||||
}
|
||||
CloseClipboard();
|
||||
clipboardFiles_clearLocal();
|
||||
windowsCB.localOwner = GetClipboardOwner();
|
||||
windowsCB.localSequence = GetClipboardSequenceNumber();
|
||||
windowsCB.localTypes = 0;
|
||||
@@ -865,12 +1146,96 @@ fail:
|
||||
GlobalFree(handles[i]);
|
||||
}
|
||||
|
||||
static void handleRemoteFiles(
|
||||
const struct WindowsClipboardOperation * operation)
|
||||
{
|
||||
if (!windowsCB.oleInitialized)
|
||||
{
|
||||
DEBUG_WARN("OLE is unavailable; cannot publish guest clipboard files");
|
||||
return;
|
||||
}
|
||||
|
||||
const uint64_t presentation =
|
||||
clipboardFiles_remotePresentationAcquire();
|
||||
if (!presentation)
|
||||
{
|
||||
DEBUG_WARN("Failed to acquire the guest clipboard file offer");
|
||||
return;
|
||||
}
|
||||
|
||||
IDataObject * object = NULL;
|
||||
if (!windowsClipboardFiles_createRemoteDataObject(
|
||||
presentation, &object) || !object)
|
||||
{
|
||||
clipboardFiles_remotePresentationRelease(presentation);
|
||||
DEBUG_WARN("Failed to create the guest clipboard file object");
|
||||
return;
|
||||
}
|
||||
|
||||
if (operation->generation != atomic_load_explicit(
|
||||
&windowsCBGeneration, memory_order_acquire))
|
||||
{
|
||||
object->lpVtbl->Release(object);
|
||||
return;
|
||||
}
|
||||
|
||||
const HRESULT status = setOleClipboard(object);
|
||||
if (FAILED(status))
|
||||
{
|
||||
DEBUG_WINERROR("Failed to publish guest clipboard files", status);
|
||||
object->lpVtbl->Release(object);
|
||||
return;
|
||||
}
|
||||
|
||||
if (operation->generation != atomic_load_explicit(
|
||||
&windowsCBGeneration, memory_order_acquire))
|
||||
{
|
||||
const HRESULT clearStatus = OleIsCurrentClipboard(object) == S_OK ?
|
||||
setOleClipboard(NULL) : S_OK;
|
||||
releaseRemoteFilesReference();
|
||||
if (FAILED(clearStatus) && OleIsCurrentClipboard(object) == S_OK)
|
||||
{
|
||||
DEBUG_WINERROR("Failed to withdraw stale guest clipboard files",
|
||||
clearStatus);
|
||||
windowsCB.remoteFiles = object;
|
||||
}
|
||||
else
|
||||
object->lpVtbl->Release(object);
|
||||
return;
|
||||
}
|
||||
|
||||
releaseRemoteFilesReference();
|
||||
windowsCB.remoteFiles = object;
|
||||
windowsCB.localOwner = GetClipboardOwner();
|
||||
windowsCB.localSequence = GetClipboardSequenceNumber();
|
||||
windowsCB.localTypes = 0;
|
||||
clipboardFiles_clearLocal();
|
||||
clipboardFiles_remotePresentationDelivered(presentation);
|
||||
}
|
||||
|
||||
static void handleRemoteRelease(
|
||||
const struct WindowsClipboardOperation * operation)
|
||||
{
|
||||
if (operation->generation != atomic_load_explicit(
|
||||
&windowsCBGeneration, memory_order_acquire) ||
|
||||
GetClipboardOwner() != windowsCB.window || !openClipboard())
|
||||
&windowsCBGeneration, memory_order_acquire))
|
||||
return;
|
||||
|
||||
if (windowsCB.remoteFiles)
|
||||
{
|
||||
const bool current = remoteFilesAreCurrent();
|
||||
if (!clearRemoteFilesClipboard())
|
||||
return;
|
||||
if (current)
|
||||
{
|
||||
windowsCB.localOwner = GetClipboardOwner();
|
||||
windowsCB.localSequence = GetClipboardSequenceNumber();
|
||||
windowsCB.localTypes = 0;
|
||||
clipboardFiles_clearLocal();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (GetClipboardOwner() != windowsCB.window || !openClipboard())
|
||||
return;
|
||||
|
||||
bool released = false;
|
||||
@@ -884,6 +1249,7 @@ static void handleRemoteRelease(
|
||||
windowsCB.localOwner = GetClipboardOwner();
|
||||
windowsCB.localSequence = GetClipboardSequenceNumber();
|
||||
windowsCB.localTypes = 0;
|
||||
clipboardFiles_clearLocal();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -997,7 +1363,7 @@ static void handleRemoteNotice(
|
||||
return;
|
||||
if (operation->dataType == LG_CLIPBOARD_DATA_FILES)
|
||||
{
|
||||
handleRemoteRelease(operation);
|
||||
handleRemoteFiles(operation);
|
||||
return;
|
||||
}
|
||||
if (operation->dataType >= LG_CLIPBOARD_DATA_NONE)
|
||||
@@ -1052,6 +1418,13 @@ static bool eventInit(HWND window)
|
||||
if (!window)
|
||||
return false;
|
||||
|
||||
const HRESULT oleStatus = OleInitialize(NULL);
|
||||
if (SUCCEEDED(oleStatus))
|
||||
windowsCB.oleInitialized = true;
|
||||
else
|
||||
DEBUG_WINERROR("Failed to initialize OLE for clipboard files",
|
||||
oleStatus);
|
||||
|
||||
windowsCB.pngFormat = RegisterClipboardFormatW(L"PNG");
|
||||
windowsCB.jfifFormat = RegisterClipboardFormatW(L"JFIF");
|
||||
windowsCB.jpegFormat = RegisterClipboardFormatW(L"JPEG");
|
||||
@@ -1060,6 +1433,11 @@ static bool eventInit(HWND window)
|
||||
{
|
||||
DEBUG_WINERROR("Failed to register Windows clipboard formats",
|
||||
GetLastError());
|
||||
if (windowsCB.oleInitialized)
|
||||
{
|
||||
OleUninitialize();
|
||||
windowsCB.oleInitialized = false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -1067,6 +1445,11 @@ static bool eventInit(HWND window)
|
||||
{
|
||||
DEBUG_WINERROR("Failed to register the Windows clipboard listener",
|
||||
GetLastError());
|
||||
if (windowsCB.oleInitialized)
|
||||
{
|
||||
OleUninitialize();
|
||||
windowsCB.oleInitialized = false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -1083,7 +1466,6 @@ static bool eventInit(HWND window)
|
||||
bool windowsCBHandleMessage(HWND window, UINT message,
|
||||
WPARAM wParam, LPARAM lParam, LRESULT * result)
|
||||
{
|
||||
(void)wParam;
|
||||
(void)lParam;
|
||||
|
||||
switch (message)
|
||||
@@ -1102,6 +1484,13 @@ bool windowsCBHandleMessage(HWND window, UINT message,
|
||||
updateLocalClipboard();
|
||||
*result = 0;
|
||||
return true;
|
||||
|
||||
case WM_TIMER:
|
||||
if ((UINT_PTR)wParam != WINDOWS_CB_RETRY_TIMER_ID)
|
||||
return false;
|
||||
retryLocalClipboard();
|
||||
*result = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
@@ -1113,6 +1502,7 @@ void windowsCBEventShutdown(void)
|
||||
bool listener;
|
||||
struct WindowsClipboardOperation * operations;
|
||||
|
||||
clearLocalRetry();
|
||||
AcquireSRWLockExclusive(&windowsCBQueueLock);
|
||||
window = windowsCB.window;
|
||||
listener = windowsCB.listener;
|
||||
@@ -1129,6 +1519,13 @@ void windowsCBEventShutdown(void)
|
||||
if (listener && window && !RemoveClipboardFormatListener(window))
|
||||
DEBUG_WINERROR("Failed to remove the Windows clipboard listener",
|
||||
GetLastError());
|
||||
if (!clearRemoteFilesClipboard())
|
||||
releaseRemoteFilesReference();
|
||||
if (windowsCB.oleInitialized)
|
||||
{
|
||||
OleUninitialize();
|
||||
windowsCB.oleInitialized = false;
|
||||
}
|
||||
freeOperations(operations);
|
||||
}
|
||||
|
||||
|
||||
@@ -61,7 +61,7 @@ bool clipboard_requestCancel(LG_ClipboardRequest request,
|
||||
bool clipboard_request(LG_ClipboardData type,
|
||||
LG_ClipboardReplyFn replyFn, void * opaque);
|
||||
|
||||
/* File-dataset transport entry points used by clipboard_files.c. */
|
||||
/* File-dataset transport entry points used by platform clipboard backends. */
|
||||
bool clipboard_fileAcquire(uint64_t dataset, uint64_t acquisition);
|
||||
bool clipboard_fileAcquired(uint64_t dataset, uint64_t acquisition,
|
||||
LG_ClipboardFileError error);
|
||||
|
||||
@@ -23,6 +23,10 @@
|
||||
|
||||
#include "interface/clipboard.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
bool clipboardFiles_init(void);
|
||||
void clipboardFiles_free(void);
|
||||
|
||||
@@ -83,4 +87,8 @@ bool clipboardFiles_testLocalRequest(
|
||||
const LG_ClipboardFileRequest * request);
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
43
client/include/platform/windows/clipboard_files.h
Normal file
43
client/include/platform/windows/clipboard_files.h
Normal file
@@ -0,0 +1,43 @@
|
||||
/**
|
||||
* 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_PLATFORM_WINDOWS_CLIPBOARD_FILES_
|
||||
#define _H_LG_PLATFORM_WINDOWS_CLIPBOARD_FILES_
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <windows.h>
|
||||
#include <ole2.h>
|
||||
#include <shellapi.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
bool windowsClipboardFiles_setLocalDrop(HDROP drop);
|
||||
bool windowsClipboardFiles_createRemoteDataObject(uint64_t presentation,
|
||||
IDataObject ** dataObject);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
88
client/include/platform/windows/clipboard_files_win32.h
Normal file
88
client/include/platform/windows/clipboard_files_win32.h
Normal file
@@ -0,0 +1,88 @@
|
||||
/**
|
||||
* 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_PLATFORM_WINDOWS_CLIPBOARD_FILES_WIN32_
|
||||
#define _H_LG_PLATFORM_WINDOWS_CLIPBOARD_FILES_WIN32_
|
||||
|
||||
#include "interface/clipboard.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <windows.h>
|
||||
#include <ole2.h>
|
||||
#include <shellapi.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct WindowsClipboardLocalFiles WindowsClipboardLocalFiles;
|
||||
typedef struct WindowsClipboardRemoteManifest WindowsClipboardRemoteManifest;
|
||||
|
||||
typedef struct WindowsClipboardRemoteProviderOps
|
||||
{
|
||||
HRESULT (*read )(void * opaque, uint64_t dataset, uint64_t acquisition,
|
||||
uint64_t node, uint64_t offset, void * data, ULONG length,
|
||||
ULONG * read);
|
||||
void (*release)(void * opaque, uint64_t dataset, uint64_t acquisition);
|
||||
}
|
||||
WindowsClipboardRemoteProviderOps;
|
||||
|
||||
/* Capture copies the selection metadata. List and read return malloc-owned
|
||||
* buffers which the caller releases with free(). */
|
||||
WindowsClipboardLocalFiles * windowsClipboardLocalFilesCapture(HDROP drop,
|
||||
LG_ClipboardFileError * error);
|
||||
void windowsClipboardLocalFilesRetain(WindowsClipboardLocalFiles * files);
|
||||
void windowsClipboardLocalFilesRelease(WindowsClipboardLocalFiles * files);
|
||||
bool windowsClipboardLocalFilesList(WindowsClipboardLocalFiles * files,
|
||||
uint64_t node, void ** data, size_t * size,
|
||||
LG_ClipboardFileError * error);
|
||||
bool windowsClipboardLocalFilesRead(WindowsClipboardLocalFiles * files,
|
||||
uint64_t node, uint64_t offset, uint32_t length, void ** data,
|
||||
size_t * size, LG_ClipboardFileError * error);
|
||||
|
||||
WindowsClipboardRemoteManifest * windowsClipboardRemoteManifestCreate(void);
|
||||
void windowsClipboardRemoteManifestFree(
|
||||
WindowsClipboardRemoteManifest * manifest);
|
||||
/* Call NextDirectory before each LIST request, then append the complete
|
||||
* response for the returned node. The first returned node is the wire root. */
|
||||
bool windowsClipboardRemoteManifestAppendList(
|
||||
WindowsClipboardRemoteManifest * manifest, uint64_t parent,
|
||||
const void * data, size_t size, LG_ClipboardFileError * error);
|
||||
bool windowsClipboardRemoteManifestNextDirectory(
|
||||
WindowsClipboardRemoteManifest * manifest, uint64_t * node);
|
||||
bool windowsClipboardRemoteManifestHasEntries(
|
||||
const WindowsClipboardRemoteManifest * manifest);
|
||||
|
||||
/* The data object owns one provider lease after a successful return. The
|
||||
* release callback runs once after both the object and all of its streams are
|
||||
* released. A failed call does not invoke it. */
|
||||
HRESULT windowsClipboardRemoteCreateDataObject(
|
||||
WindowsClipboardRemoteManifest * manifest, uint64_t dataset,
|
||||
uint64_t acquisition, const WindowsClipboardRemoteProviderOps * ops,
|
||||
void * opaque, IDataObject ** object);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -3,6 +3,7 @@ project(client_platform_Windows LANGUAGES C CXX)
|
||||
|
||||
add_library(client_platform_Windows STATIC
|
||||
clipboard_files.c
|
||||
clipboard_files_win32.c
|
||||
config.c
|
||||
font.cpp
|
||||
input.c
|
||||
@@ -16,6 +17,7 @@ target_link_libraries(client_platform_Windows
|
||||
ole32
|
||||
shell32
|
||||
shlwapi
|
||||
user32
|
||||
uuid
|
||||
)
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
2570
client/platform/Windows/clipboard_files_win32.c
Normal file
2570
client/platform/Windows/clipboard_files_win32.c
Normal file
File diff suppressed because it is too large
Load Diff
@@ -40,17 +40,6 @@ set_tests_properties(event-source-tests PROPERTIES
|
||||
TIMEOUT 10
|
||||
)
|
||||
|
||||
add_executable(windows-clipboard-files-tests
|
||||
clipboard_files_windows_test.c
|
||||
../platform/Windows/clipboard_files.c
|
||||
)
|
||||
add_test(NAME windows-clipboard-files-tests
|
||||
COMMAND windows-clipboard-files-tests
|
||||
)
|
||||
set_tests_properties(windows-clipboard-files-tests PROPERTIES
|
||||
TIMEOUT 10
|
||||
)
|
||||
|
||||
if(LG_CLIENT_PLATFORM_LINUX)
|
||||
add_executable(config-platform-tests
|
||||
config_platform_test.c
|
||||
|
||||
@@ -1,103 +0,0 @@
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
|
||||
#include "core/clipboard_files.h"
|
||||
#include "test.h"
|
||||
|
||||
static struct
|
||||
{
|
||||
uint64_t dataset;
|
||||
uint64_t transfer;
|
||||
LG_ClipboardFileError error;
|
||||
unsigned int acquired;
|
||||
unsigned int cancelled;
|
||||
}
|
||||
trace;
|
||||
|
||||
bool clipboard_fileAcquired(uint64_t dataset, uint64_t acquisition,
|
||||
LG_ClipboardFileError error)
|
||||
{
|
||||
trace.dataset = dataset;
|
||||
trace.transfer = acquisition;
|
||||
trace.error = error;
|
||||
++trace.acquired;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool clipboard_fileCancel(uint64_t dataset, uint64_t request,
|
||||
LG_ClipboardFileError reason)
|
||||
{
|
||||
trace.dataset = dataset;
|
||||
trace.transfer = request;
|
||||
trace.error = reason;
|
||||
++trace.cancelled;
|
||||
return true;
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
const LG_ClipboardFileRequest request =
|
||||
{
|
||||
.dataset = UINT64_C(0x1234),
|
||||
.request = UINT64_C(0x5678),
|
||||
.node = 1,
|
||||
.operation = LG_CLIPBOARD_FILE_READ,
|
||||
};
|
||||
|
||||
CHECK(clipboardFiles_init());
|
||||
CHECK(!clipboardFiles_remoteOffer(request.dataset));
|
||||
CHECK(!clipboardFiles_remoteReady(request.dataset));
|
||||
|
||||
char * data = (char *)(uintptr_t)1;
|
||||
size_t size = 1;
|
||||
CHECK(!clipboardFiles_getRemote("text/uri-list", &data, &size));
|
||||
CHECK(!data);
|
||||
CHECK(size == 0);
|
||||
|
||||
data = (char *)(uintptr_t)1;
|
||||
size = 1;
|
||||
CHECK(!clipboardFiles_getRemotePresentation(
|
||||
1, "text/uri-list", &data, &size));
|
||||
CHECK(!data);
|
||||
CHECK(size == 0);
|
||||
|
||||
clipboardFiles_localAcquire(request.dataset, request.request);
|
||||
CHECK(trace.acquired == 1);
|
||||
CHECK(trace.dataset == request.dataset);
|
||||
CHECK(trace.transfer == request.request);
|
||||
CHECK(trace.error == LG_CLIPBOARD_FILE_ERROR_NOT_SUPPORTED);
|
||||
|
||||
clipboardFiles_localRequest(&request);
|
||||
CHECK(trace.cancelled == 1);
|
||||
CHECK(trace.dataset == request.dataset);
|
||||
CHECK(trace.transfer == request.request);
|
||||
CHECK(trace.error == LG_CLIPBOARD_FILE_ERROR_NOT_SUPPORTED);
|
||||
|
||||
clipboardFiles_localRequest(NULL);
|
||||
CHECK(trace.cancelled == 1);
|
||||
CHECK(clipboardFiles_remoteDataBegin(&request, 0) ==
|
||||
LG_CLIPBOARD_RESULT_FAILED);
|
||||
CHECK(clipboardFiles_remoteDataChunk(&request, 0, NULL, 0) ==
|
||||
LG_CLIPBOARD_RESULT_FAILED);
|
||||
CHECK(clipboardFiles_remoteDataEnd(&request, 0) ==
|
||||
LG_CLIPBOARD_RESULT_FAILED);
|
||||
clipboardFiles_free();
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user