mirror of
https://github.com/WarmUpTill/SceneSwitcher.git
synced 2026-08-29 12:45:28 -05:00
Compare commits
14 Commits
1.34.2
...
item-trans
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4c977a2e2f | ||
|
|
0f1273ab4c | ||
|
|
67f6b1b459 | ||
|
|
a04c51397d | ||
|
|
2073ef6ce9 | ||
|
|
7dfd9ddcb5 | ||
|
|
6215c39024 | ||
|
|
1c856911e8 | ||
|
|
ed17c2ac6d | ||
|
|
1ce332a91d | ||
|
|
2aa27fc051 | ||
|
|
31b239d5dc | ||
|
|
7c3779e287 | ||
|
|
3f13c33b10 |
@@ -157,7 +157,9 @@ else()
|
||||
set_target_properties(${target} PROPERTIES INSTALL_RPATH
|
||||
"$ORIGIN:$ORIGIN/..")
|
||||
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()
|
||||
|
||||
function(install_advss_plugin_dependency_target target dep)
|
||||
|
||||
@@ -6,7 +6,11 @@ include(buildspec_common)
|
||||
|
||||
# _check_dependencies_windows: Set up Windows slice for _check_dependencies
|
||||
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(dependencies_dir "${CMAKE_CURRENT_SOURCE_DIR}/.deps")
|
||||
|
||||
@@ -2546,6 +2546,7 @@ AdvSceneSwitcher.selectCurrentScene="Current Scene"
|
||||
AdvSceneSwitcher.selectPreviewScene="Preview Scene"
|
||||
AdvSceneSwitcher.selectAnyScene="Any Scene"
|
||||
AdvSceneSwitcher.currentTransition="Current Transition"
|
||||
AdvSceneSwitcher.noneTransition="None"
|
||||
AdvSceneSwitcher.anyTransition="Any Transition"
|
||||
AdvSceneSwitcher.selectTransition="--select transition--"
|
||||
AdvSceneSwitcher.selectWindow="--select window--"
|
||||
|
||||
@@ -294,6 +294,30 @@ std::vector<WindowInfo> GetWindows(const WindowQueryOptions &options)
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
@@ -12,35 +12,82 @@
|
||||
|
||||
namespace advss {
|
||||
|
||||
std::mutex FocusNotifier::_mutex;
|
||||
int FocusNotifier::activePID = -1;
|
||||
std::string FocusNotifier::activeTitle = {};
|
||||
std::map<std::string, std::pair<int, std::string>> FocusNotifier::_windows;
|
||||
|
||||
int FocusNotifier::getActiveWindowPID()
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(_mutex);
|
||||
return activePID;
|
||||
}
|
||||
|
||||
std::string FocusNotifier::getActiveWindowTitle()
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(_mutex);
|
||||
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)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(_mutex);
|
||||
activePID = pid;
|
||||
}
|
||||
|
||||
void FocusNotifier::focusTitle(const QString &title)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(_mutex);
|
||||
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()
|
||||
{
|
||||
const QDBusConnectionInterface *interface =
|
||||
QDBusConnection::sessionBus().interface();
|
||||
if (!interface)
|
||||
if (!interface) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const QStringList services =
|
||||
interface->registeredServiceNames().value();
|
||||
@@ -53,25 +100,32 @@ bool startKWinScript(QString &scriptObjectPath)
|
||||
"/tmp/AdvancedSceneSwitcher/KWinFocusNotifier.js";
|
||||
|
||||
const QString script =
|
||||
R"(workspace.windowActivated.connect(function(client) {
|
||||
if (!client) return;
|
||||
if (!client.pid) return;
|
||||
if (!client.caption) return;
|
||||
R"(var adss = "com.github.AdvancedSceneSwitcher";
|
||||
var adssPath = "/com/github/AdvancedSceneSwitcher";
|
||||
|
||||
callDBus(
|
||||
"com.github.AdvancedSceneSwitcher",
|
||||
"/com/github/AdvancedSceneSwitcher",
|
||||
"com.github.AdvancedSceneSwitcher",
|
||||
"focusChanged",
|
||||
client.pid
|
||||
);
|
||||
callDBus(
|
||||
"com.github.AdvancedSceneSwitcher",
|
||||
"/com/github/AdvancedSceneSwitcher",
|
||||
"com.github.AdvancedSceneSwitcher",
|
||||
"focusTitle",
|
||||
client.caption
|
||||
);
|
||||
function trackWindow(window) {
|
||||
var id = window.internalId.toString();
|
||||
callDBus(adss, adssPath, adss, "windowAdded", id, window.pid, window.caption);
|
||||
window.captionChanged.connect(function() {
|
||||
callDBus(adss, adssPath, adss, "windowTitleChanged", id, window.caption);
|
||||
});
|
||||
}
|
||||
|
||||
var windows = workspace.windowList();
|
||||
for (var i = 0; i < windows.length; i++) {
|
||||
trackWindow(windows[i]);
|
||||
}
|
||||
|
||||
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())) {
|
||||
|
||||
@@ -2,7 +2,11 @@
|
||||
|
||||
#include <QObject>
|
||||
#include <QString>
|
||||
#include <map>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
namespace advss {
|
||||
|
||||
@@ -10,18 +14,27 @@ class FocusNotifier final : public QObject {
|
||||
Q_OBJECT
|
||||
Q_CLASSINFO("D-Bus Interface", "com.github.AdvancedSceneSwitcher")
|
||||
|
||||
static std::mutex _mutex;
|
||||
static int activePID;
|
||||
static std::string activeTitle;
|
||||
// Maps KWin internal window ID to {pid, title}
|
||||
static std::map<std::string, std::pair<int, std::string>> _windows;
|
||||
|
||||
public:
|
||||
using QObject::QObject;
|
||||
|
||||
static int getActiveWindowPID();
|
||||
static std::string getActiveWindowTitle();
|
||||
static std::vector<std::pair<int, std::string>> getWindowList();
|
||||
|
||||
public slots:
|
||||
void focusChanged(const int pid);
|
||||
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();
|
||||
|
||||
@@ -164,4 +164,9 @@ std::shared_ptr<MacroSegment> MacroActionEdit::Data() const
|
||||
return *_entryData;
|
||||
}
|
||||
|
||||
void MacroActionEdit::FocusTypeSelection()
|
||||
{
|
||||
_actionSelection->setFocus();
|
||||
}
|
||||
|
||||
} // namespace advss
|
||||
|
||||
@@ -17,6 +17,7 @@ public:
|
||||
std::shared_ptr<MacroAction> * = nullptr);
|
||||
void SetupWidgets(bool basicSetup = false);
|
||||
void SetEntryData(std::shared_ptr<MacroAction> *);
|
||||
void FocusTypeSelection() override;
|
||||
|
||||
private slots:
|
||||
void ActionSelectionChanged(const QString &text);
|
||||
|
||||
@@ -263,6 +263,7 @@ bool MacroActionMacro::Load(obs_data_t *obj)
|
||||
if (obs_data_has_user_value(obj, "nestedMacro")) {
|
||||
OBSDataAutoRelease nestedMacroData =
|
||||
obs_data_get_obj(obj, "nestedMacro");
|
||||
_nestedMacro = std::make_shared<Macro>();
|
||||
_nestedMacro->Load(nestedMacroData);
|
||||
}
|
||||
|
||||
@@ -309,6 +310,15 @@ void MacroActionMacro::ResolveVariablesToFixedValues()
|
||||
{
|
||||
_actionIndex.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,
|
||||
|
||||
@@ -305,4 +305,9 @@ std::shared_ptr<MacroSegment> MacroConditionEdit::Data() const
|
||||
return *_entryData;
|
||||
}
|
||||
|
||||
void MacroConditionEdit::FocusTypeSelection()
|
||||
{
|
||||
_conditionSelection->setFocus();
|
||||
}
|
||||
|
||||
} // namespace advss
|
||||
|
||||
@@ -43,6 +43,7 @@ public:
|
||||
void SetRootNode(bool);
|
||||
void SetupWidgets(bool basicSetup = false);
|
||||
void SetEntryData(std::shared_ptr<MacroCondition> *);
|
||||
void FocusTypeSelection() override;
|
||||
|
||||
private slots:
|
||||
void LogicSelectionChanged(int idx);
|
||||
|
||||
@@ -650,6 +650,18 @@ void MacroEdit::HighlightCondition(int idx, QColor color) const
|
||||
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)
|
||||
{
|
||||
MacroSegmentEdit *widget = nullptr;
|
||||
@@ -1087,10 +1099,7 @@ void MacroEdit::AddMacroAction(Macro *macro, int idx, const std::string &id,
|
||||
ui->actionsList->SetHelpMsgVisible(false);
|
||||
emit(MacroSegmentOrderChanged());
|
||||
|
||||
QTimer::singleShot(0, this, [this]() {
|
||||
ui->actionsList->ensureWidgetVisible(
|
||||
ui->actionsList->WidgetAt(currentActionIdx));
|
||||
});
|
||||
ScrollAndFocusNewSegment(ui->actionsList, idx, ¤tActionIdx);
|
||||
}
|
||||
|
||||
void MacroEdit::AddMacroAction(int idx)
|
||||
@@ -1398,10 +1407,8 @@ void MacroEdit::AddMacroElseAction(Macro *macro, int idx, const std::string &id,
|
||||
ui->elseActionsList->SetHelpMsgVisible(false);
|
||||
emit(MacroSegmentOrderChanged());
|
||||
|
||||
QTimer::singleShot(0, this, [this]() {
|
||||
ui->elseActionsList->ensureWidgetVisible(
|
||||
ui->elseActionsList->WidgetAt(currentElseActionIdx));
|
||||
});
|
||||
ScrollAndFocusNewSegment(ui->elseActionsList, idx,
|
||||
¤tElseActionIdx);
|
||||
}
|
||||
|
||||
void MacroEdit::AddMacroElseAction(int idx)
|
||||
@@ -1611,10 +1618,7 @@ void MacroEdit::AddMacroCondition(Macro *macro, int idx, const std::string &id,
|
||||
ui->conditionsList->SetHelpMsgVisible(false);
|
||||
emit(MacroSegmentOrderChanged());
|
||||
|
||||
QTimer::singleShot(0, this, [this]() {
|
||||
ui->conditionsList->ensureWidgetVisible(
|
||||
ui->conditionsList->WidgetAt(currentConditionIdx));
|
||||
});
|
||||
ScrollAndFocusNewSegment(ui->conditionsList, idx, ¤tConditionIdx);
|
||||
}
|
||||
|
||||
void MacroEdit::on_conditionAdd_clicked()
|
||||
|
||||
@@ -116,6 +116,8 @@ private:
|
||||
void PopulateMacroActions(Macro &m, uint32_t afterIdx = 0);
|
||||
void PopulateMacroElseActions(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
|
||||
SetupContextMenu(const QPoint &pos,
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
#include "macro-export-import-dialog.hpp"
|
||||
#include "macro-export-extensions.hpp"
|
||||
#include "obs-module-helper.hpp"
|
||||
#include "section.hpp"
|
||||
|
||||
#include <obs.hpp>
|
||||
#include <QDialogButtonBox>
|
||||
#include <QLabel>
|
||||
#include <QLayout>
|
||||
#include <QScrollArea>
|
||||
#include <QSplitter>
|
||||
#include <algorithm>
|
||||
#include <numeric>
|
||||
|
||||
@@ -44,10 +47,14 @@ static bool isValidData(const QString &json)
|
||||
// Extension section
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
Section *MacroExportImportDialog::BuildExtensionSection()
|
||||
QWidget *MacroExportImportDialog::BuildExtensionWidget()
|
||||
{
|
||||
auto outerSection = new Section(300, this);
|
||||
outerSection->AddHeaderWidget(new QLabel(obs_module_text(
|
||||
auto wrapper = new QWidget(this);
|
||||
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")));
|
||||
|
||||
auto outerContent = new QWidget();
|
||||
@@ -57,7 +64,6 @@ Section *MacroExportImportDialog::BuildExtensionSection()
|
||||
|
||||
const auto &extensions = GetMacroExportExtensions();
|
||||
|
||||
// Build a sorted index list (alphabetical by translated display name).
|
||||
std::vector<int> order(extensions.size());
|
||||
std::iota(order.begin(), order.end(), 0);
|
||||
std::sort(order.begin(), order.end(), [&](int a, int b) {
|
||||
@@ -81,6 +87,7 @@ Section *MacroExportImportDialog::BuildExtensionSection()
|
||||
&MacroExportImportDialog::UpdateExportString);
|
||||
outerLayout->addWidget(ui.mainCheck);
|
||||
_extensionUIs.append(std::move(ui));
|
||||
_extensionOrder.push_back(extIdx);
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -88,11 +95,18 @@ Section *MacroExportImportDialog::BuildExtensionSection()
|
||||
// Section: the main checkbox sits in the header (next to the
|
||||
// toggle arrow) and the per-item checkboxes live in the
|
||||
// collapsible content area.
|
||||
const auto items = ext.getExportItems();
|
||||
auto items = ext.getExportItems();
|
||||
if (items.isEmpty()) {
|
||||
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);
|
||||
|
||||
ui.mainCheck =
|
||||
@@ -132,13 +146,22 @@ Section *MacroExportImportDialog::BuildExtensionSection()
|
||||
UpdateExportString();
|
||||
});
|
||||
|
||||
innerSection->SetContent(subWidget, false);
|
||||
innerSection->SetContent(subWidget, true);
|
||||
outerLayout->addWidget(innerSection);
|
||||
_extensionUIs.append(std::move(ui));
|
||||
_extensionOrder.push_back(extIdx);
|
||||
}
|
||||
|
||||
outerSection->SetContent(outerContent, true);
|
||||
return outerSection;
|
||||
outerLayout->addStretch();
|
||||
|
||||
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);
|
||||
|
||||
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);
|
||||
setLayout(layout);
|
||||
|
||||
@@ -209,19 +245,26 @@ QString MacroExportImportDialog::BuildExportJson() const
|
||||
}
|
||||
|
||||
const auto &extensions = GetMacroExportExtensions();
|
||||
for (int i = 0;
|
||||
i < (int)extensions.size() && i < (int)_extensionUIs.size(); ++i) {
|
||||
if (!_extensionUIs[i].mainCheck->isChecked()) {
|
||||
continue;
|
||||
}
|
||||
for (int i = 0; i < (int)_extensionUIs.size(); ++i) {
|
||||
const auto &ui = _extensionUIs[i];
|
||||
|
||||
QStringList selectedIds;
|
||||
for (const auto &[id, cb] : _extensionUIs[i].itemChecks) {
|
||||
if (cb->isChecked()) {
|
||||
selectedIds << id;
|
||||
if (ui.itemChecks.isEmpty()) {
|
||||
if (!ui.mainCheck->isChecked()) {
|
||||
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);
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
#pragma once
|
||||
|
||||
#include "section.hpp"
|
||||
|
||||
#include <QCheckBox>
|
||||
#include <QDialog>
|
||||
#include <QList>
|
||||
#include <QPlainTextEdit>
|
||||
|
||||
#include <obs-data.h>
|
||||
#include <vector>
|
||||
|
||||
namespace advss {
|
||||
|
||||
@@ -34,7 +33,7 @@ private:
|
||||
QList<QPair<QString, QCheckBox *>> itemChecks;
|
||||
};
|
||||
|
||||
Section *BuildExtensionSection();
|
||||
QWidget *BuildExtensionWidget();
|
||||
QString BuildExportJson() const;
|
||||
void RefreshExportText();
|
||||
|
||||
@@ -42,6 +41,7 @@ private:
|
||||
QPlainTextEdit *_importExportString;
|
||||
QCheckBox *_usePlainText;
|
||||
QList<ExtensionUI> _extensionUIs;
|
||||
std::vector<int> _extensionOrder;
|
||||
};
|
||||
|
||||
} // namespace advss
|
||||
|
||||
@@ -134,6 +134,7 @@ public:
|
||||
void SetSelected(bool);
|
||||
virtual std::shared_ptr<MacroSegment> Data() const = 0;
|
||||
virtual void SetupWidgets(bool basicSetup = false) = 0;
|
||||
virtual void FocusTypeSelection() {}
|
||||
|
||||
void SetupVarMappings(MacroSegment *segment);
|
||||
|
||||
|
||||
@@ -135,7 +135,7 @@ QStringList GetSourceNames()
|
||||
}
|
||||
|
||||
void PopulateTransitionSelection(QComboBox *sel, bool addCurrent, bool addAny,
|
||||
bool addSelect)
|
||||
bool addSelect, bool addNone)
|
||||
{
|
||||
|
||||
obs_frontend_source_list *transitions = new obs_frontend_source_list();
|
||||
@@ -168,6 +168,11 @@ void PopulateTransitionSelection(QComboBox *sel, bool addCurrent, bool addAny,
|
||||
addSelect ? 1 : 0,
|
||||
obs_module_text("AdvSceneSwitcher.anyTransition"));
|
||||
}
|
||||
if (addNone) {
|
||||
sel->insertItem(
|
||||
addSelect ? 1 : 0,
|
||||
obs_module_text("AdvSceneSwitcher.noneTransition"));
|
||||
}
|
||||
}
|
||||
|
||||
void PopulateWindowSelection(QComboBox *sel, bool addSelect)
|
||||
|
||||
@@ -19,7 +19,8 @@ EXPORT QStringList GetSourceNames();
|
||||
|
||||
EXPORT void PopulateTransitionSelection(QComboBox *sel, bool addCurrent = true,
|
||||
bool addAny = false,
|
||||
bool addSelect = true);
|
||||
bool addSelect = true,
|
||||
bool addNone = false);
|
||||
EXPORT void PopulateWindowSelection(QComboBox *sel, bool addSelect = true);
|
||||
void PopulateAudioSelection(QComboBox *sel, bool addSelect = true);
|
||||
void PopulateVideoSelection(QComboBox *sel, bool addMainOutput = false,
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
macro(add_plugin plugin)
|
||||
option(ENABLE_${plugin}_PLUGIN "Enable the ${plugin} plugin" ON)
|
||||
if(ENABLE_${plugin}_PLUGIN)
|
||||
set_property(GLOBAL APPEND PROPERTY ADVSS_PLUGINS_PENDING ${plugin})
|
||||
add_subdirectory(${plugin})
|
||||
else()
|
||||
set_property(GLOBAL APPEND PROPERTY ADVSS_PLUGINS_DISABLED_BY_OPTION
|
||||
${plugin})
|
||||
endif()
|
||||
endmacro()
|
||||
|
||||
@@ -37,3 +41,32 @@ add_plugin(stream-deck)
|
||||
add_plugin(twitch)
|
||||
add_plugin(usb)
|
||||
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 "")
|
||||
|
||||
@@ -160,12 +160,11 @@ static void attachRestoreContext(obs_sceneitem_t *item,
|
||||
|
||||
static void setSceneItemVisibility(obs_sceneitem_t *item,
|
||||
const bool setTransition,
|
||||
const OBSWeakSource &transitionWeak,
|
||||
const TransitionSelection &transitionSel,
|
||||
const bool setDuration,
|
||||
const Duration &duration,
|
||||
MacroActionSceneVisibility::Action action)
|
||||
{
|
||||
const OBSSourceAutoRelease transition = OBSGetStrongRef(transitionWeak);
|
||||
const bool itemIsVisible = obs_sceneitem_visible(item);
|
||||
|
||||
const OBSSource currentTransition =
|
||||
@@ -175,6 +174,16 @@ static void setSceneItemVisibility(obs_sceneitem_t *item,
|
||||
|
||||
OBSSource privateTransitionSource = nullptr;
|
||||
if (setTransition) {
|
||||
OBSSourceAutoRelease transition;
|
||||
if (transitionSel.GetType() ==
|
||||
TransitionSelection::Type::CURRENT) {
|
||||
transition = obs_source_get_ref(
|
||||
obs_sceneitem_get_transition(item,
|
||||
!itemIsVisible));
|
||||
} else {
|
||||
transition =
|
||||
OBSGetStrongRef(transitionSel.GetTransition());
|
||||
}
|
||||
privateTransitionSource = SetSceneItemTransition(
|
||||
item, transition, !itemIsVisible);
|
||||
} else {
|
||||
@@ -229,8 +238,7 @@ bool MacroActionSceneVisibility::PerformAction()
|
||||
{
|
||||
auto items = _source.GetSceneItems(_scene);
|
||||
for (const auto &item : items) {
|
||||
setSceneItemVisibility(item, _updateTransition,
|
||||
_transition.GetTransition(),
|
||||
setSceneItemVisibility(item, _updateTransition, _transition,
|
||||
_updateDuration, _duration, _action);
|
||||
}
|
||||
return true;
|
||||
@@ -340,7 +348,7 @@ MacroActionSceneVisibilityEdit::MacroActionSceneVisibilityEdit(
|
||||
},
|
||||
SceneItemSelectionWidget::NameClashMode::ALL)),
|
||||
_updateTransition(new QCheckBox(this)),
|
||||
_transitions(new TransitionSelectionWidget(this, false, false)),
|
||||
_transitions(new TransitionSelectionWidget(this, true, false, true)),
|
||||
_updateDuration(new QCheckBox(this)),
|
||||
_duration(new DurationSelection(this, false)),
|
||||
_durationLayout(new QHBoxLayout),
|
||||
@@ -474,8 +482,11 @@ void MacroActionSceneVisibilityEdit::ActionChanged(int value)
|
||||
|
||||
void MacroActionSceneVisibilityEdit::SetWidgetVisibility()
|
||||
{
|
||||
const auto transitionType = _entryData->_transition.GetType();
|
||||
const bool hideDurationSelection =
|
||||
_entryData->_updateTransition &&
|
||||
transitionType != TransitionSelection::Type::CURRENT &&
|
||||
transitionType != TransitionSelection::Type::NONE &&
|
||||
IsFixedLengthTransition(
|
||||
_entryData->_transition.GetTransition());
|
||||
|
||||
|
||||
@@ -406,15 +406,17 @@ void MacroActionTransitionEdit::SetWidgetVisibility()
|
||||
MacroActionTransition::Type::TBAR;
|
||||
const bool isReleaseTbar = _entryData->_type ==
|
||||
MacroActionTransition::Type::RELEASE_TBAR;
|
||||
_sources->setVisible(
|
||||
_entryData->_type == MacroActionTransition::Type::SOURCE_HIDE ||
|
||||
_entryData->_type == MacroActionTransition::Type::SOURCE_SHOW);
|
||||
const bool isSourceTransition =
|
||||
_entryData->_type == MacroActionTransition::Type::SOURCE_SHOW ||
|
||||
_entryData->_type == MacroActionTransition::Type::SOURCE_HIDE;
|
||||
_sources->setVisible(isSourceTransition);
|
||||
_scenes->setVisible(_entryData->_type !=
|
||||
MacroActionTransition::Type::SCENE &&
|
||||
!isTbar && !isReleaseTbar);
|
||||
SetLayoutVisible(_transitionLayout, !isTbar && !isReleaseTbar);
|
||||
SetLayoutVisible(_durationLayout, !isTbar && !isReleaseTbar);
|
||||
SetLayoutVisible(_tbarLayout, isTbar);
|
||||
_transitions->EnableNoneEntry(isSourceTransition);
|
||||
adjustSize();
|
||||
}
|
||||
|
||||
|
||||
@@ -163,7 +163,7 @@ void MacroConditionTransition::TransitionStarted(void *data, calldata_t *cd)
|
||||
// Look up the original by name to get a comparable reference.
|
||||
const bool studioModeActive =
|
||||
obs_frontend_preview_program_mode_active();
|
||||
if (studioModeActive) {
|
||||
if (studioModeActive && startSource) {
|
||||
OBSSourceAutoRelease startSourceByName = obs_get_source_by_name(
|
||||
obs_source_get_name(startSource));
|
||||
if (startSourceByName) {
|
||||
|
||||
@@ -48,6 +48,8 @@ OBSWeakSource TransitionSelection::GetTransition() const
|
||||
obs_source_release(source);
|
||||
return weakSource;
|
||||
}
|
||||
case Type::NONE:
|
||||
return nullptr;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -63,6 +65,8 @@ std::string TransitionSelection::ToString() const
|
||||
return obs_module_text("AdvSceneSwitcher.currentTransition");
|
||||
case Type::ANY:
|
||||
return obs_module_text("AdvSceneSwitcher.anyTransition");
|
||||
case Type::NONE:
|
||||
return obs_module_text("AdvSceneSwitcher.noneTransition");
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -70,14 +74,16 @@ std::string TransitionSelection::ToString() const
|
||||
}
|
||||
|
||||
TransitionSelectionWidget::TransitionSelectionWidget(QWidget *parent,
|
||||
bool current, bool any)
|
||||
bool current, bool any,
|
||||
bool none)
|
||||
: FilterComboBox(parent,
|
||||
obs_module_text("AdvSceneSwitcher.selectTransition")),
|
||||
_addCurrent(current),
|
||||
_addAny(any)
|
||||
_addAny(any),
|
||||
_addNone(none)
|
||||
{
|
||||
setDuplicatesEnabled(true);
|
||||
PopulateTransitionSelection(this, current, any, false);
|
||||
PopulateTransitionSelection(this, current, any, false, none);
|
||||
|
||||
QWidget::connect(this, SIGNAL(currentTextChanged(const QString &)),
|
||||
this, SLOT(SelectionChanged(const QString &)));
|
||||
@@ -86,8 +92,9 @@ TransitionSelectionWidget::TransitionSelectionWidget(QWidget *parent,
|
||||
void TransitionSelectionWidget::SetTransition(const TransitionSelection &t)
|
||||
{
|
||||
// Order of entries
|
||||
// 1. Any transition
|
||||
// 2. Current transition
|
||||
// 1. None transition
|
||||
// 2. Any transition
|
||||
// 3. Current transition
|
||||
// 4. Transitions
|
||||
|
||||
switch (t.GetType()) {
|
||||
@@ -102,6 +109,10 @@ void TransitionSelectionWidget::SetTransition(const TransitionSelection &t)
|
||||
setCurrentIndex(findText(QString::fromStdString(
|
||||
obs_module_text("AdvSceneSwitcher.anyTransition"))));
|
||||
break;
|
||||
case TransitionSelection::Type::NONE:
|
||||
setCurrentIndex(findText(QString::fromStdString(
|
||||
obs_module_text("AdvSceneSwitcher.noneTransition"))));
|
||||
break;
|
||||
default:
|
||||
setCurrentIndex(-1);
|
||||
break;
|
||||
@@ -120,6 +131,17 @@ void TransitionSelectionWidget::EnableAnyEntry(bool enable)
|
||||
Populate();
|
||||
}
|
||||
|
||||
void TransitionSelectionWidget::EnableNoneEntry(bool enable)
|
||||
{
|
||||
if (_addNone == enable) {
|
||||
return;
|
||||
}
|
||||
const auto selection = GetCurrentSelection();
|
||||
_addNone = enable;
|
||||
Populate();
|
||||
SetTransition(selection);
|
||||
}
|
||||
|
||||
void TransitionSelectionWidget::showEvent(QShowEvent *event)
|
||||
{
|
||||
FilterComboBox::showEvent(event);
|
||||
@@ -133,7 +155,7 @@ void TransitionSelectionWidget::Populate()
|
||||
{
|
||||
const QSignalBlocker blocker(this);
|
||||
clear();
|
||||
PopulateTransitionSelection(this, _addCurrent, _addAny);
|
||||
PopulateTransitionSelection(this, _addCurrent, _addAny, true, _addNone);
|
||||
}
|
||||
|
||||
static bool isFirstEntry(const QComboBox *l, QString name, int idx)
|
||||
@@ -163,6 +185,9 @@ TransitionSelection TransitionSelectionWidget::GetCurrentSelection() const
|
||||
if (IsAnyTransitionSelected(text)) {
|
||||
result._type = TransitionSelection::Type::ANY;
|
||||
}
|
||||
if (IsNoneTransitionSelected(text)) {
|
||||
result._type = TransitionSelection::Type::NONE;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@@ -187,6 +212,16 @@ bool TransitionSelectionWidget::IsAnyTransitionSelected(
|
||||
return false;
|
||||
}
|
||||
|
||||
bool TransitionSelectionWidget::IsNoneTransitionSelected(
|
||||
const QString &name) const
|
||||
{
|
||||
if (name == QString::fromStdString((obs_module_text(
|
||||
"AdvSceneSwitcher.noneTransition")))) {
|
||||
return isFirstEntry(this, name, currentIndex());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void TransitionSelectionWidget::SelectionChanged(const QString &)
|
||||
{
|
||||
emit TransitionChanged(GetCurrentSelection());
|
||||
|
||||
@@ -16,6 +16,7 @@ public:
|
||||
TRANSITION,
|
||||
CURRENT,
|
||||
ANY,
|
||||
NONE,
|
||||
};
|
||||
|
||||
Type GetType() const { return _type; }
|
||||
@@ -33,10 +34,11 @@ class TransitionSelectionWidget : public FilterComboBox {
|
||||
|
||||
public:
|
||||
TransitionSelectionWidget(QWidget *parent, bool current = true,
|
||||
bool any = false);
|
||||
bool any = false, bool none = false);
|
||||
void SetTransition(const TransitionSelection &);
|
||||
void EnableCurrentEntry(bool enable);
|
||||
void EnableAnyEntry(bool enable);
|
||||
void EnableNoneEntry(bool enable);
|
||||
|
||||
protected:
|
||||
void showEvent(QShowEvent *event) override;
|
||||
@@ -52,9 +54,11 @@ private:
|
||||
TransitionSelection GetCurrentSelection() const;
|
||||
bool IsCurrentTransitionSelected(const QString &name) const;
|
||||
bool IsAnyTransitionSelected(const QString &name) const;
|
||||
bool IsNoneTransitionSelected(const QString &name) const;
|
||||
|
||||
bool _addCurrent;
|
||||
bool _addAny;
|
||||
bool _addNone;
|
||||
};
|
||||
|
||||
} // namespace advss
|
||||
|
||||
@@ -26,7 +26,7 @@ if(NOT OpenVR_DIR)
|
||||
set(OpenVR_DIR ${ADVSS_SOURCE_DIR}/deps/openvr)
|
||||
endif()
|
||||
|
||||
if(EXISTS ${OpenVR_DIR})
|
||||
if(EXISTS "${OpenVR_DIR}/CMakeLists.txt")
|
||||
set(SIZEOF_VOIDP ${CMAKE_SIZEOF_VOID_P})
|
||||
if((NOT APPLE) AND (CMAKE_SIZEOF_VOID_P EQUAL 8))
|
||||
set(PROCESSOR_ARCH "64")
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include "twitch-helpers.hpp"
|
||||
|
||||
#include <log-helper.hpp>
|
||||
#include <plugin-state-helpers.hpp>
|
||||
|
||||
#undef DispatchMessage
|
||||
|
||||
@@ -339,6 +340,24 @@ static constexpr std::string_view defaultURL =
|
||||
std::map<TwitchChatConnection::ChatMapKey, std::weak_ptr<TwitchChatConnection>>
|
||||
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,
|
||||
const TwitchChannel &channel)
|
||||
: QObject(nullptr),
|
||||
|
||||
@@ -84,6 +84,7 @@ public:
|
||||
static std::shared_ptr<TwitchChatConnection>
|
||||
GetChatConnection(const TwitchToken &token,
|
||||
const TwitchChannel &channel);
|
||||
static void DisconnectAll();
|
||||
[[nodiscard]] ChatMessageBuffer RegisterForMessages();
|
||||
[[nodiscard]] ChatMessageBuffer RegisterForWhispers();
|
||||
void SendChatMessage(const std::string &message);
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include "twitch-helpers.hpp"
|
||||
|
||||
#include <log-helper.hpp>
|
||||
#include <plugin-state-helpers.hpp>
|
||||
|
||||
#ifdef VERIFY_TIMESTAMPS
|
||||
#include "date/tz.h"
|
||||
@@ -71,6 +72,22 @@ void EventSub::UnregisterInstance()
|
||||
_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()
|
||||
{
|
||||
_client->reset();
|
||||
|
||||
@@ -53,6 +53,7 @@ public:
|
||||
|
||||
void Connect();
|
||||
void Disconnect();
|
||||
static void DisconnectAll();
|
||||
[[nodiscard]] EventSubMessageBuffer RegisterForEvents();
|
||||
bool SubscriptionIsActive(const std::string &id);
|
||||
static std::string AddEventSubscription(std::shared_ptr<TwitchToken>,
|
||||
|
||||
@@ -547,16 +547,18 @@ bool MacroConditionTwitch::CheckChatMessageRemove(TwitchToken &token)
|
||||
|
||||
bool MacroConditionTwitch::ChatConnectionIsSetup(TwitchToken &token)
|
||||
{
|
||||
if (_chatConnection) {
|
||||
_chatConnection->ConnectToChat();
|
||||
return true;
|
||||
}
|
||||
|
||||
_chatConnection =
|
||||
TwitchChatConnection::GetChatConnection(token, _channel);
|
||||
if (!_chatConnection) {
|
||||
_chatConnection = TwitchChatConnection::GetChatConnection(
|
||||
token, _channel);
|
||||
if (!_chatConnection) {
|
||||
return false;
|
||||
}
|
||||
_chatBuffer = _chatConnection->RegisterForMessages();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
_chatBuffer = _chatConnection->RegisterForMessages();
|
||||
return false;
|
||||
}
|
||||
|
||||
bool MacroConditionTwitch::HandleChatEvents(
|
||||
|
||||
Reference in New Issue
Block a user