Compare commits

..

2 Commits
ui ... test

Author SHA1 Message Date
WarmUpTill
9d38d22270 Tests: Make building tests optional, enable for CI 2026-04-29 22:58:45 +02:00
WarmUpTill
de007176d5 Tests: Handle QFile::open() return value 2026-04-29 22:58:41 +02:00
35 changed files with 69 additions and 286 deletions

View File

@@ -99,6 +99,7 @@ AdvSceneSwitcher.macroList.deleted="gelöscht"
AdvSceneSwitcher.macroList.duplicate="\"%1\" ist bereits ausgewählt!" AdvSceneSwitcher.macroList.duplicate="\"%1\" ist bereits ausgewählt!"
; Macro Logic ; Macro Logic
AdvSceneSwitcher.logic.none="Eintrag ignorieren"
AdvSceneSwitcher.logic.and="Und" AdvSceneSwitcher.logic.and="Und"
AdvSceneSwitcher.logic.or="Oder" AdvSceneSwitcher.logic.or="Oder"
AdvSceneSwitcher.logic.andNot="Und nicht" AdvSceneSwitcher.logic.andNot="Und nicht"

View File

@@ -306,6 +306,7 @@ AdvSceneSwitcher.macroList.deleted="deleted"
AdvSceneSwitcher.macroList.duplicate="\"%1\" is alreay selected!" AdvSceneSwitcher.macroList.duplicate="\"%1\" is alreay selected!"
# Macro Logic # Macro Logic
AdvSceneSwitcher.logic.none="Ignore entry"
AdvSceneSwitcher.logic.and="And" AdvSceneSwitcher.logic.and="And"
AdvSceneSwitcher.logic.or="Or" AdvSceneSwitcher.logic.or="Or"
AdvSceneSwitcher.logic.andNot="And not" AdvSceneSwitcher.logic.andNot="And not"
@@ -1075,8 +1076,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.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.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.conditions.false="Only if conditions evaluate to false"
AdvSceneSwitcher.action.macro.type.run.updateConditionMatchState="Check conditions now, even if macro is paused" 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.\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.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.actionType.regular="actions" AdvSceneSwitcher.action.macro.type.run.actionType.regular="actions"
AdvSceneSwitcher.action.macro.type.run.actionType.else="else-actions" AdvSceneSwitcher.action.macro.type.run.actionType.else="else-actions"
AdvSceneSwitcher.action.macro.type.run.skipWhenPaused="Skip execution when macro is paused" AdvSceneSwitcher.action.macro.type.run.skipWhenPaused="Skip execution when macro is paused"

View File

@@ -83,6 +83,7 @@ AdvSceneSwitcher.macroTab.highlightTrueConditions="Resaltar condiciones de la ma
AdvSceneSwitcher.macroTab.highlightPerformedActions="Resaltar acciones realizadas recientemente de la macro seleccionada actualmente" AdvSceneSwitcher.macroTab.highlightPerformedActions="Resaltar acciones realizadas recientemente de la macro seleccionada actualmente"
; Lógica de macros ; Lógica de macros
AdvSceneSwitcher.logic.none="Omitir entrada"
AdvSceneSwitcher.logic.and="Y" AdvSceneSwitcher.logic.and="Y"
AdvSceneSwitcher.logic.or="O" AdvSceneSwitcher.logic.or="O"
AdvSceneSwitcher.logic.andNot="Y no" AdvSceneSwitcher.logic.andNot="Y no"

View File

@@ -131,6 +131,7 @@ AdvSceneSwitcher.macroList.deleted="supprimé"
AdvSceneSwitcher.macroList.duplicate="\"%1\" est déjà sélectionné !" AdvSceneSwitcher.macroList.duplicate="\"%1\" est déjà sélectionné !"
; Macro Logic ; Macro Logic
AdvSceneSwitcher.logic.none="Ignorer l'entrée"
AdvSceneSwitcher.logic.and="Et" AdvSceneSwitcher.logic.and="Et"
AdvSceneSwitcher.logic.or="Ou" AdvSceneSwitcher.logic.or="Ou"
AdvSceneSwitcher.logic.andNot="Et pas" AdvSceneSwitcher.logic.andNot="Et pas"

View File

@@ -275,6 +275,7 @@ AdvSceneSwitcher.macroList.deleted="削除"
AdvSceneSwitcher.macroList.duplicate="\"%1\" はすでに選択されています!" AdvSceneSwitcher.macroList.duplicate="\"%1\" はすでに選択されています!"
# Macro Logic # Macro Logic
AdvSceneSwitcher.logic.none="入力無視"
; AdvSceneSwitcher.logic.and="And" ; AdvSceneSwitcher.logic.and="And"
; AdvSceneSwitcher.logic.or="Or" ; AdvSceneSwitcher.logic.or="Or"
; AdvSceneSwitcher.logic.andNot="And not" ; AdvSceneSwitcher.logic.andNot="And not"

View File

@@ -239,6 +239,7 @@ AdvSceneSwitcher.macroList.deleted="excluída"
AdvSceneSwitcher.macroList.duplicate="\"%1\" já está selecionada!" AdvSceneSwitcher.macroList.duplicate="\"%1\" já está selecionada!"
; Macro Logic ; Macro Logic
AdvSceneSwitcher.logic.none="Ignorar entrada"
AdvSceneSwitcher.logic.and="E" AdvSceneSwitcher.logic.and="E"
AdvSceneSwitcher.logic.or="Ou" AdvSceneSwitcher.logic.or="Ou"
AdvSceneSwitcher.logic.andNot="E não" AdvSceneSwitcher.logic.andNot="E não"

View File

