mirror of
https://github.com/gnif/LookingGlass.git
synced 2026-08-22 15:11:31 +00:00
Extend the clipboard target boundary with ordered prefix delivery. Pass up to four queued LGMP stream records to the Helper ring in one operation, apply state only to the accepted prefix, and retain the remaining suffix for exact retries. The channel holds its write lock across the prefix and emits one pipe doorbell per stream window instead of one per record.
259 lines
8.2 KiB
C++
259 lines
8.2 KiB
C++
/**
|
|
* 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
|
|
*/
|
|
|
|
#pragma once
|
|
|
|
#include "CSRWLock.h"
|
|
#include "transport/IClipboardSource.h"
|
|
#include "transport/lgmp/CLGMPClipboardFiles.h"
|
|
|
|
#include "common/KVMFRClipboard.h"
|
|
#include "common/LGMPConfig.h"
|
|
|
|
#include <Windows.h>
|
|
|
|
#include <stdint.h>
|
|
|
|
extern "C" {
|
|
#include "lgmp/host.h"
|
|
#include "lgmp/stream.h"
|
|
}
|
|
|
|
class CLGMPHost;
|
|
|
|
class CLGMPClipboardTransport final : public IClipboardSource
|
|
{
|
|
private:
|
|
static constexpr unsigned MEMORY_COUNT =
|
|
KVMFR_CLIPBOARD_STREAM_SLOT_COUNT;
|
|
static constexpr unsigned INTERNAL_TARGET_COUNT =
|
|
CLGMPClipboardFiles::MAX_ACQUISITIONS +
|
|
CLGMPClipboardFiles::MAX_REQUESTS + 3;
|
|
static constexpr ULONGLONG OWNER_LEASE_MS = 1000;
|
|
static constexpr ULONGLONG STREAM_DRAIN_TIMEOUT_MS = 1000;
|
|
static constexpr DWORD ACTIVE_POLL_MS = 5;
|
|
static constexpr DWORD IDLE_POLL_MS = 50;
|
|
static constexpr unsigned STREAM_TARGET_COUNT =
|
|
KVMFR_CLIPBOARD_STREAM_SLOT_COUNT;
|
|
static constexpr unsigned STREAM_DRAIN_MAX = 64;
|
|
static constexpr uint32_t SLOT_BYTES =
|
|
KVMFR_CLIPBOARD_STREAM_RECORD_BYTES;
|
|
|
|
enum class PostResult
|
|
{
|
|
POSTED,
|
|
BUSY,
|
|
GONE,
|
|
FAILED,
|
|
};
|
|
|
|
enum class StreamDisposition
|
|
{
|
|
READY,
|
|
DISCARD,
|
|
STALE,
|
|
INVALID,
|
|
};
|
|
|
|
struct Transfer
|
|
{
|
|
uint64_t transfer = 0;
|
|
uint64_t clipboardGeneration = 0;
|
|
uint64_t nextOffset = 0;
|
|
uint64_t sizeHint = KVMFR_CLIPBOARD_SIZE_UNKNOWN;
|
|
KVMFRClipboardFormat format = KVMFR_CLIPBOARD_FORMAT_NONE;
|
|
uint32_t nextSequence = 0;
|
|
bool began = false;
|
|
|
|
void Clear() { *this = {}; }
|
|
bool Active() const { return transfer != 0; }
|
|
};
|
|
|
|
struct PendingTarget
|
|
{
|
|
bool valid = false;
|
|
bool acknowledge = false;
|
|
KVMFRClipboardMessage record = {};
|
|
|
|
void Clear()
|
|
{
|
|
valid = false;
|
|
acknowledge = false;
|
|
record = {};
|
|
}
|
|
};
|
|
|
|
struct StreamTarget
|
|
{
|
|
KVMFRClipboardMessage record = {};
|
|
uint8_t data[KVMFR_CLIPBOARD_DATA_BYTES] = {};
|
|
|
|
void Clear() { record = {}; }
|
|
};
|
|
|
|
CLGMPHost& m_host;
|
|
|
|
PLGMPHostQueue m_queue = nullptr;
|
|
PLGMPHostStream m_hostToClientStream = nullptr;
|
|
PLGMPHostStream m_clientToHostStream = nullptr;
|
|
PLGMPMemory m_statusMemory [MEMORY_COUNT] = {};
|
|
PLGMPMemory m_messageMemory[MEMORY_COUNT] = {};
|
|
|
|
CSRWLock m_lifecycleLock;
|
|
CSRWLock m_lock;
|
|
IClipboardTarget * m_target = nullptr;
|
|
HANDLE m_stopEvent = nullptr;
|
|
HANDLE m_wakeEvent = nullptr;
|
|
HANDLE m_pollTimer = nullptr;
|
|
HANDLE m_thread = nullptr;
|
|
|
|
bool m_available = false;
|
|
bool m_statusDirty = true;
|
|
bool m_cachedValid = false;
|
|
bool m_replayPending = false;
|
|
bool m_outboundBlocked = false;
|
|
bool m_ownerReleasing = false;
|
|
bool m_releaseClearHelper = false;
|
|
bool m_failed = false;
|
|
uint32_t m_endpointGeneration = 0;
|
|
uint32_t m_ownerClientID = 0;
|
|
uint32_t m_ownerGeneration = 0;
|
|
uint32_t m_statusSerial = 0;
|
|
uint32_t m_messageSerial = 0;
|
|
uint32_t m_helperFormats = 0;
|
|
uint32_t m_clientFormats = 0;
|
|
uint32_t m_blockedOwnerClientID = 0;
|
|
uint32_t m_blockedOwnerGeneration = 0;
|
|
uint64_t m_ownerDeadline = 0;
|
|
uint64_t m_ownerDrainDeadline = 0;
|
|
uint64_t m_clientClipboardGeneration = 0;
|
|
uint64_t m_discardClientToHelper = 0;
|
|
uint64_t m_discardHelperToClient = 0;
|
|
const char * m_ownerReleaseReason = nullptr;
|
|
KVMFRClipboardMessage m_cachedClipboard = {};
|
|
KVMFRClipboardMessage m_blockedOutbound = {};
|
|
KVMFRStreamDescriptor m_hostToClientDescriptor = {};
|
|
KVMFRStreamDescriptor m_clientToHostDescriptor = {};
|
|
Transfer m_clientToHelper;
|
|
Transfer m_helperToClient;
|
|
PendingTarget m_pendingTarget;
|
|
StreamTarget m_streamTarget[STREAM_TARGET_COUNT];
|
|
unsigned m_streamTargetHead = 0;
|
|
unsigned m_streamTargetCount = 0;
|
|
unsigned m_streamTargetPrepared = 0;
|
|
KVMFRClipboardMessage m_internalTarget[INTERNAL_TARGET_COUNT] = {};
|
|
unsigned m_internalTargetCount = 0;
|
|
CLGMPClipboardFiles m_files;
|
|
|
|
bool Initialize();
|
|
void DeInit();
|
|
bool InitializeStreams();
|
|
void DeInitStreams();
|
|
bool BindStreams(uint32_t clientID) const;
|
|
bool TryUnbindStreams();
|
|
void ForceUnbindStreams() const;
|
|
|
|
static DWORD CALLBACK ThreadProc(void * context);
|
|
void Thread();
|
|
void Wake() const;
|
|
|
|
bool IsOwner(uint32_t clientID, uint32_t generation) const;
|
|
bool OwnerSubscribed() const;
|
|
void RenewLease();
|
|
void BlockOutbound(const KVMFRClipboardMessage& record,
|
|
uint32_t ownerClientID, uint32_t ownerGeneration);
|
|
void ClearOutboundBlock();
|
|
bool BlockedOwnerLost(const KVMFRClipboardMessage& record) const;
|
|
void ReleaseOwner(const char * reason, bool clearHelper,
|
|
bool force = false);
|
|
void FinishOwnerRelease();
|
|
bool RetryOwnerRelease();
|
|
void ResetProtocol(bool keepClipboard);
|
|
void QueueHelperClear();
|
|
void QueueTransferCancel(const Transfer& transfer);
|
|
void QueueStaleRequestCancel(
|
|
const KVMFRClipboardMessage& request);
|
|
void QueueStaleFileRecord(const KVMFRClipboardMessage& record);
|
|
void QueueFileDisconnect();
|
|
bool QueueInternalTarget(const KVMFRClipboardMessage& record);
|
|
bool PumpInternalTarget();
|
|
void ClearStreamTargets();
|
|
void ClearQueuedStreamTargets();
|
|
bool QueueStreamTarget(const KVMFRClipboardMessage& record,
|
|
const uint8_t * data);
|
|
void PopStreamTargets(unsigned count);
|
|
StreamDisposition PreviewStreamTarget(
|
|
const KVMFRClipboardMessage& record, Transfer& transfer,
|
|
CLGMPClipboardFiles& files);
|
|
bool PublishStreamTargets();
|
|
bool RetryStreamTargets();
|
|
bool PumpStreamTarget();
|
|
bool DrainStream(bool& received);
|
|
|
|
PLGMPMemory FindAvailable(PLGMPMemory (&memory)[MEMORY_COUNT]) const;
|
|
PostResult PostForOwner(uint64_t udata, PLGMPMemory memory);
|
|
bool PublishStatus();
|
|
bool ReplayClipboard();
|
|
bool DrainMessage();
|
|
bool ProcessMessage(uint32_t clientID,
|
|
const KVMFRClipboardMessage& message);
|
|
bool RetryTarget();
|
|
void FinishTarget(bool accepted);
|
|
void DropPendingTarget();
|
|
void Fail(const char * operation, LGMP_STATUS status);
|
|
|
|
bool ValidateClaim(const KVMFRClipboardMessage& message) const;
|
|
bool ValidateOwnedControl(const KVMFRClipboardMessage& message) const;
|
|
bool ValidateInboundRecord(const KVMFRClipboardMessage& message) const;
|
|
bool ValidateOutboundRecord(const KVMFRClipboardMessage& message) const;
|
|
bool ValidateChunk(const Transfer& transfer,
|
|
const KVMFRClipboardMessage& message) const;
|
|
void AdvanceChunk(Transfer& transfer,
|
|
const KVMFRClipboardMessage& message);
|
|
void ApplyInbound(const KVMFRClipboardMessage& message);
|
|
void ApplyOutbound(const KVMFRClipboardMessage& message);
|
|
bool BeginTarget(const KVMFRClipboardMessage& message,
|
|
bool acknowledge);
|
|
|
|
ClipboardChannelResult SendControl(
|
|
const KVMFRClipboardMessage& record);
|
|
ClipboardChannelResult SendData(
|
|
const KVMFRClipboardMessage& record, const uint8_t * data);
|
|
|
|
friend class CLGMPTransport;
|
|
|
|
public:
|
|
explicit CLGMPClipboardTransport(CLGMPHost& host) :
|
|
m_host(host) {}
|
|
~CLGMPClipboardTransport() override;
|
|
|
|
CLGMPClipboardTransport(const CLGMPClipboardTransport&) = delete;
|
|
CLGMPClipboardTransport& operator=(
|
|
const CLGMPClipboardTransport&) = delete;
|
|
|
|
bool Start(IClipboardTarget& target) override;
|
|
void Stop() override;
|
|
void ClipboardState(bool available, uint32_t generation) override;
|
|
ClipboardChannelResult SendClipboard(
|
|
const KVMFRClipboardMessage& record,
|
|
const uint8_t * data) override;
|
|
void ClipboardReset(uint32_t generation, uint32_t reason) override;
|
|
};
|