diff --git a/CMakeLists.txt b/CMakeLists.txt
index 74d433cf..7c624b58 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -53,6 +53,7 @@ if(BUILD_OUT_OF_TREE)
src/general.cpp
src/pause-switch.cpp
src/random.cpp
+ src/time-switch.cpp
)
set(advanced-scene-switcher_UI
@@ -182,6 +183,7 @@ else ()
src/general.cpp
src/pause-switch.cpp
src/random.cpp
+ src/time-switch.cpp
)
set(advanced-scene-switcher_UI
diff --git a/forms/advanced-scene-switcher.ui b/forms/advanced-scene-switcher.ui
index 1564578c..12d12b74 100644
--- a/forms/advanced-scene-switcher.ui
+++ b/forms/advanced-scene-switcher.ui
@@ -2416,7 +2416,7 @@
ms
- 00
+ 0
1000000
@@ -2543,6 +2543,140 @@
+
+
+ Time
+
+
+ -
+
+
-
+
+
+ At
+
+
+
+ -
+
+
+ HH:mm:ss
+
+
+
+ -
+
+
+ switch to
+
+
+
+ -
+
+
+
+ 100
+ 0
+
+
+
+
+ -
+
+
+ using the
+
+
+
+ -
+
+
+ -
+
+
+ transition
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+
+
+ -
+
+
+ true
+
+
+ true
+
+
+
+ -
+
+
+ 4
+
+
-
+
+
+
+ 22
+ 22
+
+
+
+ true
+
+
+ addIconSmall
+
+
+
+ -
+
+
+
+ 22
+ 22
+
+
+
+ true
+
+
+ removeIconSmall
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+
+
+
+
diff --git a/src/advanced-scene-switcher.cpp b/src/advanced-scene-switcher.cpp
index a4356ab0..70e5ef49 100644
--- a/src/advanced-scene-switcher.cpp
+++ b/src/advanced-scene-switcher.cpp
@@ -57,6 +57,7 @@ SceneSwitcher::SceneSwitcher(QWidget *parent)
ui->randomScenes->addItem(name);
ui->fileScenes->addItem(name);
ui->mediaScenes->addItem(name);
+ ui->timeScenes->addItem(name);
temp++;
}
@@ -91,6 +92,7 @@ SceneSwitcher::SceneSwitcher(QWidget *parent)
ui->sceneRoundTripScenes2->addItem(PREVIOUS_SCENE_NAME);
ui->idleScenes->addItem(PREVIOUS_SCENE_NAME);
ui->mediaScenes->addItem(PREVIOUS_SCENE_NAME);
+ ui->timeScenes->addItem(PREVIOUS_SCENE_NAME);
obs_frontend_source_list *transitions = new obs_frontend_source_list();
obs_frontend_get_transitions(transitions);
@@ -108,6 +110,7 @@ SceneSwitcher::SceneSwitcher(QWidget *parent)
ui->randomTransitions->addItem(name);
ui->fileTransitions->addItem(name);
ui->mediaTransitions->addItem(name);
+ ui->timeTransitions->addItem(name);
}
obs_frontend_source_list_free(transitions);
@@ -305,6 +308,19 @@ SceneSwitcher::SceneSwitcher(QWidget *parent)
item->setData(Qt::UserRole, listText);
}
+ for (auto &s : switcher->timeSwitches) {
+ string sceneName = (s.usePreviousScene)
+ ? PREVIOUS_SCENE_NAME
+ : GetWeakSourceName(s.scene);
+ string transitionName = GetWeakSourceName(s.transition);
+ QString listText = MakeTimeSwitchName(
+ sceneName.c_str(), transitionName.c_str(), s.time);
+
+ QListWidgetItem *item =
+ new QListWidgetItem(listText, ui->timeSwitches);
+ item->setData(Qt::UserRole, listText);
+ }
+
ui->idleCheckBox->setChecked(switcher->idleData.idleEnable);
ui->idleScenes->setCurrentText(
switcher->idleData.usePreviousScene
@@ -375,6 +391,9 @@ SceneSwitcher::SceneSwitcher(QWidget *parent)
case MEDIA_FUNC:
s = "Media";
break;
+ case TIME_FUNC:
+ s = "Time";
+ break;
}
QString text(s.c_str());
QListWidgetItem *item =
@@ -407,6 +426,7 @@ static void SaveSceneSwitcher(obs_data_t *save_data, bool saving, void *)
obs_data_array_t *randomArray = obs_data_array_create();
obs_data_array_t *fileArray = obs_data_array_create();
obs_data_array_t *mediaArray = obs_data_array_create();
+ obs_data_array_t *timeArray = obs_data_array_create();
switcher->Prune();
@@ -755,6 +775,35 @@ static void SaveSceneSwitcher(obs_data_t *save_data, bool saving, void *)
obs_data_release(array_obj);
}
+ for (TimeSwitch &s : switcher->timeSwitches) {
+ obs_data_t *array_obj = obs_data_create();
+
+ obs_source_t *sceneSource =
+ obs_weak_source_get_source(s.scene);
+ obs_source_t *transition =
+ obs_weak_source_get_source(s.transition);
+ if ((s.usePreviousScene || sceneSource) && transition) {
+ const char *sceneName =
+ obs_source_get_name(sceneSource);
+ const char *transitionName =
+ obs_source_get_name(transition);
+ obs_data_set_string(
+ array_obj, "scene",
+ s.usePreviousScene ? PREVIOUS_SCENE_NAME
+ : sceneName);
+ obs_data_set_string(array_obj, "transition",
+ transitionName);
+ obs_data_set_string(
+ array_obj, "time",
+ s.time.toString().toStdString().c_str());
+ obs_data_array_push_back(timeArray, array_obj);
+ }
+ obs_source_release(sceneSource);
+ obs_source_release(transition);
+
+ obs_data_release(array_obj);
+ }
+
string nonMatchingSceneName =
GetWeakSourceName(switcher->nonMatchingScene);
@@ -781,6 +830,7 @@ static void SaveSceneSwitcher(obs_data_t *save_data, bool saving, void *)
obs_data_set_array(obj, "randomSwitches", randomArray);
obs_data_set_array(obj, "fileSwitches", fileArray);
obs_data_set_array(obj, "mediaSwitches", mediaArray);
+ obs_data_set_array(obj, "timeSwitches", timeArray);
string autoStopSceneName =
GetWeakSourceName(switcher->autoStopScene);
@@ -826,6 +876,8 @@ static void SaveSceneSwitcher(obs_data_t *save_data, bool saving, void *)
switcher->functionNamesByPriority[5]);
obs_data_set_int(obj, "priority6",
switcher->functionNamesByPriority[6]);
+ obs_data_set_int(obj, "priority7",
+ switcher->functionNamesByPriority[7]);
obs_data_set_obj(save_data, "advanced-scene-switcher", obj);
@@ -842,6 +894,7 @@ static void SaveSceneSwitcher(obs_data_t *save_data, bool saving, void *)
obs_data_array_release(randomArray);
obs_data_array_release(fileArray);
obs_data_array_release(mediaArray);
+ obs_data_array_release(timeArray);
obs_data_release(obj);
} else {
@@ -874,6 +927,8 @@ static void SaveSceneSwitcher(obs_data_t *save_data, bool saving, void *)
obs_data_get_array(obj, "fileSwitches");
obs_data_array_t *mediaArray =
obs_data_get_array(obj, "mediaSwitches");
+ obs_data_array_t *timeArray =
+ obs_data_get_array(obj, "timeSwitches");
if (!obj)
obj = obs_data_create();
@@ -1187,6 +1242,36 @@ static void SaveSceneSwitcher(obs_data_t *save_data, bool saving, void *)
GetWeakTransitionByName(transition), state,
restriction, time,
(strcmp(scene, PREVIOUS_SCENE_NAME) == 0));
+
+ obs_data_release(array_obj);
+ }
+
+ switcher->timeSwitches.clear();
+ count = obs_data_array_count(timeArray);
+
+ for (size_t i = 0; i < count; i++) {
+ obs_data_t *array_obj =
+ obs_data_array_item(timeArray, i);
+
+ const char *scene =
+ obs_data_get_string(array_obj, "scene");
+ const char *transition =
+ obs_data_get_string(array_obj, "transition");
+ QTime time = QTime::fromString(
+ obs_data_get_string(array_obj, "time"));
+
+ string timeSwitchStr =
+ MakeTimeSwitchName(scene, transition, time)
+ .toUtf8()
+ .constData();
+
+ switcher->timeSwitches.emplace_back(
+ GetWeakSourceByName(scene),
+ GetWeakTransitionByName(transition), time,
+ (strcmp(scene, PREVIOUS_SCENE_NAME) == 0),
+ timeSwitchStr);
+
+ obs_data_release(array_obj);
}
string autoStopScene =
@@ -1230,6 +1315,7 @@ static void SaveSceneSwitcher(obs_data_t *save_data, bool saving, void *)
obs_data_set_default_int(obj, "priority4", DEFAULT_PRIORITY_4);
obs_data_set_default_int(obj, "priority5", DEFAULT_PRIORITY_5);
obs_data_set_default_int(obj, "priority6", DEFAULT_PRIORITY_6);
+ obs_data_set_default_int(obj, "priority7", DEFAULT_PRIORITY_7);
switcher->functionNamesByPriority[0] =
(obs_data_get_int(obj, "priority0"));
@@ -1245,6 +1331,8 @@ static void SaveSceneSwitcher(obs_data_t *save_data, bool saving, void *)
(obs_data_get_int(obj, "priority5"));
switcher->functionNamesByPriority[6] =
(obs_data_get_int(obj, "priority6"));
+ switcher->functionNamesByPriority[6] =
+ (obs_data_get_int(obj, "priority7"));
if (!switcher->prioFuncsValid()) {
switcher->functionNamesByPriority[0] =
(DEFAULT_PRIORITY_0);
@@ -1260,6 +1348,8 @@ static void SaveSceneSwitcher(obs_data_t *save_data, bool saving, void *)
(DEFAULT_PRIORITY_5);
switcher->functionNamesByPriority[6] =
(DEFAULT_PRIORITY_6);
+ switcher->functionNamesByPriority[7] =
+ (DEFAULT_PRIORITY_7);
}
obs_data_array_release(array);
@@ -1273,6 +1363,8 @@ static void SaveSceneSwitcher(obs_data_t *save_data, bool saving, void *)
obs_data_array_release(ignoreIdleWindowsArray);
obs_data_array_release(randomArray);
obs_data_array_release(fileArray);
+ obs_data_array_release(mediaArray);
+ obs_data_array_release(timeArray);
obs_data_release(obj);
@@ -1356,7 +1448,11 @@ void SwitcherData::Thread()
case MEDIA_FUNC:
checkMediaSwitch(match, scene, transition);
break;
+ case TIME_FUNC:
+ checkTimeSwitch(match, scene, transition);
+ break;
}
+
if (switcher->stop) {
goto endLoop;
}
diff --git a/src/headers/advanced-scene-switcher.hpp b/src/headers/advanced-scene-switcher.hpp
index 88317c64..179130d0 100644
--- a/src/headers/advanced-scene-switcher.hpp
+++ b/src/headers/advanced-scene-switcher.hpp
@@ -42,6 +42,7 @@ public:
int executableFindByData(const QString &exe);
int IgnoreIdleWindowsFindByData(const QString &window);
int randomFindByData(const QString &scene);
+ int timeFindByData(const QString &timeStr);
void UpdateNonMatchingScene(const QString &name);
void UpdateAutoStopScene(const QString &name);
@@ -122,6 +123,10 @@ public slots:
void on_mediaAdd_clicked();
void on_mediaRemove_clicked();
+ void on_timeSwitches_currentRowChanged(int idx);
+ void on_timeAdd_clicked();
+ void on_timeRemove_clicked();
+
void on_priorityUp_clicked();
void on_priorityDown_clicked();
diff --git a/src/headers/switcher-data-structs.hpp b/src/headers/switcher-data-structs.hpp
index 6b88e49c..2aae300b 100644
--- a/src/headers/switcher-data-structs.hpp
+++ b/src/headers/switcher-data-structs.hpp
@@ -23,6 +23,7 @@
#define SCREEN_REGION_FUNC 4
#define WINDOW_TITLE_FUNC 5
#define MEDIA_FUNC 6
+#define TIME_FUNC 7
#define DEFAULT_PRIORITY_0 READ_FILE_FUNC
#define DEFAULT_PRIORITY_1 ROUND_TRIP_FUNC
@@ -31,6 +32,7 @@
#define DEFAULT_PRIORITY_4 SCREEN_REGION_FUNC
#define DEFAULT_PRIORITY_5 WINDOW_TITLE_FUNC
#define DEFAULT_PRIORITY_6 MEDIA_FUNC
+#define DEFAULT_PRIORITY_7 TIME_FUNC
using namespace std;
@@ -225,6 +227,26 @@ struct MediaSwitch {
}
};
+struct TimeSwitch {
+ OBSWeakSource scene;
+ OBSWeakSource transition;
+ QTime time;
+ bool matched;
+ bool usePreviousScene;
+ string timeSwitchStr;
+
+ inline TimeSwitch(OBSWeakSource scene_, OBSWeakSource transition_,
+ QTime time_, bool usePreviousScene_,
+ string timeSwitchStr_)
+ : scene(scene_),
+ transition(transition_),
+ time(time_),
+ usePreviousScene(usePreviousScene_),
+ timeSwitchStr(timeSwitchStr_)
+ {
+ }
+};
+
typedef enum { NO_SWITCH = 0, SWITCH = 1, RANDOM_SWITCH = 2 } NoMatch;
/********************************************************************************
@@ -279,10 +301,12 @@ struct SwitcherData {
vector mediaSwitches;
+ vector timeSwitches;
+
vector functionNamesByPriority = vector{
DEFAULT_PRIORITY_0, DEFAULT_PRIORITY_1, DEFAULT_PRIORITY_2,
DEFAULT_PRIORITY_3, DEFAULT_PRIORITY_4, DEFAULT_PRIORITY_5,
- DEFAULT_PRIORITY_6};
+ DEFAULT_PRIORITY_6, DEFAULT_PRIORITY_7};
void Thread();
void Start();
@@ -313,6 +337,8 @@ struct SwitcherData {
OBSWeakSource &transition, int &delay);
void checkMediaSwitch(bool &match, OBSWeakSource &scene,
OBSWeakSource &transition);
+ void checkTimeSwitch(bool &match, OBSWeakSource &scene,
+ OBSWeakSource &transition);
void Prune()
{
@@ -399,6 +425,13 @@ struct SwitcherData {
fileSwitches.erase(fileSwitches.begin() + i--);
}
+ for (size_t i = 0; i < timeSwitches.size(); i++) {
+ TimeSwitch &s = timeSwitches[i];
+ if (!WeakSourceValid(s.scene) ||
+ !WeakSourceValid(s.transition))
+ timeSwitches.erase(timeSwitches.begin() + i--);
+ }
+
if (!idleData.usePreviousScene &&
!WeakSourceValid(idleData.scene) ||
!WeakSourceValid(idleData.transition)) {
diff --git a/src/headers/utility.hpp b/src/headers/utility.hpp
index f1cca4f9..dc98a350 100644
--- a/src/headers/utility.hpp
+++ b/src/headers/utility.hpp
@@ -5,9 +5,9 @@
#include "switcher-data-structs.hpp"
using namespace std;
-static inline bool WeakSourceValid(obs_weak_source_t* ws)
+static inline bool WeakSourceValid(obs_weak_source_t *ws)
{
- obs_source_t* source = obs_weak_source_get_source(ws);
+ obs_source_t *source = obs_weak_source_get_source(ws);
if (source)
obs_source_release(source);
return !!source;
@@ -107,7 +107,7 @@ static inline QString MakeFileSwitchName(const QString &scene,
return switchName;
}
-typedef enum {
+typedef enum {
TIME_RESTRICTION_NONE,
TIME_RESTRICTION_SHORTER,
TIME_RESTRICTION_LONGER,
@@ -159,6 +159,15 @@ MakeMediaSwitchName(const QString &source, const QString &scene,
return switchName;
}
+static inline QString MakeTimeSwitchName(const QString &scene,
+ const QString &transition, QTime &time)
+{
+ QString switchName = QStringLiteral("At ") + time.toString() +
+ QStringLiteral(" switch to ") + scene +
+ QStringLiteral(" using ") + transition;
+ return switchName;
+}
+
static inline string GetWeakSourceName(obs_weak_source_t *weak_source)
{
string name;
diff --git a/src/priority.cpp b/src/priority.cpp
index caaf99bd..22019b9c 100644
--- a/src/priority.cpp
+++ b/src/priority.cpp
@@ -36,7 +36,7 @@ bool SwitcherData::prioFuncsValid()
for (int p : functionNamesByPriority)
{
- if (p < 0 || p > 6)
+ if (p < 0 || p > 7)
return false;
}
return true;
diff --git a/src/time-switch.cpp b/src/time-switch.cpp
new file mode 100644
index 00000000..e45cd98f
--- /dev/null
+++ b/src/time-switch.cpp
@@ -0,0 +1,147 @@
+#include "headers/advanced-scene-switcher.hpp"
+
+void SceneSwitcher::on_timeSwitches_currentRowChanged(int idx)
+{
+ if (loading)
+ return;
+ if (idx == -1)
+ return;
+
+ QListWidgetItem *item = ui->timeSwitches->item(idx);
+
+ QString timeScenestr = item->data(Qt::UserRole).toString();
+
+ lock_guard lock(switcher->m);
+ for (auto &s : switcher->timeSwitches) {
+ if (timeScenestr.compare(s.timeSwitchStr.c_str()) == 0) {
+ QString sceneName = GetWeakSourceName(s.scene).c_str();
+ QString transitionName =
+ GetWeakSourceName(s.transition).c_str();
+ ui->timeScenes->setCurrentText(sceneName);
+ ui->timeEdit->setTime(s.time);
+ ui->timeTransitions->setCurrentText(transitionName);
+ break;
+ }
+ }
+}
+
+int SceneSwitcher::timeFindByData(const QString &timeStr)
+{
+ QRegExp rx("At " + timeStr + " switch to .*");
+ int count = ui->timeSwitches->count();
+
+ for (int i = 0; i < count; i++) {
+ QListWidgetItem *item = ui->timeSwitches->item(i);
+ QString str = item->data(Qt::UserRole).toString();
+
+ if (rx.exactMatch(str))
+ return i;
+ }
+
+ return -1;
+}
+
+void SceneSwitcher::on_timeAdd_clicked()
+{
+ QString sceneName = ui->timeScenes->currentText();
+ QString transitionName = ui->timeTransitions->currentText();
+ QTime time = ui->timeEdit->time();
+
+ if (sceneName.isEmpty())
+ return;
+
+ OBSWeakSource source = GetWeakSourceByQString(sceneName);
+ OBSWeakSource transition = GetWeakTransitionByQString(transitionName);
+
+ QString text = MakeTimeSwitchName(sceneName, transitionName, time);
+ QVariant v = QVariant::fromValue(text);
+
+ int idx = timeFindByData(time.toString());
+
+ if (idx == -1) {
+ lock_guard lock(switcher->m);
+ switcher->timeSwitches.emplace_back(
+ source, transition, time,
+ (sceneName == QString(PREVIOUS_SCENE_NAME)),
+ text.toUtf8().constData());
+
+ QListWidgetItem *item =
+ new QListWidgetItem(text, ui->timeSwitches);
+ item->setData(Qt::UserRole, v);
+ } else {
+ QListWidgetItem *item = ui->timeSwitches->item(idx);
+ item->setText(text);
+
+ {
+ lock_guard lock(switcher->m);
+ for (auto &s : switcher->timeSwitches) {
+ if (s.time == time) {
+ s.scene = source;
+ s.transition = transition;
+ s.usePreviousScene =
+ (sceneName ==
+ QString(PREVIOUS_SCENE_NAME));
+ s.timeSwitchStr =
+ text.toUtf8().constData();
+ ;
+ break;
+ }
+ }
+ }
+
+ ui->timeSwitches->sortItems();
+ }
+}
+
+void SceneSwitcher::on_timeRemove_clicked()
+{
+ QListWidgetItem *item = ui->timeSwitches->currentItem();
+ if (!item)
+ return;
+
+ string text = item->data(Qt::UserRole).toString().toUtf8().constData();
+
+ {
+ lock_guard lock(switcher->m);
+ auto &switches = switcher->timeSwitches;
+
+ for (auto it = switches.begin(); it != switches.end(); ++it) {
+ auto &s = *it;
+
+ if (s.timeSwitchStr == text) {
+ switches.erase(it);
+ break;
+ }
+ }
+ }
+
+ delete item;
+}
+
+void SwitcherData::checkTimeSwitch(bool &match, OBSWeakSource &scene,
+ OBSWeakSource &transition)
+{
+ if (timeSwitches.size() == 0)
+ return;
+
+ QTime now = QTime::currentTime();
+
+ for (TimeSwitch &s : timeSwitches) {
+
+ QTime validSwitchTimeWindow = s.time.addMSecs(interval);
+
+ match = s.time <= now && now <= validSwitchTimeWindow;
+ if (!match &&
+ validSwitchTimeWindow.msecsSinceStartOfDay() < interval) {
+ // check for overflow
+ match = now >= s.time || now <= validSwitchTimeWindow;
+ }
+
+ if (match) {
+ scene = (s.usePreviousScene) ? previousScene : s.scene;
+ transition = s.transition;
+ match = true;
+ break;
+ }
+ }
+}