From 5995563a8059081299ab6d7f56f6e830b14045da Mon Sep 17 00:00:00 2001 From: WarmUpTill Date: Wed, 1 Feb 2023 16:36:18 +0100 Subject: [PATCH] Add action to open projector --- CMakeLists.txt | 2 + data/locale/en-US.ini | 11 + src/macro-core/macro-action-projector.cpp | 290 ++++++++++++++++++++++ src/macro-core/macro-action-projector.hpp | 73 ++++++ 4 files changed, 376 insertions(+) create mode 100644 src/macro-core/macro-action-projector.cpp create mode 100644 src/macro-core/macro-action-projector.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index e4e4abae..0e6e7236 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -113,6 +113,8 @@ target_sources( src/macro-core/macro-action-plugin-state.hpp src/macro-core/macro-action-profile.cpp 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-random.cpp src/macro-core/macro-action-random.hpp src/macro-core/macro-action-recording.cpp diff --git a/data/locale/en-US.ini b/data/locale/en-US.ini index 822b8b99..494798c6 100644 --- a/data/locale/en-US.ini +++ b/data/locale/en-US.ini @@ -594,6 +594,17 @@ AdvSceneSwitcher.action.variable.currentSegmentValue="Current value:" AdvSceneSwitcher.action.variable.entry="{{actions}}{{variables}}{{variables2}}{{strValue}}{{numValue}}{{segmentIndex}}" AdvSceneSwitcher.action.variable.entry.substringIndex="Substring start:{{subStringStart}} Substring size:{{subStringSize}}" AdvSceneSwitcher.action.variable.entry.substringRegex="Assign value of{{regexMatchIdx}}match using regular expression:" +AdvSceneSwitcher.action.projector="Projector" +AdvSceneSwitcher.action.projector.type.source="Source" +AdvSceneSwitcher.action.projector.type.scene="Scene" +AdvSceneSwitcher.action.projector.type.preview="Preview" +AdvSceneSwitcher.action.projector.type.program="Program" +AdvSceneSwitcher.action.projector.type.multiview="Multiview" +AdvSceneSwitcher.action.projector.display="Display" +AdvSceneSwitcher.action.projector.windowed="Windowed" +AdvSceneSwitcher.action.projector.fullscreen="Fullscreen" +AdvSceneSwitcher.action.projector.entry="Open{{windowTypes}}projector of{{types}}{{scenes}}{{sources}}" +AdvSceneSwitcher.action.projector.entry.monitor="on{{monitors}}" ; Transition Tab diff --git a/src/macro-core/macro-action-projector.cpp b/src/macro-core/macro-action-projector.cpp new file mode 100644 index 00000000..de638052 --- /dev/null +++ b/src/macro-core/macro-action-projector.cpp @@ -0,0 +1,290 @@ +#include "macro-action-projector.hpp" +#include "advanced-scene-switcher.hpp" +#include "utility.hpp" + +#include + +const std::string MacroActionProjector::id = "projector"; + +bool MacroActionProjector::_registered = MacroActionFactory::Register( + MacroActionProjector::id, + {MacroActionProjector::Create, MacroActionProjectorEdit::Create, + "AdvSceneSwitcher.action.projector"}); + +const static std::map selectionTypes = { + {MacroActionProjector::Type::SOURCE, + "AdvSceneSwitcher.action.projector.type.source"}, + {MacroActionProjector::Type::SCENE, + "AdvSceneSwitcher.action.projector.type.scene"}, + {MacroActionProjector::Type::PREVIEW, + "AdvSceneSwitcher.action.projector.type.preview"}, + {MacroActionProjector::Type::PROGRAM, + "AdvSceneSwitcher.action.projector.type.program"}, + {MacroActionProjector::Type::MULTIVIEW, + "AdvSceneSwitcher.action.projector.type.multiview"}, +}; + +bool MacroActionProjector::PerformAction() +{ + std::string name = ""; + const char *type = ""; + + switch (_type) { + case MacroActionProjector::Type::SOURCE: + type = "Source"; + name = GetWeakSourceName(_source.GetSource()); + if (name.empty()) { + return true; + } + break; + case MacroActionProjector::Type::SCENE: + type = "Scene"; + name = GetWeakSourceName(_scene.GetScene()); + if (name.empty()) { + return true; + } + break; + case MacroActionProjector::Type::PREVIEW: + type = "Preview"; + break; + case MacroActionProjector::Type::PROGRAM: + type = "StudioProgram"; + break; + case MacroActionProjector::Type::MULTIVIEW: + type = "Multiview"; + break; + } + + obs_frontend_open_projector(type, _fullscreen ? _monitor : -1, "", + name.c_str()); + + return true; +} + +void MacroActionProjector::LogAction() const +{ + auto it = selectionTypes.find(_type); + if (it != selectionTypes.end()) { + vblog(LOG_INFO, + "performed projector action \"%s\" with" + "source \"%s\"," + "scene \"%s\"," + "monitor %d", + it->second.c_str(), _source.ToString(true).c_str(), + _scene.ToString(true).c_str(), _monitor); + } else { + blog(LOG_WARNING, "ignored unknown projector action %d", + static_cast(_type)); + } +} + +bool MacroActionProjector::Save(obs_data_t *obj) const +{ + MacroAction::Save(obj); + obs_data_set_int(obj, "type", static_cast(_type)); + obs_data_set_int(obj, "monitor", _monitor); + obs_data_set_bool(obj, "fullscreen", _fullscreen); + _scene.Save(obj); + _source.Save(obj); + return true; +} + +bool MacroActionProjector::Load(obs_data_t *obj) +{ + MacroAction::Load(obj); + _type = static_cast(obs_data_get_int(obj, "type")); + _monitor = obs_data_get_int(obj, "monitor"); + _fullscreen = obs_data_get_bool(obj, "fullscreen"); + _scene.Load(obj); + _source.Load(obj); + return true; +} + +static inline void populateSelectionTypes(QComboBox *list) +{ + for (auto entry : selectionTypes) { + list->addItem(obs_module_text(entry.second.c_str())); + } +} + +static inline void populateWindowTypes(QComboBox *list) +{ + list->addItem( + obs_module_text("AdvSceneSwitcher.action.projector.windowed")); + list->addItem(obs_module_text( + "AdvSceneSwitcher.action.projector.fullscreen")); +} + +static QStringList getMonitorNames() +{ + QStringList monitorNames; + QList screens = QGuiApplication::screens(); + for (int i = 0; i < screens.size(); i++) { + QScreen *screen = screens[i]; + QRect screenGeometry = screen->geometry(); + qreal ratio = screen->devicePixelRatio(); + QString name = ""; +#if defined(__APPLE__) || defined(_WIN32) + name = screen->name(); +#else + name = screen->model().simplified(); + if (name.length() > 1 && name.endsWith("-")) { + name.chop(1); + } +#endif + name = name.simplified(); + + if (name.length() == 0) { + name = QString("%1 %2") + .arg(obs_module_text( + "AdvSceneSwitcher.action.projector.display")) + .arg(QString::number(i + 1)); + } + QString str = + QString("%1: %2x%3 @ %4,%5") + .arg(name, + QString::number(screenGeometry.width() * + ratio), + QString::number(screenGeometry.height() * + ratio), + QString::number(screenGeometry.x()), + QString::number(screenGeometry.y())); + + monitorNames << str; + } + return monitorNames; +} + +MacroActionProjectorEdit::MacroActionProjectorEdit( + QWidget *parent, std::shared_ptr entryData) + : QWidget(parent), + _windowTypes(new QComboBox()), + _types(new QComboBox()), + _scenes(new SceneSelectionWidget(window(), true, false, true, true, + true)), + _sources(new SourceSelectionWidget(window(), QStringList(), true)), + _monitorSelection(new QHBoxLayout()), + _monitors(new QComboBox()) +{ + populateWindowTypes(_windowTypes); + populateSelectionTypes(_types); + auto sources = GetSourceNames(); + sources.sort(); + _sources->SetSourceNameList(sources); + _monitors->addItems(getMonitorNames()); + + QWidget::connect(_windowTypes, SIGNAL(currentIndexChanged(int)), this, + SLOT(WindowTypeChanged(int))); + QWidget::connect(_types, SIGNAL(currentIndexChanged(int)), this, + SLOT(TypeChanged(int))); + QWidget::connect(_scenes, SIGNAL(SceneChanged(const SceneSelection &)), + this, SLOT(SceneChanged(const SceneSelection &))); + QWidget::connect(_sources, + SIGNAL(SourceChanged(const SourceSelection &)), this, + SLOT(SourceChanged(const SourceSelection &))); + QWidget::connect(_monitors, SIGNAL(currentIndexChanged(int)), this, + SLOT(MonitorChanged(int))); + + std::unordered_map widgetPlaceholders = { + {"{{windowTypes}}", _windowTypes}, {"{{types}}", _types}, + {"{{scenes}}", _scenes}, {"{{sources}}", _sources}, + {"{{monitors}}", _monitors}, + }; + + placeWidgets(obs_module_text( + "AdvSceneSwitcher.action.projector.entry.monitor"), + _monitorSelection, widgetPlaceholders); + + QHBoxLayout *mainLayout = new QHBoxLayout; + placeWidgets(obs_module_text("AdvSceneSwitcher.action.projector.entry"), + mainLayout, widgetPlaceholders); + mainLayout->insertLayout(mainLayout->count() - 1, _monitorSelection); + setLayout(mainLayout); + + _entryData = entryData; + UpdateEntryData(); + _loading = false; +} + +void MacroActionProjectorEdit::UpdateEntryData() +{ + if (!_entryData) { + return; + } + _windowTypes->setCurrentIndex(_entryData->_fullscreen ? 1 : 0); + _types->setCurrentIndex(static_cast(_entryData->_type)); + _scenes->SetScene(_entryData->_scene); + _sources->SetSource(_entryData->_source); + _monitors->setCurrentIndex(_entryData->_monitor); + SetWidgetVisibility(); +} + +void MacroActionProjectorEdit::SceneChanged(const SceneSelection &s) +{ + if (_loading || !_entryData) { + return; + } + + std::lock_guard lock(switcher->m); + _entryData->_scene = s; +} + +void MacroActionProjectorEdit::SourceChanged(const SourceSelection &source) +{ + if (_loading || !_entryData) { + return; + } + + std::lock_guard lock(switcher->m); + _entryData->_source = source; +} + +void MacroActionProjectorEdit::MonitorChanged(int value) +{ + if (_loading || !_entryData) { + return; + } + + std::lock_guard lock(switcher->m); + _entryData->_monitor = value; +} + +void MacroActionProjectorEdit::WindowTypeChanged(int value) +{ + if (_loading || !_entryData) { + return; + } + + std::lock_guard lock(switcher->m); + _entryData->_fullscreen = + _windowTypes->currentText() == + obs_module_text("AdvSceneSwitcher.action.projector.fullscreen"); + SetWidgetVisibility(); +} + +void MacroActionProjectorEdit::TypeChanged(int value) +{ + if (_loading || !_entryData) { + return; + } + + std::lock_guard lock(switcher->m); + _entryData->_type = static_cast(value); + SetWidgetVisibility(); +} + +void MacroActionProjectorEdit::SetWidgetVisibility() +{ + if (!_entryData) { + return; + } + + _scenes->setVisible(_entryData->_type == + MacroActionProjector::Type::SCENE); + _sources->setVisible(_entryData->_type == + MacroActionProjector::Type::SOURCE); + setLayoutVisible(_monitorSelection, _entryData->_fullscreen); + + adjustSize(); + updateGeometry(); +} diff --git a/src/macro-core/macro-action-projector.hpp b/src/macro-core/macro-action-projector.hpp new file mode 100644 index 00000000..dc2962f0 --- /dev/null +++ b/src/macro-core/macro-action-projector.hpp @@ -0,0 +1,73 @@ +#pragma once +#include "macro-action-edit.hpp" +#include "scene-selection.hpp" +#include "source-selection.hpp" + +class MacroActionProjector : public MacroAction { +public: + MacroActionProjector(Macro *m) : MacroAction(m) {} + bool PerformAction(); + void LogAction() const; + bool Save(obs_data_t *obj) const; + bool Load(obs_data_t *obj); + std::string GetId() const { return id; }; + static std::shared_ptr Create(Macro *m) + { + return std::make_shared(m); + } + + enum class Type { + SOURCE, + SCENE, + PREVIEW, + PROGRAM, + MULTIVIEW, + }; + + Type _type = Type::SCENE; + SourceSelection _source; + SceneSelection _scene; + int _monitor = 0; + bool _fullscreen = true; + +private: + static bool _registered; + static const std::string id; +}; + +class MacroActionProjectorEdit : public QWidget { + Q_OBJECT + +public: + MacroActionProjectorEdit( + QWidget *parent, + std::shared_ptr entryData = nullptr); + void UpdateEntryData(); + static QWidget *Create(QWidget *parent, + std::shared_ptr action) + { + return new MacroActionProjectorEdit( + parent, std::dynamic_pointer_cast( + action)); + } + +private slots: + void WindowTypeChanged(int value); + void TypeChanged(int value); + void SceneChanged(const SceneSelection &); + void SourceChanged(const SourceSelection &); + void MonitorChanged(int value); + +protected: + QComboBox *_windowTypes; + QComboBox *_types; + SceneSelectionWidget *_scenes; + SourceSelectionWidget *_sources; + QHBoxLayout *_monitorSelection; + QComboBox *_monitors; + std::shared_ptr _entryData; + +private: + void SetWidgetVisibility(); + bool _loading = true; +};