Compare commits

...

11 Commits

Author SHA1 Message Date
WarmUpTill
ba7886c8c1 Fix variables of nested macros' actions not being resolved 2026-06-16 20:43:09 +02:00
WarmUpTill
801268d08b Fix nested macro actions being executed twice when added to a queue 2026-06-16 20:29:11 +02:00
WarmUpTill
7dfd9ddcb5 Print plugin enabled/disabled summary during CMake configure
Some checks are pending
debian-build / build (push) Waiting to run
Push to master / Check Formatting 🔍 (push) Waiting to run
Push to master / Build Project 🧱 (push) Waiting to run
Push to master / Create Release 🛫 (push) Blocked by required conditions
2026-06-16 17:40:22 +02:00
WarmUpTill
6215c39024 Don't fail cmake configure step if openvr deps are not met 2026-06-16 17:40:22 +02:00
WarmUpTill
1c856911e8 Set default value for CMAKE_GENERATOR_PLATFORM on Windows 2026-06-16 17:40:22 +02:00
WarmUpTill
ed17c2ac6d Disconnect Twitch chat and event connection on plugin stop
Some checks failed
debian-build / build (push) Has been cancelled
Push to master / Check Formatting 🔍 (push) Has been cancelled
Push to master / Build Project 🧱 (push) Has been cancelled
Push to master / Create Release 🛫 (push) Has been cancelled
2026-06-13 22:05:32 +02:00
WarmUpTill
1ce332a91d Track all kWin wayland windows 2026-06-13 22:01:52 +02:00
WarmUpTill
2aa27fc051 Fix window conditions not triggering for native Wayland windows
When KWin compat is active, GetWindows() only returned XWayland windows
since it relied on X11's _NET_CLIENT_LIST. Native Wayland windows were
invisible to conditions even though GetCurrentWindowTitle() reported them
correctly via the KWin DBus notifier.
2026-06-13 22:01:52 +02:00
WarmUpTill
31b239d5dc Focus segment type selection when new segment is added
Some checks failed
debian-build / build (push) Has been cancelled
Push to master / Check Formatting 🔍 (push) Has been cancelled
Push to master / Build Project 🧱 (push) Has been cancelled
Push to master / Create Release 🛫 (push) Has been cancelled
2026-06-11 18:25:29 +02:00
WarmUpTill
7c3779e287 Sort macro export dialog resources alphabetically and add scrollbars 2026-06-11 18:25:29 +02:00
WarmUpTill
3f13c33b10 Fix crash on transition start in studio mode 2026-06-11 18:25:29 +02:00
23 changed files with 308 additions and 66 deletions

View File

@@ -157,7 +157,9 @@ else()
set_target_properties(${target} PROPERTIES INSTALL_RPATH set_target_properties(${target} PROPERTIES INSTALL_RPATH
"$ORIGIN:$ORIGIN/..") "$ORIGIN:$ORIGIN/..")
endif() endif()
message(STATUS "ADVSS: ENABLED PLUGIN ${target}") get_filename_component(_plugin_dir_name "${CMAKE_CURRENT_SOURCE_DIR}" NAME)
set_property(GLOBAL APPEND PROPERTY ADVSS_PLUGINS_ENABLED
${_plugin_dir_name})
endfunction() endfunction()
function(install_advss_plugin_dependency_target target dep) function(install_advss_plugin_dependency_target target dep)

View File

@@ -6,7 +6,11 @@ include(buildspec_common)
# _check_dependencies_windows: Set up Windows slice for _check_dependencies # _check_dependencies_windows: Set up Windows slice for _check_dependencies
function(_check_dependencies_windows) function(_check_dependencies_windows)
set(arch ${CMAKE_GENERATOR_PLATFORM}) if(CMAKE_GENERATOR_PLATFORM)
set(arch ${CMAKE_GENERATOR_PLATFORM})
else()
set(arch x64)
endif()
set(platform windows-${arch}) set(platform windows-${arch})
set(dependencies_dir "${CMAKE_CURRENT_SOURCE_DIR}/.deps") set(dependencies_dir "${CMAKE_CURRENT_SOURCE_DIR}/.deps")

View File

@@ -294,6 +294,30 @@ std::vector<WindowInfo> GetWindows(const WindowQueryOptions &options)
result.emplace_back(std::move(info)); result.emplace_back(std::move(info));
} }
// When KWin compat is active, native Wayland windows are not tracked by
// X11 and therefore do not appear in the list above. Merge in any window
// reported by KWin that is not already present (matched by title) so that
// title and focus conditions work for native Wayland windows.
if (KWin) {
for (const auto &[pid, title] :
FocusNotifier::getWindowList()) {
bool found = false;
for (const auto &info : result) {
if (info.title == title) {
found = true;
break;
}
}
if (!found) {
WindowInfo waylandWindow;
waylandWindow.title = title;
waylandWindow.focused =
(title == foregroundTitle);
result.emplace_back(std::move(waylandWindow));
}
}
}
return result; return result;
} }

View File

@@ -12,35 +12,82 @@
namespace advss { namespace advss {
std::mutex FocusNotifier::_mutex;
int FocusNotifier::activePID = -1; int FocusNotifier::activePID = -1;
std::string FocusNotifier::activeTitle = {}; std::string FocusNotifier::activeTitle = {};
std::map<std::string, std::pair<int, std::string>> FocusNotifier::_windows;
int FocusNotifier::getActiveWindowPID() int FocusNotifier::getActiveWindowPID()
{ {
std::lock_guard<std::mutex> lock(_mutex);
return activePID; return activePID;
} }
std::string FocusNotifier::getActiveWindowTitle() std::string FocusNotifier::getActiveWindowTitle()
{ {
std::lock_guard<std::mutex> lock(_mutex);
return activeTitle; return activeTitle;
} }
std::vector<std::pair<int, std::string>> FocusNotifier::getWindowList()
{
std::lock_guard<std::mutex> lock(_mutex);
std::vector<std::pair<int, std::string>> result;
result.reserve(_windows.size());
for (const auto &entry : _windows) {
result.emplace_back(entry.second);
}
return result;
}
void FocusNotifier::focusChanged(const int pid) void FocusNotifier::focusChanged(const int pid)
{ {
std::lock_guard<std::mutex> lock(_mutex);
activePID = pid; activePID = pid;
} }
void FocusNotifier::focusTitle(const QString &title) void FocusNotifier::focusTitle(const QString &title)
{ {
std::lock_guard<std::mutex> lock(_mutex);
activeTitle = title.toStdString(); activeTitle = title.toStdString();
} }
void FocusNotifier::focusUpdate(const int pid, const QString &title)
{
std::lock_guard<std::mutex> lock(_mutex);
activePID = pid;
activeTitle = title.toStdString();
}
void FocusNotifier::windowAdded(const QString &id, const int pid,
const QString &title)
{
std::lock_guard<std::mutex> lock(_mutex);
_windows[id.toStdString()] = {pid, title.toStdString()};
}
void FocusNotifier::windowRemoved(const QString &id)
{
std::lock_guard<std::mutex> lock(_mutex);
_windows.erase(id.toStdString());
}
void FocusNotifier::windowTitleChanged(const QString &id, const QString &title)
{
std::lock_guard<std::mutex> lock(_mutex);
auto it = _windows.find(id.toStdString());
if (it != _windows.end()) {
it->second.second = title.toStdString();
}
}
bool isKWinAvailable() bool isKWinAvailable()
{ {
const QDBusConnectionInterface *interface = const QDBusConnectionInterface *interface =
QDBusConnection::sessionBus().interface(); QDBusConnection::sessionBus().interface();
if (!interface) if (!interface) {
return false; return false;
}
const QStringList services = const QStringList services =
interface->registeredServiceNames().value(); interface->registeredServiceNames().value();
@@ -53,25 +100,32 @@ bool startKWinScript(QString &scriptObjectPath)
"/tmp/AdvancedSceneSwitcher/KWinFocusNotifier.js"; "/tmp/AdvancedSceneSwitcher/KWinFocusNotifier.js";
const QString script = const QString script =
R"(workspace.windowActivated.connect(function(client) { R"(var adss = "com.github.AdvancedSceneSwitcher";
if (!client) return; var adssPath = "/com/github/AdvancedSceneSwitcher";
if (!client.pid) return;
if (!client.caption) return;
callDBus( function trackWindow(window) {
"com.github.AdvancedSceneSwitcher", var id = window.internalId.toString();
"/com/github/AdvancedSceneSwitcher", callDBus(adss, adssPath, adss, "windowAdded", id, window.pid, window.caption);
"com.github.AdvancedSceneSwitcher", window.captionChanged.connect(function() {
"focusChanged", callDBus(adss, adssPath, adss, "windowTitleChanged", id, window.caption);
client.pid });
); }
callDBus(
"com.github.AdvancedSceneSwitcher", var windows = workspace.windowList();
"/com/github/AdvancedSceneSwitcher", for (var i = 0; i < windows.length; i++) {
"com.github.AdvancedSceneSwitcher", trackWindow(windows[i]);
"focusTitle", }
client.caption
); workspace.windowAdded.connect(trackWindow);
workspace.windowRemoved.connect(function(window) {
callDBus(adss, adssPath, adss, "windowRemoved", window.internalId.toString());
});
workspace.windowActivated.connect(function(client) {
callDBus(adss, adssPath, adss, "focusUpdate",
client ? client.pid : 0,
client ? client.caption : "");
}))"; }))";
if (const QDir dir; !dir.mkpath(QFileInfo(scriptPath).absolutePath())) { if (const QDir dir; !dir.mkpath(QFileInfo(scriptPath).absolutePath())) {

View File

@@ -2,7 +2,11 @@
#include <QObject> #include <QObject>
#include <QString> #include <QString>
#include <map>
#include <mutex>
#include <string> #include <string>
#include <utility>
#include <vector>
namespace advss { namespace advss {
@@ -10,18 +14,27 @@ class FocusNotifier final : public QObject {
Q_OBJECT Q_OBJECT
Q_CLASSINFO("D-Bus Interface", "com.github.AdvancedSceneSwitcher") Q_CLASSINFO("D-Bus Interface", "com.github.AdvancedSceneSwitcher")
static std::mutex _mutex;
static int activePID; static int activePID;
static std::string activeTitle; static std::string activeTitle;
// Maps KWin internal window ID to {pid, title}
static std::map<std::string, std::pair<int, std::string>> _windows;
public: public:
using QObject::QObject; using QObject::QObject;
static int getActiveWindowPID(); static int getActiveWindowPID();
static std::string getActiveWindowTitle(); static std::string getActiveWindowTitle();
static std::vector<std::pair<int, std::string>> getWindowList();
public slots: public slots:
void focusChanged(const int pid); void focusChanged(const int pid);
void focusTitle(const QString &title); void focusTitle(const QString &title);
void focusUpdate(const int pid, const QString &title);
void windowAdded(const QString &id, const int pid,
const QString &title);
void windowRemoved(const QString &id);
void windowTitleChanged(const QString &id, const QString &title);
}; };
bool isKWinAvailable(); bool isKWinAvailable();

View File

@@ -164,4 +164,9 @@ std::shared_ptr<MacroSegment> MacroActionEdit::Data() const
return *_entryData; return *_entryData;
} }
void MacroActionEdit::FocusTypeSelection()
{
_actionSelection->setFocus();
}
} // namespace advss } // namespace advss

View File

@@ -17,6 +17,7 @@ public:
std::shared_ptr<MacroAction> * = nullptr); std::shared_ptr<MacroAction> * = nullptr);
void SetupWidgets(bool basicSetup = false); void SetupWidgets(bool basicSetup = false);
void SetEntryData(std::shared_ptr<MacroAction> *); void SetEntryData(std::shared_ptr<MacroAction> *);
void FocusTypeSelection() override;
private slots: private slots:
void ActionSelectionChanged(const QString &text); void ActionSelectionChanged(const QString &text);

View File

@@ -263,6 +263,7 @@ bool MacroActionMacro::Load(obs_data_t *obj)
if (obs_data_has_user_value(obj, "nestedMacro")) { if (obs_data_has_user_value(obj, "nestedMacro")) {
OBSDataAutoRelease nestedMacroData = OBSDataAutoRelease nestedMacroData =
obs_data_get_obj(obj, "nestedMacro"); obs_data_get_obj(obj, "nestedMacro");
_nestedMacro = std::make_shared<Macro>();
_nestedMacro->Load(nestedMacroData); _nestedMacro->Load(nestedMacroData);
} }
@@ -309,6 +310,15 @@ void MacroActionMacro::ResolveVariablesToFixedValues()
{ {
_actionIndex.ResolveVariables(); _actionIndex.ResolveVariables();
_label.ResolveVariables(); _label.ResolveVariables();
if (_action == Action::NESTED_MACRO) {
for (auto &action : _nestedMacro->Actions()) {
action->ResolveVariablesToFixedValues();
}
for (auto &action : _nestedMacro->ElseActions()) {
action->ResolveVariablesToFixedValues();
}
}
} }
static void runActionsHelper(Macro *macro, bool runElseActions, bool setInputs, static void runActionsHelper(Macro *macro, bool runElseActions, bool setInputs,

View File

@@ -305,4 +305,9 @@ std::shared_ptr<MacroSegment> MacroConditionEdit::Data() const
return *_entryData; return *_entryData;
} }
void MacroConditionEdit::FocusTypeSelection()
{
_conditionSelection->setFocus();
}
} // namespace advss } // namespace advss

View File

@@ -43,6 +43,7 @@ public:
void SetRootNode(bool); void SetRootNode(bool);
void SetupWidgets(bool basicSetup = false); void SetupWidgets(bool basicSetup = false);
void SetEntryData(std::shared_ptr<MacroCondition> *); void SetEntryData(std::shared_ptr<MacroCondition> *);
void FocusTypeSelection() override;
private slots: private slots:
void LogicSelectionChanged(int idx); void LogicSelectionChanged(int idx);

View File

@@ -650,6 +650,18 @@ void MacroEdit::HighlightCondition(int idx, QColor color) const
ui->conditionsList->Highlight(idx, color); ui->conditionsList->Highlight(idx, color);
} }
void MacroEdit::ScrollAndFocusNewSegment(MacroSegmentList *list, int newIdx,
int *currentIdx)
{
QTimer::singleShot(0, this, [this, list, newIdx, currentIdx]() {
list->ensureWidgetVisible(list->WidgetAt(*currentIdx));
auto *widget = list->WidgetAt(newIdx);
if (widget) {
widget->FocusTypeSelection();
}
});
}
static void resetSegmentHighlights(MacroSegmentList *list) static void resetSegmentHighlights(MacroSegmentList *list)
{ {
MacroSegmentEdit *widget = nullptr; MacroSegmentEdit *widget = nullptr;
@@ -1087,10 +1099,7 @@ void MacroEdit::AddMacroAction(Macro *macro, int idx, const std::string &id,
ui->actionsList->SetHelpMsgVisible(false); ui->actionsList->SetHelpMsgVisible(false);
emit(MacroSegmentOrderChanged()); emit(MacroSegmentOrderChanged());
QTimer::singleShot(0, this, [this]() { ScrollAndFocusNewSegment(ui->actionsList, idx, &currentActionIdx);
ui->actionsList->ensureWidgetVisible(
ui->actionsList->WidgetAt(currentActionIdx));
});
} }
void MacroEdit::AddMacroAction(int idx) void MacroEdit::AddMacroAction(int idx)
@@ -1398,10 +1407,8 @@ void MacroEdit::AddMacroElseAction(Macro *macro, int idx, const std::string &id,
ui->elseActionsList->SetHelpMsgVisible(false); ui->elseActionsList->SetHelpMsgVisible(false);
emit(MacroSegmentOrderChanged()); emit(MacroSegmentOrderChanged());
QTimer::singleShot(0, this, [this]() { ScrollAndFocusNewSegment(ui->elseActionsList, idx,
ui->elseActionsList->ensureWidgetVisible( &currentElseActionIdx);
ui->elseActionsList->WidgetAt(currentElseActionIdx));
});
} }
void MacroEdit::AddMacroElseAction(int idx) void MacroEdit::AddMacroElseAction(int idx)
@@ -1611,10 +1618,7 @@ void MacroEdit::AddMacroCondition(Macro *macro, int idx, const std::string &id,
ui->conditionsList->SetHelpMsgVisible(false); ui->conditionsList->SetHelpMsgVisible(false);
emit(MacroSegmentOrderChanged()); emit(MacroSegmentOrderChanged());
QTimer::singleShot(0, this, [this]() { ScrollAndFocusNewSegment(ui->conditionsList, idx, &currentConditionIdx);
ui->conditionsList->ensureWidgetVisible(
ui->conditionsList->WidgetAt(currentConditionIdx));
});
} }
void MacroEdit::on_conditionAdd_clicked() void MacroEdit::on_conditionAdd_clicked()

