Add action trigger modes
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

* always -> same as old behavior, if "on change" was disabled
* results changes -> same as old behavior, if "on change" was enabled
* any condition changes
* any condition changes and evaluates to true
This commit is contained in:
WarmUpTill 2026-03-02 19:27:42 +01:00 committed by WarmUpTill
parent d4425df694
commit be8744f0d0
16 changed files with 143 additions and 55 deletions

View File

@ -76,7 +76,6 @@ AdvSceneSwitcher.macroTab.name="Name:"
AdvSceneSwitcher.macroTab.run="Makro ausführen" AdvSceneSwitcher.macroTab.run="Makro ausführen"
AdvSceneSwitcher.macroTab.runFail="Ausführen von \"%1\" fehlgeschlagen!\nEntweder ist eine der Aktionen fehlgeschlagen oder das Makro wird bereits ausgeführt.\nSoll die aktuelle Ausführung gestoppt werden?" AdvSceneSwitcher.macroTab.runFail="Ausführen von \"%1\" fehlgeschlagen!\nEntweder ist eine der Aktionen fehlgeschlagen oder das Makro wird bereits ausgeführt.\nSoll die aktuelle Ausführung gestoppt werden?"
AdvSceneSwitcher.macroTab.runInParallel="Parallel zu anderen Makros ausführen" AdvSceneSwitcher.macroTab.runInParallel="Parallel zu anderen Makros ausführen"
AdvSceneSwitcher.macroTab.onChange="Nur bei Änderung ausführen"
AdvSceneSwitcher.macroTab.defaultname="Makro %1" AdvSceneSwitcher.macroTab.defaultname="Makro %1"
AdvSceneSwitcher.macroTab.defaultGroupName="Gruppe %1" AdvSceneSwitcher.macroTab.defaultGroupName="Gruppe %1"
AdvSceneSwitcher.macroTab.removeGroupPopup.text="Sicher, dass \"%1\" und alle zugehörigen Elemente gelöscht werden?" AdvSceneSwitcher.macroTab.removeGroupPopup.text="Sicher, dass \"%1\" und alle zugehörigen Elemente gelöscht werden?"

View File

@ -177,7 +177,12 @@ AdvSceneSwitcher.macroTab.run.tooltip="Run all macro actions regardless of condi
AdvSceneSwitcher.macroTab.runElse="Run macro (else)" AdvSceneSwitcher.macroTab.runElse="Run macro (else)"
AdvSceneSwitcher.macroTab.runFail="Running \"%1\" failed!\nEither one of the actions failed or the macro is running already.\nDo you want to stop it?" AdvSceneSwitcher.macroTab.runFail="Running \"%1\" failed!\nEither one of the actions failed or the macro is running already.\nDo you want to stop it?"
AdvSceneSwitcher.macroTab.runInParallel="Run macro in parallel to other macros" AdvSceneSwitcher.macroTab.runInParallel="Run macro in parallel to other macros"
AdvSceneSwitcher.macroTab.onChange="Perform actions only on condition change" AdvSceneSwitcher.macroTab.actionTriggerMode.label="Perform actions:"
AdvSceneSwitcher.macroTab.actionTriggerMode.tooltip="Controls when the actions of this macro are performed.\n\n\"Always\" - actions are performed every time the conditions are met.\n\"Macro result changed\" - actions are only performed when the macro transitions between matching and not matching.\n\"Any condition changed\" - actions are only performed when any individual condition changes its result.\n\"Any condition triggered\" - actions are only performed when any individual condition changes from false to true."
AdvSceneSwitcher.macroTab.actionTriggerMode.always="always"
AdvSceneSwitcher.macroTab.actionTriggerMode.onOverallChange="only when result changes"
AdvSceneSwitcher.macroTab.actionTriggerMode.onAnyConditionChange="only when any condition changes"
AdvSceneSwitcher.macroTab.actionTriggerMode.onAnyConditionTriggered="only when any condition becomes true"
AdvSceneSwitcher.macroTab.defaultname="Macro %1" AdvSceneSwitcher.macroTab.defaultname="Macro %1"
AdvSceneSwitcher.macroTab.defaultGroupName="Group %1" AdvSceneSwitcher.macroTab.defaultGroupName="Group %1"
AdvSceneSwitcher.macroTab.macroNameExists="The name \"%1\" is already used by a macro." AdvSceneSwitcher.macroTab.macroNameExists="The name \"%1\" is already used by a macro."

