Compare commits

..

15 Commits
1.9 ... 1.9.1

Author SHA1 Message Date
WarmUpTill
33667a1f23 Fix crash on advancing to unset scene for extended sequence (#130) 2021-02-24 18:53:52 +01:00
WarmUpTill
7947ad2608 Add option to extend scene sequences (#121)
Extending a scene sequence allows for more complex switching setups, but should also simplify some setups.
Sequences like "A -> B -> A -> C", which previously were only possible by creating a copy of A, can now be specified in a single entry.
To extend a sequence either select the sequence you want to modify and click the extend sequence button or simply double click the entry.
2021-02-24 18:19:23 +01:00
WarmUpTill
920c188213 Add scene trigger for starting and stopping the scene switcher (#129) 2021-02-23 21:00:25 +01:00
WarmUpTill
28fbff1adc Allow specifying transition when reading scenes from file. (#128)
The transition can be specified on the second line of the file.
For example:
Scene 1
Slide
2021-02-21 20:17:32 +01:00
WarmUpTill
326617aad7 Fix checkSwitchInfoFromFile ignoring file switch puase (#127) 2021-02-21 20:16:51 +01:00
WarmUpTill
499ba7dd21 Update the UI's status element if the status changed externally (#126) 2021-02-20 18:58:07 +01:00
WarmUpTill
f4902933ab Fix getCurrentScene() returning garbage if scene group has type random (#125) 2021-02-20 18:08:47 +01:00
WarmUpTill
9aa0b31bff Add scene trigger to unmute source (#124) 2021-02-20 18:08:35 +01:00
WarmUpTill
6d5086b971 Add option to select 'current transition' (#123)
* move addSelectionEntry to AdvSceneSwitcher class and allow select entry to be 'selectable'

Can be useful for implementing 'current transition' as an option or just allowing to select 'None' for screen region ignore

* Add option to select 'current transition'
2021-02-19 19:03:38 +01:00
WarmUpTill
f32f47db4b Install qtbase5-private-dev on Linux (#122) 2021-02-13 19:28:24 +01:00
WarmUpTill
74c0cd1736 Adjust sleep log message and comment for sceneChangedDuringWait() (#120) 2021-02-13 19:14:28 +01:00
WarmUpTill
a74a952b1d Fix log messages containing two '[adv-ss]' tags (#118) 2021-02-07 18:41:47 +01:00
WarmUpTill
5bbfef81e0 Improve scene trigger handling (#117) 2021-02-06 18:23:57 +01:00
WarmUpTill
c31d1cd975 Delay setting of default scene transitions (#116) 2021-02-06 18:23:41 +01:00
WarmUpTill
81d91a9139 resovle name conflict (#115) 2021-02-04 07:24:52 +01:00
23 changed files with 876 additions and 245 deletions

View File

@@ -161,6 +161,7 @@ jobs:
pkg-config \ pkg-config \
python3-dev \ python3-dev \
qtbase5-dev \ qtbase5-dev \
qtbase5-private-dev \
libqt5svg5-dev \ libqt5svg5-dev \
swig \ swig \
libxss-dev libxss-dev

View File

@@ -7,6 +7,13 @@ list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake/")
include(GetGitRevisionDescription) include(GetGitRevisionDescription)
get_git_head_revision(GIT_REFSPEC GIT_SHA1) get_git_head_revision(GIT_REFSPEC GIT_SHA1)
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/cmake/version.cpp.in" "${CMAKE_CURRENT_BINARY_DIR}/src/version.cpp" @ONLY) configure_file("${CMAKE_CURRENT_SOURCE_DIR}/cmake/version.cpp.in" "${CMAKE_CURRENT_BINARY_DIR}/src/version.cpp" @ONLY)
if(WIN32)
get_filename_component(ISS_PLUGIN_FILES_DIR "${CMAKE_BINARY_DIR}\\..\\package" ABSOLUTE)
file(TO_NATIVE_PATH "${ISS_PLUGIN_FILES_DIR}" ISS_PLUGIN_FILES_DIR)
get_filename_component(ISS_MSVC_REDIST_HELPER_DIR "${CMAKE_PACKAGE_PREFIX}\\.." ABSOLUTE)
file(TO_NATIVE_PATH "${ISS_MSVC_REDIST_HELPER_DIR}" ISS_MSVC_REDIST_HELPER_DIR)
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/CI/windows/setup.iss.in" "${CMAKE_CURRENT_BINARY_DIR}/CI/windows/setup.iss" @ONLY)
endif()
if(WIN32) if(WIN32)
get_filename_component(ISS_PLUGIN_FILES_DIR "${CMAKE_BINARY_DIR}\\..\\package" ABSOLUTE) get_filename_component(ISS_PLUGIN_FILES_DIR "${CMAKE_BINARY_DIR}\\..\\package" ABSOLUTE)

View File

@@ -232,7 +232,10 @@ AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.stopStreaming="Streamen stop
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.startStreaming="Streamen starten" AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.startStreaming="Streamen starten"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.startReplayBuffer="Replay Buffer starten" AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.startReplayBuffer="Replay Buffer starten"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.stopReplayBuffer="Replay Buffer stoppen" AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.stopReplayBuffer="Replay Buffer stoppen"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.muteSource="Quelle stumm schalten" AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.muteSource="Audio Quelle stumm schalten"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.unmuteSource="Audio Quelle aktiv schalten"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.startSwitcher="Szenenwechsler starten"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.stopSwitcher="Szenenwechsler stoppen"
AdvSceneSwitcher.sceneTriggerTab.entry="Wenn {{scenes}} {{triggers}} {{actions}} {{audioSources}} nach {{duration}} " AdvSceneSwitcher.sceneTriggerTab.entry="Wenn {{scenes}} {{triggers}} {{actions}} {{audioSources}} nach {{duration}} "
AdvSceneSwitcher.sceneTriggerTab.help="Dieser Tab ermöglicht es automatisiert Handlungen, wie etwa das Stoppen von einer Aufnahme oder des Streamens, bei einem Szenenwechsel auszuführen." AdvSceneSwitcher.sceneTriggerTab.help="Dieser Tab ermöglicht es automatisiert Handlungen, wie etwa das Stoppen von einer Aufnahme oder des Streamens, bei einem Szenenwechsel auszuführen."
@@ -249,6 +252,7 @@ AdvSceneSwitcher.browse="Öffnen"
AdvSceneSwitcher.selectScene="--Szene auswählen--" AdvSceneSwitcher.selectScene="--Szene auswählen--"
AdvSceneSwitcher.selectPreviousScene="Vorhergehende Szene" AdvSceneSwitcher.selectPreviousScene="Vorhergehende Szene"
AdvSceneSwitcher.currentTransition="Aktueller Szenenübergang"
AdvSceneSwitcher.selectTransition="--Szenenübergang auswählen--" AdvSceneSwitcher.selectTransition="--Szenenübergang auswählen--"
AdvSceneSwitcher.selectWindow="--Fenster auswählen--" AdvSceneSwitcher.selectWindow="--Fenster auswählen--"
AdvSceneSwitcher.selectAudioSource="--Audio Quelle auswählen--" AdvSceneSwitcher.selectAudioSource="--Audio Quelle auswählen--"

View File

@@ -186,6 +186,8 @@ AdvSceneSwitcher.sceneSequenceTab.fileType="Text files (*.txt)"
AdvSceneSwitcher.sceneSequenceTab.interruptible="interruptible" AdvSceneSwitcher.sceneSequenceTab.interruptible="interruptible"
AdvSceneSwitcher.sceneSequenceTab.interruptibleHint="Other switching methods are allowed to interrupt this scene sequence" AdvSceneSwitcher.sceneSequenceTab.interruptibleHint="Other switching methods are allowed to interrupt this scene sequence"
AdvSceneSwitcher.sceneSequenceTab.entry="When {{startScenes}} is active switch to {{scenes}} after {{delay}} {{delayUnits}} using {{transitions}} {{interruptible}}" AdvSceneSwitcher.sceneSequenceTab.entry="When {{startScenes}} is active switch to {{scenes}} after {{delay}} {{delayUnits}} using {{transitions}} {{interruptible}}"
AdvSceneSwitcher.sceneSequenceTab.extendEdit="Extend Sequence"
AdvSceneSwitcher.sceneSequenceTab.extendEntry="After {{delay}} {{delayUnits}} switch to {{scenes}} using {{transitions}}"
AdvSceneSwitcher.sceneSequenceTab.help="This tab will allow you to automatically switch to a different scene if a scene was active for a configured period of time.\nFor example, you could automatically cycle back and forth between two scenes automatically.\n\nClick on the highlighted plus symbol to continue." AdvSceneSwitcher.sceneSequenceTab.help="This tab will allow you to automatically switch to a different scene if a scene was active for a configured period of time.\nFor example, you could automatically cycle back and forth between two scenes automatically.\n\nClick on the highlighted plus symbol to continue."
; Audio Tab ; Audio Tab
@@ -233,6 +235,9 @@ AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.startStreaming="start stream
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.startReplayBuffer="start replay buffer" AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.startReplayBuffer="start replay buffer"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.stopReplayBuffer="stop replay buffer" AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.stopReplayBuffer="stop replay buffer"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.muteSource="mute source" AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.muteSource="mute source"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.unmuteSource="unmute source"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.startSwitcher="start the scene switcher"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.stopSwitcher="stop the scene switcher"
AdvSceneSwitcher.sceneTriggerTab.entry="When {{scenes}} {{triggers}} {{actions}} {{audioSources}} after {{duration}}" AdvSceneSwitcher.sceneTriggerTab.entry="When {{scenes}} {{triggers}} {{actions}} {{audioSources}} after {{duration}}"
AdvSceneSwitcher.sceneTriggerTab.help="This tab allows you to trigger actions on scene changes, like stopping recording or streaming." AdvSceneSwitcher.sceneTriggerTab.help="This tab allows you to trigger actions on scene changes, like stopping recording or streaming."
@@ -248,6 +253,7 @@ AdvSceneSwitcher.browse="Browse"
AdvSceneSwitcher.selectScene="--select scene--" AdvSceneSwitcher.selectScene="--select scene--"
AdvSceneSwitcher.selectPreviousScene="Previous Scene" AdvSceneSwitcher.selectPreviousScene="Previous Scene"
AdvSceneSwitcher.currentTransition="Current Transition"
AdvSceneSwitcher.selectTransition="--select transition--" AdvSceneSwitcher.selectTransition="--select transition--"
AdvSceneSwitcher.selectWindow="--select window--" AdvSceneSwitcher.selectWindow="--select window--"
AdvSceneSwitcher.selectAudioSource="--select audio source--" AdvSceneSwitcher.selectAudioSource="--select audio source--"

View File

@@ -2824,6 +2824,20 @@
</property> </property>
</widget> </widget>
</item> </item>
<item>
<widget class="Line" name="line">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="sequenceEdit">
<property name="text">
<string>AdvSceneSwitcher.sceneSequenceTab.extendEdit</string>
</property>
</widget>
</item>
<item> <item>
<spacer name="horizontalSpacer_8"> <spacer name="horizontalSpacer_8">
<property name="orientation"> <property name="orientation">

View File

@@ -92,8 +92,9 @@ bool AdvSceneSwitcher::DisplayMessage(QString msg, bool question)
return false; return false;
} }
void addSelectionEntry(QComboBox *sel, const char *description, void AdvSceneSwitcher::addSelectionEntry(QComboBox *sel,
const char *tooltip = "") const char *description,
bool selectable, const char *tooltip)
{ {
sel->addItem(description); sel->addItem(description);
@@ -106,14 +107,17 @@ void addSelectionEntry(QComboBox *sel, const char *description,
QModelIndex firstIndex = QModelIndex firstIndex =
model->index(0, sel->modelColumn(), sel->rootModelIndex()); model->index(0, sel->modelColumn(), sel->rootModelIndex());
QStandardItem *firstItem = model->itemFromIndex(firstIndex); QStandardItem *firstItem = model->itemFromIndex(firstIndex);
firstItem->setSelectable(false); if (!selectable) {
firstItem->setEnabled(false); firstItem->setSelectable(false);
firstItem->setEnabled(false);
}
} }
void AdvSceneSwitcher::populateSceneSelection(QComboBox *sel, bool addPrevious, void AdvSceneSwitcher::populateSceneSelection(QComboBox *sel, bool addPrevious,
bool addSceneGroup, bool addSceneGroup,
bool addSelect, bool addSelect,
std::string selectText) std::string selectText,
bool selectable)
{ {
sel->clear(); sel->clear();
@@ -122,10 +126,11 @@ void AdvSceneSwitcher::populateSceneSelection(QComboBox *sel, bool addPrevious,
addSelectionEntry( addSelectionEntry(
sel, sel,
obs_module_text("AdvSceneSwitcher.selectScene"), obs_module_text("AdvSceneSwitcher.selectScene"),
selectable,
obs_module_text( obs_module_text(
"AdvSceneSwitcher.invaildEntriesWillNotBeSaved")); "AdvSceneSwitcher.invaildEntriesWillNotBeSaved"));
} else { } else {
addSelectionEntry(sel, selectText.c_str()); addSelectionEntry(sel, selectText.c_str(), selectable);
} }
} }
@@ -150,12 +155,20 @@ void AdvSceneSwitcher::populateSceneSelection(QComboBox *sel, bool addPrevious,
} }
void AdvSceneSwitcher::populateTransitionSelection(QComboBox *sel, void AdvSceneSwitcher::populateTransitionSelection(QComboBox *sel,
bool addSelect) bool addCurrent,
bool addSelect,
bool selectable)
{ {
if (addSelect) { if (addSelect) {
addSelectionEntry( addSelectionEntry(
sel, sel,
obs_module_text("AdvSceneSwitcher.selectTransition")); obs_module_text("AdvSceneSwitcher.selectTransition"),
selectable);
}
if (addCurrent) {
sel->addItem(
obs_module_text("AdvSceneSwitcher.currentTransition"));
} }
obs_frontend_source_list *transitions = new obs_frontend_source_list(); obs_frontend_source_list *transitions = new obs_frontend_source_list();
@@ -197,6 +210,7 @@ void AdvSceneSwitcher::populateAudioSelection(QComboBox *sel, bool addSelect)
addSelectionEntry( addSelectionEntry(
sel, sel,
obs_module_text("AdvSceneSwitcher.selectAudioSource"), obs_module_text("AdvSceneSwitcher.selectAudioSource"),
false,
obs_module_text( obs_module_text(
"AdvSceneSwitcher.invaildEntriesWillNotBeSaved")); "AdvSceneSwitcher.invaildEntriesWillNotBeSaved"));
} }
@@ -227,6 +241,7 @@ void AdvSceneSwitcher::populateMediaSelection(QComboBox *sel, bool addSelect)
addSelectionEntry( addSelectionEntry(
sel, sel,
obs_module_text("AdvSceneSwitcher.selectMediaSource"), obs_module_text("AdvSceneSwitcher.selectMediaSource"),
false,
obs_module_text( obs_module_text(
"AdvSceneSwitcher.invaildEntriesWillNotBeSaved")); "AdvSceneSwitcher.invaildEntriesWillNotBeSaved"));
} }
@@ -428,9 +443,6 @@ void SwitcherData::Thread()
OBSWeakSource scene; OBSWeakSource scene;
OBSWeakSource transition; OBSWeakSource transition;
bool defTransitionMatch = false;
OBSWeakSource defTransition;
endTime = std::chrono::high_resolution_clock::now(); endTime = std::chrono::high_resolution_clock::now();
auto runTime = auto runTime =
@@ -449,7 +461,7 @@ void SwitcherData::Thread()
} }
} }
vblog(LOG_INFO, "sleep for %ld", duration.count()); vblog(LOG_INFO, "try to sleep for %ld", duration.count());
cv.wait_for(lock, duration); cv.wait_for(lock, duration);
startTime = std::chrono::high_resolution_clock::now(); startTime = std::chrono::high_resolution_clock::now();
@@ -457,63 +469,17 @@ void SwitcherData::Thread()
linger = 0; linger = 0;
switcher->Prune(); switcher->Prune();
if (switcher->stop) { if (switcher->stop) {
break; break;
} }
if (checkPause()) { if (checkPause()) {
continue; continue;
} }
match = checkForMatch(scene, transition, linger);
checkDefaultSceneTransitions(defTransitionMatch, defTransition); if (switcher->stop) {
break;
for (int switchFuncName : functionNamesByPriority) {
switch (switchFuncName) {
case read_file_func:
checkSwitchInfoFromFile(match, scene,
transition);
checkFileContent(match, scene, transition);
break;
case idle_func:
checkIdleSwitch(match, scene, transition);
break;
case exe_func:
checkExeSwitch(match, scene, transition);
break;
case screen_region_func:
checkScreenRegionSwitch(match, scene,
transition);
break;
case window_title_func:
checkWindowTitleSwitch(match, scene,
transition);
break;
case round_trip_func:
checkSceneSequence(match, scene, transition,
linger);
break;
case media_func:
checkMediaSwitch(match, scene, transition);
break;
case time_func:
checkTimeSwitch(match, scene, transition);
break;
case audio_func:
checkAudioSwitch(match, scene, transition);
break;
}
if (switcher->stop) {
goto endLoop;
}
if (match) {
break;
}
} }
checkNoMatchSwitch(match, scene, transition, sleep); checkNoMatchSwitch(match, scene, transition, sleep);
checkSwitchCooldown(match); checkSwitchCooldown(match);
if (linger) { if (linger) {
@@ -544,10 +510,6 @@ void SwitcherData::Thread()
// leading to a deadlock, so we have to unlock() // leading to a deadlock, so we have to unlock()
lock.unlock(); lock.unlock();
if (!match && defTransitionMatch) {
setCurrentDefTransition(defTransition);
}
if (match) { if (match) {
switchScene(scene, transition, switchScene(scene, transition,
tansitionOverrideOverride); tansitionOverrideOverride);
@@ -555,10 +517,64 @@ void SwitcherData::Thread()
writeSceneInfoToFile(); writeSceneInfoToFile();
} }
endLoop:
blog(LOG_INFO, "stopped"); blog(LOG_INFO, "stopped");
} }
bool SwitcherData::checkForMatch(OBSWeakSource &scene,
OBSWeakSource &transition, int &linger)
{
bool match = false;
if (uninterruptibleSceneSequenceActive) {
checkSceneSequence(match, scene, transition, linger);
if (match) {
return match;
}
}
for (int switchFuncName : functionNamesByPriority) {
switch (switchFuncName) {
case read_file_func:
checkSwitchInfoFromFile(match, scene, transition);
checkFileContent(match, scene, transition);
break;
case idle_func:
checkIdleSwitch(match, scene, transition);
break;
case exe_func:
checkExeSwitch(match, scene, transition);
break;
case screen_region_func:
checkScreenRegionSwitch(match, scene, transition);
break;
case window_title_func:
checkWindowTitleSwitch(match, scene, transition);
break;
case round_trip_func:
checkSceneSequence(match, scene, transition, linger);
break;
case media_func:
checkMediaSwitch(match, scene, transition);
break;
case time_func:
checkTimeSwitch(match, scene, transition);
break;
case audio_func:
checkAudioSwitch(match, scene, transition);
break;
}
if (switcher->stop) {
return false;
}
if (match) {
break;
}
}
return match;
}
void switchScene(OBSWeakSource &scene, OBSWeakSource &transition, void switchScene(OBSWeakSource &scene, OBSWeakSource &transition,
bool &transitionOverrideOverride) bool &transitionOverrideOverride)
{ {
@@ -643,7 +659,7 @@ extern "C" void FreeSceneSwitcher()
void handleSceneChange(SwitcherData *s) void handleSceneChange(SwitcherData *s)
{ {
// Stop waiting if scene was manually changed // Stop waiting if scene was changed
if (s->sceneChangedDuringWait()) { if (s->sceneChangedDuringWait()) {
s->cv.notify_one(); s->cv.notify_one();
} }
@@ -658,12 +674,8 @@ void handleSceneChange(SwitcherData *s)
s->previousSceneHelper = ws; s->previousSceneHelper = ws;
} }
switcher->checkTriggers(); s->checkTriggers();
} s->checkDefaultSceneTransitions();
void handleTransitionStop(SwitcherData *s)
{
s->checkedDefTransition = false;
} }
void setLiveTime(SwitcherData *s) void setLiveTime(SwitcherData *s)
@@ -702,9 +714,6 @@ static void OBSEvent(enum obs_frontend_event event, void *switcher)
case OBS_FRONTEND_EVENT_SCENE_CHANGED: case OBS_FRONTEND_EVENT_SCENE_CHANGED:
handleSceneChange((SwitcherData *)switcher); handleSceneChange((SwitcherData *)switcher);
break; break;
case OBS_FRONTEND_EVENT_TRANSITION_STOPPED:
handleTransitionStop((SwitcherData *)switcher);
break;
case OBS_FRONTEND_EVENT_RECORDING_STARTED: case OBS_FRONTEND_EVENT_RECORDING_STARTED:
setLiveTime((SwitcherData *)switcher); setLiveTime((SwitcherData *)switcher);
checkAutoStartRecording((SwitcherData *)switcher); checkAutoStartRecording((SwitcherData *)switcher);

View File

@@ -129,6 +129,7 @@ void AdvSceneSwitcher::SetStarted()
ui->pluginRunningText->setText( ui->pluginRunningText->setText(
obs_module_text("AdvSceneSwitcher.status.active")); obs_module_text("AdvSceneSwitcher.status.active"));
ui->pluginRunningText->disconnect(inactivePluse); ui->pluginRunningText->disconnect(inactivePluse);
currentStatusActive = true;
} }
void AdvSceneSwitcher::SetStopped() void AdvSceneSwitcher::SetStopped()
@@ -139,6 +140,7 @@ void AdvSceneSwitcher::SetStopped()
obs_module_text("AdvSceneSwitcher.status.inactive")); obs_module_text("AdvSceneSwitcher.status.inactive"));
inactivePluse = PulseWidget(ui->pluginRunningText, QColor(Qt::red), inactivePluse = PulseWidget(ui->pluginRunningText, QColor(Qt::red),
QColor(0, 0, 0, 0), "QLabel "); QColor(0, 0, 0, 0), "QLabel ");
currentStatusActive = false;
} }
void AdvSceneSwitcher::on_toggleStartButton_clicked() void AdvSceneSwitcher::on_toggleStartButton_clicked()
@@ -681,6 +683,21 @@ void populateAutoStartEventSelection(QComboBox *cb)
"AdvSceneSwitcher.generalTab.status.autoStart.recordingAndStreaming")); "AdvSceneSwitcher.generalTab.status.autoStart.recordingAndStreaming"));
} }
void AdvSceneSwitcher::updateStatus()
{
if (switcher->th && switcher->th->isRunning()) {
if (currentStatusActive) {
return;
}
SetStarted();
} else {
if (!currentStatusActive) {
return;
}
SetStopped();
}
}
void AdvSceneSwitcher::setupGeneralTab() void AdvSceneSwitcher::setupGeneralTab()
{ {
populateSceneSelection(ui->noMatchSwitchScene, false); populateSceneSelection(ui->noMatchSwitchScene, false);
@@ -780,4 +797,10 @@ void AdvSceneSwitcher::setupGeneralTab()
} else { } else {
SetStopped(); SetStopped();
} }
// Updates the UI status element if the status changed externally
// (e.g. via hotkeys)
QTimer *statusTimer = new QTimer(this);
connect(statusTimer, SIGNAL(timeout()), this, SLOT(updateStatus()));
statusTimer->start(1000);
} }