View File

@@ -116,6 +116,8 @@ private:
void PopulateMacroActions(Macro &m, uint32_t afterIdx = 0); void PopulateMacroActions(Macro &m, uint32_t afterIdx = 0);
void PopulateMacroElseActions(Macro &m, uint32_t afterIdx = 0); void PopulateMacroElseActions(Macro &m, uint32_t afterIdx = 0);
void PopulateMacroConditions(Macro &m, uint32_t afterIdx = 0); void PopulateMacroConditions(Macro &m, uint32_t afterIdx = 0);
void ScrollAndFocusNewSegment(MacroSegmentList *list, int newIdx,
int *currentIdx);
void SetupMacroSegmentSelection(MacroSection type, int idx); void SetupMacroSegmentSelection(MacroSection type, int idx);
void void
SetupContextMenu(const QPoint &pos, SetupContextMenu(const QPoint &pos,

View File

@@ -1,11 +1,14 @@
#include "macro-export-import-dialog.hpp" #include "macro-export-import-dialog.hpp"
#include "macro-export-extensions.hpp" #include "macro-export-extensions.hpp"
#include "obs-module-helper.hpp" #include "obs-module-helper.hpp"
#include "section.hpp"
#include <obs.hpp> #include <obs.hpp>
#include <QDialogButtonBox> #include <QDialogButtonBox>
#include <QLabel> #include <QLabel>
#include <QLayout> #include <QLayout>
#include <QScrollArea>
#include <QSplitter>
#include <algorithm> #include <algorithm>
#include <numeric> #include <numeric>
@@ -44,10 +47,14 @@ static bool isValidData(const QString &json)
// Extension section // Extension section
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
Section *MacroExportImportDialog::BuildExtensionSection() QWidget *MacroExportImportDialog::BuildExtensionWidget()
{ {
auto outerSection = new Section(300, this); auto wrapper = new QWidget(this);
outerSection->AddHeaderWidget(new QLabel(obs_module_text( auto wrapperLayout = new QVBoxLayout(wrapper);
wrapperLayout->setContentsMargins(0, 0, 0, 0);
wrapperLayout->setSpacing(4);
wrapperLayout->addWidget(new QLabel(obs_module_text(
"AdvSceneSwitcher.macroTab.export.additionalContent"))); "AdvSceneSwitcher.macroTab.export.additionalContent")));
auto outerContent = new QWidget(); auto outerContent = new QWidget();
@@ -57,7 +64,6 @@ Section *MacroExportImportDialog::BuildExtensionSection()
const auto &extensions = GetMacroExportExtensions(); const auto &extensions = GetMacroExportExtensions();
// Build a sorted index list (alphabetical by translated display name).
std::vector<int> order(extensions.size()); std::vector<int> order(extensions.size());
std::iota(order.begin(), order.end(), 0); std::iota(order.begin(), order.end(), 0);
std::sort(order.begin(), order.end(), [&](int a, int b) { std::sort(order.begin(), order.end(), [&](int a, int b) {
@@ -81,6 +87,7 @@ Section *MacroExportImportDialog::BuildExtensionSection()
&MacroExportImportDialog::UpdateExportString); &MacroExportImportDialog::UpdateExportString);
outerLayout->addWidget(ui.mainCheck); outerLayout->addWidget(ui.mainCheck);
_extensionUIs.append(std::move(ui)); _extensionUIs.append(std::move(ui));
_extensionOrder.push_back(extIdx);
continue; continue;
} }
@@ -88,11 +95,18 @@ Section *MacroExportImportDialog::BuildExtensionSection()
// Section: the main checkbox sits in the header (next to the // Section: the main checkbox sits in the header (next to the
// toggle arrow) and the per-item checkboxes live in the // toggle arrow) and the per-item checkboxes live in the
// collapsible content area. // collapsible content area.
const auto items = ext.getExportItems(); auto items = ext.getExportItems();
if (items.isEmpty()) { if (items.isEmpty()) {
continue; continue;
} }
std::sort(items.begin(), items.end(),
[](const QPair<QString, QString> &a,
const QPair<QString, QString> &b) {
return QString::localeAwareCompare(
a.second, b.second) < 0;
});
auto innerSection = new Section(200, outerContent); auto innerSection = new Section(200, outerContent);
ui.mainCheck = ui.mainCheck =
@@ -132,13 +146,22 @@ Section *MacroExportImportDialog::BuildExtensionSection()
UpdateExportString(); UpdateExportString();
}); });
innerSection->SetContent(subWidget, false); innerSection->SetContent(subWidget, true);
outerLayout->addWidget(innerSection); outerLayout->addWidget(innerSection);
_extensionUIs.append(std::move(ui)); _extensionUIs.append(std::move(ui));
_extensionOrder.push_back(extIdx);
} }
outerSection->SetContent(outerContent, true); outerLayout->addStretch();
return outerSection;
auto scrollArea = new QScrollArea(wrapper);
scrollArea->setWidget(outerContent);
scrollArea->setWidgetResizable(true);
scrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
scrollArea->setFrameShape(QFrame::NoFrame);
wrapperLayout->addWidget(scrollArea);
return wrapper;
} }
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
@@ -180,12 +203,25 @@ MacroExportImportDialog::MacroExportImportDialog(Type type,
auto layout = new QVBoxLayout(this); auto layout = new QVBoxLayout(this);
if (type == Type::EXPORT_MACRO && !GetMacroExportExtensions().empty()) { if (type == Type::EXPORT_MACRO && !GetMacroExportExtensions().empty()) {
layout->addWidget(BuildExtensionSection()); auto splitter = new QSplitter(Qt::Vertical, this);
splitter->setChildrenCollapsible(false);
splitter->addWidget(BuildExtensionWidget());
auto textWidget = new QWidget(splitter);
auto textLayout = new QVBoxLayout(textWidget);
textLayout->setContentsMargins(0, 0, 0, 0);
textLayout->addWidget(label);
textLayout->addWidget(_importExportString);
textLayout->addWidget(_usePlainText);
splitter->addWidget(textWidget);
layout->addWidget(splitter);
} else {
layout->addWidget(label);
layout->addWidget(_importExportString);
layout->addWidget(_usePlainText);
} }
layout->addWidget(label);
layout->addWidget(_importExportString);
layout->addWidget(_usePlainText);
layout->addWidget(buttons); layout->addWidget(buttons);
setLayout(layout); setLayout(layout);
@@ -209,19 +245,26 @@ QString MacroExportImportDialog::BuildExportJson() const
} }
const auto &extensions = GetMacroExportExtensions(); const auto &extensions = GetMacroExportExtensions();
for (int i = 0; for (int i = 0; i < (int)_extensionUIs.size(); ++i) {
i < (int)extensions.size() && i < (int)_extensionUIs.size(); ++i) { const auto &ui = _extensionUIs[i];
if (!_extensionUIs[i].mainCheck->isChecked()) {
continue;
}
QStringList selectedIds; QStringList selectedIds;
for (const auto &[id, cb] : _extensionUIs[i].itemChecks) { if (ui.itemChecks.isEmpty()) {
if (cb->isChecked()) { if (!ui.mainCheck->isChecked()) {
selectedIds << id; continue;
}
} else {
for (const auto &[id, cb] : ui.itemChecks) {
if (cb->isChecked()) {
selectedIds << id;
}
}
if (selectedIds.isEmpty()) {
continue;
} }
} }
extensions[i].save(data, selectedIds);
extensions[_extensionOrder[i]].save(data, selectedIds);
} }
const char *json = obs_data_get_json(data); const char *json = obs_data_get_json(data);