View File

@ -72,7 +72,6 @@ AdvSceneSwitcher.macroTab.add="Agregar nueva macro"
AdvSceneSwitcher.macroTab.name="Nombre:" AdvSceneSwitcher.macroTab.name="Nombre:"
AdvSceneSwitcher.macroTab.run="Ejecutar macro" AdvSceneSwitcher.macroTab.run="Ejecutar macro"
AdvSceneSwitcher.macroTab.runInParallel="Ejecutar macro en paralelo a otras macros" AdvSceneSwitcher.macroTab.runInParallel="Ejecutar macro en paralelo a otras macros"
AdvSceneSwitcher.macroTab.onChange="Realizar acciones solo en el cambio de condición"
AdvSceneSwitcher.macroTab.defaultname="Macro %1" AdvSceneSwitcher.macroTab.defaultname="Macro %1"
AdvSceneSwitcher.macroTab.copy="Crear copia" AdvSceneSwitcher.macroTab.copy="Crear copia"
AdvSceneSwitcher.macroTab.expandAll="Expandir todo" AdvSceneSwitcher.macroTab.expandAll="Expandir todo"

View File

@ -78,7 +78,6 @@ AdvSceneSwitcher.macroTab.add="Ajouter une nouvelle macro"
AdvSceneSwitcher.macroTab.name="Nom :" AdvSceneSwitcher.macroTab.name="Nom :"
AdvSceneSwitcher.macroTab.run="Exécuter la macro" AdvSceneSwitcher.macroTab.run="Exécuter la macro"
AdvSceneSwitcher.macroTab.runInParallel="Exécuter la macro en parallèle avec d'autres macros" AdvSceneSwitcher.macroTab.runInParallel="Exécuter la macro en parallèle avec d'autres macros"
AdvSceneSwitcher.macroTab.onChange="Exécuter des actions uniquement en cas de changement de condition"
AdvSceneSwitcher.macroTab.defaultname="Macro %1" AdvSceneSwitcher.macroTab.defaultname="Macro %1"
AdvSceneSwitcher.macroTab.defaultGroupName="Groupe %1" AdvSceneSwitcher.macroTab.defaultGroupName="Groupe %1"
AdvSceneSwitcher.macroTab.removeSingleMacroPopup.text="Êtes-vous sûr de vouloir supprimer \"%1\" ?" AdvSceneSwitcher.macroTab.removeSingleMacroPopup.text="Êtes-vous sûr de vouloir supprimer \"%1\" ?"

View File

@ -164,7 +164,6 @@ AdvSceneSwitcher.macroTab.run.tooltip="条件に関係なくすべてのマク
AdvSceneSwitcher.macroTab.runElse="マクロ実行else" AdvSceneSwitcher.macroTab.runElse="マクロ実行else"
AdvSceneSwitcher.macroTab.runFail="\"%1\" の実行に失敗しました!\nいずれかのアクションが失敗したか、マクロがすでに実行されています。\n停止しますか" AdvSceneSwitcher.macroTab.runFail="\"%1\" の実行に失敗しました!\nいずれかのアクションが失敗したか、マクロがすでに実行されています。\n停止しますか"
AdvSceneSwitcher.macroTab.runInParallel="他のマクロと並行してマクロを実行する" AdvSceneSwitcher.macroTab.runInParallel="他のマクロと並行してマクロを実行する"
AdvSceneSwitcher.macroTab.onChange="条件変更時のみアクションを実行"
AdvSceneSwitcher.macroTab.defaultname="マクロ %1" AdvSceneSwitcher.macroTab.defaultname="マクロ %1"
AdvSceneSwitcher.macroTab.defaultGroupName="グループ %1" AdvSceneSwitcher.macroTab.defaultGroupName="グループ %1"
AdvSceneSwitcher.macroTab.macroNameExists="名前 \"%1\" は既にマクロで使用されています。" AdvSceneSwitcher.macroTab.macroNameExists="名前 \"%1\" は既にマクロで使用されています。"

View File