View File

@@ -7,9 +7,9 @@
#include "switcher-data-structs.hpp" #include "switcher-data-structs.hpp"
#define blog(level, msg, ...) blog(level, "[adv-ss] " msg, ##__VA_ARGS__) #define blog(level, msg, ...) blog(level, "[adv-ss] " msg, ##__VA_ARGS__)
#define vblog(level, msg, ...) \ #define vblog(level, msg, ...) \
if (verbose) { \ if (verbose) { \
blog(level, "[adv-ss] " msg, ##__VA_ARGS__); \ blog(level, msg, ##__VA_ARGS__); \
} }
class QCloseEvent; class QCloseEvent;
@@ -23,6 +23,7 @@ class AdvSceneSwitcher : public QDialog {
public: public:
std::unique_ptr<Ui_AdvSceneSwitcher> ui; std::unique_ptr<Ui_AdvSceneSwitcher> ui;
bool loading = true; bool loading = true;
bool currentStatusActive = false;
AdvSceneSwitcher(QWidget *parent); AdvSceneSwitcher(QWidget *parent);
@@ -39,6 +40,7 @@ public:
int IgnoreIdleWindowsFindByData(const QString &window); int IgnoreIdleWindowsFindByData(const QString &window);
void UpdateNonMatchingScene(const QString &name); void UpdateNonMatchingScene(const QString &name);
void OpenSequenceExtendEdit(SequenceWidget *sw);
void SetEditSceneGroup(SceneGroup &sg); void SetEditSceneGroup(SceneGroup &sg);
void loadUI(); void loadUI();
@@ -62,14 +64,20 @@ public:
static bool DisplayMessage(QString msg, bool question = false); static bool DisplayMessage(QString msg, bool question = false);
static void AskBackup(obs_data_t *obj); static void AskBackup(obs_data_t *obj);
static void addSelectionEntry(QComboBox *sel, const char *description,
bool selectable = false,
const char *tooltip = "");
static void populateSceneSelection(QComboBox *sel, static void populateSceneSelection(QComboBox *sel,
bool addPrevious = false, bool addPrevious = false,
bool addSceneGroup = false, bool addSceneGroup = false,
bool addSelect = true, bool addSelect = true,
std::string selectText = ""); std::string selectText = "",
bool selectable = false);
static void populateTransitionSelection(QComboBox *sel, static void populateTransitionSelection(QComboBox *sel,
bool addSelect = true); bool addCurrent = true,
bool addSelect = true,
bool selectable = false);
static void populateWindowSelection(QComboBox *sel, static void populateWindowSelection(QComboBox *sel,
bool addSelect = true); bool addSelect = true);
static void populateAudioSelection(QComboBox *sel, static void populateAudioSelection(QComboBox *sel,
@@ -129,10 +137,12 @@ public slots:
void on_sceneSequenceAdd_clicked(); void on_sceneSequenceAdd_clicked();
void on_sceneSequenceRemove_clicked(); void on_sceneSequenceRemove_clicked();
void on_sceneSequenceSave_clicked();
void on_sceneSequenceLoad_clicked();
void on_sceneSequenceUp_clicked(); void on_sceneSequenceUp_clicked();
void on_sceneSequenceDown_clicked(); void on_sceneSequenceDown_clicked();
void on_sceneSequenceSave_clicked();
void on_sceneSequenceLoad_clicked();
void on_sequenceEdit_clicked();
void on_sceneSequenceSwitches_itemDoubleClicked(QListWidgetItem *item);
void on_verboseLogging_stateChanged(int state); void on_verboseLogging_stateChanged(int state);
void on_uiHintsDisable_stateChanged(int state); void on_uiHintsDisable_stateChanged(int state);
@@ -213,6 +223,7 @@ public slots:
void on_threadPriority_currentTextChanged(const QString &text); void on_threadPriority_currentTextChanged(const QString &text);
void updateScreenRegionCursorPos(); void updateScreenRegionCursorPos();
void updateStatus();
void on_close_clicked(); void on_close_clicked();

View File

@@ -37,7 +37,7 @@ struct SceneGroup {
int currentCount = -1; int currentCount = -1;
std::chrono::high_resolution_clock::time_point lastAdvTime; std::chrono::high_resolution_clock::time_point lastAdvTime;
OBSWeakSource lastRandomScene = nullptr; int lastRandomScene = -1;
inline SceneGroup(){}; inline SceneGroup(){};
inline SceneGroup(std::string name_) : name(name_){}; inline SceneGroup(std::string name_) : name(name_){};

View File

@@ -23,6 +23,10 @@ enum class sceneTriggerAction {
STOP_REPLAY_BUFFER, STOP_REPLAY_BUFFER,
MUTE_SOURCE, MUTE_SOURCE,
UNMUTE_SOURCE,
START_SWITCHER,
STOP_SWITCHER,
}; };
struct SceneTrigger : SceneSwitcherEntry { struct SceneTrigger : SceneSwitcherEntry {
@@ -36,7 +40,8 @@ struct SceneTrigger : SceneSwitcherEntry {
void save(obs_data_t *obj); void save(obs_data_t *obj);
void load(obs_data_t *obj); void load(obs_data_t *obj);
bool checkMatch(OBSWeakSource previousScene); bool checkMatch(OBSWeakSource currentScene,
OBSWeakSource previousScene);
void performAction(); void performAction();
void logMatch(); void logMatch();
}; };

View File

@@ -8,6 +8,7 @@ struct SceneSwitcherEntry {
OBSWeakSource scene = nullptr; OBSWeakSource scene = nullptr;
OBSWeakSource transition = nullptr; OBSWeakSource transition = nullptr;
bool usePreviousScene = false; bool usePreviousScene = false;
bool useCurrentTransition = false;
virtual const char *getType() = 0; virtual const char *getType() = 0;
virtual bool initialized(); virtual bool initialized();
@@ -64,14 +65,15 @@ class SwitchWidget : public QWidget {
public: public:
SwitchWidget(QWidget *parent, SceneSwitcherEntry *s, SwitchWidget(QWidget *parent, SceneSwitcherEntry *s,
bool usePreviousScene = true, bool addSceneGroup = false); bool usePreviousScene = true, bool addSceneGroup = false,
bool addCurrentTransition = true);
virtual SceneSwitcherEntry *getSwitchData(); virtual SceneSwitcherEntry *getSwitchData();
virtual void setSwitchData(SceneSwitcherEntry *s); virtual void setSwitchData(SceneSwitcherEntry *s);
void showSwitchData(); void showSwitchData();
static void swapSwitchData(SwitchWidget *s1, SwitchWidget *s2); static void swapSwitchData(SwitchWidget *s1, SwitchWidget *s2);
private slots: protected slots:
void SceneChanged(const QString &text); void SceneChanged(const QString &text);
void TransitionChanged(const QString &text); void TransitionChanged(const QString &text);
void SceneGroupAdd(const QString &name); void SceneGroupAdd(const QString &name);

View File

@@ -1,4 +1,6 @@
#pragma once #pragma once
#include <QVBoxLayout>
#include "switch-generic.hpp" #include "switch-generic.hpp"
constexpr auto round_trip_func = 1; constexpr auto round_trip_func = 1;
@@ -12,42 +14,77 @@ typedef enum {
struct SceneSequenceSwitch : SceneSwitcherEntry { struct SceneSequenceSwitch : SceneSwitcherEntry {
static bool pause; static bool pause;
SwitchTargetType startTargetType = SwitchTargetType::Scene;
OBSWeakSource startScene = nullptr; OBSWeakSource startScene = nullptr;
double delay = 0; double delay = 0;
int delayMultiplier = 1; int delayMultiplier = 1;
bool interruptible = false; bool interruptible = false;
unsigned int matchCount = 0; unsigned int matchCount = 0;
// nullptr marks start point and reaching end of extended sequence
SceneSequenceSwitch *activeSequence = nullptr;
std::unique_ptr<SceneSequenceSwitch> extendedSequence = nullptr;
const char *getType() { return "sequence"; } const char *getType() { return "sequence"; }
bool initialized(); bool initialized();
bool valid(); bool valid();
void save(obs_data_t *obj); void save(obs_data_t *obj, bool saveExt = true);
void load(obs_data_t *obj); void load(obs_data_t *obj, bool saveExt = true);
bool reduce();
SceneSequenceSwitch *extend();
bool checkMatch(OBSWeakSource currentScene, int &linger,
SceneSequenceSwitch *root = nullptr);
bool checkDurationMatchInterruptible();
void prepareUninterruptibleMatch(OBSWeakSource currentScene,
int &linger);
void advanceActiveSequence();
void logAdvanceSequence();
void logSequenceCanceled();
}; };
class SequenceWidget : public SwitchWidget { class SequenceWidget : public SwitchWidget {
Q_OBJECT Q_OBJECT
public: public:
SequenceWidget(QWidget *parent, SceneSequenceSwitch *s); SequenceWidget(QWidget *parent, SceneSequenceSwitch *s,
bool extendSequence = false,
bool editExtendMode = false);
SceneSequenceSwitch *getSwitchData(); SceneSequenceSwitch *getSwitchData();
void setSwitchData(SceneSequenceSwitch *s); void setSwitchData(SceneSequenceSwitch *s);
static void swapSwitchData(SequenceWidget *s1, SequenceWidget *s2); static void swapSwitchData(SequenceWidget *s1, SequenceWidget *s2);
void UpdateDelay(); void UpdateDelay();
void UpdateExtendText();
void setExtendedSequenceStartScene();
private slots: private slots:
void SceneChanged(const QString &text);
void DelayChanged(double delay); void DelayChanged(double delay);
void DelayUnitsChanged(int idx); void DelayUnitsChanged(int idx);
void StartSceneChanged(const QString &text); void StartSceneChanged(const QString &text);
void InterruptibleChanged(int state); void InterruptibleChanged(int state);
void ExtendClicked();
void ReduceClicked();
private: protected:
QDoubleSpinBox *delay; QDoubleSpinBox *delay;
QComboBox *delayUnits; QComboBox *delayUnits;
QComboBox *startScenes; QComboBox *startScenes;
QCheckBox *interruptible; QCheckBox *interruptible;
QVBoxLayout *extendSequenceLayout;
QPushButton *extend;
QPushButton *reduce;
// I would prefer having a list of only widgets of type editExtendMode
// but I am not sure how to best implement that using a QListWidget.
//
// So use edit button to bring up edit widget and
// add a label to disaplay current extended sequence state.
QLabel *extendText;
SceneSequenceSwitch *switchData; SceneSequenceSwitch *switchData;
}; };

View File

@@ -20,6 +20,8 @@ struct DefaultSceneTransition : SceneSwitcherEntry {
static bool pause; static bool pause;
const char *getType() { return "def_transition"; } const char *getType() { return "def_transition"; }
bool checkMatch(OBSWeakSource currentScene);
void setTransition();
inline DefaultSceneTransition(){}; inline DefaultSceneTransition(){};
inline DefaultSceneTransition(OBSWeakSource scene_, inline DefaultSceneTransition(OBSWeakSource scene_,

View File

@@ -24,6 +24,7 @@
constexpr auto default_interval = 300; constexpr auto default_interval = 300;
constexpr auto previous_scene_name = "Previous Scene"; constexpr auto previous_scene_name = "Previous Scene";
constexpr auto current_transition_name = "Current Transition";
typedef enum { NO_SWITCH = 0, SWITCH = 1, RANDOM_SWITCH = 2 } NoMatch; typedef enum { NO_SWITCH = 0, SWITCH = 1, RANDOM_SWITCH = 2 } NoMatch;
typedef enum { PERSIST = 0, START = 1, STOP = 2 } StartupBehavior; typedef enum { PERSIST = 0, START = 1, STOP = 2 } StartupBehavior;
@@ -84,6 +85,7 @@ struct SwitcherData {
std::vector<std::string> ignoreWindowsSwitches; std::vector<std::string> ignoreWindowsSwitches;
bool uninterruptibleSceneSequenceActive = false;
std::deque<SceneSequenceSwitch> sceneSequenceSwitches; std::deque<SceneSequenceSwitch> sceneSequenceSwitches;
std::deque<RandomSwitch> randomSwitches; std::deque<RandomSwitch> randomSwitches;
@@ -100,7 +102,6 @@ struct SwitcherData {
std::deque<SceneTransition> sceneTransitions; std::deque<SceneTransition> sceneTransitions;
std::deque<DefaultSceneTransition> defaultSceneTransitions; std::deque<DefaultSceneTransition> defaultSceneTransitions;
bool checkedDefTransition = false;
std::deque<MediaSwitch> mediaSwitches; std::deque<MediaSwitch> mediaSwitches;
@@ -160,13 +161,14 @@ struct SwitcherData {
void Stop(); void Stop();
bool sceneChangedDuringWait(); bool sceneChangedDuringWait();
bool prioFuncsValid(); bool prioFuncsValid();
void writeSceneInfoToFile(); void writeSceneInfoToFile();
void writeToStatusFile(QString status); void writeToStatusFile(QString status);
bool checkPause();
void checkDefaultSceneTransitions(bool &match, bool checkForMatch(OBSWeakSource &scene, OBSWeakSource &transition,
OBSWeakSource &transition); int &linger);
void setCurrentDefTransition(OBSWeakSource &transition);
void checkSceneSequence(bool &match, OBSWeakSource &scene, void checkSceneSequence(bool &match, OBSWeakSource &scene,
OBSWeakSource &transition, int &linger); OBSWeakSource &transition, int &linger);
void checkIdleSwitch(bool &match, OBSWeakSource &scene, void checkIdleSwitch(bool &match, OBSWeakSource &scene,
@@ -195,6 +197,8 @@ struct SwitcherData {
OBSWeakSource &transition, int &sleep); OBSWeakSource &transition, int &sleep);
void checkSwitchCooldown(bool &match); void checkSwitchCooldown(bool &match);
void checkTriggers(); void checkTriggers();
bool checkPause();
void checkDefaultSceneTransitions();
void saveSettings(obs_data_t *obj); void saveSettings(obs_data_t *obj);
void saveWindowTitleSwitches(obs_data_t *obj); void saveWindowTitleSwitches(obs_data_t *obj);

View File

@@ -51,27 +51,26 @@ OBSWeakSource SceneGroup::getNextSceneTime()
OBSWeakSource SceneGroup::getNextSceneRandom() OBSWeakSource SceneGroup::getNextSceneRandom()
{ {
if (scenes.size() == 1) { if (scenes.size() == 1) {
return *scenes.begin(); currentIdx = 0;
return scenes[currentIdx];
} }
std::vector<OBSWeakSource> rs(scenes); int rIdx = lastRandomScene;
std::random_device rng; do {
std::mt19937 urng(rng()); rIdx = rand() % scenes.size();
std::shuffle(rs.begin(), rs.end(), urng); } while (rIdx == lastRandomScene);
for (OBSWeakSource &s : rs) {
if (s == lastRandomScene) {
continue;
}
lastRandomScene = s; lastRandomScene = rIdx;
currentIdx = rIdx;
return s; return scenes[currentIdx];
}
return nullptr;
} }
OBSWeakSource SceneGroup::getCurrentScene() OBSWeakSource SceneGroup::getCurrentScene()
{ {
if (scenes.size() < currentIdx + 1) {
return nullptr;
}
return scenes[currentIdx]; return scenes[currentIdx];
} }

View File

@@ -133,6 +133,15 @@ void SceneTrigger::logMatch()
case sceneTriggerAction::MUTE_SOURCE: case sceneTriggerAction::MUTE_SOURCE:
actionName = "MUTE (" + GetWeakSourceName(audioSource) + ")"; actionName = "MUTE (" + GetWeakSourceName(audioSource) + ")";
break; break;
case sceneTriggerAction::UNMUTE_SOURCE:
actionName = "UNMUTE (" + GetWeakSourceName(audioSource) + ")";
break;
case sceneTriggerAction::START_SWITCHER:
actionName = "START SCENE SWITCHER";
break;
case sceneTriggerAction::STOP_SWITCHER:
actionName = "STOP SCENE SWITCHER";
break;
default: default:
actionName = "UNKOWN"; actionName = "UNKOWN";
break; break;
@@ -176,24 +185,35 @@ void frontEndActionThread(sceneTriggerAction action, double delay)
case sceneTriggerAction::STOP_REPLAY_BUFFER: case sceneTriggerAction::STOP_REPLAY_BUFFER:
obs_frontend_replay_buffer_stop(); obs_frontend_replay_buffer_stop();
break; break;
case sceneTriggerAction::MUTE_SOURCE:
obs_frontend_replay_buffer_stop();
break;
default: default:
blog(LOG_WARNING, "ignoring unexpected frontend action '%d'",
action);
break; break;
} }
} }
void muteThread(OBSWeakSource source, double delay) void muteThread(OBSWeakSource source, double delay, bool mute)
{ {
long long mil = delay * 1000; long long mil = delay * 1000;
std::this_thread::sleep_for(std::chrono::milliseconds(mil)); std::this_thread::sleep_for(std::chrono::milliseconds(mil));
auto s = obs_weak_source_get_source(source); auto s = obs_weak_source_get_source(source);
obs_source_set_muted(s, true); obs_source_set_muted(s, mute);
obs_source_release(s); obs_source_release(s);
} }
void statusThread(double delay, bool stop)
{
long long mil = delay * 1000;
std::this_thread::sleep_for(std::chrono::milliseconds(mil));
if (stop) {
switcher->Stop();
} else {
switcher->Start();
}
}
bool isFrontendAction(sceneTriggerAction triggerAction) bool isFrontendAction(sceneTriggerAction triggerAction)
{ {
return triggerAction == sceneTriggerAction::START_RECORDING || return triggerAction == sceneTriggerAction::START_RECORDING ||
@@ -206,6 +226,18 @@ bool isFrontendAction(sceneTriggerAction triggerAction)
triggerAction == sceneTriggerAction::STOP_REPLAY_BUFFER; triggerAction == sceneTriggerAction::STOP_REPLAY_BUFFER;
} }
bool isAudioAction(sceneTriggerAction t)
{
return t == sceneTriggerAction::MUTE_SOURCE ||
t == sceneTriggerAction::UNMUTE_SOURCE;
}
bool isSwitcherStatusAction(sceneTriggerAction t)
{
return t == sceneTriggerAction::START_SWITCHER ||
t == sceneTriggerAction::STOP_SWITCHER;
}
void SceneTrigger::performAction() void SceneTrigger::performAction()
{ {
if (triggerAction == sceneTriggerAction::NONE) { if (triggerAction == sceneTriggerAction::NONE) {
@@ -216,20 +248,22 @@ void SceneTrigger::performAction()
if (isFrontendAction(triggerAction)) { if (isFrontendAction(triggerAction)) {
t = std::thread(frontEndActionThread, triggerAction, duration); t = std::thread(frontEndActionThread, triggerAction, duration);
} else if (isAudioAction(triggerAction)) {
bool mute = triggerAction == sceneTriggerAction::MUTE_SOURCE;
t = std::thread(muteThread, audioSource, duration, mute);
} else if (isSwitcherStatusAction(triggerAction)) {
bool stop = triggerAction == sceneTriggerAction::STOP_SWITCHER;
t = std::thread(statusThread, duration, stop);
} else { } else {
t = std::thread(muteThread, audioSource, duration); blog(LOG_WARNING, "ignoring unkown action '%d'", triggerAction);
} }
t.detach(); t.detach();
} }
bool SceneTrigger::checkMatch(OBSWeakSource previousScene) bool SceneTrigger::checkMatch(OBSWeakSource currentScene,
OBSWeakSource previousScene)
{ {
OBSSource source = obs_frontend_get_current_scene();
OBSWeakSource currentScene = obs_source_get_weak_source(source);
obs_source_release(source);
obs_weak_source_release(currentScene);
switch (triggerType) { switch (triggerType) {
case sceneTriggerType::NONE: case sceneTriggerType::NONE:
return false; return false;
@@ -245,16 +279,26 @@ bool SceneTrigger::checkMatch(OBSWeakSource previousScene)
void SwitcherData::checkTriggers() void SwitcherData::checkTriggers()
{ {
if (SceneTrigger::pause || stop) { if (SceneTrigger::pause) {
return; return;
} }
OBSSource source = obs_frontend_get_current_scene();
OBSWeakSource currentScene = obs_source_get_weak_source(source);
for (auto &t : sceneTriggers) { for (auto &t : sceneTriggers) {
if (t.checkMatch(previousScene)) { if (stop && !isSwitcherStatusAction(t.triggerAction)) {
continue;
}
if (t.checkMatch(currentScene, previousScene)) {
t.logMatch(); t.logMatch();
t.performAction(); t.performAction();
} }
} }
obs_source_release(source);
obs_weak_source_release(currentScene);
} }
void SwitcherData::saveSceneTriggers(obs_data_t *obj) void SwitcherData::saveSceneTriggers(obs_data_t *obj)
@@ -425,10 +469,13 @@ void SceneTrigger::load(obs_data_t *obj)
audioSource = GetWeakSourceByName(audioSourceName); audioSource = GetWeakSourceByName(audioSourceName);
} }
inline void populateTriggers(QComboBox *list) static inline void populateTriggers(QComboBox *list)
{ {
list->addItem(obs_module_text( AdvSceneSwitcher::addSelectionEntry(
"AdvSceneSwitcher.sceneTriggerTab.sceneTriggerType.none")); list,
obs_module_text(
"AdvSceneSwitcher.sceneTriggerTab.sceneTriggerType.none"));
list->addItem(obs_module_text( list->addItem(obs_module_text(
"AdvSceneSwitcher.sceneTriggerTab.sceneTriggerType.sceneActive")); "AdvSceneSwitcher.sceneTriggerTab.sceneTriggerType.sceneActive"));
list->addItem(obs_module_text( list->addItem(obs_module_text(
@@ -439,8 +486,11 @@ inline void populateTriggers(QComboBox *list)
inline void populateActions(QComboBox *list) inline void populateActions(QComboBox *list)
{ {
list->addItem(obs_module_text( AdvSceneSwitcher::addSelectionEntry(
"AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.none")); list,
obs_module_text(
"AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.none"));
list->addItem(obs_module_text( list->addItem(obs_module_text(
"AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.startRecording")); "AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.startRecording"));
list->addItem(obs_module_text( list->addItem(obs_module_text(
@@ -459,6 +509,12 @@ inline void populateActions(QComboBox *list)
"AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.stopReplayBuffer")); "AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.stopReplayBuffer"));
list->addItem(obs_module_text( list->addItem(obs_module_text(
"AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.muteSource")); "AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.muteSource"));
list->addItem(obs_module_text(
"AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.unmuteSource"));
list->addItem(obs_module_text(
"AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.startSwitcher"));
list->addItem(obs_module_text(
"AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.stopSwitcher"));
} }
SceneTriggerWidget::SceneTriggerWidget(QWidget *parent, SceneTrigger *s) SceneTriggerWidget::SceneTriggerWidget(QWidget *parent, SceneTrigger *s)
@@ -495,7 +551,7 @@ SceneTriggerWidget::SceneTriggerWidget(QWidget *parent, SceneTrigger *s)
audioSources->setCurrentText( audioSources->setCurrentText(
GetWeakSourceName(s->audioSource).c_str()); GetWeakSourceName(s->audioSource).c_str());
if (s->triggerAction == sceneTriggerAction::MUTE_SOURCE) { if (isAudioAction(s->triggerAction)) {
audioSources->show(); audioSources->show();
} else { } else {
audioSources->hide(); audioSources->hide();
@@ -560,7 +616,7 @@ void SceneTriggerWidget::TriggerActionChanged(int index)
static_cast<sceneTriggerAction>(index); static_cast<sceneTriggerAction>(index);
} }
if (switchData->triggerAction == sceneTriggerAction::MUTE_SOURCE) { if (isAudioAction(switchData->triggerAction)) {
audioSources->show(); audioSources->show();
} else { } else {
audioSources->hide(); audioSources->hide();

View File

@@ -118,29 +118,37 @@ void SwitcherData::writeToStatusFile(QString msg)
void SwitcherData::checkSwitchInfoFromFile(bool &match, OBSWeakSource &scene, void SwitcherData::checkSwitchInfoFromFile(bool &match, OBSWeakSource &scene,
OBSWeakSource &transition) OBSWeakSource &transition)
{ {
if (!fileIO.readEnabled || fileIO.readPath.empty()) { if (!fileIO.readEnabled || fileIO.readPath.empty() ||
FileSwitch::pause) {
return; return;
} }
QFile file(QString::fromStdString(fileIO.readPath)); QFile file(QString::fromStdString(fileIO.readPath));
if (file.open(QIODevice::ReadOnly)) { if (!file.open(QIODevice::ReadOnly)) {
QTextStream in(&file); return;
QString sceneStr = in.readLine();
obs_source_t *sceneRead =
obs_get_source_by_name(sceneStr.toUtf8().constData());
if (sceneRead) {
obs_weak_source_t *sceneReadWs =
obs_source_get_weak_source(sceneRead);
match = true;
scene = sceneReadWs;
transition = nullptr;
obs_weak_source_release(sceneReadWs);
obs_source_release(sceneRead);
}
file.close();
} }
QTextStream in(&file);
QString sceneStr = in.readLine();
OBSWeakSource sceneRead = GetWeakSourceByQString(sceneStr);
QString transitionStr = in.readLine();
OBSWeakSource transitionRead =
GetWeakTransitionByQString(transitionStr);
if (sceneRead) {
match = true;
scene = sceneRead;
transition = transitionRead;
vblog(LOG_INFO,
"match for 'file' - read scene '%s' and transition '%s' from file '%s'",
sceneStr.toUtf8().constData(),
transitionStr.toUtf8().constData(),
fileIO.readPath.c_str());
}
file.close();
} }
static size_t WriteCallback(void *contents, size_t size, size_t nmemb, static size_t WriteCallback(void *contents, size_t size, size_t nmemb,

View File

@@ -5,14 +5,15 @@ bool SceneSwitcherEntry::initialized()
{ {
return (usePreviousScene || WeakSourceValid(scene) || return (usePreviousScene || WeakSourceValid(scene) ||
SceneGroupValid(group)) && SceneGroupValid(group)) &&
transition; (useCurrentTransition || transition);
} }
bool SceneSwitcherEntry::valid() bool SceneSwitcherEntry::valid()
{ {
return !initialized() || ((usePreviousScene || WeakSourceValid(scene) || return !initialized() ||
SceneGroupValid(group)) && ((usePreviousScene || WeakSourceValid(scene) ||
WeakSourceValid(transition)); SceneGroupValid(group)) &&
(useCurrentTransition || WeakSourceValid(transition)));
} }
void SceneSwitcherEntry::logMatchScene() void SceneSwitcherEntry::logMatchScene()
@@ -82,8 +83,11 @@ void SceneSwitcherEntry::save(obs_data_t *obj, const char *targetTypeSaveName,
obs_data_set_string(obj, targetSaveName, targetName.c_str()); obs_data_set_string(obj, targetSaveName, targetName.c_str());
obs_data_set_string(obj, transitionSaveName, std::string transitionName = current_transition_name;
GetWeakSourceName(transition).c_str()); if (!useCurrentTransition) {
transitionName = GetWeakSourceName(transition);
}
obs_data_set_string(obj, transitionSaveName, transitionName.c_str());
} }
void SceneSwitcherEntry::load(obs_data_t *obj, const char *targetTypeLoadName, void SceneSwitcherEntry::load(obs_data_t *obj, const char *targetTypeLoadName,
@@ -104,11 +108,14 @@ void SceneSwitcherEntry::load(obs_data_t *obj, const char *targetTypeLoadName,
group = GetSceneGroupByName(targetName); group = GetSceneGroupByName(targetName);
} }
usePreviousScene = strcmp(targetName, previous_scene_name) == 0;
const char *transitionName = const char *transitionName =
obs_data_get_string(obj, transitionLoadName); obs_data_get_string(obj, transitionLoadName);
transition = GetWeakTransitionByName(transitionName); transition = GetWeakTransitionByName(transitionName);
usePreviousScene = strcmp(targetName, previous_scene_name) == 0; useCurrentTransition =
strcmp(transitionName, current_transition_name) == 0;
} }
void SwitchWidget::SceneGroupAdd(const QString &name) void SwitchWidget::SceneGroupAdd(const QString &name)
@@ -166,7 +173,8 @@ void SwitchWidget::SceneGroupRename(const QString &oldName,
} }
SwitchWidget::SwitchWidget(QWidget *parent, SceneSwitcherEntry *s, SwitchWidget::SwitchWidget(QWidget *parent, SceneSwitcherEntry *s,
bool usePreviousScene, bool addSceneGroup) bool usePreviousScene, bool addSceneGroup,
bool addCurrentTransition)
{ {
scenes = new QComboBox(); scenes = new QComboBox();
transitions = new QComboBox(); transitions = new QComboBox();
@@ -194,7 +202,8 @@ SwitchWidget::SwitchWidget(QWidget *parent, SceneSwitcherEntry *s,
AdvSceneSwitcher::populateSceneSelection(scenes, usePreviousScene, AdvSceneSwitcher::populateSceneSelection(scenes, usePreviousScene,
addSceneGroup); addSceneGroup);
AdvSceneSwitcher::populateTransitionSelection(transitions); AdvSceneSwitcher::populateTransitionSelection(transitions,
addCurrentTransition);
switchData = s; switchData = s;
showSwitchData(); showSwitchData();
@@ -218,6 +227,10 @@ void SwitchWidget::showSwitchData()
transitions->setCurrentText( transitions->setCurrentText(
GetWeakSourceName(switchData->transition).c_str()); GetWeakSourceName(switchData->transition).c_str());
if (switchData->useCurrentTransition) {
transitions->setCurrentText(
obs_module_text("AdvSceneSwitcher.currentTransition"));
}
if (switchData->usePreviousScene) { if (switchData->usePreviousScene) {
scenes->setCurrentText(obs_module_text( scenes->setCurrentText(obs_module_text(
@@ -278,4 +291,5 @@ void SwitchWidget::TransitionChanged(const QString &text)
std::lock_guard<std::mutex> lock(switcher->m); std::lock_guard<std::mutex> lock(switcher->m);
switchData->transition = GetWeakTransitionByQString(text); switchData->transition = GetWeakTransitionByQString(text);
switchData->useCurrentTransition = switchData->transition == nullptr;
} }

View File

@@ -353,7 +353,8 @@ ScreenRegionWidget::ScreenRegionWidget(QWidget *parent, ScreenRegionSwitch *s)
AdvSceneSwitcher::populateSceneSelection( AdvSceneSwitcher::populateSceneSelection(
excludeScenes, false, false, true, excludeScenes, false, false, true,
obs_module_text( obs_module_text(
"AdvSceneSwitcher.screenRegionTab.excludeScenes.None")); "AdvSceneSwitcher.screenRegionTab.excludeScenes.None"),
true);
if (s) { if (s) {
excludeScenes->setCurrentText( excludeScenes->setCurrentText(

View File

@@ -4,6 +4,8 @@
#include "headers/advanced-scene-switcher.hpp" #include "headers/advanced-scene-switcher.hpp"
#include "headers/utility.hpp" #include "headers/utility.hpp"
constexpr auto max_extend_text_size = 150;
bool SceneSequenceSwitch::pause = false; bool SceneSequenceSwitch::pause = false;
static QMetaObject::Connection addPulse; static QMetaObject::Connection addPulse;
@@ -159,30 +161,41 @@ void AdvSceneSwitcher::on_sceneSequenceLoad_clicked()
close(); close();
} }
bool matchInterruptible(SwitcherData *switcher, SceneSequenceSwitch &s) void AdvSceneSwitcher::OpenSequenceExtendEdit(SequenceWidget *sw)
{ {
bool durationReached = s.matchCount * (switcher->interval / 1000.0) >= QDialog edit;
s.delay; SequenceWidget editWidget(this, sw->getSwitchData(), false, true);
QHBoxLayout layout;
layout.setSizeConstraint(QLayout::SetFixedSize);
layout.addWidget(&editWidget);
edit.setLayout(&layout);
edit.setWindowTitle(obs_module_text(
"AdvSceneSwitcher.sceneSequenceTab.extendEdit"));
edit.exec();
s.matchCount++; sw->UpdateExtendText();
if (durationReached) {
return true;
}
return false;
} }
bool matchUninterruptible(SwitcherData *switcher, SceneSequenceSwitch &s, void AdvSceneSwitcher::on_sequenceEdit_clicked()
obs_source_t *currentSource, int &linger)
{ {
// scene was already active for the previous cycle so remove this time int index = ui->sceneSequenceSwitches->currentRow();
int dur = s.delay * 1000 - switcher->interval; if (index == -1) {
if (dur > 0) { return;
switcher->waitScene = currentSource;
linger = dur;
} }
return true; SequenceWidget *currentWidget =
(SequenceWidget *)ui->sceneSequenceSwitches->itemWidget(
ui->sceneSequenceSwitches->item(index));
OpenSequenceExtendEdit(currentWidget);
}
void AdvSceneSwitcher::on_sceneSequenceSwitches_itemDoubleClicked(
QListWidgetItem *item)
{
SequenceWidget *currentWidget =
(SequenceWidget *)ui->sceneSequenceSwitches->itemWidget(item);
OpenSequenceExtendEdit(currentWidget);
} }
void SwitcherData::checkSceneSequence(bool &match, OBSWeakSource &scene, void SwitcherData::checkSceneSequence(bool &match, OBSWeakSource &scene,
@@ -192,38 +205,53 @@ void SwitcherData::checkSceneSequence(bool &match, OBSWeakSource &scene,
return; return;
} }
obs_source_t *currentSource = obs_frontend_get_current_scene(); obs_source_t *currentSceneSource = obs_frontend_get_current_scene();
obs_weak_source_t *ws = obs_source_get_weak_source(currentSource); obs_weak_source_t *currentScene =
obs_source_get_weak_source(currentSceneSource);
for (SceneSequenceSwitch &s : sceneSequenceSwitches) { for (SceneSequenceSwitch &s : sceneSequenceSwitches) {
if (!s.initialized()) { // Continue the active uninterruptible sequence and skip others
if (uninterruptibleSceneSequenceActive &&
s.activeSequence == nullptr) {
continue; continue;
} }
if (s.startScene == ws) { bool matched = s.checkMatch(currentScene, linger);
if (!match) {
if (s.interruptible) {
match = matchInterruptible(switcher, s);
} else {
match = matchUninterruptible(
switcher, s, currentSource,
linger);
}
if (match) { if (!match && matched) {
scene = s.getScene(); match = matched;
transition = s.transition;
if (switcher->verbose) { if (s.activeSequence) {
s.logMatch(); scene = s.activeSequence->getScene();
} transition = s.activeSequence->transition;
} else {
scene = s.getScene();
transition = s.transition;
if (verbose) {
s.logMatch();
} }
} }
} else {
s.matchCount = 0; s.advanceActiveSequence();
if (verbose) {
s.logAdvanceSequence();
}
// Ignore other switching methods if sequence is not
// interruptible and has not reached its end
if (s.activeSequence) {
uninterruptibleSceneSequenceActive =
!s.interruptible;
}
} }
} }
obs_source_release(currentSource);
obs_weak_source_release(ws); if (!match) {
uninterruptibleSceneSequenceActive = false;
}
obs_source_release(currentSceneSource);
obs_weak_source_release(currentScene);
} }
void SwitcherData::saveSceneSequenceSwitches(obs_data_t *obj) void SwitcherData::saveSceneSequenceSwitches(obs_data_t *obj)
@@ -291,18 +319,32 @@ bool SceneSequenceSwitch::valid()
(SceneSwitcherEntry::valid() && WeakSourceValid(startScene)); (SceneSwitcherEntry::valid() && WeakSourceValid(startScene));
} }
void SceneSequenceSwitch::save(obs_data_t *obj) void SceneSequenceSwitch::save(obs_data_t *obj, bool saveExt)
{ {
SceneSwitcherEntry::save(obj); SceneSwitcherEntry::save(obj);
obs_data_set_int(obj, "startTargetType",
static_cast<int>(startTargetType));
obs_data_set_string(obj, "startScene", obs_data_set_string(obj, "startScene",
GetWeakSourceName(startScene).c_str()); GetWeakSourceName(startScene).c_str());
obs_data_set_double(obj, "delay", delay); obs_data_set_double(obj, "delay", delay);
obs_data_set_int(obj, "delayMultiplier", delayMultiplier); obs_data_set_int(obj, "delayMultiplier", delayMultiplier);
obs_data_set_bool(obj, "interruptible", interruptible); obs_data_set_bool(obj, "interruptible", interruptible);
if (saveExt) {
auto cur = extendedSequence.get();
obs_data_array_t *extendScenes = obs_data_array_create();
while (cur) {
obs_data_t *array_obj = obs_data_create();
cur->save(array_obj, false);
obs_data_array_push_back(extendScenes, array_obj);
obs_data_release(array_obj);
cur = cur->extendedSequence.get();
}
obs_data_set_array(obj, "extendScenes", extendScenes);
obs_data_array_release(extendScenes);
}
} }
// To be removed in future version // To be removed in future version
@@ -341,14 +383,15 @@ bool loadOldScequence(obs_data_t *obj, SceneSequenceSwitch *s)
return true; return true;
} }
void SceneSequenceSwitch::load(obs_data_t *obj) void SceneSequenceSwitch::load(obs_data_t *obj, bool saveExt)
{ {
if (loadOldScequence(obj, this)) { if (loadOldScequence(obj, this)) {
return; return;
} }
SceneSwitcherEntry::load(obj); SceneSwitcherEntry::load(obj);
startTargetType = static_cast<SwitchTargetType>(
obs_data_get_int(obj, "startTargetType"));
const char *scene = obs_data_get_string(obj, "startScene"); const char *scene = obs_data_get_string(obj, "startScene");
startScene = GetWeakSourceByName(scene); startScene = GetWeakSourceByName(scene);
@@ -360,6 +403,174 @@ void SceneSequenceSwitch::load(obs_data_t *obj)
delayMultiplier = 1; delayMultiplier = 1;
interruptible = obs_data_get_bool(obj, "interruptible"); interruptible = obs_data_get_bool(obj, "interruptible");
if (saveExt) {
auto cur = this;
obs_data_array_t *extendScenes =
obs_data_get_array(obj, "extendScenes");
size_t count = obs_data_array_count(extendScenes);
for (size_t i = 0; i < count; i++) {
obs_data_t *array_obj =
obs_data_array_item(extendScenes, i);
cur->extendedSequence =
std::make_unique<SceneSequenceSwitch>();
cur->extendedSequence->load(array_obj, false);
cur = cur->extendedSequence.get();
obs_data_release(array_obj);
}
obs_data_array_release(extendScenes);
}
}
bool SceneSequenceSwitch::reduce()
{
// Reset activeSequence just in case it was one of the deleted entries
activeSequence = nullptr;
if (!extendedSequence) {
return true;
}
if (extendedSequence->reduce()) {
extendedSequence.reset(nullptr);
}
return false;
}
SceneSequenceSwitch *SceneSequenceSwitch::extend()
{
if (extendedSequence) {
return extendedSequence->extend();
}
extendedSequence = std::make_unique<SceneSequenceSwitch>();
extendedSequence->startScene = scene;
return extendedSequence.get();
}
bool SceneSequenceSwitch::checkMatch(OBSWeakSource currentScene, int &linger,
SceneSequenceSwitch *root)
{
if (!initialized()) {
if (root) {
root->activeSequence = nullptr;
}
return false;
}
bool match = false;
if (activeSequence) {
return activeSequence->checkMatch(currentScene, linger, this);
}
if (startScene == currentScene) {
if (interruptible) {
match = checkDurationMatchInterruptible();
} else {
match = true;
prepareUninterruptibleMatch(currentScene, linger);
}
} else {
matchCount = 0;
if (root) {
root->activeSequence = nullptr;
logSequenceCanceled();
}
}
return match;
}
bool SceneSequenceSwitch::checkDurationMatchInterruptible()
{
bool durationReached = matchCount * (switcher->interval / 1000.0) >=
delay;
matchCount++;
if (durationReached) {
matchCount = 0;
return true;
}
return false;
}
void SceneSequenceSwitch::prepareUninterruptibleMatch(
OBSWeakSource currentScene, int &linger)
{
int dur = delay * 1000;
if (dur > 0) {
switcher->waitScene = obs_weak_source_get_source(currentScene);
obs_source_release(switcher->waitScene);
linger = dur;
}
}
void SceneSequenceSwitch::advanceActiveSequence()
{
// Set start Scene
OBSWeakSource currentSceneGroupScene = nullptr;
if (targetType == SwitchTargetType::SceneGroup && group) {
currentSceneGroupScene = group->getCurrentScene();
}
if (activeSequence) {
activeSequence = activeSequence->extendedSequence.get();
} else {
activeSequence = extendedSequence.get();
}
if (activeSequence) {
if (activeSequence->startTargetType ==
SwitchTargetType::SceneGroup) {
activeSequence->startScene = currentSceneGroupScene;
}
if (activeSequence->targetType == SwitchTargetType::Scene &&
!activeSequence->scene) {
blog(LOG_WARNING,
"cannot advance sequence - null scene set");
activeSequence = nullptr;
return;
}
if (activeSequence->targetType ==
SwitchTargetType::SceneGroup &&
activeSequence->group &&
activeSequence->group->scenes.empty()) {
blog(LOG_WARNING,
"cannot advance sequence - no scenes specified in '%s'",
activeSequence->group->name.c_str());
activeSequence = nullptr;
return;
}
// Reinit old matchCount value in case it was previously set
activeSequence->matchCount = 0;
}
}
void SceneSequenceSwitch::logAdvanceSequence()
{
if (activeSequence) {
std::string targetName =
GetWeakSourceName(activeSequence->scene);
if (activeSequence->targetType ==
SwitchTargetType::SceneGroup &&
activeSequence->group) {
targetName = activeSequence->group->name;
}
blog(LOG_INFO, "continuing sequence with '%s' -> '%s'",
GetWeakSourceName(activeSequence->startScene).c_str(),
targetName.c_str());
}
}
void SceneSequenceSwitch::logSequenceCanceled()
{
blog(LOG_INFO, "unexpected scene change - cancel sequence");
} }
void populateDelayUnits(QComboBox *list) void populateDelayUnits(QComboBox *list)
@@ -369,14 +580,82 @@ void populateDelayUnits(QComboBox *list)
list->addItem(obs_module_text("AdvSceneSwitcher.unit.hours")); list->addItem(obs_module_text("AdvSceneSwitcher.unit.hours"));
} }
SequenceWidget::SequenceWidget(QWidget *parent, SceneSequenceSwitch *s) QString makeExtendText(SceneSequenceSwitch *s, int curLen = 0)
: SwitchWidget(parent, s, true, true)
{ {
if (!s) {
return "";
}
QString ext = "";
ext = QString::number(s->delay / s->delayMultiplier) + " ";
switch (s->delayMultiplier) {
case 1:
ext += obs_module_text("AdvSceneSwitcher.unit.secends");
break;
case 60:
ext += obs_module_text("AdvSceneSwitcher.unit.minutes");
break;
case 60 * 60:
ext += obs_module_text("AdvSceneSwitcher.unit.hours");
break;
default:
ext += obs_module_text("?????");
break;
}
QString sceneName = GetWeakSourceName(s->scene).c_str();
if (s->targetType == SwitchTargetType::SceneGroup && s->group) {
sceneName = QString::fromStdString(s->group->name);
}
if (sceneName.isEmpty()) {
sceneName = obs_module_text("AdvSceneSwitcher.selectScene");
}
ext += " -> [" + sceneName + "]";
if (ext.length() + curLen > max_extend_text_size) {
return "...";
}
if (s->extendedSequence.get()) {
return ext +=
" | " + makeExtendText(s->extendedSequence.get(),
curLen + ext.length());
} else {
return ext;
}
}
SequenceWidget::SequenceWidget(QWidget *parent, SceneSequenceSwitch *s,
bool extendSequence, bool editExtendMode)
: SwitchWidget(parent, s, !extendSequence, true)
{
this->setParent(parent);
delay = new QDoubleSpinBox(); delay = new QDoubleSpinBox();
delayUnits = new QComboBox(); delayUnits = new QComboBox();
startScenes = new QComboBox(); startScenes = new QComboBox();
interruptible = new QCheckBox(obs_module_text( interruptible = new QCheckBox(obs_module_text(
"AdvSceneSwitcher.sceneSequenceTab.interruptible")); "AdvSceneSwitcher.sceneSequenceTab.interruptible"));
extendText = new QLabel();
extend = new QPushButton();
reduce = new QPushButton();
extend->setProperty("themeID",
QVariant(QStringLiteral("addIconSmall")));
reduce->setProperty("themeID",
QVariant(QStringLiteral("removeIconSmall")));
extend->setMaximumSize(22, 22);
reduce->setMaximumSize(22, 22);
// We need to extend the generic SwitchWidget::SceneChanged()
// with our own SequenceWidget::SceneChanged()
// so the old singal / slot needs to be disconnected
scenes->disconnect();
QWidget::connect(scenes, SIGNAL(currentTextChanged(const QString &)),
this, SLOT(SceneChanged(const QString &)));
QWidget::connect(delay, SIGNAL(valueChanged(double)), this, QWidget::connect(delay, SIGNAL(valueChanged(double)), this,
SLOT(DelayChanged(double))); SLOT(DelayChanged(double)));
@@ -388,6 +667,11 @@ SequenceWidget::SequenceWidget(QWidget *parent, SceneSequenceSwitch *s)
QWidget::connect(interruptible, SIGNAL(stateChanged(int)), this, QWidget::connect(interruptible, SIGNAL(stateChanged(int)), this,
SLOT(InterruptibleChanged(int))); SLOT(InterruptibleChanged(int)));
QWidget::connect(extend, SIGNAL(clicked()), this,
SLOT(ExtendClicked()));
QWidget::connect(reduce, SIGNAL(clicked()), this,
SLOT(ReduceClicked()));
delay->setMaximum(99999.000000); delay->setMaximum(99999.000000);
AdvSceneSwitcher::populateSceneSelection(startScenes, false); AdvSceneSwitcher::populateSceneSelection(startScenes, false);
populateDelayUnits(delayUnits); populateDelayUnits(delayUnits);
@@ -413,17 +697,62 @@ SequenceWidget::SequenceWidget(QWidget *parent, SceneSequenceSwitch *s)
interruptible->setChecked(s->interruptible); interruptible->setChecked(s->interruptible);
} }
QHBoxLayout *mainLayout = new QHBoxLayout; if (extendSequence) {
std::unordered_map<std::string, QWidget *> widgetPlaceholders = { QHBoxLayout *mainLayout = new QHBoxLayout;
{"{{startScenes}}", startScenes}, std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
{"{{scenes}}", scenes}, {"{{scenes}}", scenes},
{"{{delay}}", delay}, {"{{delay}}", delay},
{"{{delayUnits}}", delayUnits}, {"{{delayUnits}}", delayUnits},
{"{{transitions}}", transitions}, {"{{transitions}}", transitions}};
{"{{interruptible}}", interruptible}}; placeWidgets(
placeWidgets(obs_module_text("AdvSceneSwitcher.sceneSequenceTab.entry"), obs_module_text(
mainLayout, widgetPlaceholders); "AdvSceneSwitcher.sceneSequenceTab.extendEntry"),
setLayout(mainLayout); mainLayout, widgetPlaceholders);
setLayout(mainLayout);
} else {
QHBoxLayout *startSequence = new QHBoxLayout;
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
{"{{startScenes}}", startScenes},
{"{{scenes}}", scenes},
{"{{delay}}", delay},
{"{{delayUnits}}", delayUnits},
{"{{transitions}}", transitions},
{"{{interruptible}}", interruptible}};
placeWidgets(obs_module_text(
"AdvSceneSwitcher.sceneSequenceTab.entry"),
startSequence, widgetPlaceholders);
//exetend widgets placed here
extendSequenceLayout = new QVBoxLayout;
if (s) {
if (!editExtendMode) {
extendText->setText(makeExtendText(
s->extendedSequence.get()));
} else {
auto cur = s->extendedSequence.get();
while (cur != nullptr) {
extendSequenceLayout->addWidget(
new SequenceWidget(parent, cur,
true, true));
cur = cur->extendedSequence.get();
}
}
}
QHBoxLayout *extendSequenceControlsLayout = new QHBoxLayout;
if (editExtendMode) {
extendSequenceControlsLayout->addWidget(extend);
extendSequenceControlsLayout->addWidget(reduce);
}
extendSequenceControlsLayout->addStretch();
QVBoxLayout *mainLayout = new QVBoxLayout;
mainLayout->addLayout(startSequence);
mainLayout->addLayout(extendSequenceLayout);
mainLayout->addWidget(extendText);
mainLayout->addLayout(extendSequenceControlsLayout);
setLayout(mainLayout);
}
switchData = s; switchData = s;
@@ -493,6 +822,32 @@ void SequenceWidget::DelayUnitsChanged(int idx)
UpdateDelay(); UpdateDelay();
} }
void SequenceWidget::setExtendedSequenceStartScene()
{
switchData->extendedSequence->startScene = switchData->scene;
switchData->extendedSequence->startTargetType = SwitchTargetType::Scene;
if (switchData->targetType == SwitchTargetType::SceneGroup) {
switchData->extendedSequence->startScene = nullptr;
switchData->extendedSequence->startTargetType =
SwitchTargetType::SceneGroup;
}
}
void SequenceWidget::SceneChanged(const QString &text)
{
if (loading || !switchData) {
return;
}
SwitchWidget::SceneChanged(text);
std::lock_guard<std::mutex> lock(switcher->m);
if (switchData->extendedSequence) {
setExtendedSequenceStartScene();
}
}
void SequenceWidget::StartSceneChanged(const QString &text) void SequenceWidget::StartSceneChanged(const QString &text)
{ {
if (loading || !switchData) { if (loading || !switchData) {
@@ -511,4 +866,45 @@ void SequenceWidget::InterruptibleChanged(int state)
std::lock_guard<std::mutex> lock(switcher->m); std::lock_guard<std::mutex> lock(switcher->m);
switchData->interruptible = state; switchData->interruptible = state;
auto cur = switchData->extendedSequence.get();
while (cur != nullptr) {
cur->interruptible = state;
cur = cur->extendedSequence.get();
}
}
void SequenceWidget::UpdateExtendText()
{
extendText->setText(makeExtendText(switchData->extendedSequence.get()));
}
void SequenceWidget::ExtendClicked()
{
if (loading || !switchData) {
return;
}
std::lock_guard<std::mutex> lock(switcher->m);
auto es = switchData->extend();
SequenceWidget *ew = new SequenceWidget(this->parentWidget(), es, true);
extendSequenceLayout->addWidget(ew);
}
void SequenceWidget::ReduceClicked()
{
if (loading || !switchData) {
return;
}
std::lock_guard<std::mutex> lock(switcher->m);
switchData->reduce();
int count = extendSequenceLayout->count();
auto item = extendSequenceLayout->takeAt(count - 1);
if (item) {
item->widget()->setVisible(false);
delete item;
}
} }

View File

@@ -241,7 +241,7 @@ void TimeSwitch::load(obs_data_t *obj)
time = QTime::fromString(obs_data_get_string(obj, "time")); time = QTime::fromString(obs_data_get_string(obj, "time"));
} }
inline void populateTriggers(QComboBox *list) static inline void populateTriggers(QComboBox *list)
{ {
list->addItem(obs_module_text("AdvSceneSwitcher.timeTab.anyDay")); list->addItem(obs_module_text("AdvSceneSwitcher.timeTab.anyDay"));
list->addItem(obs_module_text("AdvSceneSwitcher.timeTab.mondays")); list->addItem(obs_module_text("AdvSceneSwitcher.timeTab.mondays"));

View File

@@ -1,3 +1,5 @@
#include <thread>
#include "headers/advanced-scene-switcher.hpp" #include "headers/advanced-scene-switcher.hpp"
#include "headers/utility.hpp" #include "headers/utility.hpp"
@@ -148,42 +150,28 @@ void AdvSceneSwitcher::on_defaultTransitionsDown_clicked()
switcher->defaultSceneTransitions[index + 1]); switcher->defaultSceneTransitions[index + 1]);
} }
void SwitcherData::checkDefaultSceneTransitions(bool &match, void SwitcherData::checkDefaultSceneTransitions()
OBSWeakSource &transition)
{ {
if (checkedDefTransition || DefaultSceneTransition::pause) { if (DefaultSceneTransition::pause || stop) {
return; return;
} }
obs_source_t *currentSource = obs_frontend_get_current_scene(); obs_source_t *currentSceneSource = obs_frontend_get_current_scene();
obs_weak_source_t *ws = obs_source_get_weak_source(currentSource); obs_weak_source_t *currentScene =
obs_source_get_weak_source(currentSceneSource);
for (DefaultSceneTransition &s : defaultSceneTransitions) {
if (s.scene == ws) {
if (!s.initialized()) {
continue;
}
match = true;
transition = s.transition;
for (auto &t : defaultSceneTransitions) {
if (t.checkMatch(currentScene)) {
if (verbose) { if (verbose) {
s.logMatch(); t.logMatch();
} }
t.setTransition();
break; break;
} }
} }
obs_source_release(currentSource);
obs_weak_source_release(ws);
checkedDefTransition = true; obs_weak_source_release(currentScene);
} obs_source_release(currentSceneSource);
void SwitcherData::setCurrentDefTransition(OBSWeakSource &transition)
{
obs_source_t *transitionSource = obs_weak_source_get_source(transition);
obs_frontend_set_current_transition(transitionSource);
obs_source_release(transitionSource);
} }
void AdvSceneSwitcher::on_transitionOverridecheckBox_stateChanged(int state) void AdvSceneSwitcher::on_transitionOverridecheckBox_stateChanged(int state)
@@ -439,7 +427,7 @@ bool SceneTransition::valid()
TransitionSwitchWidget::TransitionSwitchWidget(QWidget *parent, TransitionSwitchWidget::TransitionSwitchWidget(QWidget *parent,
SceneTransition *s) SceneTransition *s)
: SwitchWidget(parent, s, false) : SwitchWidget(parent, s, false, false, false)
{ {
scenes2 = new QComboBox(); scenes2 = new QComboBox();
@@ -498,7 +486,7 @@ void TransitionSwitchWidget::Scene2Changed(const QString &text)
DefTransitionSwitchWidget::DefTransitionSwitchWidget(QWidget *parent, DefTransitionSwitchWidget::DefTransitionSwitchWidget(QWidget *parent,
DefaultSceneTransition *s) DefaultSceneTransition *s)
: SwitchWidget(parent, s, false) : SwitchWidget(parent, s, false, false, false)
{ {
QHBoxLayout *mainLayout = new QHBoxLayout; QHBoxLayout *mainLayout = new QHBoxLayout;
std::unordered_map<std::string, QWidget *> widgetPlaceholders = { std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
@@ -533,3 +521,37 @@ void DefTransitionSwitchWidget::swapSwitchData(DefTransitionSwitchWidget *s1,
s1->setSwitchData(s2->getSwitchData()); s1->setSwitchData(s2->getSwitchData());
s2->setSwitchData(t); s2->setSwitchData(t);
} }
bool DefaultSceneTransition::checkMatch(OBSWeakSource currentScene)
{
return scene == currentScene;
}
void setTransitionDelayed(OBSWeakSource transition)
{
// A hardcoded delay of 50 ms before switching transition type is
// necessary due to OBS_FRONTEND_EVENT_SCENE_CHANGED seemingly firing a
// bit too early and thus leading to canceled transitions.
//
// The same is to be the case for OBS_FRONTEND_EVENT_TRANSITION_STOPPED.
//
// 50 ms was chosen as it seems to avoid the problem mentioned above and
// becuase that is the minimum value which can be chosen for the scene
// switcher's check interval.
// Thus it can be made sure that the delayed setting of the transition
// does not interfere with any new scene changes triggered by the scene
// switcher
std::this_thread::sleep_for(std::chrono::milliseconds(50));
obs_source_t *transitionSource = obs_weak_source_get_source(transition);
obs_frontend_set_current_transition(transitionSource);
obs_source_release(transitionSource);
}
void DefaultSceneTransition::setTransition()
{
std::thread t;
t = std::thread(setTransitionDelayed, transition);
t.detach();
}

View File

@@ -43,6 +43,16 @@ void SwitcherData::Prune()
sceneSequenceSwitches.erase( sceneSequenceSwitches.erase(
sceneSequenceSwitches.begin() + i--); sceneSequenceSwitches.begin() + i--);
} }
auto cur = &s;
while (cur != nullptr) {
if (cur->extendedSequence &&
!cur->extendedSequence->valid()) {
cur->extendedSequence.reset(nullptr);
s.activeSequence = nullptr;
}
cur = cur->extendedSequence.get();
}
} }
for (size_t i = 0; i < sceneTransitions.size(); i++) { for (size_t i = 0; i < sceneTransitions.size(); i++) {