mirror of
https://github.com/gnif/LookingGlass.git
synced 2026-08-22 15:11:31 +00:00
[idd] transport: centralize source and control state
This commit is contained in:
161
idd/LGIdd/transport/CControlHub.cpp
Normal file
161
idd/LGIdd/transport/CControlHub.cpp
Normal 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;
|
||||
}
|
||||
65
idd/LGIdd/transport/CControlHub.h
Normal file
65
idd/LGIdd/transport/CControlHub.h
Normal 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;
|
||||
};
|
||||
@@ -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()
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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() {}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user