mirror of
https://github.com/gnif/LookingGlass.git
synced 2026-08-22 15:11:31 +00:00
[idd] clipboard: batch helper stream delivery
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.
This commit is contained in:
@@ -384,14 +384,31 @@ void CClipboardHub::ClipboardReset(uint64_t epoch, uint32_t reason)
|
|||||||
ClipboardChannelResult CClipboardHub::SendClipboard(
|
ClipboardChannelResult CClipboardHub::SendClipboard(
|
||||||
const KVMFRClipboardMessage& record, const uint8_t * data)
|
const KVMFRClipboardMessage& record, const uint8_t * data)
|
||||||
{
|
{
|
||||||
// Keep the shared lock through Send so Unbind cannot advance the binding
|
const ClipboardChannelWrite write = { record, data };
|
||||||
// generation after validation but before the record enters the channel.
|
size_t accepted = 0;
|
||||||
CSRWSharedLock lock(m_lock);
|
return SendClipboardBatch(&write, 1, accepted);
|
||||||
if (!ValidClientDirection(record) ||
|
}
|
||||||
m_stopped || !m_available || !m_active || !m_running || m_failed ||
|
|
||||||
!m_source || record.generation != m_generation)
|
ClipboardChannelResult CClipboardHub::SendClipboardBatch(
|
||||||
|
const ClipboardChannelWrite * records, size_t count, size_t& accepted)
|
||||||
|
{
|
||||||
|
accepted = 0;
|
||||||
|
if (!records || !count || count > KVMFR_CLIPBOARD_SLOT_COUNT)
|
||||||
return ClipboardChannelResult::FAILED;
|
return ClipboardChannelResult::FAILED;
|
||||||
return m_channel.Send(record, data);
|
|
||||||
|
// Keep the shared lock through Send so Unbind cannot advance the binding
|
||||||
|
// generation after validation but before the records enter the channel.
|
||||||
|
CSRWSharedLock lock(m_lock);
|
||||||
|
if (m_stopped || !m_available || !m_active || !m_running || m_failed ||
|
||||||
|
!m_source)
|
||||||
|
return ClipboardChannelResult::FAILED;
|
||||||
|
|
||||||
|
for (size_t i = 0; i < count; ++i)
|
||||||
|
if (!ValidClientDirection(records[i].record) ||
|
||||||
|
records[i].record.generation != m_generation)
|
||||||
|
return ClipboardChannelResult::FAILED;
|
||||||
|
|
||||||
|
return m_channel.SendBatch(records, count, accepted);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CClipboardHub::ClipboardReceiveReady()
|
void CClipboardHub::ClipboardReceiveReady()
|
||||||
|
|||||||
@@ -73,6 +73,9 @@ public:
|
|||||||
ClipboardChannelResult SendClipboard(
|
ClipboardChannelResult SendClipboard(
|
||||||
const KVMFRClipboardMessage& record,
|
const KVMFRClipboardMessage& record,
|
||||||
const uint8_t * data) override;
|
const uint8_t * data) override;
|
||||||
|
ClipboardChannelResult SendClipboardBatch(
|
||||||
|
const ClipboardChannelWrite * records,
|
||||||
|
size_t count, size_t& accepted) override;
|
||||||
void ClipboardReceiveReady() override;
|
void ClipboardReceiveReady() override;
|
||||||
void ClipboardFailed() override;
|
void ClipboardFailed() override;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -42,6 +42,12 @@ public:
|
|||||||
virtual ClipboardChannelResult SendClipboard(
|
virtual ClipboardChannelResult SendClipboard(
|
||||||
const KVMFRClipboardMessage& record, const uint8_t * data) = 0;
|
const KVMFRClipboardMessage& record, const uint8_t * data) = 0;
|
||||||
|
|
||||||
|
// Sends an ordered prefix of client-to-Helper records. accepted reports
|
||||||
|
// how many records were consumed for every result; the source retains and
|
||||||
|
// retries an unaccepted suffix when the target returns BUSY.
|
||||||
|
virtual ClipboardChannelResult SendClipboardBatch(
|
||||||
|
const ClipboardChannelWrite * records, size_t count, size_t& accepted) = 0;
|
||||||
|
|
||||||
// The source calls ClipboardReceiveReady after a Helper-to-client record
|
// The source calls ClipboardReceiveReady after a Helper-to-client record
|
||||||
// returned BUSY and it can accept an exact retry of that record.
|
// returned BUSY and it can accept an exact retry of that record.
|
||||||
virtual void ClipboardReceiveReady() = 0;
|
virtual void ClipboardReceiveReady() = 0;
|
||||||
|
|||||||
@@ -596,7 +596,7 @@ void CLGMPClipboardTransport::QueueFileDisconnect()
|
|||||||
bool CLGMPClipboardTransport::QueueInternalTarget(
|
bool CLGMPClipboardTransport::QueueInternalTarget(
|
||||||
const KVMFRClipboardMessage& record)
|
const KVMFRClipboardMessage& record)
|
||||||
{
|
{
|
||||||
if (!m_pendingTarget.valid && !m_streamTargetBusy &&
|
if (!m_pendingTarget.valid && !m_streamTargetPrepared &&
|
||||||
!m_internalTargetCount)
|
!m_internalTargetCount)
|
||||||
return BeginTarget(record, false);
|
return BeginTarget(record, false);
|
||||||
if (m_internalTargetCount == INTERNAL_TARGET_COUNT)
|
if (m_internalTargetCount == INTERNAL_TARGET_COUNT)
|
||||||
@@ -610,7 +610,7 @@ bool CLGMPClipboardTransport::QueueInternalTarget(
|
|||||||
|
|
||||||
bool CLGMPClipboardTransport::PumpInternalTarget()
|
bool CLGMPClipboardTransport::PumpInternalTarget()
|
||||||
{
|
{
|
||||||
if (m_pendingTarget.valid || m_streamTargetBusy ||
|
if (m_pendingTarget.valid || m_streamTargetPrepared ||
|
||||||
!m_internalTargetCount)
|
!m_internalTargetCount)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
@@ -627,24 +627,25 @@ void CLGMPClipboardTransport::ClearStreamTargets()
|
|||||||
target.Clear();
|
target.Clear();
|
||||||
m_streamTargetHead = 0;
|
m_streamTargetHead = 0;
|
||||||
m_streamTargetCount = 0;
|
m_streamTargetCount = 0;
|
||||||
m_streamTargetBusy = false;
|
m_streamTargetPrepared = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CLGMPClipboardTransport::ClearQueuedStreamTargets()
|
void CLGMPClipboardTransport::ClearQueuedStreamTargets()
|
||||||
{
|
{
|
||||||
if (!m_streamTargetBusy)
|
if (!m_streamTargetPrepared)
|
||||||
{
|
{
|
||||||
ClearStreamTargets();
|
ClearStreamTargets();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// The BUSY head has already crossed the target boundary and must receive
|
// The prepared prefix has already crossed the target boundary and must
|
||||||
// the same drain treatment as a BUSY PendingTarget. Only discard records
|
// receive the same drain treatment as a BUSY PendingTarget. Only discard
|
||||||
// which have not yet been presented to the target.
|
// records which have not yet been presented to the target.
|
||||||
for (unsigned i = 1; i < m_streamTargetCount; ++i)
|
for (unsigned i = m_streamTargetPrepared;
|
||||||
|
i < m_streamTargetCount; ++i)
|
||||||
m_streamTarget[
|
m_streamTarget[
|
||||||
(m_streamTargetHead + i) % STREAM_TARGET_COUNT].Clear();
|
(m_streamTargetHead + i) % STREAM_TARGET_COUNT].Clear();
|
||||||
m_streamTargetCount = 1;
|
m_streamTargetCount = m_streamTargetPrepared;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CLGMPClipboardTransport::QueueStreamTarget(
|
bool CLGMPClipboardTransport::QueueStreamTarget(
|
||||||
@@ -663,39 +664,92 @@ bool CLGMPClipboardTransport::QueueStreamTarget(
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CLGMPClipboardTransport::PopStreamTarget()
|
void CLGMPClipboardTransport::PopStreamTargets(unsigned count)
|
||||||
{
|
{
|
||||||
StreamTarget& target = m_streamTarget[m_streamTargetHead];
|
for (unsigned i = 0; i < count; ++i)
|
||||||
target.Clear();
|
m_streamTarget[
|
||||||
|
(m_streamTargetHead + i) % STREAM_TARGET_COUNT].Clear();
|
||||||
m_streamTargetHead =
|
m_streamTargetHead =
|
||||||
(m_streamTargetHead + 1) % STREAM_TARGET_COUNT;
|
(m_streamTargetHead + count) % STREAM_TARGET_COUNT;
|
||||||
--m_streamTargetCount;
|
m_streamTargetCount -= count;
|
||||||
m_streamTargetBusy = false;
|
m_streamTargetPrepared = count < m_streamTargetPrepared ?
|
||||||
|
m_streamTargetPrepared - count : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CLGMPClipboardTransport::PublishStreamTarget()
|
CLGMPClipboardTransport::StreamDisposition
|
||||||
|
CLGMPClipboardTransport::PreviewStreamTarget(
|
||||||
|
const KVMFRClipboardMessage& record, Transfer& transfer,
|
||||||
|
CLGMPClipboardFiles& files)
|
||||||
{
|
{
|
||||||
StreamTarget& target = m_streamTarget[m_streamTargetHead];
|
if (record.type == KVMFR_CLIPBOARD_MESSAGE_DATA)
|
||||||
|
{
|
||||||
|
if (record.transfer == m_discardClientToHelper)
|
||||||
|
return StreamDisposition::DISCARD;
|
||||||
|
if (record.token ||
|
||||||
|
!kvmfrClipboardTransferFromHelper(record.transfer) ||
|
||||||
|
!ValidateChunk(transfer, record))
|
||||||
|
return StreamDisposition::INVALID;
|
||||||
|
AdvanceChunk(transfer, record);
|
||||||
|
return StreamDisposition::READY;
|
||||||
|
}
|
||||||
|
|
||||||
|
const CLGMPClipboardFiles::Direction direction =
|
||||||
|
CLGMPClipboardFiles::Direction::CLIENT_TO_HELPER;
|
||||||
|
if (files.IsStaleTerminal(record, direction))
|
||||||
|
return StreamDisposition::STALE;
|
||||||
|
if (!files.Validate(record, direction, 0, false))
|
||||||
|
return StreamDisposition::INVALID;
|
||||||
|
files.Apply(record, direction);
|
||||||
|
return StreamDisposition::READY;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CLGMPClipboardTransport::PublishStreamTargets()
|
||||||
|
{
|
||||||
|
ClipboardChannelWrite writes[STREAM_TARGET_COUNT] = {};
|
||||||
|
for (unsigned i = 0; i < m_streamTargetPrepared; ++i)
|
||||||
|
{
|
||||||
|
StreamTarget& target = m_streamTarget[
|
||||||
|
(m_streamTargetHead + i) % STREAM_TARGET_COUNT];
|
||||||
|
writes[i].record = target.record;
|
||||||
|
writes[i].data = target.record.length ? target.data : nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t accepted = 0;
|
||||||
const ClipboardChannelResult result = m_target ?
|
const ClipboardChannelResult result = m_target ?
|
||||||
m_target->SendClipboard(target.record,
|
m_target->SendClipboardBatch(writes, m_streamTargetPrepared,
|
||||||
target.record.length ? target.data : nullptr) :
|
accepted) :
|
||||||
ClipboardChannelResult::FAILED;
|
ClipboardChannelResult::FAILED;
|
||||||
|
if (accepted > m_streamTargetPrepared ||
|
||||||
|
(result == ClipboardChannelResult::ACCEPTED &&
|
||||||
|
accepted != m_streamTargetPrepared))
|
||||||
|
{
|
||||||
|
DEBUG_ERROR("Helper accepted an invalid clipboard stream prefix: "
|
||||||
|
"accepted=%zu offered=%u", accepted, m_streamTargetPrepared);
|
||||||
|
ReleaseOwner("invalid Helper clipboard batch result", false);
|
||||||
|
m_failed = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (size_t i = 0; i < accepted; ++i)
|
||||||
|
{
|
||||||
|
const StreamTarget& target = m_streamTarget[
|
||||||
|
(m_streamTargetHead + i) % STREAM_TARGET_COUNT];
|
||||||
|
ApplyInbound(target.record);
|
||||||
|
}
|
||||||
|
if (accepted)
|
||||||
|
{
|
||||||
|
PopStreamTargets(static_cast<unsigned>(accepted));
|
||||||
|
if (m_ownerClientID && !m_ownerReleasing)
|
||||||
|
RenewLease();
|
||||||
|
}
|
||||||
|
|
||||||
if (result == ClipboardChannelResult::BUSY)
|
if (result == ClipboardChannelResult::BUSY)
|
||||||
{
|
{
|
||||||
m_streamTargetBusy = true;
|
|
||||||
if (m_ownerClientID)
|
if (m_ownerClientID)
|
||||||
RenewLease();
|
RenewLease();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (result == ClipboardChannelResult::ACCEPTED)
|
|
||||||
{
|
|
||||||
ApplyInbound(target.record);
|
|
||||||
if (m_ownerClientID && !m_ownerReleasing)
|
|
||||||
RenewLease();
|
|
||||||
}
|
|
||||||
PopStreamTarget();
|
|
||||||
|
|
||||||
if (result == ClipboardChannelResult::FAILED)
|
if (result == ClipboardChannelResult::FAILED)
|
||||||
{
|
{
|
||||||
ReleaseOwner("Helper delivery failed", false);
|
ReleaseOwner("Helper delivery failed", false);
|
||||||
@@ -704,86 +758,87 @@ bool CLGMPClipboardTransport::PublishStreamTarget()
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CLGMPClipboardTransport::RetryStreamTarget()
|
bool CLGMPClipboardTransport::RetryStreamTargets()
|
||||||
{
|
{
|
||||||
if (!m_streamTargetBusy || m_pendingTarget.valid)
|
if (!m_streamTargetPrepared || m_pendingTarget.valid)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
// The retained head is already validated and stamped. Retry it exactly;
|
// The retained prefix is already validated and stamped. Retry it exactly;
|
||||||
// owner-release state may have changed while the target was BUSY.
|
// owner-release state may have changed while the target was BUSY.
|
||||||
m_streamTargetBusy = false;
|
return PublishStreamTargets();
|
||||||
return PublishStreamTarget();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CLGMPClipboardTransport::ProcessStreamTarget()
|
|
||||||
{
|
|
||||||
StreamTarget& target = m_streamTarget[m_streamTargetHead];
|
|
||||||
const KVMFRClipboardMessage record = target.record;
|
|
||||||
if (!m_ownerClientID)
|
|
||||||
{
|
|
||||||
PopStreamTarget();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (m_ownerReleasing && !m_releaseClearHelper)
|
|
||||||
{
|
|
||||||
PopStreamTarget();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (record.type == KVMFR_CLIPBOARD_MESSAGE_DATA &&
|
|
||||||
record.transfer == m_discardClientToHelper)
|
|
||||||
{
|
|
||||||
PopStreamTarget();
|
|
||||||
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)
|
|
||||||
{
|
|
||||||
PopStreamTarget();
|
|
||||||
ReleaseOwner("invalid clipboard stream data", true);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!m_ownerReleasing)
|
|
||||||
RenewLease();
|
|
||||||
if (staleFileData)
|
|
||||||
{
|
|
||||||
PopStreamTarget();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
target.record.generation = m_endpointGeneration;
|
|
||||||
return PublishStreamTarget();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CLGMPClipboardTransport::PumpStreamTarget()
|
bool CLGMPClipboardTransport::PumpStreamTarget()
|
||||||
{
|
{
|
||||||
while (!m_pendingTarget.valid && !m_streamTargetBusy &&
|
while (!m_pendingTarget.valid && !m_streamTargetPrepared &&
|
||||||
m_streamTargetCount)
|
m_streamTargetCount)
|
||||||
if (!ProcessStreamTarget())
|
{
|
||||||
|
if (!m_ownerClientID ||
|
||||||
|
(m_ownerReleasing && !m_releaseClearHelper))
|
||||||
|
{
|
||||||
|
PopStreamTargets(1);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dependent chunks are validated against the state the preceding chunk
|
||||||
|
// would establish, without committing that state before Helper accepts
|
||||||
|
// the corresponding prefix.
|
||||||
|
Transfer transfer = m_clientToHelper;
|
||||||
|
CLGMPClipboardFiles files = m_files;
|
||||||
|
StreamDisposition disposition = StreamDisposition::READY;
|
||||||
|
for (unsigned i = 0; i < m_streamTargetCount; ++i)
|
||||||
|
{
|
||||||
|
StreamTarget& target = m_streamTarget[
|
||||||
|
(m_streamTargetHead + i) % STREAM_TARGET_COUNT];
|
||||||
|
disposition = PreviewStreamTarget(target.record, transfer, files);
|
||||||
|
if (disposition != StreamDisposition::READY)
|
||||||
|
break;
|
||||||
|
target.record.generation = m_endpointGeneration;
|
||||||
|
++m_streamTargetPrepared;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_streamTargetPrepared)
|
||||||
|
{
|
||||||
|
if (!m_ownerReleasing)
|
||||||
|
RenewLease();
|
||||||
|
if (!PublishStreamTargets())
|
||||||
return false;
|
return false;
|
||||||
|
if (m_streamTargetPrepared || m_failed)
|
||||||
|
return true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
PopStreamTargets(1);
|
||||||
|
if (disposition == StreamDisposition::STALE)
|
||||||
|
{
|
||||||
|
if (!m_ownerReleasing)
|
||||||
|
RenewLease();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (disposition == StreamDisposition::INVALID)
|
||||||
|
ReleaseOwner("invalid clipboard stream data", true);
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CLGMPClipboardTransport::DrainStream(bool& received)
|
bool CLGMPClipboardTransport::DrainStream(bool& received)
|
||||||
{
|
{
|
||||||
received = false;
|
received = false;
|
||||||
if (!m_ownerClientID || !m_clientToHostStream)
|
if (m_failed || !m_ownerClientID || !m_clientToHostStream)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
for (unsigned drained = 0;
|
for (unsigned drained = 0; drained < STREAM_DRAIN_MAX;)
|
||||||
drained < STREAM_DRAIN_MAX &&
|
|
||||||
m_streamTargetCount < STREAM_TARGET_COUNT; ++drained)
|
|
||||||
{
|
{
|
||||||
|
if (m_streamTargetCount == STREAM_TARGET_COUNT)
|
||||||
|
{
|
||||||
|
// Submit only after the bounded staging window is full. A retained
|
||||||
|
// prefix or pending control keeps the window backpressured here.
|
||||||
|
if (!PumpStreamTarget())
|
||||||
|
return false;
|
||||||
|
if (m_streamTargetCount == STREAM_TARGET_COUNT)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
LGMPStreamBuffer buffer = {};
|
LGMPStreamBuffer buffer = {};
|
||||||
LGMP_STATUS status =
|
LGMP_STATUS status =
|
||||||
lgmpHostStreamReadPeek(m_clientToHostStream, &buffer);
|
lgmpHostStreamReadPeek(m_clientToHostStream, &buffer);
|
||||||
@@ -794,11 +849,15 @@ bool CLGMPClipboardTransport::DrainStream(bool& received)
|
|||||||
{
|
{
|
||||||
if (m_ownerReleasing && status == LGMP_ERR_STREAM_UNBOUND)
|
if (m_ownerReleasing && status == LGMP_ERR_STREAM_UNBOUND)
|
||||||
break;
|
break;
|
||||||
|
if (!PumpStreamTarget())
|
||||||
|
return false;
|
||||||
ReleaseOwner("clipboard stream binding lost", true);
|
ReleaseOwner("clipboard stream binding lost", true);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (status != LGMP_OK)
|
if (status != LGMP_OK)
|
||||||
{
|
{
|
||||||
|
if (!PumpStreamTarget())
|
||||||
|
return false;
|
||||||
Fail("lgmpHostStreamReadPeek", status);
|
Fail("lgmpHostStreamReadPeek", status);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -828,11 +887,12 @@ bool CLGMPClipboardTransport::DrainStream(bool& received)
|
|||||||
}
|
}
|
||||||
if (!queued)
|
if (!queued)
|
||||||
{
|
{
|
||||||
|
if (!PumpStreamTarget())
|
||||||
|
return false;
|
||||||
ReleaseOwner("invalid clipboard stream record", true);
|
ReleaseOwner("invalid clipboard stream record", true);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (!PumpStreamTarget())
|
++drained;
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return PumpStreamTarget();
|
return PumpStreamTarget();
|
||||||
@@ -1970,10 +2030,9 @@ void CLGMPClipboardTransport::Thread()
|
|||||||
|
|
||||||
// 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() || !RetryStreamTarget() ||
|
if (!RetryTarget() || !RetryStreamTargets() ||
|
||||||
!PumpInternalTarget() ||
|
!PumpInternalTarget() ||
|
||||||
!PumpStreamTarget() ||
|
(!m_streamTargetCount && m_internalTargetCount == 0 &&
|
||||||
(!m_streamTargetBusy && m_internalTargetCount == 0 &&
|
|
||||||
!DrainMessage()) ||
|
!DrainMessage()) ||
|
||||||
!DrainStream(streamReceived) ||
|
!DrainStream(streamReceived) ||
|
||||||
!RetryOwnerRelease() ||
|
!RetryOwnerRelease() ||
|
||||||
|
|||||||
@@ -64,6 +64,14 @@ private:
|
|||||||
FAILED,
|
FAILED,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum class StreamDisposition
|
||||||
|
{
|
||||||
|
READY,
|
||||||
|
DISCARD,
|
||||||
|
STALE,
|
||||||
|
INVALID,
|
||||||
|
};
|
||||||
|
|
||||||
struct Transfer
|
struct Transfer
|
||||||
{
|
{
|
||||||
uint64_t transfer = 0;
|
uint64_t transfer = 0;
|
||||||
@@ -149,7 +157,7 @@ private:
|
|||||||
StreamTarget m_streamTarget[STREAM_TARGET_COUNT];
|
StreamTarget m_streamTarget[STREAM_TARGET_COUNT];
|
||||||
unsigned m_streamTargetHead = 0;
|
unsigned m_streamTargetHead = 0;
|
||||||
unsigned m_streamTargetCount = 0;
|
unsigned m_streamTargetCount = 0;
|
||||||
bool m_streamTargetBusy = false;
|
unsigned m_streamTargetPrepared = 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;
|
||||||
@@ -190,12 +198,14 @@ private:
|
|||||||
void ClearQueuedStreamTargets();
|
void ClearQueuedStreamTargets();
|
||||||
bool QueueStreamTarget(const KVMFRClipboardMessage& record,
|
bool QueueStreamTarget(const KVMFRClipboardMessage& record,
|
||||||
const uint8_t * data);
|
const uint8_t * data);
|
||||||
void PopStreamTarget();
|
void PopStreamTargets(unsigned count);
|
||||||
bool PublishStreamTarget();
|
StreamDisposition PreviewStreamTarget(
|
||||||
bool RetryStreamTarget();
|
const KVMFRClipboardMessage& record, Transfer& transfer,
|
||||||
|
CLGMPClipboardFiles& files);
|
||||||
|
bool PublishStreamTargets();
|
||||||
|
bool RetryStreamTargets();
|
||||||
bool PumpStreamTarget();
|
bool PumpStreamTarget();
|
||||||
bool DrainStream(bool& received);
|
bool DrainStream(bool& received);
|
||||||
bool ProcessStreamTarget();
|
|
||||||
|
|
||||||
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