diff --git a/CMakeLists.txt b/CMakeLists.txt index 6753351e..17386077 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -32,6 +32,7 @@ set(advanced-scene-switcher_SOURCES hotkey.cpp general.cpp pause-switch.cpp + random.cpp ) set(advanced-scene-switcher_UI ${advanced-scene-switcher_UI} diff --git a/advanced-scene-switcher.cpp b/advanced-scene-switcher.cpp index 2dcc8d71..c1286dd8 100644 --- a/advanced-scene-switcher.cpp +++ b/advanced-scene-switcher.cpp @@ -54,6 +54,7 @@ SceneSwitcher::SceneSwitcher(QWidget* parent) ui->defaultTransitionsScene->addItem(name); ui->executableScenes->addItem(name); ui->idleScenes->addItem(name); + ui->randomScenes->addItem(name); temp++; } @@ -70,17 +71,28 @@ SceneSwitcher::SceneSwitcher(QWidget* parent) ui->defaultTransitionsTransitions->addItem(name); ui->executableTransitions->addItem(name); ui->idleTransitions->addItem(name); + ui->randomTransitions->addItem(name); } obs_frontend_source_list_free(transitions); - if (switcher->switchIfNotMatching) + if (switcher->switchIfNotMatching == SWITCH) + { ui->noMatchSwitch->setChecked(true); - else + ui->noMatchSwitchScene->setEnabled(true); + } + else if (switcher->switchIfNotMatching == NO_SWITCH) + { ui->noMatchDontSwitch->setChecked(true); - + ui->noMatchSwitchScene->setEnabled(false); + } + else + { + ui->noMatchRandomSwitch->setChecked(true); + ui->noMatchSwitchScene->setEnabled(false); + } ui->noMatchSwitchScene->setCurrentText(GetWeakSourceName(switcher->nonMatchingScene).c_str()); ui->checkInterval->setValue(switcher->interval); @@ -219,6 +231,17 @@ SceneSwitcher::SceneSwitcher(QWidget* parent) item->setData(Qt::UserRole, text); } + for (auto& s : switcher->randomSwitches) + { + string sceneName = GetWeakSourceName(s.scene); + string transitionName = GetWeakSourceName(s.transition); + QString text = MakeRandomSwitchName( + sceneName.c_str(), transitionName.c_str(), s.delay); + + QListWidgetItem* item = new QListWidgetItem(text, ui->randomScenesList); + item->setData(Qt::UserRole, text); + } + ui->idleCheckBox->setChecked(switcher->idleData.idleEnable); ui->idleScenes->setCurrentText(GetWeakSourceName(switcher->idleData.scene).c_str()); ui->idleTransitions->setCurrentText(GetWeakSourceName(switcher->idleData.transition).c_str()); @@ -290,6 +313,8 @@ static void SaveSceneSwitcher(obs_data_t* save_data, bool saving, void*) obs_data_array_t* sceneTransitionsArray = obs_data_array_create(); obs_data_array_t* defaultTransitionsArray = obs_data_array_create(); obs_data_array_t* ignoreIdleWindowsArray = obs_data_array_create(); + obs_data_array_t* executableArray = obs_data_array_create(); + obs_data_array_t* randomArray = obs_data_array_create(); switcher->Prune(); @@ -445,8 +470,6 @@ static void SaveSceneSwitcher(obs_data_t* save_data, bool saving, void*) obs_data_release(array_obj); } - obs_data_array_t* executableArray = obs_data_array_create(); - for (ExecutableSceneSwitch& s : switcher->executableSwitches) { obs_data_t* array_obj = obs_data_create(); @@ -470,6 +493,29 @@ static void SaveSceneSwitcher(obs_data_t* save_data, bool saving, void*) obs_data_release(array_obj); } + for (RandomSwitch& s : switcher->randomSwitches) + { + obs_data_t* array_obj = obs_data_create(); + + obs_source_t* source = obs_weak_source_get_source(s.scene); + obs_source_t* transition = obs_weak_source_get_source(s.transition); + + if (source && transition) + { + const char* sceneName = obs_source_get_name(source); + const char* transitionName = obs_source_get_name(transition); + obs_data_set_string(array_obj, "scene", sceneName); + obs_data_set_string(array_obj, "transition", transitionName); + obs_data_set_double(array_obj, "delay", s.delay); + obs_data_set_string(array_obj, "str", s.randomSwitchStr.c_str()); + obs_data_array_push_back(randomArray, array_obj); + obs_source_release(source); + obs_source_release(transition); + } + + obs_data_release(array_obj); + } + for (string& window : switcher->ignoreIdleWindows) { obs_data_t* array_obj = obs_data_create(); @@ -482,7 +528,7 @@ static void SaveSceneSwitcher(obs_data_t* save_data, bool saving, void*) obs_data_set_int(obj, "interval", switcher->interval); obs_data_set_string(obj, "non_matching_scene", nonMatchingSceneName.c_str()); - obs_data_set_bool(obj, "switch_if_not_matching", switcher->switchIfNotMatching); + obs_data_set_int(obj, "switch_if_not_matching", switcher->switchIfNotMatching); obs_data_set_bool(obj, "active", !switcher->stop); obs_data_set_array(obj, "switches", array); @@ -495,6 +541,7 @@ static void SaveSceneSwitcher(obs_data_t* save_data, bool saving, void*) obs_data_set_array(obj, "defaultTransitions", defaultTransitionsArray); obs_data_set_array(obj, "executableSwitches", executableArray); obs_data_set_array(obj, "ignoreIdleWindows", ignoreIdleWindowsArray); + obs_data_set_array(obj, "randomSwitches", randomArray); string autoStopSceneName = GetWeakSourceName(switcher->autoStopScene); @@ -532,6 +579,7 @@ static void SaveSceneSwitcher(obs_data_t* save_data, bool saving, void*) obs_data_array_release(defaultTransitionsArray); obs_data_array_release(executableArray); obs_data_array_release(ignoreIdleWindowsArray); + obs_data_array_release(randomArray); obs_data_release(obj); } @@ -550,6 +598,7 @@ static void SaveSceneSwitcher(obs_data_t* save_data, bool saving, void*) obs_data_array_t* defaultTransitionsArray = obs_data_get_array(obj, "defaultTransitions"); obs_data_array_t* executableArray = obs_data_get_array(obj, "executableSwitches"); obs_data_array_t* ignoreIdleWindowsArray = obs_data_get_array(obj, "ignoreIdleWindows"); + obs_data_array_t* randomArray = obs_data_get_array(obj, "randomSwitches"); if (!obj) obj = obs_data_create(); @@ -557,7 +606,7 @@ static void SaveSceneSwitcher(obs_data_t* save_data, bool saving, void*) obs_data_set_default_int(obj, "interval", DEFAULT_INTERVAL); switcher->interval = obs_data_get_int(obj, "interval"); - switcher->switchIfNotMatching = obs_data_get_bool(obj, "switch_if_not_matching"); + switcher->switchIfNotMatching = (NoMatch)obs_data_get_int(obj, "switch_if_not_matching"); string nonMatchingScene = obs_data_get_string(obj, "non_matching_scene"); bool active = obs_data_get_bool(obj, "active"); @@ -737,6 +786,23 @@ static void SaveSceneSwitcher(obs_data_t* save_data, bool saving, void*) obs_data_release(array_obj); } + count = obs_data_array_count(randomArray); + + for (size_t i = 0; i < count; i++) + { + obs_data_t* array_obj = obs_data_array_item(randomArray, i); + + const char* scene = obs_data_get_string(array_obj, "scene"); + const char* transition = obs_data_get_string(array_obj, "transition"); + double delay = obs_data_get_double(array_obj, "delay"); + string str = obs_data_get_string(array_obj, "str"); + + switcher->randomSwitches.emplace_back( + GetWeakSourceByName(scene), GetWeakTransitionByName(transition), delay, str); + + obs_data_release(array_obj); + } + string autoStopScene = obs_data_get_string(obj, "autoStopSceneName"); switcher->autoStopEnable = obs_data_get_bool(obj, "autoStopEnable"); switcher->autoStopScene = GetWeakSourceByName(autoStopScene.c_str()); @@ -785,6 +851,7 @@ static void SaveSceneSwitcher(obs_data_t* save_data, bool saving, void*) obs_data_array_release(defaultTransitionsArray); obs_data_array_release(executableArray); obs_data_array_release(ignoreIdleWindowsArray); + obs_data_array_release(randomArray); obs_data_release(obj); @@ -854,6 +921,8 @@ void SwitcherData::Thread() //to avoid scene duplication when rapidly switching scene collection this_thread::sleep_for(chrono::seconds(2)); + int extraSleep = 0; + while (true) { startLoop: @@ -861,7 +930,12 @@ void SwitcherData::Thread() bool match = false; OBSWeakSource scene; OBSWeakSource transition; - chrono::milliseconds duration(interval); + chrono::milliseconds duration; + if (extraSleep > interval) + duration = chrono::milliseconds(extraSleep); + else + duration = chrono::milliseconds(interval); + extraSleep = 0; switcher->Prune(); //sleep for a bit cv.wait_for(lock, duration); @@ -919,12 +993,16 @@ void SwitcherData::Thread() } } - if (!match && switchIfNotMatching && nonMatchingScene) + if (!match && switchIfNotMatching == SWITCH && nonMatchingScene) { match = true; scene = nonMatchingScene; transition = nullptr; } + if (!match && switchIfNotMatching == RANDOM_SWITCH) + { + checkRandom(match, scene, transition, extraSleep); + } if (match) { switchScene(scene, transition); diff --git a/advanced-scene-switcher.hpp b/advanced-scene-switcher.hpp index d6959fb6..8d09d942 100644 --- a/advanced-scene-switcher.hpp +++ b/advanced-scene-switcher.hpp @@ -36,6 +36,7 @@ public: int DefaultTransitionsFindByData(const QString &scene); int executableFindByData(const QString &exe); int IgnoreIdleWindowsFindByData(const QString& window); + int randomFindByData(const QString& scene); void UpdateNonMatchingScene(const QString &name); void UpdateAutoStopScene(const QString &name); @@ -48,6 +49,7 @@ public slots: void on_remove_clicked(); void on_noMatchDontSwitch_clicked(); void on_noMatchSwitch_clicked(); + void on_noMatchRandomSwitch_clicked(); void on_startAtLaunch_toggled(bool value); void on_noMatchSwitchScene_currentTextChanged(const QString &text); void on_checkInterval_valueChanged(int value); @@ -103,6 +105,10 @@ public slots: void on_ignoreIdleAdd_clicked(); void on_ignoreIdleRemove_clicked(); + void on_randomAdd_clicked(); + void on_randomRemove_clicked(); + void on_randomScenesList_currentRowChanged(int idx); + void on_priorityUp_clicked(); void on_priorityDown_clicked(); diff --git a/forms/advanced-scene-switcher.ui b/forms/advanced-scene-switcher.ui index 9cc63926..6cb36495 100644 --- a/forms/advanced-scene-switcher.ui +++ b/forms/advanced-scene-switcher.ui @@ -7,7 +7,7 @@ 0 0 981 - 405 + 428 @@ -205,6 +205,13 @@ + + + + Switch to any Scene on Random Tab + + + @@ -1912,6 +1919,149 @@ + + + Random + + + + + + + + If no switch condition is met switch to + + + + + + + + 0 + 0 + + + + + 100 + 0 + + + + + + + + using the + + + + + + + + + + transition for a duration of + + + + + + + s + + + 999999999.990000009536743 + + + + + + + or any of the options below: + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + 0 + 0 + + + + true + + + + + + + + + + 22 + 22 + + + + true + + + addIconSmall + + + + + + + + 22 + 22 + + + + true + + + removeIconSmall + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Write To File / Read From File diff --git a/general.cpp b/general.cpp index 30cf3fca..37f3ff03 100644 --- a/general.cpp +++ b/general.cpp @@ -32,7 +32,8 @@ void SceneSwitcher::on_noMatchDontSwitch_clicked() return; lock_guard lock(switcher->m); - switcher->switchIfNotMatching = false; + switcher->switchIfNotMatching = NO_SWITCH; + ui->noMatchSwitchScene->setEnabled(false); } void SceneSwitcher::on_noMatchSwitch_clicked() @@ -41,10 +42,21 @@ void SceneSwitcher::on_noMatchSwitch_clicked() return; lock_guard lock(switcher->m); - switcher->switchIfNotMatching = true; + switcher->switchIfNotMatching = SWITCH; + ui->noMatchSwitchScene->setEnabled(true); UpdateNonMatchingScene(ui->noMatchSwitchScene->currentText()); } +void SceneSwitcher::on_noMatchRandomSwitch_clicked() +{ + if (loading) + return; + + lock_guard lock(switcher->m); + switcher->switchIfNotMatching = RANDOM_SWITCH; + ui->noMatchSwitchScene->setEnabled(false); +} + void SceneSwitcher::on_noMatchSwitchScene_currentTextChanged(const QString& text) { if (loading) diff --git a/random.cpp b/random.cpp new file mode 100644 index 00000000..650effe2 --- /dev/null +++ b/random.cpp @@ -0,0 +1,144 @@ +#include "advanced-scene-switcher.hpp" + +void SceneSwitcher::on_randomScenesList_currentRowChanged(int idx) +{ + if (loading) + return; + if (idx == -1) + return; + + QListWidgetItem* item = ui->randomScenesList->item(idx); + + QString randomSceneStr = item->data(Qt::UserRole).toString(); + + lock_guard lock(switcher->m); + for (auto& s : switcher->randomSwitches) + { + if (randomSceneStr.compare(s.randomSwitchStr.c_str()) == 0) + { + QString sceneName = GetWeakSourceName(s.scene).c_str(); + QString transitionName = GetWeakSourceName(s.transition).c_str(); + ui->randomScenes->setCurrentText(sceneName); + ui->randomSpinBox->setValue(s.delay); + ui->randomTransitions->setCurrentText(transitionName); + break; + } + } +} + +int SceneSwitcher::randomFindByData(const QString& randomStr) +{ + int count = ui->randomScenesList->count(); + + for (int i = 0; i < count; i++) + { + QListWidgetItem* item = ui->randomScenesList->item(i); + QString str = item->data(Qt::UserRole).toString(); + + if (str == randomStr) + return i; + } + + return -1; +} + +void SceneSwitcher::on_randomAdd_clicked() +{ + QString sceneName = ui->randomScenes->currentText(); + QString transitionName = ui->randomTransitions->currentText(); + double delay = ui->randomSpinBox->value(); + + + if (sceneName.isEmpty()) + return; + + OBSWeakSource source = GetWeakSourceByQString(sceneName); + OBSWeakSource transition = GetWeakTransitionByQString(transitionName); + + + QString text = MakeRandomSwitchName(sceneName, transitionName, delay); + QVariant v = QVariant::fromValue(text); + + int idx = randomFindByData(text); + + if (idx == -1) + { + lock_guard lock(switcher->m); + switcher->randomSwitches.emplace_back( + source, transition, delay, text.toUtf8().constData()); + + QListWidgetItem* item = new QListWidgetItem(text, ui->randomScenesList); + item->setData(Qt::UserRole, v); + } + else + { + QListWidgetItem* item = ui->randomScenesList->item(idx); + item->setText(text); + + { + lock_guard lock(switcher->m); + for (auto& s : switcher->randomSwitches) + { + if (s.scene == source) + { + s.delay = delay; + s.transition = transition; + s.randomSwitchStr = text.toUtf8().constData();; + break; + } + } + } + + ui->randomScenesList->sortItems(); + } +} + + +void SceneSwitcher::on_randomRemove_clicked() +{ + QListWidgetItem* item = ui->randomScenesList->currentItem(); + if (!item) + return; + + string text = item->data(Qt::UserRole).toString().toUtf8().constData(); + + { + lock_guard lock(switcher->m); + auto& switches = switcher->randomSwitches; + + for (auto it = switches.begin(); it != switches.end(); ++it) + { + auto& s = *it; + + if (s.randomSwitchStr == text) + { + switches.erase(it); + break; + } + } + } + + delete item; +} + + +void SwitcherData::checkRandom(bool& match, OBSWeakSource& scene, OBSWeakSource& transition, int& delay) +{ + int size = randomSwitches.size(); + if (randomSwitches.size() == 0) + return; + + vector rs = randomSwitches; + std::random_shuffle(rs.begin(), rs.end()); + for (RandomSwitch& r : rs) + { + if (r.scene == lastRandomScene) + continue; + scene = r.scene; + transition = r.transition; + delay = (int)r.delay * 1000; + match = true; + lastRandomScene = r.scene; + break; + } +} diff --git a/switcher-data-structs.hpp b/switcher-data-structs.hpp index 4ed1f037..41b15efc 100644 --- a/switcher-data-structs.hpp +++ b/switcher-data-structs.hpp @@ -100,6 +100,23 @@ struct SceneRoundTripSwitch } }; +struct RandomSwitch +{ + OBSWeakSource scene; + OBSWeakSource transition; + double delay; + string randomSwitchStr; + + inline RandomSwitch(OBSWeakSource scene_, OBSWeakSource transition_ + , double delay_, string str) + : scene(scene_) + , transition(transition_) + , delay(delay_) + , randomSwitchStr(str) + { + } +}; + struct SceneTransition { OBSWeakSource scene1; @@ -148,6 +165,11 @@ struct IdleData OBSWeakSource transition; }; +typedef enum { + NO_SWITCH = 0, + SWITCH = 1, + RANDOM_SWITCH = 2 +} NoMatch; @@ -164,14 +186,16 @@ struct SwitcherData vector switches; string lastTitle; OBSWeakSource nonMatchingScene; + OBSWeakSource lastRandomScene; int interval = DEFAULT_INTERVAL; - bool switchIfNotMatching = false; + NoMatch switchIfNotMatching; bool startAtLaunch = false; vector screenRegionSwitches; vector pauseScenesSwitches; vector pauseWindowsSwitches; vector ignoreWindowsSwitches; vector sceneRoundTripSwitches; + vector randomSwitches; bool autoStopEnable = false; OBSWeakSource autoStopScene; string waitSceneName; //indicates which scene was active when we startet waiting on something @@ -206,6 +230,7 @@ struct SwitcherData void checkExeSwitch(bool& match, OBSWeakSource& scene, OBSWeakSource& transition); void checkScreenRegionSwitch(bool& match, OBSWeakSource& scene, OBSWeakSource& transition); void checkSwitchInfoFromFile(bool& match, OBSWeakSource& scene, OBSWeakSource& transition); + void checkRandom(bool& match, OBSWeakSource& scene, OBSWeakSource& transition, int& delay); @@ -220,10 +245,17 @@ struct SwitcherData if (nonMatchingScene && !WeakSourceValid(nonMatchingScene)) { - switchIfNotMatching = false; + switchIfNotMatching = NO_SWITCH; nonMatchingScene = nullptr; } + for (size_t i = 0; i < randomSwitches.size(); i++) + { + RandomSwitch& s = randomSwitches[i]; + if (!WeakSourceValid(s.scene) || !WeakSourceValid(s.transition)) + randomSwitches.erase(randomSwitches.begin() + i--); + } + for (size_t i = 0; i < screenRegionSwitches.size(); i++) { ScreenRegionSwitch& s = screenRegionSwitches[i]; diff --git a/utility.hpp b/utility.hpp index 2fec2530..59e47f44 100644 --- a/utility.hpp +++ b/utility.hpp @@ -62,6 +62,13 @@ static inline QString MakeDefaultSceneTransitionName( return scene + QStringLiteral(" --> ") + transition; } +static inline QString MakeRandomSwitchName( + const QString& scene, const QString& transition, double& delay) +{ + return QStringLiteral("[") + scene + QStringLiteral(", ") + transition + QStringLiteral("]: ") + + QString::number(delay) + QStringLiteral(" seconds"); +} + static inline string GetWeakSourceName(obs_weak_source_t* weak_source) { string name;