mirror of
https://github.com/gnif/LookingGlass.git
synced 2026-08-22 07:01:30 +00:00
[idd] transport: configure active instances
This commit is contained in:
@@ -64,6 +64,7 @@
|
||||
<ClCompile Include="transport\CFrameHub.cpp" />
|
||||
<ClCompile Include="transport\CInputHub.cpp" />
|
||||
<ClCompile Include="transport\CTransportManager.cpp" />
|
||||
<ClCompile Include="transport\TransportConfig.cpp" />
|
||||
<ClCompile Include="transport\TransportFactory.cpp" />
|
||||
<ClCompile Include="transport\lgmp\CIVSHMEM.cpp" />
|
||||
<ClCompile Include="transport\lgmp\CRecovery.cpp" />
|
||||
@@ -123,6 +124,7 @@
|
||||
<ClInclude Include="transport\IInputTransport.h" />
|
||||
<ClInclude Include="transport\IInputSource.h" />
|
||||
<ClInclude Include="transport\ITransport.h" />
|
||||
<ClInclude Include="transport\TransportConfig.h" />
|
||||
<ClInclude Include="transport\PreparedFrameBuffer.h" />
|
||||
<ClInclude Include="transport\TransportFactory.h" />
|
||||
<ClInclude Include="transport\lgmp\CIVSHMEM.h" />
|
||||
|
||||
@@ -193,6 +193,9 @@
|
||||
<ClInclude Include="transport\ITransport.h">
|
||||
<Filter>Transport</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="transport\TransportConfig.h">
|
||||
<Filter>Transport</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="transport\PreparedFrameBuffer.h">
|
||||
<Filter>Transport</Filter>
|
||||
</ClInclude>
|
||||
@@ -324,6 +327,9 @@
|
||||
<ClCompile Include="transport\CTransportManager.cpp">
|
||||
<Filter>Transport</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="transport\TransportConfig.cpp">
|
||||
<Filter>Transport</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="transport\TransportFactory.cpp">
|
||||
<Filter>Transport</Filter>
|
||||
</ClCompile>
|
||||
|
||||
@@ -80,6 +80,21 @@ CSettings::DisplayModes CSettings::LoadModes()
|
||||
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)
|
||||
{
|
||||
WCHAR buf[64];
|
||||
@@ -272,7 +287,8 @@ unsigned CSettings::GetDefaultRefreshMilliHz() const
|
||||
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;
|
||||
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;
|
||||
|
||||
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<wchar_t> 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<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);
|
||||
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<std::wstring> &out) const
|
||||
{
|
||||
return ReadMultiStringValue(L"Modes", out);
|
||||
}
|
||||
|
||||
static std::wstring trim(const std::wstring &s)
|
||||
{
|
||||
size_t b = 0, e = s.size();
|
||||
|
||||
@@ -19,6 +19,8 @@
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include "transport/TransportConfig.h"
|
||||
|
||||
#include <windows.h>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
@@ -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<std::wstring>& out) const;
|
||||
bool ReadModesValue(std::vector<std::wstring> &out) const;
|
||||
bool ParseModeString(const std::wstring& in, DisplayMode& out);
|
||||
};
|
||||
|
||||
@@ -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> 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<ITransport> transport;
|
||||
CreateFn create = nullptr;
|
||||
TransportInstance config;
|
||||
{
|
||||
CSRWSharedLock entryLock(entry.lock);
|
||||
transport = entry.transport;
|
||||
create = entry.create;
|
||||
config = entry.config;
|
||||
}
|
||||
|
||||
if (!transport)
|
||||
{
|
||||
std::unique_ptr<ITransport> created = create();
|
||||
std::unique_ptr<ITransport> 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);
|
||||
|
||||
@@ -25,13 +25,18 @@
|
||||
#include "transport/CFrameHub.h"
|
||||
#include "transport/CInputHub.h"
|
||||
#include "transport/ITransport.h"
|
||||
#include "transport/TransportConfig.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
static_assert(TRANSPORT_MAX_INSTANCES == FRAME_MAX_SINKS,
|
||||
"The transport and frame limits must match");
|
||||
|
||||
class CTransportManager final
|
||||
{
|
||||
public:
|
||||
using CreateFn = std::unique_ptr<ITransport> (*)();
|
||||
using CreateFn = std::unique_ptr<ITransport> (*)(
|
||||
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();
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
|
||||
#include "transport/DirectFrameBufferMemory.h"
|
||||
#include "transport/FrameMemoryLimits.h"
|
||||
#include "transport/TransportConfig.h"
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
@@ -30,8 +31,6 @@ class IControlSink;
|
||||
class IFrameSink;
|
||||
class IInputSource;
|
||||
|
||||
using BackendId = uint32_t;
|
||||
|
||||
struct SourceKey
|
||||
{
|
||||
BackendId backend = 0;
|
||||
|
||||
373
idd/LGIdd/transport/TransportConfig.cpp
Normal file
373
idd/LGIdd/transport/TransportConfig.cpp
Normal 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;
|
||||
}
|
||||
74
idd/LGIdd/transport/TransportConfig.h
Normal file
74
idd/LGIdd/transport/TransportConfig.h
Normal 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);
|
||||
@@ -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 <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> 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<CTransportManager>();
|
||||
return manager;
|
||||
if (usedDefaults)
|
||||
DEBUG_WARN("Unsupported transport instance configuration; using defaults");
|
||||
|
||||
return Build(resolved);
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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<bool> m_ready = false;
|
||||
|
||||
public:
|
||||
CLGMPTransport();
|
||||
explicit CLGMPTransport(const TransportInstance& config);
|
||||
~CLGMPTransport() override = default;
|
||||
|
||||
CLGMPTransport(const CLGMPTransport&) = delete;
|
||||
|
||||
Reference in New Issue
Block a user