diff --git a/idd/LGIdd/LGIdd.vcxproj b/idd/LGIdd/LGIdd.vcxproj index 59174665..98d168bb 100644 --- a/idd/LGIdd/LGIdd.vcxproj +++ b/idd/LGIdd/LGIdd.vcxproj @@ -64,6 +64,7 @@ + @@ -123,6 +124,7 @@ + diff --git a/idd/LGIdd/LGIdd.vcxproj.filters b/idd/LGIdd/LGIdd.vcxproj.filters index 723e3880..4940774e 100644 --- a/idd/LGIdd/LGIdd.vcxproj.filters +++ b/idd/LGIdd/LGIdd.vcxproj.filters @@ -193,6 +193,9 @@ Transport + + Transport + Transport @@ -324,6 +327,9 @@ Transport + + Transport + Transport diff --git a/idd/LGIdd/config/CSettings.cpp b/idd/LGIdd/config/CSettings.cpp index a9efdd1a..b431bd19 100644 --- a/idd/LGIdd/config/CSettings.cpp +++ b/idd/LGIdd/config/CSettings.cpp @@ -80,6 +80,21 @@ CSettings::DisplayModes CSettings::LoadModes() return displayModes; } +TransportInstances CSettings::LoadTransportInstances() const +{ + std::vector 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) { WCHAR buf[64]; @@ -272,7 +287,8 @@ unsigned CSettings::GetDefaultRefreshMilliHz() const return refreshMilliHz ? refreshMilliHz : 60000; } -bool CSettings::ReadModesValue(std::vector &out) const +bool CSettings::ReadMultiStringValue(const wchar_t * name, + std::vector& out) const { HKEY hKey = nullptr; LONG st = RegOpenKeyExW(HKEY_LOCAL_MACHINE, LGIDD_REGKEY, 0, KEY_QUERY_VALUE, &hKey); @@ -280,29 +296,44 @@ bool CSettings::ReadModesValue(std::vector &out) const return false; 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) { RegCloseKey(hKey); return false; } - std::vector buf(cb / sizeof(wchar_t)); - st = RegGetValueW(hKey, nullptr, L"Modes", RRF_RT_REG_MULTI_SZ, &type, buf.data(), &cb); + const DWORD capacity = cb; + std::vector buf( + capacity / sizeof(wchar_t) + 2, L'\0'); + st = RegGetValueW(hKey, nullptr, name, RRF_RT_REG_MULTI_SZ, + &type, buf.data(), &cb); RegCloseKey(hKey); - if (st != ERROR_SUCCESS) + if (st != ERROR_SUCCESS || cb > capacity || cb % sizeof(wchar_t)) return false; - const wchar_t* p = buf.data(); - while (*p) + const size_t length = cb / sizeof(wchar_t); + size_t begin = 0; + while (begin < length && buf[begin]) { - out.emplace_back(p); - p += (wcslen(p) + 1); + size_t end = begin; + 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(); } +bool CSettings::ReadModesValue(std::vector &out) const +{ + return ReadMultiStringValue(L"Modes", out); +} + static std::wstring trim(const std::wstring &s) { size_t b = 0, e = s.size(); diff --git a/idd/LGIdd/config/CSettings.h b/idd/LGIdd/config/CSettings.h index 7076b7c9..82572420 100644 --- a/idd/LGIdd/config/CSettings.h +++ b/idd/LGIdd/config/CSettings.h @@ -19,6 +19,8 @@ */ #pragma once +#include "transport/TransportConfig.h" + #include #include #include @@ -39,6 +41,7 @@ class CSettings CSettings(); DisplayModes LoadModes(); + TransportInstances LoadTransportInstances() const; bool SetExtraMode(const DisplayMode & mode); bool GetExtraMode(DisplayMode & mode); unsigned GetDefaultRefreshMilliHz() const; @@ -47,6 +50,8 @@ class CSettings bool ReadBoolValue(const wchar_t* name, bool defaultValue = false); private: + bool ReadMultiStringValue(const wchar_t * name, + std::vector& out) const; bool ReadModesValue(std::vector &out) const; bool ParseModeString(const std::wstring& in, DisplayMode& out); }; diff --git a/idd/LGIdd/transport/CTransportManager.cpp b/idd/LGIdd/transport/CTransportManager.cpp index 4ce34cd5..644355c6 100644 --- a/idd/LGIdd/transport/CTransportManager.cpp +++ b/idd/LGIdd/transport/CTransportManager.cpp @@ -238,29 +238,34 @@ void CTransportManager::EndCall(Entry& entry, } } -bool CTransportManager::Add(BackendId id, const char * name, bool required, - bool primary, CreateFn create) +bool CTransportManager::Add(TransportInstance config, bool primary, + CreateFn create) { CSRWExclusiveLock managerLock(m_lock); if (!m_phaseIdle || !m_stoppedEvent || m_phase != Phase::IDLE || 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)) return false; + if (primary && (!config.required || + !(config.services & TRANSPORT_SERVICE_FRAME))) + return false; + for (unsigned i = 0; i < m_entryCount; ++i) - if (m_entries[i]->id == id) + if (m_entries[i]->id == config.id) return false; std::unique_ptr entry(new (std::nothrow) Entry); if (!entry || !entry->idleEvent) return false; - entry->id = id; - entry->name = name; - entry->required = required; + entry->id = config.id; + entry->required = config.required; entry->primary = primary; entry->create = create; + entry->config = std::move(config); Entry * raw = entry.get(); m_entries[m_entryCount++] = std::move(entry); @@ -273,15 +278,17 @@ ITransport::OpenResult CTransportManager::OpenEntry(Entry& entry) { std::shared_ptr transport; CreateFn create = nullptr; + TransportInstance config; { CSRWSharedLock entryLock(entry.lock); transport = entry.transport; create = entry.create; + config = entry.config; } if (!transport) { - std::unique_ptr created = create(); + std::unique_ptr created = create(config); transport.reset(created.release()); CSRWExclusiveLock entryLock(entry.lock); entry.transport = transport; @@ -353,6 +360,8 @@ bool CTransportManager::AddServices(Entry& entry) 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; @@ -368,6 +377,8 @@ bool CTransportManager::AddServices(Entry& entry) 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; @@ -380,14 +391,16 @@ bool CTransportManager::AddServices(Entry& entry) } if (!transport) - return !primary; + return !required; const uint64_t now = GetTickCount64(); const bool attach = now >= retryAt; bool controlRetry = false; bool frameRetry = 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(); 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(); 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(); if (input && m_input.Bind(id, epoch, *input)) @@ -450,7 +467,11 @@ bool CTransportManager::AddServices(Entry& entry) 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() @@ -650,26 +671,25 @@ void CTransportManager::HandleProcessResult( bool exposed = false; bool primary = false; - bool required = false; - const char * name = nullptr; + std::wstring name; { CSRWSharedLock entryLock(entry.lock); exposed = entry.exposed; primary = entry.primary; - required = entry.required; - name = entry.name; - } - - if (primary && exposed) - { - (void)name; - DEBUG_WARN("Transport %s requested a restart while its frame interfaces " - "are active", name); - return; + name = entry.config.kind; } 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); return; @@ -774,6 +794,9 @@ bool CTransportManager::Initialize() { CSRWSharedLock entryLock(entry.lock); transport = entry.transport; + if (entry.required && entry.state != State::INITIALIZED && + entry.state != State::READY) + success = false; } EndCall(entry, transport); } @@ -818,7 +841,7 @@ bool CTransportManager::Setup(size_t alignment) Entry& entry = *entries[i]; if (!BeginCall(entry, Call::LIFECYCLE, true)) { - if (entry.primary) + if (entry.required) success = false; continue; } @@ -831,14 +854,18 @@ bool CTransportManager::Setup(size_t alignment) transport = entry.transport; } - if ((state == State::INITIALIZED || state == State::READY) && - !SetupEntry(entry, alignment)) + if (state != State::INITIALIZED && state != State::READY) + { + if (entry.required) + success = false; + } + else if (!SetupEntry(entry, alignment)) { { CSRWSharedLock entryLock(entry.lock); state = entry.state; } - if (entry.primary) + if (entry.required) success = false; else if (state == State::FAILED) ScheduleRetry(entry); diff --git a/idd/LGIdd/transport/CTransportManager.h b/idd/LGIdd/transport/CTransportManager.h index ae3424da..cc12ed94 100644 --- a/idd/LGIdd/transport/CTransportManager.h +++ b/idd/LGIdd/transport/CTransportManager.h @@ -25,13 +25,18 @@ #include "transport/CFrameHub.h" #include "transport/CInputHub.h" #include "transport/ITransport.h" +#include "transport/TransportConfig.h" #include +static_assert(TRANSPORT_MAX_INSTANCES == FRAME_MAX_SINKS, + "The transport and frame limits must match"); + class CTransportManager final { public: - using CreateFn = std::unique_ptr (*)(); + using CreateFn = std::unique_ptr (*)( + const TransportInstance& config); using OpenResult = ITransport::OpenResult; using ProcessResult = ITransport::ProcessResult; using Recovery = ITransport::Recovery; @@ -90,7 +95,7 @@ private: DWORD callOwner = 0; bool stopRequested = false; BackendId id = 0; - const char * name = nullptr; + TransportInstance config; bool required = false; bool primary = false; CreateFn create = nullptr; @@ -166,8 +171,7 @@ public: CTransportManager(const CTransportManager&) = delete; CTransportManager& operator=(const CTransportManager&) = delete; - bool Add(BackendId id, const char * name, bool required, bool primary, - CreateFn create); + bool Add(TransportInstance config, bool primary, CreateFn create); OpenResult Open(); bool Initialize(); diff --git a/idd/LGIdd/transport/ITransport.h b/idd/LGIdd/transport/ITransport.h index b6607449..3924f70e 100644 --- a/idd/LGIdd/transport/ITransport.h +++ b/idd/LGIdd/transport/ITransport.h @@ -22,6 +22,7 @@ #include "transport/DirectFrameBufferMemory.h" #include "transport/FrameMemoryLimits.h" +#include "transport/TransportConfig.h" #include #include @@ -30,8 +31,6 @@ class IControlSink; class IFrameSink; class IInputSource; -using BackendId = uint32_t; - struct SourceKey { BackendId backend = 0; diff --git a/idd/LGIdd/transport/TransportConfig.cpp b/idd/LGIdd/transport/TransportConfig.cpp new file mode 100644 index 00000000..a34025ac --- /dev/null +++ b/idd/LGIdd/transport/TransportConfig.cpp @@ -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 +#include +#include +#include + +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(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(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(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(kindIndex)) + ++active; + if (active == kinds[kindIndex].activeLimit || + result.size() == TRANSPORT_MAX_INSTANCES) + return false; + + ResolvedTransportInstance selected; + selected.config = instance; + selected.kindIndex = static_cast(kindIndex); + if (instance.required && + (instance.services & TRANSPORT_SERVICE_FRAME) && + (primary < 0 || instance.priority > + result[primary].config.priority)) + primary = static_cast(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& 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; +} diff --git a/idd/LGIdd/transport/TransportConfig.h b/idd/LGIdd/transport/TransportConfig.h new file mode 100644 index 00000000..6223d324 --- /dev/null +++ b/idd/LGIdd/transport/TransportConfig.h @@ -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 +#include +#include + +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; + +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; + +TransportInstances DefaultTransportInstances(); +bool ParseTransportInstances(const std::vector& entries, + TransportInstances& instances); +bool ResolveTransportInstances(const TransportInstances& configured, + const TransportKind * kinds, unsigned kindCount, + ResolvedTransportInstances& resolved, bool& usedDefaults); diff --git a/idd/LGIdd/transport/TransportFactory.cpp b/idd/LGIdd/transport/TransportFactory.cpp index 0dd5d73b..2d864175 100644 --- a/idd/LGIdd/transport/TransportFactory.cpp +++ b/idd/LGIdd/transport/TransportFactory.cpp @@ -20,21 +20,66 @@ #include "transport/TransportFactory.h" +#include "CDebug.h" +#include "config/CSettings.h" #include "transport/CTransportManager.h" #include "transport/lgmp/CLGMPTransport.h" #include -static std::unique_ptr CreateLGMP() +namespace { - return std::unique_ptr(new (std::nothrow) CLGMPTransport()); + struct Provider + { + CTransportManager::CreateFn create; + }; + + std::unique_ptr CreateLGMP( + const TransportInstance& config) + { + return std::unique_ptr( + 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 Build( + const ResolvedTransportInstances& instances) + { + std::unique_ptr manager( + new (std::nothrow) CTransportManager()); + if (!manager) + return std::unique_ptr(); + + for (const ResolvedTransportInstance& instance : instances) + if (!manager->Add(instance.config, instance.primary, + PROVIDERS[instance.kindIndex].create)) + return std::unique_ptr(); + return manager; + } } std::unique_ptr CreateTransport() { - std::unique_ptr manager( - new (std::nothrow) CTransportManager()); - if (!manager || !manager->Add(1, "LGMP", true, true, CreateLGMP)) + TransportInstances instances = g_settings.LoadTransportInstances(); + ResolvedTransportInstances resolved; + bool usedDefaults = false; + if (!ResolveTransportInstances(instances, KINDS, ARRAYSIZE(KINDS), + resolved, usedDefaults)) return std::unique_ptr(); - return manager; + if (usedDefaults) + DEBUG_WARN("Unsupported transport instance configuration; using defaults"); + + return Build(resolved); } diff --git a/idd/LGIdd/transport/lgmp/CLGMPTransport.cpp b/idd/LGIdd/transport/lgmp/CLGMPTransport.cpp index cd7f92b6..ae847cce 100644 --- a/idd/LGIdd/transport/lgmp/CLGMPTransport.cpp +++ b/idd/LGIdd/transport/lgmp/CLGMPTransport.cpp @@ -48,7 +48,8 @@ static bool TranslateFrameScheduleFlags( return true; } -CLGMPTransport::CLGMPTransport() : +CLGMPTransport::CLGMPTransport(const TransportInstance& config) : + m_config(config), m_control(m_host), m_frames(m_host, m_ivshmem), m_input(m_host) diff --git a/idd/LGIdd/transport/lgmp/CLGMPTransport.h b/idd/LGIdd/transport/lgmp/CLGMPTransport.h index c7730159..4ac4104f 100644 --- a/idd/LGIdd/transport/lgmp/CLGMPTransport.h +++ b/idd/LGIdd/transport/lgmp/CLGMPTransport.h @@ -33,6 +33,7 @@ class CLGMPTransport final : public ITransport { private: + 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; @@ -44,7 +45,7 @@ private: std::atomic m_ready = false; public: - CLGMPTransport(); + explicit CLGMPTransport(const TransportInstance& config); ~CLGMPTransport() override = default; CLGMPTransport(const CLGMPTransport&) = delete;