diff --git a/CMakeLists.txt b/CMakeLists.txt index c6650336..44d1871e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -104,6 +104,7 @@ set(advanced-scene-switcher_HEADERS src/headers/macro-condition-recording.hpp src/headers/macro-condition-region.hpp src/headers/macro-condition-scene.hpp + src/headers/macro-condition-source.hpp src/headers/macro-condition-streaming.hpp src/headers/macro-condition-video.hpp src/headers/macro-condition-window.hpp @@ -170,6 +171,7 @@ set(advanced-scene-switcher_SOURCES src/macro-condition-recording.cpp src/macro-condition-region.cpp src/macro-condition-scene.cpp + src/macro-condition-source.cpp src/macro-condition-streaming.cpp src/macro-condition-video.cpp src/macro-condition-window.cpp diff --git a/data/locale/en-US.ini b/data/locale/en-US.ini index 35820bf7..c189551a 100644 --- a/data/locale/en-US.ini +++ b/data/locale/en-US.ini @@ -133,6 +133,15 @@ AdvSceneSwitcher.condition.counter.type.equal="Exactly" AdvSceneSwitcher.condition.counter.reset="Reset" AdvSceneSwitcher.condition.counter.entry.line1="{{macros}} was executed {{conditions}} {{count}} times" AdvSceneSwitcher.condition.counter.entry.line2="Current count: {{currentCount}} {{resetCount}}" +AdvSceneSwitcher.condition.source="Source" +AdvSceneSwitcher.condition.source.type.active="Is active" +AdvSceneSwitcher.condition.source.type.showing="Is showing" +AdvSceneSwitcher.condition.source.type.settings="Settings match" +AdvSceneSwitcher.condition.source.regex="Use regular expressions" +AdvSceneSwitcher.condition.source.getSettings="Get current settings" +AdvSceneSwitcher.condition.source.entry.line1="{{sources}} {{conditions}}" +AdvSceneSwitcher.condition.source.entry.line2="{{settings}}" +AdvSceneSwitcher.condition.source.entry.line3="{{regex}} {{getSettings}}" ; Macro Actions AdvSceneSwitcher.action.switchScene="Switch scene" diff --git a/src/headers/macro-condition-source.hpp b/src/headers/macro-condition-source.hpp new file mode 100644 index 00000000..d0f92e26 --- /dev/null +++ b/src/headers/macro-condition-source.hpp @@ -0,0 +1,69 @@ +#pragma once +#include "macro.hpp" +#include +#include +#include + +enum class SourceCondition { + ACTIVE, + SHOWING, + SETTINGS, +}; + +class MacroConditionSource : public MacroCondition { +public: + bool CheckCondition(); + bool Save(obs_data_t *obj); + bool Load(obs_data_t *obj); + std::string GetId() { return id; }; + static std::shared_ptr Create() + { + return std::make_shared(); + } + + OBSWeakSource _source = nullptr; + SourceCondition _condition = SourceCondition::ACTIVE; + std::string _settings = ""; + bool _regex = false; + +private: + static bool _registered; + static const std::string id; +}; + +class MacroConditionSourceEdit : public QWidget { + Q_OBJECT + +public: + MacroConditionSourceEdit( + QWidget *parent, + std::shared_ptr cond = nullptr); + void UpdateEntryData(); + static QWidget *Create(QWidget *parent, + std::shared_ptr cond) + { + return new MacroConditionSourceEdit( + parent, + std::dynamic_pointer_cast(cond)); + } + +private slots: + void SourceChanged(const QString &text); + void ConditionChanged(int cond); + void GetSettingsClicked(); + void SettingsChanged(); + void RegexChanged(int); + +protected: + QComboBox *_sources; + QComboBox *_conditions; + QPushButton *_getSettings; + QPlainTextEdit *_settings; + QCheckBox *_regex; + + std::shared_ptr _entryData; + +private: + void SetSettingsSelectionVisible(bool visible); + bool _loading = true; +}; diff --git a/src/macro-condition-source.cpp b/src/macro-condition-source.cpp new file mode 100644 index 00000000..2fff7c60 --- /dev/null +++ b/src/macro-condition-source.cpp @@ -0,0 +1,232 @@ +#include "headers/macro-condition-edit.hpp" +#include "headers/macro-condition-source.hpp" +#include "headers/utility.hpp" +#include "headers/advanced-scene-switcher.hpp" + +#include + +const std::string MacroConditionSource::id = "source"; + +bool MacroConditionSource::_registered = MacroConditionFactory::Register( + MacroConditionSource::id, + {MacroConditionSource::Create, MacroConditionSourceEdit::Create, + "AdvSceneSwitcher.condition.source"}); + +static std::map sourceConditionTypes = { + {SourceCondition::ACTIVE, + "AdvSceneSwitcher.condition.source.type.active"}, + {SourceCondition::SHOWING, + "AdvSceneSwitcher.condition.source.type.showing"}, + {SourceCondition::SETTINGS, + "AdvSceneSwitcher.condition.source.type.settings"}, +}; + +std::string getSourceSettings(OBSWeakSource ws) +{ + auto s = obs_weak_source_get_source(ws); + obs_data_t *data = obs_source_get_settings(s); + std::string settings = obs_data_get_json(data); + obs_data_release(data); + obs_source_release(s); + + return settings; +} + +bool checkSettings(const OBSWeakSource &source, const std::string &settings, + bool useRegex) +{ + bool ret = false; + std::string currentSettings = getSourceSettings(source); + if (useRegex) { + try { + std::regex expr(settings); + ret = std::regex_match(currentSettings, expr); + } catch (const std::regex_error &) { + } + } else { + ret = currentSettings == settings; + } + return ret; +} + +bool MacroConditionSource::CheckCondition() +{ + if (!_source) { + return false; + } + + bool ret = false; + auto s = obs_weak_source_get_source(_source); + + switch (_condition) { + case SourceCondition::ACTIVE: + ret = obs_source_active(s); + break; + case SourceCondition::SHOWING: + ret = obs_source_showing(s); + break; + case SourceCondition::SETTINGS: + ret = checkSettings(_source, _settings, _regex); + break; + default: + break; + } + + obs_source_release(s); + + return ret; +} + +bool MacroConditionSource::Save(obs_data_t *obj) +{ + MacroCondition::Save(obj); + obs_data_set_string(obj, "source", GetWeakSourceName(_source).c_str()); + obs_data_set_int(obj, "condition", static_cast(_condition)); + return true; +} + +bool MacroConditionSource::Load(obs_data_t *obj) +{ + MacroCondition::Load(obj); + const char *sourceName = obs_data_get_string(obj, "source"); + _source = GetWeakSourceByName(sourceName); + _condition = static_cast( + obs_data_get_int(obj, "condition")); + return true; +} + +static inline void populateConditionSelection(QComboBox *list) +{ + for (auto entry : sourceConditionTypes) { + list->addItem(obs_module_text(entry.second.c_str())); + } +} + +MacroConditionSourceEdit::MacroConditionSourceEdit( + QWidget *parent, std::shared_ptr entryData) + : QWidget(parent) +{ + _sources = new QComboBox(); + _conditions = new QComboBox(); + + _getSettings = new QPushButton(obs_module_text( + "AdvSceneSwitcher.condition.source.getSettings")); + _settings = new QPlainTextEdit(); + _regex = new QCheckBox( + obs_module_text("AdvSceneSwitcher.condition.source.regex")); + + populateConditionSelection(_conditions); + populateSourceSelection(_sources); + + QWidget::connect(_sources, SIGNAL(currentTextChanged(const QString &)), + this, SLOT(SourceChanged(const QString &))); + QWidget::connect(_conditions, SIGNAL(currentIndexChanged(int)), this, + SLOT(ConditionChanged(int))); + QWidget::connect(_getSettings, SIGNAL(clicked()), this, + SLOT(GetSettingsClicked())); + QWidget::connect(_settings, SIGNAL(textChanged()), this, + SLOT(SettingsChanged())); + QWidget::connect(_regex, SIGNAL(stateChanged(int)), this, + SLOT(RegexChanged(int))); + + QVBoxLayout *mainLayout = new QVBoxLayout; + QHBoxLayout *line1Layout = new QHBoxLayout; + QHBoxLayout *line2Layout = new QHBoxLayout; + QHBoxLayout *line3Layout = new QHBoxLayout; + std::unordered_map widgetPlaceholders = { + {"{{sources}}", _sources}, {"{{conditions}}", _conditions}, + {"{{settings}}", _settings}, {"{{getSettings}}", _getSettings}, + {"{{regex}}", _regex}, + }; + placeWidgets(obs_module_text( + "AdvSceneSwitcher.condition.source.entry.line1"), + line1Layout, widgetPlaceholders); + placeWidgets(obs_module_text( + "AdvSceneSwitcher.condition.source.entry.line2"), + line2Layout, widgetPlaceholders, false); + placeWidgets(obs_module_text( + "AdvSceneSwitcher.condition.source.entry.line3"), + line3Layout, widgetPlaceholders); + mainLayout->addLayout(line1Layout); + mainLayout->addLayout(line2Layout); + mainLayout->addLayout(line3Layout); + setLayout(mainLayout); + + _entryData = entryData; + UpdateEntryData(); + _loading = false; +} + +void MacroConditionSourceEdit::SourceChanged(const QString &text) +{ + if (_loading || !_entryData) { + return; + } + + std::lock_guard lock(switcher->m); + _entryData->_source = GetWeakSourceByQString(text); +} + +void MacroConditionSourceEdit::ConditionChanged(int index) +{ + if (_loading || !_entryData) { + return; + } + + std::lock_guard lock(switcher->m); + _entryData->_condition = static_cast(index); + SetSettingsSelectionVisible(_entryData->_condition == + SourceCondition::SETTINGS); +} + +void MacroConditionSourceEdit::GetSettingsClicked() +{ + if (_loading || !_entryData || !_entryData->_source) { + return; + } + + _settings->setPlainText( + QString::fromStdString(getSourceSettings(_entryData->_source))); +} + +void MacroConditionSourceEdit::SettingsChanged() +{ + if (_loading || !_entryData) { + return; + } + + std::lock_guard lock(switcher->m); + _entryData->_settings = _settings->toPlainText().toStdString(); +} + +void MacroConditionSourceEdit::RegexChanged(int state) +{ + if (_loading || !_entryData) { + return; + } + + std::lock_guard lock(switcher->m); + _entryData->_regex = state; +} + +void MacroConditionSourceEdit::SetSettingsSelectionVisible(bool visible) +{ + _settings->setVisible(visible); + _getSettings->setVisible(visible); + _regex->setVisible(visible); +} + +void MacroConditionSourceEdit::UpdateEntryData() +{ + if (!_entryData) { + return; + } + + _sources->setCurrentText( + GetWeakSourceName(_entryData->_source).c_str()); + _conditions->setCurrentIndex(static_cast(_entryData->_condition)); + _settings->setPlainText(QString::fromStdString(_entryData->_settings)); + _regex->setChecked(_entryData->_regex); + SetSettingsSelectionVisible(_entryData->_condition == + SourceCondition::SETTINGS); +}