diff --git a/data/locale/en-US.ini b/data/locale/en-US.ini index d7537f68..1de939bd 100644 --- a/data/locale/en-US.ini +++ b/data/locale/en-US.ini @@ -18,6 +18,8 @@ AdvSceneSwitcher.generalTab.status.autoStart.recording="Recording" AdvSceneSwitcher.generalTab.status.autoStart.streaming="Streaming" AdvSceneSwitcher.generalTab.status.autoStart.recordingAndStreaming="Recording or Streaming" AdvSceneSwitcher.generalTab.status.checkInterval="Check conditions every" +AdvSceneSwitcher.generalTab.status.checkIntervalTooLow="⚠️ Conflict with macro \"%1\"!" +AdvSceneSwitcher.generalTab.status.checkIntervalTooLow.tooltip="Macro \"%1\" won't be able to check its condition at the desired interval of %2.\nEither change the condition check value on the General tab or in the settings of macro \"%3\"." AdvSceneSwitcher.generalTab.generalBehavior="General behavior" AdvSceneSwitcher.generalTab.generalBehavior.onNoMet="If no actions are performed for" AdvSceneSwitcher.generalTab.generalBehavior.onNoMetDelayTooltip="Will only ever be as accurate as the configured check interval." @@ -198,6 +200,8 @@ AdvSceneSwitcher.macroTab.newMacroUseShortCircuitEvaluation="Enable short circui AdvSceneSwitcher.macroTab.currentRegisterHotkeys="Register hotkeys to control the pause state of selected macro" AdvSceneSwitcher.macroTab.currentUseShortCircuitEvaluation="Enable short circuit evaluation of macro conditions for currently selected macro" AdvSceneSwitcher.macroTab.shortCircuit.tooltip="Enabling short circuit evaluation might improve the performance, as some condition checks are skipped, if the overall macro cannot be evaluated to \"true\" anymore.\nHowever, please note that condition checks, which are skipped over, will also not update their duration modifier checks." +AdvSceneSwitcher.macroTab.currentUseCustomConditionCheckInterval="Check conditions of the currently selected macro at custom interval:" +AdvSceneSwitcher.macroTab.currentUseCustomConditionCheckIntervalWarning="⚠️ The selected value is lower than the interval configured on the General tab.\nThe configured value not have any effect!" AdvSceneSwitcher.macroTab.currentSkipExecutionOnStartup="Skip execution of actions of current macro on startup" AdvSceneSwitcher.macroTab.currentStopActionsIfNotDone="Stop and rerun actions of the currently selected macro, if the actions are still running, when a new execution is triggered" AdvSceneSwitcher.macroTab.currentRegisterDock="Register dock widget to control the pause state of selected macro or run it manually" diff --git a/forms/advanced-scene-switcher.ui b/forms/advanced-scene-switcher.ui index d6ed4865..10d5827a 100644 --- a/forms/advanced-scene-switcher.ui +++ b/forms/advanced-scene-switcher.ui @@ -137,11 +137,22 @@ - - - AdvSceneSwitcher.generalTab.status.checkInterval - - + + + + + AdvSceneSwitcher.generalTab.status.checkInterval + + + + + + + + + + + diff --git a/lib/advanced-scene-switcher.hpp b/lib/advanced-scene-switcher.hpp index f17fede2..f1a4a1c7 100644 --- a/lib/advanced-scene-switcher.hpp +++ b/lib/advanced-scene-switcher.hpp @@ -381,6 +381,8 @@ signals: void SceneGroupRenamed(const QString &oldName, const QString newName); /* --- End of legacy tab section --- */ +private: + void SetCheckIntervalTooLowVisibility() const; }; void OpenSettingsWindow(); diff --git a/lib/general.cpp b/lib/general.cpp index cc79c84d..f61bf3c9 100644 --- a/lib/general.cpp +++ b/lib/general.cpp @@ -150,6 +150,8 @@ void AdvSceneSwitcher::on_checkInterval_valueChanged(int value) std::lock_guard lock(switcher->m); switcher->interval = value; + + SetCheckIntervalTooLowVisibility(); } void AdvSceneSwitcher::closeEvent(QCloseEvent *) @@ -823,6 +825,28 @@ static void setupGeneralTabInactiveWarning(QTabWidget *tabs) inactiveTimer->start(); } +void advss::AdvSceneSwitcher::SetCheckIntervalTooLowVisibility() const +{ + auto macro = GetMacroWithInvalidConditionInterval(); + if (!macro) { + ui->checkIntervalTooLowWarning->hide(); + return; + } + + const QString labelTextFormat(obs_module_text( + "AdvSceneSwitcher.generalTab.status.checkIntervalTooLow")); + const QString labelTooltipFormat(obs_module_text( + "AdvSceneSwitcher.generalTab.status.checkIntervalTooLow.tooltip")); + const QString name = QString::fromStdString(macro->Name()); + const QString duration = QString::fromStdString( + macro->GetCustomConditionCheckInterval().ToString()); + + ui->checkIntervalTooLowWarning->setText(labelTextFormat.arg(name)); + ui->checkIntervalTooLowWarning->setToolTip( + labelTooltipFormat.arg(name).arg(duration).arg(name)); + ui->checkIntervalTooLowWarning->show(); +} + void AdvSceneSwitcher::SetupGeneralTab() { PopulateSceneSelection(ui->noMatchSwitchScene, false); @@ -851,6 +875,7 @@ void AdvSceneSwitcher::SetupGeneralTab() SLOT(NoMatchDelayDurationChanged(const Duration &))); ui->checkInterval->setValue(switcher->interval); + SetCheckIntervalTooLowVisibility(); ui->enableCooldown->setChecked(switcher->enableCooldown); ui->cooldownTime->setEnabled(switcher->enableCooldown); diff --git a/lib/macro/macro-settings.cpp b/lib/macro/macro-settings.cpp index 314162cf..05899766 100644 --- a/lib/macro/macro-settings.cpp +++ b/lib/macro/macro-settings.cpp @@ -1,7 +1,8 @@ #include "macro-settings.hpp" #include "layout-helpers.hpp" -#include "obs-module-helper.hpp" #include "macro.hpp" +#include "obs-module-helper.hpp" +#include "plugin-state-helpers.hpp" #include #include @@ -63,6 +64,12 @@ MacroSettingsDialog::MacroSettingsDialog(QWidget *parent, "AdvSceneSwitcher.macroTab.currentRegisterHotkeys"))), _currentUseShortCircuitEvaluation(new QCheckBox(obs_module_text( "AdvSceneSwitcher.macroTab.currentUseShortCircuitEvaluation"))), + _currentUseCustomConditionCheckInterval(new QCheckBox(obs_module_text( + "AdvSceneSwitcher.macroTab.currentUseCustomConditionCheckInterval"))), + _currentCustomConditionCheckInterval( + new DurationSelection(this, true, 0.01)), + _currentCustomConditionCheckIntervalWarning(new QLabel(obs_module_text( + "AdvSceneSwitcher.macroTab.currentUseCustomConditionCheckIntervalWarning"))), _currentSkipOnStartup(new QCheckBox(obs_module_text( "AdvSceneSwitcher.macroTab.currentSkipExecutionOnStartup"))), _currentStopActionsIfNotDone(new QCheckBox(obs_module_text( @@ -118,6 +125,17 @@ MacroSettingsDialog::MacroSettingsDialog(QWidget *parent, generalLayout->addWidget(_currentStopActionsIfNotDone); generalLayout->addWidget(_currentUseShortCircuitEvaluation); generalLayout->addWidget(_newMacroUseShortCircuitEvaluation); + + auto durationLayout = new QHBoxLayout(); + durationLayout->addWidget(_currentUseCustomConditionCheckInterval); + durationLayout->addWidget(_currentCustomConditionCheckInterval); + durationLayout->addStretch(); + auto customConditionIntervalLayout = new QVBoxLayout(); + customConditionIntervalLayout->addLayout(durationLayout); + customConditionIntervalLayout->addWidget( + _currentCustomConditionCheckIntervalWarning); + + generalLayout->addLayout(customConditionIntervalLayout); generalOptions->setLayout(generalLayout); auto inputOptions = new QGroupBox( @@ -193,6 +211,13 @@ MacroSettingsDialog::MacroSettingsDialog(QWidget *parent, &MacroSettingsDialog::PauseButtonEnableChanged); connect(_currentMacroDockAddStatusLabel, &QCheckBox::stateChanged, this, &MacroSettingsDialog::StatusLabelEnableChanged); + connect(_currentUseCustomConditionCheckInterval, + &QCheckBox::stateChanged, this, [this](int state) { + _currentCustomConditionCheckInterval->setEnabled(state); + }); + connect(_currentCustomConditionCheckInterval, + &DurationSelection::DurationChanged, this, + [this]() { SetCustomConditionIntervalWarningVisibility(); }); auto scrollArea = new QScrollArea(this); scrollArea->setWidgetResizable(true); @@ -231,6 +256,13 @@ MacroSettingsDialog::MacroSettingsDialog(QWidget *parent, _currentMacroRegisterHotkeys->setChecked(macro->PauseHotkeysEnabled()); _currentUseShortCircuitEvaluation->setChecked( macro->ShortCircuitEvaluationEnabled()); + _currentUseCustomConditionCheckInterval->setChecked( + macro->CustomConditionCheckIntervalEnabled()); + _currentCustomConditionCheckInterval->SetDuration( + macro->GetCustomConditionCheckInterval()); + _currentCustomConditionCheckInterval->setEnabled( + macro->CustomConditionCheckIntervalEnabled()); + SetCustomConditionIntervalWarningVisibility(); _currentSkipOnStartup->setChecked(macro->SkipExecOnStart()); _currentStopActionsIfNotDone->setChecked(macro->StopActionsIfNotDone()); _currentInputs->SetInputs(macro->GetInputVariables()); @@ -332,6 +364,15 @@ void MacroSettingsDialog::Resize() _dockOptions->updateGeometry(); } +void MacroSettingsDialog::SetCustomConditionIntervalWarningVisibility() const +{ + _currentCustomConditionCheckIntervalWarning->setVisible( + _currentUseCustomConditionCheckInterval->isChecked() && + GetIntervalValue() > + _currentCustomConditionCheckInterval->GetDuration() + .Milliseconds()); +} + bool MacroSettingsDialog::AskForSettings(QWidget *parent, GlobalMacroSettings &userInput, Macro *macro) @@ -358,6 +399,10 @@ bool MacroSettingsDialog::AskForSettings(QWidget *parent, dialog._currentMacroRegisterHotkeys->isChecked()); macro->SetShortCircuitEvaluation( dialog._currentUseShortCircuitEvaluation->isChecked()); + macro->SetCustomConditionCheckIntervalEnabled( + dialog._currentUseCustomConditionCheckInterval->isChecked()); + macro->SetCustomConditionCheckInterval( + dialog._currentCustomConditionCheckInterval->GetDuration()); macro->SetSkipExecOnStart(dialog._currentSkipOnStartup->isChecked()); macro->SetStopActionsIfNotDone( dialog._currentStopActionsIfNotDone->isChecked()); diff --git a/lib/macro/macro-settings.hpp b/lib/macro/macro-settings.hpp index 0a0ed51e..c944d2ce 100644 --- a/lib/macro/macro-settings.hpp +++ b/lib/macro/macro-settings.hpp @@ -1,5 +1,6 @@ #pragma once #include "variable-line-edit.hpp" +#include "duration-control.hpp" #include "macro-input.hpp" #include @@ -44,6 +45,7 @@ private slots: private: void Resize(); + void SetCustomConditionIntervalWarningVisibility() const; // Global macro settings QCheckBox *_highlightExecutedMacros; @@ -54,6 +56,9 @@ private: // Current macro specific settings QCheckBox *_currentMacroRegisterHotkeys; QCheckBox *_currentUseShortCircuitEvaluation; + QCheckBox *_currentUseCustomConditionCheckInterval; + DurationSelection *_currentCustomConditionCheckInterval; + QLabel *_currentCustomConditionCheckIntervalWarning; QCheckBox *_currentSkipOnStartup; QCheckBox *_currentStopActionsIfNotDone; MacroInputSelection *_currentInputs; diff --git a/lib/macro/macro-tab.cpp b/lib/macro/macro-tab.cpp index 1a3b89b0..724e108d 100644 --- a/lib/macro/macro-tab.cpp +++ b/lib/macro/macro-tab.cpp @@ -795,6 +795,8 @@ void AdvSceneSwitcher::on_macroSettings_clicked() resetSegmentHighlights(ui->actionsList); resetSegmentHighlights(ui->elseActionsList); } + + SetCheckIntervalTooLowVisibility(); } static void moveControlsToSplitter(QSplitter *splitter, int idx, diff --git a/lib/macro/macro.cpp b/lib/macro/macro.cpp index 8885eae0..f00492e7 100644 --- a/lib/macro/macro.cpp +++ b/lib/macro/macro.cpp @@ -314,6 +314,21 @@ bool Macro::WasExecutedSince(const TimePoint &time) const return _lastExecutionTime > time; } +bool Macro::ConditionsShouldBeChecked() const +{ + if (!_useCustomConditionCheckInterval) { + return true; + } + + const auto timePassed = std::chrono::high_resolution_clock::now() - + LastConditionCheckTime(); + const auto timePassedMs = + std::chrono::duration_cast( + timePassed); + return timePassedMs.count() >= + _customConditionCheckInterval.Milliseconds(); +} + bool Macro::ShouldRunActions() const { const bool hasActionsToExecute = @@ -426,6 +441,26 @@ bool Macro::ShortCircuitEvaluationEnabled() const return _useShortCircuitEvaluation; } +void Macro::SetCustomConditionCheckIntervalEnabled(bool enable) +{ + _useCustomConditionCheckInterval = enable; +} + +bool Macro::CustomConditionCheckIntervalEnabled() const +{ + return _useCustomConditionCheckInterval; +} + +void Macro::SetCustomConditionCheckInterval(const Duration &duration) +{ + _customConditionCheckInterval = duration; +} + +Duration Macro::GetCustomConditionCheckInterval() const +{ + return _customConditionCheckInterval; +} + void Macro::SetPaused(bool pause) { if (_paused && !pause) { @@ -645,13 +680,6 @@ bool Macro::Save(obs_data_t *obj, bool saveForCopy) const if (!saveForCopy) { obs_data_set_string(obj, "name", _name.c_str()); } - obs_data_set_bool(obj, "pause", _paused); - obs_data_set_bool(obj, "parallel", _runInParallel); - 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) { @@ -662,6 +690,17 @@ bool Macro::Save(obs_data_t *obj, bool saveForCopy) const return true; } + obs_data_set_bool(obj, "pause", _paused); + obs_data_set_bool(obj, "parallel", _runInParallel); + 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, "useCustomConditionCheckInterval", + _useCustomConditionCheckInterval); + _customConditionCheckInterval.Save(obj, "customConditionCheckInterval"); + SaveDockSettings(obj, saveForCopy); SaveSplitterPos(_actionConditionSplitterPosition, obj, @@ -710,13 +749,6 @@ bool Macro::Save(obs_data_t *obj, bool saveForCopy) const bool Macro::Load(obs_data_t *obj) { _name = obs_data_get_string(obj, "name"); - _paused = obs_data_get_bool(obj, "pause"); - _runInParallel = obs_data_get_bool(obj, "parallel"); - _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) { @@ -728,6 +760,17 @@ bool Macro::Load(obs_data_t *obj) return true; } + _paused = obs_data_get_bool(obj, "pause"); + _runInParallel = obs_data_get_bool(obj, "parallel"); + _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"); + _useCustomConditionCheckInterval = + obs_data_get_bool(obj, "useCustomConditionCheckInterval"); + _customConditionCheckInterval.Load(obj, "customConditionCheckInterval"); + LoadDockSettings(obj); LoadSplitterPos(_actionConditionSplitterPosition, obj, @@ -1289,6 +1332,14 @@ bool CheckMacros() { bool matchFound = false; for (const auto &m : macros) { + if (!m->ConditionsShouldBeChecked()) { + vblog(LOG_INFO, + "skipping condition check for macro \"%s\" " + "(custom check interval)", + m->Name().c_str()); + continue; + } + if (m->CheckConditions() || m->ElseActions().size() > 0) { matchFound = true; // This has to be performed here for now as actions are @@ -1386,4 +1437,27 @@ void InvalidateMacroTempVarValues() } } +std::shared_ptr GetMacroWithInvalidConditionInterval() +{ + if (macros.empty()) { + return {}; + } + + for (const auto ¯o : macros) { + if (!macro) { + continue; + } + if (!macro->CustomConditionCheckIntervalEnabled()) { + continue; + } + + if (macro->GetCustomConditionCheckInterval().Milliseconds() < + GetIntervalValue()) { + return macro; + } + } + + return {}; +} + } // namespace advss diff --git a/lib/macro/macro.hpp b/lib/macro/macro.hpp index f19f75ff..e1abacb7 100644 --- a/lib/macro/macro.hpp +++ b/lib/macro/macro.hpp @@ -35,6 +35,7 @@ public: bool CheckConditions(bool ignorePause = false); bool ConditionsMatched() const { return _matched; } TimePoint LastConditionCheckTime() const { return _lastCheckTime; } + bool ConditionsShouldBeChecked() const; bool ShouldRunActions() const; bool PerformActions(bool match, bool forceParallel = false, @@ -60,6 +61,11 @@ public: void SetShortCircuitEvaluation(bool useShortCircuitEvaluation); bool ShortCircuitEvaluationEnabled() const; + void SetCustomConditionCheckIntervalEnabled(bool enable); + bool CustomConditionCheckIntervalEnabled() const; + void SetCustomConditionCheckInterval(const Duration &); + Duration GetCustomConditionCheckInterval() const; + int RunCount() const { return _runCount; }; void ResetRunCount() { _runCount = 0; }; @@ -188,10 +194,13 @@ private: bool _isCollapsed = false; bool _useShortCircuitEvaluation = false; + bool _useCustomConditionCheckInterval = true; + Duration _customConditionCheckInterval = 0.3; + bool _conditionSateChanged = false; + bool _runInParallel = false; bool _matched = false; bool _lastMatched = false; - bool _conditionSateChanged = false; bool _performActionsOnChange = true; bool _skipExecOnStart = false; bool _stopActionsIfNotDone = false; @@ -239,5 +248,6 @@ Macro *GetMacroByName(const char *name); Macro *GetMacroByQString(const QString &name); std::weak_ptr GetWeakMacroByName(const char *name); void InvalidateMacroTempVarValues(); +std::shared_ptr GetMacroWithInvalidConditionInterval(); } // namespace advss diff --git a/lib/utils/duration-control.hpp b/lib/utils/duration-control.hpp index c1bd7d68..8ae68dce 100644 --- a/lib/utils/duration-control.hpp +++ b/lib/utils/duration-control.hpp @@ -16,6 +16,7 @@ public: bool showUnitSelection = true, double minValue = 0.0); EXPORT void SetDuration(const Duration &); + Duration GetDuration() const { return _current; } EXPORT QDoubleSpinBox *SpinBox() { return _duration->SpinBox(); } private slots: