mirror of
https://github.com/gnif/LookingGlass.git
synced 2026-08-22 15:11:31 +00:00
Carry DATA and FILE_DATA only through the duplex SPSC streams. Keep LGMP queues for control and status. Remove grants, commits, ACKs, and queue-payload transport negotiation. Poll adaptively while ownership or release is active. Preserve FIFO release and graceful draining. Quarantine payloads crossed by a newer OFFER or CLEAR, and retire data-plane state on detach.
245 lines
7.9 KiB
C++
245 lines
7.9 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,
|
|
};
|
|
|
|
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 = {};
|
|
uint8_t data[KVMFR_CLIPBOARD_DATA_BYTES] = {};
|
|
|
|
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_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;
|
|
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();
|
|
bool QueueStreamTarget(const KVMFRClipboardMessage& record,
|
|
const uint8_t * data);
|
|
bool PumpStreamTarget();
|
|
bool DrainStream(bool& received);
|
|
bool ProcessStreamTarget(const KVMFRClipboardMessage& record,
|
|
const uint8_t * data);
|
|
|
|
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,
|
|
const uint8_t * data, 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;
|
|
};
|