From 06c29bced5a33953c4c8235f0f9a92573fb5ef80 Mon Sep 17 00:00:00 2001 From: WarmUpTill <19472752+WarmUpTill@users.noreply.github.com> Date: Fri, 1 May 2026 18:59:47 +0200 Subject: [PATCH 1/9] Replace "Ignore Entry" condition logic with SwitchButton toggle --- data/locale/de-DE.ini | 1 - data/locale/en-US.ini | 1 - data/locale/es-ES.ini | 1 - data/locale/fr-FR.ini | 1 - data/locale/ja-JP.ini | 1 - data/locale/pt-BR.ini | 1 - data/locale/ru-RU.ini | 1 - data/locale/tr-TR.ini | 1 - data/locale/zh-CN.ini | 1 - lib/macro/macro-condition-edit.cpp | 22 ++++++++++++++++++++-- lib/macro/macro-condition-edit.hpp | 4 ++++ lib/macro/macro-condition.cpp | 7 ++++++- lib/macro/macro-segment.cpp | 8 +++++++- lib/macro/macro.cpp | 9 +++++++++ lib/utils/condition-logic.cpp | 3 +-- 15 files changed, 47 insertions(+), 15 deletions(-) diff --git a/data/locale/de-DE.ini b/data/locale/de-DE.ini index a31c0867..3812d4a9 100644 --- a/data/locale/de-DE.ini +++ b/data/locale/de-DE.ini @@ -99,7 +99,6 @@ AdvSceneSwitcher.macroList.deleted="gelöscht" AdvSceneSwitcher.macroList.duplicate="\"%1\" ist bereits ausgewählt!" ; Macro Logic -AdvSceneSwitcher.logic.none="Eintrag ignorieren" AdvSceneSwitcher.logic.and="Und" AdvSceneSwitcher.logic.or="Oder" AdvSceneSwitcher.logic.andNot="Und nicht" diff --git a/data/locale/en-US.ini b/data/locale/en-US.ini index 1c58b335..567161c2 100644 --- a/data/locale/en-US.ini +++ b/data/locale/en-US.ini @@ -306,7 +306,6 @@ AdvSceneSwitcher.macroList.deleted="deleted" AdvSceneSwitcher.macroList.duplicate="\"%1\" is alreay selected!" # Macro Logic -AdvSceneSwitcher.logic.none="Ignore entry" AdvSceneSwitcher.logic.and="And" AdvSceneSwitcher.logic.or="Or" AdvSceneSwitcher.logic.andNot="And not" diff --git a/data/locale/es-ES.ini b/data/locale/es-ES.ini index c1516c19..d7d7a49f 100644 --- a/data/locale/es-ES.ini +++ b/data/locale/es-ES.ini @@ -83,7 +83,6 @@ AdvSceneSwitcher.macroTab.highlightTrueConditions="Resaltar condiciones de la ma AdvSceneSwitcher.macroTab.highlightPerformedActions="Resaltar acciones realizadas recientemente de la macro seleccionada actualmente" ; Lógica de macros -AdvSceneSwitcher.logic.none="Omitir entrada" AdvSceneSwitcher.logic.and="Y" AdvSceneSwitcher.logic.or="O" AdvSceneSwitcher.logic.andNot="Y no" diff --git a/data/locale/fr-FR.ini b/data/locale/fr-FR.ini index 21173201..9973533b 100644 --- a/data/locale/fr-FR.ini +++ b/data/locale/fr-FR.ini @@ -131,7 +131,6 @@ AdvSceneSwitcher.macroList.deleted="supprimé" AdvSceneSwitcher.macroList.duplicate="\"%1\" est déjà sélectionné !" ; Macro Logic -AdvSceneSwitcher.logic.none="Ignorer l'entrée" AdvSceneSwitcher.logic.and="Et" AdvSceneSwitcher.logic.or="Ou" AdvSceneSwitcher.logic.andNot="Et pas" diff --git a/data/locale/ja-JP.ini b/data/locale/ja-JP.ini index 23444e06..5fc8c5eb 100644 --- a/data/locale/ja-JP.ini +++ b/data/locale/ja-JP.ini @@ -275,7 +275,6 @@ AdvSceneSwitcher.macroList.deleted="削除" AdvSceneSwitcher.macroList.duplicate="\"%1\" はすでに選択されています!" # Macro Logic -AdvSceneSwitcher.logic.none="入力無視" ; AdvSceneSwitcher.logic.and="And" ; AdvSceneSwitcher.logic.or="Or" ; AdvSceneSwitcher.logic.andNot="And not" diff --git a/data/locale/pt-BR.ini b/data/locale/pt-BR.ini index 38fe61b3..21476e55 100644 --- a/data/locale/pt-BR.ini +++ b/data/locale/pt-BR.ini @@ -239,7 +239,6 @@ AdvSceneSwitcher.macroList.deleted="excluída" AdvSceneSwitcher.macroList.duplicate="\"%1\" já está selecionada!" ; Macro Logic -AdvSceneSwitcher.logic.none="Ignorar entrada" AdvSceneSwitcher.logic.and="E" AdvSceneSwitcher.logic.or="Ou" AdvSceneSwitcher.logic.andNot="E não" diff --git a/data/locale/ru-RU.ini b/data/locale/ru-RU.ini index d1425056..18ace96f 100644 --- a/data/locale/ru-RU.ini +++ b/data/locale/ru-RU.ini @@ -70,7 +70,6 @@ AdvSceneSwitcher.macroTab.name="Имя:" AdvSceneSwitcher.macroTab.defaultname="Макрос %1" AdvSceneSwitcher.macroTab.copy="Создать копию" ; Macro Logic -AdvSceneSwitcher.logic.none="Игнорировать вход" AdvSceneSwitcher.logic.and="И" AdvSceneSwitcher.logic.or="Или" AdvSceneSwitcher.logic.andNot="И не" diff --git a/data/locale/tr-TR.ini b/data/locale/tr-TR.ini index 9d7a3743..fd1310ea 100644 --- a/data/locale/tr-TR.ini +++ b/data/locale/tr-TR.ini @@ -78,7 +78,6 @@ AdvSceneSwitcher.macroTab.expandAll="Hepsini Genişlet" AdvSceneSwitcher.macroTab.collapseAll="Hepsini Küçült" ; Macro Logic -AdvSceneSwitcher.logic.none="Girişi yoksay" AdvSceneSwitcher.logic.and="Ve" AdvSceneSwitcher.logic.or="Ya da" AdvSceneSwitcher.logic.andNot="ve değil" diff --git a/data/locale/zh-CN.ini b/data/locale/zh-CN.ini index ee2b7183..83fc970c 100644 --- a/data/locale/zh-CN.ini +++ b/data/locale/zh-CN.ini @@ -259,7 +259,6 @@ AdvSceneSwitcher.macroList.deleted="删除" AdvSceneSwitcher.macroList.duplicate="\"%1\" 已选择!" ; Macro Logic -AdvSceneSwitcher.logic.none="忽略条件" AdvSceneSwitcher.logic.and="且" AdvSceneSwitcher.logic.or="或" AdvSceneSwitcher.logic.andNot="且不" diff --git a/lib/macro/macro-condition-edit.cpp b/lib/macro/macro-condition-edit.cpp index 85edcf62..fef70ad6 100644 --- a/lib/macro/macro-condition-edit.cpp +++ b/lib/macro/macro-condition-edit.cpp @@ -5,6 +5,7 @@ #include "path-helpers.hpp" #include "plugin-state-helpers.hpp" #include "section.hpp" +#include "switch-button.hpp" #include "ui-helpers.hpp" #include "utility.hpp" @@ -102,12 +103,15 @@ MacroConditionEdit::MacroConditionEdit( QWidget *parent, std::shared_ptr *entryData, bool isRootCondition) : MacroSegmentEdit(parent), + _enable(new SwitchButton()), _logicSelection(new QComboBox()), _conditionSelection(new FilterComboBox()), _dur(new DurationModifierEdit()), _entryData(entryData), _isRoot(isRootCondition) { + QWidget::connect(_enable, SIGNAL(checked(bool)), this, + SLOT(ConditionEnableChanged(bool))); QWidget::connect(_logicSelection, SIGNAL(currentIndexChanged(int)), this, SLOT(LogicSelectionChanged(int))); QWidget::connect(_conditionSelection, @@ -122,6 +126,7 @@ MacroConditionEdit::MacroConditionEdit( Logic::PopulateLogicTypeSelection(_logicSelection, isRootCondition); populateConditionSelection(_conditionSelection); + _section->AddHeaderWidget(_enable); _section->AddHeaderWidget(_logicSelection); _section->AddHeaderWidget(_conditionSelection); _section->AddHeaderWidget(_headerInfo); @@ -154,8 +159,17 @@ void MacroConditionEdit::LogicSelectionChanged(int idx) const auto logic = static_cast( _logicSelection->itemData(idx).toInt()); (*_entryData)->SetLogicType(logic); +} - SetEnableAppearance(logic != Logic::Type::NONE); +void MacroConditionEdit::ConditionEnableChanged(bool value) +{ + if (_loading || !_entryData) { + return; + } + + auto lock = LockContext(); + (*_entryData)->SetEnabled(value); + SetDisableEffect(!value); } bool MacroConditionEdit::IsRootNode() const @@ -168,7 +182,9 @@ void MacroConditionEdit::SetLogicSelection() const auto logic = (*_entryData)->GetLogicType(); _logicSelection->setCurrentIndex( _logicSelection->findData(static_cast(logic))); - SetEnableAppearance(logic != Logic::Type::NONE); + const bool enabled = (*_entryData)->Enabled(); + _enable->setChecked(enabled); + SetEnableAppearance(enabled); } void MacroConditionEdit::SetRootNode(bool root) @@ -239,10 +255,12 @@ void MacroConditionEdit::ConditionSelectionChanged(const QString &text) { auto lock = LockContext(); auto logic = (*_entryData)->GetLogicType(); + const bool enabled = (*_entryData)->Enabled(); _entryData->reset(); *_entryData = MacroConditionFactory::Create(id, macro); (*_entryData)->SetIndex(idx); (*_entryData)->SetLogicType(logic); + (*_entryData)->SetEnabled(enabled); (*_entryData)->PostLoad(); RunAndClearPostLoadSteps(); } diff --git a/lib/macro/macro-condition-edit.hpp b/lib/macro/macro-condition-edit.hpp index b3450518..7c50b885 100644 --- a/lib/macro/macro-condition-edit.hpp +++ b/lib/macro/macro-condition-edit.hpp @@ -8,6 +8,8 @@ namespace advss { +class SwitchButton; + class DurationModifierEdit : public QWidget { Q_OBJECT public: @@ -47,11 +49,13 @@ private slots: void ConditionSelectionChanged(const QString &text); void DurationChanged(const Duration &value); void DurationModifierChanged(DurationModifier::Type m); + void ConditionEnableChanged(bool); private: void SetLogicSelection(); std::shared_ptr Data() const; + SwitchButton *_enable; QComboBox *_logicSelection; FilterComboBox *_conditionSelection; DurationModifierEdit *_dur; diff --git a/lib/macro/macro-condition.cpp b/lib/macro/macro-condition.cpp index f855f405..dc26f9dc 100644 --- a/lib/macro/macro-condition.cpp +++ b/lib/macro/macro-condition.cpp @@ -31,6 +31,10 @@ bool MacroCondition::Load(obs_data_t *obj) { MacroSegment::Load(obj); _logic.Load(obj, "logic"); + if (_logic.GetType() == Logic::Type::NONE) { + SetEnabled(false); + _logic.SetType(Logic::Type::AND); + } _durationModifier.Load(obj); return true; } @@ -50,7 +54,8 @@ void MacroCondition::ValidateLogicSelection(bool isRootCondition, return; } - _logic.SetType(Logic::Type::NONE); + _logic.SetType(Logic::Type::AND); + SetEnabled(false); blog(LOG_WARNING, "setting invalid logic selection to 'ignore' for macro %s", context); diff --git a/lib/macro/macro-segment.cpp b/lib/macro/macro-segment.cpp index 2103bc6e..64d8d89a 100644 --- a/lib/macro/macro-segment.cpp +++ b/lib/macro/macro-segment.cpp @@ -80,7 +80,7 @@ bool MacroSegment::Save(obs_data_t *obj) const obs_data_set_bool(data, "useCustomLabel", _useCustomLabel); obs_data_set_string(data, "customLabel", _customLabel.c_str()); obs_data_set_bool(data, "enabled", _enabled); - obs_data_set_int(data, "version", 1); + obs_data_set_int(data, "version", 2); OBSDataArrayAutoRelease mappingsArray = obs_data_array_create(); for (const auto &mapping : _varMappings) { @@ -112,6 +112,12 @@ bool MacroSegment::Load(obs_data_t *obj) _enabled = obs_data_get_bool(obj, "enabled"); } + // Reset the previously unused "enabled" value for conditions to "true" + if (obs_data_get_int(data, "version") < 2 && + obs_data_has_user_value(obj, "logic")) { + _enabled = true; + } + _varMappings.clear(); OBSDataArrayAutoRelease mappingsArray = obs_data_get_array(data, "varMappings"); diff --git a/lib/macro/macro.cpp b/lib/macro/macro.cpp index 55e68c9b..2369b780 100644 --- a/lib/macro/macro.cpp +++ b/lib/macro/macro.cpp @@ -157,6 +157,15 @@ bool Macro::CheckConditionHelper( return conditionMatched; }; + if (!condition->Enabled()) { + vblog(LOG_INFO, "ignoring condition '%s' for '%s'", + condition->GetId().c_str(), _name.c_str()); + if (!_useShortCircuitEvaluation) { + (void)evaluateCondition(); + } + return _matched; + } + const auto logicType = condition->GetLogicType(); if (logicType == Logic::Type::NONE) { vblog(LOG_INFO, "ignoring condition '%s' for '%s'", diff --git a/lib/utils/condition-logic.cpp b/lib/utils/condition-logic.cpp index 67deac74..efa915da 100644 --- a/lib/utils/condition-logic.cpp +++ b/lib/utils/condition-logic.cpp @@ -8,7 +8,6 @@ namespace advss { const std::map Logic::localeMap = { - {Logic::Type::NONE, {"AdvSceneSwitcher.logic.none"}}, {Logic::Type::AND, {"AdvSceneSwitcher.logic.and"}}, {Logic::Type::OR, {"AdvSceneSwitcher.logic.or"}}, {Logic::Type::AND_NOT, {"AdvSceneSwitcher.logic.andNot"}}, @@ -67,7 +66,7 @@ void Logic::PopulateLogicTypeSelection(QComboBox *list, bool isRootCondition) return typeValue < rootOffset; }} : std::function{[](int typeValue) { - return typeValue >= rootOffset; + return typeValue > rootOffset; }}; for (const auto &[type, name] : localeMap) { const int typeValue = static_cast(type); From 27116c0cd391db222069f13fb2c66758f2305aec Mon Sep 17 00:00:00 2001 From: WarmUpTill <19472752+WarmUpTill@users.noreply.github.com> Date: Tue, 5 May 2026 19:11:04 +0200 Subject: [PATCH 2/9] Ensure plugin is stopped before running cleanup steps --- lib/advanced-scene-switcher.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/advanced-scene-switcher.cpp b/lib/advanced-scene-switcher.cpp index f800406d..5279d020 100644 --- a/lib/advanced-scene-switcher.cpp +++ b/lib/advanced-scene-switcher.cpp @@ -506,6 +506,8 @@ bool SwitcherData::AnySceneTransitionStarted() ******************************************************************************/ extern "C" EXPORT void FreeSceneSwitcher() { + switcher->Stop(); + PlatformCleanup(); RunPluginCleanupSteps(); From 964b6c6c719d454643069c6600e3a267e2d1f458 Mon Sep 17 00:00:00 2001 From: WarmUpTill <19472752+WarmUpTill@users.noreply.github.com> Date: Mon, 4 May 2026 20:47:17 +0200 Subject: [PATCH 3/9] Don't highlight action trigger mode when macro is paused --- lib/macro/macro-tab.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/macro/macro-tab.cpp b/lib/macro/macro-tab.cpp index d5aba05b..3a8bfd80 100644 --- a/lib/macro/macro-tab.cpp +++ b/lib/macro/macro-tab.cpp @@ -557,6 +557,10 @@ void AdvSceneSwitcher::HighlightOnChange() const return; } + if (macro->Paused()) { + return; + } + if (macro->ActionTriggerModePreventedActionsSince( lastOnChangeHighlightCheckTime)) { HighlightWidget(ui->actionTriggerMode, Qt::yellow, From 5d3c83c2921f6b18ff594454e66e8e3ed122fd73 Mon Sep 17 00:00:00 2001 From: WarmUpTill <19472752+WarmUpTill@users.noreply.github.com> Date: Mon, 4 May 2026 21:07:43 +0200 Subject: [PATCH 4/9] Don't log "on change" when macro is paused --- lib/macro/macro.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/macro/macro.cpp b/lib/macro/macro.cpp index 2369b780..197e57c6 100644 --- a/lib/macro/macro.cpp +++ b/lib/macro/macro.cpp @@ -389,7 +389,7 @@ bool Macro::ShouldRunActions() const !_paused && (_matched || _elseActions.size() > 0) && _actionModeMatch; - if (VerboseLoggingEnabled() && !_actionModeMatch) { + if (VerboseLoggingEnabled() && !_actionModeMatch && !_paused) { if (_matched && _actions.size() > 0) { blog(LOG_INFO, "skip actions for Macro %s (on change)", _name.c_str()); From 312fe1d6487a05014b0f65f3b1365c445a415028 Mon Sep 17 00:00:00 2001 From: WarmUpTill <19472752+WarmUpTill@users.noreply.github.com> Date: Tue, 5 May 2026 23:00:11 +0200 Subject: [PATCH 5/9] Don't highlight paused macro in macro list --- lib/macro/macro.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/macro/macro.cpp b/lib/macro/macro.cpp index 197e57c6..51a0f151 100644 --- a/lib/macro/macro.cpp +++ b/lib/macro/macro.cpp @@ -290,7 +290,7 @@ bool Macro::CheckConditions(bool ignorePause) const bool hasActionsToExecute = _matched ? (_actions.size() > 0) : (_elseActions.size() > 0); - if (!_actionModeMatch && hasActionsToExecute) { + if (!_actionModeMatch && hasActionsToExecute && !_paused) { _lastActionRunModePreventTime = std::chrono::high_resolution_clock::now(); } From 61ee58ba70d9321435f37ca97948131b634e0ea4 Mon Sep 17 00:00:00 2001 From: WarmUpTill <19472752+WarmUpTill@users.noreply.github.com> Date: Tue, 5 May 2026 20:00:40 +0200 Subject: [PATCH 6/9] Enable resolving plugin symbols in OBS crash reports --- lib/win/advanced-scene-switcher-win.cpp | 82 +++++++++++++++++++++++++ 1 file changed, 82 insertions(+) diff --git a/lib/win/advanced-scene-switcher-win.cpp b/lib/win/advanced-scene-switcher-win.cpp index 2ba00294..530a3c05 100644 --- a/lib/win/advanced-scene-switcher-win.cpp +++ b/lib/win/advanced-scene-switcher-win.cpp @@ -1,10 +1,12 @@ #include "platform-funcs.hpp" +#include "plugin-state-helpers.hpp" #include #include #include #include #include +#include #include #include #include @@ -16,6 +18,9 @@ #include #include +#define ADVSS_WIDEN_(x) L##x +#define ADVSS_WIDEN(x) ADVSS_WIDEN_(x) + namespace advss { #define MAX_SEARCH 1000 @@ -500,9 +505,86 @@ int SecondsSinceLastInput() return (getTime() - getLastInputTime()) / 1000; } +static void addPluginFolderToSymbolPath() +{ + // This runs after OBS_FRONTEND_EVENT_FINISHED_LOADING, which fires after + // obs_load_all_modules() completes. By that point OBS has already called + // reset_win32_symbol_paths() -> SymInitializeW(), so DbgHelp is + // initialized and we can append our plugins subfolder (where the PDB + // files live) to the existing search path. + HMODULE dbghelp = LoadLibraryW(L"DbgHelp"); + if (!dbghelp) { + return; + } + + typedef BOOL(WINAPI * SymGetSearchPathW_t)(HANDLE, PWSTR, DWORD); + typedef BOOL(WINAPI * SymSetSearchPathW_t)(HANDLE, PCWSTR); + typedef BOOL(WINAPI * SymRefreshModuleList_t)(HANDLE); + + auto symGetSearchPathW = reinterpret_cast( + GetProcAddress(dbghelp, "SymGetSearchPathW")); + auto symSetSearchPathW = reinterpret_cast( + GetProcAddress(dbghelp, "SymSetSearchPathW")); + auto symRefreshModuleList = reinterpret_cast( + GetProcAddress(dbghelp, "SymRefreshModuleList")); + + if (!symGetSearchPathW || !symSetSearchPathW || !symRefreshModuleList) { + FreeLibrary(dbghelp); + return; + } + + HMODULE hModule = NULL; + if (!GetModuleHandleExW( + GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | + GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, + reinterpret_cast(addPluginFolderToSymbolPath), + &hModule)) { + FreeLibrary(dbghelp); + return; + } + + wchar_t dllDir[MAX_PATH]; + if (!GetModuleFileNameW(hModule, dllDir, MAX_PATH)) { + FreeLibrary(dbghelp); + return; + } + + wchar_t *lastSep = wcsrchr(dllDir, L'\\'); + if (!lastSep) { + FreeLibrary(dbghelp); + return; + } + *lastSep = L'\0'; + + wchar_t pluginsPath[MAX_PATH]; + wcsncpy_s(pluginsPath, MAX_PATH, dllDir, _TRUNCATE); + wcsncat_s(pluginsPath, MAX_PATH, L"\\" ADVSS_WIDEN(ADVSS_PLUGIN_FOLDER), + _TRUNCATE); + + constexpr DWORD currentPathLen = 4096; + constexpr DWORD newPathLen = 8192; + auto currentPath = std::make_unique(currentPathLen); + auto newPath = std::make_unique(newPathLen); + + symGetSearchPathW(GetCurrentProcess(), currentPath.get(), + currentPathLen); + + if (currentPath[0] != L'\0') { + _snwprintf_s(newPath.get(), newPathLen, _TRUNCATE, L"%s;%s", + currentPath.get(), pluginsPath); + } else { + wcsncpy_s(newPath.get(), newPathLen, pluginsPath, _TRUNCATE); + } + + symSetSearchPathW(GetCurrentProcess(), newPath.get()); + symRefreshModuleList(GetCurrentProcess()); + FreeLibrary(dbghelp); +} + void PlatformInit() { CoInitialize(NULL); + AddFinishedLoadingStep(addPluginFolderToSymbolPath); } void PlatformCleanup() From 6da151f7d7b37c0893d786259ce0adc547741d7a Mon Sep 17 00:00:00 2001 From: WarmUpTill <19472752+WarmUpTill@users.noreply.github.com> Date: Thu, 7 May 2026 19:34:20 +0200 Subject: [PATCH 7/9] Fix variable tab not listing variables on startup --- lib/utils/plugin-state-helpers.cpp | 30 ++++++++++++++++++++++++++++ lib/utils/plugin-state-helpers.hpp | 2 ++ lib/variables/variable-tab.cpp | 1 - lib/variables/variable.cpp | 4 ++-- tests/stubs/plugin-state-helpers.cpp | 2 ++ 5 files changed, 36 insertions(+), 3 deletions(-) diff --git a/lib/utils/plugin-state-helpers.cpp b/lib/utils/plugin-state-helpers.cpp index fa4fce75..75ca49b7 100644 --- a/lib/utils/plugin-state-helpers.cpp +++ b/lib/utils/plugin-state-helpers.cpp @@ -75,6 +75,18 @@ static std::vector> &getStopSteps() return steps; } +static std::vector> &getEarlySaveSteps() +{ + static std::vector> steps; + return steps; +} + +static std::vector> &getEarlyLoadSteps() +{ + static std::vector> steps; + return steps; +} + static std::vector> &getSaveSteps() { static std::vector> steps; @@ -109,6 +121,18 @@ void LoadPluginSettings(obs_data_t *obj) GetSwitcher()->LoadSettings(obj); } +void AddEarlySaveStep(std::function step) +{ + std::lock_guard lock(mutex); + getEarlySaveSteps().emplace_back(step); +} + +void AddEarlyLoadStep(std::function step) +{ + std::lock_guard lock(mutex); + getEarlyLoadSteps().emplace_back(step); +} + void AddSaveStep(std::function step) { std::lock_guard lock(mutex); @@ -136,6 +160,9 @@ void AddIntervalResetStep(std::function step) void RunSaveSteps(obs_data_t *obj) { std::lock_guard lock(mutex); + for (const auto &func : getEarlySaveSteps()) { + func(obj); + } for (const auto &func : getSaveSteps()) { func(obj); } @@ -144,6 +171,9 @@ void RunSaveSteps(obs_data_t *obj) void RunLoadSteps(obs_data_t *obj) { std::lock_guard lock(mutex); + for (const auto &func : getEarlyLoadSteps()) { + func(obj); + } for (const auto &func : getLoadSteps()) { func(obj); } diff --git a/lib/utils/plugin-state-helpers.hpp b/lib/utils/plugin-state-helpers.hpp index 1df274d3..0ee36edc 100644 --- a/lib/utils/plugin-state-helpers.hpp +++ b/lib/utils/plugin-state-helpers.hpp @@ -8,6 +8,8 @@ namespace advss { void SavePluginSettings(obs_data_t *); EXPORT void LoadPluginSettings(obs_data_t *); +void AddEarlySaveStep(std::function); +void AddEarlyLoadStep(std::function); EXPORT void AddSaveStep(std::function); EXPORT void AddLoadStep(std::function); EXPORT void AddPostLoadStep(std::function); diff --git a/lib/variables/variable-tab.cpp b/lib/variables/variable-tab.cpp index 3a4ed292..84f3d534 100644 --- a/lib/variables/variable-tab.cpp +++ b/lib/variables/variable-tab.cpp @@ -63,7 +63,6 @@ static void load(obs_data_t *data) { tabSettings.Load(data, "tabSettings"); dockSettings.Load(data, "dockSettings"); - enableDock(obs_data_get_bool(data, "addVariablesDock")); } diff --git a/lib/variables/variable.cpp b/lib/variables/variable.cpp index 61b3ec8d..9ae25c07 100644 --- a/lib/variables/variable.cpp +++ b/lib/variables/variable.cpp @@ -20,8 +20,8 @@ static std::chrono::high_resolution_clock::time_point lastVariableChange{}; static bool setup() { - AddSaveStep(SaveVariables); - AddLoadStep(LoadVariables); + AddEarlySaveStep(SaveVariables); + AddEarlyLoadStep(LoadVariables); AddPluginCleanupStep([]() { variables.clear(); }); return true; } diff --git a/tests/stubs/plugin-state-helpers.cpp b/tests/stubs/plugin-state-helpers.cpp index eb94fb8b..b037ba56 100644 --- a/tests/stubs/plugin-state-helpers.cpp +++ b/tests/stubs/plugin-state-helpers.cpp @@ -4,6 +4,8 @@ namespace advss { void SavePluginSettings(obs_data_t *) {} void LoadPluginSettings(obs_data_t *) {} +void AddEarlySaveStep(std::function) {} +void AddEarlyLoadStep(std::function) {} void AddSaveStep(std::function) {} void AddLoadStep(std::function) {} void AddPostLoadStep(std::function) {} From 9fff05c57c8f1f3a0aee7a11db7b3ab914976656 Mon Sep 17 00:00:00 2001 From: WarmUpTill <19472752+WarmUpTill@users.noreply.github.com> Date: Fri, 8 May 2026 20:44:48 +0200 Subject: [PATCH 8/9] Fix "Run Macro" help being visible when option is not selected --- lib/macro/macro-action-macro.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/macro/macro-action-macro.cpp b/lib/macro/macro-action-macro.cpp index 725921db..164e897f 100644 --- a/lib/macro/macro-action-macro.cpp +++ b/lib/macro/macro-action-macro.cpp @@ -871,6 +871,8 @@ void MacroActionMacroEdit::SetWidgetVisibility() } else { _noConditionsWarning->setVisible(false); } + _runMacroHelp->setVisible(action == + MacroActionMacro::Action::RUN_MACRO); _nestedMacro->setVisible(action == MacroActionMacro::Action::NESTED_MACRO); From 6d7e23c88f6614081f2ba681cbe7224ca15b17c2 Mon Sep 17 00:00:00 2001 From: WarmUpTill <19472752+WarmUpTill@users.noreply.github.com> Date: Fri, 8 May 2026 21:04:00 +0200 Subject: [PATCH 9/9] locale: Clarify "reevaluate condition state" option for run macro action --- data/locale/en-US.ini | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/data/locale/en-US.ini b/data/locale/en-US.ini index 567161c2..96b8b4ab 100644 --- a/data/locale/en-US.ini +++ b/data/locale/en-US.ini @@ -1075,8 +1075,8 @@ AdvSceneSwitcher.action.macro.type.runActions="Run macro actions" AdvSceneSwitcher.action.macro.type.run.conditions.ignore="Do not consider condition state" AdvSceneSwitcher.action.macro.type.run.conditions.true="Only if conditions evaluate to true" AdvSceneSwitcher.action.macro.type.run.conditions.false="Only if conditions evaluate to false" -AdvSceneSwitcher.action.macro.type.run.updateConditionMatchState="Reevaluate the condition state before executing this action" -AdvSceneSwitcher.action.macro.type.run.updateConditionMatchState.help="The plugin operates in phases:\n * The phase evaluating the macro conditions\n * The phase running macro actions\nMacros executed before this particular action might have side effects on the condition state of macros.\nCheck this option if you want those side effects to be taken into account when evaluating the condition state." +AdvSceneSwitcher.action.macro.type.run.updateConditionMatchState="Check conditions now, even if macro is paused" +AdvSceneSwitcher.action.macro.type.run.updateConditionMatchState.help="The plugin operates in phases:\n * The phase evaluating the macro conditions\n * The phase running macro actions\nMacros executed before this particular action might have side effects on the condition state of macros.\nAdditionally, paused macros are assumed to not match their conditions.\nCheck this option to force a fresh condition evaluation before running this action, regardless of whether the macro is paused." AdvSceneSwitcher.action.macro.type.run.actionType.regular="actions" AdvSceneSwitcher.action.macro.type.run.actionType.else="else-actions" AdvSceneSwitcher.action.macro.type.run.skipWhenPaused="Skip execution when macro is paused"