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:
380
idd/LGIdd/transport/CClipboardHub.cpp
Normal file
380
idd/LGIdd/transport/CClipboardHub.cpp
Normal file
@@ -0,0 +1,380 @@
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
|
||||
#include "transport/CClipboardHub.h"
|
||||
|
||||
#include "Seq.h"
|
||||
|
||||
namespace
|
||||
{
|
||||
bool ValidHelperDirection(const KVMFRClipboardMessage& record)
|
||||
{
|
||||
if (record.type == KVMFR_CLIPBOARD_MESSAGE_REQUEST)
|
||||
return kvmfrClipboardTransferFromHelper(record.transfer);
|
||||
if (record.type == KVMFR_CLIPBOARD_MESSAGE_DATA)
|
||||
return kvmfrClipboardTransferFromClient(record.transfer);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
CClipboardHub::CClipboardHub(CClipboardChannel& channel) :
|
||||
m_channel(channel)
|
||||
{
|
||||
m_channel.SetHandler(this);
|
||||
}
|
||||
|
||||
CClipboardHub::~CClipboardHub()
|
||||
{
|
||||
Stop();
|
||||
}
|
||||
|
||||
void CClipboardHub::AdvanceGenerationNL()
|
||||
{
|
||||
Seq::Inc(m_generation);
|
||||
}
|
||||
|
||||
bool CClipboardHub::MarkFailed(IClipboardSource& source)
|
||||
{
|
||||
CSRWExclusiveLock lock(m_lock);
|
||||
if (m_source != &source || (!m_active && !m_reserved))
|
||||
return false;
|
||||
|
||||
const bool changed = !m_failed;
|
||||
m_failed = true;
|
||||
m_failurePending = true;
|
||||
return changed;
|
||||
}
|
||||
|
||||
ClipboardChannelResult CClipboardHub::HoldOrDiscard(
|
||||
const KVMFRClipboardMessage& record)
|
||||
{
|
||||
// Preserve the pending clipboard publication until a source is rebound.
|
||||
// Transfer-scoped records belong to the source which admitted them and
|
||||
// must never cross a transport generation.
|
||||
if (record.type == KVMFR_CLIPBOARD_MESSAGE_OFFER ||
|
||||
record.type == KVMFR_CLIPBOARD_MESSAGE_CLEAR)
|
||||
return ClipboardChannelResult::BUSY;
|
||||
|
||||
if (record.type == KVMFR_CLIPBOARD_MESSAGE_REQUEST)
|
||||
{
|
||||
KVMFRClipboardMessage cancel = {};
|
||||
cancel.version = KVMFR_CLIPBOARD_VERSION;
|
||||
cancel.type = KVMFR_CLIPBOARD_MESSAGE_CANCEL;
|
||||
cancel.generation = record.generation;
|
||||
cancel.clipboardGeneration = record.clipboardGeneration;
|
||||
cancel.transfer = record.transfer;
|
||||
cancel.format = record.format;
|
||||
cancel.token = ERROR_DEVICE_NOT_CONNECTED;
|
||||
const ClipboardChannelResult result = m_channel.Send(cancel);
|
||||
if (result != ClipboardChannelResult::ACCEPTED)
|
||||
return result;
|
||||
}
|
||||
|
||||
return ClipboardChannelResult::ACCEPTED;
|
||||
}
|
||||
|
||||
bool CClipboardHub::Bind(
|
||||
BackendId backend, uint32_t epoch, IClipboardSource& source)
|
||||
{
|
||||
CSRWExclusiveLock lifecycleLock(m_lifecycleLock);
|
||||
if (!backend || !epoch)
|
||||
return false;
|
||||
|
||||
bool available;
|
||||
uint32_t generation;
|
||||
uint64_t channelEpoch;
|
||||
{
|
||||
CSRWExclusiveLock lock(m_lock);
|
||||
if (m_stopped || m_source || m_active || m_reserved)
|
||||
return false;
|
||||
|
||||
AdvanceGenerationNL();
|
||||
m_source = &source;
|
||||
m_backend = backend;
|
||||
m_epoch = epoch;
|
||||
m_active = true;
|
||||
m_reserved = true;
|
||||
m_failed = false;
|
||||
m_failurePending = false;
|
||||
available = m_available;
|
||||
generation = m_generation;
|
||||
channelEpoch = m_channelEpoch;
|
||||
}
|
||||
|
||||
CSRWExclusiveLock callbackLock(m_callbackLock);
|
||||
const bool started = source.Start(*this);
|
||||
bool attached = false;
|
||||
{
|
||||
CSRWExclusiveLock lock(m_lock);
|
||||
if (started && m_source == &source && m_backend == backend &&
|
||||
m_epoch == epoch && m_active && m_reserved && !m_failed &&
|
||||
!m_stopped)
|
||||
{
|
||||
m_running = true;
|
||||
m_reserved = false;
|
||||
available = m_available;
|
||||
generation = m_generation;
|
||||
channelEpoch = m_channelEpoch;
|
||||
attached = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_active = false;
|
||||
m_running = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!attached)
|
||||
{
|
||||
source.Stop();
|
||||
CSRWExclusiveLock lock(m_lock);
|
||||
if (m_source == &source && m_backend == backend && m_epoch == epoch)
|
||||
{
|
||||
m_source = nullptr;
|
||||
m_backend = 0;
|
||||
m_epoch = 0;
|
||||
m_active = false;
|
||||
m_running = false;
|
||||
m_reserved = false;
|
||||
m_failed = false;
|
||||
m_failurePending = false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
source.ClipboardState(available, generation);
|
||||
callbackLock.Unlock();
|
||||
if (available && channelEpoch)
|
||||
m_channel.Kick(channelEpoch);
|
||||
return true;
|
||||
}
|
||||
|
||||
void CClipboardHub::Unbind(BackendId backend, uint32_t epoch)
|
||||
{
|
||||
CSRWExclusiveLock lifecycleLock(m_lifecycleLock);
|
||||
IClipboardSource * source = nullptr;
|
||||
bool stop = false;
|
||||
{
|
||||
CSRWExclusiveLock lock(m_lock);
|
||||
if ((!m_active && !m_failed && !m_reserved) ||
|
||||
m_backend != backend || m_epoch != epoch || !m_source)
|
||||
return;
|
||||
|
||||
source = m_source;
|
||||
stop = m_running || m_reserved;
|
||||
m_active = false;
|
||||
m_running = false;
|
||||
m_reserved = true;
|
||||
m_failed = false;
|
||||
m_failurePending = false;
|
||||
AdvanceGenerationNL();
|
||||
}
|
||||
|
||||
CSRWExclusiveLock callbackLock(m_callbackLock);
|
||||
if (stop)
|
||||
source->Stop();
|
||||
|
||||
CSRWExclusiveLock lock(m_lock);
|
||||
if (m_source == source && m_backend == backend && m_epoch == epoch)
|
||||
{
|
||||
m_source = nullptr;
|
||||
m_backend = 0;
|
||||
m_epoch = 0;
|
||||
m_active = false;
|
||||
m_running = false;
|
||||
m_reserved = false;
|
||||
m_failed = false;
|
||||
m_failurePending = false;
|
||||
}
|
||||
}
|
||||
|
||||
bool CClipboardHub::TakeFailure(SourceKey& source)
|
||||
{
|
||||
CSRWSharedLock lifecycleLock(m_lifecycleLock);
|
||||
CSRWExclusiveLock lock(m_lock);
|
||||
if (!m_failurePending)
|
||||
return false;
|
||||
|
||||
source = {};
|
||||
source.backend = m_backend;
|
||||
source.epoch = m_epoch;
|
||||
m_failurePending = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
void CClipboardHub::Stop()
|
||||
{
|
||||
CSRWExclusiveLock lifecycleLock(m_lifecycleLock);
|
||||
IClipboardSource * source = nullptr;
|
||||
bool stop = false;
|
||||
{
|
||||
CSRWExclusiveLock lock(m_lock);
|
||||
if (m_stopped)
|
||||
return;
|
||||
|
||||
m_stopped = true;
|
||||
source = m_source;
|
||||
stop = source && (m_running || m_reserved);
|
||||
m_active = false;
|
||||
m_running = false;
|
||||
m_reserved = source != nullptr;
|
||||
m_failed = false;
|
||||
m_failurePending = false;
|
||||
AdvanceGenerationNL();
|
||||
}
|
||||
|
||||
// Clearing the channel handler is a callback-quiescence barrier. It must
|
||||
// precede taking m_callbackLock because an in-flight channel callback owns
|
||||
// the channel's handler lock while waiting for m_callbackLock.
|
||||
m_channel.ClearHandler(this);
|
||||
|
||||
CSRWExclusiveLock callbackLock(m_callbackLock);
|
||||
if (stop)
|
||||
source->Stop();
|
||||
|
||||
CSRWExclusiveLock lock(m_lock);
|
||||
m_source = nullptr;
|
||||
m_backend = 0;
|
||||
m_epoch = 0;
|
||||
m_active = false;
|
||||
m_running = false;
|
||||
m_reserved = false;
|
||||
m_failed = false;
|
||||
m_failurePending = false;
|
||||
}
|
||||
|
||||
void CClipboardHub::ClipboardState(bool available, uint64_t epoch)
|
||||
{
|
||||
CSRWExclusiveLock callbackLock(m_callbackLock);
|
||||
IClipboardSource * source = nullptr;
|
||||
uint32_t generation;
|
||||
{
|
||||
CSRWExclusiveLock lock(m_lock);
|
||||
if (m_stopped)
|
||||
return;
|
||||
|
||||
if (m_available != available || m_channelEpoch != epoch)
|
||||
AdvanceGenerationNL();
|
||||
m_available = available;
|
||||
m_channelEpoch = epoch;
|
||||
generation = m_generation;
|
||||
if (m_active && m_running && !m_failed)
|
||||
source = m_source;
|
||||
}
|
||||
|
||||
if (source)
|
||||
source->ClipboardState(available, generation);
|
||||
}
|
||||
|
||||
ClipboardChannelResult CClipboardHub::ClipboardRecord(
|
||||
const KVMFRClipboardMessage& record, const uint8_t * data)
|
||||
{
|
||||
if (!ValidHelperDirection(record))
|
||||
return ClipboardChannelResult::FAILED;
|
||||
|
||||
CSRWExclusiveLock callbackLock(m_callbackLock);
|
||||
IClipboardSource * source = nullptr;
|
||||
uint32_t generation;
|
||||
{
|
||||
CSRWSharedLock lock(m_lock);
|
||||
if (m_stopped || !m_available)
|
||||
return ClipboardChannelResult::ACCEPTED;
|
||||
if (m_failed || !m_active || !m_running || !m_source)
|
||||
return HoldOrDiscard(record);
|
||||
source = m_source;
|
||||
generation = m_generation;
|
||||
}
|
||||
|
||||
KVMFRClipboardMessage stamped = record;
|
||||
stamped.generation = generation;
|
||||
const ClipboardChannelResult result =
|
||||
source->SendClipboard(stamped, data);
|
||||
if (result != ClipboardChannelResult::FAILED)
|
||||
return result;
|
||||
|
||||
if (MarkFailed(*source))
|
||||
{
|
||||
const uint64_t epoch = m_channel.Epoch();
|
||||
if (epoch)
|
||||
m_channel.Kick(epoch);
|
||||
}
|
||||
return HoldOrDiscard(record);
|
||||
}
|
||||
|
||||
void CClipboardHub::ClipboardReset(uint64_t epoch, uint32_t reason)
|
||||
{
|
||||
CSRWExclusiveLock callbackLock(m_callbackLock);
|
||||
IClipboardSource * source = nullptr;
|
||||
uint32_t generation;
|
||||
{
|
||||
CSRWExclusiveLock lock(m_lock);
|
||||
if (m_stopped || m_channelEpoch != epoch)
|
||||
return;
|
||||
|
||||
AdvanceGenerationNL();
|
||||
generation = m_generation;
|
||||
if (m_active && m_running && !m_failed)
|
||||
source = m_source;
|
||||
}
|
||||
|
||||
if (source)
|
||||
source->ClipboardReset(generation, reason);
|
||||
}
|
||||
|
||||
ClipboardChannelResult CClipboardHub::SendClipboard(
|
||||
const KVMFRClipboardMessage& record, const uint8_t * data)
|
||||
{
|
||||
// Keep the shared lock through Send so Unbind cannot advance the binding
|
||||
// generation after validation but before the record enters the channel.
|
||||
CSRWSharedLock lock(m_lock);
|
||||
if (m_stopped || !m_available || !m_active || !m_running || m_failed ||
|
||||
!m_source || record.generation != m_generation)
|
||||
return ClipboardChannelResult::FAILED;
|
||||
return m_channel.Send(record, data);
|
||||
}
|
||||
|
||||
void CClipboardHub::ClipboardReceiveReady()
|
||||
{
|
||||
{
|
||||
CSRWSharedLock lock(m_lock);
|
||||
if (m_stopped || !m_available || !m_active || m_failed ||
|
||||
!m_running)
|
||||
return;
|
||||
}
|
||||
|
||||
const uint64_t epoch = m_channel.Epoch();
|
||||
if (epoch)
|
||||
m_channel.Kick(epoch);
|
||||
}
|
||||
|
||||
void CClipboardHub::ClipboardFailed()
|
||||
{
|
||||
IClipboardSource * source;
|
||||
{
|
||||
CSRWSharedLock lock(m_lock);
|
||||
source = m_source;
|
||||
}
|
||||
if (!source || !MarkFailed(*source))
|
||||
return;
|
||||
|
||||
const uint64_t epoch = m_channel.Epoch();
|
||||
if (epoch)
|
||||
m_channel.Kick(epoch);
|
||||
}
|
||||
78
idd/LGIdd/transport/CClipboardHub.h
Normal file
78
idd/LGIdd/transport/CClipboardHub.h
Normal file
@@ -0,0 +1,78 @@
|
||||
/**
|
||||
* 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 "CClipboardChannel.h"
|
||||
#include "CSRWLock.h"
|
||||
#include "transport/IClipboardSource.h"
|
||||
#include "transport/ITransport.h"
|
||||
|
||||
class CClipboardHub final : public IClipboardTarget,
|
||||
private IClipboardChannelHandler
|
||||
{
|
||||
private:
|
||||
CClipboardChannel& m_channel;
|
||||
CSRWLock m_lifecycleLock;
|
||||
CSRWLock m_callbackLock;
|
||||
CSRWLock m_lock;
|
||||
|
||||
IClipboardSource * m_source = nullptr;
|
||||
BackendId m_backend = 0;
|
||||
uint32_t m_epoch = 0;
|
||||
uint64_t m_channelEpoch = 0;
|
||||
uint32_t m_generation = 1;
|
||||
bool m_available = false;
|
||||
bool m_active = false;
|
||||
bool m_running = false;
|
||||
bool m_reserved = false;
|
||||
bool m_failed = false;
|
||||
bool m_failurePending = false;
|
||||
bool m_stopped = false;
|
||||
|
||||
void AdvanceGenerationNL();
|
||||
bool MarkFailed(IClipboardSource& source);
|
||||
ClipboardChannelResult HoldOrDiscard(
|
||||
const KVMFRClipboardMessage& record);
|
||||
|
||||
void ClipboardState(bool available, uint64_t epoch) override;
|
||||
ClipboardChannelResult ClipboardRecord(
|
||||
const KVMFRClipboardMessage& record,
|
||||
const uint8_t * data) override;
|
||||
void ClipboardReset(uint64_t epoch, uint32_t reason) override;
|
||||
|
||||
public:
|
||||
explicit CClipboardHub(CClipboardChannel& channel);
|
||||
~CClipboardHub() override;
|
||||
|
||||
CClipboardHub(const CClipboardHub&) = delete;
|
||||
CClipboardHub& operator=(const CClipboardHub&) = delete;
|
||||
|
||||
bool Bind(BackendId backend, uint32_t epoch, IClipboardSource& source);
|
||||
void Unbind(BackendId backend, uint32_t epoch);
|
||||
bool TakeFailure(SourceKey& source);
|
||||
void Stop();
|
||||
|
||||
ClipboardChannelResult SendClipboard(
|
||||
const KVMFRClipboardMessage& record,
|
||||
const uint8_t * data) override;
|
||||
void ClipboardReceiveReady() override;
|
||||
void ClipboardFailed() override;
|
||||
};
|
||||
@@ -23,7 +23,9 @@
|
||||
#include "Atomic.h"
|
||||
#include "capture/CFrameGraph.h"
|
||||
#include "CDebug.h"
|
||||
#include "ipc/CPipeServer.h"
|
||||
#include "Seq.h"
|
||||
#include "transport/IClipboardSource.h"
|
||||
#include "transport/ITexStage.h"
|
||||
|
||||
#include <Windows.h>
|
||||
@@ -121,7 +123,8 @@ CTransportManager::Entry::~Entry()
|
||||
CloseHandle(idleEvent);
|
||||
}
|
||||
|
||||
CTransportManager::CTransportManager() : m_tex(m_frameRev)
|
||||
CTransportManager::CTransportManager() :
|
||||
m_tex(m_frameRev), m_clipboard(g_pipe.Clipboard())
|
||||
{
|
||||
m_phaseIdle = CreateEvent(nullptr, TRUE, TRUE, nullptr);
|
||||
m_stoppedEvent = CreateEvent(nullptr, TRUE, FALSE, nullptr);
|
||||
@@ -492,48 +495,55 @@ bool CTransportManager::InitializeEntry(Entry& entry)
|
||||
bool CTransportManager::AddServices(Entry& entry)
|
||||
{
|
||||
std::shared_ptr<ITransport> transport;
|
||||
BackendId id = 0;
|
||||
uint32_t epoch = 0;
|
||||
bool primary = false;
|
||||
bool required = false;
|
||||
uint32_t services = 0;
|
||||
bool controlAdded = false;
|
||||
bool controlFailed = false;
|
||||
bool controlAbsent = false;
|
||||
bool inputAdded = false;
|
||||
bool inputFailed = false;
|
||||
bool inputAbsent = false;
|
||||
bool frameAdded = false;
|
||||
bool frameBound = false;
|
||||
bool frameAbsent = false;
|
||||
uint64_t retryAt = 0;
|
||||
BackendId id = 0;
|
||||
uint32_t epoch = 0;
|
||||
bool primary = false;
|
||||
bool required = false;
|
||||
uint32_t services = 0;
|
||||
bool controlAdded = false;
|
||||
bool controlFailed = false;
|
||||
bool controlAbsent = false;
|
||||
bool inputAdded = false;
|
||||
bool inputFailed = false;
|
||||
bool inputAbsent = false;
|
||||
bool clipboardAdded = false;
|
||||
bool clipboardFailed = false;
|
||||
bool clipboardAbsent = false;
|
||||
bool frameAdded = false;
|
||||
bool frameBound = false;
|
||||
bool frameAbsent = false;
|
||||
uint64_t retryAt = 0;
|
||||
{
|
||||
CSRWSharedLock entryLock(entry.lock);
|
||||
transport = entry.transport;
|
||||
id = entry.id;
|
||||
epoch = entry.epoch;
|
||||
primary = entry.primary;
|
||||
required = entry.required;
|
||||
services = entry.config.services;
|
||||
controlAdded = entry.controlAdded;
|
||||
controlFailed = entry.controlFailed;
|
||||
controlAbsent = entry.controlAbsent;
|
||||
inputAdded = entry.inputAdded;
|
||||
inputFailed = entry.inputFailed;
|
||||
inputAbsent = entry.inputAbsent;
|
||||
frameAdded = entry.frameAdded;
|
||||
frameAbsent = entry.frameAbsent;
|
||||
retryAt = entry.serviceRetryAt;
|
||||
transport = entry.transport;
|
||||
id = entry.id;
|
||||
epoch = entry.epoch;
|
||||
primary = entry.primary;
|
||||
required = entry.required;
|
||||
services = entry.config.services;
|
||||
controlAdded = entry.controlAdded;
|
||||
controlFailed = entry.controlFailed;
|
||||
controlAbsent = entry.controlAbsent;
|
||||
inputAdded = entry.inputAdded;
|
||||
inputFailed = entry.inputFailed;
|
||||
inputAbsent = entry.inputAbsent;
|
||||
clipboardAdded = entry.clipboardAdded;
|
||||
clipboardFailed = entry.clipboardFailed;
|
||||
clipboardAbsent = entry.clipboardAbsent;
|
||||
frameAdded = entry.frameAdded;
|
||||
frameAbsent = entry.frameAbsent;
|
||||
retryAt = entry.serviceRetryAt;
|
||||
}
|
||||
|
||||
if (!transport)
|
||||
return !required;
|
||||
|
||||
const uint64_t now = GetTickCount64();
|
||||
const bool attach = now >= retryAt;
|
||||
bool controlRetry = false;
|
||||
bool frameRetry = false;
|
||||
bool inputRetry = false;
|
||||
const uint64_t now = GetTickCount64();
|
||||
const bool attach = now >= retryAt;
|
||||
bool controlRetry = false;
|
||||
bool frameRetry = false;
|
||||
bool inputRetry = false;
|
||||
bool clipboardRetry = false;
|
||||
if (!(services & TRANSPORT_SERVICE_CONTROL))
|
||||
controlAbsent = true;
|
||||
else if (attach && !controlAdded && !controlFailed && !controlAbsent)
|
||||
@@ -610,7 +620,30 @@ bool CTransportManager::AddServices(Entry& entry)
|
||||
}
|
||||
}
|
||||
|
||||
if (attach && (controlRetry || inputRetry || frameRetry))
|
||||
if (!(services & TRANSPORT_SERVICE_CLIPBOARD))
|
||||
clipboardAbsent = true;
|
||||
else if (attach && !clipboardAdded && !clipboardFailed &&
|
||||
!clipboardAbsent)
|
||||
{
|
||||
IClipboardSource * clipboard = transport->Clipboard();
|
||||
if (clipboard && m_clipboard.Bind(id, epoch, *clipboard))
|
||||
{
|
||||
CSRWExclusiveLock entryLock(entry.lock);
|
||||
entry.clipboardAdded = true;
|
||||
clipboardAdded = true;
|
||||
}
|
||||
else if (clipboard)
|
||||
clipboardRetry = true;
|
||||
else
|
||||
{
|
||||
clipboardAbsent = true;
|
||||
CSRWExclusiveLock entryLock(entry.lock);
|
||||
entry.clipboardAbsent = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (attach && (controlRetry || inputRetry || frameRetry ||
|
||||
clipboardRetry))
|
||||
{
|
||||
CSRWExclusiveLock entryLock(entry.lock);
|
||||
entry.serviceRetryAt = now + SERVICE_RETRY_DELAY_MS;
|
||||
@@ -623,9 +656,10 @@ bool CTransportManager::AddServices(Entry& entry)
|
||||
}
|
||||
|
||||
const bool servicesReady =
|
||||
(!(services & TRANSPORT_SERVICE_FRAME) || frameAdded) &&
|
||||
(!(services & TRANSPORT_SERVICE_CONTROL) || controlAdded) &&
|
||||
(!(services & TRANSPORT_SERVICE_INPUT) || inputAdded);
|
||||
(!(services & TRANSPORT_SERVICE_FRAME) || frameAdded) &&
|
||||
(!(services & TRANSPORT_SERVICE_CONTROL) || controlAdded) &&
|
||||
(!(services & TRANSPORT_SERVICE_INPUT) || inputAdded) &&
|
||||
(!(services & TRANSPORT_SERVICE_CLIPBOARD) || clipboardAdded);
|
||||
return !required || servicesReady;
|
||||
}
|
||||
|
||||
@@ -720,6 +754,35 @@ void CTransportManager::HandleServiceFailures()
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
source = {};
|
||||
while (m_clipboard.TakeFailure(source))
|
||||
{
|
||||
Entry * entries[FRAME_MAX_SINKS] = {};
|
||||
const unsigned count = Entries(entries);
|
||||
for (unsigned i = 0; i < count; ++i)
|
||||
{
|
||||
Entry& entry = *entries[i];
|
||||
bool restart = false;
|
||||
{
|
||||
CSRWExclusiveLock entryLock(entry.lock);
|
||||
if (entry.id != source.backend || entry.epoch != source.epoch ||
|
||||
!entry.clipboardAdded)
|
||||
continue;
|
||||
entry.clipboardAdded = false;
|
||||
entry.clipboardFailed = true;
|
||||
restart = !entry.exposed;
|
||||
}
|
||||
|
||||
m_clipboard.Unbind(source.backend, source.epoch);
|
||||
if (restart)
|
||||
{
|
||||
RemoveServices(entry);
|
||||
ScheduleRetry(entry);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool CTransportManager::SetupEntry(Entry& entry, size_t alignment)
|
||||
@@ -770,12 +833,13 @@ void CTransportManager::ScheduleRetry(Entry& entry)
|
||||
|
||||
void CTransportManager::RemoveServices(Entry& entry)
|
||||
{
|
||||
BackendId id = 0;
|
||||
uint32_t epoch = 0;
|
||||
bool frameAdded = false;
|
||||
bool frameLegacy = false;
|
||||
bool controlAdded = false;
|
||||
bool inputAdded = false;
|
||||
BackendId id = 0;
|
||||
uint32_t epoch = 0;
|
||||
bool frameAdded = false;
|
||||
bool frameLegacy = false;
|
||||
bool controlAdded = false;
|
||||
bool inputAdded = false;
|
||||
bool clipboardAdded = false;
|
||||
{
|
||||
CSRWExclusiveLock entryLock(entry.lock);
|
||||
id = entry.id;
|
||||
@@ -784,11 +848,13 @@ void CTransportManager::RemoveServices(Entry& entry)
|
||||
frameLegacy = entry.frameLegacy;
|
||||
controlAdded = entry.controlAdded;
|
||||
inputAdded = entry.inputAdded;
|
||||
clipboardAdded = entry.clipboardAdded;
|
||||
entry.frameAdded = false;
|
||||
entry.frameLegacy = false;
|
||||
entry.texSink = nullptr;
|
||||
entry.controlAdded = false;
|
||||
entry.inputAdded = false;
|
||||
entry.clipboardAdded = false;
|
||||
entry.frameRetryAt = 0;
|
||||
}
|
||||
|
||||
@@ -796,6 +862,8 @@ void CTransportManager::RemoveServices(Entry& entry)
|
||||
m_tex.Drop(id, epoch);
|
||||
if (inputAdded)
|
||||
m_input.Unbind(id, epoch);
|
||||
if (clipboardAdded)
|
||||
m_clipboard.Unbind(id, epoch);
|
||||
if (controlAdded)
|
||||
m_control.Remove(id, epoch);
|
||||
if (frameLegacy)
|
||||
@@ -835,6 +903,8 @@ void CTransportManager::RetryEntry(Entry& entry, uint64_t now,
|
||||
entry.controlAbsent = false;
|
||||
entry.inputFailed = false;
|
||||
entry.inputAbsent = false;
|
||||
entry.clipboardFailed = false;
|
||||
entry.clipboardAbsent = false;
|
||||
entry.frameAbsent = false;
|
||||
entry.serviceRetryAt = 0;
|
||||
Seq::Inc(entry.epoch);
|
||||
@@ -1601,6 +1671,7 @@ void CTransportManager::Stop()
|
||||
}
|
||||
|
||||
m_input.Stop();
|
||||
m_clipboard.Stop();
|
||||
|
||||
for (unsigned i = count; i > 0; --i)
|
||||
{
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
|
||||
#include "Atomic.h"
|
||||
#include "CSRWLock.h"
|
||||
#include "transport/CClipboardHub.h"
|
||||
#include "transport/CControlHub.h"
|
||||
#include "transport/CFrameHub.h"
|
||||
#include "transport/CInputHub.h"
|
||||
@@ -109,6 +110,9 @@ private:
|
||||
bool inputAdded = false;
|
||||
bool inputFailed = false;
|
||||
bool inputAbsent = false;
|
||||
bool clipboardAdded = false;
|
||||
bool clipboardFailed = false;
|
||||
bool clipboardAbsent = false;
|
||||
bool frameAdded = false;
|
||||
bool frameLegacy = false;
|
||||
ITexSink * texSink = nullptr;
|
||||
@@ -132,6 +136,7 @@ private:
|
||||
CFrameHub m_frames;
|
||||
CTexHub m_tex;
|
||||
CInputHub m_input;
|
||||
CClipboardHub m_clipboard;
|
||||
CRecoveryHub m_recovery;
|
||||
Entry * m_primary = nullptr;
|
||||
bool m_initialized = false;
|
||||
|
||||
77
idd/LGIdd/transport/IClipboardSource.h
Normal file
77
idd/LGIdd/transport/IClipboardSource.h
Normal file
@@ -0,0 +1,77 @@
|
||||
/**
|
||||
* 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 "CClipboardChannel.h"
|
||||
#include "common/KVMFRClipboard.h"
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
class IClipboardTarget
|
||||
{
|
||||
public:
|
||||
virtual ~IClipboardTarget() = default;
|
||||
|
||||
// Sends one client-to-Helper record. record.generation must be the binding
|
||||
// generation most recently supplied by ClipboardState; transports which
|
||||
// expose their own endpoint generation validate and replace it at this
|
||||
// adapter boundary. clipboardGeneration remains the clipboard content
|
||||
// generation and is preserved end-to-end.
|
||||
//
|
||||
// BUSY consumes neither the record nor its borrowed data; the source
|
||||
// retains and retries the exact record from its bounded worker/Process
|
||||
// drain.
|
||||
virtual ClipboardChannelResult SendClipboard(
|
||||
const KVMFRClipboardMessage& record, const uint8_t * data) = 0;
|
||||
|
||||
// The source calls ClipboardReceiveReady after a Helper-to-client record
|
||||
// returned BUSY and it can accept an exact retry of that record.
|
||||
virtual void ClipboardReceiveReady() = 0;
|
||||
virtual void ClipboardFailed() = 0;
|
||||
};
|
||||
|
||||
class IClipboardSource
|
||||
{
|
||||
public:
|
||||
virtual ~IClipboardSource() = default;
|
||||
|
||||
// Start attaches the target, but the source must wait for the initial
|
||||
// ClipboardState callback before calling target.SendClipboard. Stop is a
|
||||
// callback-quiescence barrier and leaves no work which can access target;
|
||||
// it also cleans up a partially completed or failed Start.
|
||||
virtual bool Start(IClipboardTarget& target) = 0;
|
||||
virtual void Stop() = 0;
|
||||
|
||||
// All callbacks into the source, including Start and Stop, are serialized.
|
||||
// record.generation is stamped with the current binding generation. A
|
||||
// generation change invalidates records from an earlier generation and
|
||||
// cancels their transfers; clipboardGeneration is unaffected. data is
|
||||
// borrowed only for the duration of SendClipboard. BUSY consumes neither
|
||||
// record nor data, and the channel retries the same record after
|
||||
// ClipboardReceiveReady (or its bounded polling fallback).
|
||||
virtual void ClipboardState(bool available, uint32_t generation) = 0;
|
||||
virtual ClipboardChannelResult SendClipboard(
|
||||
const KVMFRClipboardMessage& record, const uint8_t * data) = 0;
|
||||
|
||||
// Reset advances generation before the callback. The source cancels all
|
||||
// transfers from the preceding generation.
|
||||
virtual void ClipboardReset(uint32_t generation, uint32_t reason) = 0;
|
||||
};
|
||||
@@ -29,6 +29,7 @@
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
class IClipboardSource;
|
||||
class IControlSink;
|
||||
class IFrameSink;
|
||||
class IInputSource;
|
||||
@@ -178,4 +179,5 @@ public:
|
||||
virtual ITexSink * TexSink() { return nullptr; }
|
||||
virtual IControlSink * Control() { return nullptr; }
|
||||
virtual IInputSource * Input() { return nullptr; }
|
||||
virtual IClipboardSource * Clipboard() { return nullptr; }
|
||||
};
|
||||
|
||||
@@ -125,6 +125,8 @@ namespace
|
||||
service = TRANSPORT_SERVICE_CONTROL;
|
||||
else if (Equal(name, L"input"))
|
||||
service = TRANSPORT_SERVICE_INPUT;
|
||||
else if (Equal(name, L"clipboard"))
|
||||
service = TRANSPORT_SERVICE_CLIPBOARD;
|
||||
else
|
||||
return false;
|
||||
|
||||
|
||||
@@ -28,11 +28,13 @@ using BackendId = uint32_t;
|
||||
|
||||
enum TransportService : uint32_t
|
||||
{
|
||||
TRANSPORT_SERVICE_FRAME = 1U << 0,
|
||||
TRANSPORT_SERVICE_CONTROL = 1U << 1,
|
||||
TRANSPORT_SERVICE_INPUT = 1U << 2,
|
||||
TRANSPORT_SERVICE_ALL = TRANSPORT_SERVICE_FRAME |
|
||||
TRANSPORT_SERVICE_CONTROL | TRANSPORT_SERVICE_INPUT,
|
||||
TRANSPORT_SERVICE_FRAME = 1U << 0,
|
||||
TRANSPORT_SERVICE_CONTROL = 1U << 1,
|
||||
TRANSPORT_SERVICE_INPUT = 1U << 2,
|
||||
TRANSPORT_SERVICE_CLIPBOARD = 1U << 3,
|
||||
TRANSPORT_SERVICE_ALL = TRANSPORT_SERVICE_FRAME |
|
||||
TRANSPORT_SERVICE_CONTROL | TRANSPORT_SERVICE_INPUT |
|
||||
TRANSPORT_SERVICE_CLIPBOARD,
|
||||
};
|
||||
|
||||
struct TransportInstance
|
||||
|
||||
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