#include "RuntimeServices.h" #include "ControlServer.h" #include "OscServer.h" #include "RuntimeControlBridge.h" #include "RuntimeHost.h" #include RuntimeServices::RuntimeServices() : mControlServer(std::make_unique()), mOscServer(std::make_unique()), mPollRunning(false), mRegistryChanged(false), mReloadRequested(false), mPollFailed(false) { } RuntimeServices::~RuntimeServices() { Stop(); } bool RuntimeServices::Start(OpenGLComposite& composite, RuntimeHost& runtimeHost, std::string& error) { Stop(); if (!StartRuntimeControlServices(composite, runtimeHost, *this, *mControlServer, *mOscServer, error)) { Stop(); return false; } return true; } void RuntimeServices::BeginPolling(RuntimeHost& runtimeHost) { StartPolling(runtimeHost); } void RuntimeServices::Stop() { StopPolling(); if (mOscServer) mOscServer->Stop(); if (mControlServer) mControlServer->Stop(); } void RuntimeServices::BroadcastState() { if (mControlServer) mControlServer->BroadcastState(); } void RuntimeServices::RequestBroadcastState() { if (mControlServer) mControlServer->RequestBroadcastState(); } bool RuntimeServices::QueueOscUpdate(const std::string& layerKey, const std::string& parameterKey, const std::string& valueJson, std::string& error) { (void)error; PendingOscUpdate update; update.layerKey = layerKey; update.parameterKey = parameterKey; update.valueJson = valueJson; const std::string routeKey = layerKey + "\n" + parameterKey; { std::lock_guard lock(mPendingOscMutex); mPendingOscUpdates[routeKey] = std::move(update); } return true; } bool RuntimeServices::ApplyPendingOscUpdates(std::vector& appliedUpdates, std::string& error) { appliedUpdates.clear(); std::map pending; { std::lock_guard lock(mPendingOscMutex); if (mPendingOscUpdates.empty()) return true; pending.swap(mPendingOscUpdates); } for (const auto& entry : pending) { JsonValue targetValue; std::string parseError; if (!ParseJson(entry.second.valueJson, targetValue, parseError)) { OutputDebugStringA(("OSC queued value parse failed: " + parseError + "\n").c_str()); continue; } AppliedOscUpdate appliedUpdate; appliedUpdate.routeKey = entry.first; appliedUpdate.layerKey = entry.second.layerKey; appliedUpdate.parameterKey = entry.second.parameterKey; appliedUpdate.targetValue = targetValue; appliedUpdates.push_back(std::move(appliedUpdate)); } (void)error; return true; } bool RuntimeServices::QueueOscCommit(const std::string& routeKey, const std::string& layerKey, const std::string& parameterKey, const JsonValue& value, uint64_t generation, std::string& error) { (void)error; PendingOscCommit commit; commit.routeKey = routeKey; commit.layerKey = layerKey; commit.parameterKey = parameterKey; commit.value = value; commit.generation = generation; { std::lock_guard lock(mPendingOscCommitMutex); mPendingOscCommits[routeKey] = std::move(commit); } return true; } void RuntimeServices::ConsumeCompletedOscCommits(std::vector& completedCommits) { completedCommits.clear(); std::lock_guard lock(mCompletedOscCommitMutex); if (mCompletedOscCommits.empty()) return; completedCommits.swap(mCompletedOscCommits); } RuntimePollEvents RuntimeServices::ConsumePollEvents() { RuntimePollEvents events; events.registryChanged = mRegistryChanged.exchange(false); events.reloadRequested = mReloadRequested.exchange(false); events.failed = mPollFailed.exchange(false); if (events.failed) { std::lock_guard lock(mPollErrorMutex); events.error = mPollError; } return events; } void RuntimeServices::StartPolling(RuntimeHost& runtimeHost) { if (mPollRunning.exchange(true)) return; mPollThread = std::thread([this, &runtimeHost]() { PollLoop(runtimeHost); }); } void RuntimeServices::StopPolling() { if (!mPollRunning.exchange(false)) return; if (mPollThread.joinable()) mPollThread.join(); } void RuntimeServices::PollLoop(RuntimeHost& runtimeHost) { while (mPollRunning) { std::map pendingCommits; { std::lock_guard lock(mPendingOscCommitMutex); pendingCommits.swap(mPendingOscCommits); } for (const auto& entry : pendingCommits) { std::string commitError; if (runtimeHost.UpdateLayerParameterByControlKey( entry.second.layerKey, entry.second.parameterKey, entry.second.value, false, commitError)) { CompletedOscCommit completedCommit; completedCommit.routeKey = entry.second.routeKey; completedCommit.generation = entry.second.generation; std::lock_guard lock(mCompletedOscCommitMutex); mCompletedOscCommits.push_back(std::move(completedCommit)); } else if (!commitError.empty()) { OutputDebugStringA(("OSC commit failed: " + commitError + "\n").c_str()); } } bool registryChanged = false; bool reloadRequested = false; std::string runtimeError; if (!runtimeHost.PollFileChanges(registryChanged, reloadRequested, runtimeError)) { { std::lock_guard lock(mPollErrorMutex); mPollError = runtimeError; } mPollFailed = true; } else { if (registryChanged) mRegistryChanged = true; if (reloadRequested) mReloadRequested = true; } for (int i = 0; i < 25 && mPollRunning; ++i) Sleep(10); } }