diff --git a/data/locale/en-US.ini b/data/locale/en-US.ini index 53b41eb8..686e78b5 100644 --- a/data/locale/en-US.ini +++ b/data/locale/en-US.ini @@ -67,6 +67,8 @@ AdvSceneSwitcher.macroTab.edit.action="Action type:" AdvSceneSwitcher.macroTab.add="Add new macro" AdvSceneSwitcher.macroTab.name="Name:" AdvSceneSwitcher.macroTab.run="Run macro" +AdvSceneSwitcher.macroTab.runFail="Running \"%1\" failed!\nEither one of the actions failed or the macro is running already." +AdvSceneSwitcher.macroTab.runInParallel="Run macro in parallel to other macros" AdvSceneSwitcher.macroTab.defaultname="Macro %1" AdvSceneSwitcher.macroTab.exists="Macro name exists already" AdvSceneSwitcher.macroTab.copy="Create copy" diff --git a/forms/advanced-scene-switcher.ui b/forms/advanced-scene-switcher.ui index 84515f79..72619507 100644 --- a/forms/advanced-scene-switcher.ui +++ b/forms/advanced-scene-switcher.ui @@ -722,7 +722,7 @@ - + @@ -740,6 +740,13 @@ + + + + AdvSceneSwitcher.macroTab.runInParallel + + + diff --git a/src/advanced-scene-switcher.cpp b/src/advanced-scene-switcher.cpp index 18ce42c6..30fc3aa9 100644 --- a/src/advanced-scene-switcher.cpp +++ b/src/advanced-scene-switcher.cpp @@ -98,6 +98,11 @@ static void SaveSceneSwitcher(obs_data_t *save_data, bool saving, void *) obs_data_set_obj(save_data, "advanced-scene-switcher", obj); obs_data_release(obj); } else { + // Stop the scene switcher at least once to + // avoid scene duplication issues with scene collection changes + bool start = !switcher->stop; + switcher->Stop(); + switcher->m.lock(); obs_data_t *obj = obs_data_get_obj(save_data, "advanced-scene-switcher"); @@ -111,10 +116,6 @@ static void SaveSceneSwitcher(obs_data_t *save_data, bool saving, void *) obs_data_release(obj); switcher->m.unlock(); - // Stop the scene switcher at least once to - // avoid scene duplication issues with scene collection changes - bool start = !switcher->stop; - switcher->Stop(); if (start) { switcher->Start(); } @@ -410,9 +411,9 @@ void SwitcherData::Stop() { if (th && th->isRunning()) { stop = true; - cv.notify_one(); + cv.notify_all(); abortMacroWait = true; - macroWaitCv.notify_one(); + macroWaitCv.notify_all(); th->wait(); delete th; th = nullptr; diff --git a/src/headers/advanced-scene-switcher.hpp b/src/headers/advanced-scene-switcher.hpp index 54e332ad..edd5141c 100644 --- a/src/headers/advanced-scene-switcher.hpp +++ b/src/headers/advanced-scene-switcher.hpp @@ -116,6 +116,7 @@ public slots: void on_macroDown_clicked(); void on_macroName_editingFinished(); void on_runMacro_clicked(); + void on_runMacroInParallel_stateChanged(int value); void on_macros_currentRowChanged(int idx); void on_macros_itemChanged(QListWidgetItem *); void on_conditionAdd_clicked(); diff --git a/src/headers/macro.hpp b/src/headers/macro.hpp index 4b08cb25..4ac1d92d 100644 --- a/src/headers/macro.hpp +++ b/src/headers/macro.hpp @@ -2,13 +2,14 @@ #include "macro-segment.hpp" #include "duration-control.hpp" +#include #include #include #include #include +#include #include #include -#include constexpr auto macro_func = 10; constexpr auto default_priority_10 = macro_func; @@ -73,10 +74,12 @@ public: virtual ~Macro(); bool CeckMatch(); - bool PerformAction(); + bool PerformAction(bool forceParallel = false); bool Matched() { return _matched; } std::string Name() { return _name; } void SetName(const std::string &name); + void SetRunInParallel(bool parallel) { _runInParallel = parallel; } + bool RunInParallel() { return _runInParallel; } void SetPaused(bool pause = true); bool Paused() { return _paused; } int GetCount() { return _count; }; @@ -103,16 +106,23 @@ private: void ClearHotkeys(); void SetHotkeysDesc(); void ResetTimers(); + void RunActions(bool &ret); + void RunActions(); std::string _name = ""; std::deque> _conditions; std::deque> _actions; + bool _runInParallel = false; bool _matched = false; bool _paused = false; int _count = 0; obs_hotkey_id _pauseHotkey = OBS_INVALID_HOTKEY_ID; obs_hotkey_id _unpauseHotkey = OBS_INVALID_HOTKEY_ID; obs_hotkey_id _togglePauseHotkey = OBS_INVALID_HOTKEY_ID; + + bool _stop = false; + bool _done = true; + std::thread _thread; }; Macro *GetMacroByName(const char *name); diff --git a/src/macro-action-edit.cpp b/src/macro-action-edit.cpp index 23edee2e..3d67cf16 100644 --- a/src/macro-action-edit.cpp +++ b/src/macro-action-edit.cpp @@ -184,7 +184,7 @@ void AdvSceneSwitcher::RemoveMacroAction(int idx) std::lock_guard lock(switcher->m); macro->Actions().erase(macro->Actions().begin() + idx); switcher->abortMacroWait = true; - switcher->macroWaitCv.notify_one(); + switcher->macroWaitCv.notify_all(); macro->UpdateActionIndices(); clearLayout(ui->macroEditActionLayout, idx); diff --git a/src/macro-tab.cpp b/src/macro-tab.cpp index 6ef2b333..79a3e37f 100644 --- a/src/macro-tab.cpp +++ b/src/macro-tab.cpp @@ -82,7 +82,7 @@ void AdvSceneSwitcher::on_macroRemove_clicked() { std::lock_guard lock(switcher->m); switcher->abortMacroWait = true; - switcher->macroWaitCv.notify_one(); + switcher->macroWaitCv.notify_all(); int idx = ui->macros->currentRow(); QString::fromStdString(switcher->macros[idx]->Name()); switcher->macros.erase(switcher->macros.begin() + idx); @@ -180,7 +180,22 @@ void AdvSceneSwitcher::on_runMacro_clicked() return; } - macro->PerformAction(); + bool ret = macro->PerformAction(true); + if (!ret) { + QString err = + obs_module_text("AdvSceneSwitcher.macroTab.runFail"); + DisplayMessage(err.arg(QString::fromStdString(macro->Name()))); + } +} + +void AdvSceneSwitcher::on_runMacroInParallel_stateChanged(int value) +{ + Macro *macro = getSelectedMacro(); + if (!macro) { + return; + } + std::lock_guard lock(switcher->m); + macro->SetRunInParallel(value); } void AdvSceneSwitcher::PopulateMacroActions(Macro &m, uint32_t afterIdx) @@ -215,6 +230,7 @@ void AdvSceneSwitcher::PopulateMacroConditions(Macro &m, uint32_t afterIdx) void AdvSceneSwitcher::SetEditMacro(Macro &m) { ui->macroName->setText(m.Name().c_str()); + ui->runMacroInParallel->setChecked(m.RunInParallel()); clearLayout(ui->macroEditConditionLayout); clearLayout(ui->macroEditActionLayout); diff --git a/src/macro.cpp b/src/macro.cpp index f83f00aa..8818b25d 100644 --- a/src/macro.cpp +++ b/src/macro.cpp @@ -29,6 +29,10 @@ Macro::Macro(const std::string &name) Macro::~Macro() { + _stop = true; + if (_thread.joinable()) { + _thread.join(); + } ClearHotkeys(); } @@ -104,15 +108,22 @@ bool Macro::CeckMatch() return _matched; } -bool Macro::PerformAction() +bool Macro::PerformAction(bool forceParallel) { + if (!_done) { + vblog(LOG_INFO, "macro %s already running", _name.c_str()); + return false; + } + bool ret = true; - for (auto &a : _actions) { - a->LogAction(); - ret = ret && a->PerformAction(); - if (!ret) { - return false; + _done = false; + if (_runInParallel || forceParallel) { + if (_thread.joinable()) { + _thread.join(); } + _thread = std::thread([this] { RunActions(); }); + } else { + RunActions(ret); } return ret; } @@ -130,6 +141,27 @@ void Macro::ResetTimers() } } +void Macro::RunActions(bool &retVal) +{ + bool ret = true; + for (auto &a : _actions) { + a->LogAction(); + ret = ret && a->PerformAction(); + if (!ret || _stop) { + retVal = ret; + _done = true; + return; + } + } + _done = true; +} + +void Macro::RunActions() +{ + bool unused; + RunActions(unused); +} + void Macro::SetPaused(bool pause) { if (_paused && !pause) { @@ -160,6 +192,7 @@ bool Macro::Save(obs_data_t *obj) { 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_array_t *pauseHotkey = obs_hotkey_save(_pauseHotkey); obs_data_set_array(obj, "pauseHotkey", pauseHotkey); @@ -237,6 +270,7 @@ 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"); obs_data_array_t *pauseHotkey = obs_data_get_array(obj, "pauseHotkey"); obs_hotkey_load(_pauseHotkey, pauseHotkey); @@ -541,7 +575,6 @@ bool SwitcherData::runMacros() if (!m->PerformAction()) { blog(LOG_WARNING, "abort macro: %s", m->Name().c_str()); - return false; } } }