diff --git a/CMakeLists.txt b/CMakeLists.txt index 863266434..aed79fc6a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -20,6 +20,12 @@ if(BUILD_OUT_OF_TREE) include(helpers) endif() +# OBS 31 no longer defines find_qt so check if we need to include it for in-tree +# builds +if(NOT COMMAND foo) + include("${CMAKE_CURRENT_SOURCE_DIR}/cmake/common/helpers_common.cmake") +endif() + set(LIB_NAME "${PROJECT_NAME}-lib") add_library(${PROJECT_NAME} MODULE) add_library(${LIB_NAME} SHARED) diff --git a/data/locale/en-US.ini b/data/locale/en-US.ini index 8f8e28860..24c341dd2 100644 --- a/data/locale/en-US.ini +++ b/data/locale/en-US.ini @@ -769,6 +769,8 @@ AdvSceneSwitcher.condition.streamDeck.checkData="Check data field" AdvSceneSwitcher.condition.streamDeck.startListen="Start listening" AdvSceneSwitcher.condition.streamDeck.stopListen="Stop listening" AdvSceneSwitcher.condition.streamDeck.pluginDownload="

The Stream Deck plugin can be found here on GitHub.

" +AdvSceneSwitcher.condition.gameCapture="Game capture" +AdvSceneSwitcher.condition.gameCapture.entry="{{sources}}hooked a game." # Macro Actions AdvSceneSwitcher.action.unknown="Unknown action" @@ -1971,6 +1973,13 @@ AdvSceneSwitcher.tempVar.queue.size="Size" AdvSceneSwitcher.tempVar.queue.running="Is running" AdvSceneSwitcher.tempVar.queue.running.description="Returns \"true\" if the queue is started and \"false\" if it is stopped." +AdvSceneSwitcher.tempVar.gameCapture.title="Title" +AdvSceneSwitcher.tempVar.gameCapture.title.description="Window title of the application captured by the source." +AdvSceneSwitcher.tempVar.gameCapture.title="Class" +AdvSceneSwitcher.tempVar.gameCapture.title.description="Window class of the application captured by the source." +AdvSceneSwitcher.tempVar.gameCapture.title="Executable" +AdvSceneSwitcher.tempVar.gameCapture.title.description="Executable name of the application captured by the source." + AdvSceneSwitcher.selectScene="--select scene--" AdvSceneSwitcher.selectPreviousScene="Previous Scene" AdvSceneSwitcher.selectCurrentScene="Current Scene" diff --git a/plugins/base/CMakeLists.txt b/plugins/base/CMakeLists.txt index f2ec114b0..81075217d 100644 --- a/plugins/base/CMakeLists.txt +++ b/plugins/base/CMakeLists.txt @@ -89,6 +89,8 @@ target_sources( macro-condition-filter.hpp macro-condition-folder.cpp macro-condition-folder.hpp + macro-condition-game-capture.cpp + macro-condition-game-capture.hpp macro-condition-hotkey.cpp macro-condition-hotkey.hpp macro-condition-idle.cpp diff --git a/plugins/base/macro-condition-game-capture.cpp b/plugins/base/macro-condition-game-capture.cpp new file mode 100644 index 000000000..a21871fee --- /dev/null +++ b/plugins/base/macro-condition-game-capture.cpp @@ -0,0 +1,150 @@ +#include "macro-condition-game-capture.hpp" +#include "layout-helpers.hpp" + +namespace advss { + +const std::string MacroConditionGameCapture::id = "game_capture"; + +bool MacroConditionGameCapture::_registered = MacroConditionFactory::Register( + MacroConditionGameCapture::id, + {MacroConditionGameCapture::Create, + MacroConditionGameCaptureEdit::Create, + "AdvSceneSwitcher.condition.gameCapture"}); + +bool MacroConditionGameCapture::CheckCondition() +{ + auto source = OBSGetStrongRef(_source.GetSource()); + if (source != _lastSource) { + SetupSignalHandler(source); + return false; + } + + std::lock_guard lock(_mtx); + if (_hooked) { + SetTempVarValue("title", _title); + SetTempVarValue("class", _class); + SetTempVarValue("executable", _executable); + } + + return _hooked; +} + +bool MacroConditionGameCapture::Save(obs_data_t *obj) const +{ + MacroCondition::Save(obj); + _source.Save(obj); + return true; +} + +bool MacroConditionGameCapture::Load(obs_data_t *obj) +{ + MacroCondition::Load(obj); + _source.Load(obj); + return true; +} + +void MacroConditionGameCapture::HookedSignalReceived(void *data, calldata_t *cd) +{ + auto condition = static_cast(data); + std::lock_guard lock(condition->_mtx); + if (!calldata_get_string(cd, "title", &condition->_title)) { + condition->_title = ""; + } + if (!calldata_get_string(cd, "class", &condition->_class)) { + condition->_class = ""; + } + if (!calldata_get_string(cd, "executable", &condition->_executable)) { + condition->_executable = ""; + } + condition->_hooked = true; +} + +void MacroConditionGameCapture::UnhookedSignalReceived(void *data, calldata_t *) +{ + auto condition = static_cast(data); + std::lock_guard lock(condition->_mtx); + condition->_hooked = false; +} + +void MacroConditionGameCapture::SetupTempVars() +{ + MacroCondition::SetupTempVars(); + AddTempvar( + "title", + obs_module_text("AdvSceneSwitcher.tempVar.gameCapture.title"), + obs_module_text( + "AdvSceneSwitcher.tempVar.gameCapture.title.description")); + AddTempvar( + "class", + obs_module_text("AdvSceneSwitcher.tempVar.gameCapture.class"), + obs_module_text( + "AdvSceneSwitcher.tempVar.gameCapture.class.description")); + AddTempvar( + "executable", + obs_module_text( + "AdvSceneSwitcher.tempVar.gameCapture.executable"), + obs_module_text( + "AdvSceneSwitcher.tempVar.gameCapture.executable.description")); +} + +void MacroConditionGameCapture::SetupSignalHandler(obs_source_t *source) +{ + signal_handler_t *sh = obs_source_get_signal_handler(source); + _hookSignal = OBSSignal(sh, "hooked", HookedSignalReceived, this); + _hookSignal = OBSSignal(sh, "unhooked", UnhookedSignalReceived, this); +} + +static QStringList getGameCaptureSourceNames() +{ + auto sourceEnum = [](void *param, obs_source_t *source) -> bool /* -- */ + { + QStringList *list = reinterpret_cast(param); + + if (strcmp(obs_source_get_unversioned_id(source), + "game_capture") == 0) { + *list << obs_source_get_name(source); + } + return true; + }; + + QStringList list; + obs_enum_sources(sourceEnum, &list); + return list; +} + +MacroConditionGameCaptureEdit::MacroConditionGameCaptureEdit( + QWidget *parent, std::shared_ptr entryData) + : QWidget(parent), + _sources(new SourceSelectionWidget(this, QStringList(), true)) +{ + auto sources = getGameCaptureSourceNames(); + sources.sort(); + _sources->SetSourceNameList(sources); + + QWidget::connect(_sources, + SIGNAL(SourceChanged(const SourceSelection &)), this, + SLOT(SourceChanged(const SourceSelection &))); + + auto layout = new QHBoxLayout; + PlaceWidgets( + obs_module_text("AdvSceneSwitcher.condition.gameCapture.entry"), + layout, {{"{{sources}}", _sources}}); + setLayout(layout); + + _entryData = entryData; + UpdateEntryData(); + _loading = false; +} + +void MacroConditionGameCaptureEdit::SourceChanged(const SourceSelection &source) +{ + GUARD_LOADING_AND_LOCK(); + _entryData->_source = source; +} + +void MacroConditionGameCaptureEdit::UpdateEntryData() +{ + _sources->SetSource(_entryData->_source); +} + +} // namespace advss diff --git a/plugins/base/macro-condition-game-capture.hpp b/plugins/base/macro-condition-game-capture.hpp new file mode 100644 index 000000000..97af046a4 --- /dev/null +++ b/plugins/base/macro-condition-game-capture.hpp @@ -0,0 +1,71 @@ +#pragma once +#include "macro-condition-edit.hpp" +#include "source-selection.hpp" + +#include +#include + +namespace advss { + +class MacroConditionGameCapture : public MacroCondition { +public: + MacroConditionGameCapture(Macro *m) : MacroCondition(m) {} + bool CheckCondition(); + 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); + } + + SourceSelection _source; + +private: + static void HookedSignalReceived(void *data, calldata_t *); + static void UnhookedSignalReceived(void *data, calldata_t *); + + void SetupTempVars(); + void SetupSignalHandler(obs_source_t *source); + + obs_source_t *_lastSource = nullptr; + OBSSignal _hookSignal; + + bool _hooked = false; + const char *_title = ""; + const char *_class = ""; + const char *_executable = ""; + std::mutex _mtx; + + static bool _registered; + static const std::string id; +}; + +class MacroConditionGameCaptureEdit : public QWidget { + Q_OBJECT + +public: + MacroConditionGameCaptureEdit( + QWidget *parent, + std::shared_ptr cond = nullptr); + void UpdateEntryData(); + static QWidget *Create(QWidget *parent, + std::shared_ptr cond) + { + return new MacroConditionGameCaptureEdit( + parent, + std::dynamic_pointer_cast( + cond)); + } + +private slots: + void SourceChanged(const SourceSelection &); + +private: + SourceSelectionWidget *_sources; + std::shared_ptr _entryData; + + bool _loading = true; +}; + +} // namespace advss