[idd] transport: configure active instances

This commit is contained in:
Geoffrey McRae
2026-08-12 23:26:26 +10:00
parent 4768396180
commit c601468871
12 changed files with 621 additions and 53 deletions

View File

@@ -64,6 +64,7 @@
<ClCompile Include="transport\CFrameHub.cpp" /> <ClCompile Include="transport\CFrameHub.cpp" />
<ClCompile Include="transport\CInputHub.cpp" /> <ClCompile Include="transport\CInputHub.cpp" />
<ClCompile Include="transport\CTransportManager.cpp" /> <ClCompile Include="transport\CTransportManager.cpp" />
<ClCompile Include="transport\TransportConfig.cpp" />
<ClCompile Include="transport\TransportFactory.cpp" /> <ClCompile Include="transport\TransportFactory.cpp" />
<ClCompile Include="transport\lgmp\CIVSHMEM.cpp" /> <ClCompile Include="transport\lgmp\CIVSHMEM.cpp" />
<ClCompile Include="transport\lgmp\CRecovery.cpp" /> <ClCompile Include="transport\lgmp\CRecovery.cpp" />
@@ -123,6 +124,7 @@
<ClInclude Include="transport\IInputTransport.h" /> <ClInclude Include="transport\IInputTransport.h" />
<ClInclude Include="transport\IInputSource.h" /> <ClInclude Include="transport\IInputSource.h" />
<ClInclude Include="transport\ITransport.h" /> <ClInclude Include="transport\ITransport.h" />
<ClInclude Include="transport\TransportConfig.h" />
<ClInclude Include="transport\PreparedFrameBuffer.h" /> <ClInclude Include="transport\PreparedFrameBuffer.h" />
<ClInclude Include="transport\TransportFactory.h" /> <ClInclude Include="transport\TransportFactory.h" />
<ClInclude Include="transport\lgmp\CIVSHMEM.h" /> <ClInclude Include="transport\lgmp\CIVSHMEM.h" />

View File

@@ -193,6 +193,9 @@
<ClInclude Include="transport\ITransport.h"> <ClInclude Include="transport\ITransport.h">
<Filter>Transport</Filter> <Filter>Transport</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="transport\TransportConfig.h">
<Filter>Transport</Filter>
</ClInclude>
<ClInclude Include="transport\PreparedFrameBuffer.h"> <ClInclude Include="transport\PreparedFrameBuffer.h">
<Filter>Transport</Filter> <Filter>Transport</Filter>
</ClInclude> </ClInclude>
@@ -324,6 +327,9 @@
<ClCompile Include="transport\CTransportManager.cpp"> <ClCompile Include="transport\CTransportManager.cpp">
<Filter>Transport</Filter> <Filter>Transport</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="transport\TransportConfig.cpp">
<Filter>Transport</Filter>
</ClCompile>
<ClCompile Include="transport\TransportFactory.cpp"> <ClCompile Include="transport\TransportFactory.cpp">
<Filter>Transport</Filter> <Filter>Transport</Filter>
</ClCompile> </ClCompile>

View File

