This commit is contained in:
WarmUpTill 2026-05-08 21:08:47 +02:00 committed by GitHub
commit 3a135de1b5
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
24 changed files with 177 additions and 22 deletions

View File

@ -99,7 +99,6 @@ AdvSceneSwitcher.macroList.deleted="gelöscht"
AdvSceneSwitcher.macroList.duplicate="\"%1\" ist bereits ausgewählt!"
; Macro Logic
AdvSceneSwitcher.logic.none="Eintrag ignorieren"
AdvSceneSwitcher.logic.and="Und"
AdvSceneSwitcher.logic.or="Oder"
AdvSceneSwitcher.logic.andNot="Und nicht"

View File

@ -306,7 +306,6 @@ AdvSceneSwitcher.macroList.deleted="deleted"
AdvSceneSwitcher.macroList.duplicate="\"%1\" is alreay selected!"
# Macro Logic
AdvSceneSwitcher.logic.none="Ignore entry"
AdvSceneSwitcher.logic.and="And"
AdvSceneSwitcher.logic.or="Or"
AdvSceneSwitcher.logic.andNot="And not"
@ -1076,8 +1075,8 @@ AdvSceneSwitcher.action.macro.type.runActions="Run macro actions"
AdvSceneSwitcher.action.macro.type.run.conditions.ignore="Do not consider condition state"
AdvSceneSwitcher.action.macro.type.run.conditions.true="Only if conditions evaluate to true"
AdvSceneSwitcher.action.macro.type.run.conditions.false="Only if conditions evaluate to false"
AdvSceneSwitcher.action.macro.type.run.updateConditionMatchState="Reevaluate the condition state before executing this action"
AdvSceneSwitcher.action.macro.type.run.updateConditionMatchState.help="The plugin operates in phases:\n * The phase evaluating the macro conditions\n * The phase running macro actions\nMacros executed before this particular action might have side effects on the condition state of macros.\nCheck this option if you want those side effects to be taken into account when evaluating the condition state."
AdvSceneSwitcher.action.macro.type.run.updateConditionMatchState="Check conditions now, even if macro is paused"
AdvSceneSwitcher.action.macro.type.run.updateConditionMatchState.help="The plugin operates in phases:\n * The phase evaluating the macro conditions\n * The phase running macro actions\nMacros executed before this particular action might have side effects on the condition state of macros.\nAdditionally, paused macros are assumed to not match their conditions.\nCheck this option to force a fresh condition evaluation before running this action, regardless of whether the macro is paused."
AdvSceneSwitcher.action.macro.type.run.actionType.regular="actions"
AdvSceneSwitcher.action.macro.type.run.actionType.else="else-actions"
AdvSceneSwitcher.action.macro.type.run.skipWhenPaused="Skip execution when macro is paused"

View File

@ -83,7 +83,6 @@ AdvSceneSwitcher.macroTab.highlightTrueConditions="Resaltar condiciones de la ma
AdvSceneSwitcher.macroTab.highlightPerformedActions="Resaltar acciones realizadas recientemente de la macro seleccionada actualmente"
; Lógica de macros
AdvSceneSwitcher.logic.none="Omitir entrada"
AdvSceneSwitcher.logic.and="Y"
AdvSceneSwitcher.logic.or="O"
AdvSceneSwitcher.logic.andNot="Y no"

View File

@ -131,7 +131,6 @@ AdvSceneSwitcher.macroList.deleted="supprimé"
AdvSceneSwitcher.macroList.duplicate="\"%1\" est déjà sélectionné !"
; Macro Logic
AdvSceneSwitcher.logic.none="Ignorer l'entrée"
AdvSceneSwitcher.logic.and="Et"
AdvSceneSwitcher.logic.or="Ou"
AdvSceneSwitcher.logic.andNot="Et pas"

View File

@ -275,7 +275,6 @@ AdvSceneSwitcher.macroList.deleted="削除"
AdvSceneSwitcher.macroList.duplicate="\"%1\" はすでに選択されています!"
# Macro Logic
AdvSceneSwitcher.logic.none="入力無視"
; AdvSceneSwitcher.logic.and="And"
; AdvSceneSwitcher.logic.or="Or"
; AdvSceneSwitcher.logic.andNot="And not"

View File

@ -239,7 +239,6 @@ AdvSceneSwitcher.macroList.deleted="excluída"
AdvSceneSwitcher.macroList.duplicate="\"%1\" já está selecionada!"
; Macro Logic
AdvSceneSwitcher.logic.none="Ignorar entrada"
AdvSceneSwitcher.logic.and="E"
AdvSceneSwitcher.logic.or="Ou"
AdvSceneSwitcher.logic.andNot="E não"