View File

@@ -1,13 +1,12 @@
#pragma once #pragma once
#include "section.hpp"
#include <QCheckBox> #include <QCheckBox>
#include <QDialog> #include <QDialog>
#include <QList> #include <QList>
#include <QPlainTextEdit> #include <QPlainTextEdit>
#include <obs-data.h> #include <obs-data.h>
#include <vector>
namespace advss { namespace advss {
@@ -34,7 +33,7 @@ private:
QList<QPair<QString, QCheckBox *>> itemChecks; QList<QPair<QString, QCheckBox *>> itemChecks;
}; };
Section *BuildExtensionSection(); QWidget *BuildExtensionWidget();
QString BuildExportJson() const; QString BuildExportJson() const;
void RefreshExportText(); void RefreshExportText();
@@ -42,6 +41,7 @@ private:
QPlainTextEdit *_importExportString; QPlainTextEdit *_importExportString;
QCheckBox *_usePlainText; QCheckBox *_usePlainText;
QList<ExtensionUI> _extensionUIs; QList<ExtensionUI> _extensionUIs;
std::vector<int> _extensionOrder;
}; };
} // namespace advss } // namespace advss

View File

@@ -134,6 +134,7 @@ public:
void SetSelected(bool); void SetSelected(bool);
virtual std::shared_ptr<MacroSegment> Data() const = 0; virtual std::shared_ptr<MacroSegment> Data() const = 0;
virtual void SetupWidgets(bool basicSetup = false) = 0; virtual void SetupWidgets(bool basicSetup = false) = 0;
virtual void FocusTypeSelection() {}
void SetupVarMappings(MacroSegment *segment); void SetupVarMappings(MacroSegment *segment);