@@ -80,6 +80,21 @@ CSettings::DisplayModes CSettings::LoadModes()
return displayModes; return displayModes;
} }
TransportInstances CSettings::LoadTransportInstances() const
{
std::vector<std::wstring> entries;
if (!ReadMultiStringValue(L"TransportInstances", entries))
return DefaultTransportInstances();
TransportInstances instances;
if (!ParseTransportInstances(entries, instances))
{
DEBUG_WARN("Invalid transport instance configuration; using defaults");
return DefaultTransportInstances();
}
return instances;
}
bool CSettings::SetExtraMode(const DisplayMode& mode) bool CSettings::SetExtraMode(const DisplayMode& mode)
{ {
WCHAR buf[64]; WCHAR buf[64];
@@ -272,7 +287,8 @@ unsigned CSettings::GetDefaultRefreshMilliHz() const
return refreshMilliHz ? refreshMilliHz : 60000; return refreshMilliHz ? refreshMilliHz : 60000;
} }
bool CSettings::ReadModesValue(std::vector<std::wstring> &out) const bool CSettings::ReadMultiStringValue(const wchar_t * name,
std::vector<std::wstring>& out) const
{ {
HKEY hKey = nullptr; HKEY hKey = nullptr;
LONG st = RegOpenKeyExW(HKEY_LOCAL_MACHINE, LGIDD_REGKEY, 0, KEY_QUERY_VALUE, &hKey); LONG st = RegOpenKeyExW(HKEY_LOCAL_MACHINE, LGIDD_REGKEY, 0, KEY_QUERY_VALUE, &hKey);
@@ -280,29 +296,44 @@ bool CSettings::ReadModesValue(std::vector<std::wstring> &out) const
return false; return false;
DWORD type = 0, cb = 0; DWORD type = 0, cb = 0;
st = RegGetValueW(hKey, nullptr, L"Modes", RRF_RT_REG_MULTI_SZ, &type, nullptr, &cb); st = RegGetValueW(hKey, nullptr, name, RRF_RT_REG_MULTI_SZ,
&type, nullptr, &cb);
if (st != ERROR_SUCCESS || cb == 0) if (st != ERROR_SUCCESS || cb == 0)
{ {
RegCloseKey(hKey); RegCloseKey(hKey);
return false; return false;
} }
std::vector<wchar_t> buf(cb / sizeof(wchar_t)); const DWORD capacity = cb;
st = RegGetValueW(hKey, nullptr, L"Modes", RRF_RT_REG_MULTI_SZ, &type, buf.data(), &cb); std::vector<wchar_t> buf(
capacity / sizeof(wchar_t) + 2, L'\0');
st = RegGetValueW(hKey, nullptr, name, RRF_RT_REG_MULTI_SZ,
&type, buf.data(), &cb);
RegCloseKey(hKey); RegCloseKey(hKey);
if (st != ERROR_SUCCESS) if (st != ERROR_SUCCESS || cb > capacity || cb % sizeof(wchar_t))
return false; return false;
const wchar_t* p = buf.data(); const size_t length = cb / sizeof(wchar_t);
while (*p) size_t begin = 0;
while (begin < length && buf[begin])
{ {
out.emplace_back(p); size_t end = begin;
p += (wcslen(p) + 1); while (end < length && buf[end])
++end;
if (end == length)
return false;
out.emplace_back(buf.data() + begin, end - begin);
begin = end + 1;
} }
return !out.empty(); return !out.empty();
} }
bool CSettings::ReadModesValue(std::vector<std::wstring> &out) const
{
return ReadMultiStringValue(L"Modes", out);
}
static std::wstring trim(const std::wstring &s) static std::wstring trim(const std::wstring &s)
{ {
size_t b = 0, e = s.size(); size_t b = 0, e = s.size();

View File

@@ -19,6 +19,8 @@
*/ */
#pragma once #pragma once
#include "transport/TransportConfig.h"
#include <windows.h> #include <windows.h>
#include <vector> #include <vector>
#include <string> #include <string>
@@ -39,6 +41,7 @@ class CSettings
CSettings(); CSettings();
DisplayModes LoadModes(); DisplayModes LoadModes();
TransportInstances LoadTransportInstances() const;
bool SetExtraMode(const DisplayMode & mode); bool SetExtraMode(const DisplayMode & mode);
bool GetExtraMode(DisplayMode & mode); bool GetExtraMode(DisplayMode & mode);
unsigned GetDefaultRefreshMilliHz() const; unsigned GetDefaultRefreshMilliHz() const;
@@ -47,6 +50,8 @@ class CSettings
bool ReadBoolValue(const wchar_t* name, bool defaultValue = false); bool ReadBoolValue(const wchar_t* name, bool defaultValue = false);
private: private:
bool ReadMultiStringValue(const wchar_t * name,
std::vector<std::wstring>& out) const;
bool ReadModesValue(std::vector<std::wstring> &out) const; bool ReadModesValue(std::vector<std::wstring> &out) const;
bool ParseModeString(const std::wstring& in, DisplayMode& out); bool ParseModeString(const std::wstring& in, DisplayMode& out);
}; };

View File