View File

@ -70,7 +70,6 @@ AdvSceneSwitcher.macroTab.name="Имя:"
AdvSceneSwitcher.macroTab.defaultname="Макрос %1"
AdvSceneSwitcher.macroTab.copy="Создать копию"
; Macro Logic
AdvSceneSwitcher.logic.none="Игнорировать вход"
AdvSceneSwitcher.logic.and="И"
AdvSceneSwitcher.logic.or="Или"
AdvSceneSwitcher.logic.andNot="И не"

View File

@ -78,7 +78,6 @@ AdvSceneSwitcher.macroTab.expandAll="Hepsini Genişlet"
AdvSceneSwitcher.macroTab.collapseAll="Hepsini Küçült"
; Macro Logic
AdvSceneSwitcher.logic.none="Girişi yoksay"
AdvSceneSwitcher.logic.and="Ve"
AdvSceneSwitcher.logic.or="Ya da"
AdvSceneSwitcher.logic.andNot="ve değil"

View File

@ -259,7 +259,6 @@ AdvSceneSwitcher.macroList.deleted="删除"
AdvSceneSwitcher.macroList.duplicate="\"%1\" 已选择!"
; Macro Logic
AdvSceneSwitcher.logic.none="忽略条件"
AdvSceneSwitcher.logic.and="且"
AdvSceneSwitcher.logic.or="或"
AdvSceneSwitcher.logic.andNot="且不"

View File

@ -506,6 +506,8 @@ bool SwitcherData::AnySceneTransitionStarted()
******************************************************************************/
extern "C" EXPORT void FreeSceneSwitcher()
{
switcher->Stop();
PlatformCleanup();
RunPluginCleanupSteps();

View File

@ -871,6 +871,8 @@ void MacroActionMacroEdit::SetWidgetVisibility()
} else {
_noConditionsWarning->setVisible(false);
}
_runMacroHelp->setVisible(action ==
MacroActionMacro::Action::RUN_MACRO);
_nestedMacro->setVisible(action ==
MacroActionMacro::Action::NESTED_MACRO);

View File