@ -147,7 +147,6 @@ AdvSceneSwitcher.macroTab.run.tooltip="Execute todas as ações da macro indepen
AdvSceneSwitcher.macroTab.runElse="Executar macro (alternativa)" AdvSceneSwitcher.macroTab.runElse="Executar macro (alternativa)"
AdvSceneSwitcher.macroTab.runFail="Falha ao executar \"%1\"!\nUma das ações falhou ou a macro já está em execução.\nDeseja interrompê-la?" AdvSceneSwitcher.macroTab.runFail="Falha ao executar \"%1\"!\nUma das ações falhou ou a macro já está em execução.\nDeseja interrompê-la?"
AdvSceneSwitcher.macroTab.runInParallel="Executar macro em paralelo com outras macros" AdvSceneSwitcher.macroTab.runInParallel="Executar macro em paralelo com outras macros"
AdvSceneSwitcher.macroTab.onChange="Executar ações apenas quando houver mudança na condição"
AdvSceneSwitcher.macroTab.defaultname="Macro %1" AdvSceneSwitcher.macroTab.defaultname="Macro %1"
AdvSceneSwitcher.macroTab.defaultGroupName="Grupo %1" AdvSceneSwitcher.macroTab.defaultGroupName="Grupo %1"
AdvSceneSwitcher.macroTab.macroNameExists="O nome \"%1\" já está em uso por uma macro." AdvSceneSwitcher.macroTab.macroNameExists="O nome \"%1\" já está em uso por uma macro."

View File

@ -72,7 +72,6 @@ AdvSceneSwitcher.macroTab.add="Yeni Makro ekle"
AdvSceneSwitcher.macroTab.name="İsim:" AdvSceneSwitcher.macroTab.name="İsim:"
AdvSceneSwitcher.macroTab.run="Makro Çalıştırma" AdvSceneSwitcher.macroTab.run="Makro Çalıştırma"
AdvSceneSwitcher.macroTab.runInParallel="Makroyu diğer makrolara paralel olarak çalıştırın" AdvSceneSwitcher.macroTab.runInParallel="Makroyu diğer makrolara paralel olarak çalıştırın"
AdvSceneSwitcher.macroTab.onChange="Eylemleri yalnızca koşul değişikliğinde gerçekleştirin"
AdvSceneSwitcher.macroTab.defaultname="Makro %1" AdvSceneSwitcher.macroTab.defaultname="Makro %1"
AdvSceneSwitcher.macroTab.copy="Kopya oluştur" AdvSceneSwitcher.macroTab.copy="Kopya oluştur"
AdvSceneSwitcher.macroTab.expandAll="Hepsini Genişlet" AdvSceneSwitcher.macroTab.expandAll="Hepsini Genişlet"

View File

@ -151,7 +151,6 @@ AdvSceneSwitcher.macroTab.run.tooltip="无论条件如何,都运行所有宏
AdvSceneSwitcher.macroTab.runElse="运行宏(不满足条件)" AdvSceneSwitcher.macroTab.runElse="运行宏(不满足条件)"
AdvSceneSwitcher.macroTab.runFail="运行 \"%1\" 失败!\n要么其中一个操作失败要么宏已在运行中.\n你想停止它吗?" AdvSceneSwitcher.macroTab.runFail="运行 \"%1\" 失败!\n要么其中一个操作失败要么宏已在运行中.\n你想停止它吗?"
AdvSceneSwitcher.macroTab.runInParallel="与其他宏并行运行宏" AdvSceneSwitcher.macroTab.runInParallel="与其他宏并行运行宏"
AdvSceneSwitcher.macroTab.onChange="仅在条件结果发生变化时执行操作(条件结果不变时只执行一次操作)"
AdvSceneSwitcher.macroTab.defaultname="宏 %1" AdvSceneSwitcher.macroTab.defaultname="宏 %1"
AdvSceneSwitcher.macroTab.defaultGroupName="分组 %1" AdvSceneSwitcher.macroTab.defaultGroupName="分组 %1"
AdvSceneSwitcher.macroTab.macroNameExists="名称 \"%1\" 已被宏使用." AdvSceneSwitcher.macroTab.macroNameExists="名称 \"%1\" 已被宏使用."

View File