@@ -238,29 +238,34 @@ void CTransportManager::EndCall(Entry& entry,
} }
} }
bool CTransportManager::Add(BackendId id, const char * name, bool required, bool CTransportManager::Add(TransportInstance config, bool primary,
bool primary, CreateFn create) CreateFn create)
{ {
CSRWExclusiveLock managerLock(m_lock); CSRWExclusiveLock managerLock(m_lock);
if (!m_phaseIdle || !m_stoppedEvent || m_phase != Phase::IDLE || if (!m_phaseIdle || !m_stoppedEvent || m_phase != Phase::IDLE ||
m_started || m_stopping || m_started || m_stopping ||
m_stopped || !id || !name || !create || m_stopped || !config.enabled || !config.id || config.kind.empty() ||
(config.services & ~TRANSPORT_SERVICE_ALL) || !create ||
m_entryCount == FRAME_MAX_SINKS || (primary && m_primary)) m_entryCount == FRAME_MAX_SINKS || (primary && m_primary))
return false; return false;
if (primary && (!config.required ||
!(config.services & TRANSPORT_SERVICE_FRAME)))
return false;
for (unsigned i = 0; i < m_entryCount; ++i) for (unsigned i = 0; i < m_entryCount; ++i)
if (m_entries[i]->id == id) if (m_entries[i]->id == config.id)
return false; return false;
std::unique_ptr<Entry> entry(new (std::nothrow) Entry); std::unique_ptr<Entry> entry(new (std::nothrow) Entry);
if (!entry || !entry->idleEvent) if (!entry || !entry->idleEvent)
return false; return false;
entry->id = id; entry->id = config.id;
entry->name = name; entry->required = config.required;
entry->required = required;
entry->primary = primary; entry->primary = primary;
entry->create = create; entry->create = create;
entry->config = std::move(config);
Entry * raw = entry.get(); Entry * raw = entry.get();
m_entries[m_entryCount++] = std::move(entry); m_entries[m_entryCount++] = std::move(entry);
@@ -273,15 +278,17 @@ ITransport::OpenResult CTransportManager::OpenEntry(Entry& entry)
{ {
std::shared_ptr<ITransport> transport; std::shared_ptr<ITransport> transport;
CreateFn create = nullptr; CreateFn create = nullptr;
TransportInstance config;
{ {
CSRWSharedLock entryLock(entry.lock); CSRWSharedLock entryLock(entry.lock);
transport = entry.transport; transport = entry.transport;
create = entry.create; create = entry.create;
config = entry.config;
} }
if (!transport) if (!transport)
{ {
std::unique_ptr<ITransport> created = create(); std::unique_ptr<ITransport> created = create(config);
transport.reset(created.release()); transport.reset(created.release());
CSRWExclusiveLock entryLock(entry.lock); CSRWExclusiveLock entryLock(entry.lock);
entry.transport = transport; entry.transport = transport;
@@ -353,6 +360,8 @@ bool CTransportManager::AddServices(Entry& entry)
BackendId id = 0; BackendId id = 0;
uint32_t epoch = 0; uint32_t epoch = 0;
bool primary = false; bool primary = false;
bool required = false;
uint32_t services = 0;
bool controlAdded = false; bool controlAdded = false;
bool controlFailed = false; bool controlFailed = false;
bool controlAbsent = false; bool controlAbsent = false;
@@ -368,6 +377,8 @@ bool CTransportManager::AddServices(Entry& entry)
id = entry.id; id = entry.id;
epoch = entry.epoch; epoch = entry.epoch;
primary = entry.primary; primary = entry.primary;
required = entry.required;
services = entry.config.services;
controlAdded = entry.controlAdded; controlAdded = entry.controlAdded;
controlFailed = entry.controlFailed; controlFailed = entry.controlFailed;
controlAbsent = entry.controlAbsent; controlAbsent = entry.controlAbsent;
@@ -380,14 +391,16 @@ bool CTransportManager::AddServices(Entry& entry)
} }
if (!transport) if (!transport)
return !primary; return !required;
const uint64_t now = GetTickCount64(); const uint64_t now = GetTickCount64();
const bool attach = now >= retryAt; const bool attach = now >= retryAt;
bool controlRetry = false; bool controlRetry = false;
bool frameRetry = false; bool frameRetry = false;
bool inputRetry = false; bool inputRetry = false;
if (attach && !controlAdded && !controlFailed && !controlAbsent) if (!(services & TRANSPORT_SERVICE_CONTROL))
controlAbsent = true;
else if (attach && !controlAdded && !controlFailed && !controlAbsent)
{ {
IControlSink * control = transport->Control(); IControlSink * control = transport->Control();
if (control && m_control.Add(id, epoch, *control)) if (control && m_control.Add(id, epoch, *control))
@@ -406,7 +419,9 @@ bool CTransportManager::AddServices(Entry& entry)
} }
} }
if (attach && !frameAdded && !frameAbsent) if (!(services & TRANSPORT_SERVICE_FRAME))
frameAbsent = true;
else if (attach && !frameAdded && !frameAbsent)
{ {
IFrameSink * frame = transport->FrameSink(); IFrameSink * frame = transport->FrameSink();
if (frame && m_frames.Bind(id, epoch, primary, *frame)) if (frame && m_frames.Bind(id, epoch, primary, *frame))
@@ -425,7 +440,9 @@ bool CTransportManager::AddServices(Entry& entry)
} }
} }
if (attach && !inputAdded && !inputFailed && !inputAbsent) if (!(services & TRANSPORT_SERVICE_INPUT))
inputAbsent = true;
else if (attach && !inputAdded && !inputFailed && !inputAbsent)
{ {
IInputSource * input = transport->Input(); IInputSource * input = transport->Input();
if (input && m_input.Bind(id, epoch, *input)) if (input && m_input.Bind(id, epoch, *input))
@@ -450,7 +467,11 @@ bool CTransportManager::AddServices(Entry& entry)
entry.serviceRetryAt = now + SERVICE_RETRY_DELAY_MS; entry.serviceRetryAt = now + SERVICE_RETRY_DELAY_MS;
} }
return !primary || frameAdded; const bool servicesReady =
(!(services & TRANSPORT_SERVICE_FRAME) || frameAdded) &&
(!(services & TRANSPORT_SERVICE_CONTROL) || controlAdded) &&
(!(services & TRANSPORT_SERVICE_INPUT) || inputAdded);
return !required || servicesReady;
} }
void CTransportManager::HandleServiceFailures() void CTransportManager::HandleServiceFailures()
@@ -650,26 +671,25 @@ void CTransportManager::HandleProcessResult(
bool exposed = false; bool exposed = false;
bool primary = false; bool primary = false;
bool required = false; std::wstring name;
const char * name = nullptr;
{ {
CSRWSharedLock entryLock(entry.lock); CSRWSharedLock entryLock(entry.lock);
exposed = entry.exposed; exposed = entry.exposed;
primary = entry.primary; primary = entry.primary;
required = entry.required; name = entry.config.kind;
name = entry.name;
}
if (primary && exposed)
{
(void)name;
DEBUG_WARN("Transport %s requested a restart while its frame interfaces "
"are active", name);
return;
} }
RemoveServices(entry); RemoveServices(entry);
if (result == ProcessResult::RETRY || !required) if (primary && exposed)
{
DEBUG_WARN("Transport %ls stopped while its frame interfaces are active",
name.c_str());
CSRWExclusiveLock entryLock(entry.lock);
entry.state = State::FAILED;
return;
}
if (result == ProcessResult::RETRY)
{ {
ScheduleRetry(entry); ScheduleRetry(entry);
return; return;
@@ -774,6 +794,9 @@ bool CTransportManager::Initialize()
{ {
CSRWSharedLock entryLock(entry.lock); CSRWSharedLock entryLock(entry.lock);
transport = entry.transport; transport = entry.transport;
if (entry.required && entry.state != State::INITIALIZED &&
entry.state != State::READY)
success = false;
} }
EndCall(entry, transport); EndCall(entry, transport);
} }
@@ -818,7 +841,7 @@ bool CTransportManager::Setup(size_t alignment)
Entry& entry = *entries[i]; Entry& entry = *entries[i];
if (!BeginCall(entry, Call::LIFECYCLE, true)) if (!BeginCall(entry, Call::LIFECYCLE, true))
{ {
if (entry.primary) if (entry.required)
success = false; success = false;
continue; continue;
} }
@@ -831,14 +854,18 @@ bool CTransportManager::Setup(size_t alignment)
transport = entry.transport; transport = entry.transport;
} }
if ((state == State::INITIALIZED || state == State::READY) && if (state != State::INITIALIZED && state != State::READY)
!SetupEntry(entry, alignment)) {
if (entry.required)
success = false;
}
else if (!SetupEntry(entry, alignment))
{ {
{ {
CSRWSharedLock entryLock(entry.lock); CSRWSharedLock entryLock(entry.lock);
state = entry.state; state = entry.state;
} }
if (entry.primary) if (entry.required)
success = false; success = false;
else if (state == State::FAILED) else if (state == State::FAILED)
ScheduleRetry(entry); ScheduleRetry(entry);

View File

@@ -25,13 +25,18 @@
#include "transport/CFrameHub.h" #include "transport/CFrameHub.h"
#include "transport/CInputHub.h" #include "transport/CInputHub.h"
#include "transport/ITransport.h" #include "transport/ITransport.h"
#include "transport/TransportConfig.h"
#include <memory> #include <memory>
static_assert(TRANSPORT_MAX_INSTANCES == FRAME_MAX_SINKS,
"The transport and frame limits must match");
class CTransportManager final class CTransportManager final
{ {
public: public:
using CreateFn = std::unique_ptr<ITransport> (*)(); using CreateFn = std::unique_ptr<ITransport> (*)(
const TransportInstance& config);
using OpenResult = ITransport::OpenResult; using OpenResult = ITransport::OpenResult;
using ProcessResult = ITransport::ProcessResult; using ProcessResult = ITransport::ProcessResult;
using Recovery = ITransport::Recovery; using Recovery = ITransport::Recovery;
@@ -90,7 +95,7 @@ private:
DWORD callOwner = 0; DWORD callOwner = 0;
bool stopRequested = false; bool stopRequested = false;
BackendId id = 0; BackendId id = 0;
const char * name = nullptr; TransportInstance config;
bool required = false; bool required = false;
bool primary = false; bool primary = false;
CreateFn create = nullptr; CreateFn create = nullptr;
@@ -166,8 +171,7 @@ public:
CTransportManager(const CTransportManager&) = delete; CTransportManager(const CTransportManager&) = delete;
CTransportManager& operator=(const CTransportManager&) = delete; CTransportManager& operator=(const CTransportManager&) = delete;
bool Add(BackendId id, const char * name, bool required, bool primary, bool Add(TransportInstance config, bool primary, CreateFn create);
CreateFn create);
OpenResult Open(); OpenResult Open();
bool Initialize(); bool Initialize();

View File

@@ -22,6 +22,7 @@
#include "transport/DirectFrameBufferMemory.h" #include "transport/DirectFrameBufferMemory.h"
#include "transport/FrameMemoryLimits.h" #include "transport/FrameMemoryLimits.h"
#include "transport/TransportConfig.h"
#include <stddef.h> #include <stddef.h>
#include <stdint.h> #include <stdint.h>
@@ -30,8 +31,6 @@ class IControlSink;
class IFrameSink; class IFrameSink;
class IInputSource; class IInputSource;
using BackendId = uint32_t;
struct SourceKey struct SourceKey
{ {
BackendId backend = 0; BackendId backend = 0;

View File

@@ -0,0 +1,373 @@
/**
* 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/TransportConfig.h"
#include <errno.h>
#include <limits.h>
#include <stdlib.h>
#include <wctype.h>
namespace
{
enum Field : unsigned
{
FIELD_ID = 1U << 0,
FIELD_KIND = 1U << 1,
FIELD_ENABLED = 1U << 2,
FIELD_REQUIRED = 1U << 3,
FIELD_SERVICES = 1U << 4,
FIELD_PRIORITY = 1U << 5,
FIELD_SETTINGS = 1U << 6,
};
std::wstring Trim(const std::wstring& value)
{
size_t begin = 0;
size_t end = value.size();
while (begin < end && iswspace(value[begin]))
++begin;
while (end > begin && iswspace(value[end - 1]))
--end;
return value.substr(begin, end - begin);
}
bool Equal(const std::wstring& left, const wchar_t * right)
{
if (!right)
return false;
size_t index = 0;
for (; index < left.size() && right[index]; ++index)
if (towlower(left[index]) != towlower(right[index]))
return false;
return index == left.size() && !right[index];
}
bool ParseBool(const std::wstring& value, bool& result)
{
if (Equal(value, L"true") || value == L"1")
{
result = true;
return true;
}
if (Equal(value, L"false") || value == L"0")
{
result = false;
return true;
}
return false;
}
bool ParseId(const std::wstring& value, BackendId& result)
{
if (value.empty() || value[0] == L'-')
return false;
wchar_t * end = nullptr;
errno = 0;
const unsigned long long parsed = wcstoull(value.c_str(), &end, 10);
if (errno == ERANGE || !end || *end || !parsed || parsed > UINT32_MAX)
return false;
result = static_cast<BackendId>(parsed);
return true;
}
bool ParsePriority(const std::wstring& value, int32_t& result)
{
if (value.empty())
return false;
wchar_t * end = nullptr;
errno = 0;
const long long parsed = wcstoll(value.c_str(), &end, 10);
if (errno == ERANGE || !end || *end || parsed < INT32_MIN ||
parsed > INT32_MAX)
return false;
result = static_cast<int32_t>(parsed);
return true;
}
bool ParseKind(const std::wstring& value, std::wstring& result)
{
if (value.empty() || value.size() > 32)
return false;
for (wchar_t character : value)
if (!iswalnum(character) && character != L'_' && character != L'-')
return false;
result = value;
return true;
}
bool AddService(const std::wstring& name, uint32_t& services)
{
uint32_t service = 0;
if (Equal(name, L"frame"))
service = TRANSPORT_SERVICE_FRAME;
else if (Equal(name, L"control"))
service = TRANSPORT_SERVICE_CONTROL;
else if (Equal(name, L"input"))
service = TRANSPORT_SERVICE_INPUT;
else
return false;
if (services & service)
return false;
services |= service;
return true;
}
bool ParseServices(const std::wstring& value, uint32_t& services)
{
if (Equal(value, L"all"))
{
services = TRANSPORT_SERVICE_ALL;
return true;
}
if (Equal(value, L"none"))
{
services = 0;
return true;
}
services = 0;
size_t begin = 0;
while (begin < value.size())
{
const size_t separator = value.find(L',', begin);
const size_t end = separator == std::wstring::npos ?
value.size() : separator;
if (!AddService(Trim(value.substr(begin, end - begin)), services))
return false;
if (separator == std::wstring::npos)
return true;
begin = separator + 1;
}
return false;
}
bool SetField(const std::wstring& key, const std::wstring& value,
TransportInstance& instance, unsigned& fields)
{
unsigned field = 0;
bool valid = false;
if (Equal(key, L"id"))
{
field = FIELD_ID;
valid = ParseId(Trim(value), instance.id);
}
else if (Equal(key, L"kind"))
{
field = FIELD_KIND;
valid = ParseKind(Trim(value), instance.kind);
}
else if (Equal(key, L"enabled"))
{
field = FIELD_ENABLED;
valid = ParseBool(Trim(value), instance.enabled);
}
else if (Equal(key, L"required"))
{
field = FIELD_REQUIRED;
valid = ParseBool(Trim(value), instance.required);
}
else if (Equal(key, L"services"))
{
field = FIELD_SERVICES;
valid = ParseServices(Trim(value), instance.services);
}
else if (Equal(key, L"priority"))
{
field = FIELD_PRIORITY;
valid = ParsePriority(Trim(value), instance.priority);
}
if (!field || (fields & field) || !valid)
return false;
fields |= field;
return true;
}
bool ParseInstance(const std::wstring& source,
TransportInstance& instance)
{
const std::wstring& line = source;
if (Trim(line).empty() || line.size() > 4096)
return false;
unsigned fields = 0;
size_t begin = 0;
while (begin < line.size())
{
const size_t separator = line.find(L';', begin);
const size_t end = separator == std::wstring::npos ?
line.size() : separator;
const std::wstring field = line.substr(begin, end - begin);
const size_t equals = field.find(L'=');
if (equals == std::wstring::npos)
return false;
const std::wstring key = Trim(field.substr(0, equals));
if (Equal(key, L"settings"))
{
if (fields & FIELD_SETTINGS)
return false;
instance.settings = line.substr(begin + equals + 1);
fields |= FIELD_SETTINGS;
begin = line.size();
break;
}
if (!SetField(key, field.substr(equals + 1), instance, fields))
return false;
if (separator == std::wstring::npos)
break;
begin = separator + 1;
if (begin == line.size())
return false;
}
return (fields & (FIELD_ID | FIELD_KIND)) ==
(FIELD_ID | FIELD_KIND);
}
int FindKind(const std::wstring& name,
const TransportKind * kinds, unsigned kindCount)
{
for (unsigned i = 0; i < kindCount; ++i)
if (kinds[i].name && Equal(name, kinds[i].name))
return static_cast<int>(i);
return -1;
}
bool TryResolve(const TransportInstances& instances,
const TransportKind * kinds, unsigned kindCount,
ResolvedTransportInstances& resolved)
{
if (!kinds || !kindCount || instances.empty() ||
instances.size() > TRANSPORT_MAX_INSTANCES)
return false;
ResolvedTransportInstances result;
int primary = -1;
for (const TransportInstance& instance : instances)
{
if (!instance.id || instance.kind.empty() ||
(instance.services & ~TRANSPORT_SERVICE_ALL))
return false;
for (const TransportInstance& other : instances)
if (&other != &instance && other.id == instance.id)
return false;
const int kindIndex = FindKind(instance.kind, kinds, kindCount);
if (kindIndex < 0 || !kinds[kindIndex].activeLimit)
return false;
if (!instance.enabled)
continue;
unsigned active = 0;
for (const ResolvedTransportInstance& existing : result)
if (existing.kindIndex == static_cast<unsigned>(kindIndex))
++active;
if (active == kinds[kindIndex].activeLimit ||
result.size() == TRANSPORT_MAX_INSTANCES)
return false;
ResolvedTransportInstance selected;
selected.config = instance;
selected.kindIndex = static_cast<unsigned>(kindIndex);
if (instance.required &&
(instance.services & TRANSPORT_SERVICE_FRAME) &&
(primary < 0 || instance.priority >
result[primary].config.priority))
primary = static_cast<int>(result.size());
result.push_back(selected);
}
if (primary < 0)
return false;
result[primary].primary = true;
resolved.swap(result);
return true;
}
}
TransportInstances DefaultTransportInstances()
{
TransportInstance instance;
instance.id = 1;
instance.kind = L"LGMP";
instance.enabled = true;
instance.required = true;
instance.services = TRANSPORT_SERVICE_ALL;
instance.priority = 0;
TransportInstances instances;
instances.push_back(instance);
return instances;
}
bool ParseTransportInstances(const std::vector<std::wstring>& entries,
TransportInstances& instances)
{
TransportInstances parsed;
if (entries.empty() || entries.size() > TRANSPORT_MAX_INSTANCES)
return false;
bool hasRequiredFrame = false;
for (const std::wstring& entry : entries)
{
TransportInstance instance;
if (!ParseInstance(entry, instance))
return false;
for (const TransportInstance& existing : parsed)
if (existing.id == instance.id)
return false;
if (instance.enabled && instance.required &&
(instance.services & TRANSPORT_SERVICE_FRAME))
hasRequiredFrame = true;
parsed.push_back(instance);
}
if (!hasRequiredFrame)
return false;
instances.swap(parsed);
return true;
}
bool ResolveTransportInstances(const TransportInstances& configured,
const TransportKind * kinds, unsigned kindCount,
ResolvedTransportInstances& resolved, bool& usedDefaults)
{
usedDefaults = false;
if (TryResolve(configured, kinds, kindCount, resolved))
return true;
usedDefaults = true;
const TransportInstances defaults = DefaultTransportInstances();
if (TryResolve(defaults, kinds, kindCount, resolved))
return true;
resolved.clear();
return false;
}

View File

@@ -0,0 +1,74 @@
/**
* 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 <stdint.h>
#include <string>
#include <vector>
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,
};
struct TransportInstance
{
BackendId id = 0;
std::wstring kind;
bool enabled = true;
bool required = false;
uint32_t services = TRANSPORT_SERVICE_ALL;
int32_t priority = 0;
std::wstring settings;
};
using TransportInstances = std::vector<TransportInstance>;
static const unsigned TRANSPORT_MAX_INSTANCES = 8;
struct TransportKind
{
const wchar_t * name;
unsigned activeLimit;
};
struct ResolvedTransportInstance
{
TransportInstance config;
unsigned kindIndex = 0;
bool primary = false;
};
using ResolvedTransportInstances =
std::vector<ResolvedTransportInstance>;
TransportInstances DefaultTransportInstances();
bool ParseTransportInstances(const std::vector<std::wstring>& entries,
TransportInstances& instances);
bool ResolveTransportInstances(const TransportInstances& configured,
const TransportKind * kinds, unsigned kindCount,
ResolvedTransportInstances& resolved, bool& usedDefaults);

View File

@@ -20,21 +20,66 @@
#include "transport/TransportFactory.h" #include "transport/TransportFactory.h"
#include "CDebug.h"
#include "config/CSettings.h"
#include "transport/CTransportManager.h" #include "transport/CTransportManager.h"
#include "transport/lgmp/CLGMPTransport.h" #include "transport/lgmp/CLGMPTransport.h"
#include <new> #include <new>
static std::unique_ptr<ITransport> CreateLGMP() namespace
{ {
return std::unique_ptr<ITransport>(new (std::nothrow) CLGMPTransport()); struct Provider
{
CTransportManager::CreateFn create;
};
std::unique_ptr<ITransport> CreateLGMP(
const TransportInstance& config)
{
return std::unique_ptr<ITransport>(
new (std::nothrow) CLGMPTransport(config));
}
const TransportKind KINDS[] =
{
{ L"LGMP", 1 },
};
const Provider PROVIDERS[] =
{
{ CreateLGMP },
};
static_assert(ARRAYSIZE(KINDS) == ARRAYSIZE(PROVIDERS),
"Every transport kind must have a provider");
std::unique_ptr<CTransportManager> Build(
const ResolvedTransportInstances& instances)
{
std::unique_ptr<CTransportManager> manager(
new (std::nothrow) CTransportManager());
if (!manager)
return std::unique_ptr<CTransportManager>();
for (const ResolvedTransportInstance& instance : instances)
if (!manager->Add(instance.config, instance.primary,
PROVIDERS[instance.kindIndex].create))
return std::unique_ptr<CTransportManager>();
return manager;
}
} }
std::unique_ptr<CTransportManager> CreateTransport() std::unique_ptr<CTransportManager> CreateTransport()
{ {
std::unique_ptr<CTransportManager> manager( TransportInstances instances = g_settings.LoadTransportInstances();
new (std::nothrow) CTransportManager()); ResolvedTransportInstances resolved;
if (!manager || !manager->Add(1, "LGMP", true, true, CreateLGMP)) bool usedDefaults = false;
if (!ResolveTransportInstances(instances, KINDS, ARRAYSIZE(KINDS),
resolved, usedDefaults))
return std::unique_ptr<CTransportManager>(); return std::unique_ptr<CTransportManager>();
return manager; if (usedDefaults)
DEBUG_WARN("Unsupported transport instance configuration; using defaults");
return Build(resolved);
} }

View File

@@ -48,7 +48,8 @@ static bool TranslateFrameScheduleFlags(
return true; return true;
} }
CLGMPTransport::CLGMPTransport() : CLGMPTransport::CLGMPTransport(const TransportInstance& config) :
m_config(config),
m_control(m_host), m_control(m_host),
m_frames(m_host, m_ivshmem), m_frames(m_host, m_ivshmem),
m_input(m_host) m_input(m_host)

View File

@@ -33,6 +33,7 @@
class CLGMPTransport final : public ITransport class CLGMPTransport final : public ITransport
{ {
private: private:
TransportInstance m_config;
// Keep this declaration order. Destruction must release frame and control // Keep this declaration order. Destruction must release frame and control
// allocations before the LGMP host and its IVSHMEM mapping are destroyed. // allocations before the LGMP host and its IVSHMEM mapping are destroyed.
CIVSHMEM m_ivshmem; CIVSHMEM m_ivshmem;
@@ -44,7 +45,7 @@ private:
std::atomic<bool> m_ready = false; std::atomic<bool> m_ready = false;
public: public:
CLGMPTransport(); explicit CLGMPTransport(const TransportInstance& config);
~CLGMPTransport() override = default; ~CLGMPTransport() override = default;
CLGMPTransport(const CLGMPTransport&) = delete; CLGMPTransport(const CLGMPTransport&) = delete;