mirror of
https://github.com/WarmUpTill/SceneSwitcher.git
synced 2026-03-21 17:34:57 -05:00
Add "Run" condition
Allows to use external programs as conditions
This commit is contained in:
parent
b77f8717fe
commit
16fa91c2a1
|
|
@ -416,6 +416,9 @@ AdvSceneSwitcher.condition.variable.type.equalsVariable="equals variable"
|
|||
AdvSceneSwitcher.condition.variable.type.lessThanVariable="is less than variable"
|
||||
AdvSceneSwitcher.condition.variable.type.greaterThanVariable="is greater than variable"
|
||||
AdvSceneSwitcher.condition.variable.entry="{{variables}}{{conditions}}{{strValue}}{{numValue}}{{variables2}}"
|
||||
AdvSceneSwitcher.condition.run="Run"
|
||||
AdvSceneSwitcher.condition.run.entry="Process exits before timeout of{{timeout}} seconds"
|
||||
AdvSceneSwitcher.condition.run.entry.exit="{{checkExitCode}}Check for exit code{{exitCode}}"
|
||||
|
||||
; Macro Actions
|
||||
AdvSceneSwitcher.action.switchScene="Switch scene"
|
||||
|
|
@ -463,11 +466,6 @@ AdvSceneSwitcher.action.streaming.type.stop="Stop streaming"
|
|||
AdvSceneSwitcher.action.streaming.type.start="Start streaming"
|
||||
AdvSceneSwitcher.action.streaming.entry="{{actions}}"
|
||||
AdvSceneSwitcher.action.run="Run"
|
||||
AdvSceneSwitcher.action.run.arguments="Arguments:"
|
||||
AdvSceneSwitcher.action.run.addArgument="Add argument"
|
||||
AdvSceneSwitcher.action.run.addArgumentDescription="Add new argument:"
|
||||
AdvSceneSwitcher.action.run.entry="Run {{filePath}}"
|
||||
AdvSceneSwitcher.action.run.entry.workingDirectory="Working directory:{{workingDirectory}}"
|
||||
AdvSceneSwitcher.action.sceneVisibility="Scene item visibility"
|
||||
AdvSceneSwitcher.action.sceneVisibility.type.show="Show"
|
||||
AdvSceneSwitcher.action.sceneVisibility.type.hide="Hide"
|
||||
|
|
@ -921,6 +919,13 @@ AdvSceneSwitcher.regex.dotMatchNewline=". matches newlines"
|
|||
AdvSceneSwitcher.regex.multiLine="^ and $ match start/end of line"
|
||||
AdvSceneSwitcher.regex.extendedPattern="Enable Qt's ExtendedPatternSyntax"
|
||||
|
||||
AdvSceneSwitcher.process.showAdvanced="Show advanced settings"
|
||||
AdvSceneSwitcher.process.arguments="Arguments:"
|
||||
AdvSceneSwitcher.process.addArgument="Add argument"
|
||||
AdvSceneSwitcher.process.addArgumentDescription="Add new argument:"
|
||||
AdvSceneSwitcher.process.entry="Run{{filePath}}{{advancedSettings}}"
|
||||
AdvSceneSwitcher.process.entry.workingDirectory="Working directory:{{workingDirectory}}"
|
||||
|
||||
AdvSceneSwitcher.selectScene="--select scene--"
|
||||
AdvSceneSwitcher.selectPreviousScene="Previous Scene"
|
||||
AdvSceneSwitcher.selectCurrentScene="Current Scene"
|
||||
|
|
|
|||
222
src/macro-core/macro-condition-run.cpp
Normal file
222
src/macro-core/macro-condition-run.cpp
Normal file
|
|
@ -0,0 +1,222 @@
|
|||
#include "macro-condition-edit.hpp"
|
||||
#include "macro-condition-run.hpp"
|
||||
#include "utility.hpp"
|
||||
#include "advanced-scene-switcher.hpp"
|
||||
|
||||
#include <QProcess>
|
||||
#include <QDesktopServices>
|
||||
|
||||
const std::string MacroConditionRun::id = "run";
|
||||
|
||||
bool MacroConditionRun::_registered = MacroConditionFactory::Register(
|
||||
MacroConditionRun::id,
|
||||
{MacroConditionRun::Create, MacroConditionRunEdit::Create,
|
||||
"AdvSceneSwitcher.condition.run"});
|
||||
|
||||
MacroConditionRun::~MacroConditionRun()
|
||||
{
|
||||
if (_thread.joinable()) {
|
||||
_thread.join();
|
||||
}
|
||||
}
|
||||
|
||||
bool MacroConditionRun::CheckCondition()
|
||||
{
|
||||
if (!_threadDone) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ret = false;
|
||||
|
||||
switch (_procStatus) {
|
||||
case MacroConditionRun::Status::FAILED_TO_START:
|
||||
SetVariableValue("Failed to start process");
|
||||
ret = false;
|
||||
break;
|
||||
case MacroConditionRun::Status::TIMEOUT:
|
||||
SetVariableValue("Timeout while running process");
|
||||
ret = false;
|
||||
break;
|
||||
case MacroConditionRun::Status::OK:
|
||||
ret = _checkExitCode ? _exitCode == _procExitCode : true;
|
||||
SetVariableValue(std::to_string(_procExitCode));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (_thread.joinable()) {
|
||||
_thread.join();
|
||||
}
|
||||
_threadDone = false;
|
||||
_thread = std::thread(&MacroConditionRun::RunProcess, this);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void MacroConditionRun::RunProcess()
|
||||
{
|
||||
QProcess process;
|
||||
process.setWorkingDirectory(
|
||||
QString::fromStdString(_procConfig.WorkingDir()));
|
||||
process.start(QString::fromStdString(_procConfig.Path()),
|
||||
_procConfig.Args());
|
||||
int timeout = _timeout.seconds * 1000;
|
||||
|
||||
vblog(LOG_INFO, "run \"%s\" with a timeout of %d ms",
|
||||
_procConfig.Path().c_str(), timeout);
|
||||
|
||||
bool procFinishedInTime = process.waitForFinished(timeout);
|
||||
|
||||
if (!procFinishedInTime) {
|
||||
if (process.error() == QProcess::FailedToStart) {
|
||||
vblog(LOG_INFO, "failed to start \"%s\"!",
|
||||
_procConfig.Path().c_str());
|
||||
_procStatus = Status::FAILED_TO_START;
|
||||
} else {
|
||||
vblog(LOG_INFO,
|
||||
"timeout while running \"%s\"\nAttempting to kill process!",
|
||||
_procConfig.Path().c_str());
|
||||
process.kill();
|
||||
process.waitForFinished();
|
||||
_procStatus = Status::TIMEOUT;
|
||||
}
|
||||
}
|
||||
|
||||
bool validExitCode = process.exitStatus() == QProcess::NormalExit;
|
||||
|
||||
if ((_checkExitCode && !validExitCode) || !procFinishedInTime) {
|
||||
_threadDone = true;
|
||||
return;
|
||||
}
|
||||
|
||||
_procExitCode = process.exitCode();
|
||||
_procStatus = Status::OK;
|
||||
_threadDone = true;
|
||||
}
|
||||
|
||||
bool MacroConditionRun::Save(obs_data_t *obj) const
|
||||
{
|
||||
MacroCondition::Save(obj);
|
||||
_procConfig.Save(obj);
|
||||
obs_data_set_bool(obj, "checkExitCode", _checkExitCode);
|
||||
obs_data_set_int(obj, "exitCode", _exitCode);
|
||||
_timeout.Save(obj, "timeout");
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MacroConditionRun::Load(obs_data_t *obj)
|
||||
{
|
||||
MacroCondition::Load(obj);
|
||||
_procConfig.Load(obj);
|
||||
_checkExitCode = obs_data_get_bool(obj, "checkExitCode");
|
||||
_exitCode = obs_data_get_int(obj, "exitCode");
|
||||
_timeout.Load(obj, "timeout");
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string MacroConditionRun::GetShortDesc() const
|
||||
{
|
||||
return _procConfig.Path();
|
||||
}
|
||||
|
||||
MacroConditionRunEdit::MacroConditionRunEdit(
|
||||
QWidget *parent, std::shared_ptr<MacroConditionRun> entryData)
|
||||
: QWidget(parent),
|
||||
_procConfig(new ProcessConfigEdit(this)),
|
||||
_checkExitCode(new QCheckBox()),
|
||||
_exitCode(new QSpinBox()),
|
||||
_timeout(new DurationSelection(this, false, 0.1))
|
||||
{
|
||||
_exitCode->setMinimum(-99999);
|
||||
_exitCode->setMaximum(999999);
|
||||
|
||||
QWidget::connect(_procConfig,
|
||||
SIGNAL(ConfigChanged(const ProcessConfig &)), this,
|
||||
SLOT(ProcessConfigChanged(const ProcessConfig &)));
|
||||
QWidget::connect(_timeout, SIGNAL(DurationChanged(double)), this,
|
||||
SLOT(TimeoutChanged(double)));
|
||||
QWidget::connect(_checkExitCode, SIGNAL(stateChanged(int)), this,
|
||||
SLOT(CheckExitCodeChanged(int)));
|
||||
QWidget::connect(_exitCode, SIGNAL(valueChanged(int)), this,
|
||||
SLOT(ExitCodeChanged(int)));
|
||||
|
||||
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
|
||||
{"{{checkExitCode}}", _checkExitCode},
|
||||
{"{{exitCode}}", _exitCode},
|
||||
{"{{timeout}}", _timeout},
|
||||
};
|
||||
|
||||
auto exitLayout = new QHBoxLayout();
|
||||
placeWidgets(
|
||||
obs_module_text("AdvSceneSwitcher.condition.run.entry.exit"),
|
||||
exitLayout, widgetPlaceholders);
|
||||
auto timeoutLayout = new QHBoxLayout();
|
||||
placeWidgets(obs_module_text("AdvSceneSwitcher.condition.run.entry"),
|
||||
timeoutLayout, widgetPlaceholders);
|
||||
|
||||
auto *layout = new QVBoxLayout;
|
||||
layout->addLayout(timeoutLayout);
|
||||
layout->addWidget(_procConfig);
|
||||
layout->addLayout(exitLayout);
|
||||
setLayout(layout);
|
||||
|
||||
_entryData = entryData;
|
||||
UpdateEntryData();
|
||||
_loading = false;
|
||||
}
|
||||
|
||||
void MacroConditionRunEdit::UpdateEntryData()
|
||||
{
|
||||
if (!_entryData) {
|
||||
return;
|
||||
}
|
||||
_procConfig->SetProcessConfig(_entryData->_procConfig);
|
||||
_timeout->SetDuration(_entryData->_timeout);
|
||||
_checkExitCode->setChecked(_entryData->_checkExitCode);
|
||||
_exitCode->setValue(_entryData->_exitCode);
|
||||
}
|
||||
|
||||
void MacroConditionRunEdit::TimeoutChanged(double seconds)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_timeout.seconds = seconds;
|
||||
}
|
||||
|
||||
void MacroConditionRunEdit::CheckExitCodeChanged(int state)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_checkExitCode = state;
|
||||
}
|
||||
|
||||
void MacroConditionRunEdit::ExitCodeChanged(int exitCode)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_exitCode = exitCode;
|
||||
}
|
||||
|
||||
void MacroConditionRunEdit::ProcessConfigChanged(const ProcessConfig &conf)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_procConfig = conf;
|
||||
adjustSize();
|
||||
updateGeometry();
|
||||
emit HeaderInfoChanged(
|
||||
QString::fromStdString(_entryData->GetShortDesc()));
|
||||
}
|
||||
79
src/macro-core/macro-condition-run.hpp
Normal file
79
src/macro-core/macro-condition-run.hpp
Normal file
|
|
@ -0,0 +1,79 @@
|
|||
#pragma once
|
||||
#include "macro.hpp"
|
||||
#include "process-config.hpp"
|
||||
#include "duration-control.hpp"
|
||||
|
||||
#include <QCheckBox>
|
||||
#include <QSpinBox>
|
||||
|
||||
class MacroConditionRun : public MacroCondition {
|
||||
public:
|
||||
MacroConditionRun(Macro *m) : MacroCondition(m, true) {}
|
||||
~MacroConditionRun();
|
||||
bool CheckCondition();
|
||||
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<MacroCondition> Create(Macro *m)
|
||||
{
|
||||
return std::make_shared<MacroConditionRun>(m);
|
||||
}
|
||||
|
||||
ProcessConfig _procConfig;
|
||||
bool _checkExitCode = true;
|
||||
int _exitCode = 0;
|
||||
Duration _timeout = Duration(0.1);
|
||||
|
||||
private:
|
||||
enum class Status {
|
||||
NONE,
|
||||
FAILED_TO_START,
|
||||
TIMEOUT,
|
||||
OK,
|
||||
};
|
||||
|
||||
void RunProcess();
|
||||
|
||||
std::thread _thread;
|
||||
std::atomic_bool _threadDone{true};
|
||||
Status _procStatus = Status::NONE;
|
||||
int _procExitCode = 0;
|
||||
|
||||
static bool _registered;
|
||||
static const std::string id;
|
||||
};
|
||||
|
||||
class MacroConditionRunEdit : public QWidget {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
MacroConditionRunEdit(QWidget *parent,
|
||||
std::shared_ptr<MacroConditionRun> cond = nullptr);
|
||||
void UpdateEntryData();
|
||||
static QWidget *Create(QWidget *parent,
|
||||
std::shared_ptr<MacroCondition> cond)
|
||||
{
|
||||
return new MacroConditionRunEdit(
|
||||
parent,
|
||||
std::dynamic_pointer_cast<MacroConditionRun>(cond));
|
||||
}
|
||||
|
||||
private slots:
|
||||
void ProcessConfigChanged(const ProcessConfig &);
|
||||
void TimeoutChanged(double);
|
||||
void CheckExitCodeChanged(int);
|
||||
void ExitCodeChanged(int);
|
||||
signals:
|
||||
void HeaderInfoChanged(const QString &);
|
||||
|
||||
protected:
|
||||
ProcessConfigEdit *_procConfig;
|
||||
QCheckBox *_checkExitCode;
|
||||
QSpinBox *_exitCode;
|
||||
DurationSelection *_timeout;
|
||||
std::shared_ptr<MacroConditionRun> _entryData;
|
||||
|
||||
private:
|
||||
bool _loading = true;
|
||||
};
|
||||
Loading…
Reference in New Issue
Block a user