Add option for short circuit evaluation of conditions

This commit is contained in:
WarmUpTill
2024-08-30 21:21:21 +02:00
committed by WarmUpTill
parent 0906c6bb3f
commit c7c48d03e9
9 changed files with 168 additions and 46 deletions

View File

@@ -20,6 +20,8 @@ void GlobalMacroSettings::Save(obs_data_t *obj) const
obs_data_set_bool(data, "highlightActions", _highlightActions);
obs_data_set_bool(data, "newMacroRegisterHotkey",
_newMacroRegisterHotkeys);
obs_data_set_bool(data, "newMacroUseShortCircuitEvaluation",
_newMacroUseShortCircuitEvaluation);
obs_data_set_obj(obj, "macroSettings", data);
obs_data_release(data);
}
@@ -38,6 +40,8 @@ void GlobalMacroSettings::Load(obs_data_t *obj)
_highlightActions = obs_data_get_bool(data, "highlightActions");
_newMacroRegisterHotkeys =
obs_data_get_bool(data, "newMacroRegisterHotkey");
_newMacroUseShortCircuitEvaluation =
obs_data_get_bool(data, "newMacroUseShortCircuitEvaluation");
obs_data_release(data);
}
@@ -45,16 +49,20 @@ MacroSettingsDialog::MacroSettingsDialog(QWidget *parent,
const GlobalMacroSettings &settings,
Macro *macro)
: QDialog(parent),
_executed(new QCheckBox(obs_module_text(
_highlightExecutedMacros(new QCheckBox(obs_module_text(
"AdvSceneSwitcher.macroTab.highlightExecutedMacros"))),
_conditions(new QCheckBox(obs_module_text(
_highlightConditions(new QCheckBox(obs_module_text(
"AdvSceneSwitcher.macroTab.highlightTrueConditions"))),
_actions(new QCheckBox(obs_module_text(
_highlightActions(new QCheckBox(obs_module_text(
"AdvSceneSwitcher.macroTab.highlightPerformedActions"))),
_newMacroRegisterHotkeys(new QCheckBox(obs_module_text(
"AdvSceneSwitcher.macroTab.newMacroRegisterHotkey"))),
_newMacroUseShortCircuitEvaluation(new QCheckBox(obs_module_text(
"AdvSceneSwitcher.macroTab.newMacroUseShortCircuitEvaluation"))),
_currentMacroRegisterHotkeys(new QCheckBox(obs_module_text(
"AdvSceneSwitcher.macroTab.currentDisableHotkeys"))),
_currentUseShortCircuitEvaluation(new QCheckBox(obs_module_text(
"AdvSceneSwitcher.macroTab.currentUseShortCircuitEvaluation"))),
_currentSkipOnStartup(new QCheckBox(obs_module_text(
"AdvSceneSwitcher.macroTab.currentSkipExecutionOnStartup"))),
_currentStopActionsIfNotDone(new QCheckBox(obs_module_text(
@@ -83,12 +91,17 @@ MacroSettingsDialog::MacroSettingsDialog(QWidget *parent,
setWindowModality(Qt::WindowModality::WindowModal);
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
_newMacroUseShortCircuitEvaluation->setToolTip(obs_module_text(
"AdvSceneSwitcher.macroTab.shortCircuit.tooltip"));
_currentUseShortCircuitEvaluation->setToolTip(obs_module_text(
"AdvSceneSwitcher.macroTab.shortCircuit.tooltip"));
auto highlightOptions = new QGroupBox(
obs_module_text("AdvSceneSwitcher.macroTab.highlightSettings"));
auto highlightLayout = new QVBoxLayout;
highlightLayout->addWidget(_executed);
highlightLayout->addWidget(_conditions);
highlightLayout->addWidget(_actions);
highlightLayout->addWidget(_highlightExecutedMacros);
highlightLayout->addWidget(_highlightConditions);
highlightLayout->addWidget(_highlightActions);
highlightOptions->setLayout(highlightLayout);
auto hotkeyOptions = new QGroupBox(
@@ -103,6 +116,8 @@ MacroSettingsDialog::MacroSettingsDialog(QWidget *parent,
auto generalLayout = new QVBoxLayout;
generalLayout->addWidget(_currentSkipOnStartup);
generalLayout->addWidget(_currentStopActionsIfNotDone);
generalLayout->addWidget(_currentUseShortCircuitEvaluation);
generalLayout->addWidget(_newMacroUseShortCircuitEvaluation);
generalOptions->setLayout(generalLayout);
auto inputOptions = new QGroupBox(
@@ -198,10 +213,12 @@ MacroSettingsDialog::MacroSettingsDialog(QWidget *parent,
dialogLayout->addWidget(buttonbox);
setLayout(dialogLayout);
_executed->setChecked(settings._highlightExecuted);
_conditions->setChecked(settings._highlightConditions);
_actions->setChecked(settings._highlightActions);
_highlightExecutedMacros->setChecked(settings._highlightExecuted);
_highlightConditions->setChecked(settings._highlightConditions);
_highlightActions->setChecked(settings._highlightActions);
_newMacroRegisterHotkeys->setChecked(settings._newMacroRegisterHotkeys);
_newMacroUseShortCircuitEvaluation->setChecked(
settings._newMacroUseShortCircuitEvaluation);
if (!macro || macro->IsGroup()) {
hotkeyOptions->hide();
@@ -212,6 +229,8 @@ MacroSettingsDialog::MacroSettingsDialog(QWidget *parent,
}
_currentMacroRegisterHotkeys->setChecked(macro->PauseHotkeysEnabled());
_currentUseShortCircuitEvaluation->setChecked(
macro->ShortCircuitEvaluationEnabled());
_currentSkipOnStartup->setChecked(macro->SkipExecOnStart());
_currentStopActionsIfNotDone->setChecked(macro->StopActionsIfNotDone());
_currentInputs->SetInputs(macro->GetInputVariables());
@@ -322,17 +341,23 @@ bool MacroSettingsDialog::AskForSettings(QWidget *parent,
if (dialog.exec() != DialogCode::Accepted) {
return false;
}
userInput._highlightExecuted = dialog._executed->isChecked();
userInput._highlightConditions = dialog._conditions->isChecked();
userInput._highlightActions = dialog._actions->isChecked();
userInput._highlightExecuted =
dialog._highlightExecutedMacros->isChecked();
userInput._highlightConditions =
dialog._highlightConditions->isChecked();
userInput._highlightActions = dialog._highlightActions->isChecked();
userInput._newMacroRegisterHotkeys =
dialog._newMacroRegisterHotkeys->isChecked();
userInput._newMacroUseShortCircuitEvaluation =
dialog._newMacroUseShortCircuitEvaluation->isChecked();
if (!macro) {
return true;
}
macro->EnablePauseHotkeys(
dialog._currentMacroRegisterHotkeys->isChecked());
macro->SetShortCircuitEvaluation(
dialog._currentUseShortCircuitEvaluation->isChecked());
macro->SetSkipExecOnStart(dialog._currentSkipOnStartup->isChecked());
macro->SetStopActionsIfNotDone(
dialog._currentStopActionsIfNotDone->isChecked());

View File

@@ -23,6 +23,7 @@ public:
bool _highlightConditions = false;
bool _highlightActions = false;
bool _newMacroRegisterHotkeys = true;
bool _newMacroUseShortCircuitEvaluation = false;
};
// Dialog for configuring global and individual macro specific settings
@@ -44,12 +45,15 @@ private slots:
private:
void Resize();
QCheckBox *_executed;
QCheckBox *_conditions;
QCheckBox *_actions;
// Global macro settings
QCheckBox *_highlightExecutedMacros;
QCheckBox *_highlightConditions;
QCheckBox *_highlightActions;
QCheckBox *_newMacroRegisterHotkeys;
QCheckBox *_newMacroUseShortCircuitEvaluation;
// Current macro specific settings
QCheckBox *_currentMacroRegisterHotkeys;
QCheckBox *_currentUseShortCircuitEvaluation;
QCheckBox *_currentSkipOnStartup;
QCheckBox *_currentStopActionsIfNotDone;
MacroInputSelection *_currentInputs;

View File

@@ -87,7 +87,8 @@ bool AdvSceneSwitcher::AddNewMacro(std::shared_ptr<Macro> &res,
}
res = std::make_shared<Macro>(
name, GetGlobalMacroSettings()._newMacroRegisterHotkeys);
name, GetGlobalMacroSettings()._newMacroRegisterHotkeys,
GetGlobalMacroSettings()._newMacroUseShortCircuitEvaluation);
return true;
}

View File

@@ -76,13 +76,15 @@ namespace advss {
static std::deque<std::shared_ptr<Macro>> macros;
Macro::Macro(const std::string &name, const bool addHotkey)
Macro::Macro(const std::string &name, const bool addHotkey,
const bool shortCircuitEvaluation)
{
SetName(name);
if (addHotkey) {
SetupHotkeys();
}
_registerHotkeys = addHotkey;
_useShortCircuitEvaluation = shortCircuitEvaluation;
}
Macro::~Macro()
@@ -181,6 +183,61 @@ static bool checkCondition(const std::shared_ptr<MacroCondition> &condition)
return conditionMatched;
}
bool Macro::CheckConditionHelper(
const std::shared_ptr<MacroCondition> &condition) const
{
bool conditionMatched = false;
bool wasEvaluated = false;
const auto evaluateCondition = [&condition, &conditionMatched,
&wasEvaluated]() -> bool {
conditionMatched = checkCondition(condition);
conditionMatched =
condition->CheckDurationModifier(conditionMatched);
wasEvaluated = true;
return conditionMatched;
};
const auto logicType = condition->GetLogicType();
if (logicType == Logic::Type::NONE) {
vblog(LOG_INFO, "ignoring condition '%s' for '%s'",
condition->GetId().c_str(), _name.c_str());
if (!_useShortCircuitEvaluation) {
(void)evaluateCondition();
}
return _matched;
}
bool result = _useShortCircuitEvaluation
// Evaluate the condition result if needed
? Logic::ApplyConditionLogic(logicType, _matched,
evaluateCondition,
_name.c_str())
// Evaluate the condition result right away
: Logic::ApplyConditionLogic(logicType, _matched,
evaluateCondition(),
_name.c_str());
const bool isNegativeLogicType = Logic::IsNegationType(logicType);
if (wasEvaluated && ((conditionMatched && !isNegativeLogicType) ||
(!conditionMatched && isNegativeLogicType))) {
condition->EnableHighlight();
}
if (VerboseLoggingEnabled()) {
if (wasEvaluated) {
blog(LOG_INFO, "condition %s returned %d",
condition->GetId().c_str(), conditionMatched);
} else {
blog(LOG_INFO,
"condition %s evaluation skipped (short circuit)",
condition->GetId().c_str());
}
}
return result;
}
bool Macro::CeckMatch(bool ignorePause)
{
if (_isGroup) {
@@ -194,28 +251,7 @@ bool Macro::CeckMatch(bool ignorePause)
return false;
}
bool conditionMatched = checkCondition(condition);
conditionMatched =
condition->CheckDurationModifier(conditionMatched);
const auto logicType = condition->GetLogicType();
if (logicType == Logic::Type::NONE) {
vblog(LOG_INFO, "ignoring condition '%s' for '%s'",
condition->GetId().c_str(), _name.c_str());
continue;
}
vblog(LOG_INFO, "condition %s returned %d",
condition->GetId().c_str(), conditionMatched);
const bool isNegativeLogicType =
Logic::IsNegationType(logicType);
if ((conditionMatched && !isNegativeLogicType) ||
(!conditionMatched && isNegativeLogicType)) {
condition->EnableHighlight();
}
_matched = Logic::ApplyConditionLogic(
logicType, _matched, conditionMatched, _name.c_str());
_matched = CheckConditionHelper(condition);
}
vblog(LOG_INFO, "Macro %s returned %d", _name.c_str(), _matched);
@@ -394,6 +430,16 @@ void Macro::SetStopActionsIfNotDone(bool stopActionsIfNotDone)
_stopActionsIfNotDone = stopActionsIfNotDone;
}
void Macro::SetShortCircuitEvaluation(bool useShortCircuitEvaluation)
{
_useShortCircuitEvaluation = useShortCircuitEvaluation;
}
bool Macro::ShortCircuitEvaluationEnabled() const
{
return _useShortCircuitEvaluation;
}
void Macro::SetPaused(bool pause)
{
if (_paused && !pause) {
@@ -618,6 +664,8 @@ bool Macro::Save(obs_data_t *obj, bool saveForCopy) const
obs_data_set_bool(obj, "onChange", _performActionsOnChange);
obs_data_set_bool(obj, "skipExecOnStart", _skipExecOnStart);
obs_data_set_bool(obj, "stopActionsIfNotDone", _stopActionsIfNotDone);
obs_data_set_bool(obj, "useShortCircuitEvaluation",
_useShortCircuitEvaluation);
obs_data_set_bool(obj, "group", _isGroup);
if (_isGroup) {
@@ -681,6 +729,8 @@ bool Macro::Load(obs_data_t *obj)
_performActionsOnChange = obs_data_get_bool(obj, "onChange");
_skipExecOnStart = obs_data_get_bool(obj, "skipExecOnStart");
_stopActionsIfNotDone = obs_data_get_bool(obj, "stopActionsIfNotDone");
_useShortCircuitEvaluation =
obs_data_get_bool(obj, "useShortCircuitEvaluation");
_isGroup = obs_data_get_bool(obj, "group");
if (_isGroup) {

View File

@@ -23,7 +23,8 @@ class MacroDock;
class Macro {
public:
Macro(const std::string &name = "", const bool addHotkey = false);
Macro(const std::string &name = "", const bool addHotkey = false,
const bool shortCircuitEvaluation = false);
virtual ~Macro();
std::string Name() const { return _name; }
@@ -54,6 +55,9 @@ public:
void SetStopActionsIfNotDone(bool stopActionsIfNotDone);
bool StopActionsIfNotDone() const { return _stopActionsIfNotDone; }
void SetShortCircuitEvaluation(bool useShortCircuitEvaluation);
bool ShortCircuitEvaluationEnabled() const;
int RunCount() const { return _runCount; };
void ResetRunCount() { _runCount = 0; };
@@ -149,6 +153,9 @@ private:
void ClearHotkeys() const;
void SetHotkeysDesc() const;
bool
CheckConditionHelper(const std::shared_ptr<MacroCondition> &) const;
bool RunActionsHelper(
const std::deque<std::shared_ptr<MacroAction>> &actions,
bool ignorePause);
@@ -179,6 +186,7 @@ private:
bool _isGroup = false;
bool _isCollapsed = false;
bool _useShortCircuitEvaluation = false;
bool _runInParallel = false;
bool _matched = false;
bool _lastMatched = false;

View File

@@ -19,6 +19,15 @@ const std::map<Logic::Type, const char *> Logic::localeMap = {
bool Logic::ApplyConditionLogic(Type type, bool currentMatchResult,
bool conditionMatched, const char *context)
{
return ApplyConditionLogic(
type, currentMatchResult,
[conditionMatched]() { return conditionMatched; }, context);
}
bool Logic::ApplyConditionLogic(Type type, bool currentMatchResult,
const std::function<bool()> &evaluateCondition,
const char *context)
{
if (!context) {
context = "";
@@ -26,22 +35,22 @@ bool Logic::ApplyConditionLogic(Type type, bool currentMatchResult,
switch (type) {
case Type::ROOT_NONE:
return conditionMatched;
return evaluateCondition();
case Type::ROOT_NOT:
return !conditionMatched;
return !evaluateCondition();
case Type::ROOT_LAST:
break;
case Type::NONE:
vblog(LOG_INFO, "skipping condition check for '%s'", context);
return currentMatchResult;
case Type::AND:
return currentMatchResult && conditionMatched;
return currentMatchResult && evaluateCondition();
case Type::OR:
return currentMatchResult || conditionMatched;
return currentMatchResult || evaluateCondition();
case Type::AND_NOT:
return currentMatchResult && !conditionMatched;
return currentMatchResult && !evaluateCondition();
case Type::OR_NOT:
return currentMatchResult || !conditionMatched;
return currentMatchResult || !evaluateCondition();
case Type::LAST:
default:
blog(LOG_WARNING, "ignoring invalid logic check (%s)", context);

View File

@@ -1,4 +1,5 @@
#pragma once
#include <functional>
#include <map>
#include <obs-data.h>
#include <string>
@@ -39,6 +40,10 @@ public:
static bool ApplyConditionLogic(Type, bool currentMatchResult,
bool conditionMatched,
const char *context);
static bool
ApplyConditionLogic(Type, bool currentMatchResult,
const std::function<bool()> &evaluateCondition,
const char *context);
static void PopulateLogicTypeSelection(QComboBox *list,
bool isRootCondition);