Compare commits

...

33 Commits

Author SHA1 Message Date
WarmUpTill
b25c1250e5 Try adding tilde key to hotkey action 2024-01-03 19:24:08 +01:00
WarmUpTill
98f3f51ba9 Don't enable drag of macro segment when interacting with scrollbars 2023-12-31 10:13:48 +01:00
WarmUpTill
f0df9ead3c Fix macro segment widgets being squashed below a sensible minimum size
Now a horizontal scroll bar will be displayed in the corresponding
MacroSegmentList widget instead.
2023-12-29 19:31:45 +01:00
WarmUpTill
7b1a57070d Clean up locale 2023-12-29 19:31:45 +01:00
WarmUpTill
a412f2fd5e Fix ResizingPlainTextEdit hiding text in some circumstances
In some scenarios - the exact reason is not known - the widget would
scroll to the very last row, which is always empty, and result in the
text cursor and the already entered text being moved above the displayed
edit area.

It could be fixed by manually scrolling back up (e.g. by attempting to
highlight text with the mouse), but the problem would reappear as soon
as new existing text was modified.

To work around this issue text wrapping was disabled and additional
height will be added for the horizontal scroll bar, if they are visible.

There are still some issues of the height randomly flickering in the
scenarios, which would previously result in the text no longer being
visible.
2023-12-29 19:31:45 +01:00
WarmUpTill
4f0c8c4ab5 Move audio fade helpers to audio action 2023-12-29 19:31:45 +01:00
WarmUpTill
946c90a749 Move tracking of cursor position changes to cursor condition 2023-12-29 19:31:45 +01:00
WarmUpTill
7ba4eaab9c Fix replay buffer save event only triggering in a single condition 2023-12-29 19:31:45 +01:00
WarmUpTill
74553a82fc Remove deprecated "on change" toggle of media condition 2023-12-29 19:31:45 +01:00
WarmUpTill
39fcb2d743 Fix typos 2023-12-29 19:31:45 +01:00
WarmUpTill
7041feaea3 Move handling of stream start / stop time to stream condition 2023-12-29 19:31:45 +01:00
WarmUpTill
9b1ffaeded Remove switcher-data dependency from transition-selection 2023-12-29 19:31:45 +01:00
WarmUpTill
07d397bdac Remove switcher-data dependency from scene-selection 2023-12-29 19:31:45 +01:00
WarmUpTill
a86ff4f1f0 Remove switcher-data dependency from status-control 2023-12-29 19:31:45 +01:00
WarmUpTill
953eca2732 Remove switcher-data dependency from variables 2023-12-29 19:31:45 +01:00
WarmUpTill
d6c5fb149a Fix warning 2023-12-29 19:31:45 +01:00
WarmUpTill
12982708cf Remove curl-helper dependency from switcher-data 2023-12-29 19:31:45 +01:00
WarmUpTill
0cf9e1dd81 Reduce dependencies of macros to switcher data 2023-12-29 19:31:45 +01:00
WarmUpTill
71e20c4983 Move loading / saving of global macro properties 2023-12-29 19:31:45 +01:00
WarmUpTill
2147f6d3bf Rename files 2023-12-29 19:31:45 +01:00
WarmUpTill
23df42d2d3 Add more plugin state helpers 2023-12-29 19:31:45 +01:00
WarmUpTill
bcf0a0b7fd Move functionality to scene-switch-helpers 2023-12-29 19:31:45 +01:00
WarmUpTill
dd78b03a28 Add additional sync-helpers to reduce dependencies to switcher-data 2023-12-29 19:31:45 +01:00
WarmUpTill
84dd5e6930 Add macro-helpers
Added to reduce dependencies to switcher-data
2023-12-29 19:31:45 +01:00
WarmUpTill
dcee98fea7 Move macro segment factory functinos to seperate files 2023-12-29 19:31:45 +01:00
WarmUpTill
9ffb982ae5 Reset date condition type only if necessary 2023-12-29 19:31:45 +01:00
WarmUpTill
428850306f Add option to wait for spawned process to exit 2023-12-25 21:24:48 +01:00
WarmUpTill
abfd6798e3 Adapt to new process start helpers 2023-12-25 21:24:48 +01:00
WarmUpTill
fffadd94de Add helpers to launch process with given config 2023-12-25 21:24:48 +01:00
WarmUpTill
d0919776f3 Add option to set variable to scene item name at given index 2023-12-19 22:59:32 +01:00
WarmUpTill
267855fded Cleanup
* Use OBSSourceAutoRelease
* Do not return true if source selection is invalid
2023-12-17 10:40:29 +01:00
WarmUpTill
4bdebb7feb Fix crash when asking for settings backup
This dialog cannot be modal since other plugins might attempt to display
content which might result in a deadlock.
2023-12-16 19:02:38 +01:00
Michael Kirsch
ab5f69d2ef Decrease the minimal interval time to 10ms 2023-12-13 19:29:14 +01:00
104 changed files with 1453 additions and 918 deletions

View File