@@ -70,6 +70,7 @@ AdvSceneSwitcher.macroTab.name="Имя:"
AdvSceneSwitcher.macroTab.defaultname="Макрос %1" AdvSceneSwitcher.macroTab.defaultname="Макрос %1"
AdvSceneSwitcher.macroTab.copy="Создать копию" AdvSceneSwitcher.macroTab.copy="Создать копию"
; Macro Logic ; Macro Logic
AdvSceneSwitcher.logic.none="Игнорировать вход"
AdvSceneSwitcher.logic.and="И" AdvSceneSwitcher.logic.and="И"
AdvSceneSwitcher.logic.or="Или" AdvSceneSwitcher.logic.or="Или"
AdvSceneSwitcher.logic.andNot="И не" AdvSceneSwitcher.logic.andNot="И не"

View File

@@ -78,6 +78,7 @@ AdvSceneSwitcher.macroTab.expandAll="Hepsini Genişlet"
AdvSceneSwitcher.macroTab.collapseAll="Hepsini Küçült" AdvSceneSwitcher.macroTab.collapseAll="Hepsini Küçült"
; Macro Logic ; Macro Logic
AdvSceneSwitcher.logic.none="Girişi yoksay"
AdvSceneSwitcher.logic.and="Ve" AdvSceneSwitcher.logic.and="Ve"
AdvSceneSwitcher.logic.or="Ya da" AdvSceneSwitcher.logic.or="Ya da"
AdvSceneSwitcher.logic.andNot="ve değil" AdvSceneSwitcher.logic.andNot="ve değil"

View File

@@ -259,6 +259,7 @@ AdvSceneSwitcher.macroList.deleted="删除"
AdvSceneSwitcher.macroList.duplicate="\"%1\" 已选择!" AdvSceneSwitcher.macroList.duplicate="\"%1\" 已选择!"
; Macro Logic ; Macro Logic
AdvSceneSwitcher.logic.none="忽略条件"
AdvSceneSwitcher.logic.and="且" AdvSceneSwitcher.logic.and="且"
AdvSceneSwitcher.logic.or="或" AdvSceneSwitcher.logic.or="或"
AdvSceneSwitcher.logic.andNot="且不" AdvSceneSwitcher.logic.andNot="且不"

View File

