mirror of
https://github.com/WarmUpTill/SceneSwitcher.git
synced 2026-03-21 17:34:57 -05:00
Add action queues
This is intended to be used to execute long running actions which shall not be run in parallel to one another. Multiple different action queues can be created. The "Queue" action and condition can be used to modify and check the status and size of action queues.
This commit is contained in:
parent
e37e35e318
commit
cde4e24cc4
|
|
@ -87,7 +87,9 @@ target_sources(
|
|||
# Maro sources
|
||||
target_sources(
|
||||
${LIB_NAME}
|
||||
PRIVATE src/macro-core/macro-action-audio.cpp
|
||||
PRIVATE src/macro-core/action-queue.cpp
|
||||
src/macro-core/action-queue.hpp
|
||||
src/macro-core/macro-action-audio.cpp
|
||||
src/macro-core/macro-action-audio.hpp
|
||||
src/macro-core/macro-action-clipboard.cpp
|
||||
src/macro-core/macro-action-clipboard.hpp
|
||||
|
|
@ -115,6 +117,8 @@ target_sources(
|
|||
src/macro-core/macro-action-profile.hpp
|
||||
src/macro-core/macro-action-projector.cpp
|
||||
src/macro-core/macro-action-projector.hpp
|
||||
src/macro-core/macro-action-queue.cpp
|
||||
src/macro-core/macro-action-queue.hpp
|
||||
src/macro-core/macro-action-random.cpp
|
||||
src/macro-core/macro-action-random.hpp
|
||||
src/macro-core/macro-action-recording.cpp
|
||||
|
|
@ -193,6 +197,8 @@ target_sources(
|
|||
src/macro-core/macro-condition-process.hpp
|
||||
src/macro-core/macro-condition-profile.cpp
|
||||
src/macro-core/macro-condition-profile.hpp
|
||||
src/macro-core/macro-condition-queue.cpp
|
||||
src/macro-core/macro-condition-queue.hpp
|
||||
src/macro-core/macro-condition-recording.cpp
|
||||
src/macro-core/macro-condition-recording.hpp
|
||||
src/macro-core/macro-condition-replay-buffer.cpp
|
||||
|
|
|
|||
|
|
@ -557,6 +557,12 @@ AdvSceneSwitcher.condition.twitch.entry="Channel{{channel}}{{conditions}}{{point
|
|||
AdvSceneSwitcher.condition.twitch.entry.account="Check using account{{account}}"
|
||||
AdvSceneSwitcher.condition.twitch.tokenPermissionsInsufficient="Permissions of selected token are insufficient to perform selected action!"
|
||||
AdvSceneSwitcher.condition.twitch.title.title="Enter title"
|
||||
AdvSceneSwitcher.condition.queue="Queue"
|
||||
AdvSceneSwitcher.condition.queue.type.started="Started"
|
||||
AdvSceneSwitcher.condition.queue.type.stopped="Stopped"
|
||||
AdvSceneSwitcher.condition.queue.type.size="Size"
|
||||
AdvSceneSwitcher.condition.queue.entry.startStop="Queue{{queues}}is{{conditions}}"
|
||||
AdvSceneSwitcher.condition.queue.entry.size="Queue{{queues}}{{conditions}}is less than{{size}}"
|
||||
|
||||
; Macro Actions
|
||||
AdvSceneSwitcher.action.scene="Switch scene"
|
||||
|
|
@ -897,6 +903,13 @@ AdvSceneSwitcher.action.clipboard.type.copy.image="Copy image"
|
|||
AdvSceneSwitcher.action.clipboard.copy.text.text.placeholder="Enter text"
|
||||
AdvSceneSwitcher.action.clipboard.copy.image.url.placeholder="Enter direct image URL"
|
||||
AdvSceneSwitcher.action.clipboard.copy.image.url.tooltip="Currently supported formats: PNG, JPG/JPEG, BMP, GIF."
|
||||
AdvSceneSwitcher.action.queue="Queue"
|
||||
AdvSceneSwitcher.action.queue.type.add="Add"
|
||||
AdvSceneSwitcher.action.queue.type.clear="Clear"
|
||||
AdvSceneSwitcher.action.queue.type.start="Start"
|
||||
AdvSceneSwitcher.action.queue.type.stop="Stop"
|
||||
AdvSceneSwitcher.action.queue.entry.add="{{actions}}actions of macro{{macros}}to queue{{queues}}"
|
||||
AdvSceneSwitcher.action.queue.entry.other="{{actions}}queue{{queues}}"
|
||||
|
||||
; Hotkey
|
||||
AdvSceneSwitcher.hotkey.startSwitcherHotkey="Start the Advanced Scene Switcher"
|
||||
|
|
@ -962,6 +975,20 @@ AdvSceneSwitcher.connection.status.connecting="Connecting"
|
|||
AdvSceneSwitcher.connection.status.connected="Connected, but not authenticated"
|
||||
AdvSceneSwitcher.connection.status.authenticated="Connected and authenticated"
|
||||
|
||||
AdvSceneSwitcher.actionQueues.select="--select queue--"
|
||||
AdvSceneSwitcher.actionQueues.add="Add new action queue"
|
||||
AdvSceneSwitcher.actionQueues.nameNotAvailable="Action queue name already in use"
|
||||
AdvSceneSwitcher.actionQueues.configure="Configure action queue settings"
|
||||
AdvSceneSwitcher.actionQueues.invalid="Invalid action queue selection"
|
||||
AdvSceneSwitcher.actionQueues.name="Name:"
|
||||
AdvSceneSwitcher.actionQueues.runOnStartup="Run action queue when starting the plugin"
|
||||
AdvSceneSwitcher.actionQueues.running="Queue is running"
|
||||
AdvSceneSwitcher.actionQueues.stopped="Queue is stopped"
|
||||
AdvSceneSwitcher.actionQueues.start="Start action queue"
|
||||
AdvSceneSwitcher.actionQueues.stop="Stop action queue"
|
||||
AdvSceneSwitcher.actionQueues.size="Current size: %1"
|
||||
AdvSceneSwitcher.actionQueues.clear="Clear action queue"
|
||||
|
||||
AdvSceneSwitcher.regex.enable="Enable regular expressions"
|
||||
AdvSceneSwitcher.regex.configure="Configure regular expression settings"
|
||||
AdvSceneSwitcher.regex.partialMatch="Allow partial match"
|
||||
|
|
|
|||
|
|
@ -434,11 +434,14 @@ static void ResetMacros()
|
|||
}
|
||||
}
|
||||
|
||||
void AutoStartActionQueues();
|
||||
|
||||
void SwitcherData::Start()
|
||||
{
|
||||
if (!(th && th->isRunning())) {
|
||||
ResetForNextInterval();
|
||||
ResetMacros();
|
||||
AutoStartActionQueues();
|
||||
|
||||
stop = false;
|
||||
th = new SwitcherThread();
|
||||
|
|
@ -465,6 +468,7 @@ void SwitcherData::Start()
|
|||
}
|
||||
|
||||
bool CloseAllInputDialogs();
|
||||
void StopAndClearAllActionQueues();
|
||||
|
||||
void SwitcherData::Stop()
|
||||
{
|
||||
|
|
@ -474,6 +478,7 @@ void SwitcherData::Stop()
|
|||
SetMacroAbortWait(true);
|
||||
GetMacroWaitCV().notify_all();
|
||||
GetMacroTransitionCV().notify_all();
|
||||
StopAndClearAllActionQueues();
|
||||
|
||||
// Not waiting if a dialog was closed is a workaround to avoid
|
||||
// deadlocks when a variable input dialog is opened while Stop()
|
||||
|
|
@ -750,6 +755,7 @@ QWidget *GetSettingsWindow()
|
|||
|
||||
void SetupConnectionManager();
|
||||
void SetupWebsocketHelpers();
|
||||
void SetupActionQueues();
|
||||
|
||||
extern "C" void InitSceneSwitcher(obs_module_t *module, translateFunc translate)
|
||||
{
|
||||
|
|
@ -763,6 +769,7 @@ extern "C" void InitSceneSwitcher(obs_module_t *module, translateFunc translate)
|
|||
SetupDock();
|
||||
SetupConnectionManager();
|
||||
SetupWebsocketHelpers();
|
||||
SetupActionQueues();
|
||||
|
||||
obs_frontend_add_save_callback(SaveSceneSwitcher, nullptr);
|
||||
obs_frontend_add_event_callback(OBSEvent, switcher);
|
||||
|
|
|
|||
380
src/macro-core/action-queue.cpp
Normal file
380
src/macro-core/action-queue.cpp
Normal file
|
|
@ -0,0 +1,380 @@
|
|||
#include "action-queue.hpp"
|
||||
#include "plugin-state-helpers.hpp"
|
||||
|
||||
namespace advss {
|
||||
|
||||
static std::deque<std::shared_ptr<Item>> queues;
|
||||
|
||||
void SetupActionQueues()
|
||||
{
|
||||
static bool done = false;
|
||||
if (done) {
|
||||
return;
|
||||
}
|
||||
AddSaveStep(SaveActionQueues);
|
||||
AddLoadStep(LoadActionQueues);
|
||||
done = true;
|
||||
}
|
||||
|
||||
ActionQueue::ActionQueue() : Item() {}
|
||||
|
||||
ActionQueue::~ActionQueue()
|
||||
{
|
||||
Stop();
|
||||
}
|
||||
|
||||
std::shared_ptr<Item> ActionQueue::Create()
|
||||
{
|
||||
return std::make_shared<ActionQueue>();
|
||||
}
|
||||
|
||||
void ActionQueue::Save(obs_data_t *obj) const
|
||||
{
|
||||
obs_data_set_string(obj, "name", _name.c_str());
|
||||
obs_data_set_bool(obj, "runOnStartup", _runOnStartup);
|
||||
}
|
||||
|
||||
void ActionQueue::Load(obs_data_t *obj)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(_mutex);
|
||||
_name = obs_data_get_string(obj, "name");
|
||||
_runOnStartup = obs_data_get_bool(obj, "runOnStartup");
|
||||
|
||||
if (_runOnStartup) {
|
||||
Start();
|
||||
}
|
||||
}
|
||||
|
||||
void ActionQueue::Start()
|
||||
{
|
||||
if (_thread.joinable()) {
|
||||
return;
|
||||
}
|
||||
_stop = false;
|
||||
_thread = std::thread(&ActionQueue::RunActions, this);
|
||||
}
|
||||
|
||||
void ActionQueue::Stop()
|
||||
{
|
||||
_stop = true;
|
||||
_cv.notify_all();
|
||||
if (_thread.joinable()) {
|
||||
_thread.join();
|
||||
}
|
||||
}
|
||||
|
||||
bool ActionQueue::IsRunning() const
|
||||
{
|
||||
return _thread.joinable();
|
||||
}
|
||||
|
||||
bool ActionQueue::RunsOnStartup() const
|
||||
{
|
||||
return _runOnStartup;
|
||||
}
|
||||
|
||||
void ActionQueue::Clear()
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(_mutex);
|
||||
_actions.clear();
|
||||
}
|
||||
|
||||
void ActionQueue::Add(const std::shared_ptr<MacroAction> &actions)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(_mutex);
|
||||
_actions.emplace_back(actions);
|
||||
_cv.notify_all();
|
||||
}
|
||||
|
||||
bool ActionQueue::IsEmpty()
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(_mutex);
|
||||
return _actions.empty();
|
||||
}
|
||||
|
||||
size_t ActionQueue::Size()
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(_mutex);
|
||||
return _actions.size();
|
||||
}
|
||||
|
||||
void ActionQueue::RunActions()
|
||||
{
|
||||
std::shared_ptr<MacroAction> action;
|
||||
while (true) {
|
||||
{ // Grab next action to run
|
||||
std::unique_lock<std::mutex> lock(_mutex);
|
||||
while (_actions.empty() && !_stop) {
|
||||
_cv.wait(lock);
|
||||
}
|
||||
|
||||
if (_stop) {
|
||||
return;
|
||||
}
|
||||
action = _actions.front();
|
||||
_actions.pop_front();
|
||||
}
|
||||
|
||||
if (!action) {
|
||||
continue;
|
||||
}
|
||||
|
||||
vblog(LOG_INFO, "Performing action '%s' in queue '%s'",
|
||||
action->GetId().c_str(), _name.c_str());
|
||||
action->PerformAction();
|
||||
}
|
||||
}
|
||||
|
||||
ActionQueueSettingsDialog::ActionQueueSettingsDialog(QWidget *parent,
|
||||
ActionQueue &settings)
|
||||
: ItemSettingsDialog(settings, queues,
|
||||
"AdvSceneSwitcher.actionQueues.select",
|
||||
"AdvSceneSwitcher.actionQueues.add",
|
||||
"AdvSceneSwitcher.actionQueues.nameNotAvailable",
|
||||
parent),
|
||||
_queueRunStatus(new QLabel()),
|
||||
_startStopToggle(new QPushButton()),
|
||||
_queueSize(new QLabel()),
|
||||
_clear(new QPushButton(
|
||||
obs_module_text("AdvSceneSwitcher.actionQueues.clear"))),
|
||||
_runOnStartup(new QCheckBox()),
|
||||
_queue(settings)
|
||||
{
|
||||
QWidget::connect(_startStopToggle, SIGNAL(clicked()), this,
|
||||
SLOT(StartStopClicked()));
|
||||
QWidget::connect(_clear, SIGNAL(clicked()), this, SLOT(ClearClicked()));
|
||||
|
||||
_runOnStartup->setChecked(settings._runOnStartup);
|
||||
UpdateLabels();
|
||||
|
||||
auto layout = new QGridLayout();
|
||||
int row = 0;
|
||||
layout->addWidget(new QLabel(obs_module_text(
|
||||
"AdvSceneSwitcher.actionQueues.name")),
|
||||
row, 0);
|
||||
auto nameLayout = new QHBoxLayout();
|
||||
nameLayout->setContentsMargins(0, 0, 0, 0);
|
||||
nameLayout->addWidget(_name);
|
||||
nameLayout->addWidget(_nameHint);
|
||||
layout->addLayout(nameLayout, row, 1);
|
||||
++row;
|
||||
layout->addWidget(
|
||||
new QLabel(obs_module_text(
|
||||
"AdvSceneSwitcher.actionQueues.runOnStartup")),
|
||||
row, 0);
|
||||
layout->addWidget(_runOnStartup, row, 1);
|
||||
_runOnStartup->setToolTip(
|
||||
obs_module_text("AdvSceneSwitcher.actionQueues.runOnStartup"));
|
||||
++row;
|
||||
layout->addWidget(_queueRunStatus, row, 0);
|
||||
layout->addWidget(_startStopToggle, row, 1);
|
||||
++row;
|
||||
layout->addWidget(_queueSize, row, 0);
|
||||
layout->addWidget(_clear, row, 1);
|
||||
++row;
|
||||
layout->addWidget(_buttonbox, row, 0, 1, -1);
|
||||
layout->setSizeConstraint(QLayout::SetFixedSize);
|
||||
setLayout(layout);
|
||||
|
||||
auto timer = new QTimer(this);
|
||||
QWidget::connect(timer, &QTimer::timeout, this,
|
||||
&ActionQueueSettingsDialog::UpdateLabels);
|
||||
timer->start(300);
|
||||
}
|
||||
|
||||
bool ActionQueueSettingsDialog::AskForSettings(QWidget *parent,
|
||||
ActionQueue &settings)
|
||||
{
|
||||
ActionQueueSettingsDialog dialog(parent, settings);
|
||||
dialog.setWindowTitle(obs_module_text("AdvSceneSwitcher.windowTitle"));
|
||||
if (dialog.exec() != DialogCode::Accepted) {
|
||||
return false;
|
||||
}
|
||||
|
||||
settings._name = dialog._name->text().toStdString();
|
||||
settings._runOnStartup = dialog._runOnStartup->isChecked();
|
||||
return true;
|
||||
}
|
||||
|
||||
void ActionQueueSettingsDialog::StartStopClicked()
|
||||
{
|
||||
if (_queue.IsRunning()) {
|
||||
_queue.Stop();
|
||||
} else {
|
||||
_queue.Start();
|
||||
}
|
||||
UpdateLabels();
|
||||
}
|
||||
|
||||
void ActionQueueSettingsDialog::ClearClicked()
|
||||
{
|
||||
_queue.Clear();
|
||||
}
|
||||
|
||||
void ActionQueueSettingsDialog::UpdateLabels()
|
||||
{
|
||||
_queueRunStatus->setText(obs_module_text(
|
||||
_queue.IsRunning() ? "AdvSceneSwitcher.actionQueues.running"
|
||||
: "AdvSceneSwitcher.actionQueues.stopped"));
|
||||
_startStopToggle->setText(
|
||||
_queue.IsRunning()
|
||||
? obs_module_text("AdvSceneSwitcher.actionQueues.stop")
|
||||
: obs_module_text(
|
||||
"AdvSceneSwitcher.actionQueues.start"));
|
||||
_queueSize->setText(
|
||||
QString(obs_module_text("AdvSceneSwitcher.actionQueues.size"))
|
||||
.arg(QString::number(_queue.Size())));
|
||||
}
|
||||
|
||||
static bool AskForSettingsWrapper(QWidget *parent, Item &settings)
|
||||
{
|
||||
ActionQueue &queue = dynamic_cast<ActionQueue &>(settings);
|
||||
if (ActionQueueSettingsDialog::AskForSettings(parent, queue)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void ActionQueueSelection::SetActionQueue(
|
||||
const std::weak_ptr<ActionQueue> &queue_)
|
||||
{
|
||||
const QSignalBlocker blocker(_selection);
|
||||
auto queue = queue_.lock();
|
||||
if (queue) {
|
||||
_selection->setCurrentText(
|
||||
QString::fromStdString(queue->Name()));
|
||||
} else {
|
||||
_selection->setCurrentIndex(-1);
|
||||
}
|
||||
}
|
||||
|
||||
ActionQueueSelection::ActionQueueSelection(QWidget *parent)
|
||||
: ItemSelection(queues, ActionQueue::Create, AskForSettingsWrapper,
|
||||
"AdvSceneSwitcher.actionQueues.select",
|
||||
"AdvSceneSwitcher.actionQueues.add",
|
||||
"AdvSceneSwitcher.item.nameNotAvailable",
|
||||
"AdvSceneSwitcher.actionQueues.configure", parent)
|
||||
{
|
||||
// Connect to slots
|
||||
QWidget::connect(ActionQueueSignalManager::Instance(),
|
||||
SIGNAL(Rename(const QString &, const QString &)), this,
|
||||
SLOT(RenameItem(const QString &, const QString &)));
|
||||
QWidget::connect(ActionQueueSignalManager::Instance(),
|
||||
SIGNAL(Add(const QString &)), this,
|
||||
SLOT(AddItem(const QString &)));
|
||||
QWidget::connect(ActionQueueSignalManager::Instance(),
|
||||
SIGNAL(Remove(const QString &)), this,
|
||||
SLOT(RemoveItem(const QString &)));
|
||||
|
||||
// Forward signals
|
||||
QWidget::connect(this,
|
||||
SIGNAL(ItemRenamed(const QString &, const QString &)),
|
||||
ActionQueueSignalManager::Instance(),
|
||||
SIGNAL(Rename(const QString &, const QString &)));
|
||||
QWidget::connect(this, SIGNAL(ItemAdded(const QString &)),
|
||||
ActionQueueSignalManager::Instance(),
|
||||
SIGNAL(Add(const QString &)));
|
||||
QWidget::connect(this, SIGNAL(ItemRemoved(const QString &)),
|
||||
ActionQueueSignalManager::Instance(),
|
||||
SIGNAL(Remove(const QString &)));
|
||||
}
|
||||
|
||||
ActionQueueSignalManager::ActionQueueSignalManager(QObject *parent)
|
||||
: QObject(parent)
|
||||
{
|
||||
QWidget::connect(this, SIGNAL(Add(const QString &)), this,
|
||||
SLOT(StartNewQueue(const QString &)));
|
||||
}
|
||||
|
||||
ActionQueueSignalManager *ActionQueueSignalManager::Instance()
|
||||
{
|
||||
static ActionQueueSignalManager manager;
|
||||
return &manager;
|
||||
}
|
||||
|
||||
void ActionQueueSignalManager::StartNewQueue(const QString &name)
|
||||
{
|
||||
auto weakQueue = GetWeakActionQueueByQString(name);
|
||||
auto queue = weakQueue.lock();
|
||||
if (!queue) {
|
||||
return;
|
||||
}
|
||||
if (queue->RunsOnStartup()) {
|
||||
queue->Start();
|
||||
}
|
||||
}
|
||||
|
||||
void SaveActionQueues(obs_data_t *obj)
|
||||
{
|
||||
OBSDataArrayAutoRelease array = obs_data_array_create();
|
||||
for (const auto &queue : queues) {
|
||||
OBSDataAutoRelease obj = obs_data_create();
|
||||
queue->Save(obj);
|
||||
obs_data_array_push_back(array, obj);
|
||||
}
|
||||
obs_data_set_array(obj, "actionQueues", array);
|
||||
}
|
||||
|
||||
void LoadActionQueues(obs_data_t *obj)
|
||||
{
|
||||
queues.clear();
|
||||
|
||||
OBSDataArrayAutoRelease array = obs_data_get_array(obj, "actionQueues");
|
||||
size_t count = obs_data_array_count(array);
|
||||
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
OBSDataAutoRelease obj = obs_data_array_item(array, i);
|
||||
auto queue = ActionQueue::Create();
|
||||
queues.emplace_back(queue);
|
||||
queues.back()->Load(obj);
|
||||
}
|
||||
}
|
||||
|
||||
std::weak_ptr<ActionQueue> GetWeakActionQueueByName(const std::string &name)
|
||||
{
|
||||
for (const auto &queue : queues) {
|
||||
if (queue->Name() == name) {
|
||||
std::weak_ptr<ActionQueue> wp =
|
||||
std::dynamic_pointer_cast<ActionQueue>(queue);
|
||||
return wp;
|
||||
}
|
||||
}
|
||||
return std::weak_ptr<ActionQueue>();
|
||||
}
|
||||
|
||||
std::weak_ptr<ActionQueue> GetWeakActionQueueByQString(const QString &name)
|
||||
{
|
||||
return GetWeakActionQueueByName(name.toStdString());
|
||||
}
|
||||
|
||||
std::string GetActionQueueName(const std::weak_ptr<ActionQueue> &queue_)
|
||||
{
|
||||
auto queue = queue_.lock();
|
||||
if (!queue) {
|
||||
return obs_module_text("AdvSceneSwitcher.actionQueues.invalid");
|
||||
}
|
||||
return queue->Name();
|
||||
}
|
||||
|
||||
void AutoStartActionQueues()
|
||||
{
|
||||
for (auto &queue : queues) {
|
||||
auto actionQueue =
|
||||
std::dynamic_pointer_cast<ActionQueue>(queue);
|
||||
if (actionQueue->RunsOnStartup()) {
|
||||
actionQueue->Start();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void StopAndClearAllActionQueues()
|
||||
{
|
||||
for (auto &queue : queues) {
|
||||
auto actionQueue =
|
||||
std::dynamic_pointer_cast<ActionQueue>(queue);
|
||||
actionQueue->Stop();
|
||||
actionQueue->Clear();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace advss
|
||||
105
src/macro-core/action-queue.hpp
Normal file
105
src/macro-core/action-queue.hpp
Normal file
|
|
@ -0,0 +1,105 @@
|
|||
#pragma once
|
||||
#include "item-selection-helpers.hpp"
|
||||
#include "macro-action.hpp"
|
||||
|
||||
#include <deque>
|
||||
#include <obs-data.h>
|
||||
#include <QCheckBox>
|
||||
#include <thread>
|
||||
#include <condition_variable>
|
||||
|
||||
namespace advss {
|
||||
|
||||
class ActionQueueSelection;
|
||||
class ActionQueueSettingsDialog;
|
||||
|
||||
class ActionQueue : public Item {
|
||||
public:
|
||||
ActionQueue();
|
||||
~ActionQueue();
|
||||
|
||||
static std::shared_ptr<Item> Create();
|
||||
|
||||
void Save(obs_data_t *obj) const;
|
||||
void Load(obs_data_t *obj);
|
||||
|
||||
void Start();
|
||||
void Stop();
|
||||
bool IsRunning() const;
|
||||
bool RunsOnStartup() const;
|
||||
|
||||
void Clear();
|
||||
bool IsEmpty();
|
||||
|
||||
void Add(const std::shared_ptr<MacroAction> &);
|
||||
size_t Size();
|
||||
|
||||
private:
|
||||
void RunActions();
|
||||
|
||||
bool _runOnStartup = true;
|
||||
std::atomic_bool _stop = {false};
|
||||
std::mutex _mutex;
|
||||
std::condition_variable _cv;
|
||||
std::thread _thread;
|
||||
std::deque<std::shared_ptr<MacroAction>> _actions;
|
||||
|
||||
friend ActionQueueSelection;
|
||||
friend ActionQueueSettingsDialog;
|
||||
};
|
||||
|
||||
class ActionQueueSettingsDialog : public ItemSettingsDialog {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
ActionQueueSettingsDialog(QWidget *parent, ActionQueue &);
|
||||
static bool AskForSettings(QWidget *parent, ActionQueue &settings);
|
||||
|
||||
private slots:
|
||||
void StartStopClicked();
|
||||
void ClearClicked();
|
||||
void UpdateLabels();
|
||||
|
||||
private:
|
||||
QLabel *_queueRunStatus;
|
||||
QPushButton *_startStopToggle;
|
||||
QLabel *_queueSize;
|
||||
QPushButton *_clear;
|
||||
QCheckBox *_runOnStartup;
|
||||
|
||||
ActionQueue &_queue;
|
||||
};
|
||||
|
||||
class ActionQueueSelection : public ItemSelection {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
ActionQueueSelection(QWidget *parent = 0);
|
||||
void SetActionQueue(const std::weak_ptr<ActionQueue> &);
|
||||
};
|
||||
|
||||
class ActionQueueSignalManager : public QObject {
|
||||
Q_OBJECT
|
||||
public:
|
||||
ActionQueueSignalManager(QObject *parent = nullptr);
|
||||
static ActionQueueSignalManager *Instance();
|
||||
|
||||
private slots:
|
||||
void StartNewQueue(const QString &);
|
||||
|
||||
signals:
|
||||
void Rename(const QString &, const QString &);
|
||||
void Add(const QString &);
|
||||
void Remove(const QString &);
|
||||
};
|
||||
|
||||
void SetupActionQueues();
|
||||
void SaveActionQueues(obs_data_t *);
|
||||
void LoadActionQueues(obs_data_t *);
|
||||
std::weak_ptr<ActionQueue> GetWeakActionQueueByName(const std::string &name);
|
||||
std::weak_ptr<ActionQueue> GetWeakActionQueueByQString(const QString &name);
|
||||
std::string GetActionQueueName(const std::weak_ptr<ActionQueue> &);
|
||||
void AutoStartActionQueues();
|
||||
void StopAndClearAllActionQueues();
|
||||
|
||||
} // namespace advss
|
||||
218
src/macro-core/macro-action-queue.cpp
Normal file
218
src/macro-core/macro-action-queue.cpp
Normal file
|
|
@ -0,0 +1,218 @@
|
|||
#include "macro-action-queue.hpp"
|
||||
#include "macro-helpers.hpp"
|
||||
#include "utility.hpp"
|
||||
|
||||
namespace advss {
|
||||
|
||||
const std::string MacroActionQueue::id = "queue";
|
||||
|
||||
bool MacroActionQueue::_registered = MacroActionFactory::Register(
|
||||
MacroActionQueue::id,
|
||||
{MacroActionQueue::Create, MacroActionQueueEdit::Create,
|
||||
"AdvSceneSwitcher.action.queue"});
|
||||
|
||||
const static std::map<MacroActionQueue::Action, std::string> actionTypes = {
|
||||
{MacroActionQueue::Action::ADD_TO_QUEUE,
|
||||
"AdvSceneSwitcher.action.queue.type.add"},
|
||||
{MacroActionQueue::Action::START_QUEUE,
|
||||
"AdvSceneSwitcher.action.queue.type.start"},
|
||||
{MacroActionQueue::Action::STOP_QUEUE,
|
||||
"AdvSceneSwitcher.action.queue.type.stop"},
|
||||
{MacroActionQueue::Action::CLEAR_QUEUE,
|
||||
"AdvSceneSwitcher.action.queue.type.clear"},
|
||||
};
|
||||
|
||||
void MacroActionQueue::AddActions(ActionQueue *queue)
|
||||
{
|
||||
auto macro = _macro.GetMacro();
|
||||
if (!macro) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto actions = *GetMacroActions(macro.get());
|
||||
for (const auto &action : actions) {
|
||||
queue->Add(action);
|
||||
}
|
||||
}
|
||||
|
||||
bool MacroActionQueue::PerformAction()
|
||||
{
|
||||
auto queue = _queue.lock();
|
||||
if (!queue) {
|
||||
return true;
|
||||
}
|
||||
|
||||
switch (_action) {
|
||||
case Action::ADD_TO_QUEUE:
|
||||
AddActions(queue.get());
|
||||
break;
|
||||
case Action::CLEAR_QUEUE:
|
||||
queue->Clear();
|
||||
break;
|
||||
case Action::START_QUEUE:
|
||||
queue->Start();
|
||||
break;
|
||||
case Action::STOP_QUEUE:
|
||||
queue->Stop();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void MacroActionQueue::LogAction() const
|
||||
{
|
||||
auto macro = _macro.GetMacro();
|
||||
if (!macro) {
|
||||
return;
|
||||
}
|
||||
switch (_action) {
|
||||
case Action::ADD_TO_QUEUE:
|
||||
vblog(LOG_INFO, "queued actions of \"%s\" to \"%s\"",
|
||||
GetMacroName(macro.get()).c_str(),
|
||||
GetActionQueueName(_queue).c_str());
|
||||
break;
|
||||
case Action::START_QUEUE:
|
||||
vblog(LOG_INFO, "start queue \"%s\"",
|
||||
GetActionQueueName(_queue).c_str());
|
||||
break;
|
||||
case Action::STOP_QUEUE:
|
||||
vblog(LOG_INFO, "stop queue \"%s\"",
|
||||
GetActionQueueName(_queue).c_str());
|
||||
break;
|
||||
case Action::CLEAR_QUEUE:
|
||||
vblog(LOG_INFO, "cleared queue \"%s\"",
|
||||
GetActionQueueName(_queue).c_str());
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool MacroActionQueue::Save(obs_data_t *obj) const
|
||||
{
|
||||
MacroAction::Save(obj);
|
||||
_macro.Save(obj);
|
||||
obs_data_set_int(obj, "action", static_cast<int>(_action));
|
||||
obs_data_set_string(obj, "queue", GetActionQueueName(_queue).c_str());
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MacroActionQueue::Load(obs_data_t *obj)
|
||||
{
|
||||
MacroAction::Load(obj);
|
||||
_macro.Load(obj);
|
||||
_action = static_cast<MacroActionQueue::Action>(
|
||||
obs_data_get_int(obj, "action"));
|
||||
_queue = GetWeakActionQueueByName(obs_data_get_string(obj, "queue"));
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string MacroActionQueue::GetShortDesc() const
|
||||
{
|
||||
return GetActionQueueName(_queue);
|
||||
}
|
||||
|
||||
static inline void populateActionSelection(QComboBox *list)
|
||||
{
|
||||
for (const auto &[_, name] : actionTypes) {
|
||||
list->addItem(obs_module_text(name.c_str()));
|
||||
}
|
||||
}
|
||||
|
||||
MacroActionQueueEdit::MacroActionQueueEdit(
|
||||
QWidget *parent, std::shared_ptr<MacroActionQueue> entryData)
|
||||
: QWidget(parent),
|
||||
_macros(new MacroSelection(parent)),
|
||||
_queues(new ActionQueueSelection()),
|
||||
_actions(new QComboBox()),
|
||||
_layout(new QHBoxLayout())
|
||||
{
|
||||
populateActionSelection(_actions);
|
||||
|
||||
QWidget::connect(_macros, SIGNAL(currentTextChanged(const QString &)),
|
||||
this, SLOT(MacroChanged(const QString &)));
|
||||
QWidget::connect(_queues, SIGNAL(SelectionChanged(const QString &)),
|
||||
this, SLOT(QueueChanged(const QString &)));
|
||||
QWidget::connect(_actions, SIGNAL(currentIndexChanged(int)), this,
|
||||
SLOT(ActionChanged(int)));
|
||||
|
||||
setLayout(_layout);
|
||||
|
||||
_entryData = entryData;
|
||||
UpdateEntryData();
|
||||
_loading = false;
|
||||
}
|
||||
|
||||
void MacroActionQueueEdit::UpdateEntryData()
|
||||
{
|
||||
if (!_entryData) {
|
||||
return;
|
||||
}
|
||||
_actions->setCurrentIndex(static_cast<int>(_entryData->_action));
|
||||
_macros->SetCurrentMacro(_entryData->_macro);
|
||||
_queues->SetActionQueue(_entryData->_queue);
|
||||
SetWidgetVisibility();
|
||||
}
|
||||
|
||||
void MacroActionQueueEdit::MacroChanged(const QString &text)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto lock = LockContext();
|
||||
_entryData->_macro = text;
|
||||
emit HeaderInfoChanged(
|
||||
QString::fromStdString(_entryData->GetShortDesc()));
|
||||
}
|
||||
|
||||
void MacroActionQueueEdit::QueueChanged(const QString &text)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto lock = LockContext();
|
||||
_entryData->_queue = GetWeakActionQueueByQString(text);
|
||||
}
|
||||
|
||||
void MacroActionQueueEdit::ActionChanged(int value)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto lock = LockContext();
|
||||
_entryData->_action = static_cast<MacroActionQueue::Action>(value);
|
||||
SetWidgetVisibility();
|
||||
emit HeaderInfoChanged(
|
||||
QString::fromStdString(_entryData->GetShortDesc()));
|
||||
}
|
||||
|
||||
void MacroActionQueueEdit::SetWidgetVisibility()
|
||||
{
|
||||
_layout->removeWidget(_actions);
|
||||
_layout->removeWidget(_queues);
|
||||
_layout->removeWidget(_macros);
|
||||
|
||||
ClearLayout(_layout);
|
||||
|
||||
PlaceWidgets(
|
||||
obs_module_text(
|
||||
_entryData->_action ==
|
||||
MacroActionQueue::Action::ADD_TO_QUEUE
|
||||
? "AdvSceneSwitcher.action.queue.entry.add"
|
||||
: "AdvSceneSwitcher.action.queue.entry.other"),
|
||||
_layout,
|
||||
{{"{{actions}}", _actions},
|
||||
{"{{queues}}", _queues},
|
||||
{"{{macros}}", _macros}});
|
||||
|
||||
_macros->setVisible(_entryData->_action ==
|
||||
MacroActionQueue::Action::ADD_TO_QUEUE);
|
||||
_macros->HideSelectedMacro();
|
||||
}
|
||||
|
||||
} // namespace advss
|
||||
75
src/macro-core/macro-action-queue.hpp
Normal file
75
src/macro-core/macro-action-queue.hpp
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
#pragma once
|
||||
#include "macro-action-edit.hpp"
|
||||
#include "macro-selection.hpp"
|
||||
#include "action-queue.hpp"
|
||||
|
||||
#include <QHBoxLayout>
|
||||
|
||||
namespace advss {
|
||||
|
||||
class MacroActionQueue : public MacroRefAction {
|
||||
public:
|
||||
MacroActionQueue(Macro *m) : MacroAction(m), MacroRefAction(m) {}
|
||||
bool PerformAction();
|
||||
void LogAction() const;
|
||||
bool Save(obs_data_t *obj) const;
|
||||
bool Load(obs_data_t *obj);
|
||||
std::string GetShortDesc() const;
|
||||
std::string GetId() const { return id; };
|
||||
static std::shared_ptr<MacroAction> Create(Macro *m)
|
||||
{
|
||||
return std::make_shared<MacroActionQueue>(m);
|
||||
}
|
||||
|
||||
enum class Action {
|
||||
ADD_TO_QUEUE,
|
||||
START_QUEUE,
|
||||
STOP_QUEUE,
|
||||
CLEAR_QUEUE,
|
||||
};
|
||||
Action _action = Action::ADD_TO_QUEUE;
|
||||
std::weak_ptr<ActionQueue> _queue;
|
||||
|
||||
private:
|
||||
void AddActions(ActionQueue *);
|
||||
|
||||
static bool _registered;
|
||||
static const std::string id;
|
||||
};
|
||||
|
||||
class MacroActionQueueEdit : public QWidget {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
MacroActionQueueEdit(
|
||||
QWidget *parent,
|
||||
std::shared_ptr<MacroActionQueue> entryData = nullptr);
|
||||
void UpdateEntryData();
|
||||
static QWidget *Create(QWidget *parent,
|
||||
std::shared_ptr<MacroAction> action)
|
||||
{
|
||||
return new MacroActionQueueEdit(
|
||||
parent,
|
||||
std::dynamic_pointer_cast<MacroActionQueue>(action));
|
||||
}
|
||||
|
||||
private slots:
|
||||
void MacroChanged(const QString &text);
|
||||
void QueueChanged(const QString &);
|
||||
void ActionChanged(int value);
|
||||
signals:
|
||||
void HeaderInfoChanged(const QString &);
|
||||
|
||||
private:
|
||||
void SetWidgetVisibility();
|
||||
|
||||
MacroSelection *_macros;
|
||||
ActionQueueSelection *_queues;
|
||||
QComboBox *_actions;
|
||||
QHBoxLayout *_layout;
|
||||
|
||||
std::shared_ptr<MacroActionQueue> _entryData;
|
||||
bool _loading = true;
|
||||
};
|
||||
|
||||
} // namespace advss
|
||||
163
src/macro-core/macro-condition-queue.cpp
Normal file
163
src/macro-core/macro-condition-queue.cpp
Normal file
|
|
@ -0,0 +1,163 @@
|
|||
#include "macro-condition-queue.hpp"
|
||||
#include "utility.hpp"
|
||||
|
||||
namespace advss {
|
||||
|
||||
const std::string MacroConditionQueue::id = "queue";
|
||||
|
||||
bool MacroConditionQueue::_registered = MacroConditionFactory::Register(
|
||||
MacroConditionQueue::id,
|
||||
{MacroConditionQueue::Create, MacroConditionQueueEdit::Create,
|
||||
"AdvSceneSwitcher.condition.queue"});
|
||||
|
||||
const static std::map<MacroConditionQueue::Condition, std::string>
|
||||
conditionTypes = {
|
||||
{MacroConditionQueue::Condition::STARTED,
|
||||
"AdvSceneSwitcher.condition.queue.type.started"},
|
||||
{MacroConditionQueue::Condition::STOPPED,
|
||||
"AdvSceneSwitcher.condition.queue.type.stopped"},
|
||||
{MacroConditionQueue::Condition::SIZE,
|
||||
"AdvSceneSwitcher.condition.queue.type.size"},
|
||||
};
|
||||
|
||||
bool MacroConditionQueue::CheckCondition()
|
||||
{
|
||||
auto queue = _queue.lock();
|
||||
if (!queue) {
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (_condition) {
|
||||
case Condition::STARTED:
|
||||
return queue->IsRunning();
|
||||
case Condition::STOPPED:
|
||||
return !queue->IsRunning();
|
||||
case Condition::SIZE:
|
||||
return queue->Size() < _size;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool MacroConditionQueue::Save(obs_data_t *obj) const
|
||||
{
|
||||
MacroCondition::Save(obj);
|
||||
obs_data_set_int(obj, "condition", static_cast<int>(_condition));
|
||||
obs_data_set_string(obj, "queue", GetActionQueueName(_queue).c_str());
|
||||
_size.Save(obj, "size");
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MacroConditionQueue::Load(obs_data_t *obj)
|
||||
{
|
||||
MacroCondition::Load(obj);
|
||||
_condition = static_cast<Condition>(obs_data_get_int(obj, "condition"));
|
||||
_queue = GetWeakActionQueueByName(obs_data_get_string(obj, "queue"));
|
||||
_size.Load(obj, "size");
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline void populateQueueTypeSelection(QComboBox *list)
|
||||
{
|
||||
for (const auto &[_, name] : conditionTypes) {
|
||||
list->addItem(obs_module_text(name.c_str()));
|
||||
}
|
||||
}
|
||||
|
||||
MacroConditionQueueEdit::MacroConditionQueueEdit(
|
||||
QWidget *parent, std::shared_ptr<MacroConditionQueue> entryData)
|
||||
: QWidget(parent),
|
||||
_conditions(new QComboBox()),
|
||||
_queues(new ActionQueueSelection()),
|
||||
_size(new VariableSpinBox()),
|
||||
_layout(new QHBoxLayout())
|
||||
{
|
||||
populateQueueTypeSelection(_conditions);
|
||||
|
||||
_size->setMinimum(1);
|
||||
|
||||
QWidget::connect(_conditions, SIGNAL(currentIndexChanged(int)), this,
|
||||
SLOT(ConditionChanged(int)));
|
||||
QWidget::connect(_queues, SIGNAL(SelectionChanged(const QString &)),
|
||||
this, SLOT(QueueChanged(const QString &)));
|
||||
QWidget::connect(
|
||||
_size,
|
||||
SIGNAL(NumberVariableChanged(const NumberVariable<int> &)),
|
||||
this, SLOT(SizeChanged(const NumberVariable<int> &)));
|
||||
|
||||
setLayout(_layout);
|
||||
|
||||
_entryData = entryData;
|
||||
UpdateEntryData();
|
||||
_loading = false;
|
||||
}
|
||||
|
||||
void MacroConditionQueueEdit::ConditionChanged(int condition)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto lock = LockContext();
|
||||
_entryData->_condition =
|
||||
static_cast<MacroConditionQueue::Condition>(condition);
|
||||
SetWidgetVisibility();
|
||||
}
|
||||
|
||||
void MacroConditionQueueEdit::QueueChanged(const QString &text)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto lock = LockContext();
|
||||
_entryData->_queue = GetWeakActionQueueByQString(text);
|
||||
emit HeaderInfoChanged(
|
||||
QString::fromStdString(_entryData->GetShortDesc()));
|
||||
}
|
||||
|
||||
void MacroConditionQueueEdit::SizeChanged(const NumberVariable<int> &value)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto lock = LockContext();
|
||||
_entryData->_size = value;
|
||||
}
|
||||
|
||||
void MacroConditionQueueEdit::UpdateEntryData()
|
||||
{
|
||||
if (!_entryData) {
|
||||
return;
|
||||
}
|
||||
_conditions->setCurrentIndex(static_cast<int>(_entryData->_condition));
|
||||
_queues->SetActionQueue(_entryData->_queue);
|
||||
SetWidgetVisibility();
|
||||
}
|
||||
|
||||
void MacroConditionQueueEdit::SetWidgetVisibility()
|
||||
{
|
||||
_layout->removeWidget(_conditions);
|
||||
_layout->removeWidget(_queues);
|
||||
_layout->removeWidget(_size);
|
||||
|
||||
ClearLayout(_layout);
|
||||
|
||||
PlaceWidgets(
|
||||
obs_module_text(
|
||||
_entryData->_condition ==
|
||||
MacroConditionQueue::Condition::SIZE
|
||||
? "AdvSceneSwitcher.condition.queue.entry.size"
|
||||
: "AdvSceneSwitcher.condition.queue.entry.startStop"),
|
||||
_layout,
|
||||
{{"{{conditions}}", _conditions},
|
||||
{"{{queues}}", _queues},
|
||||
{"{{size}}", _size}});
|
||||
|
||||
_size->setVisible(_entryData->_condition ==
|
||||
MacroConditionQueue::Condition::SIZE);
|
||||
}
|
||||
|
||||
} // namespace advss
|
||||
74
src/macro-core/macro-condition-queue.hpp
Normal file
74
src/macro-core/macro-condition-queue.hpp
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
#pragma once
|
||||
#include "macro-condition-edit.hpp"
|
||||
#include "action-queue.hpp"
|
||||
|
||||
#include <QCheckBox>
|
||||
#include <QPushButton>
|
||||
#include <QComboBox>
|
||||
#include <QHBoxLayout>
|
||||
|
||||
namespace advss {
|
||||
|
||||
class MacroConditionQueue : public MacroCondition {
|
||||
public:
|
||||
MacroConditionQueue(Macro *m) : MacroCondition(m, true) {}
|
||||
bool CheckCondition();
|
||||
bool Save(obs_data_t *obj) const;
|
||||
bool Load(obs_data_t *obj);
|
||||
std::string GetId() const { return id; };
|
||||
static std::shared_ptr<MacroCondition> Create(Macro *m)
|
||||
{
|
||||
return std::make_shared<MacroConditionQueue>(m);
|
||||
}
|
||||
|
||||
enum class Condition {
|
||||
STARTED,
|
||||
STOPPED,
|
||||
SIZE,
|
||||
};
|
||||
Condition _condition = Condition::STARTED;
|
||||
std::weak_ptr<ActionQueue> _queue;
|
||||
IntVariable _size = 1;
|
||||
|
||||
private:
|
||||
static bool _registered;
|
||||
static const std::string id;
|
||||
};
|
||||
|
||||
class MacroConditionQueueEdit : public QWidget {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
MacroConditionQueueEdit(
|
||||
QWidget *parent,
|
||||
std::shared_ptr<MacroConditionQueue> cond = nullptr);
|
||||
void UpdateEntryData();
|
||||
static QWidget *Create(QWidget *parent,
|
||||
std::shared_ptr<MacroCondition> cond)
|
||||
{
|
||||
return new MacroConditionQueueEdit(
|
||||
parent,
|
||||
std::dynamic_pointer_cast<MacroConditionQueue>(cond));
|
||||
}
|
||||
|
||||
private slots:
|
||||
void ConditionChanged(int);
|
||||
void QueueChanged(const QString &);
|
||||
void SizeChanged(const NumberVariable<int> &);
|
||||
|
||||
signals:
|
||||
void HeaderInfoChanged(const QString &);
|
||||
|
||||
private:
|
||||
void SetWidgetVisibility();
|
||||
|
||||
QComboBox *_conditions;
|
||||
ActionQueueSelection *_queues;
|
||||
VariableSpinBox *_size;
|
||||
QHBoxLayout *_layout;
|
||||
|
||||
std::shared_ptr<MacroConditionQueue> _entryData;
|
||||
bool _loading = true;
|
||||
};
|
||||
|
||||
} // namespace advss
|
||||
Loading…
Reference in New Issue
Block a user