@ -5,6 +5,7 @@
#include "path-helpers.hpp"
#include "plugin-state-helpers.hpp"
#include "section.hpp"
#include "switch-button.hpp"
#include "ui-helpers.hpp"
#include "utility.hpp"
@ -102,12 +103,15 @@ MacroConditionEdit::MacroConditionEdit(
QWidget *parent, std::shared_ptr<MacroCondition> *entryData,
bool isRootCondition)
: MacroSegmentEdit(parent),
_enable(new SwitchButton()),
_logicSelection(new QComboBox()),
_conditionSelection(new FilterComboBox()),
_dur(new DurationModifierEdit()),
_entryData(entryData),
_isRoot(isRootCondition)
{
QWidget::connect(_enable, SIGNAL(checked(bool)), this,
SLOT(ConditionEnableChanged(bool)));
QWidget::connect(_logicSelection, SIGNAL(currentIndexChanged(int)),
this, SLOT(LogicSelectionChanged(int)));
QWidget::connect(_conditionSelection,
@ -122,6 +126,7 @@ MacroConditionEdit::MacroConditionEdit(
Logic::PopulateLogicTypeSelection(_logicSelection, isRootCondition);
populateConditionSelection(_conditionSelection);
_section->AddHeaderWidget(_enable);
_section->AddHeaderWidget(_logicSelection);
_section->AddHeaderWidget(_conditionSelection);
_section->AddHeaderWidget(_headerInfo);
@ -154,8 +159,17 @@ void MacroConditionEdit::LogicSelectionChanged(int idx)
const auto logic = static_cast<Logic::Type>(
_logicSelection->itemData(idx).toInt());
(*_entryData)->SetLogicType(logic);
}
SetEnableAppearance(logic != Logic::Type::NONE);
void MacroConditionEdit::ConditionEnableChanged(bool value)
{
if (_loading || !_entryData) {
return;
}
auto lock = LockContext();
(*_entryData)->SetEnabled(value);
SetDisableEffect(!value);
}
bool MacroConditionEdit::IsRootNode() const
@ -168,7 +182,9 @@ void MacroConditionEdit::SetLogicSelection()
const auto logic = (*_entryData)->GetLogicType();
_logicSelection->setCurrentIndex(
_logicSelection->findData(static_cast<int>(logic)));
SetEnableAppearance(logic != Logic::Type::NONE);
const bool enabled = (*_entryData)->Enabled();
_enable->setChecked(enabled);
SetEnableAppearance(enabled);
}
void MacroConditionEdit::SetRootNode(bool root)
@ -239,10 +255,12 @@ void MacroConditionEdit::ConditionSelectionChanged(const QString &text)
{
auto lock = LockContext();
auto logic = (*_entryData)->GetLogicType();
const bool enabled = (*_entryData)->Enabled();
_entryData->reset();
*_entryData = MacroConditionFactory::Create(id, macro);
(*_entryData)->SetIndex(idx);
(*_entryData)->SetLogicType(logic);
(*_entryData)->SetEnabled(enabled);
(*_entryData)->PostLoad();
RunAndClearPostLoadSteps();
}

View File

@ -8,6 +8,8 @@
namespace advss {
class SwitchButton;
class DurationModifierEdit : public QWidget {
Q_OBJECT
public:
@ -47,11 +49,13 @@ private slots:
void ConditionSelectionChanged(const QString &text);
void DurationChanged(const Duration &value);
void DurationModifierChanged(DurationModifier::Type m);
void ConditionEnableChanged(bool);
private:
void SetLogicSelection();
std::shared_ptr<MacroSegment> Data() const;
SwitchButton *_enable;
QComboBox *_logicSelection;
FilterComboBox *_conditionSelection;
DurationModifierEdit *_dur;

View File

@ -31,6 +31,10 @@ bool MacroCondition::Load(obs_data_t *obj)
{
MacroSegment::Load(obj);
_logic.Load(obj, "logic");
if (_logic.GetType() == Logic::Type::NONE) {
SetEnabled(false);
_logic.SetType(Logic::Type::AND);
}
_durationModifier.Load(obj);
return true;
}
@ -50,7 +54,8 @@ void MacroCondition::ValidateLogicSelection(bool isRootCondition,
return;
}
_logic.SetType(Logic::Type::NONE);
_logic.SetType(Logic::Type::AND);
SetEnabled(false);
blog(LOG_WARNING,
"setting invalid logic selection to 'ignore' for macro %s",
context);

View File

@ -80,7 +80,7 @@ bool MacroSegment::Save(obs_data_t *obj) const
obs_data_set_bool(data, "useCustomLabel", _useCustomLabel);
obs_data_set_string(data, "customLabel", _customLabel.c_str());
obs_data_set_bool(data, "enabled", _enabled);
obs_data_set_int(data, "version", 1);
obs_data_set_int(data, "version", 2);
OBSDataArrayAutoRelease mappingsArray = obs_data_array_create();
for (const auto &mapping : _varMappings) {
@ -112,6 +112,12 @@ bool MacroSegment::Load(obs_data_t *obj)
_enabled = obs_data_get_bool(obj, "enabled");
}
// Reset the previously unused "enabled" value for conditions to "true"
if (obs_data_get_int(data, "version") < 2 &&
obs_data_has_user_value(obj, "logic")) {
_enabled = true;
}
_varMappings.clear();
OBSDataArrayAutoRelease mappingsArray =
obs_data_get_array(data, "varMappings");

View File

@ -557,6 +557,10 @@ void AdvSceneSwitcher::HighlightOnChange() const
return;
}
if (macro->Paused()) {
return;
}
if (macro->ActionTriggerModePreventedActionsSince(
lastOnChangeHighlightCheckTime)) {
HighlightWidget(ui->actionTriggerMode, Qt::yellow,

View File

@ -157,6 +157,15 @@ bool Macro::CheckConditionHelper(
return conditionMatched;
};
if (!condition->Enabled()) {
vblog(LOG_INFO, "ignoring condition '%s' for '%s'",
condition->GetId().c_str(), _name.c_str());
if (!_useShortCircuitEvaluation) {
(void)evaluateCondition();
}
return _matched;
}
const auto logicType = condition->GetLogicType();
if (logicType == Logic::Type::NONE) {
vblog(LOG_INFO, "ignoring condition '%s' for '%s'",
@ -281,7 +290,7 @@ bool Macro::CheckConditions(bool ignorePause)
const bool hasActionsToExecute = _matched ? (_actions.size() > 0)
: (_elseActions.size() > 0);
if (!_actionModeMatch && hasActionsToExecute) {
if (!_actionModeMatch && hasActionsToExecute && !_paused) {
_lastActionRunModePreventTime =
std::chrono::high_resolution_clock::now();
}
@ -380,7 +389,7 @@ bool Macro::ShouldRunActions() const
!_paused && (_matched || _elseActions.size() > 0) &&
_actionModeMatch;
if (VerboseLoggingEnabled() && !_actionModeMatch) {
if (VerboseLoggingEnabled() && !_actionModeMatch && !_paused) {
if (_matched && _actions.size() > 0) {
blog(LOG_INFO, "skip actions for Macro %s (on change)",
_name.c_str());

View File

@ -8,7 +8,6 @@
namespace advss {
const std::map<Logic::Type, const char *> Logic::localeMap = {
{Logic::Type::NONE, {"AdvSceneSwitcher.logic.none"}},
{Logic::Type::AND, {"AdvSceneSwitcher.logic.and"}},
{Logic::Type::OR, {"AdvSceneSwitcher.logic.or"}},
{Logic::Type::AND_NOT, {"AdvSceneSwitcher.logic.andNot"}},
@ -67,7 +66,7 @@ void Logic::PopulateLogicTypeSelection(QComboBox *list, bool isRootCondition)
return typeValue < rootOffset;
}}
: std::function<bool(int)>{[](int typeValue) {
return typeValue >= rootOffset;
return typeValue > rootOffset;
}};
for (const auto &[type, name] : localeMap) {
const int typeValue = static_cast<int>(type);

View File

@ -75,6 +75,18 @@ static std::vector<std::function<void()>> &getStopSteps()
return steps;
}
static std::vector<std::function<void(obs_data_t *)>> &getEarlySaveSteps()
{
static std::vector<std::function<void(obs_data_t *)>> steps;
return steps;
}
static std::vector<std::function<void(obs_data_t *)>> &getEarlyLoadSteps()
{
static std::vector<std::function<void(obs_data_t *)>> steps;
return steps;
}
static std::vector<std::function<void(obs_data_t *)>> &getSaveSteps()
{
static std::vector<std::function<void(obs_data_t *)>> steps;
@ -109,6 +121,18 @@ void LoadPluginSettings(obs_data_t *obj)
GetSwitcher()->LoadSettings(obj);
}
void AddEarlySaveStep(std::function<void(obs_data_t *)> step)
{
std::lock_guard<std::mutex> lock(mutex);
getEarlySaveSteps().emplace_back(step);
}
void AddEarlyLoadStep(std::function<void(obs_data_t *)> step)
{
std::lock_guard<std::mutex> lock(mutex);
getEarlyLoadSteps().emplace_back(step);
}
void AddSaveStep(std::function<void(obs_data_t *)> step)
{
std::lock_guard<std::mutex> lock(mutex);
@ -136,6 +160,9 @@ void AddIntervalResetStep(std::function<void()> step)
void RunSaveSteps(obs_data_t *obj)
{
std::lock_guard<std::mutex> lock(mutex);
for (const auto &func : getEarlySaveSteps()) {
func(obj);
}
for (const auto &func : getSaveSteps()) {
func(obj);
}
@ -144,6 +171,9 @@ void RunSaveSteps(obs_data_t *obj)
void RunLoadSteps(obs_data_t *obj)
{
std::lock_guard<std::mutex> lock(mutex);
for (const auto &func : getEarlyLoadSteps()) {
func(obj);
}
for (const auto &func : getLoadSteps()) {
func(obj);
}

View File

@ -8,6 +8,8 @@ namespace advss {
void SavePluginSettings(obs_data_t *);
EXPORT void LoadPluginSettings(obs_data_t *);
void AddEarlySaveStep(std::function<void(obs_data_t *)>);
void AddEarlyLoadStep(std::function<void(obs_data_t *)>);
EXPORT void AddSaveStep(std::function<void(obs_data_t *)>);
EXPORT void AddLoadStep(std::function<void(obs_data_t *)>);
EXPORT void AddPostLoadStep(std::function<void()>);

View File

@ -63,7 +63,6 @@ static void load(obs_data_t *data)
{
tabSettings.Load(data, "tabSettings");
dockSettings.Load(data, "dockSettings");
enableDock(obs_data_get_bool(data, "addVariablesDock"));
}

View File

@ -20,8 +20,8 @@ static std::chrono::high_resolution_clock::time_point lastVariableChange{};
static bool setup()
{
AddSaveStep(SaveVariables);
AddLoadStep(LoadVariables);
AddEarlySaveStep(SaveVariables);
AddEarlyLoadStep(LoadVariables);
AddPluginCleanupStep([]() { variables.clear(); });
return true;
}

View File

@ -1,10 +1,12 @@
#include "platform-funcs.hpp"
#include "plugin-state-helpers.hpp"
#include <windows.h>
#include <UIAutomation.h>
#include <util/platform.h>
#include <TlHelp32.h>
#include <Psapi.h>
#include <memory>
#include <locale>
#include <codecvt>
#include <string>
@ -16,6 +18,9 @@
#include <QWidget>
#include <mutex>
#define ADVSS_WIDEN_(x) L##x
#define ADVSS_WIDEN(x) ADVSS_WIDEN_(x)
namespace advss {
#define MAX_SEARCH 1000
@ -500,9 +505,86 @@ int SecondsSinceLastInput()
return (getTime() - getLastInputTime()) / 1000;
}
static void addPluginFolderToSymbolPath()
{
// This runs after OBS_FRONTEND_EVENT_FINISHED_LOADING, which fires after
// obs_load_all_modules() completes. By that point OBS has already called
// reset_win32_symbol_paths() -> SymInitializeW(), so DbgHelp is
// initialized and we can append our plugins subfolder (where the PDB
// files live) to the existing search path.
HMODULE dbghelp = LoadLibraryW(L"DbgHelp");
if (!dbghelp) {
return;
}
typedef BOOL(WINAPI * SymGetSearchPathW_t)(HANDLE, PWSTR, DWORD);
typedef BOOL(WINAPI * SymSetSearchPathW_t)(HANDLE, PCWSTR);
typedef BOOL(WINAPI * SymRefreshModuleList_t)(HANDLE);
auto symGetSearchPathW = reinterpret_cast<SymGetSearchPathW_t>(
GetProcAddress(dbghelp, "SymGetSearchPathW"));
auto symSetSearchPathW = reinterpret_cast<SymSetSearchPathW_t>(
GetProcAddress(dbghelp, "SymSetSearchPathW"));
auto symRefreshModuleList = reinterpret_cast<SymRefreshModuleList_t>(
GetProcAddress(dbghelp, "SymRefreshModuleList"));
if (!symGetSearchPathW || !symSetSearchPathW || !symRefreshModuleList) {
FreeLibrary(dbghelp);
return;
}
HMODULE hModule = NULL;
if (!GetModuleHandleExW(
GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS |
GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
reinterpret_cast<LPCWSTR>(addPluginFolderToSymbolPath),
&hModule)) {
FreeLibrary(dbghelp);
return;
}
wchar_t dllDir[MAX_PATH];
if (!GetModuleFileNameW(hModule, dllDir, MAX_PATH)) {
FreeLibrary(dbghelp);
return;
}
wchar_t *lastSep = wcsrchr(dllDir, L'\\');
if (!lastSep) {
FreeLibrary(dbghelp);
return;
}
*lastSep = L'\0';
wchar_t pluginsPath[MAX_PATH];
wcsncpy_s(pluginsPath, MAX_PATH, dllDir, _TRUNCATE);
wcsncat_s(pluginsPath, MAX_PATH, L"\\" ADVSS_WIDEN(ADVSS_PLUGIN_FOLDER),
_TRUNCATE);
constexpr DWORD currentPathLen = 4096;
constexpr DWORD newPathLen = 8192;
auto currentPath = std::make_unique<wchar_t[]>(currentPathLen);
auto newPath = std::make_unique<wchar_t[]>(newPathLen);
symGetSearchPathW(GetCurrentProcess(), currentPath.get(),
currentPathLen);
if (currentPath[0] != L'\0') {
_snwprintf_s(newPath.get(), newPathLen, _TRUNCATE, L"%s;%s",
currentPath.get(), pluginsPath);
} else {
wcsncpy_s(newPath.get(), newPathLen, pluginsPath, _TRUNCATE);
}
symSetSearchPathW(GetCurrentProcess(), newPath.get());
symRefreshModuleList(GetCurrentProcess());
FreeLibrary(dbghelp);
}
void PlatformInit()
{
CoInitialize(NULL);
AddFinishedLoadingStep(addPluginFolderToSymbolPath);
}
void PlatformCleanup()

View File

@ -4,6 +4,8 @@ namespace advss {
void SavePluginSettings(obs_data_t *) {}
void LoadPluginSettings(obs_data_t *) {}
void AddEarlySaveStep(std::function<void(obs_data_t *)>) {}
void AddEarlyLoadStep(std::function<void(obs_data_t *)>) {}
void AddSaveStep(std::function<void(obs_data_t *)>) {}
void AddLoadStep(std::function<void(obs_data_t *)>) {}
void AddPostLoadStep(std::function<void()>) {}