From 211eac5313db468933ae59c86a4c3fa7f377c27c Mon Sep 17 00:00:00 2001 From: WarmUpTill Date: Sat, 29 Jul 2023 23:07:16 +0200 Subject: [PATCH] Add slide show condition It allows you to check ... * if the slide changed * the current slide index * the current slide path Important limitation: Its internal state is only updated whenever the "slide_changed" signal is sent by the particular source. --- CMakeLists.txt | 2 + data/locale/en-US.ini | 6 + src/macro-core/macro-condition-slideshow.cpp | 312 +++++++++++++++++++ src/macro-core/macro-condition-slideshow.hpp | 88 ++++++ 4 files changed, 408 insertions(+) create mode 100644 src/macro-core/macro-condition-slideshow.cpp create mode 100644 src/macro-core/macro-condition-slideshow.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index cdb1b22f..f55b43a3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -203,6 +203,8 @@ target_sources( src/macro-core/macro-condition-scene-transform.hpp src/macro-core/macro-condition-scene-visibility.cpp src/macro-core/macro-condition-scene-visibility.hpp + src/macro-core/macro-condition-slideshow.cpp + src/macro-core/macro-condition-slideshow.hpp src/macro-core/macro-condition-scene.cpp src/macro-core/macro-condition-scene.hpp src/macro-core/macro-condition-source.cpp diff --git a/data/locale/en-US.ini b/data/locale/en-US.ini index 8a246df3..3bfedef3 100644 --- a/data/locale/en-US.ini +++ b/data/locale/en-US.ini @@ -466,6 +466,12 @@ AdvSceneSwitcher.condition.display="Display" AdvSceneSwitcher.condition.display.type.displayName="Name of connected displays matches" AdvSceneSwitcher.condition.display.type.displayCount="Number of connected displays is" AdvSceneSwitcher.condition.display.entry="{{conditions}}{{displays}}{{regex}}{{displayCount}}" +AdvSceneSwitcher.condition.slideshow="Slide Show" +AdvSceneSwitcher.condition.slideshow.condition.slideChanged="Slide changed" +AdvSceneSwitcher.condition.slideshow.condition.slideIndex="Current slide number is" +AdvSceneSwitcher.condition.slideshow.condition.slidePath="Current slide path is" +AdvSceneSwitcher.condition.slideshow.updateIntervalTooltip="Information about the slide show status will only be updated based on the configured time between slides" +AdvSceneSwitcher.condition.slideshow.entry="{{sources}}{{conditions}}{{index}}{{path}}" ; Macro Actions AdvSceneSwitcher.action.switchScene="Switch scene" diff --git a/src/macro-core/macro-condition-slideshow.cpp b/src/macro-core/macro-condition-slideshow.cpp new file mode 100644 index 00000000..25201b49 --- /dev/null +++ b/src/macro-core/macro-condition-slideshow.cpp @@ -0,0 +1,312 @@ +#include "macro-condition-slideshow.hpp" +#include "macro.hpp" +#include "utility.hpp" + +namespace advss { + +const std::string MacroConditionSlideshow::id = "slideshow"; + +bool MacroConditionSlideshow::_registered = MacroConditionFactory::Register( + MacroConditionSlideshow::id, + {MacroConditionSlideshow::Create, MacroConditionSlideshowEdit::Create, + "AdvSceneSwitcher.condition.slideshow"}); + +static const std::map + conditions = { + {MacroConditionSlideshow::Condition::SLIDE_CHANGED, + "AdvSceneSwitcher.condition.slideshow.condition.slideChanged"}, + {MacroConditionSlideshow::Condition::SLIDE_INDEX, + "AdvSceneSwitcher.condition.slideshow.condition.slideIndex"}, + {MacroConditionSlideshow::Condition::SLIDE_PATH, + "AdvSceneSwitcher.condition.slideshow.condition.slidePath"}, +}; + +MacroConditionSlideshow::MacroConditionSlideshow(Macro *m) + : MacroCondition(m, true) +{ +} + +MacroConditionSlideshow::~MacroConditionSlideshow() +{ + RemoveSignalHandler(); +} + +bool MacroConditionSlideshow::CheckCondition() +{ + auto source = _source.GetSource(); + if (_currentSignalSource != source) { + Reset(); + RemoveSignalHandler(); + AddSignalHandler(source); + } + + if (!source) { + return false; + } + + switch (_condition) { + case MacroConditionSlideshow::Condition::SLIDE_CHANGED: + if (!_slideChanged) { + SetVariableValue("false"); + return false; + } + _slideChanged = false; + SetVariableValue("true"); + return true; + case MacroConditionSlideshow::Condition::SLIDE_INDEX: + if (_currentIndex == -1) { + SetVariableValue("-1"); + return false; + } + SetVariableValue(std::to_string(_currentIndex + 1)); + return _currentIndex == _index - 1; + case MacroConditionSlideshow::Condition::SLIDE_PATH: + if (_currentPath[0] == '\0') { + SetVariableValue(""); + return false; + } + SetVariableValue(_currentPath); + return std::string(_path) == std::string(_currentPath); + } + + return false; +} + +bool MacroConditionSlideshow::Save(obs_data_t *obj) const +{ + MacroCondition::Save(obj); + obs_data_set_int(obj, "condition", static_cast(_condition)); + _source.Save(obj); + _index.Save(obj, "index"); + _path.Save(obj, "path"); + return true; +} + +bool MacroConditionSlideshow::Load(obs_data_t *obj) +{ + MacroCondition::Load(obj); + _condition = static_cast(obs_data_get_int(obj, "condition")); + _source.Load(obj); + _index.Load(obj, "index"); + _path.Load(obj, "path"); + + auto s = _source.GetSource(); + AddSignalHandler(s); + return true; +} + +std::string MacroConditionSlideshow::GetShortDesc() const +{ + return _source.ToString(); +} + +void MacroConditionSlideshow::SetSource(const SourceSelection &source) +{ + Reset(); + _source = source; + RemoveSignalHandler(); + auto s = _source.GetSource(); + AddSignalHandler(s); +} + +const SourceSelection &MacroConditionSlideshow::GetSource() const +{ + return _source; +} + +void MacroConditionSlideshow::SlideChanged(void *c, calldata_t *data) +{ + auto condition = static_cast(c); + const auto macro = condition->GetMacro(); + if (macro && macro->Paused()) { + return; + } + + long long newIndex = 0; + if (!calldata_get_int(data, "index", &newIndex)) { + condition->_currentIndex = -1; + } + + // Function will be called every time the slide_time timeout is reached + // regardless of if there was actually a change in slide index. + // + // Thus we need to manually check if the slide index really changed. + if (newIndex != condition->_currentIndex) { + condition->_slideChanged = true; + } + condition->_currentIndex = newIndex; + + if (!calldata_get_string(data, "path", &condition->_currentPath)) { + condition->_currentPath = ""; + } +} + +void MacroConditionSlideshow::RemoveSignalHandler() +{ + obs_source_t *source = obs_weak_source_get_source(_currentSignalSource); + if (!source) { + return; + } + signal_handler_t *sh = obs_source_get_signal_handler(source); + signal_handler_disconnect(sh, "slide_changed", SlideChanged, this); + obs_source_release(source); +} + +void MacroConditionSlideshow::AddSignalHandler(const OBSWeakSource &s) +{ + _currentSignalSource = s; + if (!s) { + return; + } + + obs_source_t *source = obs_weak_source_get_source(s); + signal_handler_t *sh = obs_source_get_signal_handler(source); + signal_handler_connect(sh, "slide_changed", SlideChanged, this); + obs_source_release(source); +} + +void MacroConditionSlideshow::Reset() +{ + _slideChanged = false; + _currentIndex = -1; + _currentPath = ""; +} + +static void populateConditionSelection(QComboBox *list) +{ + for (const auto &[_, name] : conditions) { + list->addItem(obs_module_text(name.c_str())); + } +} + +static QStringList getSlideshowNames() +{ + auto sourceEnum = [](void *param, obs_source_t *source) -> bool /* -- */ + { + QStringList *list = reinterpret_cast(param); + std::string sourceId = obs_source_get_id(source); + if (sourceId.compare("slideshow") == 0) { + *list << obs_source_get_name(source); + } + return true; + }; + + QStringList list; + obs_enum_sources(sourceEnum, &list); + return list; +} + +MacroConditionSlideshowEdit::MacroConditionSlideshowEdit( + QWidget *parent, std::shared_ptr entryData) + : QWidget(parent), + _conditions(new QComboBox(this)), + _sources(new SourceSelectionWidget(this, QStringList(), true)), + _index(new VariableSpinBox(this)), + _path(new VariableLineEdit(this)) +{ + setToolTip(obs_module_text( + "AdvSceneSwitcher.condition.slideshow.updateIntervalTooltip")); + _index->setMinimum(1); + populateConditionSelection(_conditions); + + auto sources = getSlideshowNames(); + sources.sort(); + _sources->SetSourceNameList(sources); + + QWidget::connect(_conditions, SIGNAL(currentIndexChanged(int)), this, + SLOT(ConditionChanged(int))); + QWidget::connect(_sources, + SIGNAL(SourceChanged(const SourceSelection &)), this, + SLOT(SourceChanged(const SourceSelection &))); + QWidget::connect( + _index, + SIGNAL(NumberVariableChanged(const NumberVariable &)), + this, SLOT(IndexChanged(const NumberVariable &))); + QWidget::connect(_path, SIGNAL(editingFinished()), this, + SLOT(PathChanged())); + + auto layout = new QHBoxLayout; + PlaceWidgets( + obs_module_text("AdvSceneSwitcher.condition.slideshow.entry"), + layout, + {{"{{conditions}}", _conditions}, + {"{{sources}}", _sources}, + {"{{index}}", _index}, + {"{{path}}", _path}}); + setLayout(layout); + + _entryData = entryData; + UpdateEntryData(); + _loading = false; +} + +void MacroConditionSlideshowEdit::ConditionChanged(int index) +{ + if (_loading || !_entryData) { + return; + } + + auto lock = LockContext(); + _entryData->_condition = + static_cast(index); + SetWidgetVisibility(); +} + +void MacroConditionSlideshowEdit::SourceChanged(const SourceSelection &source) +{ + if (_loading || !_entryData) { + return; + } + + auto lock = LockContext(); + _entryData->SetSource(source); + emit HeaderInfoChanged( + QString::fromStdString(_entryData->GetShortDesc())); +} + +void MacroConditionSlideshowEdit::IndexChanged(const NumberVariable &value) +{ + if (_loading || !_entryData) { + return; + } + + auto lock = LockContext(); + _entryData->_index = value; +} + +void MacroConditionSlideshowEdit::PathChanged() +{ + if (_loading || !_entryData) { + return; + } + + auto lock = LockContext(); + _entryData->_path = _path->text().toStdString(); +} + +void MacroConditionSlideshowEdit::UpdateEntryData() +{ + if (!_entryData) { + return; + } + + _conditions->setCurrentIndex(static_cast(_entryData->_condition)); + _sources->SetSource(_entryData->GetSource()); + _index->SetValue(_entryData->_index); + _path->setText(_entryData->_path); + SetWidgetVisibility(); +} + +void MacroConditionSlideshowEdit::SetWidgetVisibility() +{ + if (!_entryData) { + return; + } + + _index->setVisible(_entryData->_condition == + MacroConditionSlideshow::Condition::SLIDE_INDEX); + _path->setVisible(_entryData->_condition == + MacroConditionSlideshow::Condition::SLIDE_PATH); +} + +} // namespace advss diff --git a/src/macro-core/macro-condition-slideshow.hpp b/src/macro-core/macro-condition-slideshow.hpp new file mode 100644 index 00000000..e1bca245 --- /dev/null +++ b/src/macro-core/macro-condition-slideshow.hpp @@ -0,0 +1,88 @@ +#pragma once +#include "macro-condition-edit.hpp" +#include "source-selection.hpp" +#include "variable-spinbox.hpp" +#include "variable-line-edit.hpp" + +namespace advss { + +class MacroConditionSlideshow : public MacroCondition { +public: + MacroConditionSlideshow(Macro *m); + ~MacroConditionSlideshow(); + 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 Create(Macro *m) + { + return std::make_shared(m); + } + + void SetSource(const SourceSelection &source); + const SourceSelection &GetSource() const; + + enum class Condition { + SLIDE_CHANGED, + SLIDE_INDEX, + SLIDE_PATH, + }; + Condition _condition = Condition::SLIDE_CHANGED; + IntVariable _index; + StringVariable _path; + +private: + static void SlideChanged(void *condition, calldata_t *); + void RemoveSignalHandler(); + void AddSignalHandler(const OBSWeakSource &); + void Reset(); + + SourceSelection _source; + OBSWeakSource _currentSignalSource; + bool _slideChanged = false; + long long _currentIndex = -1; + const char *_currentPath = ""; + + static bool _registered; + static const std::string id; +}; + +class MacroConditionSlideshowEdit : public QWidget { + Q_OBJECT + +public: + MacroConditionSlideshowEdit( + QWidget *parent, + std::shared_ptr cond = nullptr); + void UpdateEntryData(); + static QWidget *Create(QWidget *parent, + std::shared_ptr cond) + { + return new MacroConditionSlideshowEdit( + parent, + std::dynamic_pointer_cast( + cond)); + } + +private slots: + void ConditionChanged(int index); + void SourceChanged(const SourceSelection &); + void IndexChanged(const NumberVariable &); + void PathChanged(); +signals: + void HeaderInfoChanged(const QString &); + +private: + void SetWidgetVisibility(); + + QComboBox *_conditions; + VariableSpinBox *_index; + VariableLineEdit *_path; + SourceSelectionWidget *_sources; + + std::shared_ptr _entryData; + bool _loading = true; +}; + +} // namespace advss