mirror of
https://github.com/gnif/LookingGlass.git
synced 2026-08-22 07:01:30 +00:00
[idd] clipboard: add direct synchronization
This commit is contained in:
1434
idd/LGIdd/transport/lgmp/CLGMPClipboardTransport.cpp
Normal file
1434
idd/LGIdd/transport/lgmp/CLGMPClipboardTransport.cpp
Normal file
File diff suppressed because it is too large
Load Diff
211
idd/LGIdd/transport/lgmp/CLGMPClipboardTransport.h
Normal file
211
idd/LGIdd/transport/lgmp/CLGMPClipboardTransport.h
Normal file
@@ -0,0 +1,211 @@
|
||||
/**
|
||||
* 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 "common/KVMFRClipboard.h"
|
||||
#include "common/LGMPConfig.h"
|
||||
|
||||
#include <Windows.h>
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
extern "C" {
|
||||
#include "lgmp/host.h"
|
||||
}
|
||||
|
||||
class CLGMPHost;
|
||||
|
||||
class CLGMPClipboardTransport final : public IClipboardSource
|
||||
{
|
||||
private:
|
||||
static constexpr unsigned MEMORY_COUNT = 2;
|
||||
static constexpr unsigned INTERNAL_TARGET_COUNT = 3;
|
||||
static constexpr ULONGLONG OWNER_LEASE_MS = 1000;
|
||||
static constexpr DWORD ACTIVE_POLL_MS = 5;
|
||||
static constexpr DWORD IDLE_POLL_MS = 50;
|
||||
static constexpr uint32_t SLOT_BYTES =
|
||||
sizeof(KVMFRClipboardSlotHeader) + KVMFR_CLIPBOARD_DATA_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 Grant
|
||||
{
|
||||
uint32_t generation = 0;
|
||||
bool offered = false;
|
||||
bool committed = false;
|
||||
};
|
||||
|
||||
struct PendingTarget
|
||||
{
|
||||
bool valid = false;
|
||||
bool acknowledge = false;
|
||||
int grant = -1;
|
||||
KVMFRClipboardMessage record = {};
|
||||
uint8_t data[KVMFR_CLIPBOARD_DATA_BYTES] = {};
|
||||
|
||||
void Clear()
|
||||
{
|
||||
valid = false;
|
||||
acknowledge = false;
|
||||
grant = -1;
|
||||
record = {};
|
||||
}
|
||||
};
|
||||
|
||||
CLGMPHost& m_host;
|
||||
|
||||
PLGMPHostQueue m_queue = nullptr;
|
||||
PLGMPMemory m_statusMemory [MEMORY_COUNT] = {};
|
||||
PLGMPMemory m_messageMemory[MEMORY_COUNT] = {};
|
||||
PLGMPMemory m_grantMemory [MEMORY_COUNT] = {};
|
||||
PLGMPMemory m_dataMemory [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_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_dataSerial = 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_clientClipboardGeneration = 0;
|
||||
uint64_t m_discardHelperToClient = 0;
|
||||
KVMFRClipboardMessage m_cachedClipboard = {};
|
||||
KVMFRClipboardMessage m_blockedOutbound = {};
|
||||
Transfer m_clientToHelper;
|
||||
Transfer m_helperToClient;
|
||||
Grant m_grants[MEMORY_COUNT];
|
||||
PendingTarget m_pendingTarget;
|
||||
KVMFRClipboardMessage m_internalTarget[INTERNAL_TARGET_COUNT] = {};
|
||||
unsigned m_internalTargetCount = 0;
|
||||
|
||||
bool Initialize();
|
||||
void DeInit();
|
||||
|
||||
static DWORD CALLBACK ThreadProc(void * context);
|
||||
void Thread();
|
||||
void Wake();
|
||||
|
||||
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);
|
||||
void ResetProtocol(bool keepClipboard);
|
||||
void QueueHelperClear();
|
||||
void QueueTransferCancel(const Transfer& transfer);
|
||||
void QueueStaleRequestCancel(
|
||||
const KVMFRClipboardMessage& request);
|
||||
bool QueueInternalTarget(const KVMFRClipboardMessage& record);
|
||||
bool PumpInternalTarget();
|
||||
|
||||
PLGMPMemory FindAvailable(PLGMPMemory (&memory)[MEMORY_COUNT]) const;
|
||||
PostResult PostForOwner(uint64_t udata, PLGMPMemory memory);
|
||||
bool PublishStatus();
|
||||
bool PostGrants();
|
||||
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, int grant);
|
||||
|
||||
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;
|
||||
};
|
||||
@@ -52,7 +52,8 @@ bool CLGMPHost::Initialize(CIVSHMEM& ivshmem)
|
||||
KVMFR_FEATURE_SETCURSORPOS |
|
||||
KVMFR_FEATURE_WINDOWSIZE |
|
||||
KVMFR_FEATURE_FRAME_SCHEDULE |
|
||||
KVMFR_FEATURE_INPUT;
|
||||
KVMFR_FEATURE_INPUT |
|
||||
KVMFR_FEATURE_CLIPBOARD;
|
||||
strncpy_s(kvmfr.hostver, LG_VERSION_STR, sizeof(kvmfr.hostver) - 1);
|
||||
ss.write(reinterpret_cast<const char *>(&kvmfr), sizeof(kvmfr));
|
||||
}
|
||||
|
||||
@@ -67,7 +67,8 @@ CLGMPTransport::CLGMPTransport(const TransportInstance& config) :
|
||||
m_config(config),
|
||||
m_control(m_host),
|
||||
m_frames(m_host, m_ivshmem),
|
||||
m_input(m_host)
|
||||
m_input(m_host),
|
||||
m_clipboard(m_host)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -90,10 +91,10 @@ bool CLGMPTransport::Initialize()
|
||||
if (!m_host.Initialize(m_ivshmem))
|
||||
return false;
|
||||
|
||||
// Preserve the existing shared-memory layout by appending input after the
|
||||
// frame and pointer queues and retained pointer state allocations.
|
||||
// Preserve the existing shared-memory layout by appending input and then
|
||||
// clipboard after the frame/pointer queues and retained pointer state.
|
||||
if (!m_frames.Initialize() || !m_control.Initialize() ||
|
||||
!m_input.Initialize())
|
||||
!m_input.Initialize() || !m_clipboard.Initialize())
|
||||
return false;
|
||||
|
||||
m_frames.SealMemoryLayout();
|
||||
@@ -242,6 +243,7 @@ void CLGMPTransport::Stop()
|
||||
Atomic::Store(m_ready, false, std::memory_order_release);
|
||||
Abort();
|
||||
m_hasActive = false;
|
||||
m_clipboard.Stop();
|
||||
m_input.Stop();
|
||||
}
|
||||
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
#include "Atomic.h"
|
||||
#include "transport/ITransport.h"
|
||||
#include "transport/lgmp/CIVSHMEM.h"
|
||||
#include "transport/lgmp/CLGMPClipboardTransport.h"
|
||||
#include "transport/lgmp/CLGMPControl.h"
|
||||
#include "transport/lgmp/CLGMPFrameTransport.h"
|
||||
#include "transport/lgmp/CLGMPHost.h"
|
||||
@@ -32,20 +33,21 @@
|
||||
class CLGMPTransport final : public ITransport
|
||||
{
|
||||
private:
|
||||
TransportInstance m_config;
|
||||
TransportInstance m_config;
|
||||
// Keep this declaration order. Destruction must release frame and control
|
||||
// allocations before the LGMP host and its IVSHMEM mapping are destroyed.
|
||||
CIVSHMEM m_ivshmem;
|
||||
CLGMPHost m_host;
|
||||
CLGMPControl m_control;
|
||||
CLGMPFrameTransport m_frames;
|
||||
CLGMPInputTransport m_input;
|
||||
CRecovery m_recovery;
|
||||
std::atomic<bool> m_ready = false;
|
||||
FrameCfg m_activeCfg;
|
||||
FrameCfg m_pendingCfg;
|
||||
bool m_hasActive = false;
|
||||
bool m_hasPending = false;
|
||||
CIVSHMEM m_ivshmem;
|
||||
CLGMPHost m_host;
|
||||
CLGMPControl m_control;
|
||||
CLGMPFrameTransport m_frames;
|
||||
CLGMPInputTransport m_input;
|
||||
CLGMPClipboardTransport m_clipboard;
|
||||
CRecovery m_recovery;
|
||||
std::atomic<bool> m_ready = false;
|
||||
FrameCfg m_activeCfg;
|
||||
FrameCfg m_pendingCfg;
|
||||
bool m_hasActive = false;
|
||||
bool m_hasPending = false;
|
||||
|
||||
public:
|
||||
explicit CLGMPTransport(const TransportInstance& config);
|
||||
@@ -76,4 +78,5 @@ public:
|
||||
IFrameSink * FrameSink() override { return &m_frames; }
|
||||
IControlSink * Control() override { return &m_control; }
|
||||
IInputSource * Input() override { return &m_input; }
|
||||
IClipboardSource * Clipboard() override { return &m_clipboard; }
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user