mirror of
https://github.com/gnif/LookingGlass.git
synced 2026-08-22 07:01:30 +00:00
[idd] clipboard: carry payloads over streams
Create one reliable four-slot lane in each direction and advertise the stream data plane for clipboard protocol v4. Bind both lanes to the accepted owner generation, stage incoming data within a fixed window, and apply backpressure when Helper delivery is full. Drain reliable records before releasing an owner. Force stale or unresponsive bindings after a bounded deadline while preserving the existing cancellation and clipboard-clear cleanup.
This commit is contained in:
@@ -31,6 +31,16 @@ namespace
|
|||||||
{
|
{
|
||||||
static constexpr DWORD WAIT_FIRST_OBJECT_VALUE = 0;
|
static constexpr DWORD WAIT_FIRST_OBJECT_VALUE = 0;
|
||||||
|
|
||||||
|
static_assert(sizeof(LGMPStreamDescriptor) ==
|
||||||
|
sizeof(KVMFRStreamDescriptor),
|
||||||
|
"LGMP and KVMFR stream descriptor sizes differ");
|
||||||
|
static_assert(offsetof(LGMPStreamDescriptor, offset) ==
|
||||||
|
offsetof(KVMFRStreamDescriptor, offset),
|
||||||
|
"LGMP and KVMFR stream descriptor offsets differ");
|
||||||
|
static_assert(offsetof(LGMPStreamDescriptor, slotSize) ==
|
||||||
|
offsetof(KVMFRStreamDescriptor, slotSize),
|
||||||
|
"LGMP and KVMFR stream descriptor geometry differs");
|
||||||
|
|
||||||
static const LGMPQueueConfig CLIPBOARD_QUEUE_CONFIG =
|
static const LGMPQueueConfig CLIPBOARD_QUEUE_CONFIG =
|
||||||
{
|
{
|
||||||
LGMP_Q_CLIPBOARD,
|
LGMP_Q_CLIPBOARD,
|
||||||
@@ -39,12 +49,12 @@ namespace
|
|||||||
};
|
};
|
||||||
|
|
||||||
bool EmptyControl(const KVMFRClipboardMessage& message,
|
bool EmptyControl(const KVMFRClipboardMessage& message,
|
||||||
bool keepToken = false)
|
bool keepToken = false, bool keepFlags = false)
|
||||||
{
|
{
|
||||||
return !message.clipboardGeneration && !message.transfer &&
|
return !message.clipboardGeneration && !message.transfer &&
|
||||||
!message.offset && !message.size && !message.format &&
|
!message.offset && !message.size && !message.format &&
|
||||||
!message.flags && (keepToken || !message.token) && !message.length &&
|
(keepFlags || !message.flags) && (keepToken || !message.token) &&
|
||||||
!message.sequence;
|
!message.length && !message.sequence;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AddValid(uint64_t offset, uint32_t length)
|
bool AddValid(uint64_t offset, uint32_t length)
|
||||||
@@ -95,6 +105,22 @@ namespace
|
|||||||
message.type == KVMFR_CLIPBOARD_MESSAGE_CANCEL ||
|
message.type == KVMFR_CLIPBOARD_MESSAGE_CANCEL ||
|
||||||
CLGMPClipboardFiles::IsRecord(message);
|
CLGMPClipboardFiles::IsRecord(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
KVMFRStreamDescriptor StreamDescriptorToWire(
|
||||||
|
const LGMPStreamDescriptor& descriptor)
|
||||||
|
{
|
||||||
|
KVMFRStreamDescriptor wire = {};
|
||||||
|
wire.magic = descriptor.magic;
|
||||||
|
wire.version = descriptor.version;
|
||||||
|
wire.size = descriptor.size;
|
||||||
|
wire.offset = descriptor.offset;
|
||||||
|
wire.regionSize = descriptor.regionSize;
|
||||||
|
wire.direction = descriptor.direction;
|
||||||
|
wire.policy = descriptor.policy;
|
||||||
|
wire.slotCount = descriptor.slotCount;
|
||||||
|
wire.slotSize = descriptor.slotSize;
|
||||||
|
return wire;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
CLGMPClipboardTransport::~CLGMPClipboardTransport()
|
CLGMPClipboardTransport::~CLGMPClipboardTransport()
|
||||||
@@ -132,20 +158,10 @@ bool CLGMPClipboardTransport::Initialize()
|
|||||||
memset(lgmpHostMemPtr(memory), 0, sizeof(KVMFRClipboardMessage));
|
memset(lgmpHostMemPtr(memory), 0, sizeof(KVMFRClipboardMessage));
|
||||||
}
|
}
|
||||||
|
|
||||||
for (PLGMPMemory& memory : m_grantMemory)
|
if (!InitializeStreams())
|
||||||
{
|
{
|
||||||
status = m_host.Allocate(SLOT_BYTES, &memory);
|
DeInit();
|
||||||
if (status != LGMP_OK)
|
return false;
|
||||||
goto fail;
|
|
||||||
memset(lgmpHostMemPtr(memory), 0, SLOT_BYTES);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (PLGMPMemory& memory : m_dataMemory)
|
|
||||||
{
|
|
||||||
status = m_host.Allocate(SLOT_BYTES, &memory);
|
|
||||||
if (status != LGMP_OK)
|
|
||||||
goto fail;
|
|
||||||
memset(lgmpHostMemPtr(memory), 0, SLOT_BYTES);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@@ -160,6 +176,7 @@ fail:
|
|||||||
void CLGMPClipboardTransport::DeInit()
|
void CLGMPClipboardTransport::DeInit()
|
||||||
{
|
{
|
||||||
Stop();
|
Stop();
|
||||||
|
DeInitStreams();
|
||||||
for (PLGMPMemory& memory : m_dataMemory)
|
for (PLGMPMemory& memory : m_dataMemory)
|
||||||
lgmpHostMemFree(&memory);
|
lgmpHostMemFree(&memory);
|
||||||
for (PLGMPMemory& memory : m_grantMemory)
|
for (PLGMPMemory& memory : m_grantMemory)
|
||||||
@@ -171,6 +188,139 @@ void CLGMPClipboardTransport::DeInit()
|
|||||||
m_queue = nullptr;
|
m_queue = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool CLGMPClipboardTransport::InitializeStreams()
|
||||||
|
{
|
||||||
|
if (m_hostToClientStream && m_clientToHostStream)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
const LGMPStreamConfig hostToClient =
|
||||||
|
{
|
||||||
|
LGMP_STREAM_HOST_TO_CLIENT,
|
||||||
|
LGMP_STREAM_RELIABLE_FIFO,
|
||||||
|
KVMFR_CLIPBOARD_STREAM_SLOT_COUNT,
|
||||||
|
SLOT_BYTES,
|
||||||
|
};
|
||||||
|
LGMP_STATUS status = m_host.CreateStream(
|
||||||
|
hostToClient, &m_hostToClientStream);
|
||||||
|
if (status != LGMP_OK)
|
||||||
|
{
|
||||||
|
DEBUG_ERROR("Failed to create host-to-client clipboard stream: %s",
|
||||||
|
lgmpStatusString(status));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const LGMPStreamConfig clientToHost =
|
||||||
|
{
|
||||||
|
LGMP_STREAM_CLIENT_TO_HOST,
|
||||||
|
LGMP_STREAM_RELIABLE_FIFO,
|
||||||
|
KVMFR_CLIPBOARD_STREAM_SLOT_COUNT,
|
||||||
|
SLOT_BYTES,
|
||||||
|
};
|
||||||
|
status = m_host.CreateStream(clientToHost, &m_clientToHostStream);
|
||||||
|
if (status != LGMP_OK)
|
||||||
|
{
|
||||||
|
DEBUG_ERROR("Failed to create client-to-host clipboard stream: %s",
|
||||||
|
lgmpStatusString(status));
|
||||||
|
lgmpHostStreamFree(&m_hostToClientStream);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
LGMPStreamDescriptor descriptor = {};
|
||||||
|
lgmpHostStreamGetDescriptor(m_hostToClientStream, &descriptor);
|
||||||
|
m_hostToClientDescriptor = StreamDescriptorToWire(descriptor);
|
||||||
|
lgmpHostStreamGetDescriptor(m_clientToHostStream, &descriptor);
|
||||||
|
m_clientToHostDescriptor = StreamDescriptorToWire(descriptor);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CLGMPClipboardTransport::DeInitStreams()
|
||||||
|
{
|
||||||
|
ForceUnbindStreams();
|
||||||
|
lgmpHostStreamFree(&m_clientToHostStream);
|
||||||
|
lgmpHostStreamFree(&m_hostToClientStream);
|
||||||
|
m_clientToHostDescriptor = {};
|
||||||
|
m_hostToClientDescriptor = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CLGMPClipboardTransport::BindStreams(uint32_t clientID)
|
||||||
|
{
|
||||||
|
if (!m_hostToClientStream || !m_clientToHostStream)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
LGMP_STATUS status = lgmpHostStreamBind(
|
||||||
|
m_hostToClientStream, clientID, nullptr);
|
||||||
|
if (status != LGMP_OK)
|
||||||
|
{
|
||||||
|
DEBUG_ERROR("Failed to bind host-to-client clipboard stream for "
|
||||||
|
"client %u: %s", clientID, lgmpStatusString(status));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
status = lgmpHostStreamBind(m_clientToHostStream, clientID, nullptr);
|
||||||
|
if (status == LGMP_OK)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
DEBUG_ERROR("Failed to bind client-to-host clipboard stream for "
|
||||||
|
"client %u: %s", clientID, lgmpStatusString(status));
|
||||||
|
const LGMP_STATUS rollback =
|
||||||
|
lgmpHostStreamUnbind(m_hostToClientStream);
|
||||||
|
if (rollback != LGMP_OK && rollback != LGMP_ERR_STREAM_UNBOUND &&
|
||||||
|
rollback != LGMP_ERR_STREAM_BUSY)
|
||||||
|
DEBUG_ERROR("Failed to roll back host-to-client clipboard stream "
|
||||||
|
"binding: %s", lgmpStatusString(rollback));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CLGMPClipboardTransport::TryUnbindStreams()
|
||||||
|
{
|
||||||
|
bool complete = true;
|
||||||
|
if (m_clientToHostStream)
|
||||||
|
{
|
||||||
|
const LGMP_STATUS status =
|
||||||
|
lgmpHostStreamUnbind(m_clientToHostStream);
|
||||||
|
if (status == LGMP_ERR_STREAM_BUSY)
|
||||||
|
complete = false;
|
||||||
|
else if (status != LGMP_OK && status != LGMP_ERR_STREAM_UNBOUND)
|
||||||
|
{
|
||||||
|
Fail("lgmpHostStreamUnbind(client-to-host)", status);
|
||||||
|
complete = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (m_hostToClientStream)
|
||||||
|
{
|
||||||
|
const LGMP_STATUS status =
|
||||||
|
lgmpHostStreamUnbind(m_hostToClientStream);
|
||||||
|
if (status == LGMP_ERR_STREAM_BUSY)
|
||||||
|
complete = false;
|
||||||
|
else if (status != LGMP_OK && status != LGMP_ERR_STREAM_UNBOUND)
|
||||||
|
{
|
||||||
|
Fail("lgmpHostStreamUnbind(host-to-client)", status);
|
||||||
|
complete = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return complete;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CLGMPClipboardTransport::ForceUnbindStreams()
|
||||||
|
{
|
||||||
|
if (m_clientToHostStream)
|
||||||
|
{
|
||||||
|
const LGMP_STATUS status =
|
||||||
|
lgmpHostStreamForceUnbind(m_clientToHostStream);
|
||||||
|
if (status != LGMP_OK && status != LGMP_ERR_STREAM_UNBOUND)
|
||||||
|
DEBUG_ERROR("Failed to force-unbind client-to-host clipboard "
|
||||||
|
"stream: %s", lgmpStatusString(status));
|
||||||
|
}
|
||||||
|
if (m_hostToClientStream)
|
||||||
|
{
|
||||||
|
const LGMP_STATUS status =
|
||||||
|
lgmpHostStreamForceUnbind(m_hostToClientStream);
|
||||||
|
if (status != LGMP_OK && status != LGMP_ERR_STREAM_UNBOUND)
|
||||||
|
DEBUG_ERROR("Failed to force-unbind host-to-client clipboard "
|
||||||
|
"stream: %s", lgmpStatusString(status));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void CLGMPClipboardTransport::Wake()
|
void CLGMPClipboardTransport::Wake()
|
||||||
{
|
{
|
||||||
if (m_wakeEvent)
|
if (m_wakeEvent)
|
||||||
@@ -312,7 +462,8 @@ bool CLGMPClipboardTransport::BlockedOwnerLost(
|
|||||||
{
|
{
|
||||||
return m_outboundBlocked && OwnerScopedLifecycle(record) &&
|
return m_outboundBlocked && OwnerScopedLifecycle(record) &&
|
||||||
memcmp(&m_blockedOutbound, &record, sizeof(record)) == 0 &&
|
memcmp(&m_blockedOutbound, &record, sizeof(record)) == 0 &&
|
||||||
(!m_available || record.generation != m_endpointGeneration ||
|
(!m_available || m_ownerReleasing ||
|
||||||
|
record.generation != m_endpointGeneration ||
|
||||||
m_blockedOwnerClientID != m_ownerClientID ||
|
m_blockedOwnerClientID != m_ownerClientID ||
|
||||||
m_blockedOwnerGeneration != m_ownerGeneration);
|
m_blockedOwnerGeneration != m_ownerGeneration);
|
||||||
}
|
}
|
||||||
@@ -428,34 +579,231 @@ bool CLGMPClipboardTransport::PumpInternalTarget()
|
|||||||
return BeginTarget(record, nullptr, -2);
|
return BeginTarget(record, nullptr, -2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CLGMPClipboardTransport::ClearStreamTargets()
|
||||||
|
{
|
||||||
|
for (StreamTarget& target : m_streamTarget)
|
||||||
|
target.Clear();
|
||||||
|
m_streamTargetHead = 0;
|
||||||
|
m_streamTargetCount = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CLGMPClipboardTransport::QueueStreamTarget(
|
||||||
|
const KVMFRClipboardMessage& record, const uint8_t * data)
|
||||||
|
{
|
||||||
|
if (m_streamTargetCount == STREAM_TARGET_COUNT ||
|
||||||
|
(record.length && !data))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
StreamTarget& target = m_streamTarget[
|
||||||
|
(m_streamTargetHead + m_streamTargetCount) % STREAM_TARGET_COUNT];
|
||||||
|
target.record = record;
|
||||||
|
if (record.length)
|
||||||
|
memcpy(target.data, data, record.length);
|
||||||
|
++m_streamTargetCount;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CLGMPClipboardTransport::ProcessStreamTarget(
|
||||||
|
const KVMFRClipboardMessage& record, const uint8_t * data)
|
||||||
|
{
|
||||||
|
if (!m_ownerClientID ||
|
||||||
|
m_ownerTransport != KVMFR_CLIPBOARD_TRANSPORT_STREAM)
|
||||||
|
return true;
|
||||||
|
if (m_ownerReleasing && !m_releaseClearHelper)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if (record.type == KVMFR_CLIPBOARD_MESSAGE_DATA &&
|
||||||
|
record.transfer == m_discardClientToHelper)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
const bool fileData =
|
||||||
|
record.type == KVMFR_CLIPBOARD_MESSAGE_FILE_DATA;
|
||||||
|
const bool staleFileData = fileData && m_files.IsStaleTerminal(
|
||||||
|
record, CLGMPClipboardFiles::Direction::CLIENT_TO_HELPER);
|
||||||
|
const bool validData = fileData ?
|
||||||
|
(staleFileData || m_files.Validate(record,
|
||||||
|
CLGMPClipboardFiles::Direction::CLIENT_TO_HELPER, 0, false)) :
|
||||||
|
(!record.token && kvmfrClipboardTransferFromHelper(record.transfer) &&
|
||||||
|
ValidateChunk(m_clientToHelper, record));
|
||||||
|
if (!validData)
|
||||||
|
{
|
||||||
|
ReleaseOwner("invalid clipboard stream data", true);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!m_ownerReleasing)
|
||||||
|
RenewLease();
|
||||||
|
if (staleFileData)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
KVMFRClipboardMessage forwarded = record;
|
||||||
|
forwarded.generation = m_endpointGeneration;
|
||||||
|
if (!BeginTarget(forwarded, data, -2))
|
||||||
|
{
|
||||||
|
ReleaseOwner("failed to stage clipboard stream data", true);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CLGMPClipboardTransport::PumpStreamTarget()
|
||||||
|
{
|
||||||
|
while (!m_pendingTarget.valid && m_streamTargetCount)
|
||||||
|
{
|
||||||
|
StreamTarget& target = m_streamTarget[m_streamTargetHead];
|
||||||
|
const KVMFRClipboardMessage record = target.record;
|
||||||
|
const uint8_t * data = record.length ? target.data : nullptr;
|
||||||
|
target.Clear();
|
||||||
|
m_streamTargetHead =
|
||||||
|
(m_streamTargetHead + 1) % STREAM_TARGET_COUNT;
|
||||||
|
--m_streamTargetCount;
|
||||||
|
if (!ProcessStreamTarget(record, data))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CLGMPClipboardTransport::DrainStream(bool& received)
|
||||||
|
{
|
||||||
|
received = false;
|
||||||
|
if (!m_ownerClientID ||
|
||||||
|
m_ownerTransport != KVMFR_CLIPBOARD_TRANSPORT_STREAM ||
|
||||||
|
!m_clientToHostStream)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
for (unsigned drained = 0;
|
||||||
|
drained < STREAM_DRAIN_MAX &&
|
||||||
|
m_streamTargetCount < STREAM_TARGET_COUNT; ++drained)
|
||||||
|
{
|
||||||
|
LGMPStreamBuffer buffer = {};
|
||||||
|
LGMP_STATUS status =
|
||||||
|
lgmpHostStreamReadPeek(m_clientToHostStream, &buffer);
|
||||||
|
if (status == LGMP_ERR_STREAM_EMPTY)
|
||||||
|
break;
|
||||||
|
if (status == LGMP_ERR_STREAM_UNBOUND ||
|
||||||
|
status == LGMP_ERR_STREAM_STALE)
|
||||||
|
{
|
||||||
|
if (m_ownerReleasing && status == LGMP_ERR_STREAM_UNBOUND)
|
||||||
|
break;
|
||||||
|
ReleaseOwner("clipboard stream binding lost", true);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (status != LGMP_OK)
|
||||||
|
{
|
||||||
|
Fail("lgmpHostStreamReadPeek", status);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
received = true;
|
||||||
|
|
||||||
|
KVMFRClipboardMessage record = {};
|
||||||
|
if (buffer.data && buffer.size >= sizeof(record))
|
||||||
|
memcpy(&record, buffer.data, sizeof(record));
|
||||||
|
const bool valid = buffer.data &&
|
||||||
|
buffer.size == sizeof(record) + record.length &&
|
||||||
|
record.version == KVMFR_CLIPBOARD_VERSION &&
|
||||||
|
record.generation == m_ownerGeneration &&
|
||||||
|
(record.type == KVMFR_CLIPBOARD_MESSAGE_DATA ||
|
||||||
|
record.type == KVMFR_CLIPBOARD_MESSAGE_FILE_DATA) &&
|
||||||
|
record.length <= KVMFR_CLIPBOARD_DATA_BYTES &&
|
||||||
|
!(record.flags & ~(KVMFR_CLIPBOARD_FLAG_BEGIN |
|
||||||
|
KVMFR_CLIPBOARD_FLAG_END)) &&
|
||||||
|
AddValid(record.offset, record.length);
|
||||||
|
const bool queued = valid && QueueStreamTarget(record,
|
||||||
|
static_cast<const uint8_t *>(buffer.data) + sizeof(record));
|
||||||
|
|
||||||
|
status = lgmpHostStreamReadRelease(m_clientToHostStream, &buffer);
|
||||||
|
if (status != LGMP_OK)
|
||||||
|
{
|
||||||
|
Fail("lgmpHostStreamReadRelease", status);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!queued)
|
||||||
|
{
|
||||||
|
ReleaseOwner("invalid clipboard stream record", true);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (!PumpStreamTarget())
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return PumpStreamTarget();
|
||||||
|
}
|
||||||
|
|
||||||
void CLGMPClipboardTransport::ReleaseOwner(
|
void CLGMPClipboardTransport::ReleaseOwner(
|
||||||
const char * reason, bool clearHelper)
|
const char * reason, bool clearHelper, bool force)
|
||||||
{
|
{
|
||||||
if (!m_ownerClientID)
|
if (!m_ownerClientID)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
const uint32_t clientID = m_ownerClientID;
|
if (!m_ownerReleasing)
|
||||||
const uint32_t generation = m_ownerGeneration;
|
{
|
||||||
|
m_ownerReleasing = true;
|
||||||
|
m_releaseClearHelper = clearHelper;
|
||||||
|
m_ownerReleaseReason = reason;
|
||||||
|
m_ownerDeadline = 0;
|
||||||
|
m_ownerDrainDeadline = GetTickCount64() + STREAM_DRAIN_TIMEOUT_MS;
|
||||||
|
m_replayPending = false;
|
||||||
|
ClearOutboundBlock();
|
||||||
|
for (Grant& grant : m_grants)
|
||||||
|
{
|
||||||
|
grant.generation = 0;
|
||||||
|
grant.offered = false;
|
||||||
|
grant.committed = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (force)
|
||||||
|
{
|
||||||
|
m_releaseClearHelper |= clearHelper;
|
||||||
|
m_ownerReleaseReason = reason;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_ownerTransport != KVMFR_CLIPBOARD_TRANSPORT_STREAM)
|
||||||
|
{
|
||||||
|
FinishOwnerRelease();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (force)
|
||||||
|
{
|
||||||
|
ForceUnbindStreams();
|
||||||
|
FinishOwnerRelease();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (TryUnbindStreams() && !m_pendingTarget.valid &&
|
||||||
|
!m_streamTargetCount)
|
||||||
|
FinishOwnerRelease();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CLGMPClipboardTransport::FinishOwnerRelease()
|
||||||
|
{
|
||||||
|
if (!m_ownerClientID)
|
||||||
|
return;
|
||||||
|
|
||||||
|
const uint32_t clientID = m_ownerClientID;
|
||||||
|
const uint32_t generation = m_ownerGeneration;
|
||||||
|
const char * const reason = m_ownerReleaseReason ?
|
||||||
|
m_ownerReleaseReason : "unspecified";
|
||||||
const Transfer clientToHelper = m_clientToHelper;
|
const Transfer clientToHelper = m_clientToHelper;
|
||||||
const Transfer helperToClient = m_helperToClient;
|
const Transfer helperToClient = m_helperToClient;
|
||||||
m_ownerClientID = 0;
|
const bool clearHelper = m_releaseClearHelper;
|
||||||
m_ownerGeneration = 0;
|
m_ownerClientID = 0;
|
||||||
m_ownerDeadline = 0;
|
m_ownerGeneration = 0;
|
||||||
m_replayPending = false;
|
m_ownerTransport = 0;
|
||||||
|
m_ownerReleasing = false;
|
||||||
|
m_releaseClearHelper = false;
|
||||||
|
m_ownerReleaseReason = nullptr;
|
||||||
|
m_ownerDeadline = 0;
|
||||||
|
m_ownerDrainDeadline = 0;
|
||||||
|
m_replayPending = false;
|
||||||
|
ClearStreamTargets();
|
||||||
|
m_discardClientToHelper = 0;
|
||||||
m_clientToHelper.Clear();
|
m_clientToHelper.Clear();
|
||||||
m_helperToClient.Clear();
|
m_helperToClient.Clear();
|
||||||
if (helperToClient.Active())
|
if (helperToClient.Active())
|
||||||
m_discardHelperToClient = helperToClient.transfer;
|
m_discardHelperToClient = helperToClient.transfer;
|
||||||
for (Grant& grant : m_grants)
|
m_statusDirty = true;
|
||||||
{
|
|
||||||
grant.generation = 0;
|
|
||||||
grant.offered = false;
|
|
||||||
grant.committed = false;
|
|
||||||
}
|
|
||||||
m_statusDirty = true;
|
|
||||||
|
|
||||||
DEBUG_INFO("Clipboard owner %u generation %u released (%s)",
|
|
||||||
clientID, generation, reason);
|
|
||||||
if (clearHelper)
|
if (clearHelper)
|
||||||
{
|
{
|
||||||
QueueFileDisconnect();
|
QueueFileDisconnect();
|
||||||
@@ -469,6 +817,38 @@ void CLGMPClipboardTransport::ReleaseOwner(
|
|||||||
m_clientClipboardGeneration = 0;
|
m_clientClipboardGeneration = 0;
|
||||||
m_clientFormats = 0;
|
m_clientFormats = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DEBUG_INFO("Clipboard owner %u generation %u released (%s)",
|
||||||
|
clientID, generation, reason);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CLGMPClipboardTransport::RetryOwnerRelease()
|
||||||
|
{
|
||||||
|
if (!m_ownerReleasing)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if (m_ownerTransport != KVMFR_CLIPBOARD_TRANSPORT_STREAM)
|
||||||
|
{
|
||||||
|
FinishOwnerRelease();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (GetTickCount64() >= m_ownerDrainDeadline)
|
||||||
|
{
|
||||||
|
DEBUG_WARN("Timed out draining clipboard streams for owner %u "
|
||||||
|
"generation %u; discarding in-flight records",
|
||||||
|
m_ownerClientID, m_ownerGeneration);
|
||||||
|
DropPendingTarget();
|
||||||
|
ClearStreamTargets();
|
||||||
|
ForceUnbindStreams();
|
||||||
|
FinishOwnerRelease();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (TryUnbindStreams() && !m_pendingTarget.valid &&
|
||||||
|
!m_streamTargetCount)
|
||||||
|
FinishOwnerRelease();
|
||||||
|
return !m_failed;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CLGMPClipboardTransport::ResetProtocol(bool keepClipboard)
|
void CLGMPClipboardTransport::ResetProtocol(bool keepClipboard)
|
||||||
@@ -481,10 +861,12 @@ void CLGMPClipboardTransport::ResetProtocol(bool keepClipboard)
|
|||||||
m_files.Reset();
|
m_files.Reset();
|
||||||
m_clientClipboardGeneration = 0;
|
m_clientClipboardGeneration = 0;
|
||||||
m_clientFormats = 0;
|
m_clientFormats = 0;
|
||||||
|
m_discardClientToHelper = 0;
|
||||||
m_discardHelperToClient = 0;
|
m_discardHelperToClient = 0;
|
||||||
m_replayPending = false;
|
m_replayPending = false;
|
||||||
ClearOutboundBlock();
|
ClearOutboundBlock();
|
||||||
m_internalTargetCount = 0;
|
m_internalTargetCount = 0;
|
||||||
|
ClearStreamTargets();
|
||||||
for (KVMFRClipboardMessage& record : m_internalTarget)
|
for (KVMFRClipboardMessage& record : m_internalTarget)
|
||||||
record = {};
|
record = {};
|
||||||
if (!keepClipboard)
|
if (!keepClipboard)
|
||||||
@@ -561,10 +943,18 @@ bool CLGMPClipboardTransport::PublishStatus()
|
|||||||
return true;
|
return true;
|
||||||
|
|
||||||
KVMFRClipboardStatus clipboardStatus = {};
|
KVMFRClipboardStatus clipboardStatus = {};
|
||||||
clipboardStatus.version = KVMFR_CLIPBOARD_VERSION;
|
clipboardStatus.version = KVMFR_CLIPBOARD_VERSION;
|
||||||
clipboardStatus.generation = m_endpointGeneration;
|
clipboardStatus.generation = m_endpointGeneration;
|
||||||
clipboardStatus.lease = static_cast<uint32_t>(OWNER_LEASE_MS);
|
clipboardStatus.lease = static_cast<uint32_t>(OWNER_LEASE_MS);
|
||||||
clipboardStatus.slotBytes = KVMFR_CLIPBOARD_DATA_BYTES;
|
clipboardStatus.slotBytes = KVMFR_CLIPBOARD_DATA_BYTES;
|
||||||
|
if (m_hostToClientStream && m_clientToHostStream)
|
||||||
|
{
|
||||||
|
clipboardStatus.transports = KVMFR_CLIPBOARD_TRANSPORT_STREAM;
|
||||||
|
clipboardStatus.streamVersion = KVMFR_CLIPBOARD_STREAM_VERSION;
|
||||||
|
clipboardStatus.streamSlotCount = KVMFR_CLIPBOARD_STREAM_SLOT_COUNT;
|
||||||
|
clipboardStatus.hostToClient = m_hostToClientDescriptor;
|
||||||
|
clipboardStatus.clientToHost = m_clientToHostDescriptor;
|
||||||
|
}
|
||||||
if (m_available)
|
if (m_available)
|
||||||
{
|
{
|
||||||
clipboardStatus.flags |= KVMFR_CLIPBOARD_STATUS_AVAILABLE;
|
clipboardStatus.flags |= KVMFR_CLIPBOARD_STATUS_AVAILABLE;
|
||||||
@@ -575,6 +965,7 @@ bool CLGMPClipboardTransport::PublishStatus()
|
|||||||
clipboardStatus.flags |= KVMFR_CLIPBOARD_STATUS_HAS_OWNER;
|
clipboardStatus.flags |= KVMFR_CLIPBOARD_STATUS_HAS_OWNER;
|
||||||
clipboardStatus.ownerClientID = m_ownerClientID;
|
clipboardStatus.ownerClientID = m_ownerClientID;
|
||||||
clipboardStatus.ownerGeneration = m_ownerGeneration;
|
clipboardStatus.ownerGeneration = m_ownerGeneration;
|
||||||
|
clipboardStatus.ownerTransport = m_ownerTransport;
|
||||||
}
|
}
|
||||||
memcpy(lgmpHostMemPtr(memory), &clipboardStatus,
|
memcpy(lgmpHostMemPtr(memory), &clipboardStatus,
|
||||||
sizeof(clipboardStatus));
|
sizeof(clipboardStatus));
|
||||||
@@ -601,7 +992,8 @@ bool CLGMPClipboardTransport::PublishStatus()
|
|||||||
|
|
||||||
bool CLGMPClipboardTransport::PostGrants()
|
bool CLGMPClipboardTransport::PostGrants()
|
||||||
{
|
{
|
||||||
if (!m_available || !m_ownerClientID)
|
if (!m_available || !m_ownerClientID || m_ownerReleasing ||
|
||||||
|
m_ownerTransport != KVMFR_CLIPBOARD_TRANSPORT_LEGACY)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
for (unsigned i = 0; i < MEMORY_COUNT; ++i)
|
for (unsigned i = 0; i < MEMORY_COUNT; ++i)
|
||||||
@@ -631,7 +1023,7 @@ bool CLGMPClipboardTransport::PostGrants()
|
|||||||
return true;
|
return true;
|
||||||
else if (result == PostResult::GONE)
|
else if (result == PostResult::GONE)
|
||||||
{
|
{
|
||||||
ReleaseOwner("subscriber disappeared", true);
|
ReleaseOwner("subscriber disappeared", true, true);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -642,7 +1034,7 @@ bool CLGMPClipboardTransport::PostGrants()
|
|||||||
|
|
||||||
bool CLGMPClipboardTransport::ReplayClipboard()
|
bool CLGMPClipboardTransport::ReplayClipboard()
|
||||||
{
|
{
|
||||||
if (!m_replayPending || !m_ownerClientID)
|
if (!m_replayPending || !m_ownerClientID || m_ownerReleasing)
|
||||||
return true;
|
return true;
|
||||||
if (!m_cachedValid)
|
if (!m_cachedValid)
|
||||||
{
|
{
|
||||||
@@ -667,17 +1059,22 @@ bool CLGMPClipboardTransport::ReplayClipboard()
|
|||||||
m_replayPending = false;
|
m_replayPending = false;
|
||||||
}
|
}
|
||||||
else if (result == PostResult::GONE)
|
else if (result == PostResult::GONE)
|
||||||
ReleaseOwner("subscriber disappeared", true);
|
ReleaseOwner("subscriber disappeared", true, true);
|
||||||
return result != PostResult::FAILED;
|
return result != PostResult::FAILED;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CLGMPClipboardTransport::ValidateClaim(
|
bool CLGMPClipboardTransport::ValidateClaim(
|
||||||
const KVMFRClipboardMessage& message) const
|
const KVMFRClipboardMessage& message) const
|
||||||
{
|
{
|
||||||
|
KVMFRClipboardTransportFlags transports = 0;
|
||||||
|
if (m_hostToClientStream && m_clientToHostStream)
|
||||||
|
transports = KVMFR_CLIPBOARD_TRANSPORT_STREAM;
|
||||||
return message.version == KVMFR_CLIPBOARD_VERSION &&
|
return message.version == KVMFR_CLIPBOARD_VERSION &&
|
||||||
message.type == KVMFR_CLIPBOARD_MESSAGE_CLAIM &&
|
message.type == KVMFR_CLIPBOARD_MESSAGE_CLAIM &&
|
||||||
message.generation && message.token == m_endpointGeneration &&
|
message.generation && message.token == m_endpointGeneration &&
|
||||||
EmptyControl(message, true);
|
kvmfrClipboardTransportValid(message.flags) &&
|
||||||
|
(message.flags & transports) &&
|
||||||
|
EmptyControl(message, true, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CLGMPClipboardTransport::ValidateOwnedControl(
|
bool CLGMPClipboardTransport::ValidateOwnedControl(
|
||||||
@@ -707,7 +1104,8 @@ bool CLGMPClipboardTransport::ValidateOwnedControl(
|
|||||||
return ValidateInboundRecord(message);
|
return ValidateInboundRecord(message);
|
||||||
|
|
||||||
case KVMFR_CLIPBOARD_MESSAGE_COMMIT:
|
case KVMFR_CLIPBOARD_MESSAGE_COMMIT:
|
||||||
return message.token >= 1 && message.token <= MEMORY_COUNT &&
|
return m_ownerTransport == KVMFR_CLIPBOARD_TRANSPORT_LEGACY &&
|
||||||
|
message.token >= 1 && message.token <= MEMORY_COUNT &&
|
||||||
message.clipboardGeneration && message.transfer &&
|
message.clipboardGeneration && message.transfer &&
|
||||||
(kvmfrClipboardRepresentationFormatValid(message.format) ||
|
(kvmfrClipboardRepresentationFormatValid(message.format) ||
|
||||||
message.format == KVMFR_CLIPBOARD_FORMAT_FILES) &&
|
message.format == KVMFR_CLIPBOARD_FORMAT_FILES) &&
|
||||||
@@ -891,7 +1289,10 @@ void CLGMPClipboardTransport::ApplyInbound(
|
|||||||
|
|
||||||
case KVMFR_CLIPBOARD_MESSAGE_CANCEL:
|
case KVMFR_CLIPBOARD_MESSAGE_CANCEL:
|
||||||
if (m_clientToHelper.transfer == message.transfer)
|
if (m_clientToHelper.transfer == message.transfer)
|
||||||
|
{
|
||||||
|
m_discardClientToHelper = message.transfer;
|
||||||
m_clientToHelper.Clear();
|
m_clientToHelper.Clear();
|
||||||
|
}
|
||||||
if (m_helperToClient.transfer == message.transfer)
|
if (m_helperToClient.transfer == message.transfer)
|
||||||
m_helperToClient.Clear();
|
m_helperToClient.Clear();
|
||||||
break;
|
break;
|
||||||
@@ -930,6 +1331,7 @@ void CLGMPClipboardTransport::ApplyOutbound(
|
|||||||
|
|
||||||
case KVMFR_CLIPBOARD_MESSAGE_REQUEST:
|
case KVMFR_CLIPBOARD_MESSAGE_REQUEST:
|
||||||
m_clientToHelper.Clear();
|
m_clientToHelper.Clear();
|
||||||
|
m_discardClientToHelper = 0;
|
||||||
m_clientToHelper.transfer = message.transfer;
|
m_clientToHelper.transfer = message.transfer;
|
||||||
m_clientToHelper.clipboardGeneration =
|
m_clientToHelper.clipboardGeneration =
|
||||||
message.clipboardGeneration;
|
message.clipboardGeneration;
|
||||||
@@ -973,7 +1375,8 @@ void CLGMPClipboardTransport::FinishTarget(bool accepted)
|
|||||||
if (accepted)
|
if (accepted)
|
||||||
{
|
{
|
||||||
ApplyInbound(message);
|
ApplyInbound(message);
|
||||||
RenewLease();
|
if (m_ownerClientID && !m_ownerReleasing)
|
||||||
|
RenewLease();
|
||||||
}
|
}
|
||||||
if (grant >= 0 && static_cast<unsigned>(grant) < MEMORY_COUNT)
|
if (grant >= 0 && static_cast<unsigned>(grant) < MEMORY_COUNT)
|
||||||
m_grants[grant] = {};
|
m_grants[grant] = {};
|
||||||
@@ -1044,13 +1447,25 @@ bool CLGMPClipboardTransport::ProcessMessage(
|
|||||||
return true;
|
return true;
|
||||||
if (m_ownerClientID)
|
if (m_ownerClientID)
|
||||||
{
|
{
|
||||||
if (IsOwner(clientID, message.generation))
|
if (!m_ownerReleasing &&
|
||||||
|
IsOwner(clientID, message.generation))
|
||||||
RenewLease();
|
RenewLease();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (message.flags == KVMFR_CLIPBOARD_TRANSPORT_STREAM &&
|
||||||
|
!BindStreams(clientID))
|
||||||
|
{
|
||||||
|
DeInitStreams();
|
||||||
|
m_failed = true;
|
||||||
|
if (m_stopEvent)
|
||||||
|
SetEvent(m_stopEvent);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
m_ownerClientID = clientID;
|
m_ownerClientID = clientID;
|
||||||
m_ownerGeneration = message.generation;
|
m_ownerGeneration = message.generation;
|
||||||
|
m_ownerTransport = message.flags;
|
||||||
RenewLease();
|
RenewLease();
|
||||||
m_statusDirty = true;
|
m_statusDirty = true;
|
||||||
m_replayPending = m_cachedValid;
|
m_replayPending = m_cachedValid;
|
||||||
@@ -1061,6 +1476,8 @@ bool CLGMPClipboardTransport::ProcessMessage(
|
|||||||
|
|
||||||
if (!IsOwner(clientID, message.generation))
|
if (!IsOwner(clientID, message.generation))
|
||||||
return true;
|
return true;
|
||||||
|
if (m_ownerReleasing)
|
||||||
|
return true;
|
||||||
|
|
||||||
if (CLGMPClipboardFiles::IsRecord(message) &&
|
if (CLGMPClipboardFiles::IsRecord(message) &&
|
||||||
CLGMPClipboardFiles::DirectionValid(message,
|
CLGMPClipboardFiles::DirectionValid(message,
|
||||||
@@ -1255,7 +1672,7 @@ ClipboardChannelResult CLGMPClipboardTransport::SendControl(
|
|||||||
return ClipboardChannelResult::BUSY;
|
return ClipboardChannelResult::BUSY;
|
||||||
if (result == PostResult::GONE)
|
if (result == PostResult::GONE)
|
||||||
{
|
{
|
||||||
ReleaseOwner("subscriber disappeared", true);
|
ReleaseOwner("subscriber disappeared", true, true);
|
||||||
return record.type == KVMFR_CLIPBOARD_MESSAGE_OFFER ||
|
return record.type == KVMFR_CLIPBOARD_MESSAGE_OFFER ||
|
||||||
record.type == KVMFR_CLIPBOARD_MESSAGE_CLEAR ?
|
record.type == KVMFR_CLIPBOARD_MESSAGE_CLEAR ?
|
||||||
ClipboardChannelResult::ACCEPTED : ClipboardChannelResult::BUSY;
|
ClipboardChannelResult::ACCEPTED : ClipboardChannelResult::BUSY;
|
||||||
@@ -1273,12 +1690,75 @@ ClipboardChannelResult CLGMPClipboardTransport::SendData(
|
|||||||
if (!valid)
|
if (!valid)
|
||||||
return ClipboardChannelResult::FAILED;
|
return ClipboardChannelResult::FAILED;
|
||||||
|
|
||||||
|
KVMFRClipboardMessage message = record;
|
||||||
|
message.generation = m_ownerGeneration;
|
||||||
|
if (m_ownerTransport == KVMFR_CLIPBOARD_TRANSPORT_STREAM)
|
||||||
|
{
|
||||||
|
LGMPStreamBuffer buffer = {};
|
||||||
|
LGMP_STATUS status =
|
||||||
|
lgmpHostStreamWriteAcquire(m_hostToClientStream, &buffer);
|
||||||
|
if (status == LGMP_ERR_STREAM_FULL)
|
||||||
|
return ClipboardChannelResult::BUSY;
|
||||||
|
if (status == LGMP_ERR_STREAM_UNBOUND ||
|
||||||
|
status == LGMP_ERR_STREAM_STALE)
|
||||||
|
{
|
||||||
|
ReleaseOwner("clipboard stream binding lost", true);
|
||||||
|
return ClipboardChannelResult::ACCEPTED;
|
||||||
|
}
|
||||||
|
if (status != LGMP_OK)
|
||||||
|
{
|
||||||
|
Fail("lgmpHostStreamWriteAcquire", status);
|
||||||
|
return ClipboardChannelResult::FAILED;
|
||||||
|
}
|
||||||
|
|
||||||
|
const uint32_t bytes = sizeof(message) + message.length;
|
||||||
|
if (!buffer.data || buffer.capacity < bytes)
|
||||||
|
{
|
||||||
|
const LGMP_STATUS cancel =
|
||||||
|
lgmpHostStreamWriteCancel(m_hostToClientStream, &buffer);
|
||||||
|
if (cancel != LGMP_OK)
|
||||||
|
DEBUG_ERROR("Failed to cancel an undersized host-to-client "
|
||||||
|
"clipboard stream reservation: %s", lgmpStatusString(cancel));
|
||||||
|
DEBUG_ERROR("Host-to-client clipboard stream returned an "
|
||||||
|
"undersized slot: capacity=%u required=%u",
|
||||||
|
buffer.capacity, bytes);
|
||||||
|
m_failed = true;
|
||||||
|
if (m_stopEvent)
|
||||||
|
SetEvent(m_stopEvent);
|
||||||
|
return ClipboardChannelResult::FAILED;
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(buffer.data, &message, sizeof(message));
|
||||||
|
if (message.length)
|
||||||
|
memcpy(static_cast<uint8_t *>(buffer.data) + sizeof(message), data,
|
||||||
|
message.length);
|
||||||
|
status = lgmpHostStreamWriteCommit(
|
||||||
|
m_hostToClientStream, &buffer, bytes);
|
||||||
|
if (status == LGMP_OK)
|
||||||
|
{
|
||||||
|
ApplyOutbound(record);
|
||||||
|
return ClipboardChannelResult::ACCEPTED;
|
||||||
|
}
|
||||||
|
|
||||||
|
DEBUG_ERROR("Failed to commit host-to-client clipboard stream data: %s",
|
||||||
|
lgmpStatusString(status));
|
||||||
|
if (status == LGMP_ERR_STREAM_UNBOUND ||
|
||||||
|
status == LGMP_ERR_STREAM_STALE)
|
||||||
|
{
|
||||||
|
ReleaseOwner("clipboard stream binding lost", true);
|
||||||
|
return ClipboardChannelResult::ACCEPTED;
|
||||||
|
}
|
||||||
|
Fail("lgmpHostStreamWriteCommit", status);
|
||||||
|
return ClipboardChannelResult::FAILED;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_ownerTransport != KVMFR_CLIPBOARD_TRANSPORT_LEGACY)
|
||||||
|
return ClipboardChannelResult::FAILED;
|
||||||
|
|
||||||
PLGMPMemory memory = FindAvailable(m_dataMemory);
|
PLGMPMemory memory = FindAvailable(m_dataMemory);
|
||||||
if (!memory)
|
if (!memory)
|
||||||
return ClipboardChannelResult::BUSY;
|
return ClipboardChannelResult::BUSY;
|
||||||
|
|
||||||
KVMFRClipboardMessage message = record;
|
|
||||||
message.generation = m_ownerGeneration;
|
|
||||||
uint8_t * slot = static_cast<uint8_t *>(lgmpHostMemPtr(memory));
|
uint8_t * slot = static_cast<uint8_t *>(lgmpHostMemPtr(memory));
|
||||||
memcpy(slot, &message, sizeof(message));
|
memcpy(slot, &message, sizeof(message));
|
||||||
if (message.length)
|
if (message.length)
|
||||||
@@ -1298,7 +1778,7 @@ ClipboardChannelResult CLGMPClipboardTransport::SendData(
|
|||||||
return ClipboardChannelResult::BUSY;
|
return ClipboardChannelResult::BUSY;
|
||||||
if (result == PostResult::GONE)
|
if (result == PostResult::GONE)
|
||||||
{
|
{
|
||||||
ReleaseOwner("subscriber disappeared", true);
|
ReleaseOwner("subscriber disappeared", true, true);
|
||||||
return ClipboardChannelResult::ACCEPTED;
|
return ClipboardChannelResult::ACCEPTED;
|
||||||
}
|
}
|
||||||
return ClipboardChannelResult::FAILED;
|
return ClipboardChannelResult::FAILED;
|
||||||
@@ -1385,7 +1865,7 @@ ClipboardChannelResult CLGMPClipboardTransport::SendClipboard(
|
|||||||
m_helperFormats = record.type == KVMFR_CLIPBOARD_MESSAGE_OFFER ?
|
m_helperFormats = record.type == KVMFR_CLIPBOARD_MESSAGE_OFFER ?
|
||||||
record.token : 0;
|
record.token : 0;
|
||||||
m_statusDirty = true;
|
m_statusDirty = true;
|
||||||
if (!m_ownerClientID)
|
if (!m_ownerClientID || m_ownerReleasing)
|
||||||
{
|
{
|
||||||
ClearOutboundBlock();
|
ClearOutboundBlock();
|
||||||
Wake();
|
Wake();
|
||||||
@@ -1393,7 +1873,7 @@ ClipboardChannelResult CLGMPClipboardTransport::SendClipboard(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!m_ownerClientID)
|
if (!m_ownerClientID || m_ownerReleasing)
|
||||||
{
|
{
|
||||||
if (OwnerScopedLifecycle(record))
|
if (OwnerScopedLifecycle(record))
|
||||||
{
|
{
|
||||||
@@ -1462,6 +1942,7 @@ void CLGMPClipboardTransport::ClipboardState(
|
|||||||
if (generation != m_endpointGeneration || available != m_available)
|
if (generation != m_endpointGeneration || available != m_available)
|
||||||
{
|
{
|
||||||
DropPendingTarget();
|
DropPendingTarget();
|
||||||
|
ClearStreamTargets();
|
||||||
m_internalTargetCount = 0;
|
m_internalTargetCount = 0;
|
||||||
for (KVMFRClipboardMessage& record : m_internalTarget)
|
for (KVMFRClipboardMessage& record : m_internalTarget)
|
||||||
record = {};
|
record = {};
|
||||||
@@ -1472,6 +1953,7 @@ void CLGMPClipboardTransport::ClipboardState(
|
|||||||
m_files.Reset();
|
m_files.Reset();
|
||||||
m_clientClipboardGeneration = 0;
|
m_clientClipboardGeneration = 0;
|
||||||
m_clientFormats = 0;
|
m_clientFormats = 0;
|
||||||
|
m_discardClientToHelper = 0;
|
||||||
m_discardHelperToClient = 0;
|
m_discardHelperToClient = 0;
|
||||||
m_cachedClipboard = {};
|
m_cachedClipboard = {};
|
||||||
m_cachedValid = false;
|
m_cachedValid = false;
|
||||||
@@ -1491,6 +1973,7 @@ void CLGMPClipboardTransport::ClipboardReset(
|
|||||||
if (!generation)
|
if (!generation)
|
||||||
return;
|
return;
|
||||||
DropPendingTarget();
|
DropPendingTarget();
|
||||||
|
ClearStreamTargets();
|
||||||
m_internalTargetCount = 0;
|
m_internalTargetCount = 0;
|
||||||
for (KVMFRClipboardMessage& record : m_internalTarget)
|
for (KVMFRClipboardMessage& record : m_internalTarget)
|
||||||
record = {};
|
record = {};
|
||||||
@@ -1501,6 +1984,7 @@ void CLGMPClipboardTransport::ClipboardReset(
|
|||||||
m_files.Reset();
|
m_files.Reset();
|
||||||
m_clientClipboardGeneration = 0;
|
m_clientClipboardGeneration = 0;
|
||||||
m_clientFormats = 0;
|
m_clientFormats = 0;
|
||||||
|
m_discardClientToHelper = 0;
|
||||||
m_discardHelperToClient = 0;
|
m_discardHelperToClient = 0;
|
||||||
m_cachedClipboard = {};
|
m_cachedClipboard = {};
|
||||||
m_cachedValid = false;
|
m_cachedValid = false;
|
||||||
@@ -1522,6 +2006,7 @@ void CLGMPClipboardTransport::Thread()
|
|||||||
for (;;)
|
for (;;)
|
||||||
{
|
{
|
||||||
DWORD timeout = IDLE_POLL_MS;
|
DWORD timeout = IDLE_POLL_MS;
|
||||||
|
bool streamReceived = false;
|
||||||
bool notifyReady = false;
|
bool notifyReady = false;
|
||||||
IClipboardTarget * readyTarget = nullptr;
|
IClipboardTarget * readyTarget = nullptr;
|
||||||
{
|
{
|
||||||
@@ -1535,15 +2020,19 @@ void CLGMPClipboardTransport::Thread()
|
|||||||
if (m_ownerClientID && !OwnerSubscribed())
|
if (m_ownerClientID && !OwnerSubscribed())
|
||||||
{
|
{
|
||||||
DropPendingTarget();
|
DropPendingTarget();
|
||||||
ReleaseOwner("subscriber disappeared", true);
|
ReleaseOwner("subscriber disappeared", true, true);
|
||||||
}
|
}
|
||||||
else if (m_ownerClientID && GetTickCount64() >= m_ownerDeadline)
|
else if (m_ownerClientID && !m_ownerReleasing &&
|
||||||
|
GetTickCount64() >= m_ownerDeadline)
|
||||||
ReleaseOwner("lease expired", true);
|
ReleaseOwner("lease expired", true);
|
||||||
|
|
||||||
// A restarted client can reuse dataset and transfer IDs. Deliver every
|
// A restarted client can reuse dataset and transfer IDs. Deliver every
|
||||||
// old-owner cleanup record before admitting messages from a new owner.
|
// old-owner cleanup record before admitting messages from a new owner.
|
||||||
if (!RetryTarget() || !PumpInternalTarget() ||
|
if (!RetryTarget() || !PumpInternalTarget() ||
|
||||||
|
!PumpStreamTarget() ||
|
||||||
(m_internalTargetCount == 0 && !DrainMessage()) ||
|
(m_internalTargetCount == 0 && !DrainMessage()) ||
|
||||||
|
!DrainStream(streamReceived) ||
|
||||||
|
!RetryOwnerRelease() ||
|
||||||
!PublishStatus() ||
|
!PublishStatus() ||
|
||||||
!ReplayClipboard() || !PostGrants())
|
!ReplayClipboard() || !PostGrants())
|
||||||
{
|
{
|
||||||
@@ -1556,8 +2045,13 @@ void CLGMPClipboardTransport::Thread()
|
|||||||
notifyReady = m_target != nullptr;
|
notifyReady = m_target != nullptr;
|
||||||
readyTarget = m_target;
|
readyTarget = m_target;
|
||||||
}
|
}
|
||||||
if (m_pendingTarget.valid || m_internalTargetCount || m_ownerClientID)
|
if (m_pendingTarget.valid || m_internalTargetCount ||
|
||||||
|
m_streamTargetCount || m_ownerClientID)
|
||||||
timeout = ACTIVE_POLL_MS;
|
timeout = ACTIVE_POLL_MS;
|
||||||
|
if (m_ownerTransport == KVMFR_CLIPBOARD_TRANSPORT_STREAM)
|
||||||
|
timeout = STREAM_POLL_MS;
|
||||||
|
if (streamReceived)
|
||||||
|
timeout = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ClipboardReceiveReady may synchronously retry SendClipboard, which
|
// ClipboardReceiveReady may synchronously retry SendClipboard, which
|
||||||
@@ -1584,7 +2078,7 @@ void CLGMPClipboardTransport::Thread()
|
|||||||
CSRWExclusiveLock lock(m_lock);
|
CSRWExclusiveLock lock(m_lock);
|
||||||
DropPendingTarget();
|
DropPendingTarget();
|
||||||
if (m_ownerClientID)
|
if (m_ownerClientID)
|
||||||
ReleaseOwner("transport stopped", false);
|
ReleaseOwner("transport stopped", false, true);
|
||||||
target = m_target;
|
target = m_target;
|
||||||
}
|
}
|
||||||
if (notifyFailed && target)
|
if (notifyFailed && target)
|
||||||
|
|||||||
@@ -33,6 +33,7 @@
|
|||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#include "lgmp/host.h"
|
#include "lgmp/host.h"
|
||||||
|
#include "lgmp/stream.h"
|
||||||
}
|
}
|
||||||
|
|
||||||
class CLGMPHost;
|
class CLGMPHost;
|
||||||
@@ -45,10 +46,15 @@ private:
|
|||||||
CLGMPClipboardFiles::MAX_ACQUISITIONS +
|
CLGMPClipboardFiles::MAX_ACQUISITIONS +
|
||||||
CLGMPClipboardFiles::MAX_REQUESTS + 3;
|
CLGMPClipboardFiles::MAX_REQUESTS + 3;
|
||||||
static constexpr ULONGLONG OWNER_LEASE_MS = 1000;
|
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 ACTIVE_POLL_MS = 5;
|
||||||
|
static constexpr DWORD STREAM_POLL_MS = 1;
|
||||||
static constexpr DWORD IDLE_POLL_MS = 50;
|
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 =
|
static constexpr uint32_t SLOT_BYTES =
|
||||||
sizeof(KVMFRClipboardSlotHeader) + KVMFR_CLIPBOARD_DATA_BYTES;
|
KVMFR_CLIPBOARD_STREAM_RECORD_BYTES;
|
||||||
|
|
||||||
enum class PostResult
|
enum class PostResult
|
||||||
{
|
{
|
||||||
@@ -96,9 +102,19 @@ private:
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct StreamTarget
|
||||||
|
{
|
||||||
|
KVMFRClipboardMessage record = {};
|
||||||
|
uint8_t data[KVMFR_CLIPBOARD_DATA_BYTES] = {};
|
||||||
|
|
||||||
|
void Clear() { record = {}; }
|
||||||
|
};
|
||||||
|
|
||||||
CLGMPHost& m_host;
|
CLGMPHost& m_host;
|
||||||
|
|
||||||
PLGMPHostQueue m_queue = nullptr;
|
PLGMPHostQueue m_queue = nullptr;
|
||||||
|
PLGMPHostStream m_hostToClientStream = nullptr;
|
||||||
|
PLGMPHostStream m_clientToHostStream = nullptr;
|
||||||
PLGMPMemory m_statusMemory [MEMORY_COUNT] = {};
|
PLGMPMemory m_statusMemory [MEMORY_COUNT] = {};
|
||||||
PLGMPMemory m_messageMemory[MEMORY_COUNT] = {};
|
PLGMPMemory m_messageMemory[MEMORY_COUNT] = {};
|
||||||
PLGMPMemory m_grantMemory [MEMORY_COUNT] = {};
|
PLGMPMemory m_grantMemory [MEMORY_COUNT] = {};
|
||||||
@@ -111,15 +127,18 @@ private:
|
|||||||
HANDLE m_wakeEvent = nullptr;
|
HANDLE m_wakeEvent = nullptr;
|
||||||
HANDLE m_thread = nullptr;
|
HANDLE m_thread = nullptr;
|
||||||
|
|
||||||
bool m_available = false;
|
bool m_available = false;
|
||||||
bool m_statusDirty = true;
|
bool m_statusDirty = true;
|
||||||
bool m_cachedValid = false;
|
bool m_cachedValid = false;
|
||||||
bool m_replayPending = false;
|
bool m_replayPending = false;
|
||||||
bool m_outboundBlocked = false;
|
bool m_outboundBlocked = false;
|
||||||
bool m_failed = false;
|
bool m_ownerReleasing = false;
|
||||||
|
bool m_releaseClearHelper = false;
|
||||||
|
bool m_failed = false;
|
||||||
uint32_t m_endpointGeneration = 0;
|
uint32_t m_endpointGeneration = 0;
|
||||||
uint32_t m_ownerClientID = 0;
|
uint32_t m_ownerClientID = 0;
|
||||||
uint32_t m_ownerGeneration = 0;
|
uint32_t m_ownerGeneration = 0;
|
||||||
|
KVMFRClipboardTransportFlags m_ownerTransport = 0;
|
||||||
uint32_t m_statusSerial = 0;
|
uint32_t m_statusSerial = 0;
|
||||||
uint32_t m_messageSerial = 0;
|
uint32_t m_messageSerial = 0;
|
||||||
uint32_t m_dataSerial = 0;
|
uint32_t m_dataSerial = 0;
|
||||||
@@ -128,20 +147,33 @@ private:
|
|||||||
uint32_t m_blockedOwnerClientID = 0;
|
uint32_t m_blockedOwnerClientID = 0;
|
||||||
uint32_t m_blockedOwnerGeneration = 0;
|
uint32_t m_blockedOwnerGeneration = 0;
|
||||||
uint64_t m_ownerDeadline = 0;
|
uint64_t m_ownerDeadline = 0;
|
||||||
|
uint64_t m_ownerDrainDeadline = 0;
|
||||||
uint64_t m_clientClipboardGeneration = 0;
|
uint64_t m_clientClipboardGeneration = 0;
|
||||||
|
uint64_t m_discardClientToHelper = 0;
|
||||||
uint64_t m_discardHelperToClient = 0;
|
uint64_t m_discardHelperToClient = 0;
|
||||||
|
const char * m_ownerReleaseReason = nullptr;
|
||||||
KVMFRClipboardMessage m_cachedClipboard = {};
|
KVMFRClipboardMessage m_cachedClipboard = {};
|
||||||
KVMFRClipboardMessage m_blockedOutbound = {};
|
KVMFRClipboardMessage m_blockedOutbound = {};
|
||||||
|
KVMFRStreamDescriptor m_hostToClientDescriptor = {};
|
||||||
|
KVMFRStreamDescriptor m_clientToHostDescriptor = {};
|
||||||
Transfer m_clientToHelper;
|
Transfer m_clientToHelper;
|
||||||
Transfer m_helperToClient;
|
Transfer m_helperToClient;
|
||||||
Grant m_grants[MEMORY_COUNT];
|
Grant m_grants[MEMORY_COUNT];
|
||||||
PendingTarget m_pendingTarget;
|
PendingTarget m_pendingTarget;
|
||||||
|
StreamTarget m_streamTarget[STREAM_TARGET_COUNT];
|
||||||
|
unsigned m_streamTargetHead = 0;
|
||||||
|
unsigned m_streamTargetCount = 0;
|
||||||
KVMFRClipboardMessage m_internalTarget[INTERNAL_TARGET_COUNT] = {};
|
KVMFRClipboardMessage m_internalTarget[INTERNAL_TARGET_COUNT] = {};
|
||||||
unsigned m_internalTargetCount = 0;
|
unsigned m_internalTargetCount = 0;
|
||||||
CLGMPClipboardFiles m_files;
|
CLGMPClipboardFiles m_files;
|
||||||
|
|
||||||
bool Initialize();
|
bool Initialize();
|
||||||
void DeInit();
|
void DeInit();
|
||||||
|
bool InitializeStreams();
|
||||||
|
void DeInitStreams();
|
||||||
|
bool BindStreams(uint32_t clientID);
|
||||||
|
bool TryUnbindStreams();
|
||||||
|
void ForceUnbindStreams();
|
||||||
|
|
||||||
static DWORD CALLBACK ThreadProc(void * context);
|
static DWORD CALLBACK ThreadProc(void * context);
|
||||||
void Thread();
|
void Thread();
|
||||||
@@ -154,7 +186,10 @@ private:
|
|||||||
uint32_t ownerClientID, uint32_t ownerGeneration);
|
uint32_t ownerClientID, uint32_t ownerGeneration);
|
||||||
void ClearOutboundBlock();
|
void ClearOutboundBlock();
|
||||||
bool BlockedOwnerLost(const KVMFRClipboardMessage& record) const;
|
bool BlockedOwnerLost(const KVMFRClipboardMessage& record) const;
|
||||||
void ReleaseOwner(const char * reason, bool clearHelper);
|
void ReleaseOwner(const char * reason, bool clearHelper,
|
||||||
|
bool force = false);
|
||||||
|
void FinishOwnerRelease();
|
||||||
|
bool RetryOwnerRelease();
|
||||||
void ResetProtocol(bool keepClipboard);
|
void ResetProtocol(bool keepClipboard);
|
||||||
void QueueHelperClear();
|
void QueueHelperClear();
|
||||||
void QueueTransferCancel(const Transfer& transfer);
|
void QueueTransferCancel(const Transfer& transfer);
|
||||||
@@ -164,6 +199,13 @@ private:
|
|||||||
void QueueFileDisconnect();
|
void QueueFileDisconnect();
|
||||||
bool QueueInternalTarget(const KVMFRClipboardMessage& record);
|
bool QueueInternalTarget(const KVMFRClipboardMessage& record);
|
||||||
bool PumpInternalTarget();
|
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;
|
PLGMPMemory FindAvailable(PLGMPMemory (&memory)[MEMORY_COUNT]) const;
|
||||||
PostResult PostForOwner(uint64_t udata, PLGMPMemory memory);
|
PostResult PostForOwner(uint64_t udata, PLGMPMemory memory);
|
||||||
|
|||||||
Reference in New Issue
Block a user