SceneSwitcher/lib/utils/action-queue.cpp
WarmUpTill 7d0332dd0e Restructure library and plugins
The "core" macro conditions and actions have been extracted out to the
"base" plugin.

The library now mostly contains functionality which is required across
all plugins and (e.g. definitions for macro segments).

The goal is to reduce the complexity and cross-dependencies and group
the source files in a better way.

This should relsove the "library limit of 65535 objects exceeded" build
issue occuring in some Windows build environments.
2024-01-27 14:10:34 +01:00

382 lines
9.1 KiB
C++

#include "action-queue.hpp"
#include "obs-module-helper.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