mirror of
https://github.com/WarmUpTill/SceneSwitcher.git
synced 2026-08-12 04:11:06 -05:00
Add Stream Deck condition type
The Stream Deck plugin to enable communication with the Advanced Scene Switcher can be found here: https://github.com/WarmUpTill/advanced-scene-switcher-streamdeck-plugin
This commit is contained in:
403
plugins/stream-deck/macro-condition-stream-deck.cpp
Normal file
403
plugins/stream-deck/macro-condition-stream-deck.cpp
Normal file
@@ -0,0 +1,403 @@
|
||||
#include "macro-condition-stream-deck.hpp"
|
||||
#include "layout-helpers.hpp"
|
||||
#include "websocket-api.hpp"
|
||||
|
||||
#include <obs.hpp>
|
||||
|
||||
namespace advss {
|
||||
|
||||
const std::string MacroConditionStreamdeck::id = "streamdeck";
|
||||
|
||||
bool MacroConditionStreamdeck::_registered = MacroConditionFactory::Register(
|
||||
MacroConditionStreamdeck::id,
|
||||
{MacroConditionStreamdeck::Create, MacroConditionStreamdeckEdit::Create,
|
||||
"AdvSceneSwitcher.condition.streamDeck"});
|
||||
|
||||
constexpr char streamDeckMessage[] = "StreamDeckKeyEvent";
|
||||
|
||||
static StreamDeckMessageDispatcher messageDispatcher;
|
||||
|
||||
static bool setup();
|
||||
static bool setupDone = setup();
|
||||
static void receiveStreamDeckMessage(obs_data_t *request_data, obs_data_t *);
|
||||
|
||||
bool setup()
|
||||
{
|
||||
RegisterWebsocketRequest(streamDeckMessage, receiveStreamDeckMessage);
|
||||
return true;
|
||||
}
|
||||
|
||||
static void printParseError(obs_data_t *data)
|
||||
{
|
||||
blog(LOG_INFO, "received unexpected stream deck message: %s",
|
||||
obs_data_get_json(data) ? obs_data_get_json(data) : "''");
|
||||
}
|
||||
|
||||
void receiveStreamDeckMessage(obs_data_t *request_data, obs_data_t *)
|
||||
{
|
||||
StreamDeckMessage message;
|
||||
OBSDataAutoRelease data = obs_data_get_obj(request_data, "data");
|
||||
if (!data) {
|
||||
printParseError(request_data);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!obs_data_has_user_value(data, "isKeyDownEvent")) {
|
||||
printParseError(request_data);
|
||||
return;
|
||||
}
|
||||
message.keyDown = obs_data_get_bool(data, "isKeyDownEvent");
|
||||
|
||||
OBSDataAutoRelease coordinates = obs_data_get_obj(data, "coordinates");
|
||||
if (!coordinates) {
|
||||
printParseError(request_data);
|
||||
return;
|
||||
}
|
||||
|
||||
message.row = obs_data_get_int(coordinates, "row");
|
||||
message.column = obs_data_get_int(coordinates, "column");
|
||||
|
||||
OBSDataAutoRelease settings = obs_data_get_obj(data, "settings");
|
||||
if (!settings) {
|
||||
printParseError(request_data);
|
||||
return;
|
||||
}
|
||||
message.data = obs_data_get_string(settings, "data");
|
||||
|
||||
messageDispatcher.DispatchMessage(message);
|
||||
}
|
||||
|
||||
StreamDeckMessageBuffer RegisterForStreamDeckMessages()
|
||||
{
|
||||
return messageDispatcher.RegisterClient();
|
||||
}
|
||||
|
||||
MacroConditionStreamdeck::MacroConditionStreamdeck(Macro *m)
|
||||
: MacroCondition(m, true)
|
||||
{
|
||||
_messageBuffer = RegisterForStreamDeckMessages();
|
||||
}
|
||||
|
||||
bool MacroConditionStreamdeck::MessageMatches(const StreamDeckMessage &message)
|
||||
{
|
||||
const bool keyStateMatches = !_pattern.checkKeyState ||
|
||||
((message.keyDown && _pattern.keyDown) ||
|
||||
(!message.keyDown && !message.keyDown));
|
||||
const bool positionMatches = !_pattern.checkPosition ||
|
||||
(message.row == _pattern.row &&
|
||||
message.column == _pattern.column);
|
||||
const bool dataMatches =
|
||||
!_pattern.checkData ||
|
||||
(_pattern.regex.Enabled()
|
||||
? _pattern.regex.Matches(message.data, _pattern.data)
|
||||
: message.data == std::string(_pattern.data));
|
||||
return keyStateMatches && positionMatches && dataMatches;
|
||||
}
|
||||
|
||||
bool MacroConditionStreamdeck::CheckCondition()
|
||||
{
|
||||
while (!_messageBuffer->Empty()) {
|
||||
auto message = _messageBuffer->ConsumeMessage();
|
||||
if (!message) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!message->keyDown) {
|
||||
_lastMatchingKeyIsStillPressed = false;
|
||||
}
|
||||
|
||||
if (MessageMatches(*message)) {
|
||||
_lastMatchingKeyIsStillPressed = message->keyDown;
|
||||
SetTempVarValues(*message);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (_lastMatchingKeyIsStillPressed) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool MacroConditionStreamdeck::Save(obs_data_t *obj) const
|
||||
{
|
||||
MacroCondition::Save(obj);
|
||||
_pattern.Save(obj);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MacroConditionStreamdeck::Load(obs_data_t *obj)
|
||||
{
|
||||
MacroCondition::Load(obj);
|
||||
_pattern.Load(obj);
|
||||
return true;
|
||||
}
|
||||
|
||||
void MacroConditionStreamdeck::StreamDeckMessagePattern::Save(
|
||||
obs_data_t *obj) const
|
||||
{
|
||||
OBSDataAutoRelease dataObj = obs_data_create();
|
||||
obs_data_set_bool(dataObj, "checkKeyState", checkKeyState);
|
||||
obs_data_set_bool(dataObj, "keyDown", keyDown);
|
||||
obs_data_set_bool(dataObj, "checkPosition", checkPosition);
|
||||
row.Save(dataObj, "row");
|
||||
column.Save(dataObj, "column");
|
||||
obs_data_set_bool(dataObj, "checkData", checkData);
|
||||
data.Save(dataObj, "data");
|
||||
regex.Save(dataObj);
|
||||
obs_data_set_obj(obj, "pattern", dataObj);
|
||||
}
|
||||
|
||||
void MacroConditionStreamdeck::StreamDeckMessagePattern::Load(obs_data_t *obj)
|
||||
{
|
||||
OBSDataAutoRelease dataObj = obs_data_get_obj(obj, "pattern");
|
||||
checkKeyState = obs_data_get_bool(dataObj, "checkKeyState");
|
||||
keyDown = obs_data_get_bool(dataObj, "keyDown");
|
||||
checkPosition = obs_data_get_bool(dataObj, "checkPosition");
|
||||
row.Load(dataObj, "row");
|
||||
column.Load(dataObj, "column");
|
||||
checkData = obs_data_get_bool(dataObj, "checkData");
|
||||
data.Load(dataObj, "data");
|
||||
regex.Load(dataObj);
|
||||
obs_data_set_obj(obj, "pattern", dataObj);
|
||||
}
|
||||
|
||||
void MacroConditionStreamdeck::SetTempVarValues(const StreamDeckMessage &message)
|
||||
{
|
||||
SetTempVarValue("keyPressed", std::to_string(message.keyDown));
|
||||
SetTempVarValue("row", std::to_string(message.row));
|
||||
SetTempVarValue("column", std::to_string(message.column));
|
||||
SetTempVarValue("data", message.data);
|
||||
}
|
||||
|
||||
void MacroConditionStreamdeck::SetupTempVars()
|
||||
{
|
||||
AddTempvar(
|
||||
"keyPressed",
|
||||
obs_module_text(
|
||||
"AdvSceneSwitcher.tempVar.streamDeck.keyPressed"),
|
||||
obs_module_text(
|
||||
"AdvSceneSwitcher.tempVar.streamDeck.keyPressed.description"));
|
||||
AddTempvar("row",
|
||||
obs_module_text("AdvSceneSwitcher.tempVar.streamDeck.row"));
|
||||
AddTempvar(
|
||||
"column",
|
||||
obs_module_text("AdvSceneSwitcher.tempVar.streamDeck.column"));
|
||||
AddTempvar(
|
||||
"data",
|
||||
obs_module_text("AdvSceneSwitcher.tempVar.streamDeck.data"),
|
||||
obs_module_text(
|
||||
"AdvSceneSwitcher.tempVar.streamDeck.data.description"));
|
||||
}
|
||||
|
||||
MacroConditionStreamdeckEdit::MacroConditionStreamdeckEdit(
|
||||
QWidget *parent, std::shared_ptr<MacroConditionStreamdeck> entryData)
|
||||
: QWidget(parent),
|
||||
_checkKeyState(new QCheckBox(obs_module_text(
|
||||
"AdvSceneSwitcher.condition.streamDeck.checkKeyState"))),
|
||||
_keyState(new QComboBox(this)),
|
||||
_checkPosition(new QCheckBox(obs_module_text(
|
||||
"AdvSceneSwitcher.condition.streamDeck.checkKeyPosition"))),
|
||||
_row(new VariableSpinBox(this)),
|
||||
_column(new VariableSpinBox(this)),
|
||||
_checkData(new QCheckBox(obs_module_text(
|
||||
"AdvSceneSwitcher.condition.streamDeck.checkData"))),
|
||||
_data(new VariableTextEdit(this)),
|
||||
_regex(new RegexConfigWidget(this)),
|
||||
_listen(new QPushButton(
|
||||
obs_module_text(
|
||||
"AdvSceneSwitcher.condition.streamDeck.startListen"),
|
||||
this))
|
||||
{
|
||||
_keyState->addItem(
|
||||
obs_module_text(
|
||||
"AdvSceneSwitcher.condition.streamDeck.keyState.pressed"),
|
||||
true);
|
||||
_keyState->addItem(
|
||||
obs_module_text(
|
||||
"AdvSceneSwitcher.condition.streamDeck.keyState.released"),
|
||||
false);
|
||||
|
||||
connect(_checkKeyState, &QCheckBox::stateChanged, this,
|
||||
&MacroConditionStreamdeckEdit::CheckKeyStateChanged);
|
||||
connect(_keyState, QOverload<int>::of(&QComboBox::currentIndexChanged),
|
||||
this, &MacroConditionStreamdeckEdit::KeyStateChanged);
|
||||
connect(_checkPosition, &QCheckBox::stateChanged, this,
|
||||
&MacroConditionStreamdeckEdit::CheckPositionChanged);
|
||||
connect(_row,
|
||||
static_cast<void (VariableSpinBox::*)(const IntVariable &)>(
|
||||
&VariableSpinBox::NumberVariableChanged),
|
||||
this, &MacroConditionStreamdeckEdit::RowChanged);
|
||||
connect(_column,
|
||||
static_cast<void (VariableSpinBox::*)(const IntVariable &)>(
|
||||
&VariableSpinBox::NumberVariableChanged),
|
||||
this, &MacroConditionStreamdeckEdit::ColumnChanged);
|
||||
connect(_checkData, &QCheckBox::stateChanged, this,
|
||||
&MacroConditionStreamdeckEdit::CheckDataChanged);
|
||||
connect(_regex, &RegexConfigWidget::RegexConfigChanged, this,
|
||||
&MacroConditionStreamdeckEdit::RegexChanged);
|
||||
connect(_data, &VariableTextEdit::textChanged, this,
|
||||
&MacroConditionStreamdeckEdit::DataChanged);
|
||||
connect(_listen, &QPushButton::clicked, this,
|
||||
&MacroConditionStreamdeckEdit::ListenClicked);
|
||||
|
||||
auto keyStateLayout = new QHBoxLayout();
|
||||
keyStateLayout->addWidget(_checkKeyState);
|
||||
keyStateLayout->addWidget(_keyState);
|
||||
keyStateLayout->addStretch();
|
||||
|
||||
auto positionLayout = new QHBoxLayout();
|
||||
positionLayout->addWidget(_row);
|
||||
positionLayout->addWidget(_column);
|
||||
|
||||
auto dataLayout = new QHBoxLayout();
|
||||
dataLayout->addWidget(_checkData);
|
||||
dataLayout->addWidget(_regex);
|
||||
dataLayout->addStretch();
|
||||
|
||||
auto layout = new QVBoxLayout();
|
||||
layout->addLayout(keyStateLayout);
|
||||
layout->addWidget(_checkPosition);
|
||||
layout->addLayout(positionLayout);
|
||||
layout->addLayout(dataLayout);
|
||||
layout->addWidget(_data);
|
||||
layout->addWidget(_listen);
|
||||
layout->addWidget(new QLabel(obs_module_text(
|
||||
"AdvSceneSwitcher.condition.streamDeck.pluginDownload")));
|
||||
setLayout(layout);
|
||||
|
||||
_entryData = entryData;
|
||||
UpdateEntryData();
|
||||
_loading = false;
|
||||
}
|
||||
|
||||
void MacroConditionStreamdeckEdit::UpdateEntryData()
|
||||
{
|
||||
if (!_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
_checkKeyState->setChecked(_entryData->_pattern.checkKeyState);
|
||||
_keyState->setCurrentIndex(
|
||||
_keyState->findData(_entryData->_pattern.keyDown));
|
||||
_checkPosition->setChecked(_entryData->_pattern.checkPosition);
|
||||
_row->SetValue(_entryData->_pattern.row);
|
||||
_column->SetValue(_entryData->_pattern.column);
|
||||
_checkData->setChecked(_entryData->_pattern.checkData);
|
||||
_data->setPlainText(_entryData->_pattern.data);
|
||||
_regex->SetRegexConfig(_entryData->_pattern.regex);
|
||||
|
||||
SetWidgetVisibility();
|
||||
}
|
||||
|
||||
void MacroConditionStreamdeckEdit::CheckKeyStateChanged(int state)
|
||||
{
|
||||
GUARD_LOADING_AND_LOCK();
|
||||
_entryData->_pattern.checkKeyState = state;
|
||||
SetWidgetVisibility();
|
||||
}
|
||||
|
||||
void MacroConditionStreamdeckEdit::KeyStateChanged(int index)
|
||||
{
|
||||
GUARD_LOADING_AND_LOCK();
|
||||
_entryData->_pattern.keyDown = _keyState->itemData(index).toBool();
|
||||
}
|
||||
|
||||
void MacroConditionStreamdeckEdit::CheckPositionChanged(int state)
|
||||
{
|
||||
GUARD_LOADING_AND_LOCK();
|
||||
_entryData->_pattern.checkPosition = state;
|
||||
SetWidgetVisibility();
|
||||
}
|
||||
|
||||
void MacroConditionStreamdeckEdit::RowChanged(const IntVariable &value)
|
||||
{
|
||||
GUARD_LOADING_AND_LOCK();
|
||||
_entryData->_pattern.row = value;
|
||||
}
|
||||
|
||||
void MacroConditionStreamdeckEdit::ColumnChanged(const IntVariable &value)
|
||||
{
|
||||
GUARD_LOADING_AND_LOCK();
|
||||
_entryData->_pattern.column = value;
|
||||
}
|
||||
|
||||
void MacroConditionStreamdeckEdit::CheckDataChanged(int state)
|
||||
{
|
||||
GUARD_LOADING_AND_LOCK();
|
||||
_entryData->_pattern.checkData = state;
|
||||
SetWidgetVisibility();
|
||||
}
|
||||
|
||||
void MacroConditionStreamdeckEdit::RegexChanged(const RegexConfig ®ex)
|
||||
{
|
||||
GUARD_LOADING_AND_LOCK();
|
||||
_entryData->_pattern.regex = regex;
|
||||
}
|
||||
|
||||
void MacroConditionStreamdeckEdit::DataChanged()
|
||||
{
|
||||
GUARD_LOADING_AND_LOCK();
|
||||
_entryData->_pattern.data = _data->toPlainText().toStdString();
|
||||
}
|
||||
|
||||
void MacroConditionStreamdeckEdit::ListenClicked()
|
||||
{
|
||||
_isListening = !_isListening;
|
||||
|
||||
_keyState->setDisabled(_isListening);
|
||||
_row->setDisabled(_isListening);
|
||||
_column->setDisabled(_isListening);
|
||||
_data->setDisabled(_isListening);
|
||||
_regex->setDisabled(_isListening);
|
||||
|
||||
if (_isListening) {
|
||||
_messageBuffer = RegisterForStreamDeckMessages();
|
||||
_listen->setText(obs_module_text(
|
||||
"AdvSceneSwitcher.condition.streamDeck.stopListen"));
|
||||
} else {
|
||||
_updateListenSettings.stop();
|
||||
_messageBuffer.reset();
|
||||
_listen->setText(obs_module_text(
|
||||
"AdvSceneSwitcher.condition.streamDeck.startListen"));
|
||||
return;
|
||||
}
|
||||
|
||||
connect(&_updateListenSettings, &QTimer::timeout, this, [this]() {
|
||||
if (_messageBuffer->Empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
StreamDeckMessage lastMessageInBuffer;
|
||||
while (!_messageBuffer->Empty()) {
|
||||
auto message = _messageBuffer->ConsumeMessage();
|
||||
if (!message) {
|
||||
continue;
|
||||
}
|
||||
lastMessageInBuffer = *message;
|
||||
}
|
||||
|
||||
_keyState->setCurrentIndex(
|
||||
_keyState->findData(lastMessageInBuffer.keyDown));
|
||||
_row->SetFixedValue(lastMessageInBuffer.row);
|
||||
_column->SetFixedValue(lastMessageInBuffer.column);
|
||||
_data->setPlainText(lastMessageInBuffer.data);
|
||||
});
|
||||
|
||||
_updateListenSettings.start(300);
|
||||
}
|
||||
|
||||
void MacroConditionStreamdeckEdit::SetWidgetVisibility()
|
||||
{
|
||||
_keyState->setVisible(_checkKeyState->isChecked());
|
||||
_row->setVisible(_checkPosition->isChecked());
|
||||
_column->setVisible(_checkPosition->isChecked());
|
||||
_data->setVisible(_checkData->isChecked());
|
||||
_regex->setVisible(_checkData->isChecked());
|
||||
|
||||
adjustSize();
|
||||
updateGeometry();
|
||||
}
|
||||
|
||||
} // namespace advss
|
||||
Reference in New Issue
Block a user