View File

@@ -1,7 +1,11 @@
macro(add_plugin plugin) macro(add_plugin plugin)
option(ENABLE_${plugin}_PLUGIN "Enable the ${plugin} plugin" ON) option(ENABLE_${plugin}_PLUGIN "Enable the ${plugin} plugin" ON)
if(ENABLE_${plugin}_PLUGIN) if(ENABLE_${plugin}_PLUGIN)
set_property(GLOBAL APPEND PROPERTY ADVSS_PLUGINS_PENDING ${plugin})
add_subdirectory(${plugin}) add_subdirectory(${plugin})
else()
set_property(GLOBAL APPEND PROPERTY ADVSS_PLUGINS_DISABLED_BY_OPTION
${plugin})
endif() endif()
endmacro() endmacro()
@@ -37,3 +41,32 @@ add_plugin(stream-deck)
add_plugin(twitch) add_plugin(twitch)
add_plugin(usb) add_plugin(usb)
add_plugin(video) add_plugin(video)
# ---------------------------------------------------------------------------- #
# Print plugin summary
get_property(_advss_enabled GLOBAL PROPERTY ADVSS_PLUGINS_ENABLED)
get_property(_advss_pending GLOBAL PROPERTY ADVSS_PLUGINS_PENDING)
get_property(_advss_disabled_opt GLOBAL
PROPERTY ADVSS_PLUGINS_DISABLED_BY_OPTION)
set(_advss_disabled_deps)
foreach(_p ${_advss_pending})
if(NOT _p IN_LIST _advss_enabled)
list(APPEND _advss_disabled_deps ${_p})
endif()
endforeach()
message(STATUS "")
message(STATUS "ADVSS Plugin Summary")
message(STATUS "====================")
foreach(_p ${_advss_enabled})
message(STATUS " ENABLED: ${_p}")
endforeach()
foreach(_p ${_advss_disabled_deps})
message(STATUS " DISABLED: ${_p} (missing dependencies)")
endforeach()
foreach(_p ${_advss_disabled_opt})
message(STATUS " DISABLED: ${_p} (ENABLE_${_p}_PLUGIN=OFF)")
endforeach()
message(STATUS "")