@ -793,25 +793,19 @@
</widget> </widget>
</item> </item>
<item> <item>
<widget class="QCheckBox" name="runMacroOnChange"> <widget class="QLabel" name="label_18">
<property name="toolTip">
<string>AdvSceneSwitcher.macroTab.onChange</string>
</property>
<property name="text"> <property name="text">
<string/> <string>AdvSceneSwitcher.macroTab.actionTriggerMode.label</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property> </property>
</widget> </widget>
</item> </item>
<item> <item>
<widget class="QLabel" name="label_18"> <widget class="QComboBox" name="actionTriggerMode">
<property name="toolTip"> <property name="toolTip">
<string>AdvSceneSwitcher.macroTab.onChange</string> <string>AdvSceneSwitcher.macroTab.actionTriggerMode.tooltip</string>
</property>
<property name="text">
<string>AdvSceneSwitcher.macroTab.onChange</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property> </property>
</widget> </widget>
</item> </item>
@ -4125,7 +4119,7 @@
<tabstop>macroName</tabstop> <tabstop>macroName</tabstop>
<tabstop>runMacro</tabstop> <tabstop>runMacro</tabstop>
<tabstop>runMacroInParallel</tabstop> <tabstop>runMacroInParallel</tabstop>
<tabstop>runMacroOnChange</tabstop> <tabstop>actionTriggerMode</tabstop>
<tabstop>macroSettings</tabstop> <tabstop>macroSettings</tabstop>
<tabstop>macroEdit</tabstop> <tabstop>macroEdit</tabstop>
<tabstop>sceneGroups</tabstop> <tabstop>sceneGroups</tabstop>

View File

@ -108,7 +108,7 @@ public slots:
void on_macroDown_clicked() const; void on_macroDown_clicked() const;
void on_macroName_editingFinished(); void on_macroName_editingFinished();
void on_runMacroInParallel_stateChanged(int value) const; void on_runMacroInParallel_stateChanged(int value) const;
void on_runMacroOnChange_stateChanged(int value) const; void on_actionTriggerMode_currentIndexChanged(int index) const;
void MacroSelectionChanged(); void MacroSelectionChanged();
void ShowMacroContextMenu(const QPoint &); void ShowMacroContextMenu(const QPoint &);
void CopyMacro(); void CopyMacro();

View File