@@ -97,6 +97,8 @@ target_sources(
src/macro-core/macro-action-clipboard.hpp
src/macro-core/macro-action-edit.cpp
src/macro-core/macro-action-edit.hpp
src/macro-core/macro-action-factory.cpp
src/macro-core/macro-action-factory.hpp
src/macro-core/macro-action-file.cpp
src/macro-core/macro-action-file.hpp
src/macro-core/macro-action-filter.cpp
@@ -173,6 +175,8 @@ target_sources(
src/macro-core/macro-condition-display.hpp
src/macro-core/macro-condition-edit.cpp
src/macro-core/macro-condition-edit.hpp
src/macro-core/macro-condition-factory.cpp
src/macro-core/macro-condition-factory.hpp
src/macro-core/macro-condition-file.cpp
src/macro-core/macro-condition-file.hpp
src/macro-core/macro-condition-filter.cpp
@@ -233,6 +237,8 @@ target_sources(
src/macro-core/macro-condition.hpp
src/macro-core/macro-dock.cpp
src/macro-core/macro-dock.hpp
src/macro-core/macro-helpers.cpp
src/macro-core/macro-helpers.hpp
src/macro-core/macro-properties.cpp
src/macro-core/macro-properties.hpp
src/macro-core/macro-ref.cpp
@@ -291,8 +297,8 @@ target_sources(
src/utils/obs-module-helper.hpp
src/utils/osc-helpers.cpp
src/utils/osc-helpers.hpp
src/utils/plugin-state-helper.cpp
src/utils/plugin-state-helper.hpp
src/utils/plugin-state-helpers.cpp
src/utils/plugin-state-helpers.hpp
src/utils/priority-helper.cpp
src/utils/priority-helper.hpp
src/utils/process-config.cpp
@@ -323,8 +329,8 @@ target_sources(
src/utils/striped-frame.hpp
src/utils/switch-button.cpp
src/utils/switch-button.hpp
src/utils/sync-helper.cpp
src/utils/sync-helper.hpp
src/utils/sync-helpers.cpp
src/utils/sync-helpers.hpp
src/utils/temp-variable.cpp
src/utils/temp-variable.hpp
src/utils/transition-selection.cpp

View File

@@ -159,7 +159,6 @@ AdvSceneSwitcher.condition.media="Medien"
AdvSceneSwitcher.condition.media.source="Quelle"
AdvSceneSwitcher.condition.media.anyOnScene="Beliebige Medienquelle in"
AdvSceneSwitcher.condition.media.allOnScene="Alle Medienquellen in"
AdvSceneSwitcher.condition.media.matchOnChange="Nur bei Änderung übereinstimmen (Hinweis: Diese Option wird in einer zukünftigen Version entfernt werden - bitte stattdessen Dauer-Modifikatoren verwenden)"
AdvSceneSwitcher.condition.media.inconsistencyInfo="Leider verhalten sich nicht alle Medien-Quell-Typen gleich (z.B. Medien-Quelle vs. VLC-Video-Quelle \"Gestoppt\"-Status).\nBitte experimentieren, was in eurem Setup funktioniert!"
AdvSceneSwitcher.condition.media.entry="{{sourceTypes}}{{mediaSources}}{{scenes}}Status ist{{states}}und{{timeRestrictions}}{{time}}"
AdvSceneSwitcher.condition.video="Video"
@@ -574,7 +573,6 @@ AdvSceneSwitcher.action.variable.invalidSelection="Ungültige Auswahl!"
AdvSceneSwitcher.action.variable.actionNoVariableSupport="Das Abrufen von Variablenwerten aus %1 Aktionen wird nicht unterstützt!"
AdvSceneSwitcher.action.variable.conditionNoVariableSupport="Das Abrufen von Variablenwerten aus %1 Bedingungen wird nicht unterstützt!"
AdvSceneSwitcher.action.variable.currentSegmentValue="Aktueller Wert:"
AdvSceneSwitcher.action.variable.entry="{{actions}}{{variables}}{{variables2}}{{strValue}}{{numValue}}{{segmentIndex}}{{mathExpression}}{{envVariableName}}{{scenes}}{{tempVars}}"
; Transition Tab

View File

@@ -207,7 +207,6 @@ AdvSceneSwitcher.condition.media="Media"
AdvSceneSwitcher.condition.media.source="Source"
AdvSceneSwitcher.condition.media.anyOnScene="Any media source on"
AdvSceneSwitcher.condition.media.allOnScene="All media sources on"
AdvSceneSwitcher.condition.media.matchOnChange="Only match on change (Note: This option will be removed in a future version - please use duration modifiers instead)"
AdvSceneSwitcher.condition.media.inconsistencyInfo="Unfortunately not all media source types behave the same (e.g. Media Source vs. VLC Video Source \"Stopped\" state).\nSo please experiment what works for your setup!"
AdvSceneSwitcher.condition.media.entry="{{sourceTypes}}{{mediaSources}}{{scenes}}state is{{states}}and{{timeRestrictions}}{{time}}"
AdvSceneSwitcher.condition.video="Video"
@@ -610,6 +609,7 @@ AdvSceneSwitcher.action.streaming.type.username="Set username"
AdvSceneSwitcher.action.streaming.type.password="Set password"
AdvSceneSwitcher.action.streaming.entry="{{actions}}{{keyFrameInterval}}{{stringValue}}{{showPassword}}"
AdvSceneSwitcher.action.run="Run"
AdvSceneSwitcher.action.run.entry.wait="{{wait}}Wait for process exit or at most {{timeout}}"
AdvSceneSwitcher.action.sceneVisibility="Scene item visibility"
AdvSceneSwitcher.action.sceneVisibility.type.show="Show"
AdvSceneSwitcher.action.sceneVisibility.type.hide="Hide"
@@ -820,6 +820,7 @@ AdvSceneSwitcher.action.variable.type.sceneItemCount="Set to scene item count of
AdvSceneSwitcher.action.variable.type.stringLength="Set to length of string"
AdvSceneSwitcher.action.variable.type.extractJson="Extract json field with name"
AdvSceneSwitcher.action.variable.type.setToTempvar="Set to macro property"
AdvSceneSwitcher.action.variable.type.sceneItemName="Set to scene item name at index"
AdvSceneSwitcher.action.variable.askForValuePromptDefault="Assign value to variable \"%1\":"
AdvSceneSwitcher.action.variable.askForValuePrompt="Assign value to variable:"
AdvSceneSwitcher.action.variable.mathExpression.example="( 1 + 2 * 3 ) / 4"
@@ -831,7 +832,7 @@ AdvSceneSwitcher.action.variable.invalidSelection="Invalid selection!"
AdvSceneSwitcher.action.variable.actionNoVariableSupport="Getting variable values from %1 actions is not supported!"
AdvSceneSwitcher.action.variable.conditionNoVariableSupport="Getting variable values from %1 conditions is not supported!"
AdvSceneSwitcher.action.variable.currentSegmentValue="Current value:"
AdvSceneSwitcher.action.variable.entry="{{actions}}{{variables}}{{variables2}}{{strValue}}{{numValue}}{{segmentIndex}}{{mathExpression}}{{envVariableName}}{{scenes}}{{tempVars}}"
AdvSceneSwitcher.action.variable.entry="{{actions}}{{variables}}{{variables2}}{{strValue}}{{numValue}}{{segmentIndex}}{{mathExpression}}{{envVariableName}}{{scenes}}{{tempVars}}{{sceneItemIndex}}"
AdvSceneSwitcher.action.variable.entry.substringIndex="Substring start:{{subStringStart}}Substring size:{{subStringSize}}"
AdvSceneSwitcher.action.variable.entry.substringRegex="Assign value of{{regexMatchIdx}}match using regular expression:"
AdvSceneSwitcher.action.variable.entry.findAndReplace="{{findStr}}{{replaceStr}}"
@@ -1293,9 +1294,9 @@ AdvSceneSwitcher.tempVar.twitch.started_at.shoutoutReceived="Shoutout time"
AdvSceneSwitcher.tempVar.twitch.started_at.shoutoutReceived.description="The UTC timestamp (in RFC3339 format) of when the moderator sent the Shoutout."
AdvSceneSwitcher.tempVar.twitch.id.reward="Reward id"
AdvSceneSwitcher.tempVar.twitch.user_id.reward="User id redeeming reward"
AdvSceneSwitcher.tempVar.twitch.user_login.reward="User login redeeming reward"
AdvSceneSwitcher.tempVar.twitch.user_name.reward="User login redeeming reward"
AdvSceneSwitcher.tempVar.twitch.user_id.reward="User id redeeming the reward"
AdvSceneSwitcher.tempVar.twitch.user_login.reward="User login redeeming the reward"
AdvSceneSwitcher.tempVar.twitch.user_name.reward="User name redeeming the reward"
AdvSceneSwitcher.tempVar.twitch.user_input.reward="User input for reward"
AdvSceneSwitcher.tempVar.twitch.reward.reward="Reward Information"
AdvSceneSwitcher.tempVar.twitch.reward.reward.description="Basic information about the reward that was redeemed, at the time it was redeemed."

View File

@@ -129,7 +129,6 @@ AdvSceneSwitcher.condition.file.entry.line3="{{checkModificationDate}}{{checkFil
AdvSceneSwitcher.condition.media="Medios"
AdvSceneSwitcher.condition.media.anyOnScene="Cualquier fuente multimedia activada"
AdvSceneSwitcher.condition.media.allOnScene="Todas las fuentes de medios activadas"
AdvSceneSwitcher.condition.media.matchOnChange="Solo coincidir con el cambio (Nota: esta opción se eliminará en una versión futura; use modificadores de duración en su lugar)"
AdvSceneSwitcher.condition.media.inconsistencyInfo="Desafortunadamente, no todos los tipos de fuentes de medios se comportan de la misma manera (p. ej., fuente de medios frente a estado \"Detenido\" de fuente de video VLC).\n¡Así que experimente lo que funciona para su configuración!"
AdvSceneSwitcher.condition.media.entry="El estado de{{sourceTypes}}{{mediaSources}}{{scenes}}es{{states}}y{{timeRestrictions}}{{time}}"
AdvSceneSwitcher.condition.video="Video"

View File

@@ -197,7 +197,6 @@ AdvSceneSwitcher.condition.media="Média"
AdvSceneSwitcher.condition.media.source="Source"
AdvSceneSwitcher.condition.media.anyOnScene="Toute source média sur la scène"
AdvSceneSwitcher.condition.media.allOnScene="Toutes les sources média sur la scène"
AdvSceneSwitcher.condition.media.matchOnChange="Ne correspondre qu'au changement (Remarque : Cette option sera supprimée dans une version future - veuillez plutôt utiliser les modificateurs de durée)"
AdvSceneSwitcher.condition.media.inconsistencyInfo="Malheureusement, toutes les sources média ne se comportent pas de la même manière (par exemple, l'état \"Arrêté\" de la source Media par rapport à la source Vidéo VLC).\nPar conséquent, veuillez expérimenter ce qui fonctionne le mieux pour votre configuration !"
AdvSceneSwitcher.condition.media.entry="{{sourceTypes}}{{mediaSources}}{{scenes}}l'état est{{states}}et{{timeRestrictions}}{{time}}"
AdvSceneSwitcher.condition.video="Vidéo"
@@ -740,7 +739,6 @@ AdvSceneSwitcher.action.twitch.type.channel.info.title.set="Définir le titre du
AdvSceneSwitcher.action.twitch.type.channel.info.category.set="Définir la catégorie du flux"
AdvSceneSwitcher.action.twitch.type.commercial.start="Démarrer une publicité d'une durée de"
AdvSceneSwitcher.action.twitch.categorySelectionDisabled="Impossible de sélectionner une catégorie sans avoir d'abord sélectionné un compte Twitch !"
AdvSceneSwitcher.action.twitch.entry.line1="Sur{{account}}{{actions}}{{streamTitle}}{{category}}{{markerDescription}}{{clipHasDelay}}{{duration}}{{announcementColor}}{{channel}}"
; Transition Tab
AdvSceneSwitcher.transitionTab.title="Transition"

View File

@@ -119,7 +119,6 @@ AdvSceneSwitcher.condition.file.entry.line3="{{checkModificationDate}}{{checkFil
AdvSceneSwitcher.condition.media="Medya"
AdvSceneSwitcher.condition.media.anyOnScene="Herhangi bir medya kaynağı"
AdvSceneSwitcher.condition.media.allOnScene="Tüm medya kaynakları "
AdvSceneSwitcher.condition.media.matchOnChange="Yalnızca değişiklikle eşleştirin (Not: Bu seçenek gelecekteki bir sürümde kaldırılacaktır - lütfen bunun yerine zaman kısıtlamalarını kullanın)"
AdvSceneSwitcher.condition.media.entry="{{sourceTypes}}{{mediaSources}}{{scenes}}durumu{{states}}ve{{timeRestrictions}}{{time}}"
AdvSceneSwitcher.condition.video="Video"
AdvSceneSwitcher.condition.video.condition.match="Tam olarak eşleşir"

View File

@@ -185,7 +185,6 @@ AdvSceneSwitcher.condition.media="媒体"
AdvSceneSwitcher.condition.media.source="源"
AdvSceneSwitcher.condition.media.anyOnScene="任何媒体源"
AdvSceneSwitcher.condition.media.allOnScene="所有媒体源"
AdvSceneSwitcher.condition.media.matchOnChange="仅在更改时匹配(注意:此选项将在未来版本中删除-请改用时间限制)"
AdvSceneSwitcher.condition.media.inconsistencyInfo="不幸的是并非所有媒体源类型的行为都相同例如媒体源与VLC视频源“停止”状态。\n所以请尝试适合您的设置"
AdvSceneSwitcher.condition.media.entry="{{sourceTypes}}{{mediaSources}}{{scenes}} 的状态是 {{states}} 和 {{timeRestrictions}}{{time}}"
AdvSceneSwitcher.condition.video="视频"
@@ -650,7 +649,6 @@ AdvSceneSwitcher.action.variable.invalidSelection="无效选择!"
AdvSceneSwitcher.action.variable.actionNoVariableSupport="不支持从 %1 个操作获取变量值!"
AdvSceneSwitcher.action.variable.conditionNoVariableSupport="不支持从 %1 条件中获取变量值!"
AdvSceneSwitcher.action.variable.currentSegmentValue="当前值:"
AdvSceneSwitcher.action.variable.entry="{{actions}}{{variables}}{{variables2}}{{strValue}}{{numValue}}{{segmentIndex}}{{mathExpression}}{{envVariableName}}{{scenes}}{{tempVars}}"
AdvSceneSwitcher.action.variable.entry.substringIndex="子字符串开始:{{subStringStart}} 子字符串大小:{{subStringSize}}"
AdvSceneSwitcher.action.variable.entry.substringRegex="使用正则表达式为 {{regexMatchIdx}} 匹配的值:"
AdvSceneSwitcher.action.variable.entry.findAndReplace="{{findStr}}{{replaceStr}}"

View File

@@ -119,7 +119,7 @@
<string notr="true">ms</string>
</property>
<property name="minimum">
<number>50</number>
<number>10</number>
</property>
<property name="maximum">
<number>20000</number>

View File

@@ -1,5 +1,6 @@
#include "advanced-scene-switcher.hpp"
#include "switcher-data.hpp"
#include "macro-helpers.hpp"
#include "status-control.hpp"
#include "scene-switch-helpers.hpp"
#include "curl-helper.hpp"
@@ -10,6 +11,7 @@
#include <QMainWindow>
#include <QAction>
#include <QFileDialog>
#include <QTextStream>
#include <QDirIterator>
#include <regex>
#include <filesystem>
@@ -135,7 +137,7 @@ bool AdvSceneSwitcher::eventFilter(QObject *obj, QEvent *event)
/******************************************************************************
* Saving and loading
******************************************************************************/
static void AskForBackup(obs_data_t *obj);
static void AskForBackup(const QString &json);
static void SaveSceneSwitcher(obs_data_t *save_data, bool saving, void *)
{
@@ -156,16 +158,25 @@ static void SaveSceneSwitcher(obs_data_t *save_data, bool saving, void *)
switcher->Stop();
switcher->m.lock();
obs_data_t *obj =
OBSDataAutoRelease obj =
obs_data_get_obj(save_data, "advanced-scene-switcher");
if (!obj) {
obj = obs_data_create();
}
if (switcher->VersionChanged(obj, g_GIT_SHA1)) {
AskForBackup(obj);
auto json = obs_data_get_json(obj);
static QString jsonQString = json ? json : "";
std::thread t([]() {
obs_queue_task(
OBS_TASK_UI,
[](void *) {
AskForBackup(jsonQString);
},
nullptr, false);
});
t.detach();
}
switcher->LoadSettings(obj);
obs_data_release(obj);
switcher->m.unlock();
if (!switcher->stop) {
@@ -174,12 +185,12 @@ static void SaveSceneSwitcher(obs_data_t *save_data, bool saving, void *)
}
}
static void AskForBackup(obs_data_t *obj)
static void AskForBackup(const QString &json)
{
bool backupSettings = DisplayMessage(
obs_module_text("AdvSceneSwitcher.askBackup"), true);
const bool backupWasConfirmed = DisplayMessage(
obs_module_text("AdvSceneSwitcher.askBackup"), true, false);
if (!backupSettings) {
if (!backupWasConfirmed) {
return;
}
@@ -198,8 +209,8 @@ static void AskForBackup(obs_data_t *obj)
if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
return;
}
obs_data_save_json(obj, file.fileName().toUtf8().constData());
auto out = QTextStream(&file);
out << json;
}
/******************************************************************************
@@ -226,7 +237,6 @@ void SwitcherData::Thread()
// if a longer transition is used than the configured check interval
bool setPrevSceneAfterLinger = false;
bool macroMatch = false;
macroSceneSwitched = false;
endTime = std::chrono::high_resolution_clock::now();
auto runTime =
std::chrono::duration_cast<std::chrono::milliseconds>(
@@ -337,12 +347,6 @@ void SwitcherData::SetPreconditions()
// Process name
GetForegroundProcessName(currentForegroundProcess);
// Cursor
std::pair<int, int> cursorPos = GetCursorPos();
cursorPosChanged = cursorPos.first != switcher->lastCursorPos.first ||
cursorPos.second != switcher->lastCursorPos.second;
lastCursorPos = GetCursorPos();
// Macro
InvalidateMacroTempVarValues();
}
@@ -424,7 +428,7 @@ bool SwitcherData::CheckForMatch(OBSWeakSource &scene,
static void ResetMacros()
{
for (auto &m : switcher->macros) {
for (auto &m : GetMacros()) {
m->ResetRunCount();
m->ResetTimers();
}
@@ -467,9 +471,9 @@ void SwitcherData::Stop()
if (th && th->isRunning()) {
stop = true;
cv.notify_all();
abortMacroWait = true;
macroWaitCv.notify_all();
macroTransitionCv.notify_all();
SetMacroAbortWait(true);
GetMacroWaitCV().notify_all();
GetMacroTransitionCV().notify_all();
// Not waiting if a dialog was closed is a workaround to avoid
// deadlocks when a variable input dialog is opened while Stop()
@@ -542,9 +546,6 @@ extern "C" void FreeSceneSwitcher()
static void handleSceneChange()
{
switcher->lastSceneChangeTime =
std::chrono::high_resolution_clock::now();
// Stop waiting if scene was changed
if (switcher->SceneChangedDuringWait()) {
switcher->cv.notify_one();
@@ -611,28 +612,9 @@ static void handlePeviewSceneChange()
}
}
static void setReplayBufferSaved()
static void handleTransitionEnd()
{
switcher->replayBufferSaved = true;
}
static void setTranstionEnd()
{
switcher->lastTransitionEndTime =
std::chrono::high_resolution_clock::now();
switcher->macroTransitionCv.notify_all();
}
static void setStreamStarting()
{
switcher->lastStreamStartingTime =
std::chrono::high_resolution_clock::now();
}
static void setStreamStopping()
{
switcher->lastStreamStoppingTime =
std::chrono::high_resolution_clock::now();
GetMacroTransitionCV().notify_all();
}
static void handleShutdown()
@@ -641,10 +623,10 @@ static void handleShutdown()
return;
}
switcher->obsIsShuttingDown = true;
if (switcher->shutdownConditionCount) {
if (ShutdownCheckIsNecessary()) {
switcher->Stop();
switcher->CheckMacros();
switcher->RunMacros();
CheckMacros();
RunMacros();
// Unfortunately this will not work as OBS will now allow saving
// the scene collection data at this point, So any OBS specific
@@ -701,19 +683,8 @@ static void OBSEvent(enum obs_frontend_event event, void *switcher)
case OBS_FRONTEND_EVENT_STREAMING_STOPPED:
resetLiveTime();
break;
#if LIBOBS_API_VER >= MAKE_SEMANTIC_VERSION(26, 0, 0)
case OBS_FRONTEND_EVENT_REPLAY_BUFFER_SAVED:
setReplayBufferSaved();
break;
#endif
case OBS_FRONTEND_EVENT_TRANSITION_STOPPED:
setTranstionEnd();
break;
case OBS_FRONTEND_EVENT_STREAMING_STARTING:
setStreamStarting();
break;
case OBS_FRONTEND_EVENT_STREAMING_STOPPING:
setStreamStopping();
handleTransitionEnd();
break;
#if LIBOBS_API_VER >= MAKE_SEMANTIC_VERSION(27, 2, 0)
case OBS_FRONTEND_EVENT_SCENE_COLLECTION_CHANGING:

View File

@@ -3,6 +3,7 @@
#include "status-control.hpp"
#include "file-selection.hpp"
#include "filter-combo-box.hpp"
#include "variable.hpp"
#include "utility.hpp"
#include "version.h"
@@ -512,6 +513,7 @@ void SwitcherData::LoadSettings(obs_data_t *obj)
}
LoadMacros(obj);
LoadGlobalMacroProperties(obj);
loadWindowTitleSwitches(obj);
loadScreenRegionSwitches(obj);
loadPauseSwitches(obj);
@@ -548,6 +550,7 @@ void SwitcherData::SaveSettings(obs_data_t *obj)
saveSceneGroups(obj);
SaveMacros(obj);
SaveGlobalMacroProperties(obj);
SaveVariables(obj);
saveWindowTitleSwitches(obj);
saveScreenRegionSwitches(obj);

View File

@@ -160,7 +160,9 @@ enum class HotkeyType {
Key_NumpadMultiply,
Key_NumpadDivide,
Key_NumpadDecimal,
Key_NumpadEnter
Key_NumpadEnter,
Key_Tilde,
};
} // namespace advss

View File

@@ -12,6 +12,11 @@ namespace advss {
static QMetaObject::Connection addPulse;
SceneGroupEditWidget *typeEdit = nullptr;
std::deque<SceneGroup> &GetSceneGroups()
{
return switcher->sceneGroups;
}
void SceneGroup::advanceIdx()
{
currentIdx++;

View File

@@ -1,5 +1,5 @@
#pragma once
#include <deque>
#include <vector>
#include <QDialog>
#include <QLabel>
@@ -87,5 +87,6 @@ private:
SceneGroup *GetSceneGroupByName(const char *name);
SceneGroup *GetSceneGroupByQString(const QString &name);
std::deque<SceneGroup> &GetSceneGroups();
} // namespace advss

View File

@@ -177,17 +177,17 @@ static std::string getRemoteData(std::string &url)
{
std::string readBuffer;
switcher->curl.SetOpt(CURLOPT_URL, url.c_str());
switcher->curl.SetOpt(CURLOPT_WRITEFUNCTION, WriteCallback);
switcher->curl.SetOpt(CURLOPT_WRITEDATA, &readBuffer);
CurlHelper::SetOpt(CURLOPT_URL, url.c_str());
CurlHelper::SetOpt(CURLOPT_WRITEFUNCTION, WriteCallback);
CurlHelper::SetOpt(CURLOPT_WRITEDATA, &readBuffer);
// Set timeout to at least one second
int timeout = switcher->interval / 1000;
if (timeout == 0) {
timeout = 1;
}
switcher->curl.SetOpt(CURLOPT_TIMEOUT, 1);
switcher->curl.Perform();
CurlHelper::SetOpt(CURLOPT_TIMEOUT, 1);
CurlHelper::Perform();
return readBuffer;
}
@@ -422,7 +422,7 @@ void AdvSceneSwitcher::SetupFileTab()
obs_module_text("AdvSceneSwitcher.fileTab.remoteFileWarning2"));
ui->remoteFileWarningLabel->hide();
if (switcher->curl.Initialized()) {
if (CurlHelper::Initialized()) {
ui->libcurlWarning->setVisible(false);
}

View File

@@ -555,6 +555,9 @@ static std::unordered_map<HotkeyType, long> keyTable = {
{HotkeyType::Key_8, XK_8},
{HotkeyType::Key_9, XK_9},
// Other
{HotkeyType::Key_Tilde, XK_asciitilde},
{HotkeyType::Key_F1, XK_F1},
{HotkeyType::Key_F2, XK_F2},
{HotkeyType::Key_F3, XK_F3},

View File

@@ -1,5 +1,5 @@
#include "macro-action-audio.hpp"
#include "switcher-data.hpp"
#include "macro.hpp"
#include "utility.hpp"
namespace advss {
@@ -36,6 +36,16 @@ const static std::map<MacroActionAudio::FadeType, std::string> fadeTypes = {
"AdvSceneSwitcher.action.audio.fade.type.rate"},
};
namespace {
struct FadeInfo {
std::atomic_bool active = {false};
std::atomic_int id = {0};
};
}
static FadeInfo masterAudioFade;
static std::unordered_map<std::string, FadeInfo> audioFades;
constexpr auto fadeInterval = std::chrono::milliseconds(100);
constexpr float minFade = 0.000001f;
@@ -58,10 +68,9 @@ auto set_master_volume = obs_set_master_volume;
void MacroActionAudio::SetFadeActive(bool value)
{
if (_action == Action::SOURCE_VOLUME) {
switcher->activeAudioFades[_audioSource.ToString()].active =
value;
audioFades[_audioSource.ToString()].active = value;
} else {
switcher->masterAudioFade.active = value;
masterAudioFade.active = value;
}
}
@@ -69,14 +78,13 @@ bool MacroActionAudio::FadeActive()
{
bool active = true;
if (_action == Action::SOURCE_VOLUME) {
auto it = switcher->activeAudioFades.find(
_audioSource.ToString());
if (it == switcher->activeAudioFades.end()) {
auto it = audioFades.find(_audioSource.ToString());
if (it == audioFades.end()) {
return false;
}
active = it->second.active;
} else {
active = switcher->masterAudioFade.active;
active = masterAudioFade.active;
}
return active;
@@ -86,14 +94,13 @@ std::atomic_int *MacroActionAudio::GetFadeIdPtr()
{
if (_action == Action::SOURCE_VOLUME) {
auto it = switcher->activeAudioFades.find(
_audioSource.ToString());
if (it == switcher->activeAudioFades.end()) {
auto it = audioFades.find(_audioSource.ToString());
if (it == audioFades.end()) {
return nullptr;
}
return &it->second.id;
}
return &switcher->masterAudioFade.id;
return &masterAudioFade.id;
}
void MacroActionAudio::SetVolume(float vol)

View File

@@ -1,6 +1,5 @@
#include "macro-action-clipboard.hpp"
#include "curl-helper.hpp"
#include "switcher-data.hpp"
#include <QApplication>
#include <QClipboard>
@@ -34,17 +33,17 @@ static std::optional<QImage> getImageFromUrl(const char *url)
{
QByteArray response;
switcher->curl.SetOpt(CURLOPT_URL, url);
switcher->curl.SetOpt(CURLOPT_HTTPGET, 1L);
switcher->curl.SetOpt(CURLOPT_TIMEOUT_MS, 30000);
switcher->curl.SetOpt(CURLOPT_WRITEFUNCTION, writeCallback);
switcher->curl.SetOpt(CURLOPT_WRITEDATA, &response);
auto code = switcher->curl.Perform();
CurlHelper::SetOpt(CURLOPT_URL, url);
CurlHelper::SetOpt(CURLOPT_HTTPGET, 1L);
CurlHelper::SetOpt(CURLOPT_TIMEOUT_MS, 30000);
CurlHelper::SetOpt(CURLOPT_WRITEFUNCTION, writeCallback);
CurlHelper::SetOpt(CURLOPT_WRITEDATA, &response);
auto code = CurlHelper::Perform();
if (code != CURLE_OK) {
blog(LOG_WARNING,
"Retrieving image failed in %s with error: %s", __func__,
switcher->curl.GetError(code));
CurlHelper::GetError(code));
return {};
}

View File

@@ -1,6 +1,8 @@
#include "advanced-scene-switcher.hpp"
#include "switcher-data.hpp"
#include "macro-action-edit.hpp"
#include "macro-helpers.hpp"
#include "macro.hpp"
#include "macro-properties.hpp"
#include "advanced-scene-switcher.hpp"
#include "macro-action-scene-switch.hpp"
#include "section.hpp"
#include "switch-button.hpp"
@@ -10,61 +12,9 @@
namespace advss {
std::map<std::string, MacroActionInfo> &MacroActionFactory::GetMap()
{
static std::map<std::string, MacroActionInfo> _methods;
return _methods;
}
bool MacroActionFactory::Register(const std::string &id, MacroActionInfo info)
{
if (auto it = GetMap().find(id); it == GetMap().end()) {
GetMap()[id] = info;
return true;
}
return false;
}
std::shared_ptr<MacroAction> MacroActionFactory::Create(const std::string &id,
Macro *m)
{
if (auto it = GetMap().find(id); it != GetMap().end())
return it->second._createFunc(m);
return nullptr;
}
QWidget *MacroActionFactory::CreateWidget(const std::string &id,
QWidget *parent,
std::shared_ptr<MacroAction> action)
{
if (auto it = GetMap().find(id); it != GetMap().end())
return it->second._createWidgetFunc(parent, action);
return nullptr;
}
std::string MacroActionFactory::GetActionName(const std::string &id)
{
if (auto it = GetMap().find(id); it != GetMap().end()) {
return it->second._name;
}
return "unknown action";
}
std::string MacroActionFactory::GetIdByName(const QString &name)
{
for (auto it : GetMap()) {
if (name == obs_module_text(it.second._name.c_str())) {
return it.first;
}
}
return "";
}
static inline void populateActionSelection(QComboBox *list)
{
for (auto &[_, action] : MacroActionFactory::GetActionTypes()) {
for (const auto &[_, action] : MacroActionFactory::GetActionTypes()) {
QString entry(obs_module_text(action._name.c_str()));
if (list->findText(entry) == -1) {
list->addItem(entry);
@@ -80,7 +30,8 @@ static inline void populateActionSelection(QComboBox *list)
MacroActionEdit::MacroActionEdit(QWidget *parent,
std::shared_ptr<MacroAction> *entryData,
const std::string &id)
: MacroSegmentEdit(switcher->macroProperties._highlightActions, parent),
: MacroSegmentEdit(GetGlobalMacroProperties()._highlightActions,
parent),
_actionSelection(new FilterComboBox()),
_enable(new SwitchButton()),
_entryData(entryData)
@@ -134,7 +85,7 @@ void MacroActionEdit::ActionSelectionChanged(const QString &text)
auto idx = _entryData->get()->GetIndex();
auto macro = _entryData->get()->GetMacro();
{
std::lock_guard<std::mutex> lock(switcher->m);
auto lock = LockContext();
_entryData->reset();
*_entryData = MacroActionFactory::Create(id, macro);
(*_entryData)->SetIndex(idx);
@@ -185,7 +136,7 @@ void MacroActionEdit::ActionEnableChanged(bool value)
return;
}
std::lock_guard<std::mutex> lock(switcher->m);
auto lock = LockContext();
(*_entryData)->SetEnabled(value);
SetDisableEffect(!value);
}
@@ -229,7 +180,7 @@ void AdvSceneSwitcher::AddMacroAction(int idx)
id = temp.GetId();
}
{
std::lock_guard<std::mutex> lock(switcher->m);
auto lock = LockContext();
macro->Actions().emplace(
macro->Actions().begin() + idx,
MacroActionFactory::Create(id, macro.get()));
@@ -280,11 +231,11 @@ void AdvSceneSwitcher::RemoveMacroAction(int idx)
}
{
std::lock_guard<std::mutex> lock(switcher->m);
auto lock = LockContext();
ui->actionsList->Remove(idx);
macro->Actions().erase(macro->Actions().begin() + idx);
switcher->abortMacroWait = true;
switcher->macroWaitCv.notify_all();
SetMacroAbortWait(true);
GetMacroWaitCV().notify_all();
macro->UpdateActionIndices();
SetActionData(*macro);
}
@@ -425,7 +376,7 @@ void AdvSceneSwitcher::SwapActions(Macro *m, int pos1, int pos2)
std::swap(pos1, pos2);
}
std::lock_guard<std::mutex> lock(switcher->m);
auto lock = LockContext();
iter_swap(m->Actions().begin() + pos1, m->Actions().begin() + pos2);
m->UpdateActionIndices();
auto widget1 = static_cast<MacroActionEdit *>(
@@ -485,7 +436,7 @@ void AdvSceneSwitcher::MacroElseActionReorder(int to, int from)
return;
}
{
std::lock_guard<std::mutex> lock(switcher->m);
auto lock = LockContext();
auto action = macro->ElseActions().at(from);
macro->ElseActions().erase(macro->ElseActions().begin() + from);
macro->ElseActions().insert(macro->ElseActions().begin() + to,
@@ -518,7 +469,7 @@ void AdvSceneSwitcher::AddMacroElseAction(int idx)
id = temp.GetId();
}
{
std::lock_guard<std::mutex> lock(switcher->m);
auto lock = LockContext();
macro->ElseActions().emplace(
macro->ElseActions().begin() + idx,
MacroActionFactory::Create(id, macro.get()));
@@ -550,11 +501,11 @@ void AdvSceneSwitcher::RemoveMacroElseAction(int idx)
}
{
std::lock_guard<std::mutex> lock(switcher->m);
auto lock = LockContext();
ui->elseActionsList->Remove(idx);
macro->ElseActions().erase(macro->ElseActions().begin() + idx);
switcher->abortMacroWait = true;
switcher->macroWaitCv.notify_all();
SetMacroAbortWait(true);
GetMacroWaitCV().notify_all();
macro->UpdateElseActionIndices();
SetActionData(*macro);
}
@@ -572,7 +523,7 @@ void AdvSceneSwitcher::SwapElseActions(Macro *m, int pos1, int pos2)
std::swap(pos1, pos2);
}
std::lock_guard<std::mutex> lock(switcher->m);
auto lock = LockContext();
iter_swap(m->ElseActions().begin() + pos1,
m->ElseActions().begin() + pos2);
m->UpdateElseActionIndices();
@@ -635,7 +586,7 @@ void AdvSceneSwitcher::MacroActionReorder(int to, int from)
return;
}
{
std::lock_guard<std::mutex> lock(switcher->m);
auto lock = LockContext();
auto action = macro->Actions().at(from);
macro->Actions().erase(macro->Actions().begin() + from);
macro->Actions().insert(macro->Actions().begin() + to, action);

View File

@@ -1,5 +1,6 @@
#pragma once
#include "macro-action.hpp"
#include "macro-action-factory.hpp"
#include "filter-combo-box.hpp"
#include <memory>
@@ -8,32 +9,6 @@ namespace advss {
class SwitchButton;
struct MacroActionInfo {
using TCreateMethod = std::shared_ptr<MacroAction> (*)(Macro *m);
using TCreateWidgetMethod = QWidget *(*)(QWidget *parent,
std::shared_ptr<MacroAction>);
TCreateMethod _createFunc = nullptr;
TCreateWidgetMethod _createWidgetFunc = nullptr;
std::string _name;
};
class MacroActionFactory {
public:
MacroActionFactory() = delete;
static bool Register(const std::string &id, MacroActionInfo);
static std::shared_ptr<MacroAction> Create(const std::string &id,
Macro *m);
static QWidget *CreateWidget(const std::string &id, QWidget *parent,
std::shared_ptr<MacroAction> action);
static auto GetActionTypes() { return GetMap(); }
static std::string GetActionName(const std::string &id);
static std::string GetIdByName(const QString &name);
private:
static std::map<std::string, MacroActionInfo> &GetMap();
};
class MacroActionEdit : public MacroSegmentEdit {
Q_OBJECT

View File

@@ -0,0 +1,57 @@
#include "macro-action-factory.hpp"
namespace advss {
std::map<std::string, MacroActionInfo> &MacroActionFactory::GetMap()
{
static std::map<std::string, MacroActionInfo> _methods;
return _methods;
}
bool MacroActionFactory::Register(const std::string &id, MacroActionInfo info)
{
if (auto it = GetMap().find(id); it == GetMap().end()) {
GetMap()[id] = info;
return true;
}
return false;
}
std::shared_ptr<MacroAction> MacroActionFactory::Create(const std::string &id,
Macro *m)
{
if (auto it = GetMap().find(id); it != GetMap().end())
return it->second._create(m);
return nullptr;
}
QWidget *MacroActionFactory::CreateWidget(const std::string &id,
QWidget *parent,
std::shared_ptr<MacroAction> action)
{
if (auto it = GetMap().find(id); it != GetMap().end())
return it->second._createWidget(parent, action);
return nullptr;
}
std::string MacroActionFactory::GetActionName(const std::string &id)
{
if (auto it = GetMap().find(id); it != GetMap().end()) {
return it->second._name;
}
return "unknown action";
}
std::string MacroActionFactory::GetIdByName(const QString &name)
{
for (auto it : GetMap()) {
if (name == obs_module_text(it.second._name.c_str())) {
return it.first;
}
}
return "";
}
} // namespace advss

View File

@@ -0,0 +1,34 @@
#pragma once
#include "macro-action.hpp"
#include <memory>
namespace advss {
struct MacroActionInfo {
using CreateAction = std::shared_ptr<MacroAction> (*)(Macro *m);
using CreateActionWidget = QWidget *(*)(QWidget *parent,
std::shared_ptr<MacroAction>);
CreateAction _create = nullptr;
CreateActionWidget _createWidget = nullptr;
std::string _name;
};
class MacroActionFactory {
public:
MacroActionFactory() = delete;
static bool Register(const std::string &id, MacroActionInfo);
static std::shared_ptr<MacroAction> Create(const std::string &id,
Macro *m);
static QWidget *CreateWidget(const std::string &id, QWidget *parent,
std::shared_ptr<MacroAction> action);
static auto GetActionTypes() { return GetMap(); }
static std::string GetActionName(const std::string &id);
static std::string GetIdByName(const QString &name);
private:
static std::map<std::string, MacroActionInfo> &GetMap();
};
} // namespace advss

View File

@@ -14,14 +14,14 @@ bool MacroActionHotkey::_registered = MacroActionFactory::Register(
{MacroActionHotkey::Create, MacroActionHotkeyEdit::Create,
"AdvSceneSwitcher.action.hotkey"});
const static std::map<MacroActionHotkey::Action, std::string> actionTypes = {
static const std::map<MacroActionHotkey::Action, std::string> actionTypes = {
{MacroActionHotkey::Action::OBS_HOTKEY,
"AdvSceneSwitcher.action.hotkey.type.obs"},
{MacroActionHotkey::Action::CUSTOM,
"AdvSceneSwitcher.action.hotkey.type.custom"},
};
const static std::map<obs_hotkey_registerer_type, std::string> hotkeyTypes = {
static const std::map<obs_hotkey_registerer_type, std::string> hotkeyTypes = {
{OBS_HOTKEY_REGISTERER_FRONTEND,
"AdvSceneSwitcher.action.hotkey.type.obs.type.frontend"},
{OBS_HOTKEY_REGISTERER_SOURCE,
@@ -34,7 +34,7 @@ const static std::map<obs_hotkey_registerer_type, std::string> hotkeyTypes = {
"AdvSceneSwitcher.action.hotkey.type.obs.type.service"},
};
static std::unordered_map<HotkeyType, long> keyTable = {
static const std::unordered_map<HotkeyType, long> keyTable = {
// Chars
{HotkeyType::Key_A, OBS_KEY_A},
{HotkeyType::Key_B, OBS_KEY_B},
@@ -75,6 +75,9 @@ static std::unordered_map<HotkeyType, long> keyTable = {
{HotkeyType::Key_8, OBS_KEY_8},
{HotkeyType::Key_9, OBS_KEY_9},
// Other
{HotkeyType::Key_Tilde, OBS_KEY_ASCIITILDE},
{HotkeyType::Key_F1, OBS_KEY_F1},
{HotkeyType::Key_F2, OBS_KEY_F2},
{HotkeyType::Key_F3, OBS_KEY_F3},
@@ -525,6 +528,7 @@ static inline void populateKeySelection(QComboBox *list)
list->addItem("NumpadDivide");
list->addItem("NumpadDecimal");
list->addItem("NumpadEnter");
list->addItem("Tilde");
}
static void populateActionSelection(QComboBox *list)

View File

@@ -1,5 +1,4 @@
#include "macro-action-http.hpp"
#include "switcher-data.hpp"
#include "utility.hpp"
#include "curl-helper.hpp"
@@ -36,44 +35,44 @@ void MacroActionHttp::SetupHeaders()
}
struct curl_slist *headers = nullptr;
for (auto &header : _headers) {
headers = switcher->curl.SlistAppend(headers, header.c_str());
headers = CurlHelper::SlistAppend(headers, header.c_str());
}
if (!_headers.empty()) {
switcher->curl.SetOpt(CURLOPT_HTTPHEADER, headers);
CurlHelper::SetOpt(CURLOPT_HTTPHEADER, headers);
}
}
void MacroActionHttp::Get()
{
switcher->curl.SetOpt(CURLOPT_URL, _url.c_str());
switcher->curl.SetOpt(CURLOPT_HTTPGET, 1L);
switcher->curl.SetOpt(CURLOPT_TIMEOUT_MS, _timeout.Milliseconds());
CurlHelper::SetOpt(CURLOPT_URL, _url.c_str());
CurlHelper::SetOpt(CURLOPT_HTTPGET, 1L);
CurlHelper::SetOpt(CURLOPT_TIMEOUT_MS, _timeout.Milliseconds());
SetupHeaders();
std::string response;
if (IsReferencedInVars()) {
switcher->curl.SetOpt(CURLOPT_WRITEFUNCTION, WriteCB);
CurlHelper::SetOpt(CURLOPT_WRITEFUNCTION, WriteCB);
} else {
switcher->curl.SetOpt(CURLOPT_WRITEFUNCTION, DropCB);
CurlHelper::SetOpt(CURLOPT_WRITEFUNCTION, DropCB);
}
switcher->curl.SetOpt(CURLOPT_WRITEDATA, &response);
switcher->curl.Perform();
CurlHelper::SetOpt(CURLOPT_WRITEDATA, &response);
CurlHelper::Perform();
SetVariableValue(response);
}
void MacroActionHttp::Post()
{
switcher->curl.SetOpt(CURLOPT_URL, _url.c_str());
switcher->curl.SetOpt(CURLOPT_POSTFIELDS, _data.c_str());
switcher->curl.SetOpt(CURLOPT_TIMEOUT_MS, _timeout.Milliseconds());
CurlHelper::SetOpt(CURLOPT_URL, _url.c_str());
CurlHelper::SetOpt(CURLOPT_POSTFIELDS, _data.c_str());
CurlHelper::SetOpt(CURLOPT_TIMEOUT_MS, _timeout.Milliseconds());
SetupHeaders();
switcher->curl.Perform();
CurlHelper::Perform();
}
bool MacroActionHttp::PerformAction()
{
if (!switcher->curl.Initialized()) {
if (!CurlHelper::Initialized()) {
blog(LOG_WARNING,
"cannot perform http action (curl not found)");
return true;

View File

@@ -1,5 +1,5 @@
#include "macro-action-plugin-state.hpp"
#include "plugin-state-helper.hpp"
#include "plugin-state-helpers.hpp"
#include "utility.hpp"
#include <thread>

View File

@@ -14,9 +14,14 @@ bool MacroActionRun::_registered = MacroActionFactory::Register(
bool MacroActionRun::PerformAction()
{
bool procStarted = QProcess::startDetached(
QString::fromStdString(_procConfig.Path()), _procConfig.Args(),
QString::fromStdString(_procConfig.WorkingDir()));
if (_wait) {
_procConfig.StartProcessAndWait(_timeout.Milliseconds());
return true;
}
bool procStarted = _procConfig.StartProcessDetached();
// Fall back to using default application to open given file
if (!procStarted && _procConfig.Args().empty()) {
vblog(LOG_INFO, "run \"%s\" using QDesktopServices",
_procConfig.Path().c_str());
@@ -35,6 +40,9 @@ bool MacroActionRun::Save(obs_data_t *obj) const
{
MacroAction::Save(obj);
_procConfig.Save(obj);
_timeout.Save(obj);
obs_data_set_bool(obj, "wait", _wait);
obs_data_set_int(obj, "version", 1);
return true;
}
@@ -42,6 +50,12 @@ bool MacroActionRun::Load(obs_data_t *obj)
{
MacroAction::Load(obj);
_procConfig.Load(obj);
// TODO: Remove this fallback in a future version
if (!obs_data_has_user_value(obj, "version")) {
return true;
}
_timeout.Load(obj);
_wait = obs_data_get_bool(obj, "wait");
return true;
}
@@ -52,14 +66,30 @@ std::string MacroActionRun::GetShortDesc() const
MacroActionRunEdit::MacroActionRunEdit(
QWidget *parent, std::shared_ptr<MacroActionRun> entryData)
: QWidget(parent), _procConfig(new ProcessConfigEdit(this))
: QWidget(parent),
_procConfig(new ProcessConfigEdit(this)),
_waitLayout(new QHBoxLayout()),
_wait(new QCheckBox()),
_timeout(new DurationSelection(this, true, 0.1))
{
QWidget::connect(_procConfig,
SIGNAL(ConfigChanged(const ProcessConfig &)), this,
SLOT(ProcessConfigChanged(const ProcessConfig &)));
QWidget::connect(_procConfig, SIGNAL(AdvancedSettingsEnabled()), this,
SLOT(ProcessConfigAdvancedSettingsShown()));
QWidget::connect(_wait, SIGNAL(stateChanged(int)), this,
SLOT(WaitChanged(int)));
QWidget::connect(_timeout, SIGNAL(DurationChanged(const Duration &)),
this, SLOT(TimeoutChanged(const Duration &)));
auto *layout = new QVBoxLayout;
PlaceWidgets(obs_module_text("AdvSceneSwitcher.action.run.entry.wait"),
_waitLayout,
{{"{{wait}}", _wait}, {"{{timeout}}", _timeout}});
SetLayoutVisible(_waitLayout, false);
auto layout = new QVBoxLayout;
layout->addWidget(_procConfig);
layout->addLayout(_waitLayout);
setLayout(layout);
_entryData = entryData;
@@ -73,6 +103,31 @@ void MacroActionRunEdit::UpdateEntryData()
return;
}
_procConfig->SetProcessConfig(_entryData->_procConfig);
_wait->setChecked(_entryData->_wait);
_timeout->SetDuration(_entryData->_timeout);
}
void MacroActionRunEdit::ProcessConfigAdvancedSettingsShown()
{
SetLayoutVisible(_waitLayout, true);
}
void MacroActionRunEdit::WaitChanged(int value)
{
if (_loading || !_entryData) {
return;
}
auto lock = LockContext();
_entryData->_wait = value;
}
void MacroActionRunEdit::TimeoutChanged(const Duration &timeout)
{
if (_loading || !_entryData) {
return;
}
auto lock = LockContext();
_entryData->_timeout = timeout;
}
void MacroActionRunEdit::ProcessConfigChanged(const ProcessConfig &conf)

View File

@@ -1,6 +1,9 @@
#pragma once
#include "macro-action-edit.hpp"
#include "process-config.hpp"
#include "duration-control.hpp"
#include <QCheckBox>
namespace advss {
@@ -19,6 +22,8 @@ public:
}
ProcessConfig _procConfig;
bool _wait = false;
Duration _timeout = 1;
private:
static bool _registered;
@@ -42,14 +47,19 @@ public:
private slots:
void ProcessConfigChanged(const ProcessConfig &);
void ProcessConfigAdvancedSettingsShown();
void WaitChanged(int);
void TimeoutChanged(const Duration &);
signals:
void HeaderInfoChanged(const QString &);
protected:
std::shared_ptr<MacroActionRun> _entryData;
private:
ProcessConfigEdit *_procConfig;
QHBoxLayout *_waitLayout;
QCheckBox *_wait;
DurationSelection *_timeout;
std::shared_ptr<MacroActionRun> _entryData;
bool _loading = true;
};

View File

@@ -1,5 +1,5 @@
#include "macro-action-scene-collection.hpp"
#include "plugin-state-helper.hpp"
#include "plugin-state-helpers.hpp"
#include "utility.hpp"
namespace advss {

View File

@@ -1,5 +1,7 @@
#include "macro-action-scene-switch.hpp"
#include "switcher-data.hpp"
#include "macro-helpers.hpp"
#include "plugin-state-helpers.hpp"
#include "macro.hpp"
#include "scene-switch-helpers.hpp"
#include "utility.hpp"
@@ -7,7 +9,7 @@ namespace advss {
using namespace std::chrono_literals;
const std::string MacroActionSwitchScene::id = "scene_switch";
const std::string MacroActionSwitchScene::id = GetSceneSwitchActionId().data();
bool MacroActionSwitchScene::_registered = MacroActionFactory::Register(
MacroActionSwitchScene::id,
@@ -33,9 +35,9 @@ static void waitForTransitionChange(OBSWeakSource &transition,
}
bool stillTransitioning = true;
while (stillTransitioning && !switcher->abortMacroWait &&
while (stillTransitioning && !MacroWaitShouldAbort() &&
!macro->GetStop()) {
switcher->macroTransitionCv.wait_for(*lock, time);
GetMacroTransitionCV().wait_for(*lock, time);
float t = obs_transition_get_time(source);
stillTransitioning = t < 1.0f && t > 0.0f;
}
@@ -49,8 +51,8 @@ static void waitForTransitionChangeFixedDuration(
auto time = std::chrono::high_resolution_clock::now() +
std::chrono::milliseconds(duration);
while (!switcher->abortMacroWait && !macro->GetStop()) {
if (switcher->macroTransitionCv.wait_until(*lock, time) ==
while (!MacroWaitShouldAbort() && !macro->GetStop()) {
if (GetMacroTransitionCV().wait_until(*lock, time) ==
std::cv_status::timeout) {
break;
}
@@ -91,11 +93,16 @@ static OBSWeakSource getOverrideTransition(OBSWeakSource &scene)
return transition;
}
static int getExpectedTransitionDuration(OBSWeakSource &scene, OBSWeakSource &t,
static int getExpectedTransitionDuration(OBSWeakSource &scene,
OBSWeakSource &transiton_,
double duration)
{
OBSWeakSource transition = t;
if (!switcher->transitionOverrideOverride) {
OBSWeakSource transition = transiton_;
// If we are not modifying the transition override, we will have to
// check, if a transition override is set by the user and use its
// duration instead
if (!ShouldModifyTransitionOverrides()) {
auto overrideTransition = getOverrideTransition(scene);
if (overrideTransition) {
transition = overrideTransition;
@@ -104,6 +111,7 @@ static int getExpectedTransitionDuration(OBSWeakSource &scene, OBSWeakSource &t,
}
}
}
if (isUsingFixedLengthTransition(transition)) {
return -1; // no API is available to access the fixed duration
}
@@ -118,9 +126,9 @@ bool MacroActionSwitchScene::WaitForTransition(OBSWeakSource &scene,
{
const int expectedTransitionDuration = getExpectedTransitionDuration(
scene, transition, _duration.Seconds());
switcher->abortMacroWait = false;
SetMacroAbortWait(false);
std::unique_lock<std::mutex> lock(switcher->m);
std::unique_lock<std::mutex> lock(*GetMutex());
if (expectedTransitionDuration < 0) {
waitForTransitionChange(transition, &lock, GetMacro());
} else {
@@ -128,7 +136,7 @@ bool MacroActionSwitchScene::WaitForTransition(OBSWeakSource &scene,
&lock, GetMacro());
}
return !switcher->abortMacroWait;
return !MacroWaitShouldAbort();
}
bool MacroActionSwitchScene::PerformAction()
@@ -145,8 +153,7 @@ bool MacroActionSwitchScene::PerformAction()
auto transition = _transition.GetTransition();
SwitchScene({scene, transition, (int)(_duration.Milliseconds())},
obs_frontend_preview_program_mode_active());
if (_blockUntilTransitionDone && scene &&
scene != switcher->currentScene) {
if (_blockUntilTransitionDone && scene && scene != GetCurrentScene()) {
return WaitForTransition(scene, transition);
}
return true;

View File

@@ -1,6 +1,5 @@
#include "macro-action-variable.hpp"
#include "macro-condition-edit.hpp"
#include "switcher-data.hpp"
#include "advanced-scene-switcher.hpp"
#include "macro.hpp"
#include "math-helpers.hpp"
@@ -51,6 +50,8 @@ const static std::map<MacroActionVariable::Type, std::string> actionTypes = {
"AdvSceneSwitcher.action.variable.type.extractJson"},
{MacroActionVariable::Type::SET_TO_TEMPVAR,
"AdvSceneSwitcher.action.variable.type.setToTempvar"},
{MacroActionVariable::Type::SCENE_ITEM_NAME,
"AdvSceneSwitcher.action.variable.type.sceneItemName"},
};
static void apppend(Variable &var, const std::string &value)
@@ -134,6 +135,49 @@ void MacroActionVariable::HandleMathExpression(Variable *var)
var->SetValue(std::get<double>(result));
}
struct GetSceneItemNameHelper {
int curIdx = 0;
int targetIdx = 0;
std::string name = "";
};
static bool getSceneItemAtIdx(obs_scene_t *, obs_sceneitem_t *item, void *ptr)
{
auto data = reinterpret_cast<GetSceneItemNameHelper *>(ptr);
if (obs_sceneitem_is_group(item)) {
obs_scene_t *scene = obs_sceneitem_group_get_scene(item);
obs_scene_enum_items(scene, getSceneItemAtIdx, ptr);
}
if (data->curIdx == data->targetIdx) {
data->name =
obs_source_get_name(obs_sceneitem_get_source(item));
data->curIdx--;
return false;
}
data->curIdx--;
return true;
}
void MacroActionVariable::SetToSceneItemName(Variable *var)
{
auto weakSource = _scene.GetScene();
if (!weakSource) {
var->SetValue("");
return;
}
auto index = _sceneItemIndex.GetValue();
if (index < 1) {
var->SetValue("");
return;
}
auto sceneItemCount = GetSceneItemCount(weakSource);
OBSSourceAutoRelease source = obs_weak_source_get_source(weakSource);
auto scene = obs_scene_from_source(source);
GetSceneItemNameHelper data{sceneItemCount, index};
obs_scene_enum_items(scene, getSceneItemAtIdx, &data);
var->SetValue(data.name);
}
struct AskForInputParams {
QString prompt;
QString placeholder;
@@ -268,6 +312,9 @@ bool MacroActionVariable::PerformAction()
var->SetValue(*value);
return true;
}
case Type::SCENE_ITEM_NAME:
SetToSceneItemName(var.get());
return true;
}
return true;
@@ -299,6 +346,7 @@ bool MacroActionVariable::Save(obs_data_t *obj) const
_envVariableName.Save(obj, "environmentVariableName");
_scene.Save(obj);
_tempVar.Save(obj);
_sceneItemIndex.Save(obj, "sceneItemIndex");
return true;
}
@@ -328,6 +376,7 @@ bool MacroActionVariable::Load(obs_data_t *obj)
_envVariableName.Load(obj, "environmentVariableName");
_scene.Load(obj);
_tempVar.Load(obj, GetMacro());
_sceneItemIndex.Load(obj, "sceneItemIndex");
return true;
}
@@ -457,7 +506,8 @@ MacroActionVariableEdit::MacroActionVariableEdit(
_envVariable(new VariableLineEdit(this)),
_scenes(new SceneSelectionWidget(this, true, false, true, true,
true)),
_tempVars(new TempVariableSelection(this))
_tempVars(new TempVariableSelection(this)),
_sceneItemIndex(new VariableSpinBox())
{
_numValue->setMinimum(-9999999999);
_numValue->setMaximum(9999999999);
@@ -475,6 +525,7 @@ MacroActionVariableEdit::MacroActionVariableEdit(
_regexMatchIdx->setSuffix(".");
_inputPrompt->setSizePolicy(QSizePolicy::MinimumExpanding,
QSizePolicy::Preferred);
_sceneItemIndex->setMinimum(1);
populateTypeSelection(_actions);
QWidget::connect(_variables, SIGNAL(SelectionChanged(const QString &)),
@@ -523,6 +574,10 @@ MacroActionVariableEdit::MacroActionVariableEdit(
QWidget::connect(_tempVars,
SIGNAL(SelectionChanged(const TempVariableRef &)),
this, SLOT(SelectionChanged(const TempVariableRef &)));
QWidget::connect(
_sceneItemIndex,
SIGNAL(NumberVariableChanged(const NumberVariable<int> &)),
this, SLOT(SceneItemIndexChanged(const NumberVariable<int> &)));
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
{"{{variables}}", _variables},
@@ -544,6 +599,7 @@ MacroActionVariableEdit::MacroActionVariableEdit(
{"{{envVariableName}}", _envVariable},
{"{{scenes}}", _scenes},
{"{{tempVars}}", _tempVars},
{"{{sceneItemIndex}}", _sceneItemIndex},
};
auto entryLayout = new QHBoxLayout;
PlaceWidgets(obs_module_text("AdvSceneSwitcher.action.variable.entry"),
@@ -638,6 +694,7 @@ void MacroActionVariableEdit::UpdateEntryData()
_envVariable->setText(_entryData->_envVariableName);
_scenes->SetScene(_entryData->_scene);
_tempVars->SetVariable(_entryData->_tempVar);
_sceneItemIndex->SetValue(_entryData->_sceneItemIndex);
SetWidgetVisibility();
}
@@ -982,6 +1039,17 @@ void MacroActionVariableEdit::SelectionChanged(const TempVariableRef &var)
_entryData->_tempVar = var;
}
void MacroActionVariableEdit::SceneItemIndexChanged(
const NumberVariable<int> &value)
{
if (_loading || !_entryData) {
return;
}
auto lock = LockContext();
_entryData->_sceneItemIndex = value;
}
void MacroActionVariableEdit::SetWidgetVisibility()
{
if (!_entryData) {
@@ -1058,10 +1126,15 @@ void MacroActionVariableEdit::SetWidgetVisibility()
}
_envVariable->setVisible(_entryData->_type ==
MacroActionVariable::Type::ENV_VARIABLE);
_scenes->setVisible(_entryData->_type ==
MacroActionVariable::Type::SCENE_ITEM_COUNT);
_scenes->setVisible(
_entryData->_type ==
MacroActionVariable::Type::SCENE_ITEM_COUNT ||
_entryData->_type ==
MacroActionVariable::Type::SCENE_ITEM_NAME);
_tempVars->setVisible(_entryData->_type ==
MacroActionVariable::Type::SET_TO_TEMPVAR);
_sceneItemIndex->setVisible(_entryData->_type ==
MacroActionVariable::Type::SCENE_ITEM_NAME);
adjustSize();
updateGeometry();
}

View File

@@ -6,6 +6,7 @@
#include "scene-selection.hpp"
#include "variable-line-edit.hpp"
#include "variable-text-edit.hpp"
#include "variable-spinbox.hpp"
namespace advss {
@@ -44,6 +45,7 @@ public:
STRING_LENGTH,
EXTRACT_JSON,
SET_TO_TEMPVAR,
SCENE_ITEM_NAME,
};
Type _type = Type::SET_FIXED_VALUE;
@@ -75,6 +77,7 @@ public:
#endif
SceneSelection _scene;
TempVariableRef _tempVar;
IntVariable _sceneItemIndex = 1;
private:
void DecrementCurrentSegmentVariableRef();
@@ -82,6 +85,7 @@ private:
void HandleRegexSubString(Variable *);
void HandleFindAndReplace(Variable *);
void HandleMathExpression(Variable *);
void SetToSceneItemName(Variable *);
std::weak_ptr<MacroSegment> _macroSegment;
int _segmentIdxLoadValue = -1;
@@ -129,6 +133,7 @@ private slots:
void EnvVariableChanged();
void SceneChanged(const SceneSelection &);
void SelectionChanged(const TempVariableRef &var);
void SceneItemIndexChanged(const NumberVariable<int> &);
signals:
void HeaderInfoChanged(const QString &);
@@ -167,6 +172,7 @@ private:
VariableLineEdit *_envVariable;
SceneSelectionWidget *_scenes;
TempVariableSelection *_tempVars;
VariableSpinBox *_sceneItemIndex;
std::shared_ptr<MacroActionVariable> _entryData;
QTimer _timer;

View File

@@ -1,5 +1,7 @@
#include "macro-action-wait.hpp"
#include "switcher-data.hpp"
#include "macro-helpers.hpp"
#include "macro.hpp"
#include "sync-helpers.hpp"
#include "utility.hpp"
#include <random>
@@ -24,8 +26,8 @@ static std::default_random_engine re(rd());
static void waitHelper(std::unique_lock<std::mutex> *lock, Macro *macro,
std::chrono::high_resolution_clock::time_point &time)
{
while (!switcher->abortMacroWait && !macro->GetStop()) {
if (switcher->macroWaitCv.wait_until(*lock, time) ==
while (!MacroWaitShouldAbort() && !macro->GetStop()) {
if (GetMacroWaitCV().wait_until(*lock, time) ==
std::cv_status::timeout) {
break;
}
@@ -53,11 +55,11 @@ bool MacroActionWait::PerformAction()
auto time = std::chrono::high_resolution_clock::now() +
std::chrono::milliseconds((int)(sleepDuration * 1000));
switcher->abortMacroWait = false;
std::unique_lock<std::mutex> lock(switcher->m);
SetMacroAbortWait(false);
std::unique_lock<std::mutex> lock(*GetMutex());
waitHelper(&lock, GetMacro(), time);
return !switcher->abortMacroWait;
return !MacroWaitShouldAbort();
}
bool MacroActionWait::Save(obs_data_t *obj) const

View File

@@ -62,17 +62,17 @@ MacroConditionAudio::~MacroConditionAudio()
bool MacroConditionAudio::CheckOutputCondition()
{
bool ret = false;
auto s = obs_weak_source_get_source(_audioSource.GetSource());
OBSSourceAutoRelease source =
obs_weak_source_get_source(_audioSource.GetSource());
// peak will have a value from -60 db to 0 db so we need to scale it
double curVolume = ((double)_peak + 60) * 1.7;
switch (_outputCondition) {
case OutputCondition::ABOVE:
// peak will have a value from -60 db to 0 db
ret = curVolume > _volume;
break;
case OutputCondition::BELOW:
// peak will have a value from -60 db to 0 db
ret = curVolume < _volume;
break;
default:
@@ -84,21 +84,21 @@ bool MacroConditionAudio::CheckOutputCondition()
// Reset for next check
_peak = -std::numeric_limits<float>::infinity();
obs_source_release(s);
if (_audioSource.GetType() == SourceSelection::Type::VARIABLE) {
ResetVolmeter();
}
return ret;
return ret && source;
}
bool MacroConditionAudio::CheckVolumeCondition()
{
bool ret = false;
auto s = obs_weak_source_get_source(_audioSource.GetSource());
OBSSourceAutoRelease source =
obs_weak_source_get_source(_audioSource.GetSource());
float curVolume = obs_source_get_volume(s);
bool muted = obs_source_muted(s);
float curVolume = obs_source_get_volume(source);
bool muted = obs_source_muted(source);
switch (_volumeCondition) {
case VolumeCondition::ABOVE:
@@ -126,10 +126,9 @@ bool MacroConditionAudio::CheckVolumeCondition()
}
SetTempVarValue("configured_volume", std::to_string(curVolume));
SetTempVarValue("muted", muted ? "true" : "false");
SetTempVarValue("muted", (source && muted) ? "true" : "false");
obs_source_release(s);
return ret;
return ret && source;
}
bool MacroConditionAudio::CheckSyncOffset()
@@ -139,8 +138,9 @@ bool MacroConditionAudio::CheckSyncOffset()
}
bool ret = false;
auto s = obs_weak_source_get_source(_audioSource.GetSource());
auto curOffset = obs_source_get_sync_offset(s) / nsPerMs;
OBSSourceAutoRelease source =
obs_weak_source_get_source(_audioSource.GetSource());
auto curOffset = obs_source_get_sync_offset(source) / nsPerMs;
if (_outputCondition == OutputCondition::ABOVE) {
ret = curOffset > _syncOffset;
} else {
@@ -148,8 +148,7 @@ bool MacroConditionAudio::CheckSyncOffset()
}
SetVariableValue(std::to_string(curOffset));
SetTempVarValue("sync_offset", std::to_string(curOffset));
obs_source_release(s);
return ret;
return ret && source;
}
bool MacroConditionAudio::CheckMonitor()
@@ -159,13 +158,13 @@ bool MacroConditionAudio::CheckMonitor()
}
bool ret = false;
auto s = obs_weak_source_get_source(_audioSource.GetSource());
ret = obs_source_get_monitoring_type(s) == _monitorType;
OBSSourceAutoRelease source =
obs_weak_source_get_source(_audioSource.GetSource());
ret = obs_source_get_monitoring_type(source) == _monitorType;
SetVariableValue("");
SetTempVarValue("monitor",
std::to_string(obs_source_get_monitoring_type(s)));
obs_source_release(s);
return ret;
std::to_string(obs_source_get_monitoring_type(source)));
return ret && source;
}
bool MacroConditionAudio::CheckBalance()
@@ -175,8 +174,9 @@ bool MacroConditionAudio::CheckBalance()
}
bool ret = false;
auto s = obs_weak_source_get_source(_audioSource.GetSource());
auto curBalance = obs_source_get_balance_value(s);
OBSSourceAutoRelease source =
obs_weak_source_get_source(_audioSource.GetSource());
auto curBalance = obs_source_get_balance_value(source);
if (_outputCondition == OutputCondition::ABOVE) {
ret = curBalance > _balance;
} else {
@@ -184,8 +184,7 @@ bool MacroConditionAudio::CheckBalance()
}
SetVariableValue(std::to_string(curBalance));
SetTempVarValue("balance", std::to_string(curBalance));
obs_source_release(s);
return ret;
return ret && source;
}
bool MacroConditionAudio::CheckCondition()
@@ -239,13 +238,12 @@ obs_volmeter_t *AddVolmeterToSource(MacroConditionAudio *entry,
obs_volmeter_t *volmeter = obs_volmeter_create(OBS_FADER_LOG);
obs_volmeter_add_callback(volmeter, MacroConditionAudio::SetVolumeLevel,
entry);
obs_source_t *as = obs_weak_source_get_source(source);
if (!obs_volmeter_attach_source(volmeter, as)) {
const char *name = obs_source_get_name(as);
OBSSourceAutoRelease audioSource = obs_weak_source_get_source(source);
if (!obs_volmeter_attach_source(volmeter, audioSource)) {
const char *name = obs_source_get_name(audioSource);
blog(LOG_WARNING, "failed to attach volmeter to source %s",
name);
}
obs_source_release(as);
return volmeter;
}
@@ -455,10 +453,9 @@ MacroConditionAudioEdit::MacroConditionAudioEdit(
void MacroConditionAudioEdit::UpdateVolmeterSource()
{
delete _volMeter;
obs_source_t *soruce = obs_weak_source_get_source(
OBSSourceAutoRelease soruce = obs_weak_source_get_source(
_entryData->_audioSource.GetSource());
_volMeter = new VolControl(soruce);
obs_source_release(soruce);
_volMeter = new VolControl(soruce.Get());
QLayout *layout = this->layout();
layout->addWidget(_volMeter);

View File

@@ -1,5 +1,4 @@
#include "macro-condition-cursor.hpp"
#include "switcher-data.hpp"
#include "platform-funcs.hpp"
#include "utility.hpp"
@@ -61,13 +60,15 @@ bool MacroConditionCursor::CheckCondition()
std::to_string(cursorPos.second));
break;
case Condition::MOVING:
ret = switcher->cursorPosChanged;
ret = cursorPos.first != _lastCursorPosition.first ||
cursorPos.second != _lastCursorPosition.second;
break;
case Condition::CLICK:
ret = CheckClick();
break;
}
_lastCheckTime = std::chrono::high_resolution_clock::now();
_lastCursorPosition = cursorPos;
if (GetVariableValue().empty()) {
SetVariableValue(ret ? "true" : "false");

View File

@@ -41,6 +41,7 @@ public:
private:
bool CheckClick();
std::chrono::high_resolution_clock::time_point _lastCheckTime{};
std::pair<int, int> _lastCursorPosition;
static bool _registered;
static const std::string id;

View File

@@ -638,10 +638,24 @@ void MacroConditionDateEdit::AdvancedSettingsToggleClicked()
{
auto lock = LockContext();
_entryData->_dayOfWeekCheck = !_entryData->_dayOfWeekCheck;
_entryData->_condition = MacroConditionDate::Condition::AT;
}
_condition->setCurrentIndex(0);
_weekCondition->setCurrentIndex(0);
switch (_entryData->_condition) {
case MacroConditionDate::Condition::BETWEEN:
case MacroConditionDate::Condition::PATTERN:
_condition->setCurrentIndex(0);
_weekCondition->setCurrentIndex(0);
break;
case MacroConditionDate::Condition::AT:
case MacroConditionDate::Condition::AFTER:
case MacroConditionDate::Condition::BEFORE:
_weekCondition->setCurrentIndex(
static_cast<int>(_entryData->_condition));
_condition->setCurrentIndex(
static_cast<int>(_entryData->_condition));
break;
}
SetWidgetStatus();
emit HeaderInfoChanged(
QString::fromStdString(_entryData->GetShortDesc()));

View File

@@ -1,84 +1,24 @@
#include "advanced-scene-switcher.hpp"
#include "switcher-data.hpp"
#include "macro-condition-edit.hpp"
#include "macro.hpp"
#include "macro-properties.hpp"
#include "advanced-scene-switcher.hpp"
#include "macro-condition-scene.hpp"
#include "section.hpp"
#include "utility.hpp"
namespace advss {
std::map<std::string, MacroConditionInfo> &MacroConditionFactory::GetMap()
{
static std::map<std::string, MacroConditionInfo> _methods;
return _methods;
}
bool MacroConditionFactory::Register(const std::string &id,
MacroConditionInfo info)
{
if (auto it = GetMap().find(id); it == GetMap().end()) {
GetMap()[id] = info;
return true;
}
return false;
}
std::shared_ptr<MacroCondition>
MacroConditionFactory::Create(const std::string &id, Macro *m)
{
if (auto it = GetMap().find(id); it != GetMap().end()) {
return it->second._createFunc(m);
}
return nullptr;
}
QWidget *
MacroConditionFactory::CreateWidget(const std::string &id, QWidget *parent,
std::shared_ptr<MacroCondition> cond)
{
if (auto it = GetMap().find(id); it != GetMap().end()) {
return it->second._createWidgetFunc(parent, cond);
}
return nullptr;
}
std::string MacroConditionFactory::GetConditionName(const std::string &id)
{
if (auto it = GetMap().find(id); it != GetMap().end()) {
return it->second._name;
}
return "unknown condition";
}
std::string MacroConditionFactory::GetIdByName(const QString &name)
{
for (auto it : GetMap()) {
if (name == obs_module_text(it.second._name.c_str())) {
return it.first;
}
}
return "";
}
bool MacroConditionFactory::UsesDurationModifier(const std::string &id)
{
if (auto it = GetMap().find(id); it != GetMap().end()) {
return it->second._useDurationModifier;
}
return false;
}
static inline void populateLogicSelection(QComboBox *list, bool root = false)
{
if (root) {
for (auto entry : MacroCondition::logicTypes) {
for (const auto &entry : MacroCondition::logicTypes) {
if (static_cast<int>(entry.first) < logic_root_offset) {
list->addItem(obs_module_text(
entry.second._name.c_str()));
}
}
} else {
for (auto entry : MacroCondition::logicTypes) {
for (const auto &entry : MacroCondition::logicTypes) {
if (static_cast<int>(entry.first) >=
logic_root_offset) {
list->addItem(obs_module_text(
@@ -90,7 +30,7 @@ static inline void populateLogicSelection(QComboBox *list, bool root = false)
static inline void populateConditionSelection(QComboBox *list)
{
for (auto &[_, condition] :
for (const auto &[_, condition] :
MacroConditionFactory::GetConditionTypes()) {
QString entry(obs_module_text(condition._name.c_str()));
if (list->findText(entry) == -1) {
@@ -179,7 +119,7 @@ void DurationModifierEdit::Collapse(bool collapse)
MacroConditionEdit::MacroConditionEdit(
QWidget *parent, std::shared_ptr<MacroCondition> *entryData,
const std::string &id, bool root)
: MacroSegmentEdit(switcher->macroProperties._highlightConditions,
: MacroSegmentEdit(GetGlobalMacroProperties()._highlightConditions,
parent),
_logicSelection(new QComboBox()),
_conditionSelection(new FilterComboBox()),

View File

@@ -1,38 +1,12 @@
#pragma once
#include "macro-condition.hpp"
#include "macro-condition-factory.hpp"
#include "filter-combo-box.hpp"
#include <memory>
namespace advss {
struct MacroConditionInfo {
using TCreateMethod = std::shared_ptr<MacroCondition> (*)(Macro *m);
using TCreateWidgetMethod =
QWidget *(*)(QWidget *parent, std::shared_ptr<MacroCondition>);
TCreateMethod _createFunc = nullptr;
TCreateWidgetMethod _createWidgetFunc = nullptr;
std::string _name;
bool _useDurationModifier = true;
};
class MacroConditionFactory {
public:
MacroConditionFactory() = delete;
static bool Register(const std::string &, MacroConditionInfo);
static std::shared_ptr<MacroCondition> Create(const std::string &,
Macro *m);
static QWidget *CreateWidget(const std::string &id, QWidget *parent,
std::shared_ptr<MacroCondition>);
static auto GetConditionTypes() { return GetMap(); }
static std::string GetConditionName(const std::string &);
static std::string GetIdByName(const QString &name);
static bool UsesDurationModifier(const std::string &id);
private:
static std::map<std::string, MacroConditionInfo> &GetMap();
};
class DurationModifierEdit : public QWidget {
Q_OBJECT
public:

View File

@@ -0,0 +1,66 @@
#include "macro-condition-factory.hpp"
namespace advss {
std::map<std::string, MacroConditionInfo> &MacroConditionFactory::GetMap()
{
static std::map<std::string, MacroConditionInfo> _methods;
return _methods;
}
bool MacroConditionFactory::Register(const std::string &id,
MacroConditionInfo info)
{
if (auto it = GetMap().find(id); it == GetMap().end()) {
GetMap()[id] = info;
return true;
}
return false;
}
std::shared_ptr<MacroCondition>
MacroConditionFactory::Create(const std::string &id, Macro *m)
{
if (auto it = GetMap().find(id); it != GetMap().end()) {
return it->second._create(m);
}
return nullptr;
}
QWidget *
MacroConditionFactory::CreateWidget(const std::string &id, QWidget *parent,
std::shared_ptr<MacroCondition> cond)
{
if (auto it = GetMap().find(id); it != GetMap().end()) {
return it->second._createWidget(parent, cond);
}
return nullptr;
}
std::string MacroConditionFactory::GetConditionName(const std::string &id)
{
if (auto it = GetMap().find(id); it != GetMap().end()) {
return it->second._name;
}
return "unknown condition";
}
std::string MacroConditionFactory::GetIdByName(const QString &name)
{
for (auto it : GetMap()) {
if (name == obs_module_text(it.second._name.c_str())) {
return it.first;
}
}
return "";
}
bool MacroConditionFactory::UsesDurationModifier(const std::string &id)
{
if (auto it = GetMap().find(id); it != GetMap().end()) {
return it->second._useDurationModifier;
}
return false;
}
} // namespace advss

View File

@@ -0,0 +1,35 @@
#pragma once
#include "macro-condition.hpp"
#include <memory>
namespace advss {
struct MacroConditionInfo {
using CreateCondition = std::shared_ptr<MacroCondition> (*)(Macro *m);
using CreateConditionWidget =
QWidget *(*)(QWidget *parent, std::shared_ptr<MacroCondition>);
CreateCondition _create = nullptr;
CreateConditionWidget _createWidget = nullptr;
std::string _name;
bool _useDurationModifier = true;
};
class MacroConditionFactory {
public:
MacroConditionFactory() = delete;
static bool Register(const std::string &, MacroConditionInfo);
static std::shared_ptr<MacroCondition> Create(const std::string &,
Macro *m);
static QWidget *CreateWidget(const std::string &id, QWidget *parent,
std::shared_ptr<MacroCondition>);
static auto GetConditionTypes() { return GetMap(); }
static std::string GetConditionName(const std::string &);
static std::string GetIdByName(const QString &name);
static bool UsesDurationModifier(const std::string &id);
private:
static std::map<std::string, MacroConditionInfo> &GetMap();
};
} // namespace advss

View File

@@ -1,7 +1,7 @@
#include "macro-condition-file.hpp"
#include "utility.hpp"
#include "switcher-data.hpp"
#include "curl-helper.hpp"
#include "plugin-state-helpers.hpp"
#include <QTextStream>
#include <QFileDialog>
@@ -28,16 +28,16 @@ static size_t WriteCallback(void *contents, size_t size, size_t nmemb,
static std::string getRemoteData(std::string &url)
{
std::string readBuffer;
switcher->curl.SetOpt(CURLOPT_URL, url.c_str());
switcher->curl.SetOpt(CURLOPT_WRITEFUNCTION, WriteCallback);
switcher->curl.SetOpt(CURLOPT_WRITEDATA, &readBuffer);
CurlHelper::SetOpt(CURLOPT_URL, url.c_str());
CurlHelper::SetOpt(CURLOPT_WRITEFUNCTION, WriteCallback);
CurlHelper::SetOpt(CURLOPT_WRITEDATA, &readBuffer);
// Set timeout to at least one second
int timeout = switcher->interval / 1000;
int timeout = GetIntervalValue() / 1000;
if (timeout == 0) {
timeout = 1;
}
switcher->curl.SetOpt(CURLOPT_TIMEOUT, 1);
switcher->curl.Perform();
CurlHelper::SetOpt(CURLOPT_TIMEOUT, 1);
CurlHelper::Perform();
return readBuffer;
}

View File

@@ -1,5 +1,7 @@
#include "macro-condition-media.hpp"
#include "switcher-data.hpp"
#include "macro.hpp"
#include "plugin-state-helpers.hpp"
#include "scene-switch-helpers.hpp"
#include "utility.hpp"
namespace advss {
@@ -148,26 +150,20 @@ bool MacroConditionMedia::CheckMediaMatch()
if (!_source.GetSource()) {
return false;
}
bool match = false;
bool matched = CheckState() && CheckTime();
if (matched && !(_onlyMatchOnChagne && _alreadyMatched)) {
match = true;
}
_alreadyMatched = matched;
// reset for next check
_stopped = false;
_ended = false;
_next = false;
return match;
return matched;
}
void MacroConditionMedia::HandleSceneChange()
{
UpdateMediaSourcesOfSceneList();
_lastConfigureScene = switcher->currentScene;
_lastConfigureScene = GetCurrentScene();
}
bool MacroConditionMedia::CheckCondition()
@@ -194,7 +190,7 @@ bool MacroConditionMedia::CheckCondition()
break;
}
if (_lastConfigureScene != switcher->currentScene) {
if (_lastConfigureScene != GetCurrentScene()) {
HandleSceneChange();
}
@@ -210,7 +206,6 @@ bool MacroConditionMedia::Save(obs_data_t *obj) const
obs_data_set_int(obj, "state", static_cast<int>(_state));
obs_data_set_int(obj, "restriction", static_cast<int>(_restriction));
_time.Save(obj);
obs_data_set_bool(obj, "matchOnChagne", _onlyMatchOnChagne);
obs_data_set_int(obj, "version", 0);
return true;
}
@@ -268,7 +263,6 @@ bool MacroConditionMedia::Load(obs_data_t *obj)
_restriction = static_cast<MacroConditionMedia::Time>(
obs_data_get_int(obj, "restriction"));
_time.Load(obj);
_onlyMatchOnChagne = obs_data_get_bool(obj, "matchOnChagne");
if (_sourceType == Type::SOURCE) {
obs_source_t *mediasource =
@@ -408,9 +402,7 @@ MacroConditionMediaEdit::MacroConditionMediaEdit(
_sources(new SourceSelectionWidget(this, QStringList(), true)),
_states(new QComboBox()),
_timeRestrictions(new QComboBox()),
_time(new DurationSelection()),
_onChange(new QCheckBox(obs_module_text(
"AdvSceneSwitcher.condition.media.matchOnChange")))
_time(new DurationSelection())
{
_states->setToolTip(obs_module_text(
"AdvSceneSwitcher.condition.media.inconsistencyInfo"));
@@ -432,14 +424,12 @@ MacroConditionMediaEdit::MacroConditionMediaEdit(
this, SLOT(TimeRestrictionChanged(int)));
QWidget::connect(_time, SIGNAL(DurationChanged(const Duration &)), this,
SLOT(TimeChanged(const Duration &)));
QWidget::connect(_onChange, SIGNAL(stateChanged(int)), this,
SLOT(OnChangeChanged(int)));
populateSourceTypes(_sourceTypes);
populateMediaStates(_states);
populateMediaTimes(_timeRestrictions);
QHBoxLayout *entryLayout = new QHBoxLayout;
auto layout = new QHBoxLayout;
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
{"{{sourceTypes}}", _sourceTypes},
{"{{mediaSources}}", _sources},
@@ -449,11 +439,8 @@ MacroConditionMediaEdit::MacroConditionMediaEdit(
{"{{time}}", _time},
};
PlaceWidgets(obs_module_text("AdvSceneSwitcher.condition.media.entry"),
entryLayout, widgetPlaceholders);
QVBoxLayout *mainLayout = new QVBoxLayout;
mainLayout->addLayout(entryLayout);
mainLayout->addWidget(_onChange);
setLayout(mainLayout);
layout, widgetPlaceholders);
setLayout(layout);
_entryData = entryData;
UpdateEntryData();
@@ -572,28 +559,12 @@ void MacroConditionMediaEdit::TimeChanged(const Duration &dur)
}
}
void MacroConditionMediaEdit::OnChangeChanged(int value)
{
if (_loading || !_entryData) {
return;
}
auto lock = LockContext();
_entryData->_onlyMatchOnChagne = value;
if (_entryData->_sourceType != MacroConditionMedia::Type::SOURCE) {
_entryData->UpdateMediaSourcesOfSceneList();
}
}
void MacroConditionMediaEdit::SetWidgetVisibility()
{
_sources->setVisible(_entryData->_sourceType ==
MacroConditionMedia::Type::SOURCE);
_scenes->setVisible(_entryData->_sourceType !=
MacroConditionMedia::Type::SOURCE);
if (!_onChange->isChecked()) {
_onChange->hide();
}
}
static int getIdxFromMediaState(MacroConditionMedia::State state)
@@ -625,7 +596,6 @@ void MacroConditionMediaEdit::UpdateEntryData()
MacroConditionMedia::Time::TIME_RESTRICTION_NONE) {
_time->setDisabled(true);
}
_onChange->setChecked(_entryData->_onlyMatchOnChagne);
SetWidgetVisibility();
}

View File

@@ -72,7 +72,6 @@ public:
OBSWeakSource _rawSource = nullptr;
std::vector<MacroConditionMedia> _sourceGroup;
Duration _time;
bool _onlyMatchOnChagne = false;
private:
bool CheckTime();
@@ -84,9 +83,7 @@ private:
bool _stopped = false;
bool _ended = false;
bool _next = false;
// TODO: Remove _alreadyMatched as it does not make much sense when
// time restrictions for macro conditions are available.
bool _alreadyMatched = false;
// Workaround to enable use of "ended" to specify end of VLC playlist
bool _previousStateEnded = false;
// Used to keep track of scene changes
@@ -119,7 +116,6 @@ private slots:
void StateChanged(int index);
void TimeRestrictionChanged(int index);
void TimeChanged(const Duration &seconds);
void OnChangeChanged(int);
signals:
void HeaderInfoChanged(const QString &);
@@ -130,7 +126,6 @@ protected:
QComboBox *_states;
QComboBox *_timeRestrictions;
DurationSelection *_time;
QCheckBox *_onChange;
std::shared_ptr<MacroConditionMedia> _entryData;
private:

View File

@@ -1,5 +1,6 @@
#include "macro-condition-plugin-state.hpp"
#include "switcher-data.hpp"
#include "macro-helpers.hpp"
#include "plugin-state-helpers.hpp"
#include "utility.hpp"
namespace advss {
@@ -33,7 +34,7 @@ const static std::map<MacroConditionPluginState::Condition, std::string>
MacroConditionPluginState::~MacroConditionPluginState()
{
if (_condition == Condition::OBS_SHUTDOWN) {
switcher->shutdownConditionCount--;
GetShutdownConditionCount()--;
}
}
@@ -41,15 +42,15 @@ bool MacroConditionPluginState::CheckCondition()
{
switch (_condition) {
case Condition::PLUGIN_SCENE_CHANGE:
return switcher->macroSceneSwitched;
return MacroSwitchedScene();
case Condition::PLUGIN_RUNNING:
return true;
case Condition::OBS_SHUTDOWN:
return switcher->obsIsShuttingDown;
return OBSIsShuttingDown();
case Condition::PLUGIN_START:
return switcher->firstInterval;
return IsFirstInterval();
case Condition::PLUGIN_RESTART:
return switcher->firstIntervalAfterStop;
return IsFirstIntervalAfterStop();
case Condition::SCENE_COLLECTION_CHANGE:
if (_firstCheckAfterSceneCollectionChange) {
_firstCheckAfterSceneCollectionChange = false;
@@ -83,25 +84,33 @@ bool MacroConditionPluginState::Load(obs_data_t *obj)
obs_data_get_int(obj, "condition"));
switch (oldValue) {
case PluginStateCondition::SCENE_SWITCHED:
_condition = Condition::PLUGIN_SCENE_CHANGE;
SetCondition(Condition::PLUGIN_SCENE_CHANGE);
break;
case PluginStateCondition::RUNNING:
_condition = Condition::PLUGIN_RUNNING;
SetCondition(Condition::PLUGIN_RUNNING);
break;
case PluginStateCondition::SHUTDOWN:
_condition = Condition::OBS_SHUTDOWN;
SetCondition(Condition::OBS_SHUTDOWN);
break;
}
} else {
_condition = static_cast<Condition>(
obs_data_get_int(obj, "condition"));
}
if (_condition == Condition::OBS_SHUTDOWN) {
switcher->shutdownConditionCount++;
SetCondition(static_cast<Condition>(
obs_data_get_int(obj, "condition")));
}
return true;
}
void MacroConditionPluginState::SetCondition(Condition newValue)
{
if (_condition == Condition::OBS_SHUTDOWN) {
GetShutdownConditionCount()--;
}
if (newValue == Condition::OBS_SHUTDOWN) {
GetShutdownConditionCount()++;
}
_condition = newValue;
}
static inline void populateConditionSelection(QComboBox *list)
{
for (const auto &[action, name] : pluginStateConditionTypes) {
@@ -148,17 +157,9 @@ void MacroConditionPluginStateEdit::ConditionChanged(int idx)
}
auto lock = LockContext();
if (_entryData->_condition ==
MacroConditionPluginState::Condition::OBS_SHUTDOWN) {
switcher->shutdownConditionCount--;
}
_entryData->_condition =
_entryData->SetCondition(
static_cast<MacroConditionPluginState::Condition>(
_condition->itemData(idx).toInt());
if (_entryData->_condition ==
MacroConditionPluginState::Condition::OBS_SHUTDOWN) {
switcher->shutdownConditionCount++;
}
_condition->itemData(idx).toInt()));
SetWidgetVisibility();
}
@@ -168,15 +169,15 @@ void MacroConditionPluginStateEdit::UpdateEntryData()
return;
}
_condition->setCurrentIndex(
_condition->findData(static_cast<int>(_entryData->_condition)));
_condition->setCurrentIndex(_condition->findData(
static_cast<int>(_entryData->GetCondition())));
SetWidgetVisibility();
}
void MacroConditionPluginStateEdit::SetWidgetVisibility()
{
_shutdownLimitation->setVisible(
_entryData->_condition ==
_entryData->GetCondition() ==
MacroConditionPluginState::Condition::OBS_SHUTDOWN);
adjustSize();
updateGeometry();

View File

@@ -26,10 +26,11 @@ public:
SCENE_COLLECTION_CHANGE,
PLUGIN_SCENE_CHANGE,
};
Condition _condition = Condition::PLUGIN_SCENE_CHANGE;
void SetCondition(Condition);
Condition GetCondition() const { return _condition; }
private:
Condition _condition = Condition::PLUGIN_SCENE_CHANGE;
bool _firstCheckAfterSceneCollectionChange = true;
const static uint32_t _version;

View File

@@ -1,5 +1,4 @@
#include "macro-condition-process.hpp"
#include "switcher-data.hpp"
#include "platform-funcs.hpp"
#include "utility.hpp"
@@ -172,8 +171,9 @@ void MacroConditionProcessEdit::FocusChanged(int state)
void MacroConditionProcessEdit::UpdateFocusProcess()
{
_focusProcess->setText(
QString::fromStdString(switcher->currentForegroundProcess));
std::string name;
GetForegroundProcessName(name);
_focusProcess->setText(QString::fromStdString(name));
}
void MacroConditionProcessEdit::SetWidgetVisibility()

View File

@@ -178,6 +178,8 @@ bool SetupRecordingTimer()
case OBS_FRONTEND_EVENT_RECORDING_UNPAUSED:
recordingPaused = false;
break;
default:
break;
};
};
obs_frontend_add_event_callback(handleRecordingEvents, nullptr);

View File

@@ -1,5 +1,4 @@
#include "macro-condition-replay-buffer.hpp"
#include "switcher-data.hpp"
#include "utility.hpp"
namespace advss {
@@ -11,35 +10,63 @@ bool MacroConditionReplayBuffer::_registered = MacroConditionFactory::Register(
MacroConditionReplayBufferEdit::Create,
"AdvSceneSwitcher.condition.replay"});
const static std::map<ReplayBufferState, std::string> ReplayBufferStates = {
{ReplayBufferState::STOP,
"AdvSceneSwitcher.condition.replay.state.stopped"},
{ReplayBufferState::START,
"AdvSceneSwitcher.condition.replay.state.started"},
{ReplayBufferState::SAVE,
"AdvSceneSwitcher.condition.replay.state.saved"},
const static std::map<MacroConditionReplayBuffer::Condition, std::string>
conditions = {
{MacroConditionReplayBuffer::Condition::STOP,
"AdvSceneSwitcher.condition.replay.state.stopped"},
{MacroConditionReplayBuffer::Condition::START,
"AdvSceneSwitcher.condition.replay.state.started"},
{MacroConditionReplayBuffer::Condition::SAVE,
"AdvSceneSwitcher.condition.replay.state.saved"},
};
static bool replayBufferSaved = false;
static std::chrono::high_resolution_clock::time_point replayBufferSaveTime{};
static bool setupReplayBufferEventHandler();
static bool replayBufferEventHandlerIsSetup = setupReplayBufferEventHandler();
static bool setupReplayBufferEventHandler()
{
static auto handleReplayBufferEvent = [](enum obs_frontend_event event,
void *) {
switch (event) {
case OBS_FRONTEND_EVENT_REPLAY_BUFFER_SAVED:
replayBufferSaveTime =
std::chrono::high_resolution_clock::now();
break;
default:
break;
};
};
obs_frontend_add_event_callback(handleReplayBufferEvent, nullptr);
return true;
}
bool MacroConditionReplayBuffer::ReplayBufferWasSaved()
{
bool timersAreInitialized =
_saveTime.time_since_epoch().count() != 0 &&
replayBufferSaveTime.time_since_epoch().count() != 0;
bool newSaveOccurred = timersAreInitialized &&
_saveTime != replayBufferSaveTime;
_saveTime = replayBufferSaveTime;
return newSaveOccurred;
}
bool MacroConditionReplayBuffer::CheckCondition()
{
bool stateMatch = false;
switch (_state) {
case ReplayBufferState::STOP:
stateMatch = !obs_frontend_replay_buffer_active();
break;
case ReplayBufferState::START:
stateMatch = obs_frontend_replay_buffer_active();
break;
case ReplayBufferState::SAVE:
if (switcher->replayBufferSaved) {
stateMatch = true;
switcher->replayBufferSaved = false;
}
break;
case Condition::STOP:
return !obs_frontend_replay_buffer_active();
case Condition::START:
return obs_frontend_replay_buffer_active();
case Condition::SAVE:
return ReplayBufferWasSaved();
default:
break;
}
return stateMatch;
return false;
}
bool MacroConditionReplayBuffer::Save(obs_data_t *obj) const
@@ -52,37 +79,30 @@ bool MacroConditionReplayBuffer::Save(obs_data_t *obj) const
bool MacroConditionReplayBuffer::Load(obs_data_t *obj)
{
MacroCondition::Load(obj);
_state = static_cast<ReplayBufferState>(obs_data_get_int(obj, "state"));
_state = static_cast<Condition>(obs_data_get_int(obj, "state"));
return true;
}
static inline void populateStateSelection(QComboBox *list)
{
for (auto entry : ReplayBufferStates) {
for (const auto &entry : conditions) {
list->addItem(obs_module_text(entry.second.c_str()));
}
}
MacroConditionReplayBufferEdit::MacroConditionReplayBufferEdit(
QWidget *parent, std::shared_ptr<MacroConditionReplayBuffer> entryData)
: QWidget(parent)
: QWidget(parent), _state(new QComboBox())
{
_state = new QComboBox();
QWidget::connect(_state, SIGNAL(currentIndexChanged(int)), this,
SLOT(StateChanged(int)));
populateStateSelection(_state);
QHBoxLayout *mainLayout = new QHBoxLayout;
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
{"{{state}}", _state},
};
auto layout = new QHBoxLayout;
PlaceWidgets(obs_module_text("AdvSceneSwitcher.condition.replay.entry"),
mainLayout, widgetPlaceholders);
setLayout(mainLayout);
layout, {{"{{state}}", _state}});
setLayout(layout);
_entryData = entryData;
UpdateEntryData();
@@ -96,7 +116,8 @@ void MacroConditionReplayBufferEdit::StateChanged(int value)
}
auto lock = LockContext();
_entryData->_state = static_cast<ReplayBufferState>(value);
_entryData->_state =
static_cast<MacroConditionReplayBuffer::Condition>(value);
}
void MacroConditionReplayBufferEdit::UpdateEntryData()

View File

@@ -6,12 +6,6 @@
namespace advss {
enum class ReplayBufferState {
STOP,
START,
SAVE,
};
class MacroConditionReplayBuffer : public MacroCondition {
public:
MacroConditionReplayBuffer(Macro *m) : MacroCondition(m) {}
@@ -24,9 +18,18 @@ public:
return std::make_shared<MacroConditionReplayBuffer>(m);
}
ReplayBufferState _state = ReplayBufferState::STOP;
enum class Condition {
STOP,
START,
SAVE,
};
Condition _state = Condition::STOP;
private:
bool ReplayBufferWasSaved();
std::chrono::high_resolution_clock::time_point _saveTime = {};
static bool _registered;
static const std::string id;
};

View File

@@ -2,7 +2,6 @@
#include "utility.hpp"
#include <QProcess>
#include <QDesktopServices>
namespace advss {
@@ -28,17 +27,18 @@ bool MacroConditionRun::CheckCondition()
bool ret = false;
switch (_procStatus) {
case MacroConditionRun::Status::FAILED_TO_START:
switch (_error) {
case ProcessConfig::ProcStartError::FAILED_TO_START:
SetVariableValue("Failed to start process");
ret = false;
break;
case MacroConditionRun::Status::TIMEOUT:
case ProcessConfig::ProcStartError::TIMEOUT:
SetVariableValue("Timeout while running process");
ret = false;
break;
case MacroConditionRun::Status::OK:
ret = _checkExitCode ? _exitCode == _procExitCode : true;
case ProcessConfig::ProcStartError::CRASH:
SetVariableValue("Timeout while running process");
break;
case ProcessConfig::ProcStartError::NONE:
ret = _checkExitCode ? _exitCodeToCheck == _procExitCode : true;
SetVariableValue(std::to_string(_procExitCode));
break;
default:
@@ -56,42 +56,14 @@ bool MacroConditionRun::CheckCondition()
void MacroConditionRun::RunProcess()
{
QProcess process;
process.setWorkingDirectory(
QString::fromStdString(_procConfig.WorkingDir()));
process.start(QString::fromStdString(_procConfig.Path()),
_procConfig.Args());
int timeout = _timeout.Milliseconds();
auto result = _procConfig.StartProcessAndWait(_timeout.Milliseconds());
vblog(LOG_INFO, "run \"%s\" with a timeout of %d ms",
_procConfig.Path().c_str(), timeout);
bool procFinishedInTime = process.waitForFinished(timeout);
if (!procFinishedInTime) {
if (process.error() == QProcess::FailedToStart) {
vblog(LOG_INFO, "failed to start \"%s\"!",
_procConfig.Path().c_str());
_procStatus = Status::FAILED_TO_START;
} else {
vblog(LOG_INFO,
"timeout while running \"%s\"\nAttempting to kill process!",
_procConfig.Path().c_str());
process.kill();
process.waitForFinished();
_procStatus = Status::TIMEOUT;
}
if (std::holds_alternative<ProcessConfig::ProcStartError>(result)) {
_error = std::get<ProcessConfig::ProcStartError>(result);
} else {
_error = ProcessConfig::ProcStartError::NONE;
_procExitCode = std::get<int>(result);
}
bool validExitCode = process.exitStatus() == QProcess::NormalExit;
if ((_checkExitCode && !validExitCode) || !procFinishedInTime) {
_threadDone = true;
return;
}
_procExitCode = process.exitCode();
_procStatus = Status::OK;
_threadDone = true;
}
@@ -100,7 +72,7 @@ bool MacroConditionRun::Save(obs_data_t *obj) const
MacroCondition::Save(obj);
_procConfig.Save(obj);
obs_data_set_bool(obj, "checkExitCode", _checkExitCode);
obs_data_set_int(obj, "exitCode", _exitCode);
obs_data_set_int(obj, "exitCode", _exitCodeToCheck);
_timeout.Save(obj, "timeout");
return true;
}
@@ -110,7 +82,7 @@ bool MacroConditionRun::Load(obs_data_t *obj)
MacroCondition::Load(obj);
_procConfig.Load(obj);
_checkExitCode = obs_data_get_bool(obj, "checkExitCode");
_exitCode = obs_data_get_int(obj, "exitCode");
_exitCodeToCheck = obs_data_get_int(obj, "exitCode");
_timeout.Load(obj, "timeout");
return true;
}
@@ -174,7 +146,7 @@ void MacroConditionRunEdit::UpdateEntryData()
_procConfig->SetProcessConfig(_entryData->_procConfig);
_timeout->SetDuration(_entryData->_timeout);
_checkExitCode->setChecked(_entryData->_checkExitCode);
_exitCode->setValue(_entryData->_exitCode);
_exitCode->setValue(_entryData->_exitCodeToCheck);
}
void MacroConditionRunEdit::TimeoutChanged(const Duration &dur)
@@ -204,7 +176,7 @@ void MacroConditionRunEdit::ExitCodeChanged(int exitCode)
}
auto lock = LockContext();
_entryData->_exitCode = exitCode;
_entryData->_exitCodeToCheck = exitCode;
}
void MacroConditionRunEdit::ProcessConfigChanged(const ProcessConfig &conf)

View File

@@ -25,22 +25,15 @@ public:
ProcessConfig _procConfig;
bool _checkExitCode = true;
int _exitCode = 0;
int _exitCodeToCheck = 0;
Duration _timeout = Duration(0.1);
private:
enum class Status {
NONE,
FAILED_TO_START,
TIMEOUT,
OK,
};
void RunProcess();
std::thread _thread;
std::atomic_bool _threadDone{true};
Status _procStatus = Status::NONE;
ProcessConfig::ProcStartError _error =
ProcessConfig::ProcStartError::NONE;
int _procExitCode = 0;
static bool _registered;

View File

@@ -1,5 +1,5 @@
#include "macro-condition-scene.hpp"
#include "switcher-data.hpp"
#include "scene-switch-helpers.hpp"
#include "utility.hpp"
namespace advss {
@@ -53,23 +53,22 @@ static OBSWeakSource getCurrentSceneHelper(bool useTransitionTargetScene)
obs_source_release(current);
return weak;
}
return switcher->currentScene;
return GetCurrentScene();
}
static OBSWeakSource getPreviousSceneHelper(bool useTransitionTargetScene)
{
if (switcher->AnySceneTransitionStarted() && useTransitionTargetScene) {
return switcher->currentScene;
if (AnySceneTransitionStarted() && useTransitionTargetScene) {
return GetCurrentScene();
}
return switcher->previousScene;
return GetPreviousScene();
}
bool MacroConditionScene::CheckCondition()
{
bool sceneChanged = _lastSceneChangeTime !=
switcher->lastSceneChangeTime;
bool sceneChanged = _lastSceneChangeTime != GetLastSceneChangeTime();
if (sceneChanged) {
_lastSceneChangeTime = switcher->lastSceneChangeTime;
_lastSceneChangeTime = GetLastSceneChangeTime();
}
switch (_type) {
@@ -95,14 +94,14 @@ bool MacroConditionScene::CheckCondition()
return scene == _scene.GetScene(false);
}
case Type::CHANGED:
SetVariableValue(GetWeakSourceName(switcher->currentScene));
SetVariableValue(GetWeakSourceName(GetCurrentScene()));
SetTempVarValue("current",
GetWeakSourceName(switcher->currentScene));
GetWeakSourceName(GetCurrentScene()));
return sceneChanged;
case Type::NOT_CHANGED:
SetVariableValue(GetWeakSourceName(switcher->currentScene));
SetVariableValue(GetWeakSourceName(GetCurrentScene()));
SetTempVarValue("current",
GetWeakSourceName(switcher->currentScene));
GetWeakSourceName(GetCurrentScene()));
return !sceneChanged;
case Type::CURRENT_PATTERN: {
auto scene = getCurrentSceneHelper(_useTransitionTargetScene);
@@ -189,8 +188,8 @@ bool MacroConditionScene::Load(obs_data_t *obj)
break;
default:
blog(LOG_WARNING,
"failed to convert scene condition type (%d) for macro %s",
oldType, GetMacro()->Name().c_str());
"failed to convert scene condition type (%d)",
oldType);
_type = Type::CURRENT;
break;
}

View File

@@ -1,5 +1,4 @@
#include "macro-condition-streaming.hpp"
#include "switcher-data.hpp"
#include "utility.hpp"
namespace advss {
@@ -25,6 +24,32 @@ const static std::map<MacroConditionStream::Condition, std::string>
"AdvSceneSwitcher.condition.stream.state.keyFrameInterval"},
};
static bool setupStreamingEventHandler();
static bool steamingEventHandlerIsSetup = setupStreamingEventHandler();
static std::chrono::high_resolution_clock::time_point streamStartTime{};
static std::chrono::high_resolution_clock::time_point streamStopTime{};
bool setupStreamingEventHandler()
{
static auto handleStreamingEvents = [](enum obs_frontend_event event,
void *) {
switch (event) {
case OBS_FRONTEND_EVENT_STREAMING_STARTING:
streamStartTime =
std::chrono::high_resolution_clock::now();
break;
case OBS_FRONTEND_EVENT_STREAMING_STOPPING:
streamStopTime =
std::chrono::high_resolution_clock::now();
break;
default:
break;
};
};
obs_frontend_add_event_callback(handleStreamingEvents, nullptr);
return true;
}
int MacroConditionStream::GetKeyFrameInterval()
{
const auto configPath = GetPathInProfileDir("streamEncoder.json");
@@ -42,10 +67,8 @@ bool MacroConditionStream::CheckCondition()
{
bool match = false;
bool streamStarting = switcher->lastStreamStartingTime !=
_lastStreamStartingTime;
bool streamStopping = switcher->lastStreamStoppingTime !=
_lastStreamStoppingTime;
bool streamStarting = streamStartTime != _lastStreamStartingTime;
bool streamStopping = streamStopTime != _lastStreamStoppingTime;
switch (_condition) {
case Condition::STOP:
@@ -68,10 +91,10 @@ bool MacroConditionStream::CheckCondition()
}
if (streamStarting) {
_lastStreamStartingTime = switcher->lastStreamStartingTime;
_lastStreamStartingTime = streamStartTime;
}
if (streamStopping) {
_lastStreamStoppingTime = switcher->lastStreamStoppingTime;
_lastStreamStoppingTime = streamStopTime;
}
return match;
}

View File

@@ -1,5 +1,5 @@
#include "macro-condition-transition.hpp"
#include "switcher-data.hpp"
#include "scene-switch-helpers.hpp"
#include "utility.hpp"
namespace advss {
@@ -50,8 +50,8 @@ static bool isTargetScene(OBSWeakSource &target)
bool MacroConditionTransition::CheckCondition()
{
bool anyTransitionEnded = _lastTransitionEndTime !=
switcher->lastTransitionEndTime;
bool anyTransitionStarted = switcher->AnySceneTransitionStarted();
GetLastTransitionEndTime();
bool anyTransitionStarted = AnySceneTransitionStarted();
bool transitionStarted = false;
bool transitionEnded = false;
@@ -82,7 +82,7 @@ bool MacroConditionTransition::CheckCondition()
break;
case TransitionCondition::TRANSITION_SOURCE:
ret = anyTransitionStarted &&
_scene.GetScene() == switcher->currentScene;
_scene.GetScene() == GetCurrentScene();
break;
case TransitionCondition::TRANSITION_TARGET: {
auto scene = _scene.GetScene();
@@ -101,7 +101,7 @@ bool MacroConditionTransition::CheckCondition()
_ended = false;
}
if (anyTransitionEnded) {
_lastTransitionEndTime = switcher->lastTransitionEndTime;
_lastTransitionEndTime = GetLastTransitionEndTime();
}
return ret;

View File

@@ -1,5 +1,4 @@
#include "macro-condition-websocket.hpp"
#include "switcher-data.hpp"
#include "utility.hpp"
#include <regex>

View File

@@ -1,5 +1,5 @@
#include "macro-condition-window.hpp"
#include "switcher-data.hpp"
#include "plugin-state-helpers.hpp"
#include "platform-funcs.hpp"
#include "utility.hpp"
@@ -43,7 +43,8 @@ static bool windowContainsText(const std::string &window,
bool MacroConditionWindow::WindowMatchesRequirements(
const std::string &window) const
{
const bool focusCheckOK = (!_focus || window == switcher->currentTitle);
const bool focusCheckOK =
(!_focus || window == ForegroundWindowTitle());
if (!focusCheckOK) {
return false;
}
@@ -104,13 +105,13 @@ void MacroConditionWindow::SetVariableValueBasedOnMatch(
auto text = GetTextInWindow(matchWindow);
SetVariableValue(text.value_or(""));
} else {
SetVariableValue(switcher->currentTitle);
SetVariableValue(ForegroundWindowTitle());
}
}
static bool foregroundWindowChanged()
{
return switcher->currentTitle != switcher->lastTitle;
return ForegroundWindowTitle() != PreviousForegroundWindowTitle();
}
bool MacroConditionWindow::CheckCondition()
@@ -432,7 +433,7 @@ void MacroConditionWindowEdit::WindowFocusChanged(int state)
void MacroConditionWindowEdit::UpdateFocusWindow()
{
_focusWindow->setText(QString::fromStdString(switcher->currentTitle));
_focusWindow->setText(QString::fromStdString(ForegroundWindowTitle()));
}
void MacroConditionWindowEdit::SetWidgetVisibility()

View File

@@ -0,0 +1,59 @@
#include "macro-helpers.hpp"
#include "plugin-state-helpers.hpp"
namespace advss {
static std::atomic_bool abortMacroWait = {false};
static std::atomic_bool macroSceneSwitched = {false};
static std::atomic_int shutdownConditionCount = {0};
std::condition_variable &GetMacroWaitCV()
{
static std::condition_variable cv;
return cv;
}
std::condition_variable &GetMacroTransitionCV()
{
static std::condition_variable cv;
return cv;
}
std::atomic_bool &MacroWaitShouldAbort()
{
return abortMacroWait;
}
void SetMacroAbortWait(bool value)
{
abortMacroWait = value;
}
bool ShutdownCheckIsNecessary()
{
return shutdownConditionCount > 0;
}
std::atomic_int &GetShutdownConditionCount()
{
return shutdownConditionCount;
}
void SetMacroSwitchedScene(bool value)
{
static bool setupDone = false;
if (!setupDone) {
// Will always be called with switcher lock already held
AddIntervalResetStep([]() { macroSceneSwitched = false; },
false);
setupDone = true;
}
macroSceneSwitched = value;
}
bool MacroSwitchedScene()
{
return macroSceneSwitched;
}
} // namespace advss

View File

@@ -0,0 +1,21 @@
#pragma once
#include <atomic>
#include <condition_variable>
#include <string_view>
namespace advss {
constexpr std::string_view GetSceneSwitchActionId()
{
return "scene_switch";
}
std::condition_variable &GetMacroWaitCV();
std::condition_variable &GetMacroTransitionCV();
std::atomic_bool &MacroWaitShouldAbort();
void SetMacroAbortWait(bool);
bool ShutdownCheckIsNecessary();
std::atomic_int &GetShutdownConditionCount();
void SetMacroSwitchedScene(bool value);
bool MacroSwitchedScene();
} // namespace advss

View File

@@ -9,6 +9,8 @@
namespace advss {
static MacroProperties macroProperties;
void MacroProperties::Save(obs_data_t *obj) const
{
auto data = obs_data_create();
@@ -325,4 +327,19 @@ bool MacroPropertiesDialog::AskForSettings(QWidget *parent,
return true;
}
MacroProperties &GetGlobalMacroProperties()
{
return macroProperties;
}
void SaveGlobalMacroProperties(obs_data_t *obj)
{
macroProperties.Save(obj);
}
void LoadGlobalMacroProperties(obs_data_t *obj)
{
macroProperties.Load(obj);
}
} // namespace advss

View File

@@ -68,4 +68,8 @@ private:
int _conditionsFalseTextRow = -1;
};
MacroProperties &GetGlobalMacroProperties();
void SaveGlobalMacroProperties(obs_data_t *obj);
void LoadGlobalMacroProperties(obs_data_t *obj);
} // namespace advss

View File

@@ -44,8 +44,28 @@ MacroSegmentList::~MacroSegmentList()
}
}
static bool posIsInScrollbar(const QScrollBar *scrollbar, const QPoint &pos)
{
if (!scrollbar) {
return false;
}
if (!scrollbar->isVisible()) {
return false;
}
const auto geo = scrollbar->geometry();
const auto globalGeo = QRect(scrollbar->mapToGlobal(geo.topLeft()),
scrollbar->mapToGlobal(geo.bottomRight()));
return globalGeo.contains(pos);
}
int MacroSegmentList::GetDragIndex(const QPoint &pos)
{
// Don't drag widget when interacting with the scrollbars
if (posIsInScrollbar(horizontalScrollBar(), mapTo(this, pos)) ||
posIsInScrollbar(verticalScrollBar(), mapTo(this, pos))) {
return -1;
}
for (int idx = 0; idx < _contentLayout->count(); ++idx) {
auto item = _contentLayout->itemAt(idx);
if (!item) {
@@ -161,7 +181,7 @@ void MacroSegmentList::mousePressEvent(QMouseEvent *event)
#else
_dragPosition = GetDragIndex(event->globalPosition().toPoint());
#endif
emit SelectionChagned(_dragPosition);
emit SelectionChanged(_dragPosition);
} else {
_dragPosition = -1;
}

View File

@@ -28,7 +28,7 @@ public:
QVBoxLayout *ContentLayout() { return _contentLayout; }
signals:
void SelectionChagned(int idx);
void SelectionChanged(int idx);
void Reorder(int source, int target);
protected:

View File

@@ -4,7 +4,7 @@
// so it makes sense to include them here:
#include "log-helper.hpp"
#include "obs-module-helper.hpp"
#include "sync-helper.hpp"
#include "sync-helpers.hpp"
#include "temp-variable.hpp"
#include <QWidget>

View File

@@ -1,6 +1,6 @@
#include "macro-selection.hpp"
#include "macro.hpp"
#include "advanced-scene-switcher.hpp"
#include "switcher-data.hpp"
#include "utility.hpp"
#include <QStandardItemModel>
@@ -12,7 +12,7 @@ MacroSelection::MacroSelection(QWidget *parent)
: FilterComboBox(parent,
obs_module_text("AdvSceneSwitcher.selectMacro"))
{
for (const auto &m : switcher->macros) {
for (const auto &m : GetMacros()) {
if (m->IsGroup()) {
continue;
}

View File

@@ -63,7 +63,7 @@ bool AdvSceneSwitcher::AddNewMacro(std::shared_ptr<Macro> &res,
}
res = std::make_shared<Macro>(
name, switcher->macroProperties._newMacroRegisterHotkeys);
name, GetGlobalMacroProperties()._newMacroRegisterHotkeys);
return true;
}
@@ -197,8 +197,8 @@ static void addGroupSubitems(std::vector<std::shared_ptr<Macro>> &macros,
subitems.reserve(group->GroupSize());
// Find all subitems
for (auto it = switcher->macros.begin(); it < switcher->macros.end();
it++) {
auto allMacros = GetMacros();
for (auto it = allMacros.begin(); it < allMacros.end(); it++) {
if ((*it)->Name() == group->Name()) {
for (uint32_t i = 1; i <= group->GroupSize(); i++) {
subitems.emplace_back(*std::next(it, i));
@@ -243,7 +243,7 @@ void AdvSceneSwitcher::ExportMacros()
obs_data_array_push_back(macroArray, obj);
}
obs_data_set_array(data, "macros", macroArray);
switcher->SaveVariables(data);
SaveVariables(data);
obs_data_set_string(data, "version", g_GIT_TAG);
auto json = obs_data_get_json(data);
QString exportString(json);
@@ -382,7 +382,7 @@ static void importVariables(obs_data_t *obj)
if (variableWithNameExists(var->Name())) {
continue;
}
switcher->variables.emplace_back(var);
GetVariables().emplace_back(var);
}
}
@@ -429,7 +429,7 @@ void AdvSceneSwitcher::ImportMacros()
}
importedMacros.emplace_back(macro);
switcher->macros.emplace_back(macro);
GetMacros().emplace_back(macro);
if (groupSize > 0 && !macro->IsGroup()) {
Macro::PrepareMoveToGroup(group, macro);
groupSize--;
@@ -449,8 +449,8 @@ void AdvSceneSwitcher::ImportMacros()
macro->PostLoad();
}
ui->macros->Reset(switcher->macros,
switcher->macroProperties._highlightExecuted);
ui->macros->Reset(GetMacros(),
GetGlobalMacroProperties()._highlightExecuted);
}
void AdvSceneSwitcher::on_macroName_editingFinished()
@@ -729,8 +729,8 @@ void AdvSceneSwitcher::MacroSelectionChanged()
void AdvSceneSwitcher::HighlightOnChange()
{
if (!switcher->macroProperties._highlightActions &&
!switcher->macroProperties._highlightExecuted) {
if (!GetGlobalMacroProperties()._highlightActions &&
!GetGlobalMacroProperties()._highlightExecuted) {
return;
}
@@ -747,13 +747,13 @@ void AdvSceneSwitcher::HighlightOnChange()
void AdvSceneSwitcher::on_macroProperties_clicked()
{
MacroProperties prop = switcher->macroProperties;
MacroProperties prop = GetGlobalMacroProperties();
bool accepted = MacroPropertiesDialog::AskForSettings(
this, prop, GetSelectedMacro().get());
if (!accepted) {
return;
}
switcher->macroProperties = prop;
GetGlobalMacroProperties() = prop;
emit HighlightMacrosChanged(prop._highlightExecuted);
emit HighlightActionsChanged(prop._highlightActions);
emit HighlightConditionsChanged(prop._highlightConditions);
@@ -789,11 +789,11 @@ void AdvSceneSwitcher::SetupMacroTab()
{
ui->macroElseActions->installEventFilter(this);
if (switcher->macros.size() == 0 && !switcher->disableHints) {
if (GetMacros().size() == 0 && !switcher->disableHints) {
addPulse = PulseWidget(ui->macroAdd, QColor(Qt::green));
}
ui->macros->Reset(switcher->macros,
switcher->macroProperties._highlightExecuted);
ui->macros->Reset(GetMacros(),
GetGlobalMacroProperties()._highlightExecuted);
connect(ui->macros, SIGNAL(MacroSelectionAboutToChange()), this,
SLOT(MacroSelectionAboutToChange()));
connect(ui->macros, SIGNAL(MacroSelectionChanged()), this,
@@ -802,21 +802,21 @@ void AdvSceneSwitcher::SetupMacroTab()
ui->conditionsList->SetHelpMsg(
obs_module_text("AdvSceneSwitcher.macroTab.editConditionHelp"));
connect(ui->conditionsList, &MacroSegmentList::SelectionChagned, this,
connect(ui->conditionsList, &MacroSegmentList::SelectionChanged, this,
&AdvSceneSwitcher::MacroConditionSelectionChanged);
connect(ui->conditionsList, &MacroSegmentList::Reorder, this,
&AdvSceneSwitcher::MacroConditionReorder);
ui->actionsList->SetHelpMsg(
obs_module_text("AdvSceneSwitcher.macroTab.editActionHelp"));
connect(ui->actionsList, &MacroSegmentList::SelectionChagned, this,
connect(ui->actionsList, &MacroSegmentList::SelectionChanged, this,
&AdvSceneSwitcher::MacroActionSelectionChanged);
connect(ui->actionsList, &MacroSegmentList::Reorder, this,
&AdvSceneSwitcher::MacroActionReorder);
ui->elseActionsList->SetHelpMsg(obs_module_text(
"AdvSceneSwitcher.macroTab.editElseActionHelp"));
connect(ui->elseActionsList, &MacroSegmentList::SelectionChagned, this,
connect(ui->elseActionsList, &MacroSegmentList::SelectionChanged, this,
&AdvSceneSwitcher::MacroElseActionSelectionChanged);
connect(ui->elseActionsList, &MacroSegmentList::Reorder, this,
&AdvSceneSwitcher::MacroElseActionReorder);

View File

@@ -1,7 +1,7 @@
#include "macro-tree.hpp"
#include "macro.hpp"
#include "sync-helpers.hpp"
#include "utility.hpp"
#include "switcher-data.hpp"
#include <obs.h>
#include <string>
@@ -395,7 +395,7 @@ CountItemsVisibleInModel(const std::deque<std::shared_ptr<Macro>> &macros)
void MacroTreeModel::Add(std::shared_ptr<Macro> item)
{
std::lock_guard<std::mutex> lock(switcher->m);
auto lock = LockContext();
auto idx = CountItemsVisibleInModel(_macros);
beginInsertRows(QModelIndex(), idx, idx);
_macros.emplace_back(item);
@@ -408,7 +408,7 @@ void MacroTreeModel::Add(std::shared_ptr<Macro> item)
void MacroTreeModel::Remove(std::shared_ptr<Macro> item)
{
std::lock_guard<std::mutex> lock(switcher->m);
auto lock = LockContext();
auto uiStartIdx = GetItemModelIndex(item);
if (uiStartIdx == -1) {
return;
@@ -643,7 +643,7 @@ void MacroTreeModel::GroupSelectedItems(QModelIndexList &indices)
return;
}
std::lock_guard<std::mutex> lock(switcher->m);
auto lock = LockContext();
QString name = GetNewGroupName();
std::vector<std::shared_ptr<Macro>> itemsToGroup;
itemsToGroup.reserve(indices.size());
@@ -691,7 +691,7 @@ void MacroTreeModel::UngroupSelectedGroups(QModelIndexList &indices)
return;
}
std::lock_guard<std::mutex> lock(switcher->m);
auto lock = LockContext();
for (int i = indices.count() - 1; i >= 0; i--) {
std::shared_ptr<Macro> item = _macros[ModelIndexToMacroIndex(
indices[i].row(), _macros)];
@@ -1055,7 +1055,7 @@ void MacroTree::dropEvent(QDropEvent *event)
}
// Move items in backend
std::lock_guard<std::mutex> lock(switcher->m);
auto lock = LockContext();
int to = row;
try {
to = ModelIndexToMacroIndex(row, items);
@@ -1181,7 +1181,7 @@ void MacroTree::Remove(std::shared_ptr<Macro> item) const
void MacroTree::Up(std::shared_ptr<Macro> item) const
{
std::lock_guard<std::mutex> lock(switcher->m);
auto lock = LockContext();
auto above = GetModel()->Neighbor(item, true);
if (!above) {
return;
@@ -1207,7 +1207,7 @@ void MacroTree::Up(std::shared_ptr<Macro> item) const
void MacroTree::Down(std::shared_ptr<Macro> item) const
{
std::lock_guard<std::mutex> lock(switcher->m);
auto lock = LockContext();
auto below = GetModel()->Neighbor(item, false);
if (!below) {
return;

View File

@@ -1,20 +1,24 @@
#include "macro.hpp"
#include "macro-action-edit.hpp"
#include "macro-condition-edit.hpp"
#include "macro-action-factory.hpp"
#include "macro-condition-factory.hpp"
#include "macro-dock.hpp"
#include "macro-action-scene-switch.hpp"
#include "switcher-data.hpp"
#include "macro-helpers.hpp"
#include "plugin-state-helpers.hpp"
#include "hotkey.hpp"
#include "sync-helpers.hpp"
#include "utility.hpp"
#include <limits>
#undef max
#include <chrono>
#include <unordered_map>
#include <QAction>
#include <QMainWindow>
namespace advss {
constexpr int perfLogThreshold = 300;
static constexpr int perfLogThreshold = 300;
static std::deque<std::shared_ptr<Macro>> macros;
Macro::Macro(const std::string &name, const bool addHotkey)
{
@@ -33,7 +37,7 @@ Macro::~Macro()
// Keep the dock widgets in case of shutdown so they can be restored by
// OBS on startup
if (!switcher->obsIsShuttingDown) {
if (!OBSIsShuttingDown()) {
RemoveDock();
}
}
@@ -53,9 +57,8 @@ Macro::CreateGroup(const std::string &name,
void Macro::RemoveGroup(std::shared_ptr<Macro> group)
{
auto it = std::find(switcher->macros.begin(), switcher->macros.end(),
group);
if (it == switcher->macros.end()) {
auto it = std::find(macros.begin(), macros.end(), group);
if (it == macros.end()) {
return;
}
@@ -65,12 +68,12 @@ void Macro::RemoveGroup(std::shared_ptr<Macro> group)
(*m)->SetParent(nullptr);
}
switcher->macros.erase(it);
macros.erase(it);
}
void Macro::PrepareMoveToGroup(Macro *group, std::shared_ptr<Macro> item)
{
for (auto &m : switcher->macros) {
for (const auto &m : macros) {
if (m.get() == group) {
PrepareMoveToGroup(m, item);
return;
@@ -351,7 +354,7 @@ void Macro::AddHelperThread(std::thread &&newThread)
void Macro::Stop()
{
_stop = true;
switcher->macroWaitCv.notify_all();
GetMacroWaitCV().notify_all();
for (auto &t : _helperThreads) {
if (t.joinable()) {
t.join();
@@ -743,15 +746,13 @@ bool Macro::PostLoad()
bool Macro::SwitchesScene() const
{
MacroActionSwitchScene temp(nullptr);
auto sceneSwitchId = temp.GetId();
for (const auto &a : _actions) {
if (a->GetId() == sceneSwitchId) {
if (a->GetId() == GetSceneSwitchActionId()) {
return true;
}
}
for (const auto &a : _elseActions) {
if (a->GetId() == sceneSwitchId) {
if (a->GetId() == GetSceneSwitchActionId()) {
return true;
}
}
@@ -947,7 +948,7 @@ void Macro::EnableDock(bool value)
// geometry set here.
// The function calls here are only intended to attempt to restore the
// dock status when switching scene collections.
if (switcher->startupLoadDone) {
if (InitialLoadIsComplete()) {
_dock->setVisible(_dockIsVisible);
if (window->dockWidgetArea(_dock) != _dockArea) {
window->addDockWidget(_dockArea, _dock);
@@ -1163,12 +1164,10 @@ void Macro::SetHotkeysDesc() const
_name, _togglePauseHotkey);
}
void SwitcherData::SaveMacros(obs_data_t *obj)
void SaveMacros(obs_data_t *obj)
{
switcher->macroProperties.Save(obj);
obs_data_array_t *macroArray = obs_data_array_create();
for (auto &m : macros) {
for (const auto &m : macros) {
obs_data_t *array_obj = obs_data_create();
m->Save(array_obj);
@@ -1180,10 +1179,9 @@ void SwitcherData::SaveMacros(obs_data_t *obj)
obs_data_array_release(macroArray);
}
void SwitcherData::LoadMacros(obs_data_t *obj)
void LoadMacros(obs_data_t *obj)
{
Hotkey::ClearAllHotkeys();
switcher->macroProperties.Load(obj);
macros.clear();
obs_data_array_t *macroArray = obs_data_get_array(obj, "macros");
@@ -1200,7 +1198,7 @@ void SwitcherData::LoadMacros(obs_data_t *obj)
int groupCount = 0;
std::shared_ptr<Macro> group;
std::vector<std::shared_ptr<Macro>> invalidGroups;
for (auto &m : macros) {
for (const auto &m : macros) {
if (groupCount && m->IsGroup()) {
blog(LOG_ERROR,
"nested group detected - will delete \"%s\"",
@@ -1235,23 +1233,28 @@ void SwitcherData::LoadMacros(obs_data_t *obj)
}
}
bool SwitcherData::CheckMacros()
std::deque<std::shared_ptr<Macro>> &GetMacros()
{
bool ret = false;
for (auto &m : macros) {
return macros;
}
bool CheckMacros()
{
bool matchFound = false;
for (const auto &m : macros) {
if (m->CeckMatch() || m->ElseActions().size() > 0) {
ret = true;
matchFound = true;
// This has to be performed here for now as actions are
// not performed immediately after checking conditions.
if (m->SwitchesScene()) {
switcher->macroSceneSwitched = true;
SetMacroSwitchedScene(true);
}
}
}
return ret;
return matchFound;
}
bool SwitcherData::RunMacros()
bool RunMacros()
{
// Create copy of macro list as elements might be removed, inserted, or
// reordered while macros are currently being executed.
@@ -1269,15 +1272,16 @@ bool SwitcherData::RunMacros()
// the constructor of AdvSceneSwitcher() to complete.
// The constructor of AdvSceneSwitcher() cannot continue however as it
// cannot lock the main switcher mutex.
if (GetLock()) {
GetLock()->unlock();
auto lock = GetLoopLock();
if (lock) {
lock->unlock();
}
for (auto &m : runPhaseMacros) {
if (!m || !m->ShouldRunActions()) {
continue;
}
if (firstInterval && m->SkipExecOnStart()) {
if (IsFirstInterval() && m->SkipExecOnStart()) {
blog(LOG_INFO,
"skip execution of macro \"%s\" at startup",
m->Name().c_str());
@@ -1288,15 +1292,15 @@ bool SwitcherData::RunMacros()
blog(LOG_WARNING, "abort macro: %s", m->Name().c_str());
}
}
if (GetLock()) {
GetLock()->lock();
if (lock) {
lock->lock();
}
return true;
}
Macro *GetMacroByName(const char *name)
{
for (auto &m : switcher->macros) {
for (const auto &m : macros) {
if (m->Name() == name) {
return m.get();
}
@@ -1312,7 +1316,7 @@ Macro *GetMacroByQString(const QString &name)
std::weak_ptr<Macro> GetWeakMacroByName(const char *name)
{
for (auto &m : switcher->macros) {
for (const auto &m : macros) {
if (m->Name() == name) {
return m;
}
@@ -1323,7 +1327,7 @@ std::weak_ptr<Macro> GetWeakMacroByName(const char *name)
void InvalidateMacroTempVarValues()
{
for (auto &m : switcher->macros) {
for (const auto &m : macros) {
m->InvalidateTempVarValues();
}
}

View File

@@ -202,6 +202,11 @@ private:
QAction *_dockAction = nullptr;
};
void LoadMacros(obs_data_t *obj);
void SaveMacros(obs_data_t *obj);
std::deque<std::shared_ptr<Macro>> &GetMacros();
bool CheckMacros();
bool RunMacros();
Macro *GetMacroByName(const char *name);
Macro *GetMacroByQString(const QString &name);
std::weak_ptr<Macro> GetWeakMacroByName(const char *name);

View File

@@ -3,8 +3,8 @@
#include <utility.hpp>
#include <log-helper.hpp>
#include <obs-module-helper.hpp>
#include <plugin-state-helper.hpp>
#include <sync-helper.hpp>
#include <plugin-state-helpers.hpp>
#include <sync-helpers.hpp>
namespace advss {

View File

@@ -3,7 +3,7 @@
#include "twitch-helpers.hpp"
#include <log-helper.hpp>
#include <plugin-state-helper.hpp>
#include <plugin-state-helpers.hpp>
namespace advss {

View File

@@ -3,7 +3,7 @@
#include "twitch-helpers.hpp"
#include <log-helper.hpp>
#include <plugin-state-helper.hpp>
#include <plugin-state-helpers.hpp>
namespace advss {

View File

@@ -1,7 +1,7 @@
#include "token.hpp"
#include "twitch-helpers.hpp"
#include <plugin-state-helper.hpp>
#include <plugin-state-helpers.hpp>
#include <log-helper.hpp>
#include <obs-module-helper.hpp>
#include <utility.hpp>

View File

@@ -1,7 +1,7 @@
#include "macro-condition-video.hpp"
#include <macro-condition-edit.hpp>
#include <switcher-data.hpp>
#include <plugin-state-helpers.hpp>
#include <utility.hpp>
#include <QFileDialog>
@@ -222,8 +222,8 @@ void MacroConditionVideo::GetScreenshot(bool blocking)
_areaParameters.area.width,
_areaParameters.area.height);
}
new (&_screenshotData) ScreenshotHelper(
source, screenshotArea, blocking, GetSwitcher()->interval);
new (&_screenshotData) ScreenshotHelper(source, screenshotArea,
blocking, GetIntervalValue());
obs_source_release(source);
_getNextScreenshot = false;
}
@@ -1110,9 +1110,9 @@ MacroConditionVideoEdit::MacroConditionVideoEdit(
"AdvSceneSwitcher.condition.video.patternMatchMode.tip"));
populatePatternMatchModeSelection(_patternMatchMode);
_throttleCount->setMinimum(1 * GetSwitcher()->interval);
_throttleCount->setMaximum(10 * GetSwitcher()->interval);
_throttleCount->setSingleStep(GetSwitcher()->interval);
_throttleCount->setMinimum(1 * GetIntervalValue());
_throttleCount->setMaximum(10 * GetIntervalValue());
_throttleCount->setSingleStep(GetIntervalValue());
_brightness->setSizePolicy(QSizePolicy::MinimumExpanding,
QSizePolicy::Preferred);
@@ -1507,7 +1507,7 @@ void MacroConditionVideoEdit::ThrottleCountChanged(int value)
}
auto lock = LockContext();
_entryData->_throttleCount = value / GetSwitcher()->interval;
_entryData->_throttleCount = value / GetIntervalValue();
}
void MacroConditionVideoEdit::ShowMatchClicked()
@@ -1629,7 +1629,7 @@ void MacroConditionVideoEdit::UpdateEntryData()
_entryData->_patternMatchParameters.matchMode));
_throttleEnable->setChecked(_entryData->_throttleEnabled);
_throttleCount->setValue(_entryData->_throttleCount *
GetSwitcher()->interval);
GetIntervalValue());
UpdatePreviewTooltip();
SetupPreviewDialogParams();
SetWidgetVisibility();

View File

@@ -383,6 +383,9 @@ static std::map<HotkeyType, CGKeyCode> keyTable = {
{HotkeyType::Key_8, kVK_ANSI_8},
{HotkeyType::Key_9, kVK_ANSI_9},
// Other
// Not sure which key to use for tilde
{HotkeyType::Key_F1, kVK_F1},
{HotkeyType::Key_F2, kVK_F2},
{HotkeyType::Key_F3, kVK_F3},

View File

@@ -1,5 +1,5 @@
#include "status-control.hpp"
#include "switcher-data.hpp"
#include "plugin-state-helpers.hpp"
#include "utility.hpp"
#include <obs-module.h>
@@ -51,10 +51,10 @@ StatusControl::StatusControl(QWidget *parent, bool noLayout)
setLayout(layout);
}
if (switcher->stop) {
SetStopped();
} else {
if (PluginIsRunning()) {
SetStarted();
} else {
SetStopped();
}
connect(&_timer, SIGNAL(timeout()), this, SLOT(UpdateStatus()));
_timer.start(1000);
@@ -62,22 +62,18 @@ StatusControl::StatusControl(QWidget *parent, bool noLayout)
void StatusControl::ButtonClicked()
{
if (switcher->th && switcher->th->isRunning()) {
switcher->Stop();
if (PluginIsRunning()) {
StopPlugin();
SetStopped();
} else {
switcher->Start();
StartPlugin();
SetStarted();
}
}
void StatusControl::UpdateStatus()
{
if (!switcher) {
return;
}
if (switcher->th && switcher->th->isRunning()) {
if (PluginIsRunning()) {
if (!_setToStopped) {
return;
}
@@ -104,7 +100,7 @@ void StatusControl::SetStopped()
_button->setText(
obs_module_text("AdvSceneSwitcher.generalTab.status.start"));
_status->setText(obs_module_text("AdvSceneSwitcher.status.inactive"));
if (!switcher->disableHints) {
if (HighlightUIElementsEnabled()) {
_pulse = PulseWidget(_status, QColor(Qt::red),
QColor(0, 0, 0, 0));
}

View File

@@ -14,6 +14,11 @@ std::mutex *GetSwitcherMutex()
return switcher ? &switcher->m : nullptr;
}
std::unique_lock<std::mutex> *GetSwitcherLoopLock()
{
return switcher ? switcher->mainLoopLock : nullptr;
}
bool VerboseLoggingEnabled()
{
return switcher ? switcher->verbose : false;
@@ -168,8 +173,13 @@ void SwitcherData::SaveVersion(obs_data_t *obj,
obs_data_set_string(obj, "version", currentVersion.c_str());
}
void SwitcherData::AddIntervalResetStep(std::function<void()> function)
void SwitcherData::AddIntervalResetStep(std::function<void()> function,
bool tryLock)
{
if (!tryLock) {
resetIntervalSteps.emplace_back(function);
return;
}
std::lock_guard<std::mutex> lock(switcher->m);
resetIntervalSteps.emplace_back(function);
}

View File

@@ -18,10 +18,9 @@
#include "macro-properties.hpp"
#include "duration-control.hpp"
#include "curl-helper.hpp"
#include "priority-helper.hpp"
#include "log-helper.hpp"
#include "plugin-state-helper.hpp"
#include "plugin-state-helpers.hpp"
#include <condition_variable>
#include <vector>
@@ -29,7 +28,6 @@
#include <mutex>
#include <QDateTime>
#include <QThread>
#include <curl/curl.h>
#include <unordered_map>
namespace advss {
@@ -47,6 +45,7 @@ class SwitcherData;
extern SwitcherData *switcher;
SwitcherData *GetSwitcher();
std::mutex *GetSwitcherMutex();
std::unique_lock<std::mutex> *GetSwitcherLoopLock();
bool VerboseLoggingEnabled();
class SwitcherData {
@@ -54,7 +53,6 @@ public:
void Thread();
void Start();
void Stop();
std::unique_lock<std::mutex> *GetLock() { return mainLoopLock; }
const char *Translate(const char *);
obs_module_t *GetModule();
@@ -68,28 +66,22 @@ public:
void AddSaveStep(std::function<void(obs_data_t *)>);
void AddLoadStep(std::function<void(obs_data_t *)>);
void AddPostLoadStep(std::function<void()>);
void AddIntervalResetStep(std::function<void()>);
void AddIntervalResetStep(std::function<void()>, bool lock = true);
bool CheckForMatch(OBSWeakSource &scene, OBSWeakSource &transition,
int &linger, bool &setPreviousSceneAsMatch,
bool &macroMatch);
bool CheckMacros();
bool RunMacros();
void CheckNoMatchSwitch(bool &match, OBSWeakSource &scene,
OBSWeakSource &transition, int &sleep);
/* --- Start of saving / loading section --- */
void SaveSettings(obs_data_t *obj);
void SaveMacros(obs_data_t *obj);
void SaveVariables(obs_data_t *obj);
void SaveGeneralSettings(obs_data_t *obj);
void SaveHotkeys(obs_data_t *obj);
void SaveUISettings(obs_data_t *obj);
void SaveVersion(obs_data_t *obj, const std::string &currentVersion);
void LoadSettings(obs_data_t *obj);
void LoadMacros(obs_data_t *obj);
void LoadVariables(obs_data_t *obj);
void LoadGeneralSettings(obs_data_t *obj);
void LoadHotkeys(obs_data_t *obj);
void LoadUISettings(obs_data_t *obj);
@@ -110,9 +102,6 @@ public:
std::unique_lock<std::mutex> *mainLoopLock = nullptr;
bool stop = false;
std::condition_variable cv;
std::condition_variable macroWaitCv;
std::atomic_bool abortMacroWait = {false};
std::condition_variable macroTransitionCv;
std::vector<std::function<void(obs_data_t *)>> saveSteps;
std::vector<std::function<void(obs_data_t *)>> loadSteps;
@@ -122,21 +111,14 @@ public:
bool firstBoot = true;
bool transitionActive = false;
bool sceneColletionStop = false;
bool replayBufferSaved = false;
bool obsIsShuttingDown = false;
bool firstInterval = true;
bool firstIntervalAfterStop = true;
int shutdownConditionCount = 0;
bool startupLoadDone = false;
obs_source_t *waitScene = nullptr;
OBSWeakSource currentScene = nullptr;
OBSWeakSource previousScene = nullptr;
std::chrono::high_resolution_clock::time_point lastSceneChangeTime{};
std::chrono::high_resolution_clock::time_point lastTransitionEndTime{};
std::chrono::high_resolution_clock::time_point lastStreamStartingTime{};
std::chrono::high_resolution_clock::time_point lastStreamStoppingTime{};
std::chrono::high_resolution_clock::time_point lastMatchTime;
/* --- Start of General tab section --- */
@@ -161,25 +143,9 @@ public:
/* --- End of General tab section --- */
struct AudioFadeInfo {
std::atomic_bool active = {false};
std::atomic_int id = {0};
};
AudioFadeInfo masterAudioFade;
std::unordered_map<std::string, AudioFadeInfo> activeAudioFades;
MacroProperties macroProperties;
std::deque<std::shared_ptr<Macro>> macros;
bool macroSceneSwitched = false;
Curlhelper curl;
std::deque<std::shared_ptr<Item>> variables;
std::string lastTitle;
std::string currentTitle;
std::string currentForegroundProcess;
std::pair<int, int> lastCursorPos = {0, 0};
bool cursorPosChanged = false;
std::vector<int> functionNamesByPriority =
GetDefaultFunctionPriorityList();

View File

@@ -1,7 +1,7 @@
#include "connection-manager.hpp"
#include "utility.hpp"
#include "obs-module-helper.hpp"
#include "plugin-state-helper.hpp"
#include "plugin-state-helpers.hpp"
#include "name-dialog.hpp"
#include <algorithm>

View File

@@ -15,7 +15,7 @@ constexpr auto curl_library_name = "libcurl.4.dylib";
constexpr auto curl_library_name = "libcurl.so.4";
#endif
Curlhelper::Curlhelper()
CurlHelper::CurlHelper()
{
if (LoadLib()) {
_curl = _init();
@@ -23,7 +23,7 @@ Curlhelper::Curlhelper()
}
}
Curlhelper::~Curlhelper()
CurlHelper::~CurlHelper()
{
if (_lib) {
if (_cleanup) {
@@ -34,32 +34,46 @@ Curlhelper::~Curlhelper()
}
}
curl_slist *Curlhelper::SlistAppend(curl_slist *list, const char *string)
CurlHelper &CurlHelper::GetInstance()
{
if (!_initialized) {
static CurlHelper curl;
return curl;
}
bool CurlHelper::Initialized()
{
return GetInstance()._initialized;
}
curl_slist *CurlHelper::SlistAppend(curl_slist *list, const char *string)
{
auto &curl = GetInstance();
if (!curl._initialized) {
return nullptr;
}
return _slistAppend(list, string);
return curl._slistAppend(list, string);
}
CURLcode Curlhelper::Perform()
CURLcode CurlHelper::Perform()
{
if (!_initialized) {
auto &curl = GetInstance();
if (!curl._initialized) {
return CURLE_FAILED_INIT;
}
return _perform(_curl);
return curl._perform(curl._curl);
}
char *Curlhelper::GetError(CURLcode code)
char *CurlHelper::GetError(CURLcode code)
{
if (!_initialized) {
auto &curl = GetInstance();
if (!curl._initialized) {
return (char *)"CURL initialization failed";
}
return _error(code);
return curl._error(code);
}
bool Curlhelper::LoadLib()
bool CurlHelper::LoadLib()
{
_lib = new QLibrary(curl_library_name, nullptr);
if (Resolve()) {
@@ -103,7 +117,7 @@ bool Curlhelper::LoadLib()
return false;
}
bool Curlhelper::Resolve()
bool CurlHelper::Resolve()
{
_init = (initFunction)_lib->resolve("curl_easy_init");
_setopt = (setOptFunction)_lib->resolve("curl_easy_setopt");

View File

@@ -1,31 +1,35 @@
#pragma once
#include <curl/curl.h>
#include <QLibrary>
#include <atomic>
namespace advss {
typedef CURL *(*initFunction)(void);
typedef CURLcode (*setOptFunction)(CURL *, CURLoption, ...);
typedef struct curl_slist *(*slistAppendFunction)(struct curl_slist *list,
const char *string);
typedef CURLcode (*performFunction)(CURL *);
typedef void (*cleanupFunction)(CURL *);
typedef char *(*errorFunction)(CURLcode);
class Curlhelper {
class CurlHelper {
public:
Curlhelper();
~Curlhelper();
bool Initialized() { return _initialized; }
template<typename... Args> CURLcode SetOpt(CURLoption, Args...);
struct curl_slist *SlistAppend(struct curl_slist *list,
const char *string);
CURLcode Perform();
char *GetError(CURLcode code);
static bool Initialized();
template<typename... Args> static CURLcode SetOpt(CURLoption, Args...);
static struct curl_slist *SlistAppend(struct curl_slist *list,
const char *string);
static CURLcode Perform();
static char *GetError(CURLcode code);
private:
CurlHelper();
CurlHelper(const CurlHelper &) = delete;
CurlHelper &operator=(const CurlHelper &) = delete;
~CurlHelper();
typedef CURL *(*initFunction)(void);
typedef CURLcode (*setOptFunction)(CURL *, CURLoption, ...);
typedef struct curl_slist *(*slistAppendFunction)(
struct curl_slist *list, const char *string);
typedef CURLcode (*performFunction)(CURL *);
typedef void (*cleanupFunction)(CURL *);
typedef char *(*errorFunction)(CURLcode);
static CurlHelper &GetInstance();
bool LoadLib();
bool Resolve();
@@ -37,16 +41,17 @@ private:
errorFunction _error = nullptr;
CURL *_curl = nullptr;
QLibrary *_lib;
bool _initialized = false;
std::atomic_bool _initialized = {false};
};
template<typename... Args>
inline CURLcode Curlhelper::SetOpt(CURLoption option, Args... args)
inline CURLcode CurlHelper::SetOpt(CURLoption option, Args... args)
{
if (!_initialized) {
auto &curl = GetInstance();
if (!curl._initialized) {
return CURLE_FAILED_INIT;
}
return _setopt(_curl, option, args...);
return curl._setopt(curl._curl, option, args...);
}
} // namespace advss

View File

@@ -54,7 +54,7 @@ FilterComboBox::FilterComboBox(QWidget *parent, const QString &placehodler)
connect(c, QOverload<const QModelIndex &>::of(&QCompleter::highlighted),
this, &FilterComboBox::CompleterHighlightChanged);
connect(lineEdit(), &QLineEdit::textChanged, this,
&FilterComboBox::TextChagned);
&FilterComboBox::TextChanged);
}
void FilterComboBox::SetFilterBehaviourEnabled(bool value)
@@ -108,7 +108,7 @@ void FilterComboBox::CompleterHighlightChanged(const QModelIndex &index)
emit currentTextChanged(text);
}
void FilterComboBox::TextChagned(const QString &text)
void FilterComboBox::TextChanged(const QString &text)
{
auto c = completer();
const bool completerActive = c->completionCount() > 0;

View File

@@ -19,7 +19,7 @@ protected:
private slots:
void CompleterHighlightChanged(const QModelIndex &);
void TextChagned(const QString &);
void TextChanged(const QString &);
private:
int _lastCompleterHighlightRow = -1;

View File

@@ -1,7 +1,7 @@
#include "macro-segment-selection.hpp"
#include "advanced-scene-switcher.hpp"
#include "obs-module-helper.hpp"
#include "switcher-data.hpp"
#include "plugin-state-helpers.hpp"
#include "utility.hpp"
namespace advss {
@@ -108,7 +108,7 @@ void MacroSegmentSelection::IndexChanged(const IntVariable &value)
void MacroSegmentSelection::MarkSelectedSegment()
{
if (!GetSwitcher() || GetSwitcher()->disableHints) {
if (!HighlightUIElementsEnabled()) {
return;
}

View File

@@ -1,4 +1,4 @@
#include "plugin-state-helper.hpp"
#include "plugin-state-helpers.hpp"
#include "switcher-data.hpp"
namespace advss {
@@ -23,9 +23,9 @@ void AddPostLoadStep(std::function<void()> step)
GetSwitcher()->AddPostLoadStep(step);
}
void AddIntervalResetStep(std::function<void()> step)
void AddIntervalResetStep(std::function<void()> step, bool lock)
{
GetSwitcher()->AddIntervalResetStep(step);
GetSwitcher()->AddIntervalResetStep(step, lock);
}
void StopPlugin()
@@ -38,24 +38,70 @@ void StartPlugin()
GetSwitcher()->Start();
}
bool PluginIsRunning()
{
return GetSwitcher() && GetSwitcher()->th &&
GetSwitcher()->th->isRunning();
}
int GetIntervalValue()
{
return GetSwitcher()->interval;
}
void SetPluginNoMatchBehavior(NoMatchBehavior behavior)
{
GetSwitcher()->switchIfNotMatching = behavior;
}
void SetNoMatchScene(const OBSWeakSource &scene)
{
GetSwitcher()->nonMatchingScene = scene;
}
NoMatchBehavior GetPluginNoMatchBehavior()
{
return GetSwitcher()->switchIfNotMatching;
}
void SetNoMatchScene(const OBSWeakSource &scene)
{
GetSwitcher()->nonMatchingScene = scene;
}
std::string ForegroundWindowTitle()
{
return GetSwitcher()->currentTitle;
}
std::string PreviousForegroundWindowTitle()
{
return GetSwitcher()->lastTitle;
}
bool SettingsWindowIsOpened()
{
return GetSwitcher()->settingsWindowOpened;
}
bool HighlightUIElementsEnabled()
{
return GetSwitcher() && !GetSwitcher()->disableHints;
}
bool OBSIsShuttingDown()
{
return GetSwitcher()->obsIsShuttingDown;
}
bool InitialLoadIsComplete()
{
return GetSwitcher()->startupLoadDone;
}
bool IsFirstInterval()
{
return GetSwitcher()->firstInterval;
}
bool IsFirstIntervalAfterStop()
{
return GetSwitcher()->firstIntervalAfterStop;
}
} // namespace advss

View File

@@ -1,6 +1,7 @@
#pragma once
#include <functional>
#include <obs.hpp>
#include <string>
namespace advss {
@@ -9,16 +10,27 @@ void LoadPluginSettings(obs_data_t *);
void AddSaveStep(std::function<void(obs_data_t *)>);
void AddLoadStep(std::function<void(obs_data_t *)>);
void AddPostLoadStep(std::function<void()>);
void AddIntervalResetStep(std::function<void()>);
void AddIntervalResetStep(std::function<void()>, bool lock = true);
void StopPlugin();
void StartPlugin();
bool PluginIsRunning();
int GetIntervalValue();
enum class NoMatchBehavior { NO_SWITCH = 0, SWITCH = 1, RANDOM_SWITCH = 2 };
void SetPluginNoMatchBehavior(NoMatchBehavior);
NoMatchBehavior GetPluginNoMatchBehavior();
void SetNoMatchScene(const OBSWeakSource &);
std::string ForegroundWindowTitle();
std::string PreviousForegroundWindowTitle();
bool SettingsWindowIsOpened();
bool HighlightUIElementsEnabled();
bool OBSIsShuttingDown();
bool InitialLoadIsComplete();
bool IsFirstInterval();
bool IsFirstIntervalAfterStop();
} // namespace advss

View File

@@ -1,4 +1,5 @@
#include "process-config.hpp"
#include "log-helper.hpp"
#include "name-dialog.hpp"
#include "utility.hpp"
@@ -37,7 +38,7 @@ bool ProcessConfig::Load(obs_data_t *obj)
return true;
}
QStringList ProcessConfig::Args()
QStringList ProcessConfig::Args() const
{
QStringList result;
for (auto &arg : _args) {
@@ -46,6 +47,42 @@ QStringList ProcessConfig::Args()
return result;
}
bool ProcessConfig::StartProcessDetached() const
{
return QProcess::startDetached(QString::fromStdString(Path()), Args(),
QString::fromStdString(WorkingDir()));
}
std::variant<int, ProcessConfig::ProcStartError>
ProcessConfig::StartProcessAndWait(int timeout) const
{
QProcess process;
process.setWorkingDirectory(QString::fromStdString(WorkingDir()));
process.start(QString::fromStdString(Path()), Args());
vblog(LOG_INFO, "run \"%s\" with a timeout of %d ms", Path().c_str(),
timeout);
if (!process.waitForFinished(timeout)) {
if (process.error() == QProcess::FailedToStart) {
vblog(LOG_INFO, "failed to start \"%s\"!",
Path().c_str());
return ProcStartError::FAILED_TO_START;
}
vblog(LOG_INFO,
"timeout while running \"%s\"\nAttempting to kill process!",
Path().c_str());
process.kill();
process.waitForFinished();
return ProcStartError::TIMEOUT;
}
if (process.exitStatus() == QProcess::NormalExit) {
return process.exitCode();
}
vblog(LOG_INFO, "process \"%s\" crashed!", Path().c_str());
return ProcStartError::CRASH;
}
ProcessConfigEdit::ProcessConfigEdit(QWidget *parent)
: QWidget(parent),
_filePath(new FileSelection()),
@@ -91,7 +128,7 @@ ProcessConfigEdit::ProcessConfigEdit(QWidget *parent)
_advancedSettingsLayout->addWidget(_argList);
_advancedSettingsLayout->addLayout(workingDirectoryLayout);
auto *mainLayout = new QVBoxLayout;
auto mainLayout = new QVBoxLayout;
mainLayout->setContentsMargins(0, 0, 0, 0);
mainLayout->addLayout(entryLayout);
mainLayout->addLayout(_advancedSettingsLayout);
@@ -143,6 +180,9 @@ void ProcessConfigEdit::ShowAdvancedSettings(bool showAdvancedSettings)
_showAdvancedSettings->setVisible(!showAdvancedSettings);
adjustSize();
updateGeometry();
if (showAdvancedSettings) {
emit AdvancedSettingsEnabled();
}
}
} // namespace advss

View File

@@ -3,12 +3,13 @@
#include "string-list.hpp"
#include <obs-data.h>
#include <obs-module.h>
#include <obs-module-helper.hpp>
#include <QPushButton>
#include <QListWidget>
#include <QStringList>
#include <QVBoxLayout>
#include <variant>
namespace advss {
@@ -17,10 +18,21 @@ public:
bool Save(obs_data_t *obj) const;
bool Load(obs_data_t *obj);
std::string Path() { return _path; }
std::string Path() const { return _path; }
std::string UnresolvedPath() const { return _path.UnresolvedValue(); }
std::string WorkingDir() { return _workingDirectory; }
QStringList Args(); // Resolves variables
std::string WorkingDir() const { return _workingDirectory; }
QStringList Args() const; // Resolves variables
enum class ProcStartError {
NONE,
FAILED_TO_START,
TIMEOUT,
CRASH,
};
std::variant<int, ProcStartError>
StartProcessAndWait(int timeoutInMs) const;
bool StartProcessDetached() const;
private:
StringVariable _path = obs_module_text("AdvSceneSwitcher.enterPath");
@@ -44,6 +56,7 @@ private slots:
void ArgsChanged(const StringList &);
signals:
void ConfigChanged(const ProcessConfig &);
void AdvancedSettingsEnabled();
private:
void ShowAdvancedSettings(bool);

View File

@@ -1,6 +1,7 @@
#include "resizing-text-edit.hpp"
#include <obs-module.h>
#include <obs-module-helper.hpp>
#include <QScrollBar>
#include <utility.hpp>
namespace advss {
@@ -14,10 +15,12 @@ ResizingPlainTextEdit::ResizingPlainTextEdit(QWidget *parent,
_minLines(minLines),
_paddingLines(paddingLines)
{
setWordWrapMode(QTextOption::NoWrap);
QWidget::connect(this, SIGNAL(textChanged()), this,
SLOT(ResizeTexteditArea()));
QWidget::connect(this, SIGNAL(textChanged()), this,
SLOT(PreventExceedingMaxLength()));
verticalScrollBar()->installEventFilter(this);
}
int ResizingPlainTextEdit::maxLength()
@@ -30,19 +33,35 @@ void ResizingPlainTextEdit::setMaxLength(int maxLength)
_maxLength = maxLength;
}
bool ResizingPlainTextEdit::eventFilter(QObject *obj, QEvent *event)
{
if (obj == horizontalScrollBar()) {
if (event->type() == QEvent::Show) {
AddHeightForScrollBar(true);
} else if (event->type() == QEvent::Hide) {
AddHeightForScrollBar(false);
}
}
return QPlainTextEdit::eventFilter(obj, event);
}
void ResizingPlainTextEdit::ResizeTexteditArea()
{
QFontMetrics f(font());
int rowHeight = f.lineSpacing();
int numLines = document()->blockCount();
int height = 0;
if (numLines + _paddingLines < _minLines) {
setFixedHeight(_minLines * rowHeight);
height = _minLines * rowHeight;
} else if (numLines + _paddingLines < _scrollAt) {
setFixedHeight((numLines + _paddingLines) * rowHeight);
height = (numLines + _paddingLines) * rowHeight;
} else {
setFixedHeight(_scrollAt * rowHeight);
height = _scrollAt * rowHeight;
}
setFixedHeight(height + _hScrollBarAddedHeight);
adjustSize();
updateGeometry();
}
@@ -62,4 +81,21 @@ void ResizingPlainTextEdit::PreventExceedingMaxLength()
}
}
void ResizingPlainTextEdit::AddHeightForScrollBar(bool addHeight)
{
if (!addHeight) {
setFixedHeight(height() - _hScrollBarAddedHeight);
_hScrollBarAddedHeight = 0;
return;
}
// Check if we have already added space for the scroll bar
if (_hScrollBarAddedHeight > 0) {
return;
}
_hScrollBarAddedHeight = verticalScrollBar()->height();
setFixedHeight(height() + _hScrollBarAddedHeight);
}
} // namespace advss

View File

@@ -13,15 +13,21 @@ public:
int maxLength();
void setMaxLength(int maxLength);
protected:
bool eventFilter(QObject *obj, QEvent *event) override;
private slots:
void ResizeTexteditArea();
void PreventExceedingMaxLength();
private:
void AddHeightForScrollBar(bool addHeight);
const int _scrollAt;
const int _minLines;
const int _paddingLines;
int _maxLength = -1;
int _hScrollBarAddedHeight = 0;
};
} // namespace advss

View File

@@ -1,5 +1,7 @@
#include "scene-selection.hpp"
#include "switcher-data.hpp"
#include "obs-module-helper.hpp"
#include "scene-group.hpp"
#include "scene-switch-helpers.hpp"
#include "utility.hpp"
namespace advss {
@@ -108,9 +110,9 @@ OBSWeakSource SceneSelection::GetScene(bool advance) const
}
return _group->getCurrentScene();
case Type::PREVIOUS:
return switcher->previousScene;
return GetPreviousScene();
case Type::CURRENT:
return switcher->currentScene;
return GetCurrentScene();
case Type::PREVIEW: {
auto s = obs_frontend_get_current_preview_scene();
auto scene = obs_source_get_weak_source(s);
@@ -237,7 +239,7 @@ static QStringList getScenesList()
static QStringList getSceneGroupsList()
{
QStringList list;
for (const auto &sg : switcher->sceneGroups) {
for (const auto &sg : GetSceneGroups()) {
list << QString::fromStdString(sg.name);
}
list.sort();

View File

@@ -1,10 +1,36 @@
#include "scene-switch-helpers.hpp"
#include "switcher-data.hpp"
#include "log-helper.hpp"
#include "obs-frontend-api.h"
#include "switcher-data.hpp"
#include "utility.hpp"
namespace advss {
static std::chrono::high_resolution_clock::time_point lastSceneChangeTime{};
static std::chrono::high_resolution_clock::time_point lastTransitionEndTime{};
static bool setupEventHandler();
static bool eventHandlerIsSetup = setupEventHandler();
static bool setupEventHandler()
{
static auto handleEvents = [](enum obs_frontend_event event, void *) {
switch (event) {
case OBS_FRONTEND_EVENT_SCENE_CHANGED:
lastSceneChangeTime =
std::chrono::high_resolution_clock::now();
break;
case OBS_FRONTEND_EVENT_TRANSITION_STOPPED:
lastTransitionEndTime =
std::chrono::high_resolution_clock::now();
break;
default:
break;
};
};
obs_frontend_add_event_callback(handleEvents, nullptr);
return true;
}
static std::pair<obs_weak_source_t *, int>
getNextTransition(obs_weak_source_t *scene1, obs_weak_source_t *scene2)
{
@@ -35,10 +61,9 @@ void SetNextTransition(const SceneSwitchInfo &sceneSwitch,
// 3. Current transition settings
// Transition Tab
obs_weak_source_t *currentScene =
OBSWeakSourceAutoRelease currentScene =
obs_source_get_weak_source(currentSource);
auto tinfo = getNextTransition(currentScene, sceneSwitch.scene);
obs_weak_source_release(currentScene);
OBSWeakSource nextTransition = tinfo.first;
int nextTransitionDuration = tinfo.second;
@@ -53,23 +78,23 @@ void SetNextTransition(const SceneSwitchInfo &sceneSwitch,
// Current transition settings
if (!nextTransition) {
auto ct = obs_frontend_get_current_transition();
nextTransition = obs_source_get_weak_source(ct);
OBSSourceAutoRelease currentTransition =
obs_frontend_get_current_transition();
nextTransition = obs_source_get_weak_source(currentTransition);
obs_weak_source_release(nextTransition);
obs_source_release(ct);
}
if (!nextTransitionDuration) {
nextTransitionDuration = obs_frontend_get_transition_duration();
}
if (switcher->adjustActiveTransitionType) {
if (ShouldModifyActiveTransition()) {
obs_frontend_set_transition_duration(nextTransitionDuration);
auto t = obs_weak_source_get_source(nextTransition);
obs_frontend_set_current_transition(t);
obs_source_release(t);
OBSSourceAutoRelease transition =
obs_weak_source_get_source(nextTransition);
obs_frontend_set_current_transition(transition);
}
if (switcher->transitionOverrideOverride) {
if (ShouldModifyTransitionOverrides()) {
OverwriteTransitionOverride({sceneSwitch.scene, nextTransition,
nextTransitionDuration},
td);
@@ -78,8 +103,8 @@ void SetNextTransition(const SceneSwitchInfo &sceneSwitch,
void OverwriteTransitionOverride(const SceneSwitchInfo &ssi, TransitionData &td)
{
obs_source_t *scene = obs_weak_source_get_source(ssi.scene);
obs_data_t *data = obs_source_get_private_settings(scene);
OBSSourceAutoRelease scene = obs_weak_source_get_source(ssi.scene);
OBSDataAutoRelease data = obs_source_get_private_settings(scene);
td.name = obs_data_get_string(data, "transition");
td.duration = obs_data_get_int(data, "transition_duration");
@@ -88,19 +113,13 @@ void OverwriteTransitionOverride(const SceneSwitchInfo &ssi, TransitionData &td)
obs_data_set_string(data, "transition", name.c_str());
obs_data_set_int(data, "transition_duration", ssi.duration);
obs_data_release(data);
obs_source_release(scene);
}
void RestoreTransitionOverride(obs_source_t *scene, const TransitionData &td)
{
obs_data_t *data = obs_source_get_private_settings(scene);
OBSDataAutoRelease data = obs_source_get_private_settings(scene);
obs_data_set_string(data, "transition", td.name.c_str());
obs_data_set_int(data, "transition_duration", td.duration);
obs_data_release(data);
}
void SwitchScene(const SceneSwitchInfo &sceneSwitch, bool force)
@@ -110,36 +129,67 @@ void SwitchScene(const SceneSwitchInfo &sceneSwitch, bool force)
return;
}
obs_source_t *source = obs_weak_source_get_source(sceneSwitch.scene);
obs_source_t *currentSource = obs_frontend_get_current_scene();
OBSSourceAutoRelease source =
obs_weak_source_get_source(sceneSwitch.scene);
OBSSourceAutoRelease currentSource = obs_frontend_get_current_scene();
if (source && (source != currentSource || force)) {
TransitionData currentTransitionData;
SetNextTransition(sceneSwitch, currentSource,
currentTransitionData);
obs_frontend_set_current_scene(source);
if (switcher->transitionOverrideOverride) {
if (ShouldModifyTransitionOverrides()) {
RestoreTransitionOverride(source,
currentTransitionData);
}
if (switcher->verbose) {
blog(LOG_INFO, "switched scene");
}
vblog(LOG_INFO, "switched scene");
if (switcher->networkConfig.ShouldSendSceneChange()) {
switcher->server.sendMessage(sceneSwitch);
}
}
obs_source_release(currentSource);
obs_source_release(source);
}
void SwitchPreviewScene(const OBSWeakSource &ws)
{
auto source = obs_weak_source_get_source(ws);
OBSSourceAutoRelease source = obs_weak_source_get_source(ws);
obs_frontend_set_current_preview_scene(source);
obs_source_release(source);
}
std::chrono::high_resolution_clock::time_point GetLastTransitionEndTime()
{
return lastTransitionEndTime;
}
std::chrono::high_resolution_clock::time_point GetLastSceneChangeTime()
{
return lastSceneChangeTime;
}
OBSWeakSource GetCurrentScene()
{
return switcher->currentScene;
}
OBSWeakSource GetPreviousScene()
{
return switcher->previousScene;
}
bool AnySceneTransitionStarted()
{
return switcher->AnySceneTransitionStarted();
}
bool ShouldModifyTransitionOverrides()
{
return switcher->transitionOverrideOverride;
}
bool ShouldModifyActiveTransition()
{
return switcher->adjustActiveTransitionType;
}
} // namespace advss

View File

@@ -1,5 +1,6 @@
#pragma once
#include <obs.hpp>
#include <chrono>
#include <string>
namespace advss {
@@ -15,12 +16,24 @@ struct TransitionData {
int duration = 0;
};
void SetNextTransition(const SceneSwitchInfo &ssi, obs_source_t *currentSource,
TransitionData &td);
void OverwriteTransitionOverride(const SceneSwitchInfo &ssi,
TransitionData &td);
void RestoreTransitionOverride(obs_source_t *scene, const TransitionData &td);
void SwitchScene(const SceneSwitchInfo &ssi, bool force = false);
void SwitchPreviewScene(const OBSWeakSource &ws);
void SwitchScene(const SceneSwitchInfo &, bool force = false);
void SwitchPreviewScene(const OBSWeakSource &scene);
std::chrono::high_resolution_clock::time_point GetLastTransitionEndTime();
std::chrono::high_resolution_clock::time_point GetLastSceneChangeTime();
OBSWeakSource GetCurrentScene();
OBSWeakSource GetPreviousScene();
bool ShouldModifyTransitionOverrides();
bool ShouldModifyActiveTransition();
void OverwriteTransitionOverride(const SceneSwitchInfo &, TransitionData &);
void RestoreTransitionOverride(obs_source_t *scene, const TransitionData &);
bool AnySceneTransitionStarted();
// TODO: Remove at some point
void SetNextTransition(const SceneSwitchInfo &, obs_source_t *currentSource,
TransitionData &);
} // namespace advss

View File

@@ -125,6 +125,7 @@ bool Section::eventFilter(QObject *obj, QEvent *event)
// filter will cause a segfault for some reason
SetupAnimations();
}
setMinimumWidth(_content->sizeHint().width());
}
return QObject::eventFilter(obj, event);
}

View File

@@ -1,11 +0,0 @@
#include "sync-helper.hpp"
namespace advss {
std::mutex *GetSwitcherMutex();
std::lock_guard<std::mutex> LockContext()
{
return std::lock_guard<std::mutex>(*GetSwitcherMutex());
}
} // namespace advss

View File

@@ -0,0 +1,23 @@
#include "sync-helpers.hpp"
namespace advss {
std::mutex *GetSwitcherMutex();
std::unique_lock<std::mutex> *GetSwitcherLoopLock();
std::mutex *GetMutex()
{
return GetSwitcherMutex();
}
std::lock_guard<std::mutex> LockContext()
{
return std::lock_guard<std::mutex>(*GetSwitcherMutex());
}
std::unique_lock<std::mutex> *GetLoopLock()
{
return GetSwitcherLoopLock();
}
} // namespace advss

View File

@@ -3,6 +3,8 @@
namespace advss {
[[nodiscard]] std::mutex *GetMutex();
[[nodiscard]] std::lock_guard<std::mutex> LockContext();
[[nodiscard]] std::unique_lock<std::mutex> *GetLoopLock();
} // namespace advss

View File

@@ -2,7 +2,7 @@
#include "advanced-scene-switcher.hpp"
#include "macro.hpp"
#include "macro-segment.hpp"
#include "plugin-state-helper.hpp"
#include "plugin-state-helpers.hpp"
#include "utility.hpp"
#include <QVariant>

View File

@@ -1,5 +1,5 @@
#include "transition-selection.hpp"
#include "switcher-data.hpp"
#include "obs-module-helper.hpp"
namespace advss {

View File

@@ -1,5 +1,4 @@
#include "variable-spinbox.hpp"
#include "switcher-data.hpp"
#include "utility.hpp"
#include <QLayout>
@@ -175,7 +174,7 @@ void GenericVaraiableSpinbox::SetVisibilityInt()
if (_numberInt.IsFixedType()) {
_fixedValueInt->show();
_variable->hide();
_toggleType->setVisible(!switcher->variables.empty() &&
_toggleType->setVisible(!GetVariables().empty() &&
!_hideTypeToggle);
} else {
_fixedValueInt->hide();
@@ -191,7 +190,7 @@ void GenericVaraiableSpinbox::SetVisibilityDouble()
if (_numberDouble.IsFixedType()) {
_fixedValueDouble->show();
_variable->hide();
_toggleType->setVisible(!switcher->variables.empty() &&
_toggleType->setVisible(!GetVariables().empty() &&
!_hideTypeToggle);
} else {
_fixedValueDouble->hide();

View File

@@ -1,12 +1,11 @@
#include "variable-string.hpp"
#include "switcher-data.hpp"
#include "utility.hpp"
namespace advss {
void StringVariable::Resolve() const
{
if (switcher && switcher->variables.empty()) {
if (GetVariables().empty()) {
_resolvedValue = _value;
return;
}
@@ -71,11 +70,7 @@ bool StringVariable::empty() const
std::string SubstitueVariables(std::string str)
{
if (!switcher) {
return str;
}
for (const auto &v : switcher->variables) {
for (const auto &v : GetVariables()) {
const auto &variable = std::dynamic_pointer_cast<Variable>(v);
const std::string pattern = "${" + variable->Name() + "}";
ReplaceAll(str, pattern, variable->Value());

View File

@@ -1,7 +1,5 @@
#include "variable-text-edit.hpp"
#include "switcher-data.hpp"
#include <obs-module.h>
#include "obs-module-helper.hpp"
namespace advss {

Some files were not shown because too many files have changed in this diff Show More