View File

@@ -163,7 +163,7 @@ void MacroConditionTransition::TransitionStarted(void *data, calldata_t *cd)
// Look up the original by name to get a comparable reference. // Look up the original by name to get a comparable reference.
const bool studioModeActive = const bool studioModeActive =
obs_frontend_preview_program_mode_active(); obs_frontend_preview_program_mode_active();
if (studioModeActive) { if (studioModeActive && startSource) {
OBSSourceAutoRelease startSourceByName = obs_get_source_by_name( OBSSourceAutoRelease startSourceByName = obs_get_source_by_name(
obs_source_get_name(startSource)); obs_source_get_name(startSource));
if (startSourceByName) { if (startSourceByName) {

View File

@@ -26,7 +26,7 @@ if(NOT OpenVR_DIR)
set(OpenVR_DIR ${ADVSS_SOURCE_DIR}/deps/openvr) set(OpenVR_DIR ${ADVSS_SOURCE_DIR}/deps/openvr)
endif() endif()
if(EXISTS ${OpenVR_DIR}) if(EXISTS "${OpenVR_DIR}/CMakeLists.txt")
set(SIZEOF_VOIDP ${CMAKE_SIZEOF_VOID_P}) set(SIZEOF_VOIDP ${CMAKE_SIZEOF_VOID_P})
if((NOT APPLE) AND (CMAKE_SIZEOF_VOID_P EQUAL 8)) if((NOT APPLE) AND (CMAKE_SIZEOF_VOID_P EQUAL 8))
set(PROCESSOR_ARCH "64") set(PROCESSOR_ARCH "64")

View File

@@ -3,6 +3,7 @@
#include "twitch-helpers.hpp" #include "twitch-helpers.hpp"
#include <log-helper.hpp> #include <log-helper.hpp>
#include <plugin-state-helpers.hpp>
#undef DispatchMessage #undef DispatchMessage
@@ -339,6 +340,24 @@ static constexpr std::string_view defaultURL =
std::map<TwitchChatConnection::ChatMapKey, std::weak_ptr<TwitchChatConnection>> std::map<TwitchChatConnection::ChatMapKey, std::weak_ptr<TwitchChatConnection>>
TwitchChatConnection::_chatMap = {}; TwitchChatConnection::_chatMap = {};
void TwitchChatConnection::DisconnectAll()
{
for (auto &[key, weakConn] : _chatMap) {
auto conn = weakConn.lock();
if (conn) {
conn->Disconnect();
}
}
}
static bool setupChatConnectionSupport()
{
AddStopStep(TwitchChatConnection::DisconnectAll);
return true;
}
static bool _chatSetup = setupChatConnectionSupport();
TwitchChatConnection::TwitchChatConnection(const TwitchToken &token, TwitchChatConnection::TwitchChatConnection(const TwitchToken &token,
const TwitchChannel &channel) const TwitchChannel &channel)
: QObject(nullptr), : QObject(nullptr),

View File

@@ -84,6 +84,7 @@ public:
static std::shared_ptr<TwitchChatConnection> static std::shared_ptr<TwitchChatConnection>
GetChatConnection(const TwitchToken &token, GetChatConnection(const TwitchToken &token,
const TwitchChannel &channel); const TwitchChannel &channel);
static void DisconnectAll();
[[nodiscard]] ChatMessageBuffer RegisterForMessages(); [[nodiscard]] ChatMessageBuffer RegisterForMessages();
[[nodiscard]] ChatMessageBuffer RegisterForWhispers(); [[nodiscard]] ChatMessageBuffer RegisterForWhispers();
void SendChatMessage(const std::string &message); void SendChatMessage(const std::string &message);

View File

@@ -3,6 +3,7 @@
#include "twitch-helpers.hpp" #include "twitch-helpers.hpp"
#include <log-helper.hpp> #include <log-helper.hpp>
#include <plugin-state-helpers.hpp>
#ifdef VERIFY_TIMESTAMPS #ifdef VERIFY_TIMESTAMPS
#include "date/tz.h" #include "date/tz.h"
@@ -71,6 +72,22 @@ void EventSub::UnregisterInstance()
_instances.erase(it, _instances.end()); _instances.erase(it, _instances.end());
} }
void EventSub::DisconnectAll()
{
std::lock_guard<std::mutex> lock(_instancesMtx);
for (auto *instance : _instances) {
instance->Disconnect();
}
}
static bool setupEventSubSupport()
{
AddStopStep(EventSub::DisconnectAll);
return true;
}
static bool _eventSubSetup = setupEventSubSupport();
void EventSub::ConnectThread() void EventSub::ConnectThread()
{ {
_client->reset(); _client->reset();

View File

@@ -53,6 +53,7 @@ public:
void Connect(); void Connect();
void Disconnect(); void Disconnect();
static void DisconnectAll();
[[nodiscard]] EventSubMessageBuffer RegisterForEvents(); [[nodiscard]] EventSubMessageBuffer RegisterForEvents();
bool SubscriptionIsActive(const std::string &id); bool SubscriptionIsActive(const std::string &id);
static std::string AddEventSubscription(std::shared_ptr<TwitchToken>, static std::string AddEventSubscription(std::shared_ptr<TwitchToken>,

View File

@@ -547,16 +547,18 @@ bool MacroConditionTwitch::CheckChatMessageRemove(TwitchToken &token)
bool MacroConditionTwitch::ChatConnectionIsSetup(TwitchToken &token) bool MacroConditionTwitch::ChatConnectionIsSetup(TwitchToken &token)
{ {
if (_chatConnection) {
_chatConnection->ConnectToChat();
return true;
}
_chatConnection =
TwitchChatConnection::GetChatConnection(token, _channel);
if (!_chatConnection) { if (!_chatConnection) {
_chatConnection = TwitchChatConnection::GetChatConnection(
token, _channel);
if (!_chatConnection) {
return false;
}
_chatBuffer = _chatConnection->RegisterForMessages();
return false; return false;
} }
return true; _chatBuffer = _chatConnection->RegisterForMessages();
return false;
} }
bool MacroConditionTwitch::HandleChatEvents( bool MacroConditionTwitch::HandleChatEvents(