@ -7,6 +7,17 @@ MacroCondition::MacroCondition(Macro *m, bool supportsVariableValue)
{ {
} }
bool MacroCondition::EvaluateCondition()
{
bool newValue = CheckCondition();
_changed = _previousValue.has_value() && (*_previousValue != newValue);
const bool negate = _logic.IsNegationType(GetLogicType());
_risingEdge = _changed &&
((!negate && newValue) || (negate && !newValue));
_previousValue = newValue;
return newValue;
}
bool MacroCondition::Save(obs_data_t *obj) const bool MacroCondition::Save(obs_data_t *obj) const
{ {
MacroSegment::Save(obj); MacroSegment::Save(obj);

View File

@ -4,13 +4,19 @@
#include "duration-modifier.hpp" #include "duration-modifier.hpp"
#include "macro-ref.hpp" #include "macro-ref.hpp"
#include <optional>
namespace advss { namespace advss {
class EXPORT MacroCondition : public MacroSegment { class EXPORT MacroCondition : public MacroSegment {
public: public:
MacroCondition(Macro *m, bool supportsVariableValue = false); MacroCondition(Macro *m, bool supportsVariableValue = false);
virtual ~MacroCondition() = default; virtual ~MacroCondition() = default;
virtual bool CheckCondition() = 0;
bool EvaluateCondition();
bool HasChanged() const { return _changed; }
bool IsRisingEdge() const { return _risingEdge; }
virtual bool Save(obs_data_t *obj) const = 0; virtual bool Save(obs_data_t *obj) const = 0;
virtual bool Load(obs_data_t *obj) = 0; virtual bool Load(obs_data_t *obj) = 0;
@ -28,9 +34,15 @@ public:
static std::string_view GetDefaultID(); static std::string_view GetDefaultID();
protected:
virtual bool CheckCondition() = 0;
private: private:
Logic _logic = Logic(Logic::Type::ROOT_NONE); Logic _logic = Logic(Logic::Type::ROOT_NONE);
DurationModifier _durationModifier; DurationModifier _durationModifier;
std::optional<bool> _previousValue;
bool _changed = false;
bool _risingEdge = false;
}; };
class EXPORT MacroRefCondition : virtual public MacroCondition { class EXPORT MacroRefCondition : virtual public MacroCondition {

View File

@ -474,14 +474,17 @@ void AdvSceneSwitcher::on_runMacroInParallel_stateChanged(int value) const
macro->SetRunInParallel(value); macro->SetRunInParallel(value);
} }
void AdvSceneSwitcher::on_runMacroOnChange_stateChanged(int value) const void AdvSceneSwitcher::on_actionTriggerMode_currentIndexChanged(int index) const
{ {
auto macro = GetSelectedMacro(); auto macro = GetSelectedMacro();
if (!macro) { if (!macro) {
return; return;
} }
auto lock = LockContext(); auto lock = LockContext();
macro->SetMatchOnChange(value); const auto mode = static_cast<Macro::ActionTriggerMode>(
ui->actionTriggerMode->itemData(index).toInt());
macro->SetActionTriggerMode(mode);
} }
void AdvSceneSwitcher::SetMacroEditAreaDisabled(bool disable) const void AdvSceneSwitcher::SetMacroEditAreaDisabled(bool disable) const
@ -489,7 +492,7 @@ void AdvSceneSwitcher::SetMacroEditAreaDisabled(bool disable) const
ui->macroName->setDisabled(disable); ui->macroName->setDisabled(disable);
ui->runMacro->setDisabled(disable); ui->runMacro->setDisabled(disable);
ui->runMacroInParallel->setDisabled(disable); ui->runMacroInParallel->setDisabled(disable);
ui->runMacroOnChange->setDisabled(disable); ui->actionTriggerMode->setDisabled(disable);
ui->macroEdit->SetControlsDisabled(disable); ui->macroEdit->SetControlsDisabled(disable);
} }
@ -519,10 +522,12 @@ void AdvSceneSwitcher::MacroSelectionChanged()
{ {
const QSignalBlocker b1(ui->macroName); const QSignalBlocker b1(ui->macroName);
const QSignalBlocker b2(ui->runMacroInParallel); const QSignalBlocker b2(ui->runMacroInParallel);
const QSignalBlocker b3(ui->runMacroOnChange); const QSignalBlocker b3(ui->actionTriggerMode);
ui->macroName->setText(macro->Name().c_str()); ui->macroName->setText(macro->Name().c_str());
ui->runMacroInParallel->setChecked(macro->RunInParallel()); ui->runMacroInParallel->setChecked(macro->RunInParallel());
ui->runMacroOnChange->setChecked(macro->MatchOnChange()); ui->actionTriggerMode->setCurrentIndex(
ui->actionTriggerMode->findData(static_cast<int>(
macro->GetActionTriggerMode())));
} }
macro->ResetUIHelpers(); macro->ResetUIHelpers();
@ -552,9 +557,9 @@ void AdvSceneSwitcher::HighlightOnChange() const
return; return;
} }
if (macro->OnChangePreventedActionsSince( if (macro->ActionTriggerModePreventedActionsSince(
lastOnChangeHighlightCheckTime)) { lastOnChangeHighlightCheckTime)) {
HighlightWidget(ui->runMacroOnChange, Qt::yellow, HighlightWidget(ui->actionTriggerMode, Qt::yellow,
Qt::transparent, true); Qt::transparent, true);
} }
@ -682,6 +687,24 @@ void AdvSceneSwitcher::SetupMacroTab()
ui->macroSearchRegex, ui->macroSearchRegex,
ui->macroSearchShowSettings, ui->macroSearchShowSettings,
[this]() { ui->macros->RefreshFilter(); }); [this]() { ui->macros->RefreshFilter(); });
static const std::vector<
std::pair<Macro::ActionTriggerMode, const char *>>
actionTriggerModes = {
{Macro::ActionTriggerMode::ALWAYS,
"AdvSceneSwitcher.macroTab.actionTriggerMode.always"},
{Macro::ActionTriggerMode::MACRO_RESULT_CHANGED,
"AdvSceneSwitcher.macroTab.actionTriggerMode.onOverallChange"},
{Macro::ActionTriggerMode::ANY_CONDITION_CHANGED,
"AdvSceneSwitcher.macroTab.actionTriggerMode.onAnyConditionChange"},
{Macro::ActionTriggerMode::ANY_CONDITION_TRIGGERED,
"AdvSceneSwitcher.macroTab.actionTriggerMode.onAnyConditionTriggered"},
};
for (const auto &[mode, name] : actionTriggerModes) {
ui->actionTriggerMode->addItem(obs_module_text(name),
static_cast<int>(mode));
}
} }
void AdvSceneSwitcher::ShowMacroContextMenu(const QPoint &pos) void AdvSceneSwitcher::ShowMacroContextMenu(const QPoint &pos)

