Add game capture condition

This commit is contained in:
WarmUpTill 2024-12-12 19:36:55 +01:00 committed by WarmUpTill
parent ef5cf41d34
commit 4e51b56b9b
4 changed files with 282 additions and 0 deletions

View File

@ -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="<html><head/><body><p>The Stream Deck plugin can be found <a href=\"https://github.com/WarmUpTill/advanced-scene-switcher-streamdeck-plugin/releases\"><span style=\" text-decoration: underline; color:#268bd2;\">here on GitHub</span></a>.</p></body></html>"
AdvSceneSwitcher.condition.gameCapture="Game capture"
AdvSceneSwitcher.condition.gameCapture.entry="{{sources}}hooked a game."
AdvSceneSwitcher.condition.screenshot="Screenshot"
AdvSceneSwitcher.condition.screenshot.entry="A screenshot was taken"
@ -1980,6 +1982,13 @@ AdvSceneSwitcher.tempVar.queue.running.description="Returns \"true\" if the queu
AdvSceneSwitcher.tempVar.screenshot.lastScreenshotPath="Last screenshot path"
AdvSceneSwitcher.tempVar.screenshot.lastScreenshotPath.description="The filesystem path to the last screenshot taken."
AdvSceneSwitcher.tempVar.gameCapture.title="Title"
AdvSceneSwitcher.tempVar.gameCapture.title.description="Window title of the application captured by the source."
AdvSceneSwitcher.tempVar.gameCapture.class="Class"
AdvSceneSwitcher.tempVar.gameCapture.class.description="Window class of the application captured by the source."
AdvSceneSwitcher.tempVar.gameCapture.executable="Executable"
AdvSceneSwitcher.tempVar.gameCapture.executable.description="Executable name of the application captured by the source."
AdvSceneSwitcher.selectScene="--select scene--"
AdvSceneSwitcher.selectPreviousScene="Previous Scene"
AdvSceneSwitcher.selectCurrentScene="Current Scene"

View File

@ -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

View File

@ -0,0 +1,197 @@
#include "macro-condition-game-capture.hpp"
#include "layout-helpers.hpp"
namespace advss {
const std::string MacroConditionGameCapture::id = "game_capture";
#ifdef _WIN32
bool MacroConditionGameCapture::_registered = MacroConditionFactory::Register(
MacroConditionGameCapture::id,
{MacroConditionGameCapture::Create,
MacroConditionGameCaptureEdit::Create,
"AdvSceneSwitcher.condition.gameCapture"});
#else
bool MacroConditionGameCapture::_registered =
false; // For now the 'game_capture' source only exists on Windows
#endif
bool MacroConditionGameCapture::CheckCondition()
{
auto source = OBSGetStrongRef(_source.GetSource());
if (source != _lastSource) {
SetupSignalHandler(source);
return false;
}
std::lock_guard<std::mutex> 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);
SetupSignalHandler(OBSGetStrongRef(_source.GetSource()));
return true;
}
void MacroConditionGameCapture::GetCalldataInfo(calldata_t *cd)
{
const char *title = "";
if (!calldata_get_string(cd, "title", &title)) {
blog(LOG_WARNING, "%s failed to get title", __func__);
}
_title = title;
const char *className = "";
if (!calldata_get_string(cd, "class", &className)) {
blog(LOG_WARNING, "%s failed to get class", __func__);
}
_class = className;
const char *executable = "";
if (!calldata_get_string(cd, "executable", &executable)) {
blog(LOG_WARNING, "%s failed to get executable", __func__);
}
_executable = executable;
}
void MacroConditionGameCapture::HookedSignalReceived(void *data, calldata_t *cd)
{
auto condition = static_cast<MacroConditionGameCapture *>(data);
std::lock_guard<std::mutex> lock(condition->_mtx);
condition->GetCalldataInfo(cd);
condition->_hooked = true;
}
void MacroConditionGameCapture::UnhookedSignalReceived(void *data, calldata_t *)
{
auto condition = static_cast<MacroConditionGameCapture *>(data);
std::lock_guard<std::mutex> 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);
_unhookSignal = OBSSignal(sh, "unhooked", UnhookedSignalReceived, this);
_lastSource = source;
SetupInitialState(source);
}
void MacroConditionGameCapture::SetupInitialState(obs_source_t *source)
{
std::lock_guard<std::mutex> lock(_mtx);
_hooked = false;
if (!source) {
return;
}
calldata_t cd;
calldata_init(&cd);
auto ph = obs_source_get_proc_handler(source);
if (!proc_handler_call(ph, "get_hooked", &cd)) {
blog(LOG_WARNING,
"%s failed to call proc_handler for 'get_hooked'",
__func__);
return;
}
if (!calldata_get_bool(&cd, "hooked", &_hooked)) {
blog(LOG_WARNING, "%s failed to get hooked state", __func__);
}
GetCalldataInfo(&cd);
calldata_free(&cd);
}
static QStringList getGameCaptureSourceNames()
{
auto sourceEnum = [](void *param, obs_source_t *source) -> bool /* -- */
{
QStringList *list = reinterpret_cast<QStringList *>(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<MacroConditionGameCapture> 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

View File

@ -0,0 +1,74 @@
#pragma once
#include "macro-condition-edit.hpp"
#include "source-selection.hpp"
#include <QWidget>
#include <QComboBox>
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<MacroCondition> Create(Macro *m)
{
return std::make_shared<MacroConditionGameCapture>(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);
void SetupInitialState(obs_source_t *source);
void GetCalldataInfo(calldata_t *cd);
obs_source_t *_lastSource = nullptr;
OBSSignal _hookSignal;
OBSSignal _unhookSignal;
bool _hooked = false;
std::string _title = "";
std::string _class = "";
std::string _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<MacroConditionGameCapture> cond = nullptr);
void UpdateEntryData();
static QWidget *Create(QWidget *parent,
std::shared_ptr<MacroCondition> cond)
{
return new MacroConditionGameCaptureEdit(
parent,
std::dynamic_pointer_cast<MacroConditionGameCapture>(
cond));
}
private slots:
void SourceChanged(const SourceSelection &);
private:
SourceSelectionWidget *_sources;
std::shared_ptr<MacroConditionGameCapture> _entryData;
bool _loading = true;
};
} // namespace advss