[idd] transport: centralize source and control state

This commit is contained in:
Geoffrey McRae
2026-08-12 16:31:35 +10:00
parent 1ebf80fbe8
commit 801a1e58f8
10 changed files with 339 additions and 16 deletions

View File

@@ -0,0 +1,161 @@
/**
* 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/CControlHub.h"
#include <cstring>
#include <new>
#include <utility>
std::vector<std::shared_ptr<CControlHub::Sink>> CControlHub::Snapshot() const
{
CSRWSharedLock lock(m_sinkLock);
return m_sinks;
}
bool CControlHub::Add(
BackendId backend, uint32_t epoch, IControlTransport& control)
{
std::shared_ptr<Sink> sink(new (std::nothrow) Sink);
if (!sink)
return false;
sink->backend = backend;
sink->epoch = epoch;
sink->control = &control;
{
CSRWExclusiveLock lock(m_sinkLock);
for (const auto& current : m_sinks)
if (current->backend == backend && current->epoch == epoch)
return false;
m_sinks.push_back(sink);
}
Replay(sink);
return true;
}
void CControlHub::Remove(BackendId backend, uint32_t epoch)
{
std::shared_ptr<Sink> removed;
{
CSRWExclusiveLock lock(m_sinkLock);
for (auto current = m_sinks.begin(); current != m_sinks.end(); ++current)
if ((*current)->backend == backend && (*current)->epoch == epoch)
{
removed = *current;
m_sinks.erase(current);
break;
}
}
if (removed)
{
CSRWExclusiveLock lock(removed->lock);
removed->control = nullptr;
}
}
void CControlHub::Replay(const std::shared_ptr<Sink>& sink)
{
IDARG_OUT_QUERY_HWCURSOR cursor = {};
std::vector<BYTE> cursorData;
UINT sdrWhiteLevel;
bool cursorValid;
std::shared_ptr<const D12ColorTransform> transform;
{
CSRWSharedLock lock(m_stateLock);
cursor = m_cursor;
cursorData = m_cursorData;
sdrWhiteLevel = m_sdrWhiteLevel;
cursorValid = m_cursorValid;
transform = m_colorTransform;
}
CSRWSharedLock lock(sink->lock);
if (!sink->control)
return;
sink->control->SetColorTransform(std::move(transform));
if (cursorValid)
sink->control->SendCursor(
cursor, cursorData.empty() ? nullptr : cursorData.data(),
sdrWhiteLevel);
}
void CControlHub::SendCursor(const IDARG_OUT_QUERY_HWCURSOR& info,
const BYTE * data, UINT sdrWhiteLevel)
{
{
CSRWExclusiveLock lock(m_stateLock);
m_cursor.IsCursorVisible = info.IsCursorVisible;
m_cursor.X = info.X;
m_cursor.Y = info.Y;
m_cursorValid = true;
m_sdrWhiteLevel = sdrWhiteLevel;
if (info.CursorShapeInfo.CursorType !=
IDDCX_CURSOR_SHAPE_TYPE_UNINITIALIZED)
{
const size_t size =
(size_t)info.CursorShapeInfo.Height * info.CursorShapeInfo.Pitch;
m_cursor.IsCursorShapeUpdated = info.IsCursorShapeUpdated;
m_cursor.CursorShapeInfo = info.CursorShapeInfo;
m_cursorData.resize(size);
if (size)
memcpy(m_cursorData.data(), data, size);
m_shapeValid = true;
}
else if (!m_shapeValid)
m_cursor.CursorShapeInfo.CursorType =
IDDCX_CURSOR_SHAPE_TYPE_UNINITIALIZED;
}
const auto sinks = Snapshot();
for (const auto& sink : sinks)
{
CSRWSharedLock lock(sink->lock);
if (sink->control)
sink->control->SendCursor(info, data, sdrWhiteLevel);
}
}
void CControlHub::SetColorTransform(
std::shared_ptr<const D12ColorTransform> transform)
{
{
CSRWExclusiveLock lock(m_stateLock);
m_colorTransform = transform;
}
const auto sinks = Snapshot();
for (const auto& sink : sinks)
{
CSRWSharedLock lock(sink->lock);
if (sink->control)
sink->control->SetColorTransform(transform);
}
}
std::shared_ptr<const D12ColorTransform>
CControlHub::GetColorTransform() const
{
CSRWSharedLock lock(m_stateLock);
return m_colorTransform;
}

View File

@@ -0,0 +1,65 @@
/**
* 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/IControlTransport.h"
#include "transport/ITransport.h"
#include <memory>
#include <vector>
class CControlHub final : public IControlTransport
{
private:
struct Sink
{
BackendId backend;
uint32_t epoch;
CSRWLock lock;
IControlTransport * control;
};
mutable CSRWLock m_sinkLock;
std::vector<std::shared_ptr<Sink>> m_sinks;
mutable CSRWLock m_stateLock;
std::shared_ptr<const D12ColorTransform> m_colorTransform;
IDARG_OUT_QUERY_HWCURSOR m_cursor = {};
std::vector<BYTE> m_cursorData;
UINT m_sdrWhiteLevel = 0;
bool m_cursorValid = false;
bool m_shapeValid = false;
std::vector<std::shared_ptr<Sink>> Snapshot() const;
void Replay(const std::shared_ptr<Sink>& sink);
public:
bool Add(BackendId backend, uint32_t epoch, IControlTransport& control);
void Remove(BackendId backend, uint32_t epoch);
void SendCursor(const IDARG_OUT_QUERY_HWCURSOR& info,
const BYTE * data, UINT sdrWhiteLevel) override;
void SetColorTransform(
std::shared_ptr<const D12ColorTransform> transform) override;
std::shared_ptr<const D12ColorTransform>
GetColorTransform() const override;
};

View File

@@ -24,9 +24,50 @@
#include <Windows.h>
#include <new>
#include <utility>
static const uint64_t RETRY_DELAY_MS = 500;
class CSourceEvents final : public ITransportEvents
{
private:
BackendId m_backend;
uint32_t m_epoch;
ITransportEvents& m_events;
SourceKey Stamp(const SourceKey& source) const
{
SourceKey stamped = source;
stamped.backend = m_backend;
stamped.epoch = m_epoch;
return stamped;
}
public:
CSourceEvents(
BackendId backend, uint32_t epoch, ITransportEvents& events) :
m_backend(backend), m_epoch(epoch), m_events(events) {}
void OnSetCursorPos(
const SourceKey& source, int32_t x, int32_t y) override
{
m_events.OnSetCursorPos(Stamp(source), x, y);
}
void OnSetResolution(const SourceKey& source,
uint32_t width, uint32_t height) override
{
m_events.OnSetResolution(Stamp(source), width, height);
}
void OnRecoveryRequest(const SourceKey& source,
uint64_t session, uint32_t serial, bool active) override
{
m_events.OnRecoveryRequest(
Stamp(source), session, serial, active);
}
};
CTransportManager::~CTransportManager()
{
Stop();
@@ -97,6 +138,13 @@ bool CTransportManager::InitializeEntry(Entry& entry)
return false;
}
if (!m_control.Add(entry.id, entry.epoch, entry.transport->Control()))
{
entry.state = State::FAILED;
return false;
}
entry.controlAdded = true;
entry.state = State::INITIALIZED;
return true;
}
@@ -187,6 +235,7 @@ void CTransportManager::RetryEntry(Entry& entry, uint64_t now)
if (entry.primary && m_exposed)
return;
RemoveServices(entry);
if (entry.transport)
entry.transport->Stop();
entry.transport.reset();
@@ -220,10 +269,12 @@ void CTransportManager::HandleProcessResult(
if (result == ProcessResult::RETRY || !entry.required)
{
RemoveServices(entry);
ScheduleRetry(entry);
return;
}
RemoveServices(entry);
entry.state = State::FAILED;
}
@@ -238,7 +289,8 @@ ITransport::ProcessResult CTransportManager::Process(
if (entry.state != State::INITIALIZED && entry.state != State::READY)
continue;
const ProcessResult result = entry.transport->Process(events);
CSourceEvents sourceEvents(entry.id, entry.epoch, events);
const ProcessResult result = entry.transport->Process(sourceEvents);
HandleProcessResult(entry, result);
}
return ProcessResult::OK;
@@ -250,12 +302,22 @@ void CTransportManager::Stop()
++current)
{
Entry& entry = **current;
RemoveServices(entry);
if (entry.transport && entry.state != State::STOPPED)
entry.transport->Stop();
entry.state = State::STOPPED;
}
}
void CTransportManager::RemoveServices(Entry& entry)
{
if (entry.controlAdded)
{
m_control.Remove(entry.id, entry.epoch);
entry.controlAdded = false;
}
}
void CTransportManager::SyncRecovery()
{
for (const auto& current : m_entries)
@@ -299,7 +361,7 @@ IFrameTransport& CTransportManager::Frames()
IControlTransport& CTransportManager::Control()
{
return Primary().Control();
return m_control;
}
IInputTransport * CTransportManager::Input()

View File

@@ -20,6 +20,7 @@
#pragma once
#include "transport/CControlHub.h"
#include "transport/ITransport.h"
#include <memory>
@@ -53,9 +54,11 @@ private:
State state = State::CLOSED;
uint32_t epoch = 1;
uint64_t retryAt = 0;
bool controlAdded = false;
};
std::vector<std::unique_ptr<Entry>> m_entries;
CControlHub m_control;
Entry * m_primary = nullptr;
bool m_initialized = false;
bool m_setup = false;
@@ -68,6 +71,7 @@ private:
void RetryEntry(Entry& entry, uint64_t now);
void HandleProcessResult(Entry& entry, ProcessResult result);
void ScheduleRetry(Entry& entry);
void RemoveServices(Entry& entry);
ITransport& Primary();
public:

View File

@@ -30,22 +30,32 @@ class IControlTransport;
class IFrameTransport;
class IInputTransport;
using BackendId = uint32_t;
struct SourceKey
{
BackendId backend = 0;
uint32_t epoch = 0;
uint32_t client = 0;
uint32_t generation = 0;
};
class ITransportEvents
{
public:
virtual ~ITransportEvents() = default;
virtual void OnSetCursorPos(int32_t x, int32_t y) = 0;
virtual void OnSetResolution(uint32_t width, uint32_t height) = 0;
virtual void OnRecoveryRequest(
virtual void OnSetCursorPos(
const SourceKey& source, int32_t x, int32_t y) = 0;
virtual void OnSetResolution(
const SourceKey& source, uint32_t width, uint32_t height) = 0;
virtual void OnRecoveryRequest(const SourceKey& source,
uint64_t session, uint32_t serial, bool active) = 0;
};
class ITransport
{
public:
using BackendId = uint32_t;
enum class OpenResult
{
SUCCESS,
@@ -72,6 +82,8 @@ public:
virtual OpenResult Open() = 0;
virtual bool Initialize() = 0;
virtual bool Setup(size_t alignment) = 0;
// Process performs a bounded, non-blocking drain. The events reference is
// valid only for the duration of this call and must not be retained.
virtual ProcessResult Process(ITransportEvents& events) = 0;
virtual void Stop() = 0;
virtual void SyncRecovery() {}

View File

@@ -97,7 +97,7 @@ ITransport::ProcessResult CLGMPTransport::Process(ITransportEvents& events)
{
const CRecovery::Request recovery = m_recovery.Process();
if (recovery.valid)
events.OnRecoveryRequest(
events.OnRecoveryRequest(SourceKey(),
recovery.session, recovery.serial, recovery.active);
// Before the swap chain establishes the frame-buffer alignment, service
@@ -140,17 +140,21 @@ ITransport::ProcessResult CLGMPTransport::Process(ITransportEvents& events)
{
case KVMFR_MESSAGE_SETCURSORPOS:
{
SourceKey source;
source.client = sourceClientID;
KVMFRSetCursorPos * position =
reinterpret_cast<KVMFRSetCursorPos *>(msg);
events.OnSetCursorPos(position->x, position->y);
events.OnSetCursorPos(source, position->x, position->y);
break;
}
case KVMFR_MESSAGE_WINDOWSIZE:
{
SourceKey source;
source.client = sourceClientID;
KVMFRWindowSize * window =
reinterpret_cast<KVMFRWindowSize *>(msg);
events.OnSetResolution(window->w, window->h);
events.OnSetResolution(source, window->w, window->h);
break;
}