View File

@ -224,7 +224,8 @@ void MacroTreeItem::HighlightIfExecuted()
if (!wasHighlighted && if (!wasHighlighted &&
_lastHighlightCheckTime.time_since_epoch().count() != 0 && _lastHighlightCheckTime.time_since_epoch().count() != 0 &&
_macro->OnChangePreventedActionsSince(_lastHighlightCheckTime)) { _macro->ActionTriggerModePreventedActionsSince(
_lastHighlightCheckTime)) {
HighlightWidget(this, Qt::yellow, QColor(0, 0, 0, 0), true); HighlightWidget(this, Qt::yellow, QColor(0, 0, 0, 0), true);
} }

View File

@ -111,7 +111,7 @@ static bool checkCondition(const std::shared_ptr<MacroCondition> &condition)
const auto startTime = std::chrono::high_resolution_clock::now(); const auto startTime = std::chrono::high_resolution_clock::now();
bool conditionMatched = false; bool conditionMatched = false;
condition->WithLock([&condition, &conditionMatched]() { condition->WithLock([&condition, &conditionMatched]() {
conditionMatched = condition->CheckCondition(); conditionMatched = condition->EvaluateCondition();
}); });
const auto endTime = std::chrono::high_resolution_clock::now(); const auto endTime = std::chrono::high_resolution_clock::now();
const auto timeSpent = endTime - startTime; const auto timeSpent = endTime - startTime;
@ -241,12 +241,36 @@ bool Macro::CheckConditions(bool ignorePause)
vblog(LOG_INFO, "Macro %s returned %d", _name.c_str(), _matched); vblog(LOG_INFO, "Macro %s returned %d", _name.c_str(), _matched);
_conditionSateChanged = _lastMatched != _matched; _actionModeMatch = false;
switch (_actionTriggerMode) {
case Macro::ActionTriggerMode::ALWAYS:
_actionModeMatch = true;
break;
case Macro::ActionTriggerMode::MACRO_RESULT_CHANGED:
_actionModeMatch = _lastMatched != _matched;
break;
case Macro::ActionTriggerMode::ANY_CONDITION_CHANGED:
for (const auto &condition : _conditions) {
if (condition->HasChanged()) {
_actionModeMatch = true;
}
}
break;
case Macro::ActionTriggerMode::ANY_CONDITION_TRIGGERED:
for (const auto &condition : _conditions) {
if (condition->IsRisingEdge()) {
_actionModeMatch = true;
}
}
break;
default:
break;
}
const bool hasActionsToExecute = _matched ? (_actions.size() > 0) const bool hasActionsToExecute = _matched ? (_actions.size() > 0)
: (_elseActions.size() > 0); : (_elseActions.size() > 0);
if (!_conditionSateChanged && _performActionsOnChange && if (!_actionModeMatch && hasActionsToExecute) {
hasActionsToExecute) { _lastActionRunModePreventTime =
_lastOnChangeActionsPreventedTime =
std::chrono::high_resolution_clock::now(); std::chrono::high_resolution_clock::now();
} }
@ -306,9 +330,9 @@ bool Macro::WasExecutedSince(const TimePoint &time) const
return _lastExecutionTime > time; return _lastExecutionTime > time;
} }
bool Macro::OnChangePreventedActionsSince(const TimePoint &time) const bool Macro::ActionTriggerModePreventedActionsSince(const TimePoint &time) const
{ {
return _lastOnChangeActionsPreventedTime > time; return _lastActionRunModePreventTime > time;
} }
Macro::TimePoint Macro::GetLastExecutionTime() const Macro::TimePoint Macro::GetLastExecutionTime() const
@ -342,10 +366,9 @@ bool Macro::ShouldRunActions() const
const bool hasActionsToExecute = const bool hasActionsToExecute =
!_paused && (_matched || _elseActions.size() > 0) && !_paused && (_matched || _elseActions.size() > 0) &&
(!_performActionsOnChange || _conditionSateChanged); _actionModeMatch;
if (VerboseLoggingEnabled() && _performActionsOnChange && if (VerboseLoggingEnabled() && !_actionModeMatch) {
!_conditionSateChanged) {
if (_matched && _actions.size() > 0) { if (_matched && _actions.size() > 0) {
blog(LOG_INFO, "skip actions for Macro %s (on change)", blog(LOG_INFO, "skip actions for Macro %s (on change)",
_name.c_str()); _name.c_str());
@ -376,6 +399,16 @@ void Macro::ResetTimers()
_lastExecutionTime = {}; _lastExecutionTime = {};
} }
void Macro::SetActionTriggerMode(ActionTriggerMode mode)
{
_actionTriggerMode = mode;
}
Macro::ActionTriggerMode Macro::GetActionTriggerMode() const
{
return _actionTriggerMode;
}
bool Macro::RunActionsHelper( bool Macro::RunActionsHelper(
const std::deque<std::shared_ptr<MacroAction>> &actionsToRun, const std::deque<std::shared_ptr<MacroAction>> &actionsToRun,
bool ignorePause) bool ignorePause)
@ -433,11 +466,6 @@ bool Macro::WasPausedSince(const TimePoint &time) const
return _lastUnpauseTime > time; return _lastUnpauseTime > time;
} }
void Macro::SetMatchOnChange(bool onChange)
{
_performActionsOnChange = onChange;
}
void Macro::SetStopActionsIfNotDone(bool stopActionsIfNotDone) void Macro::SetStopActionsIfNotDone(bool stopActionsIfNotDone)
{ {
_stopActionsIfNotDone = stopActionsIfNotDone; _stopActionsIfNotDone = stopActionsIfNotDone;
@ -758,7 +786,8 @@ bool Macro::Save(obs_data_t *obj, bool saveForCopy) const
obs_data_set_bool(obj, "pause", _paused); obs_data_set_bool(obj, "pause", _paused);
obs_data_set_bool(obj, "parallel", _runInParallel); obs_data_set_bool(obj, "parallel", _runInParallel);
obs_data_set_bool(obj, "checkConditionsInParallel", _checkInParallel); obs_data_set_bool(obj, "checkConditionsInParallel", _checkInParallel);
obs_data_set_bool(obj, "onChange", _performActionsOnChange); obs_data_set_int(obj, "actionTriggerMode",
static_cast<int>(_actionTriggerMode));
obs_data_set_bool(obj, "skipExecOnStart", _skipExecOnStart); obs_data_set_bool(obj, "skipExecOnStart", _skipExecOnStart);
obs_data_set_bool(obj, "stopActionsIfNotDone", _stopActionsIfNotDone); obs_data_set_bool(obj, "stopActionsIfNotDone", _stopActionsIfNotDone);
obs_data_set_bool(obj, "useShortCircuitEvaluation", obs_data_set_bool(obj, "useShortCircuitEvaluation",
@ -844,7 +873,15 @@ bool Macro::Load(obs_data_t *obj)
} }
_runInParallel = obs_data_get_bool(obj, "parallel"); _runInParallel = obs_data_get_bool(obj, "parallel");
_checkInParallel = obs_data_get_bool(obj, "checkConditionsInParallel"); _checkInParallel = obs_data_get_bool(obj, "checkConditionsInParallel");
_performActionsOnChange = obs_data_get_bool(obj, "onChange"); if (obs_data_has_user_value(obj, "onChange")) {
const bool onChange = obs_data_get_bool(obj, "onChange");
_actionTriggerMode =
onChange ? ActionTriggerMode::MACRO_RESULT_CHANGED
: ActionTriggerMode::ALWAYS;
} else {
_actionTriggerMode = static_cast<ActionTriggerMode>(
obs_data_get_int(obj, "actionTriggerMode"));
}
_skipExecOnStart = obs_data_get_bool(obj, "skipExecOnStart"); _skipExecOnStart = obs_data_get_bool(obj, "skipExecOnStart");
_stopActionsIfNotDone = obs_data_get_bool(obj, "stopActionsIfNotDone"); _stopActionsIfNotDone = obs_data_get_bool(obj, "stopActionsIfNotDone");
_useShortCircuitEvaluation = _useShortCircuitEvaluation =

View File

@ -26,6 +26,16 @@ class Macro {
public: public:
enum class PauseStateSaveBehavior { PERSIST, PAUSE, UNPAUSE }; enum class PauseStateSaveBehavior { PERSIST, PAUSE, UNPAUSE };
enum class ActionTriggerMode {
// Trigger always
ALWAYS,
// Trigger when macro match result flips
MACRO_RESULT_CHANGED,
// Trigger when any individual condition changes
ANY_CONDITION_CHANGED,
// Trigger when any individual condition evaluates to true
ANY_CONDITION_TRIGGERED,
};
Macro(const std::string &name = ""); Macro(const std::string &name = "");
Macro(const std::string &name, const GlobalMacroSettings &settings); Macro(const std::string &name, const GlobalMacroSettings &settings);
@ -54,8 +64,8 @@ public:
bool GetStop() const { return _stop; } bool GetStop() const { return _stop; }
void ResetTimers(); void ResetTimers();
void SetMatchOnChange(bool onChange); void SetActionTriggerMode(ActionTriggerMode);
bool MatchOnChange() const { return _performActionsOnChange; } ActionTriggerMode GetActionTriggerMode() const;
void SetSkipExecOnStart(bool skip) { _skipExecOnStart = skip; } void SetSkipExecOnStart(bool skip) { _skipExecOnStart = skip; }
bool SkipExecOnStart() const { return _skipExecOnStart; } bool SkipExecOnStart() const { return _skipExecOnStart; }
@ -137,7 +147,7 @@ public:
const QList<int> &GetElseActionSplitterPosition() const; const QList<int> &GetElseActionSplitterPosition() const;
bool HasValidSplitterPositions() const; bool HasValidSplitterPositions() const;
bool WasExecutedSince(const TimePoint &) const; bool WasExecutedSince(const TimePoint &) const;
bool OnChangePreventedActionsSince(const TimePoint &) const; bool ActionTriggerModePreventedActionsSince(const TimePoint &) const;
TimePoint GetLastExecutionTime() const; TimePoint GetLastExecutionTime() const;
void ResetUIHelpers(); void ResetUIHelpers();
@ -169,7 +179,7 @@ private:
TimePoint _lastCheckTime{}; TimePoint _lastCheckTime{};
TimePoint _lastUnpauseTime{}; TimePoint _lastUnpauseTime{};
TimePoint _lastExecutionTime{}; TimePoint _lastExecutionTime{};
TimePoint _lastOnChangeActionsPreventedTime{}; TimePoint _lastActionRunModePreventTime{};
std::vector<std::thread> _helperThreads; std::vector<std::thread> _helperThreads;
std::deque<std::shared_ptr<MacroCondition>> _conditions; std::deque<std::shared_ptr<MacroCondition>> _conditions;
@ -184,14 +194,13 @@ private:
bool _useShortCircuitEvaluation = false; bool _useShortCircuitEvaluation = false;
bool _useCustomConditionCheckInterval = false; bool _useCustomConditionCheckInterval = false;
Duration _customConditionCheckInterval = 0.3; Duration _customConditionCheckInterval = 0.3;
bool _conditionSateChanged = false; bool _actionModeMatch = false;
bool _runInParallel = false; bool _runInParallel = false;
bool _checkInParallel = false; bool _checkInParallel = false;
bool _matched = false; bool _matched = false;
std::future<void> _conditionCheckFuture; std::future<void> _conditionCheckFuture;
bool _lastMatched = false; bool _lastMatched = false;
bool _performActionsOnChange = true;
bool _skipExecOnStart = false; bool _skipExecOnStart = false;
bool _stopActionsIfNotDone = false; bool _stopActionsIfNotDone = false;
bool _paused = false; bool _paused = false;
@ -201,6 +210,9 @@ private:
obs_hotkey_id _unpauseHotkey = OBS_INVALID_HOTKEY_ID; obs_hotkey_id _unpauseHotkey = OBS_INVALID_HOTKEY_ID;
obs_hotkey_id _togglePauseHotkey = OBS_INVALID_HOTKEY_ID; obs_hotkey_id _togglePauseHotkey = OBS_INVALID_HOTKEY_ID;
ActionTriggerMode _actionTriggerMode =
ActionTriggerMode::MACRO_RESULT_CHANGED;
PauseStateSaveBehavior _pauseSaveBehavior = PauseStateSaveBehavior _pauseSaveBehavior =
PauseStateSaveBehavior::PERSIST; PauseStateSaveBehavior::PERSIST;