Add support to snapshot vars before queue insertion
Some checks failed
debian-build / build (push) Has been cancelled
Check locale / ubuntu64 (push) Has been cancelled
Push to master / Check Formatting 🔍 (push) Has been cancelled
Push to master / Build Project 🧱 (push) Has been cancelled
Push to master / Create Release 🛫 (push) Has been cancelled

This commit is contained in:
WarmUpTill
2026-07-13 20:46:20 +02:00
committed by WarmUpTill
parent 616664e6f3
commit 7be2a4f544
5 changed files with 95 additions and 9 deletions

View File

@@ -45,6 +45,7 @@ void ActionQueue::Save(obs_data_t *obj) const
obs_data_set_string(obj, "name", _name.c_str());
obs_data_set_bool(obj, "runOnStartup", _runOnStartup);
obs_data_set_bool(obj, "resolveVariablesOnAdd", _resolveVariablesOnAdd);
obs_data_set_bool(obj, "cloneVariableContext", _cloneVariableContext);
}
void ActionQueue::Load(obs_data_t *obj)
@@ -54,6 +55,7 @@ void ActionQueue::Load(obs_data_t *obj)
_runOnStartup = obs_data_get_bool(obj, "runOnStartup");
_resolveVariablesOnAdd =
obs_data_get_bool(obj, "resolveVariablesOnAdd");
_cloneVariableContext = obs_data_get_bool(obj, "cloneVariableContext");
if (_runOnStartup) {
Start();
@@ -114,9 +116,17 @@ void ActionQueue::Add(const std::shared_ptr<MacroAction> &action)
copy->PostLoad();
RunAndClearPostLoadSteps();
copy->ResolveVariablesToFixedValues();
_actions.emplace_back(copy);
_actions.push_back({copy, {}});
} else if (_cloneVariableContext) {
auto copy = action->Copy();
OBSDataAutoRelease data = obs_data_create();
action->Save(data);
copy->Load(data);
copy->PostLoad();
RunAndClearPostLoadSteps();
_actions.push_back({copy, CreateVariableContext()});
} else {
_actions.emplace_back(action);
_actions.push_back({action, {}});
}
_cv.notify_all();
}
@@ -143,7 +153,7 @@ size_t ActionQueue::Size()
void ActionQueue::RunActions()
{
std::shared_ptr<MacroAction> action;
QueueEntry entry;
while (true) {
{ // Grab next action to run
std::unique_lock<std::mutex> lock(_mutex);
@@ -156,20 +166,25 @@ void ActionQueue::RunActions()
if (_stop) {
return;
}
action = _actions.front();
entry = _actions.front();
_actions.pop_front();
}
if (!action) {
if (!entry.action) {
continue;
}
if (ActionLoggingEnabled()) {
blog(LOG_INFO, "Performing action '%s' in queue '%s'",
action->GetId().c_str(), _name.c_str());
action->LogAction();
entry.action->GetId().c_str(), _name.c_str());
entry.action->LogAction();
}
action->PerformAction();
if (entry.context) {
SetActiveVariableContext(&*entry.context);
}
entry.action->PerformAction();
SetActiveVariableContext(nullptr);
}
}
@@ -187,6 +202,7 @@ ActionQueueSettingsDialog::ActionQueueSettingsDialog(QWidget *parent,
obs_module_text("AdvSceneSwitcher.actionQueues.clear"))),
_runOnStartup(new QCheckBox()),
_resolveVariablesOnAdd(new QCheckBox()),
_cloneVariableContext(new QCheckBox()),
_queue(settings)
{
QWidget::connect(_startStopToggle, SIGNAL(clicked()), this,
@@ -195,6 +211,7 @@ ActionQueueSettingsDialog::ActionQueueSettingsDialog(QWidget *parent,
_runOnStartup->setChecked(settings._runOnStartup);
_resolveVariablesOnAdd->setChecked(settings._resolveVariablesOnAdd);
_cloneVariableContext->setChecked(settings._cloneVariableContext);
UpdateLabels();
auto layout = new QGridLayout();
@@ -224,6 +241,14 @@ ActionQueueSettingsDialog::ActionQueueSettingsDialog(QWidget *parent,
_resolveVariablesOnAdd->setToolTip(obs_module_text(
"AdvSceneSwitcher.actionQueues.resolveVariablesOnAdd"));
++row;
layout->addWidget(
new QLabel(obs_module_text(
"AdvSceneSwitcher.actionQueues.cloneVariableContext")),
row, 0);
layout->addWidget(_cloneVariableContext, row, 1);
_cloneVariableContext->setToolTip(obs_module_text(
"AdvSceneSwitcher.actionQueues.cloneVariableContext.tooltip"));
++row;
layout->addWidget(_queueRunStatus, row, 0);
layout->addWidget(_startStopToggle, row, 1);
++row;
@@ -253,6 +278,8 @@ bool ActionQueueSettingsDialog::AskForSettings(QWidget *parent,
settings._runOnStartup = dialog._runOnStartup->isChecked();
settings._resolveVariablesOnAdd =
dialog._resolveVariablesOnAdd->isChecked();
settings._cloneVariableContext =
dialog._cloneVariableContext->isChecked();
return true;
}

View File

@@ -1,11 +1,13 @@
#pragma once
#include "item-selection-helpers.hpp"
#include "macro-action.hpp"
#include "variable.hpp"
#include <chrono>
#include <condition_variable>
#include <deque>
#include <obs-data.h>
#include <optional>
#include <QCheckBox>
#include <thread>
@@ -17,6 +19,11 @@ class ActionQueueSettingsDialog;
class ActionQueue : public Item {
using TimePoint = std::chrono::high_resolution_clock::time_point;
struct QueueEntry {
std::shared_ptr<MacroAction> action;
std::optional<VariableContext> context;
};
public:
ActionQueue();
~ActionQueue();
@@ -43,11 +50,12 @@ private:
bool _runOnStartup = true;
bool _resolveVariablesOnAdd = true;
bool _cloneVariableContext = false;
std::atomic_bool _stop = {true};
std::mutex _mutex;
std::condition_variable _cv;
std::thread _thread;
std::deque<std::shared_ptr<MacroAction>> _actions;
std::deque<QueueEntry> _actions;
TimePoint _lastEmpty;
friend ActionQueueSelection;
@@ -73,6 +81,7 @@ private:
QPushButton *_clear;
QCheckBox *_runOnStartup;
QCheckBox *_resolveVariablesOnAdd;
QCheckBox *_cloneVariableContext;
ActionQueue &_queue;
};

View File

@@ -18,6 +18,30 @@ static std::deque<std::shared_ptr<Item>> variables;
static std::mutex lastVariableChangeMutex;
static std::chrono::high_resolution_clock::time_point lastVariableChange{};
// When set, Variable::Value() and Variable::SetValue() operate on this context
// instead of the global variable state. Used by action queues to isolate
// variable reads and writes to a snapshot taken at the time the action was
// added to the queue, so that actions can modify variables without affecting
// the global state or other queue entries.
thread_local static VariableContext *activeVarContext = nullptr;
VariableContext CreateVariableContext()
{
VariableContext context;
for (const auto &v : variables) {
const auto &var = std::dynamic_pointer_cast<Variable>(v);
if (var) {
context[var->Name()] = var->Value(false);
}
}
return context;
}
void SetActiveVariableContext(VariableContext *context)
{
activeVarContext = context;
}
static bool setup()
{
AddEarlySaveStep(SaveVariables);
@@ -76,6 +100,14 @@ void Variable::Save(obs_data_t *obj) const
std::string Variable::Value(bool updateLastUsed) const
{
if (activeVarContext) {
auto it = activeVarContext->find(Name());
if (it == activeVarContext->end()) {
return "";
}
return it->second;
}
std::lock_guard<std::mutex> lock(_mutex);
if (updateLastUsed) {
UpdateLastUsed();
@@ -108,6 +140,16 @@ std::optional<int> Variable::IntValue() const
void Variable::SetValue(const std::string &value)
{
if (activeVarContext) {
auto it = activeVarContext->find(Name());
if (it == activeVarContext->end()) {
return;
}
it->second = value;
setLastVariableChangeTime();
return;
}
{
std::lock_guard<std::mutex> lock(_mutex);
_previousValue = _value;
@@ -121,6 +163,7 @@ void Variable::SetValue(const std::string &value)
}
setLastVariableChangeTime();
}
_cv.notify_all();
}

View File

@@ -8,6 +8,7 @@
#include <obs-data.h>
#include <optional>
#include <string>
#include <unordered_map>
#include <QStringList>
namespace advss {
@@ -116,6 +117,10 @@ signals:
void Remove(const QString &);
};
using VariableContext = std::unordered_map<std::string, std::string>;
VariableContext CreateVariableContext();
void SetActiveVariableContext(VariableContext *context);
std::deque<std::shared_ptr<Item>> &GetVariables();
EXPORT Variable *GetVariableByName(const std::string &name);
EXPORT Variable *GetVariableByQString(const QString &name);