@@ -506,8 +506,6 @@ bool SwitcherData::AnySceneTransitionStarted()
******************************************************************************/ ******************************************************************************/
extern "C" EXPORT void FreeSceneSwitcher() extern "C" EXPORT void FreeSceneSwitcher()
{ {
switcher->Stop();
PlatformCleanup(); PlatformCleanup();
RunPluginCleanupSteps(); RunPluginCleanupSteps();
@@ -629,20 +627,13 @@ static void handleSceneCollectionCleanup()
return; return;
} }
// OBS_FRONTEND_EVENT_SCENE_COLLECTION_CLEANUP is also called on
// shutdown.
// Here we also don't want to clear the settings.
if (switcher->obsIsShuttingDown) {
return;
}
SaveSceneSwitcher(nullptr, false, nullptr); SaveSceneSwitcher(nullptr, false, nullptr);
} }
// Note to future self: // Note to future self:
// be careful using switcher->m here as there is potential for deadlocks when using // be careful using switcher->m here as there is potential for deadlocks when using
// frontend functions such as obs_frontend_set_current_scene() // frontend functions such as obs_frontend_set_current_scene()
static void OBSEvent(enum obs_frontend_event event, void *) static void OBSEvent(enum obs_frontend_event event, void *switcher)
{ {
if (!switcher) { if (!switcher) {
return; return;
@@ -776,6 +767,8 @@ void HighlightMacroSettingsButton(bool enable)
enable); enable);
} }
void SetupActionQueues();
extern "C" EXPORT void InitSceneSwitcher(obs_module_t *module, extern "C" EXPORT void InitSceneSwitcher(obs_module_t *module,
translateFunc translate) translateFunc translate)
{ {
@@ -787,11 +780,12 @@ extern "C" EXPORT void InitSceneSwitcher(obs_module_t *module,
PlatformInit(); PlatformInit();
LoadPlugins(); LoadPlugins();
SetupDock(); SetupDock();
SetupActionQueues();
RunPluginInitSteps(); RunPluginInitSteps();
obs_frontend_add_save_callback(SaveSceneSwitcher, nullptr); obs_frontend_add_save_callback(SaveSceneSwitcher, nullptr);
obs_frontend_add_event_callback(OBSEvent, nullptr); obs_frontend_add_event_callback(OBSEvent, switcher);
QAction *action = (QAction *)obs_frontend_add_tools_menu_qaction( QAction *action = (QAction *)obs_frontend_add_tools_menu_qaction(
obs_module_text("AdvSceneSwitcher.pluginName")); obs_module_text("AdvSceneSwitcher.pluginName"));

View File

@@ -468,6 +468,7 @@ void SwitcherData::LoadSettings(obs_data_t *obj)
// Needs to be loaded before any entries which might rely on scene group // Needs to be loaded before any entries which might rely on scene group
// selections to be available. // selections to be available.
loadSceneGroups(obj); loadSceneGroups(obj);
LoadVariables(obj);
RunLoadSteps(obj); RunLoadSteps(obj);
@@ -506,6 +507,7 @@ void SwitcherData::SaveSettings(obs_data_t *obj)
saveSceneGroups(obj); saveSceneGroups(obj);
SaveMacros(obj); SaveMacros(obj);
SaveGlobalMacroSettings(obj); SaveGlobalMacroSettings(obj);
SaveVariables(obj);
saveWindowTitleSwitches(obj); saveWindowTitleSwitches(obj);
saveScreenRegionSwitches(obj); saveScreenRegionSwitches(obj);
savePauseSwitches(obj); savePauseSwitches(obj);

View File

@@ -871,8 +871,6 @@ void MacroActionMacroEdit::SetWidgetVisibility()
} else { } else {
_noConditionsWarning->setVisible(false); _noConditionsWarning->setVisible(false);
} }
_runMacroHelp->setVisible(action ==
MacroActionMacro::Action::RUN_MACRO);
_nestedMacro->setVisible(action == _nestedMacro->setVisible(action ==
MacroActionMacro::Action::NESTED_MACRO); MacroActionMacro::Action::NESTED_MACRO);

View File

@@ -5,7 +5,6 @@
#include "path-helpers.hpp" #include "path-helpers.hpp"
#include "plugin-state-helpers.hpp" #include "plugin-state-helpers.hpp"
#include "section.hpp" #include "section.hpp"
#include "switch-button.hpp"
#include "ui-helpers.hpp" #include "ui-helpers.hpp"
#include "utility.hpp" #include "utility.hpp"
@@ -103,15 +102,12 @@ MacroConditionEdit::MacroConditionEdit(
QWidget *parent, std::shared_ptr<MacroCondition> *entryData, QWidget *parent, std::shared_ptr<MacroCondition> *entryData,
bool isRootCondition) bool isRootCondition)
: MacroSegmentEdit(parent), : MacroSegmentEdit(parent),
_enable(new SwitchButton()),
_logicSelection(new QComboBox()), _logicSelection(new QComboBox()),
_conditionSelection(new FilterComboBox()), _conditionSelection(new FilterComboBox()),
_dur(new DurationModifierEdit()), _dur(new DurationModifierEdit()),
_entryData(entryData), _entryData(entryData),
_isRoot(isRootCondition) _isRoot(isRootCondition)
{ {
QWidget::connect(_enable, SIGNAL(checked(bool)), this,
SLOT(ConditionEnableChanged(bool)));
QWidget::connect(_logicSelection, SIGNAL(currentIndexChanged(int)), QWidget::connect(_logicSelection, SIGNAL(currentIndexChanged(int)),
this, SLOT(LogicSelectionChanged(int))); this, SLOT(LogicSelectionChanged(int)));
QWidget::connect(_conditionSelection, QWidget::connect(_conditionSelection,
@@ -126,7 +122,6 @@ MacroConditionEdit::MacroConditionEdit(
Logic::PopulateLogicTypeSelection(_logicSelection, isRootCondition); Logic::PopulateLogicTypeSelection(_logicSelection, isRootCondition);
populateConditionSelection(_conditionSelection); populateConditionSelection(_conditionSelection);
_section->AddHeaderWidget(_enable);
_section->AddHeaderWidget(_logicSelection); _section->AddHeaderWidget(_logicSelection);
_section->AddHeaderWidget(_conditionSelection); _section->AddHeaderWidget(_conditionSelection);
_section->AddHeaderWidget(_headerInfo); _section->AddHeaderWidget(_headerInfo);
@@ -159,17 +154,8 @@ void MacroConditionEdit::LogicSelectionChanged(int idx)
const auto logic = static_cast<Logic::Type>( const auto logic = static_cast<Logic::Type>(
_logicSelection->itemData(idx).toInt()); _logicSelection->itemData(idx).toInt());
(*_entryData)->SetLogicType(logic); (*_entryData)->SetLogicType(logic);
}
void MacroConditionEdit::ConditionEnableChanged(bool value) SetEnableAppearance(logic != Logic::Type::NONE);
{
if (_loading || !_entryData) {
return;
}
auto lock = LockContext();
(*_entryData)->SetEnabled(value);
SetDisableEffect(!value);
} }
bool MacroConditionEdit::IsRootNode() const bool MacroConditionEdit::IsRootNode() const
@@ -182,9 +168,7 @@ void MacroConditionEdit::SetLogicSelection()
const auto logic = (*_entryData)->GetLogicType(); const auto logic = (*_entryData)->GetLogicType();
_logicSelection->setCurrentIndex( _logicSelection->setCurrentIndex(
_logicSelection->findData(static_cast<int>(logic))); _logicSelection->findData(static_cast<int>(logic)));
const bool enabled = (*_entryData)->Enabled(); SetEnableAppearance(logic != Logic::Type::NONE);
_enable->setChecked(enabled);
SetEnableAppearance(enabled);
} }
void MacroConditionEdit::SetRootNode(bool root) void MacroConditionEdit::SetRootNode(bool root)
@@ -255,12 +239,10 @@ void MacroConditionEdit::ConditionSelectionChanged(const QString &text)
{ {
auto lock = LockContext(); auto lock = LockContext();
auto logic = (*_entryData)->GetLogicType(); auto logic = (*_entryData)->GetLogicType();
const bool enabled = (*_entryData)->Enabled();
_entryData->reset(); _entryData->reset();
*_entryData = MacroConditionFactory::Create(id, macro); *_entryData = MacroConditionFactory::Create(id, macro);
(*_entryData)->SetIndex(idx); (*_entryData)->SetIndex(idx);
(*_entryData)->SetLogicType(logic); (*_entryData)->SetLogicType(logic);
(*_entryData)->SetEnabled(enabled);
(*_entryData)->PostLoad(); (*_entryData)->PostLoad();
RunAndClearPostLoadSteps(); RunAndClearPostLoadSteps();
} }

View File

@@ -8,8 +8,6 @@
namespace advss { namespace advss {
class SwitchButton;
class DurationModifierEdit : public QWidget { class DurationModifierEdit : public QWidget {
Q_OBJECT Q_OBJECT
public: public:
@@ -49,13 +47,11 @@ private slots:
void ConditionSelectionChanged(const QString &text); void ConditionSelectionChanged(const QString &text);
void DurationChanged(const Duration &value); void DurationChanged(const Duration &value);
void DurationModifierChanged(DurationModifier::Type m); void DurationModifierChanged(DurationModifier::Type m);
void ConditionEnableChanged(bool);
private: private:
void SetLogicSelection(); void SetLogicSelection();
std::shared_ptr<MacroSegment> Data() const; std::shared_ptr<MacroSegment> Data() const;
SwitchButton *_enable;
QComboBox *_logicSelection; QComboBox *_logicSelection;
FilterComboBox *_conditionSelection; FilterComboBox *_conditionSelection;
DurationModifierEdit *_dur; DurationModifierEdit *_dur;

View File

@@ -31,10 +31,6 @@ bool MacroCondition::Load(obs_data_t *obj)
{ {
MacroSegment::Load(obj); MacroSegment::Load(obj);
_logic.Load(obj, "logic"); _logic.Load(obj, "logic");
if (_logic.GetType() == Logic::Type::NONE) {
SetEnabled(false);
_logic.SetType(Logic::Type::AND);
}
_durationModifier.Load(obj); _durationModifier.Load(obj);
return true; return true;
} }
@@ -54,8 +50,7 @@ void MacroCondition::ValidateLogicSelection(bool isRootCondition,
return; return;
} }
_logic.SetType(Logic::Type::AND); _logic.SetType(Logic::Type::NONE);
SetEnabled(false);
blog(LOG_WARNING, blog(LOG_WARNING,
"setting invalid logic selection to 'ignore' for macro %s", "setting invalid logic selection to 'ignore' for macro %s",
context); context);

View File

@@ -80,7 +80,7 @@ bool MacroSegment::Save(obs_data_t *obj) const
obs_data_set_bool(data, "useCustomLabel", _useCustomLabel); obs_data_set_bool(data, "useCustomLabel", _useCustomLabel);
obs_data_set_string(data, "customLabel", _customLabel.c_str()); obs_data_set_string(data, "customLabel", _customLabel.c_str());
obs_data_set_bool(data, "enabled", _enabled); obs_data_set_bool(data, "enabled", _enabled);
obs_data_set_int(data, "version", 2); obs_data_set_int(data, "version", 1);
OBSDataArrayAutoRelease mappingsArray = obs_data_array_create(); OBSDataArrayAutoRelease mappingsArray = obs_data_array_create();
for (const auto &mapping : _varMappings) { for (const auto &mapping : _varMappings) {
@@ -112,12 +112,6 @@ bool MacroSegment::Load(obs_data_t *obj)
_enabled = obs_data_get_bool(obj, "enabled"); _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(); _varMappings.clear();
OBSDataArrayAutoRelease mappingsArray = OBSDataArrayAutoRelease mappingsArray =
obs_data_get_array(data, "varMappings"); obs_data_get_array(data, "varMappings");

View File

@@ -557,10 +557,6 @@ void AdvSceneSwitcher::HighlightOnChange() const
return; return;
} }
if (macro->Paused()) {
return;
}
if (macro->ActionTriggerModePreventedActionsSince( if (macro->ActionTriggerModePreventedActionsSince(
lastOnChangeHighlightCheckTime)) { lastOnChangeHighlightCheckTime)) {
HighlightWidget(ui->actionTriggerMode, Qt::yellow, HighlightWidget(ui->actionTriggerMode, Qt::yellow,

View File

@@ -17,17 +17,6 @@
namespace advss { namespace advss {
static bool setup()
{
AddPluginCleanupStep([]() {
GetTopLevelMacros().clear();
GetTemporaryMacros().clear();
});
return true;
}
static bool setupDone = setup();
Macro::Macro(const std::string &name) : _dockSettings(this) Macro::Macro(const std::string &name) : _dockSettings(this)
{ {
SetName(name); SetName(name);
@@ -157,15 +146,6 @@ bool Macro::CheckConditionHelper(
return conditionMatched; 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(); const auto logicType = condition->GetLogicType();
if (logicType == Logic::Type::NONE) { if (logicType == Logic::Type::NONE) {
vblog(LOG_INFO, "ignoring condition '%s' for '%s'", vblog(LOG_INFO, "ignoring condition '%s' for '%s'",
@@ -290,7 +270,7 @@ bool Macro::CheckConditions(bool ignorePause)
const bool hasActionsToExecute = _matched ? (_actions.size() > 0) const bool hasActionsToExecute = _matched ? (_actions.size() > 0)
: (_elseActions.size() > 0); : (_elseActions.size() > 0);
if (!_actionModeMatch && hasActionsToExecute && !_paused) { if (!_actionModeMatch && hasActionsToExecute) {
_lastActionRunModePreventTime = _lastActionRunModePreventTime =
std::chrono::high_resolution_clock::now(); std::chrono::high_resolution_clock::now();
} }
@@ -389,7 +369,7 @@ bool Macro::ShouldRunActions() const
!_paused && (_matched || _elseActions.size() > 0) && !_paused && (_matched || _elseActions.size() > 0) &&
_actionModeMatch; _actionModeMatch;
if (VerboseLoggingEnabled() && !_actionModeMatch && !_paused) { if (VerboseLoggingEnabled() && !_actionModeMatch) {
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());

View File

@@ -16,17 +16,14 @@ static void setupTab(QTabWidget *);
static ActionQueueTable *tabWidget = nullptr; static ActionQueueTable *tabWidget = nullptr;
static bool setup() void RegisterActionQueueTab()
{ {
AddPluginInitStep([] { AddPluginInitStep([]() {
AddSetupTabCallback("actionQueueTab", ActionQueueTable::Create, AddSetupTabCallback("actionQueueTab", ActionQueueTable::Create,
setupTab); setupTab);
}); });
return true;
} }
static bool setupDone = setup();
static void setTabVisible(QTabWidget *tabWidget, bool visible) static void setTabVisible(QTabWidget *tabWidget, bool visible)
{ {
SetTabVisibleByName( SetTabVisibleByName(

View File

@@ -12,16 +12,20 @@ std::deque<std::shared_ptr<Item>> &GetActionQueues()
return queues; return queues;
} }
static bool setup() void RegisterActionQueueTab();
void SetupActionQueues()
{ {
static bool done = false;
if (done) {
return;
}
AddSaveStep(SaveActionQueues); AddSaveStep(SaveActionQueues);
AddLoadStep(LoadActionQueues); AddLoadStep(LoadActionQueues);
AddPluginCleanupStep([]() { queues.clear(); }); RegisterActionQueueTab();
return true; done = true;
} }
static bool setupDone = setup();
ActionQueue::ActionQueue() : Item() ActionQueue::ActionQueue() : Item()
{ {
_lastEmpty = std::chrono::high_resolution_clock::now(); _lastEmpty = std::chrono::high_resolution_clock::now();

View File

@@ -101,6 +101,7 @@ signals:
}; };
std::deque<std::shared_ptr<Item>> &GetActionQueues(); std::deque<std::shared_ptr<Item>> &GetActionQueues();
void SetupActionQueues();
void SaveActionQueues(obs_data_t *); void SaveActionQueues(obs_data_t *);
void LoadActionQueues(obs_data_t *); void LoadActionQueues(obs_data_t *);
void ImportQueues(obs_data_t *); void ImportQueues(obs_data_t *);

View File

@@ -8,6 +8,7 @@
namespace advss { namespace advss {
const std::map<Logic::Type, const char *> Logic::localeMap = { const std::map<Logic::Type, const char *> Logic::localeMap = {
{Logic::Type::NONE, {"AdvSceneSwitcher.logic.none"}},
{Logic::Type::AND, {"AdvSceneSwitcher.logic.and"}}, {Logic::Type::AND, {"AdvSceneSwitcher.logic.and"}},
{Logic::Type::OR, {"AdvSceneSwitcher.logic.or"}}, {Logic::Type::OR, {"AdvSceneSwitcher.logic.or"}},
{Logic::Type::AND_NOT, {"AdvSceneSwitcher.logic.andNot"}}, {Logic::Type::AND_NOT, {"AdvSceneSwitcher.logic.andNot"}},
@@ -66,7 +67,7 @@ void Logic::PopulateLogicTypeSelection(QComboBox *list, bool isRootCondition)
return typeValue < rootOffset; return typeValue < rootOffset;
}} }}
: std::function<bool(int)>{[](int typeValue) { : std::function<bool(int)>{[](int typeValue) {
return typeValue > rootOffset; return typeValue >= rootOffset;
}}; }};
for (const auto &[type, name] : localeMap) { for (const auto &[type, name] : localeMap) {
const int typeValue = static_cast<int>(type); const int typeValue = static_cast<int>(type);

View File

@@ -28,8 +28,6 @@ static constexpr bool handleUncleanShutdown = true;
static bool wasCleanShutdown = false; static bool wasCleanShutdown = false;
static bool suppressCrashDialog = false; static bool suppressCrashDialog = false;
static char *sentinelFile = nullptr;
bool GetSuppressCrashDialog() bool GetSuppressCrashDialog()
{ {
return suppressCrashDialog; return suppressCrashDialog;
@@ -60,6 +58,7 @@ static void handleShutdown(enum obs_frontend_event event, void *)
return; return;
} }
char *sentinelFile = obs_module_config_path(sentinel.data());
if (!sentinelFile) { if (!sentinelFile) {
return; return;
} }
@@ -79,8 +78,7 @@ static void handleShutdown(enum obs_frontend_event event, void *)
static void setup() static void setup()
{ {
// Freed in handleShutdown() char *sentinelFile = obs_module_config_path(sentinel.data());
sentinelFile = obs_module_config_path(sentinel.data());
if (!sentinelFile) { if (!sentinelFile) {
return; return;
} }
@@ -108,6 +106,7 @@ static void setup()
file.write("running"); file.write("running");
file.close(); file.close();
bfree(sentinelFile);
obs_frontend_add_event_callback(handleShutdown, nullptr); obs_frontend_add_event_callback(handleShutdown, nullptr);
return; return;

View File

@@ -75,18 +75,6 @@ static std::vector<std::function<void()>> &getStopSteps()
return steps; return steps;
} }
static std::vector<std::function<void(obs_data_t *)>> &getEarlySaveSteps()
{
static std::vector<std::function<void(obs_data_t *)>> steps;
return steps;
}
static std::vector<std::function<void(obs_data_t *)>> &getEarlyLoadSteps()
{
static std::vector<std::function<void(obs_data_t *)>> steps;
return steps;
}
static std::vector<std::function<void(obs_data_t *)>> &getSaveSteps() static std::vector<std::function<void(obs_data_t *)>> &getSaveSteps()
{ {
static std::vector<std::function<void(obs_data_t *)>> steps; static std::vector<std::function<void(obs_data_t *)>> steps;
@@ -121,18 +109,6 @@ void LoadPluginSettings(obs_data_t *obj)
GetSwitcher()->LoadSettings(obj); GetSwitcher()->LoadSettings(obj);
} }
void AddEarlySaveStep(std::function<void(obs_data_t *)> step)
{
std::lock_guard<std::mutex> lock(mutex);
getEarlySaveSteps().emplace_back(step);
}
void AddEarlyLoadStep(std::function<void(obs_data_t *)> step)
{
std::lock_guard<std::mutex> lock(mutex);
getEarlyLoadSteps().emplace_back(step);
}
void AddSaveStep(std::function<void(obs_data_t *)> step) void AddSaveStep(std::function<void(obs_data_t *)> step)
{ {
std::lock_guard<std::mutex> lock(mutex); std::lock_guard<std::mutex> lock(mutex);
@@ -160,9 +136,6 @@ void AddIntervalResetStep(std::function<void()> step)
void RunSaveSteps(obs_data_t *obj) void RunSaveSteps(obs_data_t *obj)
{ {
std::lock_guard<std::mutex> lock(mutex); std::lock_guard<std::mutex> lock(mutex);
for (const auto &func : getEarlySaveSteps()) {
func(obj);
}
for (const auto &func : getSaveSteps()) { for (const auto &func : getSaveSteps()) {
func(obj); func(obj);
} }
@@ -171,9 +144,6 @@ void RunSaveSteps(obs_data_t *obj)
void RunLoadSteps(obs_data_t *obj) void RunLoadSteps(obs_data_t *obj)
{ {
std::lock_guard<std::mutex> lock(mutex); std::lock_guard<std::mutex> lock(mutex);
for (const auto &func : getEarlyLoadSteps()) {
func(obj);
}
for (const auto &func : getLoadSteps()) { for (const auto &func : getLoadSteps()) {
func(obj); func(obj);
} }
@@ -340,7 +310,7 @@ bool HighlightUIElementsEnabled()
bool OBSIsShuttingDown() bool OBSIsShuttingDown()
{ {
return !GetSwitcher() || GetSwitcher()->obsIsShuttingDown; return GetSwitcher() && GetSwitcher()->obsIsShuttingDown;
} }
bool InitialLoadIsComplete() bool InitialLoadIsComplete()

View File

@@ -8,8 +8,6 @@ namespace advss {
void SavePluginSettings(obs_data_t *); void SavePluginSettings(obs_data_t *);
EXPORT void LoadPluginSettings(obs_data_t *); EXPORT void LoadPluginSettings(obs_data_t *);
void AddEarlySaveStep(std::function<void(obs_data_t *)>);
void AddEarlyLoadStep(std::function<void(obs_data_t *)>);
EXPORT void AddSaveStep(std::function<void(obs_data_t *)>); EXPORT void AddSaveStep(std::function<void(obs_data_t *)>);
EXPORT void AddLoadStep(std::function<void(obs_data_t *)>); EXPORT void AddLoadStep(std::function<void(obs_data_t *)>);
EXPORT void AddPostLoadStep(std::function<void()>); EXPORT void AddPostLoadStep(std::function<void()>);

View File

@@ -63,6 +63,7 @@ static void load(obs_data_t *data)
{ {
tabSettings.Load(data, "tabSettings"); tabSettings.Load(data, "tabSettings");
dockSettings.Load(data, "dockSettings"); dockSettings.Load(data, "dockSettings");
enableDock(obs_data_get_bool(data, "addVariablesDock")); enableDock(obs_data_get_bool(data, "addVariablesDock"));
} }

View File

@@ -1,12 +1,9 @@
#include "variable.hpp" #include "variable.hpp"
#include "math-helpers.hpp" #include "math-helpers.hpp"
#include "obs-module-helper.hpp" #include "obs-module-helper.hpp"
#include "plugin-state-helpers.hpp"
#include "ui-helpers.hpp" #include "ui-helpers.hpp"
#include "utility.hpp" #include "utility.hpp"
#include <obs.hpp>
#include <QGridLayout> #include <QGridLayout>
namespace advss { namespace advss {
@@ -18,15 +15,6 @@ static std::deque<std::shared_ptr<Item>> variables;
static std::mutex lastVariableChangeMutex; static std::mutex lastVariableChangeMutex;
static std::chrono::high_resolution_clock::time_point lastVariableChange{}; static std::chrono::high_resolution_clock::time_point lastVariableChange{};
static bool setup()
{
AddEarlySaveStep(SaveVariables);
AddEarlyLoadStep(LoadVariables);
AddPluginCleanupStep([]() { variables.clear(); });
return true;
}
static bool setupDone = setup();
static void setLastVariableChangeTime() static void setLastVariableChangeTime()
{ {
std::lock_guard<std::mutex> lock(lastVariableChangeMutex); std::lock_guard<std::mutex> lock(lastVariableChangeMutex);
@@ -430,31 +418,34 @@ static bool variableWithNameExists(const std::string &name)
void SaveVariables(obs_data_t *obj) void SaveVariables(obs_data_t *obj)
{ {
OBSDataArrayAutoRelease variablesArray = obs_data_array_create(); obs_data_array_t *variablesArray = obs_data_array_create();
for (const auto &v : variables) { for (const auto &v : variables) {
OBSDataAutoRelease array_obj = obs_data_create(); obs_data_t *array_obj = obs_data_create();
v->Save(array_obj); v->Save(array_obj);
obs_data_array_push_back(variablesArray, array_obj); obs_data_array_push_back(variablesArray, array_obj);
obs_data_release(array_obj);
} }
obs_data_set_array(obj, "variables", variablesArray); obs_data_set_array(obj, "variables", variablesArray);
obs_data_array_release(variablesArray);
} }
void LoadVariables(obs_data_t *obj) void LoadVariables(obs_data_t *obj)
{ {
variables.clear(); variables.clear();
OBSDataArrayAutoRelease variablesArray = obs_data_array_t *variablesArray = obs_data_get_array(obj, "variables");
obs_data_get_array(obj, "variables");
size_t count = obs_data_array_count(variablesArray); size_t count = obs_data_array_count(variablesArray);
for (size_t i = 0; i < count; i++) { for (size_t i = 0; i < count; i++) {
OBSDataAutoRelease array_obj = obs_data_t *array_obj = obs_data_array_item(variablesArray, i);
obs_data_array_item(variablesArray, i);
auto var = Variable::Create(); auto var = Variable::Create();
variables.emplace_back(var); variables.emplace_back(var);
variables.back()->Load(array_obj); variables.back()->Load(array_obj);
obs_data_release(array_obj);
} }
obs_data_array_release(variablesArray);
} }
static void signalImportedVariables(void *varsPtr) static void signalImportedVariables(void *varsPtr)
@@ -469,15 +460,16 @@ static void signalImportedVariables(void *varsPtr)
void ImportVariables(obs_data_t *data) void ImportVariables(obs_data_t *data)
{ {
OBSDataArrayAutoRelease array = obs_data_get_array(data, "variables"); obs_data_array_t *array = obs_data_get_array(data, "variables");
size_t count = obs_data_array_count(array); size_t count = obs_data_array_count(array);
auto importedVars = new std::vector<std::shared_ptr<Item>>; auto importedVars = new std::vector<std::shared_ptr<Item>>;
for (size_t i = 0; i < count; i++) { for (size_t i = 0; i < count; i++) {
OBSDataAutoRelease arrayElement = obs_data_array_item(array, i); obs_data_t *arrayElement = obs_data_array_item(array, i);
auto var = Variable::Create(); auto var = Variable::Create();
var->Load(arrayElement); var->Load(arrayElement);
obs_data_release(arrayElement);
if (variableWithNameExists(var->Name())) { if (variableWithNameExists(var->Name())) {
continue; continue;
@@ -487,6 +479,8 @@ void ImportVariables(obs_data_t *data)
importedVars->emplace_back(var); importedVars->emplace_back(var);
} }
obs_data_array_release(array);
QueueUITask(signalImportedVariables, importedVars); QueueUITask(signalImportedVariables, importedVars);
} }

View File

@@ -1,12 +1,10 @@
#include "platform-funcs.hpp" #include "platform-funcs.hpp"
#include "plugin-state-helpers.hpp"
#include <windows.h> #include <windows.h>
#include <UIAutomation.h> #include <UIAutomation.h>
#include <util/platform.h> #include <util/platform.h>
#include <TlHelp32.h> #include <TlHelp32.h>
#include <Psapi.h> #include <Psapi.h>
#include <memory>
#include <locale> #include <locale>
#include <codecvt> #include <codecvt>
#include <string> #include <string>
@@ -18,9 +16,6 @@
#include <QWidget> #include <QWidget>
#include <mutex> #include <mutex>
#define ADVSS_WIDEN_(x) L##x
#define ADVSS_WIDEN(x) ADVSS_WIDEN_(x)
namespace advss { namespace advss {
#define MAX_SEARCH 1000 #define MAX_SEARCH 1000
@@ -505,86 +500,9 @@ int SecondsSinceLastInput()
return (getTime() - getLastInputTime()) / 1000; 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<SymGetSearchPathW_t>(
GetProcAddress(dbghelp, "SymGetSearchPathW"));
auto symSetSearchPathW = reinterpret_cast<SymSetSearchPathW_t>(
GetProcAddress(dbghelp, "SymSetSearchPathW"));
auto symRefreshModuleList = reinterpret_cast<SymRefreshModuleList_t>(
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<LPCWSTR>(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<wchar_t[]>(currentPathLen);
auto newPath = std::make_unique<wchar_t[]>(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() void PlatformInit()
{ {
CoInitialize(NULL); CoInitialize(NULL);
AddFinishedLoadingStep(addPluginFolderToSymbolPath);
} }
void PlatformCleanup() void PlatformCleanup()

View File

@@ -23,7 +23,6 @@ bool setup()
{ {
AddSaveStep(saveConnections); AddSaveStep(saveConnections);
AddLoadStep(loadConnections); AddLoadStep(loadConnections);
AddPluginCleanupStep([]() { connections.clear(); });
return true; return true;
} }

View File

@@ -718,7 +718,6 @@ static bool setup()
{ {
AddSaveStep(SaveMqttConnections); AddSaveStep(SaveMqttConnections);
AddLoadStep(LoadMqttConnections); AddLoadStep(LoadMqttConnections);
AddPluginCleanupStep([]() { GetMqttConnections().clear(); });
return true; return true;
} }

View File

@@ -2,13 +2,9 @@
#include "log-helper.hpp" #include "log-helper.hpp"
#include "obs-module-helper.hpp" #include "obs-module-helper.hpp"
#include <obs-frontend-api.h>
#include <obs-module.h> #include <obs-module.h>
#include <obs.hpp> #include <obs.hpp>
#include <mutex>
#include <unordered_set>
#include <QDir> #include <QDir>
#include <QFileInfo> #include <QFileInfo>
@@ -32,17 +28,6 @@ const std::string_view InlineScript::_defaultLUAScript =
"end"; "end";
; ;
static std::mutex instancesMutex;
static std::unordered_set<InlineScript *> instances;
static void handleScriptingShutdown(enum obs_frontend_event event, void *)
{
if (event != OBS_FRONTEND_EVENT_SCRIPTING_SHUTDOWN) {
return;
}
InlineScript::DeregisterAll();
}
static bool setup() static bool setup()
{ {
auto sh = obs_get_signal_handler(); auto sh = obs_get_signal_handler();
@@ -50,8 +35,6 @@ static bool setup()
std::string("void ") + signalName.data() + "(string id)"; std::string("void ") + signalName.data() + "(string id)";
signal_handler_add(sh, signalDecl.c_str()); signal_handler_add(sh, signalDecl.c_str());
obs_frontend_add_event_callback(handleScriptingShutdown, nullptr);
return true; return true;
} }
static bool setupDone = setup(); static bool setupDone = setup();
@@ -115,10 +98,6 @@ static bool createScriptFile(const char *settingsFile, const char *content)
InlineScript::InlineScript() : _instanceId(_instanceIdCounter++) InlineScript::InlineScript() : _instanceId(_instanceIdCounter++)
{ {
{
std::lock_guard<std::mutex> lock(instancesMutex);
instances.insert(this);
}
Setup(); Setup();
} }
@@ -128,27 +107,9 @@ InlineScript::InlineScript(const InlineScript &other)
_textLUA(other._textLUA), _textLUA(other._textLUA),
_instanceId(_instanceIdCounter++) _instanceId(_instanceIdCounter++)
{ {
{
std::lock_guard<std::mutex> lock(instancesMutex);
instances.insert(this);
}
Setup(); Setup();
} }
InlineScript::~InlineScript()
{
std::lock_guard<std::mutex> lock(instancesMutex);
instances.erase(this);
}
void InlineScript::DeregisterAll()
{
std::lock_guard<std::mutex> lock(instancesMutex);
for (auto *instance : instances) {
instance->_script.reset();
}
}
void InlineScript::Save(obs_data_t *data) const void InlineScript::Save(obs_data_t *data) const
{ {
OBSDataAutoRelease obj = obs_data_create(); OBSDataAutoRelease obj = obs_data_create();

View File

@@ -15,7 +15,6 @@ class InlineScript {
public: public:
InlineScript(); InlineScript();
InlineScript(const InlineScript &); InlineScript(const InlineScript &);
~InlineScript();
enum Type { INLINE, FILE }; enum Type { INLINE, FILE };
@@ -35,8 +34,6 @@ public:
void ResolveVariablesToFixedValues(); void ResolveVariablesToFixedValues();
static void DeregisterAll();
private: private:
void Setup(); void Setup();
void SetupFile(); void SetupFile();

View File

@@ -8,8 +8,6 @@
#include "date/tz.h" #include "date/tz.h"
#endif #endif
using namespace std::chrono_literals;
namespace advss { namespace advss {
using websocketpp::lib::placeholders::_1; using websocketpp::lib::placeholders::_1;
@@ -30,7 +28,7 @@ static constexpr std::string_view registerSubscriptionURL =
static constexpr std::string_view registerSubscriptionPath = static constexpr std::string_view registerSubscriptionPath =
"/helix/eventsub/subscriptions"; "/helix/eventsub/subscriptions";
#endif #endif
static const auto reconnectDelay = 15s; static const int reconnectDelay = 15;
#undef DispatchMessage #undef DispatchMessage
@@ -83,10 +81,6 @@ void EventSub::ConnectThread()
} else { } else {
_client->connect(con); _client->connect(con);
_connection = connection_hdl(con); _connection = connection_hdl(con);
if (_disconnect) {
_client->close(con, websocketpp::close::status::normal,
"Twitch EventSub stopping", ec);
}
_client->run(); _client->run();
} }
@@ -104,9 +98,9 @@ void EventSub::WaitAndReconnect()
auto thread = std::thread([this]() { auto thread = std::thread([this]() {
std::unique_lock<std::mutex> lock(_waitMtx); std::unique_lock<std::mutex> lock(_waitMtx);
blog(LOG_INFO, blog(LOG_INFO,
"Twitch EventSub trying to reconnect to in %lld seconds.", "Twitch EventSub trying to reconnect to in %d seconds.",
(long long)reconnectDelay.count()); reconnectDelay);
_cv.wait_for(lock, reconnectDelay); _cv.wait_for(lock, std::chrono::seconds(reconnectDelay));
_reconnecting = false; _reconnecting = false;
if (_disconnect) { if (_disconnect) {
@@ -161,6 +155,12 @@ void EventSub::Disconnect()
_cv.notify_all(); _cv.notify_all();
} }
while (_connected) {
std::this_thread::sleep_for(std::chrono::milliseconds(10));
_client->close(_connection, websocketpp::close::status::normal,
"Twitch EventSub stopping", ec);
}
if (_thread.joinable()) { if (_thread.joinable()) {
_thread.join(); _thread.join();
} }
@@ -342,7 +342,8 @@ static bool isValidTimestamp(const std::string &timestamp)
auto duration = now - parsedTime; auto duration = now - parsedTime;
// Clocks might be off by a bit, so allow negative values also // Clocks might be off by a bit, so allow negative values also
return duration <= 10min && duration >= -1min; return duration <= std::chrono::minutes(10) &&
duration >= std::chrono::minutes(-1);
} catch (const std::exception &e) { } catch (const std::exception &e) {
blog(LOG_WARNING, "%s: %s", __func__, e.what()); blog(LOG_WARNING, "%s: %s", __func__, e.what());
return false; return false;

View File

@@ -87,7 +87,6 @@ static bool setupTwitchTokenSupport()
{ {
AddSaveStep(saveConnections); AddSaveStep(saveConnections);
AddLoadStep(loadConnections); AddLoadStep(loadConnections);
AddPluginCleanupStep([]() { twitchTokens.clear(); });
return true; return true;
} }

View File

@@ -4,8 +4,6 @@ namespace advss {
void SavePluginSettings(obs_data_t *) {} void SavePluginSettings(obs_data_t *) {}
void LoadPluginSettings(obs_data_t *) {} void LoadPluginSettings(obs_data_t *) {}
void AddEarlySaveStep(std::function<void(obs_data_t *)>) {}
void AddEarlyLoadStep(std::function<void(obs_data_t *)>) {}
void AddSaveStep(std::function<void(obs_data_t *)>) {} void AddSaveStep(std::function<void(obs_data_t *)>) {}
void AddLoadStep(std::function<void(obs_data_t *)>) {} void AddLoadStep(std::function<void(obs_data_t *)>) {}
void AddPostLoadStep(std::function<void()>) {} void AddPostLoadStep(std::function<void()>) {}