Compare commits

...

69 Commits

Author SHA1 Message Date
WarmUpTill
10d3c8c268 add handling of window title of metro apps 2020-10-24 14:43:10 +02:00
WarmUpTill
bc3fcde12d remove unnecessary nullptr checks 2020-10-23 23:57:45 +02:00
WarmUpTill
8b9c93f76c workaround / fixes on media tab
fix memory leaks due to not clearing signal handlers
add workaround for VLC source states during playlists item change
2020-10-23 23:45:04 +02:00
WarmUpTill
89f0449fcd Merge branch 'master' into UI 2020-10-22 22:02:45 +02:00
WarmUpTill
46916dc5d3 workaround for MacOS CI issue (#52) 2020-10-22 21:56:54 +02:00
WarmUpTill
babe8b5c28 rename title tab and only show windows specific hints on windows 2020-10-22 21:25:59 +02:00
WarmUpTill
b1179f4e50 fix mem leaks which could occur with new UI 2020-10-22 21:00:41 +02:00
WarmUpTill
017664b8e0 Merge branch 'master' into UI 2020-10-22 20:02:13 +02:00
WarmUpTill
8aed15f3a8 allow WS_EX_TOOLWINDOW as valid windows to match 'Task Switching' 2020-10-22 19:44:23 +02:00
WarmUpTill
161588e43b allow WS_EX_TOOLWINDOW as valid window to match 'Task Switching' 2020-10-22 19:40:21 +02:00
WarmUpTill
3dd1138f31 separate idle from file data 2020-10-21 22:15:25 +02:00
WarmUpTill
54aaeed7d7 adjust media switch handling to support vlc source 2020-10-21 21:48:21 +02:00
WarmUpTill
7df6d59c06 more include cleanup 2020-10-20 21:22:24 +02:00
WarmUpTill
b05115a7bb clean up includes 2020-10-19 21:06:35 +02:00
WarmUpTill
58acdb8f79 set process selection editable so processes can be displayed / entered which are not currently running 2020-10-18 22:42:28 +02:00
WarmUpTill
5dd3cf1945 make destructor virtual just in case 2020-10-18 20:12:41 +02:00
WarmUpTill
793e5edf01 add media state 'any' 2020-10-18 20:05:10 +02:00
WarmUpTill
6b1a01de5e move transition warning label and change default transition only once per scene change 2020-10-18 19:21:52 +02:00
WarmUpTill
0b7faee84f fix match log only printing previous scene 2020-10-18 17:43:28 +02:00
WarmUpTill
fd3be07024 fix previous scene handling 2020-10-18 17:37:33 +02:00
WarmUpTill
986422ca83 rename main SceneSwitcher widget to AdvSceneSwitcher 2020-10-18 17:18:21 +02:00
WarmUpTill
6c5c861514 update transitions UI 2020-10-18 16:22:45 +02:00
WarmUpTill
02a8dd87ca adjust log 2020-10-18 16:22:09 +02:00
WarmUpTill
a1266f71f2 fix pulse on sequence and adjust log 2020-10-18 16:21:38 +02:00
WarmUpTill
c7ad2f6ec1 rename sceneSequences widget to sceneSequenceSwitches 2020-10-18 02:03:20 +02:00
WarmUpTill
51612d9e7f do not switch if entry is uninitialized for window, time, region, random 2020-10-18 01:52:19 +02:00
WarmUpTill
491108678e update UI of scene sequence tab 2020-10-18 01:51:07 +02:00
WarmUpTill
3ac795fc72 silence warning 2020-10-18 00:01:44 +02:00
WarmUpTill
96746b3507 update screen region UI and add helper frame 2020-10-17 23:58:08 +02:00
WarmUpTill
7a1104884a change pulse default specifizer to QLabel 2020-10-16 19:42:32 +02:00
WarmUpTill
c6e2c9dcc3 add tooltips to selection entries and do not allow scrolling to them 2020-10-15 21:17:23 +02:00
WarmUpTill
a33c0905c9 allow regex entry for window switch and limit window selection size 2020-10-15 21:16:34 +02:00
WarmUpTill
7f85cdb500 update UI of window tab 2020-10-15 20:59:59 +02:00
WarmUpTill
f643ec10fd add option to disable UI hints 2020-10-14 22:03:12 +02:00
WarmUpTill
abab9f70b5 update Random Tab UI 2020-10-14 21:46:35 +02:00
WarmUpTill
8ecc197805 remove previous scene entry on time tab and unnecessary populate calls 2020-10-14 21:45:42 +02:00
WarmUpTill
0fc6c60d49 add new UI for Time tab 2020-10-14 20:55:11 +02:00
WarmUpTill
255432f3b2 clean up unused signal 2020-10-14 20:18:39 +02:00
WarmUpTill
5c38996d41 add add pulse for media/exec/audio and move inactive pluse to general 2020-10-14 20:17:51 +02:00
WarmUpTill
4d76206452 add generic PulseWidget function to highlight widgets 2020-10-13 22:12:00 +02:00
WarmUpTill
7c561797af clean up setup of audio/exe/media and fix slight leak in media 2020-10-13 20:37:02 +02:00
WarmUpTill
26697faa9f Merge branch 'master' into UI 2020-10-12 21:48:30 +02:00
WarmUpTill
f22da7ff31 fix audio valid(), skip uninit exec switches, skip setting scene and transition for audio / exec as this is done in generic switch 2020-10-12 21:48:08 +02:00
WarmUpTill
5ad820d6cd add new UI for media tab 2020-10-12 21:46:16 +02:00
WarmUpTill
1e6a022787 improve hotkey handling (#49) 2020-10-12 20:55:51 +02:00
WarmUpTill
a0a272f49e fix include path and warning 2020-10-11 20:02:37 +02:00
WarmUpTill
5b315d0574 add generic switch widget and setup new UI for exec switching 2020-10-11 19:12:31 +02:00
WarmUpTill
bc3a7cd9be move functions 2020-10-11 15:50:00 +02:00
WarmUpTill
39067f6157 fix up / down audio 2020-10-11 15:18:55 +02:00
WarmUpTill
085225bdcc audio up / down draft (signals still not working) 2020-10-11 00:32:38 +02:00
WarmUpTill
e3dc2c5f26 Merge branch 'master' into UI 2020-10-10 01:58:21 +02:00
WarmUpTill
50545e7f83 audio delete 2020-10-10 01:57:59 +02:00
WarmUpTill
616ce0c737 even more logging 2020-10-10 01:57:38 +02:00
WarmUpTill
fb1eac5268 fix crash on exit when on autoStart scene (#47) 2020-10-10 01:56:08 +02:00
WarmUpTill
ece2d61e0a more logging 2020-10-10 01:50:10 +02:00
WarmUpTill
62d54f7206 slightly adjust logging 2020-10-10 01:43:43 +02:00
WarmUpTill
611c478eb3 adjust audio add to new UI 2020-10-10 00:18:29 +02:00
WarmUpTill
3ff7a848b1 add option to add 'select ...' entry to populate functions 2020-10-10 00:17:19 +02:00
WarmUpTill
36b4d15a17 add edit in place for audio 2020-10-06 21:51:05 +02:00
WarmUpTill
02e797a3db first draft of UI improvements
group settings of the general tab
highlight if the scene switcher is inactive
add draft of allowing editing in place
define additional populate functions for audio and media
2020-10-04 16:28:36 +02:00
WarmUpTill
787b664d29 Add vlc source to media tab (#42) 2020-09-30 19:07:40 +02:00
WarmUpTill
dd63ff846c Refactor, fixes and maximized option for title tab (#40)
* move definitions of switcher structs to separate files

* remove useless comment

* cleanup and silence warnings

* add generic switch

* removed delay on main thread startup as it served no purpose

* clean up ultility.hpp

* do not save switchStr to file as it could cause issues when scenes are renamed

* rename sceneRoundTrip to sceneSequence

* add option to switch if window is maximized to title tab
2020-09-25 21:42:58 +02:00
WarmUpTill
1559d437ec adjust install_name_tool calls to new qt dir (#39) 2020-09-21 22:24:47 +02:00
WarmUpTill
8d023ee385 Add audio tab (#36)
* add volumemeter widget

* add audio tab UI

* add checkAudioSwitch functionality

* use constexpr instead of preprocessing
2020-09-21 00:52:42 +02:00
WarmUpTill
3fd3801e56 load libcurl at runtime (#33)
* load libcurl at runtime

* add warning to file tab if curl cant be loaded
2020-09-13 09:13:09 +02:00
WarmUpTill
3ec1e99f4b fix MacOS CI issues due to QT dependency (#35)
* fix MacOS CI issues due to QT deps

* adjust to new name of deps archive

* use MACOS_DEPS_VERSION instead of OSX_DEPS_VERSION

* disable python dependency during build
2020-09-13 01:30:08 +02:00
WarmUpTill
21a788b48b Improve time tab (#34)
* add support for weekdays and allow reordering on time tab

* add option to use time relative to streaming/recording start on time tab
2020-09-13 00:03:38 +02:00
WarmUpTill
a5c8d776f3 only try auto start streaming/recording once on scene chagne 2020-08-24 20:55:54 +02:00
WarmUpTill
95261ce9bb add first draft of auto start of recording / streaming 2020-08-16 19:48:54 +02:00
51 changed files with 7727 additions and 5071 deletions

View File

@@ -16,7 +16,7 @@ jobs:
runs-on: [macos-latest]
env:
QT_VERSION: 5.14.1
OSX_DEPS_VERSION: '2020-04-07'
MACOS_DEPS_VERSION: '2020-08-30'
steps:
- name: Checkout
uses: actions/checkout@v2
@@ -35,21 +35,31 @@ jobs:
shell: bash
run: |
cd UI/frontend-plugins/${{ env.PLUGIN_NAME }}/CI/macos
brew untap local/openssl
brew untap local/python2
brew update
brew bundle
cd -
- name: 'Install prerequisite: Pre-built dependencies'
if: steps.deps-cache.outputs.cache-hit != 'true'
shell: bash
run: |
curl -L -O https://github.com/obsproject/obs-deps/releases/download/${{ env.OSX_DEPS_VERSION }}/osx-deps-${{ env.OSX_DEPS_VERSION }}.tar.gz
tar -xf ./osx-deps-${{ env.OSX_DEPS_VERSION }}.tar.gz -C "/tmp"
curl -L -O https://github.com/obsproject/obs-deps/releases/download/${{ env.MACOS_DEPS_VERSION }}/macos-deps-${{ env.MACOS_DEPS_VERSION }}.tar.gz
tar -xf ./macos-deps-${{ env.MACOS_DEPS_VERSION }}.tar.gz -C "/tmp"
- name: 'Install prerequisite: Pre-built dependency Qt'
if: steps.deps-qt-cache.outputs.cache-hit != 'true'
shell: bash
run: |
curl -L -O https://github.com/obsproject/obs-deps/releases/download/${{ env.MACOS_DEPS_VERSION }}/macos-qt-${{ env.QT_VERSION }}-${{ env.MACOS_DEPS_VERSION }}.tar.gz
tar -xf ./macos-qt-${{ env.QT_VERSION }}-${{ env.MACOS_DEPS_VERSION }}.tar.gz -C "/tmp"
xattr -r -d com.apple.quarantine /tmp/obsdeps
- name: Configure
shell: bash
run: |
echo "add_subdirectory(${{ env.PLUGIN_NAME }})" >> UI/frontend-plugins/CMakeLists.txt
mkdir ./build
cd ./build
cmake -DCMAKE_OSX_DEPLOYMENT_TARGET=10.11 -DDepsPath="/tmp/obsdeps" -DQTDIR="/usr/local/Cellar/qt/${{ env.QT_VERSION }}" ..
cmake -DCMAKE_OSX_DEPLOYMENT_TARGET=10.11 -DDISABLE_PYTHON=ON -DDepsPath="/tmp/obsdeps" -DQTDIR="/tmp/obsdeps" ..
cd -
- name: Build
shell: bash
@@ -71,9 +81,9 @@ jobs:
cd UI/frontend-plugins/${{ env.PLUGIN_NAME }}
install_name_tool -change @rpath/libobs-frontend-api.dylib @executable_path/../Frameworks/libobs-frontend-api.dylib ../../../build/UI/frontend-plugins/SceneSwitcher/advanced-scene-switcher.so
install_name_tool -change @rpath/libobs.0.dylib @executable_path/../Frameworks/libobs.0.dylib ../../../build/UI/frontend-plugins/SceneSwitcher/advanced-scene-switcher.so
install_name_tool -change /usr/local/opt/qt/lib/QtWidgets.framework/Versions/5/QtWidgets @executable_path/../Frameworks/QtWidgets.framework/Versions/5/QtWidgets ../../../build/UI/frontend-plugins/SceneSwitcher/advanced-scene-switcher.so
install_name_tool -change /usr/local/opt/qt/lib/QtGui.framework/Versions/5/QtGui @executable_path/../Frameworks/QtGui.framework/Versions/5/QtGui ../../../build/UI/frontend-plugins/SceneSwitcher/advanced-scene-switcher.so
install_name_tool -change /usr/local/opt/qt/lib/QtCore.framework/Versions/5/QtCore @executable_path/../Frameworks/QtCore.framework/Versions/5/QtCore ../../../build/UI/frontend-plugins/SceneSwitcher/advanced-scene-switcher.so
install_name_tool -change /tmp/obsdeps/lib/QtWidgets.framework/Versions/5/QtWidgets @executable_path/../Frameworks/QtWidgets.framework/Versions/5/QtWidgets ../../../build/UI/frontend-plugins/SceneSwitcher/advanced-scene-switcher.so
install_name_tool -change /tmp/obsdeps/lib/QtGui.framework/Versions/5/QtGui @executable_path/../Frameworks/QtGui.framework/Versions/5/QtGui ../../../build/UI/frontend-plugins/SceneSwitcher/advanced-scene-switcher.so
install_name_tool -change /tmp/obsdeps/lib/QtCore.framework/Versions/5/QtCore @executable_path/../Frameworks/QtCore.framework/Versions/5/QtCore ../../../build/UI/frontend-plugins/SceneSwitcher/advanced-scene-switcher.so
FILE_DATE=$(date +%Y-%m-%d)
FILE_NAME=${{ env.PLUGIN_NAME }}-$FILE_DATE-${{ github.sha }}-macos.pkg
echo "::set-env name=FILE_NAME::${FILE_NAME}"

View File

@@ -2,5 +2,4 @@ brew "jack"
brew "speexdsp"
brew "cmake"
brew "freetype"
brew "fdk-aac"
brew "https://gist.githubusercontent.com/DDRBoxman/9c7a2b08933166f4b61ed9a44b242609/raw/ef4de6c587c6bd7f50210eccd5bd51ff08e6de13/qt.rb"
brew "fdk-aac"

View File

@@ -35,26 +35,44 @@ if(BUILD_OUT_OF_TREE)
src/headers/advanced-scene-switcher.hpp
src/headers/switcher-data-structs.hpp
src/headers/utility.hpp
src/headers/curl-helper.hpp
src/headers/switch-audio.hpp
src/headers/switch-executable.hpp
src/headers/switch-file.hpp
src/headers/switch-idle.hpp
src/headers/switch-media.hpp
src/headers/switch-random.hpp
src/headers/switch-screen-region.hpp
src/headers/switch-time.hpp
src/headers/switch-transitions.hpp
src/headers/switch-window.hpp
src/headers/switch-sequence.hpp
src/headers/switch-generic.hpp
)
set(advanced-scene-switcher_SOURCES
${advanced-scene-switcher_SOURCES}
src/advanced-scene-switcher.cpp
src/advanced-scene-switcher-module.c
src/scene-transitions.cpp
src/screen-region-switch.cpp
src/priority.cpp
src/executable-switch.cpp
src/idle-switch.cpp
src/scene-round-trip.cpp
src/file-switch.cpp
src/window-title-switch.cpp
src/media-switch.cpp
src/switcher-data-structs.cpp
src/switch-transitions.cpp
src/switch-screen-region.cpp
src/switch-priority.cpp
src/switch-executable.cpp
src/switch-idle.cpp
src/switch-sequence.cpp
src/switch-file.cpp
src/switch-window.cpp
src/switch-media.cpp
src/hotkey.cpp
src/general.cpp
src/pause-switch.cpp
src/random.cpp
src/time-switch.cpp
src/switch-pause.cpp
src/switch-random.cpp
src/switch-time.cpp
src/switch-audio.cpp
src/switch-generic.cpp
src/curl-helper.cpp
src/volume-control.cpp
)
set(advanced-scene-switcher_UI
@@ -105,8 +123,7 @@ if(BUILD_OUT_OF_TREE)
${LIBOBS_LIB}
${LIBOBS_FRONTEND_API_LIB}
Qt5::Core
Qt5::Widgets
${LIBCURL_LIBRARIES})
Qt5::Widgets)
# Additional commands to install the module in the correct place.
# Find all the translation files so we can copy them to the correct
@@ -167,26 +184,45 @@ else ()
src/headers/advanced-scene-switcher.hpp
src/headers/switcher-data-structs.hpp
src/headers/utility.hpp
src/headers/curl-helper.hpp
src/headers/volume-control.hpp
src/headers/switch-audio.hpp
src/headers/switch-executable.hpp
src/headers/switch-file.hpp
src/headers/switch-idle.hpp
src/headers/switch-media.hpp
src/headers/switch-random.hpp
src/headers/switch-screen-region.hpp
src/headers/switch-time.hpp
src/headers/switch-transitions.hpp
src/headers/switch-window.hpp
src/headers/switch-sequence.hpp
src/headers/switch-generic.hpp
)
set(advanced-scene-switcher_SOURCES
${advanced-scene-switcher_SOURCES}
src/advanced-scene-switcher.cpp
src/advanced-scene-switcher-module.c
src/scene-transitions.cpp
src/screen-region-switch.cpp
src/priority.cpp
src/executable-switch.cpp
src/idle-switch.cpp
src/scene-round-trip.cpp
src/file-switch.cpp
src/window-title-switch.cpp
src/media-switch.cpp
src/switcher-data-structs.cpp
src/switch-transitions.cpp
src/switch-screen-region.cpp
src/switch-priority.cpp
src/switch-executable.cpp
src/switch-idle.cpp
src/switch-sequence.cpp
src/switch-file.cpp
src/switch-window.cpp
src/switch-media.cpp
src/hotkey.cpp
src/general.cpp
src/pause-switch.cpp
src/random.cpp
src/time-switch.cpp
src/switch-pause.cpp
src/switch-random.cpp
src/switch-time.cpp
src/switch-audio.cpp
src/switch-generic.cpp
src/curl-helper.cpp
src/volume-control.cpp
)
set(advanced-scene-switcher_UI
@@ -227,8 +263,7 @@ else ()
${advanced-scene-switcher_PLATFORM_LIBS}
obs-frontend-api
Qt5::Widgets
libobs
${LIBCURL_LIBRARIES})
libobs)
install_obs_plugin(advanced-scene-switcher data)
endif()

File diff suppressed because it is too large Load Diff

View File

@@ -1,29 +1,22 @@
#include <QMainWindow>
#include <QDir>
#include <QAction>
#include <condition_variable>
#include <chrono>
#include <vector>
#include <mutex>
#include <thread>
#include <QtGui/qstandarditemmodel.h>
#include <QPropertyAnimation>
#include <QGraphicsColorizeEffect>
#include <obs-frontend-api.h>
#include <obs-module.h>
#include <obs.hpp>
#include <util/util.hpp>
#include "headers/switcher-data-structs.hpp"
#include "headers/advanced-scene-switcher.hpp"
#include "headers/utility.hpp"
#include "headers/curl-helper.hpp"
SwitcherData *switcher = nullptr;
/********************************************************************************
* Create the Advanced Scene Switcher settings window
********************************************************************************/
SceneSwitcher::SceneSwitcher(QWidget *parent)
: QDialog(parent), ui(new Ui_SceneSwitcher)
AdvSceneSwitcher::AdvSceneSwitcher(QWidget *parent)
: QDialog(parent), ui(new Ui_AdvSceneSwitcher)
{
ui->setupUi(this);
@@ -33,46 +26,7 @@ SceneSwitcher::SceneSwitcher(QWidget *parent)
loadUI();
}
void SceneSwitcher::populateSceneSelection(QComboBox *sel, bool addPrevious)
{
BPtr<char *> scenes = obs_frontend_get_scene_names();
char **temp = scenes;
while (*temp) {
const char *name = *temp;
sel->addItem(name);
temp++;
}
if (addPrevious)
sel->addItem(PREVIOUS_SCENE_NAME);
}
void SceneSwitcher::populateTransitionSelection(QComboBox *sel)
{
obs_frontend_source_list *transitions = new obs_frontend_source_list();
obs_frontend_get_transitions(transitions);
for (size_t i = 0; i < transitions->sources.num; i++) {
const char *name =
obs_source_get_name(transitions->sources.array[i]);
sel->addItem(name);
}
obs_frontend_source_list_free(transitions);
}
void SceneSwitcher::populateWindowSelection(QComboBox *sel)
{
std::vector<std::string> windows;
GetWindowList(windows);
sort(windows.begin(), windows.end());
for (std::string &window : windows) {
sel->addItem(window.c_str());
}
}
void SceneSwitcher::loadUI()
void AdvSceneSwitcher::loadUI()
{
#if __APPLE__
setMinimumHeight(700);
@@ -89,12 +43,217 @@ void SceneSwitcher::loadUI()
setupMediaTab();
setupFileTab();
setupTimeTab();
setupAudioTab();
setTabOrder();
loading = false;
}
/********************************************************************************
* UI helpers
********************************************************************************/
void addSelectionEntry(QComboBox *sel, const char *description,
const char *tooltip = "")
{
sel->addItem(description);
if (strcmp(tooltip, "") != 0) {
sel->setItemData(0, tooltip, Qt::ToolTipRole);
}
QStandardItemModel *model =
qobject_cast<QStandardItemModel *>(sel->model());
QModelIndex firstIndex =
model->index(0, sel->modelColumn(), sel->rootModelIndex());
QStandardItem *firstItem = model->itemFromIndex(firstIndex);
firstItem->setSelectable(false);
firstItem->setEnabled(false);
}
void AdvSceneSwitcher::populateSceneSelection(QComboBox *sel, bool addPrevious,
bool addSelect)
{
if (addSelect)
addSelectionEntry(sel, "--select scene--",
"invalid entries will not be saved");
BPtr<char *> scenes = obs_frontend_get_scene_names();
char **temp = scenes;
while (*temp) {
const char *name = *temp;
sel->addItem(name);
temp++;
}
if (addPrevious)
sel->addItem(previous_scene_name);
}
void AdvSceneSwitcher::populateTransitionSelection(QComboBox *sel,
bool addSelect)
{
if (addSelect)
addSelectionEntry(sel, "--select transition--");
obs_frontend_source_list *transitions = new obs_frontend_source_list();
obs_frontend_get_transitions(transitions);
for (size_t i = 0; i < transitions->sources.num; i++) {
const char *name =
obs_source_get_name(transitions->sources.array[i]);
sel->addItem(name);
}
obs_frontend_source_list_free(transitions);
}
void AdvSceneSwitcher::populateWindowSelection(QComboBox *sel, bool addSelect)
{
if (addSelect)
addSelectionEntry(sel, "--select window--");
std::vector<std::string> windows;
GetWindowList(windows);
sort(windows.begin(), windows.end());
for (std::string &window : windows) {
sel->addItem(window.c_str());
}
#ifdef WIN32
sel->setItemData(
0,
"Use \"OBS\" to specify OBS window\nUse \"Task Switching\"to specify ALT + TAB",
Qt::ToolTipRole);
#endif
}
void AdvSceneSwitcher::populateAudioSelection(QComboBox *sel, bool addSelect)
{
if (addSelect)
addSelectionEntry(sel, "--select audio source--",
"invalid entries will not be saved");
auto sourceEnum = [](void *data, obs_source_t *source) -> bool /* -- */
{
QComboBox *combo = reinterpret_cast<QComboBox *>(data);
uint32_t flags = obs_source_get_output_flags(source);
if ((flags & OBS_SOURCE_AUDIO) != 0) {
const char *name = obs_source_get_name(source);
combo->addItem(name);
}
return true;
};
obs_enum_sources(sourceEnum, sel);
}
void AdvSceneSwitcher::populateMediaSelection(QComboBox *sel, bool addSelect)
{
if (addSelect)
addSelectionEntry(sel, "--select media source--",
"invalid entries will not be saved");
auto sourceEnum = [](void *data, obs_source_t *source) -> bool /* -- */
{
QComboBox *combo = reinterpret_cast<QComboBox *>(data);
std::string sourceId = obs_source_get_id(source);
if (sourceId.compare("ffmpeg_source") == 0 ||
sourceId.compare("vlc_source") == 0) {
const char *name = obs_source_get_name(source);
combo->addItem(name);
}
return true;
};
obs_enum_sources(sourceEnum, sel);
}
void AdvSceneSwitcher::populateProcessSelection(QComboBox *sel, bool addSelect)
{
if (addSelect)
addSelectionEntry(sel, "--select process--");
QStringList processes;
GetProcessList(processes);
for (QString &process : processes)
sel->addItem(process);
}
bool AdvSceneSwitcher::listMoveUp(QListWidget *list)
{
int index = list->currentRow();
if (index == -1 || index == 0)
return false;
QWidget *row = list->itemWidget(list->currentItem());
QListWidgetItem *itemN = list->currentItem()->clone();
list->insertItem(index - 1, itemN);
list->setItemWidget(itemN, row);
list->takeItem(index + 1);
list->setCurrentRow(index - 1);
return true;
}
bool AdvSceneSwitcher::listMoveDown(QListWidget *list)
{
int index = list->currentRow();
if (index == -1 || index == list->count() - 1)
return false;
QWidget *row = list->itemWidget(list->currentItem());
QListWidgetItem *itemN = list->currentItem()->clone();
list->insertItem(index + 2, itemN);
list->setItemWidget(itemN, row);
list->takeItem(index);
list->setCurrentRow(index + 1);
return true;
}
QMetaObject::Connection AdvSceneSwitcher::PulseWidget(QWidget *widget,
QColor endColor,
QColor startColor,
QString specifier)
{
if (switcher->disableHints)
return QMetaObject::Connection();
widget->setStyleSheet(specifier + "{ \
border-style: outset; \
border-width: 2px; \
border-radius: 10px; \
border-color: rgb(0,0,0,0) \
}");
QGraphicsColorizeEffect *eEffect = new QGraphicsColorizeEffect(widget);
widget->setGraphicsEffect(eEffect);
QPropertyAnimation *paAnimation =
new QPropertyAnimation(eEffect, "color");
paAnimation->setStartValue(startColor);
paAnimation->setEndValue(endColor);
paAnimation->setDuration(1000);
// play backward to return to original state on timer end
paAnimation->setDirection(QAbstractAnimation::Backward);
auto con = QWidget::connect(
paAnimation, &QPropertyAnimation::finished, [paAnimation]() {
QTimer::singleShot(1000, [paAnimation] {
paAnimation->start();
});
});
paAnimation->start();
return con;
}
/********************************************************************************
* Saving and loading
********************************************************************************/
@@ -110,7 +269,7 @@ static void SaveSceneSwitcher(obs_data_t *save_data, bool saving, void *)
switcher->saveWindowTitleSwitches(obj);
switcher->saveScreenRegionSwitches(obj);
switcher->savePauseSwitches(obj);
switcher->saveSceneRoundTripSwitches(obj);
switcher->saveSceneSequenceSwitches(obj);
switcher->saveSceneTransitions(obj);
switcher->saveIdleSwitches(obj);
switcher->saveExecutableSwitches(obj);
@@ -118,7 +277,9 @@ static void SaveSceneSwitcher(obs_data_t *save_data, bool saving, void *)
switcher->saveFileSwitches(obj);
switcher->saveMediaSwitches(obj);
switcher->saveTimeSwitches(obj);
switcher->saveAudioSwitches(obj);
switcher->saveGeneralSettings(obj);
switcher->saveHotkeys(obj);
obs_data_set_obj(save_data, "advanced-scene-switcher", obj);
@@ -135,7 +296,7 @@ static void SaveSceneSwitcher(obs_data_t *save_data, bool saving, void *)
switcher->loadWindowTitleSwitches(obj);
switcher->loadScreenRegionSwitches(obj);
switcher->loadPauseSwitches(obj);
switcher->loadSceneRoundTripSwitches(obj);
switcher->loadSceneSequenceSwitches(obj);
switcher->loadSceneTransitions(obj);
switcher->loadIdleSwitches(obj);
switcher->loadExecutableSwitches(obj);
@@ -143,15 +304,19 @@ static void SaveSceneSwitcher(obs_data_t *save_data, bool saving, void *)
switcher->loadFileSwitches(obj);
switcher->loadMediaSwitches(obj);
switcher->loadTimeSwitches(obj);
switcher->loadAudioSwitches(obj);
switcher->loadGeneralSettings(obj);
switcher->loadHotkeys(obj);
obs_data_release(obj);
switcher->m.unlock();
if (switcher->stop)
switcher->Stop();
else
// stop the scene switcher at least once
// to avoid issues with scene collection changes
bool start = !switcher->stop;
switcher->Stop();
if (start)
switcher->Start();
}
}
@@ -161,10 +326,7 @@ static void SaveSceneSwitcher(obs_data_t *save_data, bool saving, void *)
********************************************************************************/
void SwitcherData::Thread()
{
blog(LOG_INFO, "Advanced Scene Switcher started");
//to avoid scene duplication when rapidly switching scene collection
std::this_thread::sleep_for(std::chrono::seconds(2));
blog(LOG_INFO, "started");
int sleep = 0;
while (true) {
@@ -177,15 +339,11 @@ void SwitcherData::Thread()
if (sleep > interval) {
duration = std::chrono::milliseconds(sleep);
if (verbose)
blog(LOG_INFO,
"Advanced Scene Switcher sleep for %d",
sleep);
blog(LOG_INFO, "sleep for %d", sleep);
} else {
duration = std::chrono::milliseconds(interval);
if (verbose)
blog(LOG_INFO,
"Advanced Scene Switcher sleep for %d",
interval);
blog(LOG_INFO, "sleep for %d", interval);
}
sleep = 0;
switcher->Prune();
@@ -195,50 +353,60 @@ void SwitcherData::Thread()
if (switcher->stop) {
break;
}
setDefaultSceneTransitions();
if (autoStopEnable) {
autoStopStreamAndRecording();
}
if (autoStartEnable) {
autoStartStreamRecording();
}
if (checkPause()) {
continue;
}
for (int switchFuncName : functionNamesByPriority) {
switch (switchFuncName) {
case READ_FILE_FUNC:
case read_file_func:
checkSwitchInfoFromFile(match, scene,
transition);
checkFileContent(match, scene, transition);
break;
case IDLE_FUNC:
case idle_func:
checkIdleSwitch(match, scene, transition);
break;
case EXE_FUNC:
case exe_func:
checkExeSwitch(match, scene, transition);
break;
case SCREEN_REGION_FUNC:
case screen_region_func:
checkScreenRegionSwitch(match, scene,
transition);
break;
case WINDOW_TITLE_FUNC:
case window_title_func:
checkWindowTitleSwitch(match, scene,
transition);
break;
case ROUND_TRIP_FUNC:
checkSceneRoundTrip(match, scene, transition,
lock);
case round_trip_func:
checkSceneSequence(match, scene, transition,
lock);
if (sceneChangedDuringWait()) //scene might have changed during the sleep
{
goto startLoop;
}
break;
case MEDIA_FUNC:
case media_func:
checkMediaSwitch(match, scene, transition);
break;
case TIME_FUNC:
case time_func:
checkTimeSwitch(match, scene, transition);
break;
case audio_func:
checkAudioSwitch(match, scene, transition);
break;
}
if (switcher->stop) {
@@ -264,7 +432,7 @@ void SwitcherData::Thread()
}
}
endLoop:
blog(LOG_INFO, "Advanced Scene Switcher stopped");
blog(LOG_INFO, "stopped");
}
void switchScene(OBSWeakSource &scene, OBSWeakSource &transition,
@@ -289,8 +457,7 @@ void switchScene(OBSWeakSource &scene, OBSWeakSource &transition,
lock.lock();
if (switcher->verbose)
blog(LOG_INFO,
"Advanced Scene Switcher switched scene");
blog(LOG_INFO, "switched scene");
}
obs_source_release(currentSource);
obs_source_release(source);
@@ -335,6 +502,13 @@ void SwitcherData::Stop()
********************************************************************************/
extern "C" void FreeSceneSwitcher()
{
if (loaded_curl_lib) {
if (switcher->curl && f_curl_cleanup)
f_curl_cleanup(switcher->curl);
delete loaded_curl_lib;
loaded_curl_lib = nullptr;
}
delete switcher;
switcher = nullptr;
}
@@ -355,6 +529,20 @@ void handleSceneChange(SwitcherData *s)
s->previousScene = s->PreviousScene2;
s->PreviousScene2 = ws;
}
//reset events only hanled on scene change
s->autoStartedRecently = false;
s->changedDefTransitionRecently = false;
}
void setLiveTime(SwitcherData *s)
{
s->liveTime = QDateTime::currentDateTime();
}
void resetLiveTime(SwitcherData *s)
{
s->liveTime = QDateTime();
}
static void OBSEvent(enum obs_frontend_event event, void *switcher)
@@ -366,6 +554,14 @@ static void OBSEvent(enum obs_frontend_event event, void *switcher)
case OBS_FRONTEND_EVENT_SCENE_CHANGED:
handleSceneChange((SwitcherData *)switcher);
break;
case OBS_FRONTEND_EVENT_RECORDING_STARTED:
case OBS_FRONTEND_EVENT_STREAMING_STARTED:
setLiveTime((SwitcherData *)switcher);
break;
case OBS_FRONTEND_EVENT_RECORDING_STOPPED:
case OBS_FRONTEND_EVENT_STREAMING_STOPPED:
resetLiveTime((SwitcherData *)switcher);
break;
default:
break;
}
@@ -378,11 +574,15 @@ extern "C" void InitSceneSwitcher()
switcher = new SwitcherData;
if (loadCurl() && f_curl_init) {
switcher->curl = f_curl_init();
}
auto cb = []() {
QMainWindow *window =
(QMainWindow *)obs_frontend_get_main_window();
SceneSwitcher ss(window);
AdvSceneSwitcher ss(window);
ss.exec();
};
@@ -390,25 +590,4 @@ extern "C" void InitSceneSwitcher()
obs_frontend_add_event_callback(OBSEvent, switcher);
action->connect(action, &QAction::triggered, cb);
char *path = obs_module_config_path("");
QDir dir(path);
if (!dir.exists()) {
dir.mkpath(".");
}
bfree(path);
obs_hotkey_id toggleHotkeyId = obs_hotkey_register_frontend(
"startStopToggleSwitcherHotkey",
"Toggle Start/Stop for the Advanced Scene Switcher",
startStopToggleHotkeyFunc, NULL);
loadKeybinding(toggleHotkeyId, TOGGLE_HOTKEY_PATH);
obs_hotkey_id startHotkeyId = obs_hotkey_register_frontend(
"startSwitcherHotkey", "Start the Advanced Scene Switcher",
startHotkeyFunc, NULL);
loadKeybinding(startHotkeyId, START_HOTKEY_PATH);
obs_hotkey_id stopHotkeyId = obs_hotkey_register_frontend(
"stopSwitcherHotkey", "Stop the Advanced Scene Switcher",
stopHotkeyFunc, NULL);
loadKeybinding(stopHotkeyId, STOP_HOTKEY_PATH);
}

67
src/curl-helper.cpp Normal file
View File

@@ -0,0 +1,67 @@
#include <QDir>
#include <QFileInfo>
#include <curl/curl.h>
#include <obs.hpp>
#include "headers/curl-helper.hpp"
initFunction f_curl_init = nullptr;
setOptFunction f_curl_setopt = nullptr;
performFunction f_curl_perform = nullptr;
cleanupFunction f_curl_cleanup = nullptr;
QLibrary *loaded_curl_lib = nullptr;
bool resolveCurl()
{
f_curl_init = (initFunction)loaded_curl_lib->resolve("curl_easy_init");
f_curl_setopt =
(setOptFunction)loaded_curl_lib->resolve("curl_easy_setopt");
f_curl_perform =
(performFunction)loaded_curl_lib->resolve("curl_easy_perform");
f_curl_cleanup =
(cleanupFunction)loaded_curl_lib->resolve("curl_easy_cleanup");
if (f_curl_init && f_curl_setopt && f_curl_perform && f_curl_cleanup) {
blog(LOG_INFO, "[adv-ss] curl loaded successfully");
return true;
}
blog(LOG_INFO, "[adv-ss] curl symbols not resolved");
return false;
}
bool loadCurl()
{
QStringList locations;
locations << QDir::currentPath();
#if defined(__linux__) || defined(__APPLE__)
locations << "/usr/lib";
locations << "/usr/local/lib";
locations << "/usr/lib/x86_64-linux-gnu";
locations << "/usr/local/opt/curl/lib";
#endif
for (QString path : locations) {
blog(LOG_INFO, "[adv-ss] trying '%s'", path.toUtf8().constData());
QFileInfo libPath(
QDir(path).absoluteFilePath(curl_library_name));
if (libPath.exists() && libPath.isFile()) {
QString libFilePath = libPath.absoluteFilePath();
blog(LOG_INFO, "[adv-ss] found curl library at '%s'",
libFilePath.toUtf8().constData());
loaded_curl_lib = new QLibrary(libFilePath, nullptr);
if (resolveCurl())
return true;
else {
delete loaded_curl_lib;
loaded_curl_lib = nullptr;
}
}
}
blog(LOG_WARNING, "[adv-ss] can't find the curl library");
return false;
}

View File

@@ -1,280 +0,0 @@
#include "headers/advanced-scene-switcher.hpp"
int SceneSwitcher::executableFindByData(const QString &exe)
{
int count = ui->executables->count();
for (int i = 0; i < count; i++) {
QListWidgetItem *item = ui->executables->item(i);
QString itemExe = item->data(Qt::UserRole).toString();
if (itemExe == exe)
return i;
}
return -1;
}
void SceneSwitcher::on_executables_currentRowChanged(int idx)
{
if (loading)
return;
if (idx == -1)
return;
QListWidgetItem *item = ui->executables->item(idx);
QString exec = item->data(Qt::UserRole).toString();
std::lock_guard<std::mutex> lock(switcher->m);
for (auto &s : switcher->executableSwitches) {
if (exec.compare(s.mExe) == 0) {
QString sceneName = GetWeakSourceName(s.mScene).c_str();
QString transitionName =
GetWeakSourceName(s.mTransition).c_str();
ui->executableScenes->setCurrentText(sceneName);
ui->executable->setCurrentText(exec);
ui->executableTransitions->setCurrentText(
transitionName);
ui->requiresFocusCheckBox->setChecked(s.mInFocus);
break;
}
}
}
void SceneSwitcher::on_executableUp_clicked()
{
int index = ui->executables->currentRow();
if (index != -1 && index != 0) {
ui->executables->insertItem(index - 1,
ui->executables->takeItem(index));
ui->executables->setCurrentRow(index - 1);
std::lock_guard<std::mutex> lock(switcher->m);
iter_swap(switcher->executableSwitches.begin() + index,
switcher->executableSwitches.begin() + index - 1);
}
}
void SceneSwitcher::on_executableDown_clicked()
{
int index = ui->executables->currentRow();
if (index != -1 && index != ui->executables->count() - 1) {
ui->executables->insertItem(index + 1,
ui->executables->takeItem(index));
ui->executables->setCurrentRow(index + 1);
std::lock_guard<std::mutex> lock(switcher->m);
iter_swap(switcher->executableSwitches.begin() + index,
switcher->executableSwitches.begin() + index + 1);
}
}
void SceneSwitcher::on_executableAdd_clicked()
{
QString sceneName = ui->executableScenes->currentText();
QString exeName = ui->executable->currentText();
QString transitionName = ui->executableTransitions->currentText();
bool inFocus = ui->requiresFocusCheckBox->isChecked();
if (exeName.isEmpty() || sceneName.isEmpty())
return;
OBSWeakSource source = GetWeakSourceByQString(sceneName);
OBSWeakSource transition = GetWeakTransitionByQString(transitionName);
QVariant v = QVariant::fromValue(exeName);
QString text = MakeSwitchNameExecutable(sceneName, exeName,
transitionName, inFocus);
int idx = executableFindByData(exeName);
if (idx == -1) {
std::lock_guard<std::mutex> lock(switcher->m);
switcher->executableSwitches.emplace_back(
source, transition, exeName.toUtf8().constData(),
inFocus);
QListWidgetItem *item =
new QListWidgetItem(text, ui->executables);
item->setData(Qt::UserRole, v);
} else {
QListWidgetItem *item = ui->executables->item(idx);
item->setText(text);
{
std::lock_guard<std::mutex> lock(switcher->m);
for (auto &s : switcher->executableSwitches) {
if (s.mExe == exeName) {
s.mScene = source;
s.mTransition = transition;
s.mInFocus = inFocus;
break;
}
}
}
}
}
void SceneSwitcher::on_executableRemove_clicked()
{
QListWidgetItem *item = ui->executables->currentItem();
if (!item)
return;
QString exe = item->data(Qt::UserRole).toString();
{
std::lock_guard<std::mutex> lock(switcher->m);
auto &switches = switcher->executableSwitches;
for (auto it = switches.begin(); it != switches.end(); ++it) {
auto &s = *it;
if (s.mExe == exe) {
switches.erase(it);
break;
}
}
}
delete item;
}
void SwitcherData::checkExeSwitch(bool &match, OBSWeakSource &scene,
OBSWeakSource &transition)
{
std::string title;
QStringList runningProcesses;
bool ignored = false;
// Check if current window is ignored
GetCurrentWindowTitle(title);
for (auto &window : ignoreWindowsSwitches) {
// True if ignored switch equals title
bool equals = (title == window);
// True if ignored switch matches title
bool matches = QString::fromStdString(title).contains(
QRegularExpression(QString::fromStdString(window)));
if (equals || matches) {
ignored = true;
title = lastTitle;
break;
}
}
lastTitle = title;
// Check for match
GetProcessList(runningProcesses);
for (ExecutableSceneSwitch &s : executableSwitches) {
// True if executable switch is running (direct)
bool equals = runningProcesses.contains(s.mExe);
// True if executable switch is running (regex)
bool matches = (runningProcesses.indexOf(
QRegularExpression(s.mExe)) != -1);
// True if focus is disabled OR switch is focused
bool focus = (!s.mInFocus || isInFocus(s.mExe));
// True if current window is ignored AND switch equals OR matches last window
bool ignore =
(ignored && (title == s.mExe.toStdString() ||
QString::fromStdString(title).contains(
QRegularExpression(s.mExe))));
if ((equals || matches) && (focus || ignore)) {
match = true;
scene = s.mScene;
transition = s.mTransition;
if (verbose)
blog(LOG_INFO,
"Advanced Scene Switcher exec match");
break;
}
}
}
void SwitcherData::saveExecutableSwitches(obs_data_t *obj)
{
obs_data_array_t *executableArray = obs_data_array_create();
for (ExecutableSceneSwitch &s : switcher->executableSwitches) {
obs_data_t *array_obj = obs_data_create();
obs_source_t *source = obs_weak_source_get_source(s.mScene);
obs_source_t *transition =
obs_weak_source_get_source(s.mTransition);
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_string(array_obj, "exefile",
s.mExe.toUtf8());
obs_data_set_bool(array_obj, "infocus", s.mInFocus);
obs_data_array_push_back(executableArray, array_obj);
obs_source_release(source);
obs_source_release(transition);
}
obs_data_release(array_obj);
}
obs_data_set_array(obj, "executableSwitches", executableArray);
obs_data_array_release(executableArray);
}
void SwitcherData::loadExecutableSwitches(obs_data_t *obj)
{
switcher->executableSwitches.clear();
obs_data_array_t *executableArray =
obs_data_get_array(obj, "executableSwitches");
size_t count = obs_data_array_count(executableArray);
for (size_t i = 0; i < count; i++) {
obs_data_t *array_obj = obs_data_array_item(executableArray, i);
const char *scene = obs_data_get_string(array_obj, "scene");
const char *transition =
obs_data_get_string(array_obj, "transition");
const char *exe = obs_data_get_string(array_obj, "exefile");
bool infocus = obs_data_get_bool(array_obj, "infocus");
switcher->executableSwitches.emplace_back(
GetWeakSourceByName(scene),
GetWeakTransitionByName(transition), exe, infocus);
obs_data_release(array_obj);
}
obs_data_array_release(executableArray);
}
void SceneSwitcher::setupExecutableTab()
{
populateSceneSelection(ui->executableScenes, false);
populateTransitionSelection(ui->executableTransitions);
QStringList processes;
GetProcessList(processes);
for (QString &process : processes)
ui->executable->addItem(process);
for (auto &s : switcher->executableSwitches) {
std::string sceneName = GetWeakSourceName(s.mScene);
std::string transitionName = GetWeakSourceName(s.mTransition);
QString text = MakeSwitchNameExecutable(sceneName.c_str(),
s.mExe,
transitionName.c_str(),
s.mInFocus);
QListWidgetItem *item =
new QListWidgetItem(text, ui->executables);
item->setData(Qt::UserRole, s.mExe);
}
}

View File

@@ -1,16 +1,17 @@
#include <obs-module.h>
#include <QFileDialog>
#include <QTextStream>
#include <QMessageBox>
#include "headers/advanced-scene-switcher.hpp"
#include "headers/utility.hpp"
void SceneSwitcher::on_close_clicked()
QMetaObject::Connection inactivePluse;
void AdvSceneSwitcher::on_close_clicked()
{
done(0);
}
void SceneSwitcher::UpdateNonMatchingScene(const QString &name)
void AdvSceneSwitcher::UpdateNonMatchingScene(const QString &name)
{
obs_source_t *scene = obs_get_source_by_name(name.toUtf8().constData());
obs_weak_source_t *ws = obs_source_get_weak_source(scene);
@@ -21,7 +22,7 @@ void SceneSwitcher::UpdateNonMatchingScene(const QString &name)
obs_source_release(scene);
}
void SceneSwitcher::on_noMatchDontSwitch_clicked()
void AdvSceneSwitcher::on_noMatchDontSwitch_clicked()
{
if (loading)
return;
@@ -29,9 +30,10 @@ void SceneSwitcher::on_noMatchDontSwitch_clicked()
std::lock_guard<std::mutex> lock(switcher->m);
switcher->switchIfNotMatching = NO_SWITCH;
ui->noMatchSwitchScene->setEnabled(false);
ui->randomDisabledWarning->setVisible(true);
}
void SceneSwitcher::on_noMatchSwitch_clicked()
void AdvSceneSwitcher::on_noMatchSwitch_clicked()
{
if (loading)
return;
@@ -40,9 +42,10 @@ void SceneSwitcher::on_noMatchSwitch_clicked()
switcher->switchIfNotMatching = SWITCH;
ui->noMatchSwitchScene->setEnabled(true);
UpdateNonMatchingScene(ui->noMatchSwitchScene->currentText());
ui->randomDisabledWarning->setVisible(true);
}
void SceneSwitcher::on_noMatchRandomSwitch_clicked()
void AdvSceneSwitcher::on_noMatchRandomSwitch_clicked()
{
if (loading)
return;
@@ -50,9 +53,10 @@ void SceneSwitcher::on_noMatchRandomSwitch_clicked()
std::lock_guard<std::mutex> lock(switcher->m);
switcher->switchIfNotMatching = RANDOM_SWITCH;
ui->noMatchSwitchScene->setEnabled(false);
ui->randomDisabledWarning->setVisible(false);
}
void SceneSwitcher::on_startupBehavior_currentIndexChanged(int index)
void AdvSceneSwitcher::on_startupBehavior_currentIndexChanged(int index)
{
if (loading)
return;
@@ -61,7 +65,8 @@ void SceneSwitcher::on_startupBehavior_currentIndexChanged(int index)
switcher->startupBehavior = (StartupBehavior)index;
}
void SceneSwitcher::on_noMatchSwitchScene_currentTextChanged(const QString &text)
void AdvSceneSwitcher::on_noMatchSwitchScene_currentTextChanged(
const QString &text)
{
if (loading)
return;
@@ -70,7 +75,7 @@ void SceneSwitcher::on_noMatchSwitchScene_currentTextChanged(const QString &text
UpdateNonMatchingScene(text);
}
void SceneSwitcher::on_checkInterval_valueChanged(int value)
void AdvSceneSwitcher::on_checkInterval_valueChanged(int value)
{
if (loading)
return;
@@ -79,19 +84,22 @@ void SceneSwitcher::on_checkInterval_valueChanged(int value)
switcher->interval = value;
}
void SceneSwitcher::SetStarted()
void AdvSceneSwitcher::SetStarted()
{
ui->toggleStartButton->setText("Stop");
ui->pluginRunningText->setText("Active");
ui->pluginRunningText->disconnect(inactivePluse);
}
void SceneSwitcher::SetStopped()
void AdvSceneSwitcher::SetStopped()
{
ui->toggleStartButton->setText("Start");
ui->pluginRunningText->setText("Inactive");
inactivePluse = PulseWidget(ui->pluginRunningText, QColor(Qt::red),
QColor(0, 0, 0, 0), "QLabel ");
}
void SceneSwitcher::on_toggleStartButton_clicked()
void AdvSceneSwitcher::on_toggleStartButton_clicked()
{
if (switcher->th && switcher->th->isRunning()) {
switcher->Stop();
@@ -102,12 +110,12 @@ void SceneSwitcher::on_toggleStartButton_clicked()
}
}
void SceneSwitcher::closeEvent(QCloseEvent *)
void AdvSceneSwitcher::closeEvent(QCloseEvent *)
{
obs_frontend_save();
}
void SceneSwitcher::on_autoStopScenes_currentTextChanged(const QString &text)
void AdvSceneSwitcher::on_autoStopScenes_currentTextChanged(const QString &text)
{
if (loading)
return;
@@ -116,7 +124,7 @@ void SceneSwitcher::on_autoStopScenes_currentTextChanged(const QString &text)
UpdateAutoStopScene(text);
}
void SceneSwitcher::on_autoStopSceneCheckBox_stateChanged(int state)
void AdvSceneSwitcher::on_autoStopSceneCheckBox_stateChanged(int state)
{
if (loading)
return;
@@ -133,7 +141,7 @@ void SceneSwitcher::on_autoStopSceneCheckBox_stateChanged(int state)
}
}
void SceneSwitcher::UpdateAutoStopScene(const QString &name)
void AdvSceneSwitcher::UpdateAutoStopScene(const QString &name)
{
obs_source_t *scene = obs_get_source_by_name(name.toUtf8().constData());
obs_weak_source_t *ws = obs_source_get_weak_source(scene);
@@ -159,16 +167,93 @@ void SwitcherData::autoStopStreamAndRecording()
obs_weak_source_release(ws);
}
void SceneSwitcher::on_verboseLogging_stateChanged(int state)
void AdvSceneSwitcher::on_autoStartType_currentIndexChanged(int index)
{
if (loading)
return;
std::lock_guard<std::mutex> lock(switcher->m);
switcher->autoStartType = (AutoStartType)index;
}
void AdvSceneSwitcher::on_autoStartScenes_currentTextChanged(const QString &text)
{
if (loading)
return;
std::lock_guard<std::mutex> lock(switcher->m);
UpdateAutoStartScene(text);
}
void AdvSceneSwitcher::on_autoStartSceneCheckBox_stateChanged(int state)
{
if (loading)
return;
std::lock_guard<std::mutex> lock(switcher->m);
if (!state) {
ui->autoStartScenes->setDisabled(true);
ui->autoStartType->setDisabled(true);
switcher->autoStartEnable = false;
} else {
ui->autoStartScenes->setDisabled(false);
ui->autoStartType->setDisabled(false);
switcher->autoStartEnable = true;
if (!switcher->autoStartScene)
UpdateAutoStartScene(
ui->autoStartScenes->currentText());
}
}
void AdvSceneSwitcher::UpdateAutoStartScene(const QString &name)
{
obs_source_t *scene = obs_get_source_by_name(name.toUtf8().constData());
obs_weak_source_t *ws = obs_source_get_weak_source(scene);
switcher->autoStartScene = ws;
obs_weak_source_release(ws);
obs_source_release(scene);
}
void SwitcherData::autoStartStreamRecording()
{
if (autoStartedRecently)
return;
obs_source_t *currentSource = obs_frontend_get_current_scene();
obs_weak_source_t *ws = obs_source_get_weak_source(currentSource);
if (ws && autoStartScene == ws) {
if ((switcher->autoStartType == STREAMING ||
switcher->autoStartType == RECORINDGSTREAMING) &&
!obs_frontend_streaming_active())
obs_frontend_streaming_start();
if ((switcher->autoStartType == RECORDING ||
switcher->autoStartType == RECORINDGSTREAMING) &&
!obs_frontend_recording_active())
obs_frontend_recording_start();
}
obs_source_release(currentSource);
obs_weak_source_release(ws);
autoStartedRecently = true;
}
void AdvSceneSwitcher::on_verboseLogging_stateChanged(int state)
{
if (loading)
return;
switcher->verbose = state;
}
void SceneSwitcher::on_exportSettings_clicked()
void AdvSceneSwitcher::on_uiHintsDisable_stateChanged(int state)
{
if (loading)
return;
switcher->disableHints = state;
}
void AdvSceneSwitcher::on_exportSettings_clicked()
{
QString directory = QFileDialog::getSaveFileName(
this, tr("Export Advanced Scene Switcher settings to file ..."),
@@ -185,7 +270,7 @@ void SceneSwitcher::on_exportSettings_clicked()
switcher->saveWindowTitleSwitches(obj);
switcher->saveScreenRegionSwitches(obj);
switcher->savePauseSwitches(obj);
switcher->saveSceneRoundTripSwitches(obj);
switcher->saveSceneSequenceSwitches(obj);
switcher->saveSceneTransitions(obj);
switcher->saveIdleSwitches(obj);
switcher->saveExecutableSwitches(obj);
@@ -193,14 +278,16 @@ void SceneSwitcher::on_exportSettings_clicked()
switcher->saveFileSwitches(obj);
switcher->saveMediaSwitches(obj);
switcher->saveTimeSwitches(obj);
switcher->saveAudioSwitches(obj);
switcher->saveGeneralSettings(obj);
switcher->saveHotkeys(obj);
obs_data_save_json(obj, file.fileName().toUtf8().constData());
obs_data_release(obj);
}
void SceneSwitcher::on_importSettings_clicked()
void AdvSceneSwitcher::on_importSettings_clicked()
{
std::lock_guard<std::mutex> lock(switcher->m);
@@ -229,7 +316,7 @@ void SceneSwitcher::on_importSettings_clicked()
switcher->loadWindowTitleSwitches(obj);
switcher->loadScreenRegionSwitches(obj);
switcher->loadPauseSwitches(obj);
switcher->loadSceneRoundTripSwitches(obj);
switcher->loadSceneSequenceSwitches(obj);
switcher->loadSceneTransitions(obj);
switcher->loadIdleSwitches(obj);
switcher->loadExecutableSwitches(obj);
@@ -237,7 +324,9 @@ void SceneSwitcher::on_importSettings_clicked()
switcher->loadFileSwitches(obj);
switcher->loadMediaSwitches(obj);
switcher->loadTimeSwitches(obj);
switcher->loadAudioSwitches(obj);
switcher->loadGeneralSettings(obj);
switcher->loadHotkeys(obj);
obs_data_release(obj);
@@ -290,6 +379,9 @@ int findTabIndex(QTabBar *bar, int pos)
case 11:
tabName = "Sequence";
break;
case 12:
tabName = "Audio";
break;
}
for (int i = 0; i < bar->count(); ++i) {
@@ -299,13 +391,13 @@ int findTabIndex(QTabBar *bar, int pos)
}
}
if (at == -1)
blog(LOG_INFO, "Advanced Scene Switcher failed to find tab %s",
blog(LOG_INFO, "failed to find tab %s",
tabName.toUtf8().constData());
return at;
}
void SceneSwitcher::setTabOrder()
void AdvSceneSwitcher::setTabOrder()
{
QTabBar *bar = ui->tabWidget->tabBar();
for (int i = 0; i < bar->count(); ++i) {
@@ -315,10 +407,10 @@ void SceneSwitcher::setTabOrder()
bar->moveTab(curPos, i);
}
connect(bar, &QTabBar::tabMoved, this, &SceneSwitcher::on_tabMoved);
connect(bar, &QTabBar::tabMoved, this, &AdvSceneSwitcher::on_tabMoved);
}
void SceneSwitcher::on_tabMoved(int from, int to)
void AdvSceneSwitcher::on_tabMoved(int from, int to)
{
if (loading)
return;
@@ -345,7 +437,15 @@ void SwitcherData::saveGeneralSettings(obs_data_t *obj)
obs_data_set_string(obj, "autoStopSceneName",
autoStopSceneName.c_str());
std::string autoStartSceneName =
GetWeakSourceName(switcher->autoStartScene);
obs_data_set_bool(obj, "autoStartEnable", switcher->autoStartEnable);
obs_data_set_int(obj, "autoStartType", switcher->autoStartType);
obs_data_set_string(obj, "autoStartSceneName",
autoStartSceneName.c_str());
obs_data_set_bool(obj, "verbose", switcher->verbose);
obs_data_set_bool(obj, "disableHints", switcher->disableHints);
obs_data_set_int(obj, "priority0",
switcher->functionNamesByPriority[0]);
@@ -363,6 +463,8 @@ void SwitcherData::saveGeneralSettings(obs_data_t *obj)
switcher->functionNamesByPriority[6]);
obs_data_set_int(obj, "priority7",
switcher->functionNamesByPriority[7]);
obs_data_set_int(obj, "priority8",
switcher->functionNamesByPriority[8]);
obs_data_set_int(obj, "threadPriority", switcher->threadPriority);
@@ -378,11 +480,12 @@ void SwitcherData::saveGeneralSettings(obs_data_t *obj)
obs_data_set_int(obj, "timeTabPos", switcher->tabOrder[9]);
obs_data_set_int(obj, "idleTabPos", switcher->tabOrder[10]);
obs_data_set_int(obj, "sequenceTabPos", switcher->tabOrder[11]);
obs_data_set_int(obj, "audioTabPos", switcher->tabOrder[12]);
}
void SwitcherData::loadGeneralSettings(obs_data_t *obj)
{
obs_data_set_default_int(obj, "interval", DEFAULT_INTERVAL);
obs_data_set_default_int(obj, "interval", default_interval);
switcher->interval = obs_data_get_int(obj, "interval");
obs_data_set_default_int(obj, "switch_if_not_matching", NO_SWITCH);
@@ -406,16 +509,25 @@ void SwitcherData::loadGeneralSettings(obs_data_t *obj)
switcher->autoStopEnable = obs_data_get_bool(obj, "autoStopEnable");
switcher->autoStopScene = GetWeakSourceByName(autoStopScene.c_str());
switcher->verbose = obs_data_get_bool(obj, "verbose");
std::string autoStartScene =
obs_data_get_string(obj, "autoStartSceneName");
switcher->autoStartEnable = obs_data_get_bool(obj, "autoStartEnable");
switcher->autoStartType =
(AutoStartType)obs_data_get_int(obj, "autoStartType");
switcher->autoStartScene = GetWeakSourceByName(autoStartScene.c_str());
obs_data_set_default_int(obj, "priority0", DEFAULT_PRIORITY_0);
obs_data_set_default_int(obj, "priority1", DEFAULT_PRIORITY_1);
obs_data_set_default_int(obj, "priority2", DEFAULT_PRIORITY_2);
obs_data_set_default_int(obj, "priority3", DEFAULT_PRIORITY_3);
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->verbose = obs_data_get_bool(obj, "verbose");
switcher->disableHints = obs_data_get_bool(obj, "disableHints");
obs_data_set_default_int(obj, "priority0", default_priority_0);
obs_data_set_default_int(obj, "priority1", default_priority_1);
obs_data_set_default_int(obj, "priority2", default_priority_2);
obs_data_set_default_int(obj, "priority3", default_priority_3);
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);
obs_data_set_default_int(obj, "priority8", default_priority_8);
switcher->functionNamesByPriority[0] =
(obs_data_get_int(obj, "priority0"));
@@ -433,15 +545,18 @@ void SwitcherData::loadGeneralSettings(obs_data_t *obj)
(obs_data_get_int(obj, "priority6"));
switcher->functionNamesByPriority[7] =
(obs_data_get_int(obj, "priority7"));
switcher->functionNamesByPriority[8] =
(obs_data_get_int(obj, "priority8"));
if (!switcher->prioFuncsValid()) {
switcher->functionNamesByPriority[0] = (DEFAULT_PRIORITY_0);
switcher->functionNamesByPriority[1] = (DEFAULT_PRIORITY_1);
switcher->functionNamesByPriority[2] = (DEFAULT_PRIORITY_2);
switcher->functionNamesByPriority[3] = (DEFAULT_PRIORITY_3);
switcher->functionNamesByPriority[4] = (DEFAULT_PRIORITY_4);
switcher->functionNamesByPriority[5] = (DEFAULT_PRIORITY_5);
switcher->functionNamesByPriority[6] = (DEFAULT_PRIORITY_6);
switcher->functionNamesByPriority[7] = (DEFAULT_PRIORITY_7);
switcher->functionNamesByPriority[0] = (default_priority_0);
switcher->functionNamesByPriority[1] = (default_priority_1);
switcher->functionNamesByPriority[2] = (default_priority_2);
switcher->functionNamesByPriority[3] = (default_priority_3);
switcher->functionNamesByPriority[4] = (default_priority_4);
switcher->functionNamesByPriority[5] = (default_priority_5);
switcher->functionNamesByPriority[6] = (default_priority_6);
switcher->functionNamesByPriority[7] = (default_priority_7);
switcher->functionNamesByPriority[8] = (default_priority_8);
}
obs_data_set_default_int(obj, "threadPriority",
@@ -460,6 +575,7 @@ void SwitcherData::loadGeneralSettings(obs_data_t *obj)
obs_data_set_default_int(obj, "timeTabPos", 9);
obs_data_set_default_int(obj, "idleTabPos", 10);
obs_data_set_default_int(obj, "sequenceTabPos", 11);
obs_data_set_default_int(obj, "audioTabPos", 12);
switcher->tabOrder.emplace_back(
(int)(obs_data_get_int(obj, "generalTabPos")));
@@ -485,12 +601,15 @@ void SwitcherData::loadGeneralSettings(obs_data_t *obj)
(int)(obs_data_get_int(obj, "idleTabPos")));
switcher->tabOrder.emplace_back(
(int)(obs_data_get_int(obj, "sequenceTabPos")));
switcher->tabOrder.emplace_back(
(int)(obs_data_get_int(obj, "audioTabPos")));
}
void SceneSwitcher::setupGeneralTab()
void AdvSceneSwitcher::setupGeneralTab()
{
populateSceneSelection(ui->noMatchSwitchScene, false);
populateSceneSelection(ui->autoStopScenes, false);
populateSceneSelection(ui->autoStartScenes, false);
if (switcher->switchIfNotMatching == SWITCH) {
ui->noMatchSwitch->setChecked(true);
@@ -516,35 +635,56 @@ void SceneSwitcher::setupGeneralTab()
ui->autoStopScenes->setDisabled(true);
}
ui->autoStartType->addItem("Recording");
ui->autoStartType->addItem("Streaming");
ui->autoStartType->addItem("Recording and Streaming");
ui->autoStartSceneCheckBox->setChecked(switcher->autoStartEnable);
ui->autoStartScenes->setCurrentText(
GetWeakSourceName(switcher->autoStartScene).c_str());
ui->autoStartType->setCurrentIndex(switcher->autoStartType);
if (ui->autoStartSceneCheckBox->checkState()) {
ui->autoStartScenes->setDisabled(false);
ui->autoStartType->setDisabled(false);
} else {
ui->autoStartScenes->setDisabled(true);
ui->autoStartType->setDisabled(true);
}
ui->verboseLogging->setChecked(switcher->verbose);
ui->uiHintsDisable->setChecked(switcher->disableHints);
for (int p : switcher->functionNamesByPriority) {
std::string s = "";
switch (p) {
case READ_FILE_FUNC:
case read_file_func:
s = "File Content";
break;
case ROUND_TRIP_FUNC:
case round_trip_func:
s = "Scene Sequence";
break;
case IDLE_FUNC:
case idle_func:
s = "Idle Detection";
break;
case EXE_FUNC:
case exe_func:
s = "Executable";
break;
case SCREEN_REGION_FUNC:
case screen_region_func:
s = "Screen Region";
break;
case WINDOW_TITLE_FUNC:
case window_title_func:
s = "Window Title";
break;
case MEDIA_FUNC:
case media_func:
s = "Media";
break;
case TIME_FUNC:
case time_func:
s = "Time";
break;
case audio_func:
s = "Audio";
break;
}
QString text(s.c_str());
QListWidgetItem *item =
@@ -552,7 +692,7 @@ void SceneSwitcher::setupGeneralTab()
item->setData(Qt::UserRole, text);
}
for (int i = 0; i < switcher->threadPriorities.size(); ++i) {
for (int i = 0; i < (int)switcher->threadPriorities.size(); ++i) {
ui->threadPriority->addItem(
switcher->threadPriorities[i].name.c_str());
ui->threadPriority->setItemData(

View File

@@ -1,9 +1,4 @@
#pragma once
#include <QDialog>
#include <memory>
#include <vector>
#include <string>
#ifdef BUILD_OUT_OF_TREE
#include "../../forms/ui_advanced-scene-switcher.h"
#else
@@ -11,48 +6,39 @@
#endif
#include "switcher-data-structs.hpp"
#define blog(level, msg, ...) blog(level, "[adv-ss] " msg, ##__VA_ARGS__)
class QCloseEvent;
/*******************************************************************************
* Advanced Scene Switcher window
*******************************************************************************/
class SceneSwitcher : public QDialog {
class AdvSceneSwitcher : public QDialog {
Q_OBJECT
public:
std::unique_ptr<Ui_SceneSwitcher> ui;
std::unique_ptr<Ui_AdvSceneSwitcher> ui;
bool loading = true;
SceneSwitcher(QWidget *parent);
AdvSceneSwitcher(QWidget *parent);
void closeEvent(QCloseEvent *event) override;
void SetStarted();
void SetStopped();
int FindByData(const QString &window);
int ScreenRegionFindByData(const QString &region);
int PauseScenesFindByData(const QString &scene);
int PauseWindowsFindByData(const QString &window);
int IgnoreWindowsFindByData(const QString &window);
int SceneRoundTripFindByData(const QString &scene1);
int SceneTransitionsFindByData(const QString &scene1,
const QString &scene2);
int DefaultTransitionsFindByData(const QString &scene);
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);
void UpdateAutoStartScene(const QString &name);
void UpdateIdleDataTransition(const QString &name);
void UpdateIdleDataScene(const QString &name);
void loadUI();
void populateSceneSelection(QComboBox *sel, bool addPrevious);
void populateTransitionSelection(QComboBox *sel);
void populateWindowSelection(QComboBox *sel);
void setupGeneralTab();
void setupTitleTab();
void setupExecutableTab();
@@ -65,14 +51,34 @@ public:
void setupMediaTab();
void setupFileTab();
void setupTimeTab();
void setupAudioTab();
void setTabOrder();
static void populateSceneSelection(QComboBox *sel,
bool addPrevious = false,
bool addSelect = true);
static void populateTransitionSelection(QComboBox *sel,
bool addSelect = true);
static void populateWindowSelection(QComboBox *sel,
bool addSelect = true);
static void populateAudioSelection(QComboBox *sel,
bool addSelect = true);
static void populateMediaSelection(QComboBox *sel,
bool addSelect = true);
static void populateProcessSelection(QComboBox *sel,
bool addSelect = true);
QMetaObject::Connection PulseWidget(QWidget *widget, QColor endColor,
QColor = QColor(0, 0, 0, 0),
QString specifier = "QLabel ");
bool listMoveUp(QListWidget *list);
bool listMoveDown(QListWidget *list);
public slots:
void on_switches_currentRowChanged(int idx);
void on_up_clicked();
void on_down_clicked();
void on_add_clicked();
void on_remove_clicked();
void on_windowUp_clicked();
void on_windowDown_clicked();
void on_windowAdd_clicked();
void on_windowRemove_clicked();
void on_noMatchDontSwitch_clicked();
void on_noMatchSwitch_clicked();
void on_noMatchRandomSwitch_clicked();
@@ -82,7 +88,8 @@ public slots:
void on_toggleStartButton_clicked();
void on_tabMoved(int from, int to);
void on_screenRegions_currentRowChanged(int idx);
void on_screenRegionSwitches_currentRowChanged(int idx);
void on_showFrame_clicked();
void on_screenRegionAdd_clicked();
void on_screenRegionRemove_clicked();
void on_screenRegionUp_clicked();
@@ -100,29 +107,34 @@ public slots:
void on_ignoreWindowsAdd_clicked();
void on_ignoreWindowsRemove_clicked();
void on_sceneRoundTrips_currentRowChanged(int idx);
void on_sceneRoundTripAdd_clicked();
void on_sceneRoundTripRemove_clicked();
void on_sceneRoundTripSave_clicked();
void on_sceneRoundTripLoad_clicked();
void on_sceneRoundTripUp_clicked();
void on_sceneRoundTripDown_clicked();
void on_sceneRoundTripDelayUnits_currentIndexChanged(int index);
void on_sceneSequenceAdd_clicked();
void on_sceneSequenceRemove_clicked();
void on_sceneSequenceSave_clicked();
void on_sceneSequenceLoad_clicked();
void on_sceneSequenceUp_clicked();
void on_sceneSequenceDown_clicked();
void on_autoStopSceneCheckBox_stateChanged(int state);
void on_autoStopScenes_currentTextChanged(const QString &text);
void on_autoStartSceneCheckBox_stateChanged(int state);
void on_autoStartType_currentIndexChanged(int index);
void on_autoStartScenes_currentTextChanged(const QString &text);
void on_verboseLogging_stateChanged(int state);
void on_uiHintsDisable_stateChanged(int state);
void on_exportSettings_clicked();
void on_importSettings_clicked();
void on_sceneTransitions_currentRowChanged(int idx);
void on_transitionsAdd_clicked();
void on_transitionsRemove_clicked();
void on_defaultTransitions_currentRowChanged(int idx);
void on_transitionsUp_clicked();
void on_transitionsDown_clicked();
void on_defaultTransitionsAdd_clicked();
void on_defaultTransitionsRemove_clicked();
void on_defaultTransitionsUp_clicked();
void on_defaultTransitionsDown_clicked();
void on_transitionOverridecheckBox_stateChanged(int state);
void on_browseButton_clicked();
@@ -136,7 +148,6 @@ public slots:
void on_executableDown_clicked();
void on_executableAdd_clicked();
void on_executableRemove_clicked();
void on_executables_currentRowChanged(int idx);
void on_idleCheckBox_stateChanged(int state);
void on_idleTransitions_currentTextChanged(const QString &text);
@@ -148,7 +159,6 @@ public slots:
void on_randomAdd_clicked();
void on_randomRemove_clicked();
void on_randomScenesList_currentRowChanged(int idx);
void on_fileAdd_clicked();
void on_fileRemove_clicked();
@@ -157,16 +167,20 @@ public slots:
void on_fileUp_clicked();
void on_fileDown_clicked();
void on_mediaSwitches_currentRowChanged(int idx);
void on_mediaAdd_clicked();
void on_mediaRemove_clicked();
void on_mediaUp_clicked();
void on_mediaDown_clicked();
void on_mediaTimeRestrictions_currentIndexChanged(int idx);
void on_timeSwitches_currentRowChanged(int idx);
void on_timeAdd_clicked();
void on_timeRemove_clicked();
void on_timeUp_clicked();
void on_timeDown_clicked();
void on_audioAdd_clicked();
void on_audioRemove_clicked();
void on_audioUp_clicked();
void on_audioDown_clicked();
void on_priorityUp_clicked();
void on_priorityDown_clicked();
@@ -175,6 +189,8 @@ public slots:
void updateScreenRegionCursorPos();
void on_close_clicked();
private:
};
/********************************************************************************
@@ -184,6 +200,7 @@ void GetWindowList(std::vector<std::string> &windows);
void GetWindowList(QStringList &windows); // Overloaded
void GetCurrentWindowTitle(std::string &title);
bool isFullscreen(std::string &title);
bool isMaximized(std::string &title);
/********************************************************************************
* Screenregion helper
@@ -223,22 +240,6 @@ void switchScene(OBSWeakSource &scene, OBSWeakSource &transition,
bool &transitionOverrideOverride,
std::unique_lock<std::mutex> &lock);
/********************************************************************************
* Hotkey helper
********************************************************************************/
#define TOGGLE_HOTKEY_PATH "hotkey_toggle.txt"
#define STOP_HOTKEY_PATH "hotkey_stop.txt"
#define START_HOTKEY_PATH "hotkey_start.txt"
void stopHotkeyFunc(void *data, obs_hotkey_id id, obs_hotkey_t *hotkey,
bool pressed);
void startHotkeyFunc(void *data, obs_hotkey_id id, obs_hotkey_t *hotkey,
bool pressed);
void startStopToggleHotkeyFunc(void *data, obs_hotkey_id id,
obs_hotkey_t *hotkey, bool pressed);
void loadKeybinding(obs_hotkey_id hotkeyId, std::string path);
/********************************************************************************
* Main SwitcherData
********************************************************************************/

View File

@@ -0,0 +1,26 @@
#pragma once
#include <curl/curl.h>
#include <QLibrary>
#if defined(WIN32)
constexpr auto curl_library_name = "libcurl.dll";
#elif __APPLE__
#define curl_library_name "libcurl.4.dylib"
#else
#define curl_library_name "libcurl.so.4"
#endif
typedef CURL *(*initFunction)(void);
typedef CURLcode (*setOptFunction)(CURL *, CURLoption, ...);
typedef CURLcode (*performFunction)(CURL *);
typedef void (*cleanupFunction)(CURL *);
extern initFunction f_curl_init;
extern setOptFunction f_curl_setopt;
extern performFunction f_curl_perform;
extern cleanupFunction f_curl_cleanup;
extern QLibrary *loaded_curl_lib;
bool resolveCurl();
bool loadCurl();

View File

@@ -0,0 +1,65 @@
#pragma once
#include <QSpinBox>
#include "switch-generic.hpp"
#include "volume-control.hpp"
constexpr auto audio_func = 8;
constexpr auto default_priority_8 = audio_func;
struct AudioSwitch : virtual SceneSwitcherEntry {
OBSWeakSource audioSource = nullptr;
int volumeThreshold = 0;
float peak = -1;
obs_volmeter_t *volmeter = nullptr;
const char *getType() { return "audio"; }
bool initialized();
bool valid();
static void setVolumeLevel(void *data,
const float magnitude[MAX_AUDIO_CHANNELS],
const float peak[MAX_AUDIO_CHANNELS],
const float inputPeak[MAX_AUDIO_CHANNELS]);
void resetVolmeter();
AudioSwitch(){};
AudioSwitch(OBSWeakSource scene_, OBSWeakSource transition_,
OBSWeakSource audioSource_, int volumeThreshold_,
bool usePreviousScene_);
AudioSwitch(const AudioSwitch &other);
AudioSwitch(AudioSwitch &&other);
~AudioSwitch();
AudioSwitch &operator=(const AudioSwitch &other);
AudioSwitch &operator=(AudioSwitch &&other) noexcept;
friend void swap(AudioSwitch &first, AudioSwitch &second);
};
class AudioSwitchWidget : public SwitchWidget {
Q_OBJECT
public:
AudioSwitchWidget(AudioSwitch *s);
void UpdateVolmeterSource();
AudioSwitch *getSwitchData();
void setSwitchData(AudioSwitch *s);
static void swapSwitchData(AudioSwitchWidget *as1,
AudioSwitchWidget *as2);
private slots:
void SourceChanged(const QString &text);
void VolumeThresholdChanged(int vol);
private:
QComboBox *audioSources;
QSpinBox *audioVolumeThreshold;
VolControl *volMeter;
QLabel *whenLabel;
QLabel *aboveLabel;
QLabel *switchLabel;
QLabel *usingLabel;
AudioSwitch *switchData;
};

View File

@@ -0,0 +1,48 @@
#pragma once
#include <QCheckBox>
#include "switch-generic.hpp"
constexpr auto exe_func = 3;
constexpr auto default_priority_3 = exe_func;
struct ExecutableSwitch : SceneSwitcherEntry {
QString exe = "";
bool inFocus = false;
const char *getType() { return "exec"; }
inline ExecutableSwitch(){};
inline ExecutableSwitch(OBSWeakSource scene_, OBSWeakSource transition_,
const QString &exe_, bool inFocus_)
: SceneSwitcherEntry(scene_, transition_),
exe(exe_),
inFocus(inFocus_)
{
}
};
class ExecutableSwitchWidget : public SwitchWidget {
Q_OBJECT
public:
ExecutableSwitchWidget(ExecutableSwitch *s);
ExecutableSwitch *getSwitchData();
void setSwitchData(ExecutableSwitch *s);
static void swapSwitchData(ExecutableSwitchWidget *s1,
ExecutableSwitchWidget *s2);
private slots:
void ProcessChanged(const QString &text);
void FocusChanged(int state);
private:
QComboBox *processes;
QCheckBox *requiresFocus;
QLabel *whenLabel;
QLabel *switchLabel;
QLabel *usingLabel;
ExecutableSwitch *switchData;
};

View File

@@ -0,0 +1,42 @@
#pragma once
#include "switch-generic.hpp"
constexpr auto read_file_func = 0;
constexpr auto default_priority_0 = read_file_func;
struct FileSwitch : SceneSwitcherEntry {
std::string file;
std::string text;
bool remote = false;
bool useRegex = false;
bool useTime = false;
QDateTime lastMod;
const char *getType() { return "file"; }
inline FileSwitch(OBSWeakSource scene_, OBSWeakSource transition_,
const char *file_, const char *text_, bool remote_,
bool useRegex_, bool useTime_)
: SceneSwitcherEntry(scene_, transition_),
file(file_),
text(text_),
remote(remote_),
useRegex(useRegex_),
useTime(useTime_),
lastMod()
{
}
};
struct FileIOData {
bool readEnabled = false;
std::string readPath;
bool writeEnabled = false;
std::string writePath;
};
static inline QString MakeFileSwitchName(const QString &scene,
const QString &transition,
const QString &fileName,
const QString &text, bool useRegex,
bool useTime);

View File

@@ -0,0 +1,50 @@
#pragma once
#include <obs.hpp>
#include <QComboBox>
struct SceneSwitcherEntry {
OBSWeakSource scene = nullptr;
OBSWeakSource transition = nullptr;
bool usePreviousScene = false;
virtual const char *getType() = 0;
virtual bool initialized();
virtual bool valid();
virtual void logMatch();
inline SceneSwitcherEntry() {}
inline SceneSwitcherEntry(OBSWeakSource scene_,
OBSWeakSource transition_,
bool usePreviousScene_ = false)
: scene(scene_),
transition(transition_),
usePreviousScene(usePreviousScene_)
{
}
virtual ~SceneSwitcherEntry() {}
};
class SwitchWidget : public QWidget {
Q_OBJECT
public:
SwitchWidget(SceneSwitcherEntry *s, bool usePreviousScene = true);
virtual SceneSwitcherEntry *getSwitchData();
virtual void setSwitchData(SceneSwitcherEntry *s);
static void swapSwitchData(SwitchWidget *s1, SwitchWidget *s2);
private slots:
void SceneChanged(const QString &text);
void TransitionChanged(const QString &text);
protected:
bool loading = true;
QComboBox *scenes;
QComboBox *transitions;
SceneSwitcherEntry *switchData;
};

View File

@@ -0,0 +1,14 @@
#pragma once
#include "switch-generic.hpp"
constexpr auto default_idle_time = 60;
constexpr auto idle_func = 2;
constexpr auto default_priority_2 = idle_func;
struct IdleData : SceneSwitcherEntry {
bool idleEnable = false;
int time = default_idle_time;
bool alreadySwitched = false;
const char *getType() { return "idle"; }
};

View File

@@ -0,0 +1,79 @@
#pragma once
#include "switch-generic.hpp"
constexpr auto media_func = 6;
constexpr auto default_priority_6 = media_func;
typedef enum {
TIME_RESTRICTION_NONE,
TIME_RESTRICTION_SHORTER,
TIME_RESTRICTION_LONGER,
TIME_RESTRICTION_REMAINING_SHORTER,
TIME_RESTRICTION_REMAINING_LONGER,
} time_restriction;
struct MediaSwitch : SceneSwitcherEntry {
OBSWeakSource source = nullptr;
obs_media_state state = OBS_MEDIA_STATE_NONE;
bool anyState = false;
time_restriction restriction = TIME_RESTRICTION_NONE;
int64_t time = 0;
std::atomic<bool> stopped = false;
std::atomic<bool> ended = false;
// workaround to enable use of "ended" to specify end of VLC playlist
bool previousStateEnded = false;
const char *getType() { return "media"; }
bool initialized();
bool valid();
void clearSignalHandler();
void resetSignalHandler();
static void MediaStopped(void *data, calldata_t *);
static void MediaEnded(void *data, calldata_t *);
inline MediaSwitch(){};
inline MediaSwitch(OBSWeakSource scene_, OBSWeakSource source_,
OBSWeakSource transition_, obs_media_state state_,
time_restriction restriction_, uint64_t time_,
bool usePreviousScene_);
MediaSwitch(const MediaSwitch &other);
MediaSwitch(MediaSwitch &&other);
~MediaSwitch();
MediaSwitch &operator=(const MediaSwitch &other);
MediaSwitch &operator=(MediaSwitch &&other) noexcept;
friend void swap(MediaSwitch &first, MediaSwitch &second);
};
class MediaSwitchWidget : public SwitchWidget {
Q_OBJECT
public:
MediaSwitchWidget(MediaSwitch *s);
MediaSwitch *getSwitchData();
void setSwitchData(MediaSwitch *s);
static void swapSwitchData(MediaSwitchWidget *s1,
MediaSwitchWidget *s2);
private slots:
void SourceChanged(const QString &text);
void StateChanged(int index);
void TimeRestrictionChanged(int index);
void TimeChanged(int time);
private:
QComboBox *meidaSources;
QComboBox *states;
QComboBox *timeRestrictions;
QSpinBox *time;
QLabel *whenLabel;
QLabel *stateLabel;
QLabel *andLabel;
QLabel *switchLabel;
QLabel *usingLabel;
MediaSwitch *switchData;
};

View File

@@ -0,0 +1,39 @@
#pragma once
#include "switch-generic.hpp"
struct RandomSwitch : SceneSwitcherEntry {
double delay = 0.0;
const char *getType() { return "random"; }
inline RandomSwitch() {}
inline RandomSwitch(OBSWeakSource scene_, OBSWeakSource transition_,
double delay_)
: SceneSwitcherEntry(scene_, transition_), delay(delay_)
{
}
};
class RandomSwitchWidget : public SwitchWidget {
Q_OBJECT
public:
RandomSwitchWidget(RandomSwitch *s);
RandomSwitch *getSwitchData();
void setSwitchData(RandomSwitch *s);
static void swapSwitchData(RandomSwitchWidget *s1,
RandomSwitchWidget *s2);
private slots:
void DelayChanged(double d);
private:
QDoubleSpinBox *delay;
QLabel *switchLabel;
QLabel *usingLabel;
QLabel *forLabel;
RandomSwitch *switchData;
};

View File

@@ -0,0 +1,62 @@
#pragma once
#include <QSpinBox>
#include "switch-generic.hpp"
constexpr auto screen_region_func = 4;
constexpr auto default_priority_4 = screen_region_func;
struct ScreenRegionSwitch : SceneSwitcherEntry {
int minX = 0, minY = 0, maxX = 0, maxY = 0;
const char *getType() { return "region"; }
inline ScreenRegionSwitch(){};
inline ScreenRegionSwitch(OBSWeakSource scene_,
OBSWeakSource transition_, int minX_,
int minY_, int maxX_, int maxY_)
: SceneSwitcherEntry(scene_, transition_),
minX(minX_),
minY(minY_),
maxX(maxX_),
maxY(maxY_)
{
}
};
class ScreenRegionWidget : public SwitchWidget {
Q_OBJECT
public:
ScreenRegionWidget(ScreenRegionSwitch *s);
ScreenRegionSwitch *getSwitchData();
void setSwitchData(ScreenRegionSwitch *s);
static void swapSwitchData(ScreenRegionWidget *s1,
ScreenRegionWidget *s2);
void showFrame();
void hideFrame();
private slots:
void MinXChanged(int pos);
void MinYChanged(int pos);
void MaxXChanged(int pos);
void MaxYChanged(int pos);
private:
QSpinBox *minX;
QSpinBox *minY;
QSpinBox *maxX;
QSpinBox *maxY;
QFrame helperFrame;
QLabel *cursorLabel;
QLabel *xLabel;
QLabel *switchLabel;
QLabel *usingLabel;
ScreenRegionSwitch *switchData;
void drawFrame();
};

View File

@@ -0,0 +1,64 @@
#pragma once
#include "switch-generic.hpp"
constexpr auto round_trip_func = 1;
constexpr auto default_priority_1 = round_trip_func;
typedef enum {
SECONDS,
MINUTES,
HOURS,
} delay_units;
struct SceneSequenceSwitch : SceneSwitcherEntry {
OBSWeakSource startScene = nullptr;
double delay = 0;
int delayMultiplier = 1;
const char *getType() { return "sequence"; }
bool initialized();
bool valid();
void logSleep(int dur);
inline SceneSequenceSwitch(){};
inline SceneSequenceSwitch(OBSWeakSource startScene_,
OBSWeakSource scene_,
OBSWeakSource transition_, double delay_,
int delayMultiplier_, bool usePreviousScene_)
: SceneSwitcherEntry(scene_, transition_, usePreviousScene_),
startScene(startScene_),
delay(delay_),
delayMultiplier(delayMultiplier_)
{
}
};
class SequenceWidget : public SwitchWidget {
Q_OBJECT
public:
SequenceWidget(SceneSequenceSwitch *s);
SceneSequenceSwitch *getSwitchData();
void setSwitchData(SceneSequenceSwitch *s);
static void swapSwitchData(SequenceWidget *s1, SequenceWidget *s2);
void UpdateDelay();
private slots:
void DelayChanged(double delay);
void DelayUnitsChanged(int idx);
void StartSceneChanged(const QString &text);
private:
QDoubleSpinBox *delay;
QComboBox *delayUnits;
QComboBox *startScenes;
QLabel *whenLabel;
QLabel *switchLabel;
QLabel *afterLabel;
QLabel *usingLabel;
SceneSequenceSwitch *switchData;
};

View File

@@ -0,0 +1,61 @@
#pragma once
#include <QTimeEdit>
#include "switch-generic.hpp"
constexpr auto time_func = 7;
constexpr auto default_priority_7 = time_func;
typedef enum {
ANY_DAY = 0,
MONDAY = 1,
TUSEDAY = 2,
WEDNESDAY = 3,
THURSDAY = 4,
FRIDAY = 5,
SATURDAY = 6,
SUNDAY = 7,
LIVE = 8
} timeTrigger;
struct TimeSwitch : SceneSwitcherEntry {
timeTrigger trigger = ANY_DAY;
QTime time = QTime();
const char *getType() { return "time"; }
inline TimeSwitch(){};
inline TimeSwitch(OBSWeakSource scene_, OBSWeakSource transition_,
timeTrigger trigger_, QTime time_,
bool usePreviousScene_)
: SceneSwitcherEntry(scene_, transition_, usePreviousScene_),
trigger(trigger_),
time(time_)
{
}
};
class TimeSwitchWidget : public SwitchWidget {
Q_OBJECT
public:
TimeSwitchWidget(TimeSwitch *s);
TimeSwitch *getSwitchData();
void setSwitchData(TimeSwitch *s);
static void swapSwitchData(TimeSwitchWidget *s1, TimeSwitchWidget *s2);
private slots:
void TriggerChanged(int index);
void TimeChanged(const QTime &time);
private:
QComboBox *triggers;
QTimeEdit *time;
QLabel *atLabel;
QLabel *switchLabel;
QLabel *usingLabel;
TimeSwitch *switchData;
};

View File

@@ -0,0 +1,71 @@
#pragma once
#include "switch-generic.hpp"
struct SceneTransition : SceneSwitcherEntry {
OBSWeakSource scene2 = nullptr;
const char *getType() { return "transition"; }
bool initialized();
bool valid();
inline SceneTransition(){};
inline SceneTransition(OBSWeakSource scene_, OBSWeakSource scene2_,
OBSWeakSource transition_)
: SceneSwitcherEntry(scene_, transition_), scene2(scene2_)
{
}
};
struct DefaultSceneTransition : SceneSwitcherEntry {
const char *getType() { return "def_transition"; }
inline DefaultSceneTransition(){};
inline DefaultSceneTransition(OBSWeakSource scene_,
OBSWeakSource transition_)
: SceneSwitcherEntry(scene_, transition_)
{
}
};
class TransitionSwitchWidget : public SwitchWidget {
Q_OBJECT
public:
TransitionSwitchWidget(SceneTransition *s);
SceneTransition *getSwitchData();
void setSwitchData(SceneTransition *s);
static void swapSwitchData(TransitionSwitchWidget *s1,
TransitionSwitchWidget *s2);
private slots:
void Scene2Changed(const QString &text);
private:
QComboBox *scenes2;
QLabel *switchLabel;
QLabel *toLabel;
QLabel *usingLabel;
SceneTransition *switchData;
};
class DefTransitionSwitchWidget : public SwitchWidget {
Q_OBJECT
public:
DefTransitionSwitchWidget(DefaultSceneTransition *s);
DefaultSceneTransition *getSwitchData();
void setSwitchData(DefaultSceneTransition *s);
static void swapSwitchData(DefTransitionSwitchWidget *s1,
DefTransitionSwitchWidget *s2);
private:
QLabel *whenLabel;
QLabel *switchLabel;
DefaultSceneTransition *switchData;
};

View File

@@ -0,0 +1,52 @@
#pragma once
#include "switch-generic.hpp"
constexpr auto window_title_func = 5;
constexpr auto default_priority_5 = window_title_func;
struct WindowSwitch : SceneSwitcherEntry {
std::string window = "";
bool fullscreen = false;
bool maximized = false;
bool focus = true;
const char *getType() { return "window"; }
inline WindowSwitch() {}
inline WindowSwitch(OBSWeakSource scene_, const char *window_,
OBSWeakSource transition_, bool fullscreen_,
bool maximized_, bool focus_)
: SceneSwitcherEntry(scene_, transition_),
window(window_),
fullscreen(fullscreen_),
maximized(maximized_),
focus(focus_)
{
}
};
class WindowSwitchWidget : public SwitchWidget {
Q_OBJECT
public:
WindowSwitchWidget(WindowSwitch *s);
WindowSwitch *getSwitchData();
void setSwitchData(WindowSwitch *s);
static void swapSwitchData(WindowSwitchWidget *s1,
WindowSwitchWidget *s2);
private slots:
void WindowChanged(const QString &text);
void FullscreenChanged(int state);
void MaximizedChanged(int state);
void FocusChanged(int state);
private:
QComboBox *windows;
QCheckBox *fullscreen;
QCheckBox *maximized;
QCheckBox *focused;
WindowSwitch *switchData;
};

View File

@@ -1,259 +1,34 @@
#pragma once
#include <condition_variable>
#include <chrono>
#include <string>
#include <vector>
#include <regex>
#include <deque>
#include <mutex>
#include <fstream>
#include <QDateTime>
#include <QThread>
#include "utility.hpp"
#include <curl/curl.h>
#define DEFAULT_INTERVAL 300
#include "switch-audio.hpp"
#include "switch-executable.hpp"
#include "switch-file.hpp"
#include "switch-idle.hpp"
#include "switch-media.hpp"
#include "switch-random.hpp"
#include "switch-screen-region.hpp"
#include "switch-time.hpp"
#include "switch-transitions.hpp"
#include "switch-window.hpp"
#include "switch-sequence.hpp"
#define DEFAULT_IDLE_TIME 60
#define PREVIOUS_SCENE_NAME "Previous Scene"
#define READ_FILE_FUNC 0
#define ROUND_TRIP_FUNC 1
#define IDLE_FUNC 2
#define EXE_FUNC 3
#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
#define DEFAULT_PRIORITY_2 IDLE_FUNC
#define DEFAULT_PRIORITY_3 EXE_FUNC
#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
/********************************************************************************
* Data structs for each scene switching method
********************************************************************************/
struct WindowSceneSwitch {
OBSWeakSource scene;
std::string window;
OBSWeakSource transition;
bool fullscreen;
bool focus;
inline WindowSceneSwitch(OBSWeakSource scene_, const char *window_,
OBSWeakSource transition_, bool fullscreen_,
bool focus_)
: scene(scene_),
window(window_),
transition(transition_),
fullscreen(fullscreen_),
focus(focus_)
{
}
};
struct ExecutableSceneSwitch {
OBSWeakSource mScene;
OBSWeakSource mTransition;
QString mExe;
bool mInFocus;
inline ExecutableSceneSwitch(OBSWeakSource scene,
OBSWeakSource transition,
const QString &exe, bool inFocus)
: mScene(scene),
mTransition(transition),
mExe(exe),
mInFocus(inFocus)
{
}
};
struct ScreenRegionSwitch {
OBSWeakSource scene;
OBSWeakSource transition;
int minX, minY, maxX, maxY;
std::string regionStr;
inline ScreenRegionSwitch(OBSWeakSource scene_,
OBSWeakSource transition_, int minX_,
int minY_, int maxX_, int maxY_,
std::string regionStr_)
: scene(scene_),
transition(transition_),
minX(minX_),
minY(minY_),
maxX(maxX_),
maxY(maxY_),
regionStr(regionStr_)
{
}
};
struct SceneRoundTripSwitch {
OBSWeakSource scene1;
OBSWeakSource scene2;
OBSWeakSource transition;
double delay;
bool usePreviousScene;
std::string sceneRoundTripStr;
inline SceneRoundTripSwitch(OBSWeakSource scene1_,
OBSWeakSource scene2_,
OBSWeakSource transition_, double delay_,
bool usePreviousScene_, std::string str)
: scene1(scene1_),
scene2(scene2_),
transition(transition_),
delay(delay_),
usePreviousScene(usePreviousScene_),
sceneRoundTripStr(str)
{
}
};
struct RandomSwitch {
OBSWeakSource scene;
OBSWeakSource transition;
double delay;
std::string randomSwitchStr;
inline RandomSwitch(OBSWeakSource scene_, OBSWeakSource transition_,
double delay_, std::string str)
: scene(scene_),
transition(transition_),
delay(delay_),
randomSwitchStr(str)
{
}
};
struct SceneTransition {
OBSWeakSource scene1;
OBSWeakSource scene2;
OBSWeakSource transition;
std::string sceneTransitionStr;
inline SceneTransition(OBSWeakSource scene1_, OBSWeakSource scene2_,
OBSWeakSource transition_,
std::string sceneTransitionStr_)
: scene1(scene1_),
scene2(scene2_),
transition(transition_),
sceneTransitionStr(sceneTransitionStr_)
{
}
};
struct DefaultSceneTransition {
OBSWeakSource scene;
OBSWeakSource transition;
std::string sceneTransitionStr;
inline DefaultSceneTransition(OBSWeakSource scene_,
OBSWeakSource transition_,
std::string sceneTransitionStr_)
: scene(scene_),
transition(transition_),
sceneTransitionStr(sceneTransitionStr_)
{
}
};
struct FileSwitch {
OBSWeakSource scene;
OBSWeakSource transition;
std::string file;
std::string text;
bool remote = false;
bool useRegex = false;
bool useTime = false;
QDateTime lastMod;
inline FileSwitch(OBSWeakSource scene_, OBSWeakSource transition_,
const char *file_, const char *text_, bool remote_,
bool useRegex_, bool useTime_)
: scene(scene_),
transition(transition_),
file(file_),
text(text_),
remote(remote_),
useRegex(useRegex_),
useTime(useTime_),
lastMod()
{
}
};
struct FileIOData {
bool readEnabled = false;
std::string readPath;
bool writeEnabled = false;
std::string writePath;
};
struct IdleData {
bool idleEnable = false;
int time = DEFAULT_IDLE_TIME;
OBSWeakSource scene;
OBSWeakSource transition;
bool usePreviousScene;
bool alreadySwitched = false;
};
struct MediaSwitch {
OBSWeakSource scene;
OBSWeakSource source;
OBSWeakSource transition;
obs_media_state state;
int64_t time;
time_restriction restriction;
bool matched;
bool usePreviousScene;
std::string mediaSwitchStr;
inline MediaSwitch(OBSWeakSource scene_, OBSWeakSource source_,
OBSWeakSource transition_, obs_media_state state_,
time_restriction restriction_, uint64_t time_,
bool usePreviousScene_, std::string mediaSwitchStr_)
: scene(scene_),
source(source_),
transition(transition_),
state(state_),
restriction(restriction_),
time(time_),
usePreviousScene(usePreviousScene_),
mediaSwitchStr(mediaSwitchStr_)
{
}
};
struct TimeSwitch {
OBSWeakSource scene;
OBSWeakSource transition;
QTime time;
bool matched;
bool usePreviousScene;
std::string timeSwitchStr;
inline TimeSwitch(OBSWeakSource scene_, OBSWeakSource transition_,
QTime time_, bool usePreviousScene_,
std::string timeSwitchStr_)
: scene(scene_),
transition(transition_),
time(time_),
usePreviousScene(usePreviousScene_),
timeSwitchStr(timeSwitchStr_)
{
}
};
constexpr auto default_interval = 300;
constexpr auto previous_scene_name = "Previous Scene";
typedef enum { NO_SWITCH = 0, SWITCH = 1, RANDOM_SWITCH = 2 } NoMatch;
typedef enum { PERSIST = 0, START = 1, STOP = 2 } StartupBehavior;
typedef enum {
RECORDING = 0,
STREAMING = 1,
RECORINDGSTREAMING = 2
} AutoStartType;
class SwitcherThread;
@@ -270,11 +45,13 @@ struct SwitcherData {
std::condition_variable transitionCv;
bool stop = false;
bool verbose = false;
bool disableHints = false;
bool showFrame = false;
bool tansitionOverrideOverride = false;
int interval = DEFAULT_INTERVAL;
int interval = default_interval;
obs_source_t *waitScene = NULL; //scene during which wait started
obs_source_t *waitScene = NULL;
OBSWeakSource previousScene = NULL;
OBSWeakSource PreviousScene2 = NULL;
OBSWeakSource lastRandomScene;
@@ -282,11 +59,11 @@ struct SwitcherData {
NoMatch switchIfNotMatching = NO_SWITCH;
StartupBehavior startupBehavior = PERSIST;
std::vector<WindowSceneSwitch> windowSwitches;
std::deque<WindowSwitch> windowSwitches;
std::vector<std::string> ignoreIdleWindows;
std::string lastTitle;
std::vector<ScreenRegionSwitch> screenRegionSwitches;
std::deque<ScreenRegionSwitch> screenRegionSwitches;
std::vector<OBSWeakSource> pauseScenesSwitches;
@@ -294,31 +71,41 @@ struct SwitcherData {
std::vector<std::string> ignoreWindowsSwitches;
std::vector<SceneRoundTripSwitch> sceneRoundTripSwitches;
int sceneRoundTripUnitMultiplier = 1;
std::deque<SceneSequenceSwitch> sceneSequenceSwitches;
std::vector<RandomSwitch> randomSwitches;
std::deque<RandomSwitch> randomSwitches;
IdleData idleData;
FileIOData fileIO;
IdleData idleData;
std::vector<FileSwitch> fileSwitches;
CURL *curl = nullptr;
std::vector<ExecutableSceneSwitch> executableSwitches;
std::deque<ExecutableSwitch> executableSwitches;
bool autoStopEnable = false;
OBSWeakSource autoStopScene;
std::vector<SceneTransition> sceneTransitions;
std::vector<DefaultSceneTransition> defaultSceneTransitions;
bool autoStartEnable = false;
AutoStartType autoStartType = RECORDING;
OBSWeakSource autoStartScene;
bool autoStartedRecently = false;
std::vector<MediaSwitch> mediaSwitches;
std::deque<SceneTransition> sceneTransitions;
std::deque<DefaultSceneTransition> defaultSceneTransitions;
bool changedDefTransitionRecently = false;
std::vector<TimeSwitch> timeSwitches;
std::deque<MediaSwitch> mediaSwitches;
std::deque<TimeSwitch> timeSwitches;
QDateTime liveTime;
std::deque<AudioSwitch> audioSwitches;
std::vector<int> functionNamesByPriority = std::vector<int>{
DEFAULT_PRIORITY_0, DEFAULT_PRIORITY_1, DEFAULT_PRIORITY_2,
DEFAULT_PRIORITY_3, DEFAULT_PRIORITY_4, DEFAULT_PRIORITY_5,
DEFAULT_PRIORITY_6, DEFAULT_PRIORITY_7};
default_priority_0, default_priority_1, default_priority_2,
default_priority_3, default_priority_4, default_priority_5,
default_priority_6, default_priority_7, default_priority_8};
struct ThreadPrio {
std::string name;
@@ -349,6 +136,11 @@ struct SwitcherData {
std::vector<int> tabOrder;
bool hotkeysRegistered = false;
obs_hotkey_id startHotkey;
obs_hotkey_id stopHotkey;
obs_hotkey_id toggleHotkey;
void Thread();
void Start();
void Stop();
@@ -358,10 +150,11 @@ struct SwitcherData {
void writeSceneInfoToFile();
void setDefaultSceneTransitions();
void autoStopStreamAndRecording();
void autoStartStreamRecording();
bool checkPause();
void checkSceneRoundTrip(bool &match, OBSWeakSource &scene,
OBSWeakSource &transition,
std::unique_lock<std::mutex> &lock);
void checkSceneSequence(bool &match, OBSWeakSource &scene,
OBSWeakSource &transition,
std::unique_lock<std::mutex> &lock);
void checkIdleSwitch(bool &match, OBSWeakSource &scene,
OBSWeakSource &transition);
void checkWindowTitleSwitch(bool &match, OBSWeakSource &scene,
@@ -380,11 +173,13 @@ struct SwitcherData {
OBSWeakSource &transition);
void checkTimeSwitch(bool &match, OBSWeakSource &scene,
OBSWeakSource &transition);
void checkAudioSwitch(bool &match, OBSWeakSource &scene,
OBSWeakSource &transition);
void saveWindowTitleSwitches(obs_data_t *obj);
void saveScreenRegionSwitches(obs_data_t *obj);
void savePauseSwitches(obs_data_t *obj);
void saveSceneRoundTripSwitches(obs_data_t *obj);
void saveSceneSequenceSwitches(obs_data_t *obj);
void saveSceneTransitions(obs_data_t *obj);
void saveIdleSwitches(obs_data_t *obj);
void saveExecutableSwitches(obs_data_t *obj);
@@ -392,12 +187,14 @@ struct SwitcherData {
void saveFileSwitches(obs_data_t *obj);
void saveMediaSwitches(obs_data_t *obj);
void saveTimeSwitches(obs_data_t *obj);
void saveAudioSwitches(obs_data_t *obj);
void saveGeneralSettings(obs_data_t *obj);
void saveHotkeys(obs_data_t *obj);
void loadWindowTitleSwitches(obs_data_t *obj);
void loadScreenRegionSwitches(obs_data_t *obj);
void loadPauseSwitches(obs_data_t *obj);
void loadSceneRoundTripSwitches(obs_data_t *obj);
void loadSceneSequenceSwitches(obs_data_t *obj);
void loadSceneTransitions(obs_data_t *obj);
void loadIdleSwitches(obs_data_t *obj);
void loadExecutableSwitches(obs_data_t *obj);
@@ -405,117 +202,11 @@ struct SwitcherData {
void loadFileSwitches(obs_data_t *obj);
void loadMediaSwitches(obs_data_t *obj);
void loadTimeSwitches(obs_data_t *obj);
void loadAudioSwitches(obs_data_t *obj);
void loadGeneralSettings(obs_data_t *obj);
void loadHotkeys(obs_data_t *obj);
void Prune()
{
for (size_t i = 0; i < windowSwitches.size(); i++) {
WindowSceneSwitch &s = windowSwitches[i];
if (!WeakSourceValid(s.scene) ||
!WeakSourceValid(s.transition))
windowSwitches.erase(windowSwitches.begin() +
i--);
}
if (nonMatchingScene && !WeakSourceValid(nonMatchingScene)) {
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];
if (!WeakSourceValid(s.scene) ||
!WeakSourceValid(s.transition))
screenRegionSwitches.erase(
screenRegionSwitches.begin() + i--);
}
for (size_t i = 0; i < pauseScenesSwitches.size(); i++) {
OBSWeakSource &scene = pauseScenesSwitches[i];
if (!WeakSourceValid(scene))
pauseScenesSwitches.erase(
pauseScenesSwitches.begin() + i--);
}
for (size_t i = 0; i < sceneRoundTripSwitches.size(); i++) {
SceneRoundTripSwitch &s = sceneRoundTripSwitches[i];
if (!WeakSourceValid(s.scene1) ||
(!s.usePreviousScene &&
!WeakSourceValid(s.scene2)) ||
!WeakSourceValid(s.transition))
sceneRoundTripSwitches.erase(
sceneRoundTripSwitches.begin() + i--);
}
if (!WeakSourceValid(autoStopScene)) {
autoStopScene = nullptr;
autoStopEnable = false;
}
for (size_t i = 0; i < sceneTransitions.size(); i++) {
SceneTransition &s = sceneTransitions[i];
if (!WeakSourceValid(s.scene1) ||
!WeakSourceValid(s.scene2) ||
!WeakSourceValid(s.transition))
sceneTransitions.erase(
sceneTransitions.begin() + i--);
}
for (size_t i = 0; i < defaultSceneTransitions.size(); i++) {
DefaultSceneTransition &s = defaultSceneTransitions[i];
if (!WeakSourceValid(s.scene) ||
!WeakSourceValid(s.transition))
defaultSceneTransitions.erase(
defaultSceneTransitions.begin() + i--);
}
for (size_t i = 0; i < executableSwitches.size(); i++) {
ExecutableSceneSwitch &s = executableSwitches[i];
if (!WeakSourceValid(s.mScene) ||
!WeakSourceValid(s.mTransition))
executableSwitches.erase(
executableSwitches.begin() + i--);
}
for (size_t i = 0; i < fileSwitches.size(); i++) {
FileSwitch &s = fileSwitches[i];
if (!WeakSourceValid(s.scene) ||
!WeakSourceValid(s.transition))
fileSwitches.erase(fileSwitches.begin() + i--);
}
for (size_t i = 0; i < timeSwitches.size(); i++) {
TimeSwitch &s = timeSwitches[i];
if ((!s.usePreviousScene &&
!WeakSourceValid(s.scene)) ||
!WeakSourceValid(s.transition))
timeSwitches.erase(timeSwitches.begin() + i--);
}
if (!idleData.usePreviousScene &&
!WeakSourceValid(idleData.scene) ||
!WeakSourceValid(idleData.transition)) {
idleData.idleEnable = false;
}
for (size_t i = 0; i < mediaSwitches.size(); i++) {
MediaSwitch &s = mediaSwitches[i];
if ((!s.usePreviousScene &&
!WeakSourceValid(s.scene)) ||
!WeakSourceValid(s.source) ||
!WeakSourceValid(s.transition))
mediaSwitches.erase(mediaSwitches.begin() +
i--);
}
}
void Prune();
inline ~SwitcherData() { Stop(); }
};

View File

@@ -2,7 +2,6 @@
#include <obs.hpp>
#include <QString>
#include <obs-frontend-api.h>
#include "switcher-data-structs.hpp"
static inline bool WeakSourceValid(obs_weak_source_t *ws)
{
@@ -12,173 +11,6 @@ static inline bool WeakSourceValid(obs_weak_source_t *ws)
return !!source;
}
static inline QString MakeSwitchName(const QString &scene, const QString &value,
const QString &transition, bool fullscreen,
bool focus)
{
QString name = QStringLiteral("[") + scene + QStringLiteral(", ") +
transition + QStringLiteral("]: ") + value;
if (fullscreen || focus) {
name += QStringLiteral(" (only if");
if (fullscreen)
name += QStringLiteral(" fullscreen");
if (fullscreen && focus)
name += QStringLiteral(" and");
if (focus)
name += QStringLiteral(" focused");
name += QStringLiteral(")");
}
return name;
}
static inline QString MakeSwitchNameExecutable(const QString &scene,
const QString &value,
const QString &transition,
bool inFocus)
{
if (!inFocus)
return QStringLiteral("[") + scene + QStringLiteral(", ") +
transition + QStringLiteral("]: ") + value;
return QStringLiteral("[") + scene + QStringLiteral(", ") + transition +
QStringLiteral("]: ") + value +
QStringLiteral(" (only if focused)");
}
static inline QString MakeScreenRegionSwitchName(const QString &scene,
const QString &transition,
int minX, int minY, int maxX,
int maxY)
{
return QStringLiteral("[") + scene + QStringLiteral(", ") + transition +
QStringLiteral("]: ") + QString::number(minX) +
QStringLiteral(", ") + QString::number(minY) +
QStringLiteral(" x ") + QString::number(maxX) +
QStringLiteral(", ") + QString::number(maxY);
}
static inline QString MakeSceneRoundTripSwitchName(const QString &scene1,
const QString &scene2,
const QString &transition,
double delay)
{
return scene1 + QStringLiteral(" -> wait for ") +
QString::number(delay) + QStringLiteral(" seconds -> ") +
scene2 + QStringLiteral(" (using ") + transition +
QStringLiteral(" transition)");
}
static inline QString MakeSceneTransitionName(const QString &scene1,
const QString &scene2,
const QString &transition)
{
return scene1 + QStringLiteral(" --- ") + transition +
QStringLiteral(" --> ") + scene2;
}
static inline QString MakeDefaultSceneTransitionName(const QString &scene,
const QString &transition)
{
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 QString MakeFileSwitchName(const QString &scene,
const QString &transition,
const QString &fileName,
const QString &text, bool useRegex,
bool useTime)
{
QString switchName = QStringLiteral("Switch to ") + scene +
QStringLiteral(" using ") + transition +
QStringLiteral(" if ") + fileName;
if (useTime)
switchName += QStringLiteral(" was modified and");
switchName += QStringLiteral(" contains");
if (useRegex)
switchName += QStringLiteral(" (RegEx): \n\"");
else
switchName += QStringLiteral(": \n\"");
if (text.length() > 30)
switchName += text.left(27) + QStringLiteral("...\"");
else
switchName += text + QStringLiteral("\"");
return switchName;
}
typedef enum {
TIME_RESTRICTION_NONE,
TIME_RESTRICTION_SHORTER,
TIME_RESTRICTION_LONGER,
TIME_RESTRICTION_REMAINING_SHORTER,
TIME_RESTRICTION_REMAINING_LONGER,
} time_restriction;
static inline QString
MakeMediaSwitchName(const QString &source, const QString &scene,
const QString &transition, obs_media_state state,
time_restriction restriction, uint64_t time)
{
QString switchName = QStringLiteral("Switch to ") + scene +
QStringLiteral(" using ") + transition +
QStringLiteral(" if ") + source +
QStringLiteral(" state is ");
if (state == OBS_MEDIA_STATE_NONE) {
switchName += QStringLiteral("none");
} else if (state == OBS_MEDIA_STATE_PLAYING) {
switchName += QStringLiteral("playing");
} else if (state == OBS_MEDIA_STATE_OPENING) {
switchName += QStringLiteral("opening");
} else if (state == OBS_MEDIA_STATE_BUFFERING) {
switchName += QStringLiteral("buffering");
} else if (state == OBS_MEDIA_STATE_PAUSED) {
switchName += QStringLiteral("paused");
} else if (state == OBS_MEDIA_STATE_STOPPED) {
switchName += QStringLiteral("stopped");
} else if (state == OBS_MEDIA_STATE_ENDED) {
switchName += QStringLiteral("ended");
} else if (state == OBS_MEDIA_STATE_ERROR) {
switchName += QStringLiteral("error");
}
if (restriction == TIME_RESTRICTION_SHORTER) {
switchName += QStringLiteral(" and time shorter than ");
} else if (restriction == TIME_RESTRICTION_LONGER) {
switchName += QStringLiteral(" and time longer than ");
} else if (restriction == TIME_RESTRICTION_REMAINING_SHORTER) {
switchName +=
QStringLiteral(" and time remaining shorter than ");
} else if (restriction == TIME_RESTRICTION_REMAINING_LONGER) {
switchName +=
QStringLiteral(" and time remaining longer than ");
}
if (restriction != TIME_RESTRICTION_NONE) {
switchName += std::to_string(time).c_str();
switchName += QStringLiteral(" ms");
}
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 std::string GetWeakSourceName(obs_weak_source_t *weak_source)
{
std::string name;

View File

@@ -0,0 +1,245 @@
#pragma once
#include <obs.hpp>
#include <QWidget>
#include <QPaintEvent>
#include <QSharedPointer>
#include <QTimer>
#include <QMutex>
#include <QList>
class QPushButton;
class VolumeMeterTimer;
class VolumeMeter : public QWidget {
Q_OBJECT
Q_PROPERTY(QColor backgroundNominalColor READ getBackgroundNominalColor
WRITE setBackgroundNominalColor DESIGNABLE true)
Q_PROPERTY(QColor backgroundWarningColor READ getBackgroundWarningColor
WRITE setBackgroundWarningColor DESIGNABLE true)
Q_PROPERTY(QColor backgroundErrorColor READ getBackgroundErrorColor
WRITE setBackgroundErrorColor DESIGNABLE true)
Q_PROPERTY(QColor foregroundNominalColor READ getForegroundNominalColor
WRITE setForegroundNominalColor DESIGNABLE true)
Q_PROPERTY(QColor foregroundWarningColor READ getForegroundWarningColor
WRITE setForegroundWarningColor DESIGNABLE true)
Q_PROPERTY(QColor foregroundErrorColor READ getForegroundErrorColor
WRITE setForegroundErrorColor DESIGNABLE true)
Q_PROPERTY(QColor clipColor READ getClipColor WRITE setClipColor
DESIGNABLE true)
Q_PROPERTY(QColor magnitudeColor READ getMagnitudeColor WRITE
setMagnitudeColor DESIGNABLE true)
Q_PROPERTY(QColor majorTickColor READ getMajorTickColor WRITE
setMajorTickColor DESIGNABLE true)
Q_PROPERTY(QColor minorTickColor READ getMinorTickColor WRITE
setMinorTickColor DESIGNABLE true)
// Levels are denoted in dBFS.
Q_PROPERTY(qreal minimumLevel READ getMinimumLevel WRITE setMinimumLevel
DESIGNABLE true)
Q_PROPERTY(qreal warningLevel READ getWarningLevel WRITE setWarningLevel
DESIGNABLE true)
Q_PROPERTY(qreal errorLevel READ getErrorLevel WRITE setErrorLevel
DESIGNABLE true)
Q_PROPERTY(qreal clipLevel READ getClipLevel WRITE setClipLevel
DESIGNABLE true)
Q_PROPERTY(qreal minimumInputLevel READ getMinimumInputLevel WRITE
setMinimumInputLevel DESIGNABLE true)
// Rates are denoted in dB/second.
Q_PROPERTY(qreal peakDecayRate READ getPeakDecayRate WRITE
setPeakDecayRate DESIGNABLE true)
// Time in seconds for the VU meter to integrate over.
Q_PROPERTY(
qreal magnitudeIntegrationTime READ getMagnitudeIntegrationTime
WRITE setMagnitudeIntegrationTime DESIGNABLE true)
// Duration is denoted in seconds.
Q_PROPERTY(qreal peakHoldDuration READ getPeakHoldDuration WRITE
setPeakHoldDuration DESIGNABLE true)
Q_PROPERTY(qreal inputPeakHoldDuration READ getInputPeakHoldDuration
WRITE setInputPeakHoldDuration DESIGNABLE true)
private slots:
void ClipEnding();
private:
obs_volmeter_t *obs_volmeter;
static QWeakPointer<VolumeMeterTimer> updateTimer;
QSharedPointer<VolumeMeterTimer> updateTimerRef;
inline void resetLevels();
inline void handleChannelCofigurationChange();
inline bool detectIdle(uint64_t ts);
inline void calculateBallistics(uint64_t ts,
qreal timeSinceLastRedraw = 0.0);
inline void calculateBallisticsForChannel(int channelNr, uint64_t ts,
qreal timeSinceLastRedraw);
void paintInputMeter(QPainter &painter, int x, int y, int width,
int height, float peakHold);
void paintHMeter(QPainter &painter, int x, int y, int width, int height,
float magnitude, float peak, float peakHold);
void paintHTicks(QPainter &painter, int x, int y, int width,
int height);
void paintVMeter(QPainter &painter, int x, int y, int width, int height,
float magnitude, float peak, float peakHold);
void paintVTicks(QPainter &painter, int x, int y, int height);
QMutex dataMutex;
uint64_t currentLastUpdateTime = 0;
float currentMagnitude[MAX_AUDIO_CHANNELS];
float currentPeak[MAX_AUDIO_CHANNELS];
float currentInputPeak[MAX_AUDIO_CHANNELS];
QPixmap *tickPaintCache = nullptr;
int displayNrAudioChannels = 0;
float displayMagnitude[MAX_AUDIO_CHANNELS];
float displayPeak[MAX_AUDIO_CHANNELS];
float displayPeakHold[MAX_AUDIO_CHANNELS];
uint64_t displayPeakHoldLastUpdateTime[MAX_AUDIO_CHANNELS];
float displayInputPeakHold[MAX_AUDIO_CHANNELS];
uint64_t displayInputPeakHoldLastUpdateTime[MAX_AUDIO_CHANNELS];
QFont tickFont;
QColor backgroundNominalColor;
QColor backgroundWarningColor;
QColor backgroundErrorColor;
QColor foregroundNominalColor;
QColor foregroundWarningColor;
QColor foregroundErrorColor;
QColor clipColor;
QColor magnitudeColor;
QColor majorTickColor;
QColor minorTickColor;
qreal minimumLevel;
qreal warningLevel;
qreal errorLevel;
qreal clipLevel;
qreal minimumInputLevel;
qreal peakDecayRate;
qreal magnitudeIntegrationTime;
qreal peakHoldDuration;
qreal inputPeakHoldDuration;
uint64_t lastRedrawTime = 0;
int channels = 0;
bool clipping = false;
bool vertical;
public:
explicit VolumeMeter(QWidget *parent = nullptr,
obs_volmeter_t *obs_volmeter = nullptr,
bool vertical = false);
~VolumeMeter();
void setLevels(const float magnitude[MAX_AUDIO_CHANNELS],
const float peak[MAX_AUDIO_CHANNELS],
const float inputPeak[MAX_AUDIO_CHANNELS]);
QColor getBackgroundNominalColor() const;
void setBackgroundNominalColor(QColor c);
QColor getBackgroundWarningColor() const;
void setBackgroundWarningColor(QColor c);
QColor getBackgroundErrorColor() const;
void setBackgroundErrorColor(QColor c);
QColor getForegroundNominalColor() const;
void setForegroundNominalColor(QColor c);
QColor getForegroundWarningColor() const;
void setForegroundWarningColor(QColor c);
QColor getForegroundErrorColor() const;
void setForegroundErrorColor(QColor c);
QColor getClipColor() const;
void setClipColor(QColor c);
QColor getMagnitudeColor() const;
void setMagnitudeColor(QColor c);
QColor getMajorTickColor() const;
void setMajorTickColor(QColor c);
QColor getMinorTickColor() const;
void setMinorTickColor(QColor c);
qreal getMinimumLevel() const;
void setMinimumLevel(qreal v);
qreal getWarningLevel() const;
void setWarningLevel(qreal v);
qreal getErrorLevel() const;
void setErrorLevel(qreal v);
qreal getClipLevel() const;
void setClipLevel(qreal v);
qreal getMinimumInputLevel() const;
void setMinimumInputLevel(qreal v);
qreal getPeakDecayRate() const;
void setPeakDecayRate(qreal v);
qreal getMagnitudeIntegrationTime() const;
void setMagnitudeIntegrationTime(qreal v);
qreal getPeakHoldDuration() const;
void setPeakHoldDuration(qreal v);
qreal getInputPeakHoldDuration() const;
void setInputPeakHoldDuration(qreal v);
void setPeakMeterType(enum obs_peak_meter_type peakMeterType);
virtual void mousePressEvent(QMouseEvent *event) override;
protected:
void paintEvent(QPaintEvent *event) override;
};
class VolumeMeterTimer : public QTimer {
Q_OBJECT
public:
inline VolumeMeterTimer() : QTimer() {}
void AddVolControl(VolumeMeter *meter);
void RemoveVolControl(VolumeMeter *meter);
protected:
void timerEvent(QTimerEvent *event) override;
QList<VolumeMeter *> volumeMeters;
};
class QLabel;
class QSlider;
class MuteCheckBox;
class VolControl : public QWidget {
Q_OBJECT
private:
OBSSource source;
QLabel *nameLabel;
QLabel *volLabel;
VolumeMeter *volMeter;
QSlider *slider;
float levelTotal;
float levelCount;
obs_fader_t *obs_fader;
obs_volmeter_t *obs_volmeter;
bool vertical;
static void OBSVolumeChanged(void *param, float db);
static void OBSVolumeLevel(void *data,
const float magnitude[MAX_AUDIO_CHANNELS],
const float peak[MAX_AUDIO_CHANNELS],
const float inputPeak[MAX_AUDIO_CHANNELS]);
private slots:
void SliderChanged(int vol);
void updateText();
public:
explicit VolControl(OBSSource source, bool vertical = false);
~VolControl();
inline obs_source_t *GetSource() const { return source; }
QString GetName() const;
void SetName(const QString &newName);
void SetMeterDecayRate(qreal q);
void setPeakMeterType(enum obs_peak_meter_type peakMeterType);
void EnableSlider(bool enable);
QSlider *GetSlider() const;
};

View File

@@ -1,9 +1,12 @@
#include <obs-module.h>
#include <fstream>
#include <regex>
#include "headers/advanced-scene-switcher.hpp"
void startHotkeyFunc(void *data, obs_hotkey_id id, obs_hotkey_t *hotkey,
bool pressed)
{
UNUSED_PARAMETER(id);
UNUSED_PARAMETER(data);
UNUSED_PARAMETER(hotkey);
@@ -11,33 +14,12 @@ void startHotkeyFunc(void *data, obs_hotkey_id id, obs_hotkey_t *hotkey,
if (!(switcher->th && switcher->th->isRunning()))
switcher->Start();
}
obs_data_array *hotkeyData = obs_hotkey_save(id);
if (hotkeyData != NULL) {
char *path = obs_module_config_path("");
std::ofstream file;
file.open(std::string(path).append(START_HOTKEY_PATH),
std::ofstream::trunc);
if (file.is_open()) {
size_t num = obs_data_array_count(hotkeyData);
for (size_t i = 0; i < num; i++) {
obs_data_t *data =
obs_data_array_item(hotkeyData, i);
std::string temp = obs_data_get_json(data);
obs_data_release(data);
file << temp;
}
file.close();
}
bfree(path);
}
obs_data_array_release(hotkeyData);
}
void stopHotkeyFunc(void *data, obs_hotkey_id id, obs_hotkey_t *hotkey,
bool pressed)
{
UNUSED_PARAMETER(id);
UNUSED_PARAMETER(data);
UNUSED_PARAMETER(hotkey);
@@ -45,33 +27,12 @@ void stopHotkeyFunc(void *data, obs_hotkey_id id, obs_hotkey_t *hotkey,
if (switcher->th && switcher->th->isRunning())
switcher->Stop();
}
obs_data_array *hotkeyData = obs_hotkey_save(id);
if (hotkeyData != NULL) {
char *path = obs_module_config_path("");
std::ofstream file;
file.open(std::string(path).append(STOP_HOTKEY_PATH),
std::ofstream::trunc);
if (file.is_open()) {
size_t num = obs_data_array_count(hotkeyData);
for (size_t i = 0; i < num; i++) {
obs_data_t *data =
obs_data_array_item(hotkeyData, i);
std::string temp = obs_data_get_json(data);
obs_data_release(data);
file << temp;
}
file.close();
}
bfree(path);
}
obs_data_array_release(hotkeyData);
}
void startStopToggleHotkeyFunc(void *data, obs_hotkey_id id,
obs_hotkey_t *hotkey, bool pressed)
{
UNUSED_PARAMETER(id);
UNUSED_PARAMETER(data);
UNUSED_PARAMETER(hotkey);
@@ -81,58 +42,60 @@ void startStopToggleHotkeyFunc(void *data, obs_hotkey_id id,
else
switcher->Start();
}
obs_data_array *hotkeyData = obs_hotkey_save(id);
if (hotkeyData != NULL) {
char *path = obs_module_config_path("");
std::ofstream file;
file.open(std::string(path).append(TOGGLE_HOTKEY_PATH),
std::ofstream::trunc);
if (file.is_open()) {
size_t num = obs_data_array_count(hotkeyData);
for (size_t i = 0; i < num; i++) {
obs_data_t *data =
obs_data_array_item(hotkeyData, i);
std::string temp = obs_data_get_json(data);
obs_data_release(data);
file << temp;
}
file.close();
}
bfree(path);
}
obs_data_array_release(hotkeyData);
}
std::string loadConfigFile(std::string filename)
void registerHotkeys()
{
std::ifstream settingsFile;
char *path = obs_module_config_path("");
std::string value;
switcher->startHotkey = obs_hotkey_register_frontend(
"startSwitcherHotkey", "Start the Advanced Scene Switcher",
startHotkeyFunc, NULL);
switcher->stopHotkey = obs_hotkey_register_frontend(
"stopSwitcherHotkey", "Stop the Advanced Scene Switcher",
stopHotkeyFunc, NULL);
switcher->toggleHotkey = obs_hotkey_register_frontend(
"startStopToggleSwitcherHotkey",
"Toggle Start/Stop for the Advanced Scene Switcher",
startStopToggleHotkeyFunc, NULL);
settingsFile.open(std::string(path).append(filename));
if (settingsFile.is_open()) {
settingsFile.seekg(0, std::ios::end);
value.reserve(settingsFile.tellg());
settingsFile.seekg(0, std::ios::beg);
value.assign((std::istreambuf_iterator<char>(settingsFile)),
std::istreambuf_iterator<char>());
settingsFile.close();
}
bfree(path);
return value;
switcher->hotkeysRegistered = true;
}
void loadKeybinding(obs_hotkey_id hotkeyId, std::string path)
void SwitcherData::saveHotkeys(obs_data_t *obj)
{
std::string bindings = loadConfigFile(path);
if (!bindings.empty()) {
obs_data_array_t *hotkeyData = obs_data_array_create();
obs_data_t *data = obs_data_create_from_json(bindings.c_str());
obs_data_array_insert(hotkeyData, 0, data);
obs_data_release(data);
obs_hotkey_load(hotkeyId, hotkeyData);
obs_data_array_release(hotkeyData);
}
obs_data_array_t *startHotkeyArrray =
obs_hotkey_save(switcher->startHotkey);
obs_data_set_array(obj, "startHotkey", startHotkeyArrray);
obs_data_array_release(startHotkeyArrray);
obs_data_array_t *stopHotkeyArrray =
obs_hotkey_save(switcher->stopHotkey);
obs_data_set_array(obj, "stopHotkey", stopHotkeyArrray);
obs_data_array_release(stopHotkeyArrray);
obs_data_array_t *toggleHotkeyArrray =
obs_hotkey_save(switcher->toggleHotkey);
obs_data_set_array(obj, "toggleHotkey", toggleHotkeyArrray);
obs_data_array_release(toggleHotkeyArrray);
}
void SwitcherData::loadHotkeys(obs_data_t *obj)
{
if (!switcher->hotkeysRegistered)
registerHotkeys();
obs_data_array_t *startHotkeyArrray =
obs_data_get_array(obj, "startHotkey");
obs_hotkey_load(switcher->startHotkey, startHotkeyArrray);
obs_data_array_release(startHotkeyArrray);
obs_data_array_t *stopHotkeyArrray =
obs_data_get_array(obj, "stopHotkey");
obs_hotkey_load(switcher->stopHotkey, stopHotkeyArrray);
obs_data_array_release(stopHotkeyArrray);
obs_data_array_t *toggleHotkeyArrray =
obs_data_get_array(obj, "toggleHotkey");
obs_hotkey_load(switcher->toggleHotkey, toggleHotkeyArrray);
obs_data_array_release(toggleHotkeyArrray);
}

View File

@@ -247,6 +247,55 @@ std::pair<int, int> getCursorPos()
return pos;
}
bool isMaximized(std::string &title)
{
if (!ewmhIsSupported())
return false;
// Find switch in top level windows
std::vector<Window> windows = getTopLevelWindows();
for (auto &window : windows) {
XTextProperty text;
int status = XGetTextProperty(
disp(), window, &text,
XInternAtom(disp(), "_NET_WM_NAME", true));
if (status == 0)
status = XGetTextProperty(disp(), window, &text,
XInternAtom(disp(), "WM_NAME",
true));
char *name = reinterpret_cast<char *>(text.value);
if (status == 0 || name == nullptr)
continue;
// True if switch equals window
bool equals = (title == name);
// True if switch matches window
bool matches = QString::fromStdString(name).contains(
QRegularExpression(QString::fromStdString(title)));
// If found, check if switch is maximized
if (equals || matches) {
QStringList states = getStates(window);
if (!states.isEmpty()) {
// True if window is maximized vertically
bool vertical = states.contains(
"_NET_WM_STATE_MAXIMIZED_VERT");
// True if window is maximized horizontally
bool horizontal = states.contains(
"_NET_WM_STATE_MAXIMIZED_HORZ");
return (vertical && horizontal);
}
break;
}
}
return false;
}
bool isFullscreen(std::string &title)
{
if (!ewmhIsSupported())
@@ -282,14 +331,8 @@ bool isFullscreen(std::string &title)
// True if window is fullscreen
bool fullscreen = states.contains(
"_NET_WM_STATE_FULLSCREEN");
// True if window is maximized vertically
bool vertical = states.contains(
"_NET_WM_STATE_MAXIMIZED_VERT");
// True if window is maximized horizontally
bool horizontal = states.contains(
"_NET_WM_STATE_MAXIMIZED_HORZ");
return (fullscreen || (vertical && horizontal));
return (fullscreen);
}
break;
@@ -343,4 +386,4 @@ int secondsSinceLastInput()
XCloseDisplay(display);
return idle_time;
}
}

View File

@@ -1,301 +0,0 @@
#include <obs-module.h>
#include "headers/advanced-scene-switcher.hpp"
void SceneSwitcher::on_mediaSwitches_currentRowChanged(int idx)
{
if (loading)
return;
if (idx == -1)
return;
QListWidgetItem *item = ui->mediaSwitches->item(idx);
QString mediaSceneStr = item->data(Qt::UserRole).toString();
std::lock_guard<std::mutex> lock(switcher->m);
for (auto &s : switcher->mediaSwitches) {
if (mediaSceneStr.compare(s.mediaSwitchStr.c_str()) == 0) {
QString sceneName = GetWeakSourceName(s.scene).c_str();
QString sourceName =
GetWeakSourceName(s.source).c_str();
QString transitionName =
GetWeakSourceName(s.transition).c_str();
ui->mediaScenes->setCurrentText(sceneName);
ui->mediaSources->setCurrentText(sourceName);
ui->mediaTransitions->setCurrentText(transitionName);
ui->mediaStates->setCurrentIndex(s.state);
ui->mediaTimeRestrictions->setCurrentIndex(
s.restriction);
ui->mediaTime->setValue(s.time);
break;
}
}
}
void SceneSwitcher::on_mediaAdd_clicked()
{
QString sourceName = ui->mediaSources->currentText();
QString sceneName = ui->mediaScenes->currentText();
QString transitionName = ui->mediaTransitions->currentText();
obs_media_state state =
(obs_media_state)ui->mediaStates->currentIndex();
time_restriction restriction =
(time_restriction)ui->mediaTimeRestrictions->currentIndex();
uint64_t time = ui->mediaTime->value();
if (sceneName.isEmpty() || transitionName.isEmpty() ||
sourceName.isEmpty())
return;
OBSWeakSource source = GetWeakSourceByQString(sourceName);
OBSWeakSource scene = GetWeakSourceByQString(sceneName);
OBSWeakSource transition = GetWeakTransitionByQString(transitionName);
QString switchText = MakeMediaSwitchName(sourceName, sceneName,
transitionName, state,
restriction, time);
QVariant v = QVariant::fromValue(switchText);
QListWidgetItem *item =
new QListWidgetItem(switchText, ui->mediaSwitches);
item->setData(Qt::UserRole, v);
std::lock_guard<std::mutex> lock(switcher->m);
switcher->mediaSwitches.emplace_back(
scene, source, transition, state, restriction, time,
(sceneName == QString(PREVIOUS_SCENE_NAME)),
switchText.toUtf8().constData());
}
void SceneSwitcher::on_mediaRemove_clicked()
{
QListWidgetItem *item = ui->mediaSwitches->currentItem();
if (!item)
return;
std::string mediaStr =
item->data(Qt::UserRole).toString().toUtf8().constData();
{
std::lock_guard<std::mutex> lock(switcher->m);
auto &switches = switcher->mediaSwitches;
for (auto it = switches.begin(); it != switches.end(); ++it) {
auto &s = *it;
if (s.mediaSwitchStr == mediaStr) {
switches.erase(it);
break;
}
}
}
delete item;
}
void SceneSwitcher::on_mediaUp_clicked()
{
int index = ui->mediaSwitches->currentRow();
if (index != -1 && index != 0) {
ui->mediaSwitches->insertItem(
index - 1, ui->mediaSwitches->takeItem(index));
ui->mediaSwitches->setCurrentRow(index - 1);
std::lock_guard<std::mutex> lock(switcher->m);
iter_swap(switcher->mediaSwitches.begin() + index,
switcher->mediaSwitches.begin() + index - 1);
}
}
void SceneSwitcher::on_mediaDown_clicked()
{
int index = ui->mediaSwitches->currentRow();
if (index != -1 && index != ui->mediaSwitches->count() - 1) {
ui->mediaSwitches->insertItem(
index + 1, ui->mediaSwitches->takeItem(index));
ui->mediaSwitches->setCurrentRow(index + 1);
std::lock_guard<std::mutex> lock(switcher->m);
iter_swap(switcher->mediaSwitches.begin() + index,
switcher->mediaSwitches.begin() + index + 1);
}
}
void SceneSwitcher::on_mediaTimeRestrictions_currentIndexChanged(int idx)
{
if (idx == -1)
return;
if ((time_restriction)ui->mediaTimeRestrictions->currentIndex() ==
TIME_RESTRICTION_NONE) {
ui->mediaTime->setDisabled(true);
} else {
ui->mediaTime->setDisabled(false);
}
}
void SwitcherData::checkMediaSwitch(bool &match, OBSWeakSource &scene,
OBSWeakSource &transition)
{
for (MediaSwitch &mediaSwitch : mediaSwitches) {
obs_source_t *source =
obs_weak_source_get_source(mediaSwitch.source);
auto duration = obs_source_media_get_duration(source);
auto time = obs_source_media_get_time(source);
obs_media_state state = obs_source_media_get_state(source);
bool matched =
state == mediaSwitch.state &&
(mediaSwitch.restriction == TIME_RESTRICTION_NONE ||
(mediaSwitch.restriction == TIME_RESTRICTION_LONGER &&
time > mediaSwitch.time) ||
(mediaSwitch.restriction == TIME_RESTRICTION_SHORTER &&
time < mediaSwitch.time) ||
(mediaSwitch.restriction ==
TIME_RESTRICTION_REMAINING_SHORTER &&
duration > time &&
duration - time < mediaSwitch.time) ||
(mediaSwitch.restriction ==
TIME_RESTRICTION_REMAINING_LONGER &&
duration > time &&
duration - time > mediaSwitch.time));
if (matched && !mediaSwitch.matched) {
match = true;
scene = (mediaSwitch.usePreviousScene)
? previousScene
: mediaSwitch.scene;
transition = mediaSwitch.transition;
if (verbose)
blog(LOG_INFO,
"Advanced Scene Switcher meida match");
}
mediaSwitch.matched = matched;
obs_source_release(source);
}
}
void SwitcherData::saveMediaSwitches(obs_data_t *obj)
{
obs_data_array_t *mediaArray = obs_data_array_create();
for (MediaSwitch &s : switcher->mediaSwitches) {
obs_data_t *array_obj = obs_data_create();
obs_source_t *source = obs_weak_source_get_source(s.source);
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) && source &&
transition) {
const char *sourceName = obs_source_get_name(source);
const char *sceneName =
obs_source_get_name(sceneSource);
const char *transitionName =
obs_source_get_name(transition);
obs_data_set_string(array_obj, "source", sourceName);
obs_data_set_string(array_obj, "scene",
s.usePreviousScene
? PREVIOUS_SCENE_NAME
: sceneName);
obs_data_set_string(array_obj, "transition",
transitionName);
obs_data_set_int(array_obj, "state", s.state);
obs_data_set_int(array_obj, "restriction",
s.restriction);
obs_data_set_int(array_obj, "time", s.time);
obs_data_array_push_back(mediaArray, array_obj);
}
obs_source_release(source);
obs_source_release(sceneSource);
obs_source_release(transition);
obs_data_release(array_obj);
}
obs_data_set_array(obj, "mediaSwitches", mediaArray);
obs_data_array_release(mediaArray);
}
void SwitcherData::loadMediaSwitches(obs_data_t *obj)
{
obs_data_array_t *mediaArray = obs_data_get_array(obj, "mediaSwitches");
switcher->mediaSwitches.clear();
size_t count = obs_data_array_count(mediaArray);
for (size_t i = 0; i < count; i++) {
obs_data_t *array_obj = obs_data_array_item(mediaArray, i);
const char *source = obs_data_get_string(array_obj, "source");
const char *scene = obs_data_get_string(array_obj, "scene");
const char *transition =
obs_data_get_string(array_obj, "transition");
obs_media_state state =
(obs_media_state)obs_data_get_int(array_obj, "state");
time_restriction restriction =
(time_restriction)obs_data_get_int(array_obj,
"restriction");
uint64_t time = obs_data_get_int(array_obj, "time");
std::string mediaStr = MakeMediaSwitchName(source, scene,
transition, state,
restriction, time)
.toUtf8()
.constData();
switcher->mediaSwitches.emplace_back(
GetWeakSourceByName(scene), GetWeakSourceByName(source),
GetWeakTransitionByName(transition), state, restriction,
time, (strcmp(scene, PREVIOUS_SCENE_NAME) == 0),
mediaStr);
obs_data_release(array_obj);
}
obs_data_array_release(mediaArray);
}
void SceneSwitcher::setupMediaTab()
{
populateSceneSelection(ui->mediaScenes, true);
populateTransitionSelection(ui->mediaTransitions);
auto sourceEnum = [](void *data, obs_source_t *source) -> bool /* -- */
{
QComboBox *combo = reinterpret_cast<QComboBox *>(data);
if (strcmp(obs_source_get_id(source), "ffmpeg_source") == 0) {
const char *name = obs_source_get_name(source);
combo->addItem(name);
}
return true;
};
obs_enum_sources(sourceEnum, ui->mediaSources);
ui->mediaStates->addItem("None");
ui->mediaStates->addItem("Playing");
ui->mediaStates->addItem("Opening");
ui->mediaStates->addItem("Buffering");
ui->mediaStates->addItem("Paused");
ui->mediaStates->addItem("Stopped");
ui->mediaStates->addItem("Ended");
ui->mediaStates->addItem("Error");
ui->mediaTimeRestrictions->addItem("None");
ui->mediaTimeRestrictions->addItem("Time shorter");
ui->mediaTimeRestrictions->addItem("Time longer");
ui->mediaTimeRestrictions->addItem("Time remaining shorter");
ui->mediaTimeRestrictions->addItem("Time remaining longer");
for (auto &s : switcher->mediaSwitches) {
std::string sourceName = GetWeakSourceName(s.source);
std::string sceneName = (s.usePreviousScene)
? PREVIOUS_SCENE_NAME
: GetWeakSourceName(s.scene);
std::string transitionName = GetWeakSourceName(s.transition);
QString listText = MakeMediaSwitchName(
sourceName.c_str(), sceneName.c_str(),
transitionName.c_str(), s.state, s.restriction, s.time);
QListWidgetItem *item =
new QListWidgetItem(listText, ui->mediaSwitches);
item->setData(Qt::UserRole, listText);
}
}

View File

@@ -126,6 +126,13 @@ std::pair<int, int> getCursorPos()
return pos;
}
// TODO:
// not implemented on MacOS as I cannot test it
bool isMaximized(std::string &title)
{
return false;
}
bool isFullscreen(std::string &title)
{
// Check for match

View File

@@ -1,215 +0,0 @@
#include "headers/advanced-scene-switcher.hpp"
#include <random>
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();
std::lock_guard<std::mutex> 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) {
std::lock_guard<std::mutex> 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);
{
std::lock_guard<std::mutex> 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;
std::string text =
item->data(Qt::UserRole).toString().toUtf8().constData();
{
std::lock_guard<std::mutex> 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)
{
if (randomSwitches.size() == 0)
return;
std::vector<RandomSwitch> rs(randomSwitches);
std::random_device rng;
std::mt19937 urng(rng());
std::shuffle(rs.begin(), rs.end(), urng);
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;
if (verbose)
blog(LOG_INFO, "Advanced Scene Switcher random match");
break;
}
}
void SwitcherData::saveRandomSwitches(obs_data_t *obj)
{
obs_data_array_t *randomArray = obs_data_array_create();
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);
}
obs_data_set_array(obj, "randomSwitches", randomArray);
obs_data_array_release(randomArray);
}
void SwitcherData::loadRandomSwitches(obs_data_t *obj)
{
switcher->randomSwitches.clear();
obs_data_array_t *randomArray =
obs_data_get_array(obj, "randomSwitches");
size_t 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");
const char *str = obs_data_get_string(array_obj, "str");
switcher->randomSwitches.emplace_back(
GetWeakSourceByName(scene),
GetWeakTransitionByName(transition), delay, str);
obs_data_release(array_obj);
}
obs_data_array_release(randomArray);
}
void SceneSwitcher::setupRandomTab()
{
populateSceneSelection(ui->randomScenes, false);
populateTransitionSelection(ui->randomTransitions);
for (auto &s : switcher->randomSwitches) {
std::string sceneName = GetWeakSourceName(s.scene);
std::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);
}
}

View File

@@ -1,500 +0,0 @@
#include <QFileDialog>
#include <QTextStream>
#include <QMessageBox>
#include <obs.hpp>
#include "headers/advanced-scene-switcher.hpp"
#define SECONDS_INDEX 0
#define MINUTES_INDEX 1
#define HOURS_INDEX 2
void SceneSwitcher::on_sceneRoundTripDelayUnits_currentIndexChanged(int index)
{
if (loading)
return;
std::lock_guard<std::mutex> lock(switcher->m);
double val = ui->sceneRoundTripSpinBox->value();
switch (switcher->sceneRoundTripUnitMultiplier) {
case 1:
val /= pow(60, index);
break;
case 60:
val /= pow(60, index - 1);
break;
case 3600:
val /= pow(60, index - 2);
break;
}
ui->sceneRoundTripSpinBox->setValue(val);
switch (index) {
case SECONDS_INDEX:
switcher->sceneRoundTripUnitMultiplier = 1;
break;
case MINUTES_INDEX:
switcher->sceneRoundTripUnitMultiplier = 60;
break;
case HOURS_INDEX:
switcher->sceneRoundTripUnitMultiplier = 3600;
break;
}
}
void SceneSwitcher::on_sceneRoundTripAdd_clicked()
{
QString scene1Name = ui->sceneRoundTripScenes1->currentText();
QString scene2Name = ui->sceneRoundTripScenes2->currentText();
QString transitionName = ui->sceneRoundTripTransitions->currentText();
if (scene1Name.isEmpty() || scene2Name.isEmpty())
return;
double delay = ui->sceneRoundTripSpinBox->value() *
switcher->sceneRoundTripUnitMultiplier;
if (scene1Name == scene2Name)
return;
OBSWeakSource source1 = GetWeakSourceByQString(scene1Name);
OBSWeakSource source2 = GetWeakSourceByQString(scene2Name);
OBSWeakSource transition = GetWeakTransitionByQString(transitionName);
QString text = MakeSceneRoundTripSwitchName(scene1Name, scene2Name,
transitionName, delay);
QVariant v = QVariant::fromValue(text);
int idx = SceneRoundTripFindByData(scene1Name);
if (idx == -1) {
QListWidgetItem *item =
new QListWidgetItem(text, ui->sceneRoundTrips);
item->setData(Qt::UserRole, v);
std::lock_guard<std::mutex> lock(switcher->m);
switcher->sceneRoundTripSwitches.emplace_back(
source1, source2, transition, delay,
(scene2Name == QString(PREVIOUS_SCENE_NAME)),
text.toUtf8().constData());
} else {
QListWidgetItem *item = ui->sceneRoundTrips->item(idx);
item->setText(text);
{
std::lock_guard<std::mutex> lock(switcher->m);
for (auto &s : switcher->sceneRoundTripSwitches) {
if (s.scene1 == source1) {
s.scene2 = source2;
s.delay = delay;
s.transition = transition;
s.usePreviousScene =
(scene2Name ==
QString(PREVIOUS_SCENE_NAME));
s.sceneRoundTripStr =
text.toUtf8().constData();
break;
}
}
}
}
}
void SceneSwitcher::on_sceneRoundTripRemove_clicked()
{
QListWidgetItem *item = ui->sceneRoundTrips->currentItem();
if (!item)
return;
std::string text =
item->data(Qt::UserRole).toString().toUtf8().constData();
{
std::lock_guard<std::mutex> lock(switcher->m);
auto &switches = switcher->sceneRoundTripSwitches;
for (auto it = switches.begin(); it != switches.end(); ++it) {
auto &s = *it;
if (s.sceneRoundTripStr == text) {
switches.erase(it);
break;
}
}
}
delete item;
}
void SceneSwitcher::on_sceneRoundTripSave_clicked()
{
QString directory = QFileDialog::getSaveFileName(
this, tr("Save Scene Round Trip to file ..."),
QDir::currentPath(), tr("Text files (*.txt)"));
if (directory.isEmpty())
return;
QFile file(directory);
if (!file.open(QIODevice::WriteOnly | QIODevice::Text))
return;
obs_data_t *obj = obs_data_create();
switcher->saveSceneRoundTripSwitches(obj);
obs_data_save_json(obj, file.fileName().toUtf8().constData());
obs_data_release(obj);
}
// to be removed once support for old file format is dropped
bool oldSceneRoundTripLoad(QFile *file)
{
QTextStream in(file);
std::vector<QString> lines;
std::vector<SceneRoundTripSwitch> newSceneRoundTripSwitch;
while (!in.atEnd()) {
QString line = in.readLine();
lines.push_back(line);
if (lines.size() == 5) {
OBSWeakSource scene1 = GetWeakSourceByQString(lines[0]);
OBSWeakSource scene2 = GetWeakSourceByQString(lines[1]);
OBSWeakSource transition =
GetWeakTransitionByQString(lines[4]);
if (WeakSourceValid(scene1) &&
((lines[1] == QString(PREVIOUS_SCENE_NAME)) ||
(WeakSourceValid(scene2))) &&
WeakSourceValid(transition)) {
newSceneRoundTripSwitch.emplace_back(
SceneRoundTripSwitch(
GetWeakSourceByQString(lines[0]),
GetWeakSourceByQString(lines[1]),
GetWeakTransitionByQString(
lines[4]),
lines[2].toDouble() / 1000.,
(lines[1] ==
QString(PREVIOUS_SCENE_NAME)),
lines[3].toStdString()));
}
lines.clear();
}
}
if (lines.size() != 0 || newSceneRoundTripSwitch.size() == 0)
return false;
switcher->sceneRoundTripSwitches.clear();
switcher->sceneRoundTripSwitches = newSceneRoundTripSwitch;
return true;
}
void SceneSwitcher::on_sceneRoundTripLoad_clicked()
{
std::lock_guard<std::mutex> lock(switcher->m);
QString directory = QFileDialog::getOpenFileName(
this, tr("Select a file to read Scene Round Trip from ..."),
QDir::currentPath(), tr("Text files (*.txt)"));
if (directory.isEmpty())
return;
QFile file(directory);
if (!file.open(QIODevice::ReadOnly | QIODevice::Text))
return;
obs_data_t *obj = obs_data_create_from_json_file(
file.fileName().toUtf8().constData());
if (!obj) {
QMessageBox Msgbox;
QString txt =
"Advanced Scene Switcher failed to import settings!\n";
txt += "Falling back to old format.";
Msgbox.setText(txt);
Msgbox.exec();
if (oldSceneRoundTripLoad(&file)) {
QString txt =
"Advanced Scene Switcher settings imported successfully!\n";
txt += "Please resave settings as support for old format will be dropped in next version.";
Msgbox.setText(txt);
Msgbox.exec();
close();
} else {
Msgbox.setText(
"Advanced Scene Switcher failed to import settings!");
Msgbox.exec();
}
return;
}
switcher->loadSceneRoundTripSwitches(obj);
obs_data_release(obj);
QMessageBox Msgbox;
Msgbox.setText(
"Advanced Scene Switcher settings imported successfully!");
Msgbox.exec();
close();
}
void SwitcherData::checkSceneRoundTrip(bool &match, OBSWeakSource &scene,
OBSWeakSource &transition,
std::unique_lock<std::mutex> &lock)
{
bool sceneRoundTripActive = false;
obs_source_t *currentSource = obs_frontend_get_current_scene();
obs_weak_source_t *ws = obs_source_get_weak_source(currentSource);
for (SceneRoundTripSwitch &s : sceneRoundTripSwitches) {
if (s.scene1 == ws) {
sceneRoundTripActive = true;
int dur = s.delay * 1000 - interval;
if (dur > 0) {
waitScene = currentSource;
if (verbose)
blog(LOG_INFO,
"Advanced Scene Switcher sequence sleep %d",
dur);
cv.wait_for(lock,
std::chrono::milliseconds(dur));
waitScene = nullptr;
}
obs_source_t *currentSource2 =
obs_frontend_get_current_scene();
// only switch if user hasn't changed scene manually
if (currentSource == currentSource2) {
match = true;
scene = (s.usePreviousScene) ? previousScene
: s.scene2;
transition = s.transition;
}
obs_source_release(currentSource2);
break;
}
}
obs_source_release(currentSource);
obs_weak_source_release(ws);
}
void SceneSwitcher::on_sceneRoundTrips_currentRowChanged(int idx)
{
if (loading)
return;
if (idx == -1)
return;
QListWidgetItem *item = ui->sceneRoundTrips->item(idx);
QString sceneRoundTrip = item->text();
std::lock_guard<std::mutex> lock(switcher->m);
for (auto &s : switcher->sceneRoundTripSwitches) {
if (sceneRoundTrip.compare(s.sceneRoundTripStr.c_str()) == 0) {
std::string scene1 = GetWeakSourceName(s.scene1);
std::string scene2 = GetWeakSourceName(s.scene2);
std::string transitionName =
GetWeakSourceName(s.transition);
double delay = s.delay /
switcher->sceneRoundTripUnitMultiplier;
ui->sceneRoundTripScenes1->setCurrentText(
scene1.c_str());
ui->sceneRoundTripScenes2->setCurrentText(
scene2.c_str());
ui->sceneRoundTripTransitions->setCurrentText(
transitionName.c_str());
ui->sceneRoundTripSpinBox->setValue(delay);
break;
}
}
}
int SceneSwitcher::SceneRoundTripFindByData(const QString &scene1)
{
QRegExp rx(scene1 + " ->.*");
int count = ui->sceneRoundTrips->count();
int idx = -1;
for (int i = 0; i < count; i++) {
QListWidgetItem *item = ui->sceneRoundTrips->item(i);
QString itemString = item->data(Qt::UserRole).toString();
if (rx.exactMatch(itemString)) {
idx = i;
break;
}
}
return idx;
}
void SceneSwitcher::on_sceneRoundTripUp_clicked()
{
int index = ui->sceneRoundTrips->currentRow();
if (index != -1 && index != 0) {
ui->sceneRoundTrips->insertItem(
index - 1, ui->sceneRoundTrips->takeItem(index));
ui->sceneRoundTrips->setCurrentRow(index - 1);
std::lock_guard<std::mutex> lock(switcher->m);
iter_swap(switcher->sceneRoundTripSwitches.begin() + index,
switcher->sceneRoundTripSwitches.begin() + index - 1);
}
}
void SceneSwitcher::on_sceneRoundTripDown_clicked()
{
int index = ui->sceneRoundTrips->currentRow();
if (index != -1 && index != ui->sceneRoundTrips->count() - 1) {
ui->sceneRoundTrips->insertItem(
index + 1, ui->sceneRoundTrips->takeItem(index));
ui->sceneRoundTrips->setCurrentRow(index + 1);
std::lock_guard<std::mutex> lock(switcher->m);
iter_swap(switcher->sceneRoundTripSwitches.begin() + index,
switcher->sceneRoundTripSwitches.begin() + index + 1);
}
}
void SwitcherData::saveSceneRoundTripSwitches(obs_data_t *obj)
{
obs_data_array_t *sceneRoundTripArray = obs_data_array_create();
for (SceneRoundTripSwitch &s : switcher->sceneRoundTripSwitches) {
obs_data_t *array_obj = obs_data_create();
obs_source_t *source1 = obs_weak_source_get_source(s.scene1);
obs_source_t *source2 = obs_weak_source_get_source(s.scene2);
obs_source_t *transition =
obs_weak_source_get_source(s.transition);
if (source1 && (s.usePreviousScene || source2) && transition) {
const char *sceneName1 = obs_source_get_name(source1);
const char *sceneName2 = obs_source_get_name(source2);
const char *transitionName =
obs_source_get_name(transition);
obs_data_set_string(array_obj, "sceneRoundTripScene1",
sceneName1);
obs_data_set_string(array_obj, "sceneRoundTripScene2",
s.usePreviousScene
? PREVIOUS_SCENE_NAME
: sceneName2);
obs_data_set_string(array_obj, "transition",
transitionName);
obs_data_set_double(array_obj, "delay", s.delay);
obs_data_set_string(array_obj, "sceneRoundTripStr",
s.sceneRoundTripStr.c_str());
obs_data_array_push_back(sceneRoundTripArray,
array_obj);
obs_source_release(source1);
obs_source_release(source2);
obs_source_release(transition);
}
obs_data_release(array_obj);
}
obs_data_set_array(obj, "sceneRoundTrip", sceneRoundTripArray);
obs_data_array_release(sceneRoundTripArray);
}
void SwitcherData::loadSceneRoundTripSwitches(obs_data_t *obj)
{
switcher->sceneRoundTripSwitches.clear();
obs_data_array_t *sceneRoundTripArray =
obs_data_get_array(obj, "sceneRoundTrip");
size_t count = obs_data_array_count(sceneRoundTripArray);
for (size_t i = 0; i < count; i++) {
obs_data_t *array_obj =
obs_data_array_item(sceneRoundTripArray, i);
const char *scene1 =
obs_data_get_string(array_obj, "sceneRoundTripScene1");
const char *scene2 =
obs_data_get_string(array_obj, "sceneRoundTripScene2");
const char *transition =
obs_data_get_string(array_obj, "transition");
double delay = 0;
// To be removed in future version
// to be compatible with older versions
if (!obs_data_has_user_value(array_obj, "delay")) {
delay = double(obs_data_get_int(array_obj,
"sceneRoundTripDelay"));
delay = delay * 1000. +
double(obs_data_get_int(
array_obj, "sceneRoundTripDelayMs"));
delay /= 1000.;
} else {
delay = obs_data_get_double(array_obj, "delay");
}
std::string str = MakeSceneRoundTripSwitchName(
scene1, scene2, transition, delay)
.toUtf8()
.constData();
const char *sceneRoundTripStr = str.c_str();
switcher->sceneRoundTripSwitches.emplace_back(
GetWeakSourceByName(scene1),
GetWeakSourceByName(scene2),
GetWeakTransitionByName(transition), delay,
(strcmp(scene2, PREVIOUS_SCENE_NAME) == 0),
sceneRoundTripStr);
obs_data_release(array_obj);
}
obs_data_array_release(sceneRoundTripArray);
}
void SceneSwitcher::setupSequenceTab()
{
populateSceneSelection(ui->sceneRoundTripScenes1, false);
populateSceneSelection(ui->sceneRoundTripScenes2, true);
populateTransitionSelection(ui->sceneRoundTripTransitions);
double smallestDelay = double(switcher->interval) / 1000;
for (auto &s : switcher->sceneRoundTripSwitches) {
std::string sceneName1 = GetWeakSourceName(s.scene1);
std::string sceneName2 = (s.usePreviousScene)
? PREVIOUS_SCENE_NAME
: GetWeakSourceName(s.scene2);
std::string transitionName = GetWeakSourceName(s.transition);
QString text = MakeSceneRoundTripSwitchName(
sceneName1.c_str(), sceneName2.c_str(),
transitionName.c_str(), s.delay);
QListWidgetItem *item =
new QListWidgetItem(text, ui->sceneRoundTrips);
item->setData(Qt::UserRole, text);
if (s.delay < smallestDelay)
smallestDelay = s.delay;
}
(smallestDelay * 1000 < switcher->interval)
? ui->intervalWarning->setVisible(true)
: ui->intervalWarning->setVisible(false);
ui->sceneRoundTripDelayUnits->addItem("seconds");
ui->sceneRoundTripDelayUnits->addItem("minutes");
ui->sceneRoundTripDelayUnits->addItem("hours");
switch (switcher->sceneRoundTripUnitMultiplier) {
case 1:
ui->sceneRoundTripDelayUnits->setCurrentIndex(0);
break;
case 60:
ui->sceneRoundTripDelayUnits->setCurrentIndex(1);
break;
case 3600:
ui->sceneRoundTripDelayUnits->setCurrentIndex(2);
break;
default:
break;
}
}

View File

@@ -1,515 +0,0 @@
#include "headers/advanced-scene-switcher.hpp"
void SceneSwitcher::on_transitionsAdd_clicked()
{
QString scene1Name = ui->transitionsScene1->currentText();
QString scene2Name = ui->transitionsScene2->currentText();
QString transitionName = ui->transitionsTransitions->currentText();
if (scene1Name.isEmpty() || scene2Name.isEmpty())
return;
if (scene1Name == scene2Name)
return;
OBSWeakSource source1 = GetWeakSourceByQString(scene1Name);
OBSWeakSource source2 = GetWeakSourceByQString(scene2Name);
OBSWeakSource transition = GetWeakTransitionByQString(transitionName);
QString text =
MakeSceneTransitionName(scene1Name, scene2Name, transitionName);
QVariant v = QVariant::fromValue(text);
int idx = SceneTransitionsFindByData(scene1Name, scene2Name);
if (idx == -1) {
QListWidgetItem *item =
new QListWidgetItem(text, ui->sceneTransitions);
item->setData(Qt::UserRole, v);
std::lock_guard<std::mutex> lock(switcher->m);
switcher->sceneTransitions.emplace_back(
source1, source2, transition,
text.toUtf8().constData());
} else {
QListWidgetItem *item = ui->sceneTransitions->item(idx);
item->setText(text);
{
std::lock_guard<std::mutex> lock(switcher->m);
for (auto &s : switcher->sceneTransitions) {
if (s.scene1 == source1 &&
s.scene2 == source2) {
s.transition = transition;
s.sceneTransitionStr =
text.toUtf8().constData();
break;
}
}
}
ui->sceneTransitions->sortItems();
}
}
void SceneSwitcher::on_transitionsRemove_clicked()
{
QListWidgetItem *item = ui->sceneTransitions->currentItem();
if (!item)
return;
std::string text =
item->data(Qt::UserRole).toString().toUtf8().constData();
{
std::lock_guard<std::mutex> lock(switcher->m);
auto &switches = switcher->sceneTransitions;
for (auto it = switches.begin(); it != switches.end(); ++it) {
auto &s = *it;
if (s.sceneTransitionStr == text) {
switches.erase(it);
break;
}
}
}
delete item;
}
void SceneSwitcher::on_defaultTransitionsAdd_clicked()
{
QString sceneName = ui->defaultTransitionsScene->currentText();
QString transitionName =
ui->defaultTransitionsTransitions->currentText();
if (sceneName.isEmpty() || transitionName.isEmpty())
return;
OBSWeakSource source = GetWeakSourceByQString(sceneName);
OBSWeakSource transition = GetWeakTransitionByQString(transitionName);
QString text =
MakeDefaultSceneTransitionName(sceneName, transitionName);
QVariant v = QVariant::fromValue(text);
int idx = DefaultTransitionsFindByData(sceneName);
if (idx == -1) {
QListWidgetItem *item =
new QListWidgetItem(text, ui->defaultTransitions);
item->setData(Qt::UserRole, v);
std::lock_guard<std::mutex> lock(switcher->m);
switcher->defaultSceneTransitions.emplace_back(
source, transition, text.toUtf8().constData());
} else {
QListWidgetItem *item = ui->defaultTransitions->item(idx);
item->setText(text);
{
std::lock_guard<std::mutex> lock(switcher->m);
for (auto &s : switcher->defaultSceneTransitions) {
if (s.scene == source) {
s.transition = transition;
s.sceneTransitionStr =
text.toUtf8().constData();
break;
}
}
}
ui->defaultTransitions->sortItems();
}
}
void SceneSwitcher::on_defaultTransitionsRemove_clicked()
{
QListWidgetItem *item = ui->defaultTransitions->currentItem();
if (!item)
return;
std::string text =
item->data(Qt::UserRole).toString().toUtf8().constData();
{
std::lock_guard<std::mutex> lock(switcher->m);
auto &switches = switcher->defaultSceneTransitions;
for (auto it = switches.begin(); it != switches.end(); ++it) {
auto &s = *it;
if (s.sceneTransitionStr == text) {
switches.erase(it);
break;
}
}
}
delete item;
}
void SwitcherData::setDefaultSceneTransitions()
{
obs_source_t *currentSource = obs_frontend_get_current_scene();
obs_weak_source_t *ws = obs_source_get_weak_source(currentSource);
for (DefaultSceneTransition &s : defaultSceneTransitions) {
if (s.scene == ws) {
obs_source_t *transition =
obs_weak_source_get_source(s.transition);
//This might cancel the current transition
//There is no way to be sure when the previous transition finished
obs_frontend_set_current_transition(transition);
if (verbose)
blog(LOG_INFO,
"Advanced Scene Switcher default transition set");
obs_source_release(transition);
break;
}
}
obs_source_release(currentSource);
obs_weak_source_release(ws);
}
int SceneSwitcher::SceneTransitionsFindByData(const QString &scene1,
const QString &scene2)
{
QRegExp rx(scene1 + " --- .* --> " + scene2);
int count = ui->sceneTransitions->count();
int idx = -1;
for (int i = 0; i < count; i++) {
QListWidgetItem *item = ui->sceneTransitions->item(i);
QString itemString = item->data(Qt::UserRole).toString();
if (rx.exactMatch(itemString)) {
idx = i;
break;
}
}
return idx;
}
int SceneSwitcher::DefaultTransitionsFindByData(const QString &scene)
{
QRegExp rx(scene + " --> .*");
int count = ui->defaultTransitions->count();
int idx = -1;
for (int i = 0; i < count; i++) {
QListWidgetItem *item = ui->defaultTransitions->item(i);
QString itemString = item->data(Qt::UserRole).toString();
if (rx.exactMatch(itemString)) {
idx = i;
break;
}
}
return idx;
}
void SceneSwitcher::on_sceneTransitions_currentRowChanged(int idx)
{
if (loading)
return;
if (idx == -1)
return;
QListWidgetItem *item = ui->sceneTransitions->item(idx);
QString sceneTransition = item->data(Qt::UserRole).toString();
std::lock_guard<std::mutex> lock(switcher->m);
for (auto &s : switcher->sceneTransitions) {
if (sceneTransition.compare(s.sceneTransitionStr.c_str()) ==
0) {
std::string scene1 = GetWeakSourceName(s.scene1);
std::string scene2 = GetWeakSourceName(s.scene2);
std::string transitionName =
GetWeakSourceName(s.transition);
ui->transitionsScene1->setCurrentText(scene1.c_str());
ui->transitionsScene2->setCurrentText(scene2.c_str());
ui->transitionsTransitions->setCurrentText(
transitionName.c_str());
break;
}
}
}
void SceneSwitcher::on_defaultTransitions_currentRowChanged(int idx)
{
if (loading)
return;
if (idx == -1)
return;
QListWidgetItem *item = ui->defaultTransitions->item(idx);
QString sceneTransition = item->data(Qt::UserRole).toString();
std::lock_guard<std::mutex> lock(switcher->m);
for (auto &s : switcher->defaultSceneTransitions) {
if (sceneTransition.compare(s.sceneTransitionStr.c_str()) ==
0) {
std::string scene = GetWeakSourceName(s.scene);
std::string transitionName =
GetWeakSourceName(s.transition);
ui->defaultTransitionsScene->setCurrentText(
scene.c_str());
ui->defaultTransitionsTransitions->setCurrentText(
transitionName.c_str());
break;
}
}
}
void SceneSwitcher::on_transitionOverridecheckBox_stateChanged(int state)
{
if (loading)
return;
std::lock_guard<std::mutex> lock(switcher->m);
if (!state) {
switcher->tansitionOverrideOverride = false;
} else {
switcher->tansitionOverrideOverride = true;
}
}
obs_weak_source_t *getNextTransition(obs_weak_source_t *scene1,
obs_weak_source_t *scene2)
{
obs_weak_source_t *ws = nullptr;
if (scene1 && scene2) {
for (SceneTransition &t : switcher->sceneTransitions) {
if (t.scene1 == scene1 && t.scene2 == scene2)
ws = t.transition;
}
obs_weak_source_addref(ws);
}
return ws;
}
void overwriteTransitionOverride(obs_weak_source_t *sceneWs,
obs_source_t *transition, transitionData &td)
{
obs_source_t *scene = obs_weak_source_get_source(sceneWs);
obs_data_t *data = obs_source_get_private_settings(scene);
td.name = obs_data_get_string(data, "transition");
td.duration = obs_data_get_int(data, "duration");
const char *name = obs_source_get_name(transition);
int duration = obs_frontend_get_transition_duration();
obs_data_set_string(data, "transition", name);
obs_data_set_int(data, "transition_duration", duration);
obs_data_release(data);
obs_source_release(scene);
}
void restoreTransitionOverride(obs_source_t *scene, transitionData td)
{
obs_data_t *data = obs_source_get_private_settings(scene);
obs_data_set_string(data, "transition", td.name.c_str());
obs_data_set_int(data, "transition_duration", td.duration);
obs_data_release(data);
}
void setNextTransition(OBSWeakSource &targetScene, obs_source_t *currentSource,
OBSWeakSource &transition,
bool &transitionOverrideOverride, transitionData &td)
{
obs_weak_source_t *currentScene =
obs_source_get_weak_source(currentSource);
obs_weak_source_t *nextTransitionWs =
getNextTransition(currentScene, targetScene);
obs_weak_source_release(currentScene);
obs_source_t *nextTransition = nullptr;
if (nextTransitionWs) {
nextTransition = obs_weak_source_get_source(nextTransitionWs);
} else if (transition) {
nextTransition = obs_weak_source_get_source(transition);
}
if (nextTransition) {
obs_frontend_set_current_transition(nextTransition);
}
if (transitionOverrideOverride)
overwriteTransitionOverride(targetScene, nextTransition, td);
obs_weak_source_release(nextTransitionWs);
obs_source_release(nextTransition);
}
void SwitcherData::saveSceneTransitions(obs_data_t *obj)
{
obs_data_array_t *sceneTransitionsArray = obs_data_array_create();
for (SceneTransition &s : switcher->sceneTransitions) {
obs_data_t *array_obj = obs_data_create();
obs_source_t *source1 = obs_weak_source_get_source(s.scene1);
obs_source_t *source2 = obs_weak_source_get_source(s.scene2);
obs_source_t *transition =
obs_weak_source_get_source(s.transition);
if (source1 && source2 && transition) {
const char *sceneName1 = obs_source_get_name(source1);
const char *sceneName2 = obs_source_get_name(source2);
const char *transitionName =
obs_source_get_name(transition);
obs_data_set_string(array_obj, "Scene1", sceneName1);
obs_data_set_string(array_obj, "Scene2", sceneName2);
obs_data_set_string(array_obj, "transition",
transitionName);
obs_data_set_string(array_obj, "Str",
s.sceneTransitionStr.c_str());
obs_data_array_push_back(sceneTransitionsArray,
array_obj);
obs_source_release(source1);
obs_source_release(source2);
obs_source_release(transition);
}
obs_data_release(array_obj);
}
obs_data_set_array(obj, "sceneTransitions", sceneTransitionsArray);
obs_data_array_release(sceneTransitionsArray);
obs_data_array_t *defaultTransitionsArray = obs_data_array_create();
for (DefaultSceneTransition &s : switcher->defaultSceneTransitions) {
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_string(array_obj, "Str",
s.sceneTransitionStr.c_str());
obs_data_array_push_back(defaultTransitionsArray,
array_obj);
obs_source_release(source);
obs_source_release(transition);
}
obs_data_release(array_obj);
}
obs_data_set_array(obj, "defaultTransitions", defaultTransitionsArray);
obs_data_array_release(defaultTransitionsArray);
obs_data_set_bool(obj, "tansitionOverrideOverride",
switcher->tansitionOverrideOverride);
}
void SwitcherData::loadSceneTransitions(obs_data_t *obj)
{
switcher->sceneTransitions.clear();
obs_data_array_t *sceneTransitionsArray =
obs_data_get_array(obj, "sceneTransitions");
size_t count = obs_data_array_count(sceneTransitionsArray);
for (size_t i = 0; i < count; i++) {
obs_data_t *array_obj =
obs_data_array_item(sceneTransitionsArray, i);
const char *scene1 = obs_data_get_string(array_obj, "Scene1");
const char *scene2 = obs_data_get_string(array_obj, "Scene2");
const char *transition =
obs_data_get_string(array_obj, "transition");
const char *sceneTransitionsStr =
obs_data_get_string(array_obj, "Str");
switcher->sceneTransitions.emplace_back(
GetWeakSourceByName(scene1),
GetWeakSourceByName(scene2),
GetWeakTransitionByName(transition),
sceneTransitionsStr);
obs_data_release(array_obj);
}
obs_data_array_release(sceneTransitionsArray);
switcher->defaultSceneTransitions.clear();
obs_data_array_t *defaultTransitionsArray =
obs_data_get_array(obj, "defaultTransitions");
count = obs_data_array_count(defaultTransitionsArray);
for (size_t i = 0; i < count; i++) {
obs_data_t *array_obj =
obs_data_array_item(defaultTransitionsArray, i);
const char *scene = obs_data_get_string(array_obj, "Scene");
const char *transition =
obs_data_get_string(array_obj, "transition");
const char *sceneTransitionsStr =
obs_data_get_string(array_obj, "Str");
switcher->defaultSceneTransitions.emplace_back(
GetWeakSourceByName(scene),
GetWeakTransitionByName(transition),
sceneTransitionsStr);
obs_data_release(array_obj);
}
obs_data_array_release(defaultTransitionsArray);
switcher->tansitionOverrideOverride =
obs_data_get_bool(obj, "tansitionOverrideOverride");
}
void SceneSwitcher::setupTransitionsTab()
{
populateSceneSelection(ui->transitionsScene1, false);
populateSceneSelection(ui->transitionsScene2, false);
populateSceneSelection(ui->defaultTransitionsScene, false);
populateTransitionSelection(ui->transitionsTransitions);
populateTransitionSelection(ui->defaultTransitionsTransitions);
for (auto &s : switcher->sceneTransitions) {
std::string sceneName1 = GetWeakSourceName(s.scene1);
std::string sceneName2 = GetWeakSourceName(s.scene2);
std::string transitionName = GetWeakSourceName(s.transition);
QString text = MakeSceneTransitionName(sceneName1.c_str(),
sceneName2.c_str(),
transitionName.c_str());
QListWidgetItem *item =
new QListWidgetItem(text, ui->sceneTransitions);
item->setData(Qt::UserRole, text);
}
//(transitionDurationLongerThanInterval(switcher->interval))
// ? ui->transitionWarning->setVisible(true)
// : ui->transitionWarning->setVisible(false);
for (auto &s : switcher->defaultSceneTransitions) {
std::string sceneName = GetWeakSourceName(s.scene);
std::string transitionName = GetWeakSourceName(s.transition);
QString text = MakeDefaultSceneTransitionName(
sceneName.c_str(), transitionName.c_str());
QListWidgetItem *item =
new QListWidgetItem(text, ui->defaultTransitions);
item->setData(Qt::UserRole, text);
}
ui->transitionOverridecheckBox->setChecked(
switcher->tansitionOverrideOverride);
}

View File

@@ -1,284 +0,0 @@
#include <QTimer>
#include "headers/advanced-scene-switcher.hpp"
void SwitcherData::checkScreenRegionSwitch(bool &match, OBSWeakSource &scene,
OBSWeakSource &transition)
{
std::pair<int, int> cursorPos = getCursorPos();
int minRegionSize = 99999;
for (auto &s : screenRegionSwitches) {
if (cursorPos.first >= s.minX && cursorPos.second >= s.minY &&
cursorPos.first <= s.maxX && cursorPos.second <= s.maxY) {
int regionSize = (s.maxX - s.minX) + (s.maxY - s.minY);
if (regionSize < minRegionSize) {
match = true;
scene = s.scene;
transition = s.transition;
minRegionSize = regionSize;
if (verbose)
blog(LOG_INFO,
"Advanced Scene Switcher region match");
break;
}
}
}
}
void SceneSwitcher::updateScreenRegionCursorPos()
{
std::pair<int, int> position = getCursorPos();
ui->cursorXPosition->setText(QString::number(position.first));
ui->cursorYPosition->setText(QString::number(position.second));
}
void SceneSwitcher::on_screenRegionAdd_clicked()
{
QString sceneName = ui->screenRegionScenes->currentText();
QString transitionName = ui->screenRegionsTransitions->currentText();
if (sceneName.isEmpty())
return;
int minX = ui->screenRegionMinX->value();
int minY = ui->screenRegionMinY->value();
int maxX = ui->screenRegionMaxX->value();
int maxY = ui->screenRegionMaxY->value();
std::string regionStr =
std::to_string(minX) + ", " + std::to_string(minY) + " x " +
std::to_string(maxX) + ", " + std::to_string(maxY);
QString region = QString::fromStdString(regionStr);
OBSWeakSource source = GetWeakSourceByQString(sceneName);
OBSWeakSource transition = GetWeakTransitionByQString(transitionName);
QVariant v = QVariant::fromValue(region);
QString text = MakeScreenRegionSwitchName(sceneName, transitionName,
minX, minY, maxX, maxY);
int idx = ScreenRegionFindByData(region);
if (idx == -1) {
QListWidgetItem *item =
new QListWidgetItem(text, ui->screenRegions);
item->setData(Qt::UserRole, v);
std::lock_guard<std::mutex> lock(switcher->m);
switcher->screenRegionSwitches.emplace_back(
source, transition, minX, minY, maxX, maxY, regionStr);
} else {
QListWidgetItem *item = ui->screenRegions->item(idx);
item->setText(text);
std::string curRegion = region.toUtf8().constData();
{
std::lock_guard<std::mutex> lock(switcher->m);
for (auto &s : switcher->screenRegionSwitches) {
if (s.regionStr == curRegion) {
s.scene = source;
s.transition = transition;
break;
}
}
}
}
}
void SceneSwitcher::on_screenRegionRemove_clicked()
{
QListWidgetItem *item = ui->screenRegions->currentItem();
if (!item)
return;
std::string region =
item->data(Qt::UserRole).toString().toUtf8().constData();
{
std::lock_guard<std::mutex> lock(switcher->m);
auto &switches = switcher->screenRegionSwitches;
for (auto it = switches.begin(); it != switches.end(); ++it) {
auto &s = *it;
if (s.regionStr == region) {
switches.erase(it);
break;
}
}
}
delete item;
}
void SceneSwitcher::on_screenRegions_currentRowChanged(int idx)
{
if (loading)
return;
if (idx == -1)
return;
QListWidgetItem *item = ui->screenRegions->item(idx);
QString region = item->data(Qt::UserRole).toString();
std::lock_guard<std::mutex> lock(switcher->m);
for (auto &s : switcher->screenRegionSwitches) {
if (region.compare(s.regionStr.c_str()) == 0) {
std::string name = GetWeakSourceName(s.scene);
std::string transitionName =
GetWeakSourceName(s.transition);
ui->screenRegionScenes->setCurrentText(name.c_str());
ui->screenRegionsTransitions->setCurrentText(
transitionName.c_str());
ui->screenRegionMinX->setValue(s.minX);
ui->screenRegionMinY->setValue(s.minY);
ui->screenRegionMaxX->setValue(s.maxX);
ui->screenRegionMaxY->setValue(s.maxY);
break;
}
}
}
int SceneSwitcher::ScreenRegionFindByData(const QString &region)
{
int count = ui->screenRegions->count();
int idx = -1;
for (int i = 0; i < count; i++) {
QListWidgetItem *item = ui->screenRegions->item(i);
QString itemRegion = item->data(Qt::UserRole).toString();
if (itemRegion == region) {
idx = i;
break;
}
}
return idx;
}
void SceneSwitcher::on_screenRegionUp_clicked()
{
int index = ui->screenRegions->currentRow();
if (index != -1 && index != 0) {
ui->screenRegions->insertItem(
index - 1, ui->screenRegions->takeItem(index));
ui->screenRegions->setCurrentRow(index - 1);
std::lock_guard<std::mutex> lock(switcher->m);
iter_swap(switcher->screenRegionSwitches.begin() + index,
switcher->screenRegionSwitches.begin() + index - 1);
}
}
void SceneSwitcher::on_screenRegionDown_clicked()
{
int index = ui->screenRegions->currentRow();
if (index != -1 && index != ui->screenRegions->count() - 1) {
ui->screenRegions->insertItem(
index + 1, ui->screenRegions->takeItem(index));
ui->screenRegions->setCurrentRow(index + 1);
std::lock_guard<std::mutex> lock(switcher->m);
iter_swap(switcher->screenRegionSwitches.begin() + index,
switcher->screenRegionSwitches.begin() + index + 1);
}
}
void SwitcherData::saveScreenRegionSwitches(obs_data_t *obj)
{
obs_data_array_t *screenRegionArray = obs_data_array_create();
for (ScreenRegionSwitch &s : switcher->screenRegionSwitches) {
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, "screenRegionScene",
sceneName);
obs_data_set_string(array_obj, "transition",
transitionName);
obs_data_set_int(array_obj, "minX", s.minX);
obs_data_set_int(array_obj, "minY", s.minY);
obs_data_set_int(array_obj, "maxX", s.maxX);
obs_data_set_int(array_obj, "maxY", s.maxY);
obs_data_set_string(array_obj, "screenRegionStr",
s.regionStr.c_str());
obs_data_array_push_back(screenRegionArray, array_obj);
obs_source_release(source);
obs_source_release(transition);
}
obs_data_release(array_obj);
}
obs_data_set_array(obj, "screenRegion", screenRegionArray);
obs_data_array_release(screenRegionArray);
}
void SwitcherData::loadScreenRegionSwitches(obs_data_t *obj)
{
switcher->screenRegionSwitches.clear();
obs_data_array_t *screenRegionArray =
obs_data_get_array(obj, "screenRegion");
size_t count = obs_data_array_count(screenRegionArray);
for (size_t i = 0; i < count; i++) {
obs_data_t *array_obj =
obs_data_array_item(screenRegionArray, i);
const char *scene =
obs_data_get_string(array_obj, "screenRegionScene");
const char *transition =
obs_data_get_string(array_obj, "transition");
int minX = obs_data_get_int(array_obj, "minX");
int minY = obs_data_get_int(array_obj, "minY");
int maxX = obs_data_get_int(array_obj, "maxX");
int maxY = obs_data_get_int(array_obj, "maxY");
std::string regionStr =
obs_data_get_string(array_obj, "screenRegionStr");
switcher->screenRegionSwitches.emplace_back(
GetWeakSourceByName(scene),
GetWeakTransitionByName(transition), minX, minY, maxX,
maxY, regionStr);
obs_data_release(array_obj);
}
obs_data_array_release(screenRegionArray);
}
void SceneSwitcher::setupRegionTab()
{
populateSceneSelection(ui->screenRegionScenes, false);
populateTransitionSelection(ui->screenRegionsTransitions);
for (auto &s : switcher->screenRegionSwitches) {
std::string sceneName = GetWeakSourceName(s.scene);
std::string transitionName = GetWeakSourceName(s.transition);
QString text = MakeScreenRegionSwitchName(
sceneName.c_str(), transitionName.c_str(), s.minX,
s.minY, s.maxX, s.maxY);
QListWidgetItem *item =
new QListWidgetItem(text, ui->screenRegions);
item->setData(Qt::UserRole, s.regionStr.c_str());
}
// screen region cursor position
QTimer *screenRegionTimer = new QTimer(this);
connect(screenRegionTimer, SIGNAL(timeout()), this,
SLOT(updateScreenRegionCursorPos()));
screenRegionTimer->start(1000);
}

442
src/switch-audio.cpp Normal file
View File

@@ -0,0 +1,442 @@
#include "headers/advanced-scene-switcher.hpp"
#include "headers/volume-control.hpp"
#include "headers/utility.hpp"
static QMetaObject::Connection addPulse;
void AdvSceneSwitcher::on_audioAdd_clicked()
{
ui->audioAdd->disconnect(addPulse);
std::lock_guard<std::mutex> lock(switcher->m);
switcher->audioSwitches.emplace_back();
QListWidgetItem *item;
item = new QListWidgetItem(ui->audioSwitches);
ui->audioSwitches->addItem(item);
AudioSwitchWidget *sw =
new AudioSwitchWidget(&switcher->audioSwitches.back());
item->setSizeHint(sw->minimumSizeHint());
ui->audioSwitches->setItemWidget(item, sw);
}
void AdvSceneSwitcher::on_audioRemove_clicked()
{
QListWidgetItem *item = ui->audioSwitches->currentItem();
if (!item)
return;
{
std::lock_guard<std::mutex> lock(switcher->m);
int idx = ui->audioSwitches->currentRow();
auto &switches = switcher->audioSwitches;
switches.erase(switches.begin() + idx);
}
delete item;
}
void AdvSceneSwitcher::on_audioUp_clicked()
{
int index = ui->audioSwitches->currentRow();
if (!listMoveUp(ui->audioSwitches))
return;
AudioSwitchWidget *s1 =
(AudioSwitchWidget *)ui->audioSwitches->itemWidget(
ui->audioSwitches->item(index));
AudioSwitchWidget *s2 =
(AudioSwitchWidget *)ui->audioSwitches->itemWidget(
ui->audioSwitches->item(index - 1));
AudioSwitchWidget::swapSwitchData(s1, s2);
std::lock_guard<std::mutex> lock(switcher->m);
std::swap(switcher->audioSwitches[index],
switcher->audioSwitches[index - 1]);
}
void AdvSceneSwitcher::on_audioDown_clicked()
{
int index = ui->audioSwitches->currentRow();
if (!listMoveDown(ui->audioSwitches))
return;
AudioSwitchWidget *s1 =
(AudioSwitchWidget *)ui->audioSwitches->itemWidget(
ui->audioSwitches->item(index));
AudioSwitchWidget *s2 =
(AudioSwitchWidget *)ui->audioSwitches->itemWidget(
ui->audioSwitches->item(index + 1));
AudioSwitchWidget::swapSwitchData(s1, s2);
std::lock_guard<std::mutex> lock(switcher->m);
std::swap(switcher->audioSwitches[index],
switcher->audioSwitches[index + 1]);
}
void SwitcherData::checkAudioSwitch(bool &match, OBSWeakSource &scene,
OBSWeakSource &transition)
{
if (audioSwitches.size() == 0)
return;
for (AudioSwitch &s : audioSwitches) {
if (!s.initialized())
continue;
obs_source_t *as = obs_weak_source_get_source(s.audioSource);
bool audioActive = obs_source_active(as);
obs_source_release(as);
// peak will have a value from -60 db to 0 db
bool volumeThresholdreached = ((double)s.peak + 60) * 1.7 >
s.volumeThreshold;
if (volumeThresholdreached && audioActive) {
scene = (s.usePreviousScene) ? previousScene : s.scene;
transition = s.transition;
match = true;
if (verbose)
s.logMatch();
break;
}
}
}
void SwitcherData::saveAudioSwitches(obs_data_t *obj)
{
obs_data_array_t *audioArray = obs_data_array_create();
for (AudioSwitch &s : switcher->audioSwitches) {
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);
obs_source_t *audioSrouce =
obs_weak_source_get_source(s.audioSource);
if ((s.usePreviousScene || sceneSource) && transition &&
audioSrouce) {
const char *sceneName =
obs_source_get_name(sceneSource);
const char *transitionName =
obs_source_get_name(transition);
const char *audioSourceName =
obs_source_get_name(audioSrouce);
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, "audioSource",
audioSourceName);
obs_data_set_int(array_obj, "volume",
s.volumeThreshold);
obs_data_array_push_back(audioArray, array_obj);
}
obs_source_release(sceneSource);
obs_source_release(transition);
obs_source_release(audioSrouce);
obs_data_release(array_obj);
}
obs_data_set_array(obj, "audioSwitches", audioArray);
obs_data_array_release(audioArray);
}
void SwitcherData::loadAudioSwitches(obs_data_t *obj)
{
switcher->audioSwitches.clear();
obs_data_array_t *audioArray = obs_data_get_array(obj, "audioSwitches");
size_t count = obs_data_array_count(audioArray);
for (size_t i = 0; i < count; i++) {
obs_data_t *array_obj = obs_data_array_item(audioArray, i);
const char *scene = obs_data_get_string(array_obj, "scene");
const char *transition =
obs_data_get_string(array_obj, "transition");
const char *audioSource =
obs_data_get_string(array_obj, "audioSource");
int vol = obs_data_get_int(array_obj, "volume");
switcher->audioSwitches.emplace_back(
GetWeakSourceByName(scene),
GetWeakTransitionByName(transition),
GetWeakSourceByName(audioSource), vol,
(strcmp(scene, previous_scene_name) == 0));
obs_data_release(array_obj);
}
obs_data_array_release(audioArray);
}
void AdvSceneSwitcher::setupAudioTab()
{
for (auto &s : switcher->audioSwitches) {
QListWidgetItem *item;
item = new QListWidgetItem(ui->audioSwitches);
ui->audioSwitches->addItem(item);
AudioSwitchWidget *sw = new AudioSwitchWidget(&s);
item->setSizeHint(sw->minimumSizeHint());
ui->audioSwitches->setItemWidget(item, sw);
}
if (switcher->audioSwitches.size() == 0)
addPulse = PulseWidget(ui->audioAdd, QColor(Qt::green));
}
void AudioSwitch::setVolumeLevel(void *data,
const float magnitude[MAX_AUDIO_CHANNELS],
const float peak[MAX_AUDIO_CHANNELS],
const float inputPeak[MAX_AUDIO_CHANNELS])
{
UNUSED_PARAMETER(magnitude);
UNUSED_PARAMETER(inputPeak);
AudioSwitch *s = static_cast<AudioSwitch *>(data);
s->peak = peak[0];
for (int i = 1; i < MAX_AUDIO_CHANNELS; i++)
if (peak[i] > s->peak)
s->peak = peak[i];
}
void AudioSwitch::resetVolmeter()
{
obs_volmeter_remove_callback(volmeter, setVolumeLevel, this);
obs_volmeter_destroy(volmeter);
volmeter = obs_volmeter_create(OBS_FADER_LOG);
obs_volmeter_add_callback(volmeter, setVolumeLevel, this);
obs_source_t *as = obs_weak_source_get_source(audioSource);
if (!obs_volmeter_attach_source(volmeter, as)) {
const char *name = obs_source_get_name(as);
blog(LOG_WARNING, "failed to attach volmeter to source %s",
name);
}
obs_source_release(as);
}
bool AudioSwitch::initialized()
{
return SceneSwitcherEntry::initialized() && audioSource;
}
bool AudioSwitch::valid()
{
return !initialized() ||
(SceneSwitcherEntry::valid() && WeakSourceValid(audioSource));
}
AudioSwitch::AudioSwitch(OBSWeakSource scene_, OBSWeakSource transition_,
OBSWeakSource audioSource_, int volumeThreshold_,
bool usePreviousScene_)
: SceneSwitcherEntry(scene_, transition_, usePreviousScene_),
audioSource(audioSource_),
volumeThreshold(volumeThreshold_)
{
volmeter = obs_volmeter_create(OBS_FADER_LOG);
obs_volmeter_add_callback(volmeter, setVolumeLevel, this);
obs_source_t *as = obs_weak_source_get_source(audioSource);
if (!obs_volmeter_attach_source(volmeter, as)) {
const char *name = obs_source_get_name(as);
blog(LOG_WARNING, "failed to attach volmeter to source %s",
name);
}
obs_source_release(as);
}
AudioSwitch::AudioSwitch(const AudioSwitch &other)
: SceneSwitcherEntry(other.scene, other.transition,
other.usePreviousScene),
audioSource(other.audioSource),
volumeThreshold(other.volumeThreshold)
{
volmeter = obs_volmeter_create(OBS_FADER_LOG);
obs_volmeter_add_callback(volmeter, setVolumeLevel, this);
obs_source_t *as = obs_weak_source_get_source(other.audioSource);
if (!obs_volmeter_attach_source(volmeter, as)) {
const char *name = obs_source_get_name(as);
blog(LOG_WARNING, "failed to attach volmeter to source %s",
name);
}
obs_source_release(as);
}
AudioSwitch::AudioSwitch(AudioSwitch &&other)
: SceneSwitcherEntry(other.scene, other.transition,
other.usePreviousScene),
audioSource(other.audioSource),
volumeThreshold(other.volumeThreshold),
volmeter(other.volmeter)
{
other.volmeter = nullptr;
}
AudioSwitch::~AudioSwitch()
{
obs_volmeter_remove_callback(volmeter, setVolumeLevel, this);
obs_volmeter_destroy(volmeter);
}
AudioSwitch &AudioSwitch::operator=(const AudioSwitch &other)
{
AudioSwitch t(other);
swap(*this, t);
return *this = AudioSwitch(other);
}
AudioSwitch &AudioSwitch::operator=(AudioSwitch &&other) noexcept
{
if (this == &other) {
return *this;
}
swap(*this, other);
obs_volmeter_remove_callback(other.volmeter, setVolumeLevel, this);
obs_volmeter_destroy(other.volmeter);
other.volmeter = nullptr;
return *this;
}
void swap(AudioSwitch &first, AudioSwitch &second)
{
std::swap(first.scene, second.scene);
std::swap(first.transition, second.transition);
std::swap(first.usePreviousScene, second.usePreviousScene);
std::swap(first.audioSource, second.audioSource);
std::swap(first.volumeThreshold, second.volumeThreshold);
std::swap(first.peak, second.peak);
std::swap(first.volmeter, second.volmeter);
first.resetVolmeter();
second.resetVolmeter();
}
AudioSwitchWidget::AudioSwitchWidget(AudioSwitch *s) : SwitchWidget(s)
{
audioSources = new QComboBox();
audioVolumeThreshold = new QSpinBox();
obs_source_t *soruce = nullptr;
if (s)
soruce = obs_weak_source_get_source(s->audioSource);
volMeter = new VolControl(soruce);
obs_source_release(soruce);
whenLabel = new QLabel("When the volume of");
aboveLabel = new QLabel("is above");
switchLabel = new QLabel("switch to");
usingLabel = new QLabel("using");
audioVolumeThreshold->setSuffix("%");
audioVolumeThreshold->setMaximum(100);
audioVolumeThreshold->setMinimum(0);
QWidget::connect(volMeter->GetSlider(), SIGNAL(valueChanged(int)),
audioVolumeThreshold, SLOT(setValue(int)));
QWidget::connect(audioVolumeThreshold, SIGNAL(valueChanged(int)),
volMeter->GetSlider(), SLOT(setValue(int)));
QWidget::connect(audioVolumeThreshold, SIGNAL(valueChanged(int)), this,
SLOT(VolumeThresholdChanged(int)));
QWidget::connect(audioSources,
SIGNAL(currentTextChanged(const QString &)), this,
SLOT(SourceChanged(const QString &)));
AdvSceneSwitcher::populateAudioSelection(audioSources);
if (s) {
audioSources->setCurrentText(
GetWeakSourceName(s->audioSource).c_str());
audioVolumeThreshold->setValue(s->volumeThreshold);
}
setStyleSheet("* { background-color: transparent; }");
QHBoxLayout *switchLayout = new QHBoxLayout;
switchLayout->addWidget(whenLabel);
switchLayout->addWidget(audioSources);
switchLayout->addWidget(aboveLabel);
switchLayout->addWidget(audioVolumeThreshold);
switchLayout->addWidget(switchLabel);
switchLayout->addWidget(scenes);
switchLayout->addWidget(usingLabel);
switchLayout->addWidget(transitions);
switchLayout->addStretch();
QVBoxLayout *mainLayout = new QVBoxLayout;
mainLayout->addLayout(switchLayout);
mainLayout->addWidget(volMeter);
setLayout(mainLayout);
switchData = s;
loading = false;
}
AudioSwitch *AudioSwitchWidget::getSwitchData()
{
return switchData;
}
void AudioSwitchWidget::setSwitchData(AudioSwitch *s)
{
switchData = s;
}
void AudioSwitchWidget::swapSwitchData(AudioSwitchWidget *s1,
AudioSwitchWidget *s2)
{
SwitchWidget::swapSwitchData(s1, s2);
AudioSwitch *t = s1->getSwitchData();
s1->setSwitchData(s2->getSwitchData());
s2->setSwitchData(t);
}
void AudioSwitchWidget::UpdateVolmeterSource()
{
delete volMeter;
obs_source_t *soruce =
obs_weak_source_get_source(switchData->audioSource);
volMeter = new VolControl(soruce);
obs_source_release(soruce);
QLayout *layout = this->layout();
layout->addWidget(volMeter);
QWidget::connect(volMeter->GetSlider(), SIGNAL(valueChanged(int)),
audioVolumeThreshold, SLOT(setValue(int)));
QWidget::connect(audioVolumeThreshold, SIGNAL(valueChanged(int)),
volMeter->GetSlider(), SLOT(setValue(int)));
// Slider will default to 0 so set it manually once
volMeter->GetSlider()->setValue(switchData->volumeThreshold);
}
void AudioSwitchWidget::SourceChanged(const QString &text)
{
if (loading || !switchData)
return;
std::lock_guard<std::mutex> lock(switcher->m);
switchData->audioSource = GetWeakSourceByQString(text);
switchData->resetVolmeter();
UpdateVolmeterSource();
}
void AudioSwitchWidget::VolumeThresholdChanged(int vol)
{
if (loading || !switchData)
return;
std::lock_guard<std::mutex> lock(switcher->m);
switchData->volumeThreshold = vol;
}

284
src/switch-executable.cpp Normal file
View File

@@ -0,0 +1,284 @@
#include "headers/advanced-scene-switcher.hpp"
#include "headers/utility.hpp"
static QMetaObject::Connection addPulse;
void AdvSceneSwitcher::on_executableAdd_clicked()
{
ui->executableAdd->disconnect(addPulse);
std::lock_guard<std::mutex> lock(switcher->m);
switcher->executableSwitches.emplace_back();
QListWidgetItem *item;
item = new QListWidgetItem(ui->executables);
ui->executables->addItem(item);
ExecutableSwitchWidget *sw = new ExecutableSwitchWidget(
&switcher->executableSwitches.back());
item->setSizeHint(sw->minimumSizeHint());
ui->executables->setItemWidget(item, sw);
}
void AdvSceneSwitcher::on_executableRemove_clicked()
{
QListWidgetItem *item = ui->executables->currentItem();
if (!item)
return;
{
std::lock_guard<std::mutex> lock(switcher->m);
int idx = ui->executables->currentRow();
auto &switches = switcher->executableSwitches;
switches.erase(switches.begin() + idx);
}
delete item;
}
void AdvSceneSwitcher::on_executableUp_clicked()
{
int index = ui->executables->currentRow();
if (!listMoveUp(ui->executables))
return;
ExecutableSwitchWidget *s1 =
(ExecutableSwitchWidget *)ui->executables->itemWidget(
ui->executables->item(index));
ExecutableSwitchWidget *s2 =
(ExecutableSwitchWidget *)ui->executables->itemWidget(
ui->executables->item(index - 1));
ExecutableSwitchWidget::swapSwitchData(s1, s2);
std::lock_guard<std::mutex> lock(switcher->m);
std::swap(switcher->executableSwitches[index],
switcher->executableSwitches[index - 1]);
}
void AdvSceneSwitcher::on_executableDown_clicked()
{
int index = ui->executables->currentRow();
if (!listMoveDown(ui->executables))
return;
ExecutableSwitchWidget *s1 =
(ExecutableSwitchWidget *)ui->executables->itemWidget(
ui->executables->item(index));
ExecutableSwitchWidget *s2 =
(ExecutableSwitchWidget *)ui->executables->itemWidget(
ui->executables->item(index + 1));
ExecutableSwitchWidget::swapSwitchData(s1, s2);
std::lock_guard<std::mutex> lock(switcher->m);
std::swap(switcher->executableSwitches[index],
switcher->executableSwitches[index + 1]);
}
void SwitcherData::checkExeSwitch(bool &match, OBSWeakSource &scene,
OBSWeakSource &transition)
{
std::string title;
QStringList runningProcesses;
bool ignored = false;
// Check if current window is ignored
GetCurrentWindowTitle(title);
for (auto &window : ignoreWindowsSwitches) {
// True if ignored switch equals title
bool equals = (title == window);
// True if ignored switch matches title
bool matches = QString::fromStdString(title).contains(
QRegularExpression(QString::fromStdString(window)));
if (equals || matches) {
ignored = true;
title = lastTitle;
break;
}
}
lastTitle = title;
// Check for match
GetProcessList(runningProcesses);
for (ExecutableSwitch &s : executableSwitches) {
if (!s.initialized())
continue;
// True if executable switch is running (direct)
bool equals = runningProcesses.contains(s.exe);
// True if executable switch is running (regex)
bool matches = (runningProcesses.indexOf(
QRegularExpression(s.exe)) != -1);
// True if focus is disabled OR switch is focused
bool focus = (!s.inFocus || isInFocus(s.exe));
// True if current window is ignored AND switch equals OR matches last window
bool ignore =
(ignored && (title == s.exe.toStdString() ||
QString::fromStdString(title).contains(
QRegularExpression(s.exe))));
if ((equals || matches) && (focus || ignore)) {
match = true;
scene = s.scene;
transition = s.transition;
if (verbose)
s.logMatch();
break;
}
}
}
void SwitcherData::saveExecutableSwitches(obs_data_t *obj)
{
obs_data_array_t *executableArray = obs_data_array_create();
for (ExecutableSwitch &s : switcher->executableSwitches) {
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_string(array_obj, "exefile",
s.exe.toUtf8());
obs_data_set_bool(array_obj, "infocus", s.inFocus);
obs_data_array_push_back(executableArray, array_obj);
}
obs_source_release(source);
obs_source_release(transition);
obs_data_release(array_obj);
}
obs_data_set_array(obj, "executableSwitches", executableArray);
obs_data_array_release(executableArray);
}
void SwitcherData::loadExecutableSwitches(obs_data_t *obj)
{
switcher->executableSwitches.clear();
obs_data_array_t *executableArray =
obs_data_get_array(obj, "executableSwitches");
size_t count = obs_data_array_count(executableArray);
for (size_t i = 0; i < count; i++) {
obs_data_t *array_obj = obs_data_array_item(executableArray, i);
const char *scene = obs_data_get_string(array_obj, "scene");
const char *transition =
obs_data_get_string(array_obj, "transition");
const char *exe = obs_data_get_string(array_obj, "exefile");
bool infocus = obs_data_get_bool(array_obj, "infocus");
switcher->executableSwitches.emplace_back(
GetWeakSourceByName(scene),
GetWeakTransitionByName(transition), exe, infocus);
obs_data_release(array_obj);
}
obs_data_array_release(executableArray);
}
void AdvSceneSwitcher::setupExecutableTab()
{
for (auto &s : switcher->executableSwitches) {
QListWidgetItem *item;
item = new QListWidgetItem(ui->executables);
ui->executables->addItem(item);
ExecutableSwitchWidget *sw = new ExecutableSwitchWidget(&s);
item->setSizeHint(sw->minimumSizeHint());
ui->executables->setItemWidget(item, sw);
}
if (switcher->executableSwitches.size() == 0)
addPulse = PulseWidget(ui->executableAdd, QColor(Qt::green));
}
ExecutableSwitchWidget::ExecutableSwitchWidget(ExecutableSwitch *s)
: SwitchWidget(s, false)
{
processes = new QComboBox();
requiresFocus = new QCheckBox("only if focused");
whenLabel = new QLabel("When");
switchLabel = new QLabel("is running switch to");
usingLabel = new QLabel("using");
QWidget::connect(processes, SIGNAL(currentTextChanged(const QString &)),
this, SLOT(ProcessChanged(const QString &)));
QWidget::connect(requiresFocus, SIGNAL(stateChanged(int)), this,
SLOT(FocusChanged(int)));
AdvSceneSwitcher::populateProcessSelection(processes);
processes->setEditable(true);
processes->setMaxVisibleItems(20);
if (s) {
processes->setCurrentText(s->exe);
requiresFocus->setChecked(s->inFocus);
}
setStyleSheet("* { background-color: transparent; }");
QHBoxLayout *mainLayout = new QHBoxLayout;
mainLayout->addWidget(whenLabel);
mainLayout->addWidget(processes);
mainLayout->addWidget(switchLabel);
mainLayout->addWidget(scenes);
mainLayout->addWidget(usingLabel);
mainLayout->addWidget(transitions);
mainLayout->addWidget(requiresFocus);
mainLayout->addStretch();
setLayout(mainLayout);
switchData = s;
loading = false;
}
ExecutableSwitch *ExecutableSwitchWidget::getSwitchData()
{
return switchData;
}
void ExecutableSwitchWidget::setSwitchData(ExecutableSwitch *s)
{
switchData = s;
}
void ExecutableSwitchWidget::swapSwitchData(ExecutableSwitchWidget *s1,
ExecutableSwitchWidget *s2)
{
SwitchWidget::swapSwitchData(s1, s2);
ExecutableSwitch *t = s1->getSwitchData();
s1->setSwitchData(s2->getSwitchData());
s2->setSwitchData(t);
}
void ExecutableSwitchWidget::ProcessChanged(const QString &text)
{
if (loading || !switchData)
return;
std::lock_guard<std::mutex> lock(switcher->m);
switchData->exe = text;
}
void ExecutableSwitchWidget::FocusChanged(int state)
{
if (loading || !switchData)
return;
std::lock_guard<std::mutex> lock(switcher->m);
switchData->inFocus = state;
}

View File

@@ -1,15 +1,16 @@
#include <QFileDialog>
#include <QTextStream>
#include <QDateTime>
#include <obs.hpp>
#include <curl/curl.h>
#include "headers/advanced-scene-switcher.hpp"
#include "headers/curl-helper.hpp"
#include "headers/utility.hpp"
#define LOCAL_FILE_IDX 0
#define REMOTE_FILE_IDX 1
void SceneSwitcher::on_browseButton_clicked()
void AdvSceneSwitcher::on_browseButton_clicked()
{
QString path = QFileDialog::getOpenFileName(
this, tr("Select a file to write to ..."), QDir::currentPath(),
@@ -18,7 +19,7 @@ void SceneSwitcher::on_browseButton_clicked()
ui->writePathLineEdit->setText(path);
}
void SceneSwitcher::on_readFileCheckBox_stateChanged(int state)
void AdvSceneSwitcher::on_readFileCheckBox_stateChanged(int state)
{
if (loading)
return;
@@ -35,7 +36,7 @@ void SceneSwitcher::on_readFileCheckBox_stateChanged(int state)
}
}
void SceneSwitcher::on_readPathLineEdit_textChanged(const QString &text)
void AdvSceneSwitcher::on_readPathLineEdit_textChanged(const QString &text)
{
if (loading)
return;
@@ -49,7 +50,7 @@ void SceneSwitcher::on_readPathLineEdit_textChanged(const QString &text)
switcher->fileIO.readPath = text.toUtf8().constData();
}
void SceneSwitcher::on_writePathLineEdit_textChanged(const QString &text)
void AdvSceneSwitcher::on_writePathLineEdit_textChanged(const QString &text)
{
if (loading)
return;
@@ -63,7 +64,7 @@ void SceneSwitcher::on_writePathLineEdit_textChanged(const QString &text)
switcher->fileIO.writePath = text.toUtf8().constData();
}
void SceneSwitcher::on_browseButton_2_clicked()
void AdvSceneSwitcher::on_browseButton_2_clicked()
{
QString path = QFileDialog::getOpenFileName(
this, tr("Select a file to read from ..."), QDir::currentPath(),
@@ -124,17 +125,14 @@ static size_t WriteCallback(void *contents, size_t size, size_t nmemb,
std::string getRemoteData(std::string &url)
{
CURL *curl;
CURLcode res;
std::string readBuffer;
curl = curl_easy_init();
if (curl) {
curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &readBuffer);
res = curl_easy_perform(curl);
curl_easy_cleanup(curl);
if (switcher->curl && f_curl_setopt && f_curl_perform) {
f_curl_setopt(switcher->curl, CURLOPT_URL, url.c_str());
f_curl_setopt(switcher->curl, CURLOPT_WRITEFUNCTION,
WriteCallback);
f_curl_setopt(switcher->curl, CURLOPT_WRITEDATA, &readBuffer);
f_curl_perform(switcher->curl);
}
return readBuffer;
}
@@ -220,15 +218,13 @@ void SwitcherData::checkFileContent(bool &match, OBSWeakSource &scene,
match = true;
if (verbose)
blog(LOG_INFO,
"Advanced Scene Switcher file match");
s.logMatch();
break;
}
}
}
void SceneSwitcher::on_browseButton_3_clicked()
void AdvSceneSwitcher::on_browseButton_3_clicked()
{
QString path = QFileDialog::getOpenFileName(
this, tr("Select a file to read from ..."), QDir::currentPath(),
@@ -237,7 +233,7 @@ void SceneSwitcher::on_browseButton_3_clicked()
ui->filePathLineEdit->setText(path);
}
void SceneSwitcher::on_fileType_currentIndexChanged(int idx)
void AdvSceneSwitcher::on_fileType_currentIndexChanged(int idx)
{
if (idx == -1)
return;
@@ -253,7 +249,7 @@ void SceneSwitcher::on_fileType_currentIndexChanged(int idx)
}
}
void SceneSwitcher::on_fileAdd_clicked()
void AdvSceneSwitcher::on_fileAdd_clicked()
{
QString sceneName = ui->fileScenes->currentText();
QString transitionName = ui->fileTransitions->currentText();
@@ -285,7 +281,7 @@ void SceneSwitcher::on_fileAdd_clicked()
useRegex, useTime);
}
void SceneSwitcher::on_fileRemove_clicked()
void AdvSceneSwitcher::on_fileRemove_clicked()
{
QListWidgetItem *item = ui->fileScenesList->currentItem();
if (!item)
@@ -304,7 +300,7 @@ void SceneSwitcher::on_fileRemove_clicked()
qDeleteAll(ui->fileScenesList->selectedItems());
}
void SceneSwitcher::on_fileScenesList_currentRowChanged(int idx)
void AdvSceneSwitcher::on_fileScenesList_currentRowChanged(int idx)
{
if (loading)
return;
@@ -313,7 +309,7 @@ void SceneSwitcher::on_fileScenesList_currentRowChanged(int idx)
std::lock_guard<std::mutex> lock(switcher->m);
if (switcher->fileSwitches.size() <= idx)
if ((int)switcher->fileSwitches.size() <= idx)
return;
FileSwitch s = switcher->fileSwitches[idx];
@@ -332,7 +328,7 @@ void SceneSwitcher::on_fileScenesList_currentRowChanged(int idx)
ui->fileContentTimeCheckBox->setChecked(s.useTime);
}
void SceneSwitcher::on_fileUp_clicked()
void AdvSceneSwitcher::on_fileUp_clicked()
{
int index = ui->fileScenesList->currentRow();
if (index != -1 && index != 0) {
@@ -347,7 +343,7 @@ void SceneSwitcher::on_fileUp_clicked()
}
}
void SceneSwitcher::on_fileDown_clicked()
void AdvSceneSwitcher::on_fileDown_clicked()
{
int index = ui->fileScenesList->currentRow();
if (index != -1 && index != ui->fileScenesList->count() - 1) {
@@ -385,10 +381,9 @@ void SwitcherData::saveFileSwitches(obs_data_t *obj)
obs_data_set_bool(array_obj, "useRegex", s.useRegex);
obs_data_set_bool(array_obj, "useTime", s.useTime);
obs_data_array_push_back(fileArray, array_obj);
obs_source_release(source);
obs_source_release(transition);
}
obs_source_release(source);
obs_source_release(transition);
obs_data_release(array_obj);
}
obs_data_set_array(obj, "fileSwitches", fileArray);
@@ -436,7 +431,7 @@ void SwitcherData::loadFileSwitches(obs_data_t *obj)
switcher->fileIO.writePath = obs_data_get_string(obj, "writePath");
}
void SceneSwitcher::setupFileTab()
void AdvSceneSwitcher::setupFileTab()
{
populateSceneSelection(ui->fileScenes, false);
populateTransitionSelection(ui->fileTransitions);
@@ -447,6 +442,9 @@ void SceneSwitcher::setupFileTab()
"Note that the scene switcher will try to access the remote location every " +
QString::number(switcher->interval) + "ms");
if (switcher->curl)
ui->libcurlWarning->setVisible(false);
for (auto &s : switcher->fileSwitches) {
std::string sceneName = GetWeakSourceName(s.scene);
std::string transitionName = GetWeakSourceName(s.transition);
@@ -473,3 +471,27 @@ void SceneSwitcher::setupFileTab()
ui->readPathLineEdit->setDisabled(true);
}
}
static inline QString MakeFileSwitchName(const QString &scene,
const QString &transition,
const QString &fileName,
const QString &text, bool useRegex,
bool useTime)
{
QString switchName = QStringLiteral("Switch to ") + scene +
QStringLiteral(" using ") + transition +
QStringLiteral(" if ") + fileName;
if (useTime)
switchName += QStringLiteral(" was modified and");
switchName += QStringLiteral(" contains");
if (useRegex)
switchName += QStringLiteral(" (RegEx): \n\"");
else
switchName += QStringLiteral(": \n\"");
if (text.length() > 30)
switchName += text.left(27) + QStringLiteral("...\"");
else
switchName += text + QStringLiteral("\"");
return switchName;
}

86
src/switch-generic.cpp Normal file
View File

@@ -0,0 +1,86 @@
#include "headers/advanced-scene-switcher.hpp"
#include "headers/utility.hpp"
bool SceneSwitcherEntry::initialized()
{
return (usePreviousScene || WeakSourceValid(scene)) && transition;
}
bool SceneSwitcherEntry::valid()
{
return !initialized() ||
((usePreviousScene || WeakSourceValid(scene)) &&
WeakSourceValid(transition));
}
void SceneSwitcherEntry::logMatch()
{
const char *sceneName = previous_scene_name;
if (!usePreviousScene) {
OBSSource s = obs_weak_source_get_source(scene);
sceneName = obs_source_get_name(s);
}
blog(LOG_INFO, "match for '%s' - switch to scene '%s'", getType(),
sceneName);
}
SwitchWidget::SwitchWidget(SceneSwitcherEntry *s, bool usePreviousScene)
{
scenes = new QComboBox();
transitions = new QComboBox();
QWidget::connect(scenes, SIGNAL(currentTextChanged(const QString &)),
this, SLOT(SceneChanged(const QString &)));
QWidget::connect(transitions,
SIGNAL(currentTextChanged(const QString &)), this,
SLOT(TransitionChanged(const QString &)));
AdvSceneSwitcher::populateSceneSelection(scenes, usePreviousScene);
AdvSceneSwitcher::populateTransitionSelection(transitions);
if (s) {
if (s->usePreviousScene)
scenes->setCurrentText(previous_scene_name);
else
scenes->setCurrentText(
GetWeakSourceName(s->scene).c_str());
transitions->setCurrentText(
GetWeakSourceName(s->transition).c_str());
}
switchData = s;
}
SceneSwitcherEntry *SwitchWidget::getSwitchData()
{
return switchData;
}
void SwitchWidget::setSwitchData(SceneSwitcherEntry *s)
{
switchData = s;
}
void SwitchWidget::swapSwitchData(SwitchWidget *s1, SwitchWidget *s2)
{
SceneSwitcherEntry *t = s1->getSwitchData();
s1->setSwitchData(s2->getSwitchData());
s2->setSwitchData(t);
}
void SwitchWidget::SceneChanged(const QString &text)
{
if (loading || !switchData)
return;
std::lock_guard<std::mutex> lock(switcher->m);
switchData->usePreviousScene = text.compare(previous_scene_name) == 0;
switchData->scene = GetWeakSourceByQString(text);
}
void SwitchWidget::TransitionChanged(const QString &text)
{
if (loading || !switchData)
return;
std::lock_guard<std::mutex> lock(switcher->m);
switchData->transition = GetWeakTransitionByQString(text);
}

View File

@@ -1,4 +1,7 @@
#include <regex>
#include "headers/advanced-scene-switcher.hpp"
#include "headers/utility.hpp"
void SwitcherData::checkIdleSwitch(bool &match, OBSWeakSource &scene,
OBSWeakSource &transition)
@@ -43,12 +46,12 @@ void SwitcherData::checkIdleSwitch(bool &match, OBSWeakSource &scene,
idleData.alreadySwitched = true;
if (verbose)
blog(LOG_INFO, "Advanced Scene Switcher idle match");
idleData.logMatch();
} else
idleData.alreadySwitched = false;
}
void SceneSwitcher::on_idleCheckBox_stateChanged(int state)
void AdvSceneSwitcher::on_idleCheckBox_stateChanged(int state)
{
if (loading)
return;
@@ -72,15 +75,15 @@ void SceneSwitcher::on_idleCheckBox_stateChanged(int state)
}
}
void SceneSwitcher::UpdateIdleDataTransition(const QString &name)
void AdvSceneSwitcher::UpdateIdleDataTransition(const QString &name)
{
obs_weak_source_t *transition = GetWeakTransitionByQString(name);
switcher->idleData.transition = transition;
}
void SceneSwitcher::UpdateIdleDataScene(const QString &name)
void AdvSceneSwitcher::UpdateIdleDataScene(const QString &name)
{
switcher->idleData.usePreviousScene = (name == PREVIOUS_SCENE_NAME);
switcher->idleData.usePreviousScene = (name == previous_scene_name);
obs_source_t *scene = obs_get_source_by_name(name.toUtf8().constData());
obs_weak_source_t *ws = obs_source_get_weak_source(scene);
@@ -90,7 +93,7 @@ void SceneSwitcher::UpdateIdleDataScene(const QString &name)
obs_source_release(scene);
}
void SceneSwitcher::on_idleTransitions_currentTextChanged(const QString &text)
void AdvSceneSwitcher::on_idleTransitions_currentTextChanged(const QString &text)
{
if (loading)
return;
@@ -99,7 +102,7 @@ void SceneSwitcher::on_idleTransitions_currentTextChanged(const QString &text)
UpdateIdleDataTransition(text);
}
void SceneSwitcher::on_idleScenes_currentTextChanged(const QString &text)
void AdvSceneSwitcher::on_idleScenes_currentTextChanged(const QString &text)
{
if (loading)
return;
@@ -108,7 +111,7 @@ void SceneSwitcher::on_idleScenes_currentTextChanged(const QString &text)
UpdateIdleDataScene(text);
}
void SceneSwitcher::on_idleSpinBox_valueChanged(int i)
void AdvSceneSwitcher::on_idleSpinBox_valueChanged(int i)
{
if (loading)
return;
@@ -116,7 +119,7 @@ void SceneSwitcher::on_idleSpinBox_valueChanged(int i)
switcher->idleData.time = i;
}
void SceneSwitcher::on_ignoreIdleWindows_currentRowChanged(int idx)
void AdvSceneSwitcher::on_ignoreIdleWindows_currentRowChanged(int idx)
{
if (loading)
return;
@@ -136,7 +139,7 @@ void SceneSwitcher::on_ignoreIdleWindows_currentRowChanged(int idx)
}
}
void SceneSwitcher::on_ignoreIdleAdd_clicked()
void AdvSceneSwitcher::on_ignoreIdleAdd_clicked()
{
QString windowName = ui->ignoreIdleWindowsWindows->currentText();
@@ -160,7 +163,7 @@ void SceneSwitcher::on_ignoreIdleAdd_clicked()
}
}
void SceneSwitcher::on_ignoreIdleRemove_clicked()
void AdvSceneSwitcher::on_ignoreIdleRemove_clicked()
{
QListWidgetItem *item = ui->ignoreIdleWindows->currentItem();
if (!item)
@@ -185,7 +188,7 @@ void SceneSwitcher::on_ignoreIdleRemove_clicked()
delete item;
}
int SceneSwitcher::IgnoreIdleWindowsFindByData(const QString &window)
int AdvSceneSwitcher::IgnoreIdleWindowsFindByData(const QString &window)
{
int count = ui->ignoreIdleWindows->count();
int idx = -1;
@@ -221,7 +224,7 @@ void SwitcherData::saveIdleSwitches(obs_data_t *obj)
obs_data_set_bool(obj, "idleEnable", switcher->idleData.idleEnable);
obs_data_set_string(obj, "idleSceneName",
switcher->idleData.usePreviousScene
? PREVIOUS_SCENE_NAME
? previous_scene_name
: idleSceneName.c_str());
obs_data_set_string(obj, "idleTransitionName",
idleTransitionName.c_str());
@@ -256,13 +259,13 @@ void SwitcherData::loadIdleSwitches(obs_data_t *obj)
GetWeakTransitionByName(idleTransitionName.c_str());
obs_data_set_default_bool(obj, "idleEnable", false);
switcher->idleData.idleEnable = obs_data_get_bool(obj, "idleEnable");
obs_data_set_default_int(obj, "idleTime", DEFAULT_IDLE_TIME);
obs_data_set_default_int(obj, "idleTime", default_idle_time);
switcher->idleData.time = obs_data_get_int(obj, "idleTime");
switcher->idleData.usePreviousScene =
(idleSceneName == PREVIOUS_SCENE_NAME);
(idleSceneName == previous_scene_name);
}
void SceneSwitcher::setupIdleTab()
void AdvSceneSwitcher::setupIdleTab()
{
populateSceneSelection(ui->idleScenes, true);
populateTransitionSelection(ui->idleTransitions);
@@ -279,7 +282,7 @@ void SceneSwitcher::setupIdleTab()
ui->idleCheckBox->setChecked(switcher->idleData.idleEnable);
ui->idleScenes->setCurrentText(
switcher->idleData.usePreviousScene
? PREVIOUS_SCENE_NAME
? previous_scene_name
: GetWeakSourceName(switcher->idleData.scene).c_str());
ui->idleTransitions->setCurrentText(
GetWeakSourceName(switcher->idleData.transition).c_str());

535
src/switch-media.cpp Normal file
View File

@@ -0,0 +1,535 @@
#include "headers/advanced-scene-switcher.hpp"
#include "headers/utility.hpp"
static QMetaObject::Connection addPulse;
void AdvSceneSwitcher::on_mediaAdd_clicked()
{
ui->mediaAdd->disconnect(addPulse);
std::lock_guard<std::mutex> lock(switcher->m);
switcher->mediaSwitches.emplace_back();
QListWidgetItem *item;
item = new QListWidgetItem(ui->mediaSwitches);
ui->mediaSwitches->addItem(item);
MediaSwitchWidget *sw =
new MediaSwitchWidget(&switcher->mediaSwitches.back());
item->setSizeHint(sw->minimumSizeHint());
ui->mediaSwitches->setItemWidget(item, sw);
}
void AdvSceneSwitcher::on_mediaRemove_clicked()
{
QListWidgetItem *item = ui->mediaSwitches->currentItem();
if (!item)
return;
{
std::lock_guard<std::mutex> lock(switcher->m);
int idx = ui->mediaSwitches->currentRow();
auto &switches = switcher->mediaSwitches;
switches.erase(switches.begin() + idx);
}
delete item;
}
void AdvSceneSwitcher::on_mediaUp_clicked()
{
int index = ui->mediaSwitches->currentRow();
if (!listMoveUp(ui->mediaSwitches))
return;
MediaSwitchWidget *s1 =
(MediaSwitchWidget *)ui->mediaSwitches->itemWidget(
ui->mediaSwitches->item(index));
MediaSwitchWidget *s2 =
(MediaSwitchWidget *)ui->mediaSwitches->itemWidget(
ui->mediaSwitches->item(index - 1));
MediaSwitchWidget::swapSwitchData(s1, s2);
std::lock_guard<std::mutex> lock(switcher->m);
std::swap(switcher->mediaSwitches[index],
switcher->mediaSwitches[index - 1]);
}
void AdvSceneSwitcher::on_mediaDown_clicked()
{
int index = ui->mediaSwitches->currentRow();
if (!listMoveDown(ui->mediaSwitches))
return;
MediaSwitchWidget *s1 =
(MediaSwitchWidget *)ui->mediaSwitches->itemWidget(
ui->mediaSwitches->item(index));
MediaSwitchWidget *s2 =
(MediaSwitchWidget *)ui->mediaSwitches->itemWidget(
ui->mediaSwitches->item(index + 1));
MediaSwitchWidget::swapSwitchData(s1, s2);
std::lock_guard<std::mutex> lock(switcher->m);
std::swap(switcher->mediaSwitches[index],
switcher->mediaSwitches[index + 1]);
}
void SwitcherData::checkMediaSwitch(bool &match, OBSWeakSource &scene,
OBSWeakSource &transition)
{
for (MediaSwitch &mediaSwitch : mediaSwitches) {
if (!mediaSwitch.initialized())
continue;
obs_source_t *source =
obs_weak_source_get_source(mediaSwitch.source);
auto duration = obs_source_media_get_duration(source);
auto time = obs_source_media_get_time(source);
obs_media_state state = obs_source_media_get_state(source);
obs_source_release(source);
bool matchedStopped = mediaSwitch.state ==
OBS_MEDIA_STATE_STOPPED &&
mediaSwitch.stopped;
// The signal for the state ended is intentionally not used here
// so matchedEnded can be used to specify the end of a VLC playlist
// by two consequtive matches of OBS_MEDIA_STATE_ENDED
//
// This was done to reduce the likelyhood of interpreting a single
// OBS_MEDIA_STATE_ENDED caught by obs_source_media_get_state()
// as the end of the playlist of the VLC source, while actually being
// in the middle of switching to the next item of the playlist
//
// If there is a separate obs_media_sate in the future for the
// "end of playlist reached" signal the line below can be used
// and an additional check for this new singal can be introduced
//
// bool matchedEnded = mediaSwitch.state == OBS_MEDIA_STATE_ENDED &&
// mediaSwitch.ended;
bool matchedEnded = false;
if (state == OBS_MEDIA_STATE_ENDED) {
matchedEnded = mediaSwitch.previousStateEnded;
mediaSwitch.previousStateEnded = true;
} else {
mediaSwitch.previousStateEnded = false;
}
// reset for next check
mediaSwitch.stopped = false;
mediaSwitch.ended = false;
bool matchedState = ((state == mediaSwitch.state) ||
mediaSwitch.anyState) ||
matchedStopped || matchedEnded;
bool matchedTimeNone =
(mediaSwitch.restriction == TIME_RESTRICTION_NONE);
bool matchedTimeLonger =
(mediaSwitch.restriction == TIME_RESTRICTION_LONGER) &&
(time > mediaSwitch.time);
bool matchedTimeShorter =
(mediaSwitch.restriction == TIME_RESTRICTION_SHORTER) &&
(time < mediaSwitch.time);
bool matchedTimeRemainLonger =
(mediaSwitch.restriction ==
TIME_RESTRICTION_REMAINING_LONGER) &&
(duration > time && duration - time > mediaSwitch.time);
bool matchedTimeRemainShorter =
(mediaSwitch.restriction ==
TIME_RESTRICTION_REMAINING_SHORTER) &&
(duration > time && duration - time < mediaSwitch.time);
bool matchedTime = matchedTimeNone || matchedTimeLonger ||
matchedTimeShorter ||
matchedTimeRemainLonger ||
matchedTimeRemainShorter;
bool matched = matchedState && matchedTime;
if (matched) {
match = true;
scene = (mediaSwitch.usePreviousScene)
? previousScene
: mediaSwitch.scene;
transition = mediaSwitch.transition;
if (verbose)
mediaSwitch.logMatch();
break;
}
}
}
void SwitcherData::saveMediaSwitches(obs_data_t *obj)
{
obs_data_array_t *mediaArray = obs_data_array_create();
for (MediaSwitch &s : switcher->mediaSwitches) {
obs_data_t *array_obj = obs_data_create();
obs_source_t *source = obs_weak_source_get_source(s.source);
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) && source &&
transition) {
const char *sourceName = obs_source_get_name(source);
const char *sceneName =
obs_source_get_name(sceneSource);
const char *transitionName =
obs_source_get_name(transition);
obs_data_set_string(array_obj, "source", sourceName);
obs_data_set_string(array_obj, "scene",
s.usePreviousScene
? previous_scene_name
: sceneName);
obs_data_set_string(array_obj, "transition",
transitionName);
obs_data_set_int(array_obj, "state", s.state);
obs_data_set_int(array_obj, "restriction",
s.restriction);
obs_data_set_int(array_obj, "time", s.time);
obs_data_array_push_back(mediaArray, array_obj);
}
obs_source_release(source);
obs_source_release(sceneSource);
obs_source_release(transition);
obs_data_release(array_obj);
}
obs_data_set_array(obj, "mediaSwitches", mediaArray);
obs_data_array_release(mediaArray);
}
void SwitcherData::loadMediaSwitches(obs_data_t *obj)
{
obs_data_array_t *mediaArray = obs_data_get_array(obj, "mediaSwitches");
switcher->mediaSwitches.clear();
size_t count = obs_data_array_count(mediaArray);
for (size_t i = 0; i < count; i++) {
obs_data_t *array_obj = obs_data_array_item(mediaArray, i);
const char *source = obs_data_get_string(array_obj, "source");
const char *scene = obs_data_get_string(array_obj, "scene");
const char *transition =
obs_data_get_string(array_obj, "transition");
obs_media_state state =
(obs_media_state)obs_data_get_int(array_obj, "state");
time_restriction restriction =
(time_restriction)obs_data_get_int(array_obj,
"restriction");
uint64_t time = obs_data_get_int(array_obj, "time");
switcher->mediaSwitches.emplace_back(
GetWeakSourceByName(scene), GetWeakSourceByName(source),
GetWeakTransitionByName(transition), state, restriction,
time, (strcmp(scene, previous_scene_name) == 0));
obs_data_release(array_obj);
}
obs_data_array_release(mediaArray);
}
void AdvSceneSwitcher::setupMediaTab()
{
for (auto &s : switcher->mediaSwitches) {
QListWidgetItem *item;
item = new QListWidgetItem(ui->mediaSwitches);
ui->mediaSwitches->addItem(item);
MediaSwitchWidget *sw = new MediaSwitchWidget(&s);
item->setSizeHint(sw->minimumSizeHint());
ui->mediaSwitches->setItemWidget(item, sw);
}
if (switcher->mediaSwitches.size() == 0)
addPulse = PulseWidget(ui->mediaAdd, QColor(Qt::green));
}
bool MediaSwitch::initialized()
{
return SceneSwitcherEntry::initialized() && source;
}
bool MediaSwitch::valid()
{
return !initialized() ||
(SceneSwitcherEntry::valid() && WeakSourceValid(source));
}
void MediaSwitch::clearSignalHandler()
{
obs_source_t *mediasource = obs_weak_source_get_source(source);
signal_handler_t *sh = obs_source_get_signal_handler(mediasource);
signal_handler_disconnect(sh, "media_stopped", MediaStopped, this);
signal_handler_disconnect(sh, "media_ended", MediaEnded, this);
obs_source_release(mediasource);
}
void MediaSwitch::resetSignalHandler()
{
obs_source_t *mediasource = obs_weak_source_get_source(source);
signal_handler_t *sh = obs_source_get_signal_handler(mediasource);
signal_handler_disconnect(sh, "media_stopped", MediaStopped, this);
signal_handler_disconnect(sh, "media_ended", MediaEnded, this);
signal_handler_connect(sh, "media_stopped", MediaStopped, this);
signal_handler_connect(sh, "media_ended", MediaEnded, this);
obs_source_release(mediasource);
}
void MediaSwitch::MediaStopped(void *data, calldata_t *)
{
MediaSwitch *media = static_cast<MediaSwitch *>(data);
media->stopped = true;
}
void MediaSwitch::MediaEnded(void *data, calldata_t *)
{
MediaSwitch *media = static_cast<MediaSwitch *>(data);
media->ended = true;
}
inline MediaSwitch::MediaSwitch(OBSWeakSource scene_, OBSWeakSource source_,
OBSWeakSource transition_,
obs_media_state state_,
time_restriction restriction_, uint64_t time_,
bool usePreviousScene_)
: SceneSwitcherEntry(scene_, transition_, usePreviousScene_),
source(source_),
state(state_),
restriction(restriction_),
time(time_)
{
anyState = state > 7;
obs_source_t *mediasource = obs_weak_source_get_source(source);
signal_handler_t *sh = obs_source_get_signal_handler(mediasource);
signal_handler_connect(sh, "media_stopped", MediaStopped, this);
signal_handler_connect(sh, "media_ended", MediaEnded, this);
obs_source_release(mediasource);
}
MediaSwitch::MediaSwitch(const MediaSwitch &other)
: SceneSwitcherEntry(other.scene, other.transition,
other.usePreviousScene),
source(other.source),
state(other.state),
restriction(other.restriction),
time(other.time)
{
anyState = state > 7;
obs_source_t *mediasource = obs_weak_source_get_source(source);
signal_handler_t *sh = obs_source_get_signal_handler(mediasource);
signal_handler_connect(sh, "media_stopped", MediaStopped, this);
signal_handler_connect(sh, "media_ended", MediaEnded, this);
obs_source_release(mediasource);
}
MediaSwitch::MediaSwitch(MediaSwitch &&other)
: SceneSwitcherEntry(other.scene, other.transition,
other.usePreviousScene),
source(other.source),
state(other.state),
restriction(other.restriction),
time(other.time),
anyState(other.anyState)
{
}
MediaSwitch::~MediaSwitch()
{
obs_source_t *mediasource = obs_weak_source_get_source(source);
signal_handler_t *sh = obs_source_get_signal_handler(mediasource);
signal_handler_disconnect(sh, "media_stopped", MediaStopped, this);
signal_handler_disconnect(sh, "media_ended", MediaEnded, this);
obs_source_release(mediasource);
}
MediaSwitch &MediaSwitch::operator=(const MediaSwitch &other)
{
MediaSwitch t(other);
swap(*this, t);
return *this = MediaSwitch(other);
}
MediaSwitch &MediaSwitch::operator=(MediaSwitch &&other) noexcept
{
if (this == &other) {
return *this;
}
swap(*this, other);
obs_source_t *mediasource = obs_weak_source_get_source(other.source);
signal_handler_t *sh = obs_source_get_signal_handler(mediasource);
signal_handler_disconnect(sh, "media_stopped", MediaStopped, &other);
signal_handler_disconnect(sh, "media_ended", MediaEnded, &other);
obs_source_release(mediasource);
return *this;
}
void swap(MediaSwitch &first, MediaSwitch &second)
{
std::swap(first.scene, second.scene);
std::swap(first.transition, second.transition);
std::swap(first.usePreviousScene, second.usePreviousScene);
std::swap(first.source, second.source);
std::swap(first.state, second.state);
std::swap(first.restriction, second.restriction);
std::swap(first.time, second.time);
std::swap(first.anyState, second.anyState);
first.resetSignalHandler();
second.resetSignalHandler();
}
void populateMediaStates(QComboBox *list)
{
list->addItem("None");
list->addItem("Playing");
list->addItem("Opening");
list->addItem("Buffering");
list->addItem("Paused");
list->addItem("Stopped");
list->addItem("Ended");
list->addItem("Error");
list->addItem("Any");
}
void populateTimeRestrictions(QComboBox *list)
{
list->addItem("None");
list->addItem("Time shorter");
list->addItem("Time longer");
list->addItem("Time remaining shorter");
list->addItem("Time remaining longer");
}
MediaSwitchWidget::MediaSwitchWidget(MediaSwitch *s) : SwitchWidget(s)
{
meidaSources = new QComboBox();
states = new QComboBox();
timeRestrictions = new QComboBox();
time = new QSpinBox();
whenLabel = new QLabel("When");
stateLabel = new QLabel("state is");
andLabel = new QLabel("and");
switchLabel = new QLabel("switch to");
usingLabel = new QLabel("using");
time->setSuffix("ms");
time->setMaximum(99999999);
time->setMinimum(0);
QWidget::connect(meidaSources,
SIGNAL(currentTextChanged(const QString &)), this,
SLOT(SourceChanged(const QString &)));
QWidget::connect(states, SIGNAL(currentIndexChanged(int)), this,
SLOT(StateChanged(int)));
QWidget::connect(timeRestrictions, SIGNAL(currentIndexChanged(int)),
this, SLOT(TimeRestrictionChanged(int)));
QWidget::connect(time, SIGNAL(valueChanged(int)), this,
SLOT(TimeChanged(int)));
AdvSceneSwitcher::populateMediaSelection(meidaSources);
populateMediaStates(states);
populateTimeRestrictions(timeRestrictions);
if (s) {
meidaSources->setCurrentText(
GetWeakSourceName(s->source).c_str());
states->setCurrentIndex(s->state);
timeRestrictions->setCurrentIndex(s->restriction);
time->setValue(s->time);
if (s->restriction == TIME_RESTRICTION_NONE)
time->setDisabled(true);
}
setStyleSheet("* { background-color: transparent; }");
QHBoxLayout *mainLayout = new QHBoxLayout;
mainLayout->addWidget(whenLabel);
mainLayout->addWidget(meidaSources);
mainLayout->addWidget(stateLabel);
mainLayout->addWidget(states);
mainLayout->addWidget(andLabel);
mainLayout->addWidget(timeRestrictions);
mainLayout->addWidget(time);
mainLayout->addWidget(switchLabel);
mainLayout->addWidget(scenes);
mainLayout->addWidget(usingLabel);
mainLayout->addWidget(transitions);
mainLayout->addStretch();
setLayout(mainLayout);
switchData = s;
loading = false;
}
MediaSwitch *MediaSwitchWidget::getSwitchData()
{
return switchData;
}
void MediaSwitchWidget::setSwitchData(MediaSwitch *s)
{
switchData = s;
}
void MediaSwitchWidget::swapSwitchData(MediaSwitchWidget *s1,
MediaSwitchWidget *s2)
{
SwitchWidget::swapSwitchData(s1, s2);
MediaSwitch *t = s1->getSwitchData();
s1->setSwitchData(s2->getSwitchData());
s2->setSwitchData(t);
}
void MediaSwitchWidget::SourceChanged(const QString &text)
{
if (loading || !switchData)
return;
std::lock_guard<std::mutex> lock(switcher->m);
switchData->clearSignalHandler();
switchData->source = GetWeakSourceByQString(text);
switchData->resetSignalHandler();
}
void MediaSwitchWidget::StateChanged(int index)
{
if (loading || !switchData)
return;
std::lock_guard<std::mutex> lock(switcher->m);
switchData->state = (obs_media_state)index;
switchData->anyState = switchData->state > 7;
}
void MediaSwitchWidget::TimeRestrictionChanged(int index)
{
if (loading || !switchData)
return;
if ((time_restriction)index == TIME_RESTRICTION_NONE)
time->setDisabled(true);
else
time->setDisabled(false);
std::lock_guard<std::mutex> lock(switcher->m);
switchData->restriction = (time_restriction)index;
}
void MediaSwitchWidget::TimeChanged(int time)
{
if (loading || !switchData)
return;
std::lock_guard<std::mutex> lock(switcher->m);
switchData->time = time;
}

View File

@@ -1,6 +1,9 @@
#include "headers/advanced-scene-switcher.hpp"
#include <regex>
void SceneSwitcher::on_pauseScenesAdd_clicked()
#include "headers/advanced-scene-switcher.hpp"
#include "headers/utility.hpp"
void AdvSceneSwitcher::on_pauseScenesAdd_clicked()
{
QString sceneName = ui->pauseScenesScenes->currentText();
@@ -24,7 +27,7 @@ void SceneSwitcher::on_pauseScenesAdd_clicked()
}
}
void SceneSwitcher::on_pauseScenesRemove_clicked()
void AdvSceneSwitcher::on_pauseScenesRemove_clicked()
{
QListWidgetItem *item = ui->pauseScenes->currentItem();
if (!item)
@@ -49,7 +52,7 @@ void SceneSwitcher::on_pauseScenesRemove_clicked()
delete item;
}
void SceneSwitcher::on_pauseWindowsAdd_clicked()
void AdvSceneSwitcher::on_pauseWindowsAdd_clicked()
{
QString windowName = ui->pauseWindowsWindows->currentText();
@@ -73,7 +76,7 @@ void SceneSwitcher::on_pauseWindowsAdd_clicked()
}
}
void SceneSwitcher::on_pauseWindowsRemove_clicked()
void AdvSceneSwitcher::on_pauseWindowsRemove_clicked()
{
QListWidgetItem *item = ui->pauseWindows->currentItem();
if (!item)
@@ -98,7 +101,7 @@ void SceneSwitcher::on_pauseWindowsRemove_clicked()
delete item;
}
void SceneSwitcher::on_pauseScenes_currentRowChanged(int idx)
void AdvSceneSwitcher::on_pauseScenes_currentRowChanged(int idx)
{
if (loading)
return;
@@ -119,7 +122,7 @@ void SceneSwitcher::on_pauseScenes_currentRowChanged(int idx)
}
}
void SceneSwitcher::on_pauseWindows_currentRowChanged(int idx)
void AdvSceneSwitcher::on_pauseWindows_currentRowChanged(int idx)
{
if (loading)
return;
@@ -139,7 +142,7 @@ void SceneSwitcher::on_pauseWindows_currentRowChanged(int idx)
}
}
int SceneSwitcher::PauseScenesFindByData(const QString &scene)
int AdvSceneSwitcher::PauseScenesFindByData(const QString &scene)
{
int count = ui->pauseScenes->count();
int idx = -1;
@@ -157,7 +160,7 @@ int SceneSwitcher::PauseScenesFindByData(const QString &scene)
return idx;
}
int SceneSwitcher::PauseWindowsFindByData(const QString &window)
int AdvSceneSwitcher::PauseWindowsFindByData(const QString &window)
{
int count = ui->pauseWindows->count();
int idx = -1;
@@ -221,7 +224,7 @@ bool SwitcherData::checkPause()
}
if (verbose && pause)
blog(LOG_INFO, "Advanced Scene Switcher pause match");
blog(LOG_INFO, "pause match");
return pause;
}
@@ -237,9 +240,8 @@ void SwitcherData::savePauseSwitches(obs_data_t *obj)
const char *n = obs_source_get_name(source);
obs_data_set_string(array_obj, "pauseScene", n);
obs_data_array_push_back(pauseScenesArray, array_obj);
obs_source_release(source);
}
obs_source_release(source);
obs_data_release(array_obj);
}
obs_data_set_array(obj, "pauseScenes", pauseScenesArray);
@@ -298,7 +300,7 @@ void SwitcherData::loadPauseSwitches(obs_data_t *obj)
obs_data_array_release(pauseWindowsArray);
}
void SceneSwitcher::setupPauseTab()
void AdvSceneSwitcher::setupPauseTab()
{
populateSceneSelection(ui->pauseScenesScenes, false);
populateWindowSelection(ui->pauseWindowsWindows);

View File

@@ -1,10 +1,11 @@
#include <algorithm>
#include "headers/advanced-scene-switcher.hpp"
void SceneSwitcher::on_threadPriority_currentTextChanged(const QString &text)
void AdvSceneSwitcher::on_threadPriority_currentTextChanged(const QString &text)
{
if (loading ||
ui->threadPriority->count() != switcher->threadPriorities.size())
if (loading || ui->threadPriority->count() !=
(int)switcher->threadPriorities.size())
return;
std::lock_guard<std::mutex> lock(switcher->m);
@@ -17,7 +18,7 @@ void SceneSwitcher::on_threadPriority_currentTextChanged(const QString &text)
}
}
void SceneSwitcher::on_priorityUp_clicked()
void AdvSceneSwitcher::on_priorityUp_clicked()
{
int currentIndex = ui->priorityList->currentRow();
if (currentIndex != -1 && currentIndex != 0) {
@@ -34,7 +35,7 @@ void SceneSwitcher::on_priorityUp_clicked()
}
}
void SceneSwitcher::on_priorityDown_clicked()
void AdvSceneSwitcher::on_priorityDown_clicked()
{
int currentIndex = ui->priorityList->currentRow();
if (currentIndex != -1 &&
@@ -61,7 +62,7 @@ bool SwitcherData::prioFuncsValid()
return false;
for (int p : functionNamesByPriority) {
if (p < 0 || p > 7)
if (p < 0 || p > 8)
return false;
}
return true;

205
src/switch-random.cpp Normal file
View File

@@ -0,0 +1,205 @@
#include <random>
#include "headers/advanced-scene-switcher.hpp"
#include "headers/utility.hpp"
static QMetaObject::Connection addPulse;
void AdvSceneSwitcher::on_randomAdd_clicked()
{
ui->randomAdd->disconnect(addPulse);
std::lock_guard<std::mutex> lock(switcher->m);
switcher->randomSwitches.emplace_back();
QListWidgetItem *item;
item = new QListWidgetItem(ui->randomSwitches);
ui->randomSwitches->addItem(item);
RandomSwitchWidget *sw =
new RandomSwitchWidget(&switcher->randomSwitches.back());
item->setSizeHint(sw->minimumSizeHint());
ui->randomSwitches->setItemWidget(item, sw);
}
void AdvSceneSwitcher::on_randomRemove_clicked()
{
QListWidgetItem *item = ui->randomSwitches->currentItem();
if (!item)
return;
{
std::lock_guard<std::mutex> lock(switcher->m);
int idx = ui->randomSwitches->currentRow();
auto &switches = switcher->randomSwitches;
switches.erase(switches.begin() + idx);
}
delete item;
}
void SwitcherData::checkRandom(bool &match, OBSWeakSource &scene,
OBSWeakSource &transition, int &delay)
{
if (randomSwitches.size() == 0)
return;
std::deque<RandomSwitch> rs(randomSwitches);
std::random_device rng;
std::mt19937 urng(rng());
std::shuffle(rs.begin(), rs.end(), urng);
for (RandomSwitch &r : rs) {
if (!r.initialized())
continue;
if (r.scene == lastRandomScene)
continue;
scene = r.scene;
transition = r.transition;
delay = (int)r.delay * 1000;
match = true;
lastRandomScene = r.scene;
if (verbose)
r.logMatch();
break;
}
}
void SwitcherData::saveRandomSwitches(obs_data_t *obj)
{
obs_data_array_t *randomArray = obs_data_array_create();
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_array_push_back(randomArray, array_obj);
}
obs_source_release(source);
obs_source_release(transition);
obs_data_release(array_obj);
}
obs_data_set_array(obj, "randomSwitches", randomArray);
obs_data_array_release(randomArray);
}
void SwitcherData::loadRandomSwitches(obs_data_t *obj)
{
switcher->randomSwitches.clear();
obs_data_array_t *randomArray =
obs_data_get_array(obj, "randomSwitches");
size_t 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");
switcher->randomSwitches.emplace_back(
GetWeakSourceByName(scene),
GetWeakTransitionByName(transition), delay);
obs_data_release(array_obj);
}
obs_data_array_release(randomArray);
}
void AdvSceneSwitcher::setupRandomTab()
{
for (auto &s : switcher->randomSwitches) {
QListWidgetItem *item;
item = new QListWidgetItem(ui->randomSwitches);
ui->randomSwitches->addItem(item);
RandomSwitchWidget *sw = new RandomSwitchWidget(&s);
item->setSizeHint(sw->minimumSizeHint());
ui->randomSwitches->setItemWidget(item, sw);
}
if (switcher->randomSwitches.size() == 0)
addPulse = PulseWidget(ui->randomAdd, QColor(Qt::green));
if (switcher->switchIfNotMatching != RANDOM_SWITCH)
PulseWidget(ui->randomDisabledWarning, QColor(Qt::red),
QColor(0, 0, 0, 0), "QLabel ");
else
ui->randomDisabledWarning->setVisible(false);
}
RandomSwitchWidget::RandomSwitchWidget(RandomSwitch *s) : SwitchWidget(s, false)
{
delay = new QDoubleSpinBox();
switchLabel = new QLabel("If no switch condition is met switch to ");
usingLabel = new QLabel("using");
forLabel = new QLabel("for");
QWidget::connect(delay, SIGNAL(valueChanged(double)), this,
SLOT(DelayChanged(double)));
delay->setSuffix("s");
delay->setMaximum(999999999.9);
if (s) {
delay->setValue(s->delay);
}
setStyleSheet("* { background-color: transparent; }");
QHBoxLayout *mainLayout = new QHBoxLayout;
mainLayout->addWidget(switchLabel);
mainLayout->addWidget(scenes);
mainLayout->addWidget(usingLabel);
mainLayout->addWidget(transitions);
mainLayout->addWidget(forLabel);
mainLayout->addWidget(delay);
mainLayout->addStretch();
setLayout(mainLayout);
switchData = s;
loading = false;
}
RandomSwitch *RandomSwitchWidget::getSwitchData()
{
return switchData;
}
void RandomSwitchWidget::setSwitchData(RandomSwitch *s)
{
switchData = s;
}
void RandomSwitchWidget::swapSwitchData(RandomSwitchWidget *s1,
RandomSwitchWidget *s2)
{
SwitchWidget::swapSwitchData(s1, s2);
RandomSwitch *t = s1->getSwitchData();
s1->setSwitchData(s2->getSwitchData());
s2->setSwitchData(t);
}
void RandomSwitchWidget::DelayChanged(double d)
{
if (loading || !switchData)
return;
std::lock_guard<std::mutex> lock(switcher->m);
switchData->delay = d;
}

View File

@@ -0,0 +1,394 @@
#include <QTimer>
#include "headers/advanced-scene-switcher.hpp"
#include "headers/utility.hpp"
static QMetaObject::Connection addPulse;
void clearFrames(QListWidget *list)
{
for (int i = 0; i < list->count(); ++i) {
ScreenRegionWidget *sw =
(ScreenRegionWidget *)list->itemWidget(list->item(i));
sw->hideFrame();
}
}
void showCurrentFrame(QListWidget *list)
{
QListWidgetItem *item = list->currentItem();
if (!item)
return;
ScreenRegionWidget *sw = (ScreenRegionWidget *)list->itemWidget(item);
sw->showFrame();
}
void AdvSceneSwitcher::on_showFrame_clicked()
{
switcher->showFrame = !switcher->showFrame;
if (switcher->showFrame) {
ui->showFrame->setText("Hide guide frames");
showCurrentFrame(ui->screenRegionSwitches);
} else {
ui->showFrame->setText("Show guide frames");
clearFrames(ui->screenRegionSwitches);
}
}
void AdvSceneSwitcher::on_screenRegionSwitches_currentRowChanged(int idx)
{
UNUSED_PARAMETER(idx);
if (loading)
return;
if (switcher->showFrame) {
clearFrames(ui->screenRegionSwitches);
showCurrentFrame(ui->screenRegionSwitches);
}
}
void AdvSceneSwitcher::on_screenRegionAdd_clicked()
{
ui->screenRegionAdd->disconnect(addPulse);
std::lock_guard<std::mutex> lock(switcher->m);
switcher->screenRegionSwitches.emplace_back();
QListWidgetItem *item;
item = new QListWidgetItem(ui->screenRegionSwitches);
ui->screenRegionSwitches->addItem(item);
ScreenRegionWidget *sw =
new ScreenRegionWidget(&switcher->screenRegionSwitches.back());
item->setSizeHint(sw->minimumSizeHint());
ui->screenRegionSwitches->setItemWidget(item, sw);
}
void AdvSceneSwitcher::on_screenRegionRemove_clicked()
{
QListWidgetItem *item = ui->screenRegionSwitches->currentItem();
if (!item)
return;
{
std::lock_guard<std::mutex> lock(switcher->m);
int idx = ui->screenRegionSwitches->currentRow();
auto &switches = switcher->screenRegionSwitches;
switches.erase(switches.begin() + idx);
}
delete item;
}
void AdvSceneSwitcher::on_screenRegionUp_clicked()
{
int index = ui->screenRegionSwitches->currentRow();
if (!listMoveUp(ui->screenRegionSwitches))
return;
ScreenRegionWidget *s1 =
(ScreenRegionWidget *)ui->screenRegionSwitches->itemWidget(
ui->screenRegionSwitches->item(index));
ScreenRegionWidget *s2 =
(ScreenRegionWidget *)ui->screenRegionSwitches->itemWidget(
ui->screenRegionSwitches->item(index - 1));
ScreenRegionWidget::swapSwitchData(s1, s2);
std::lock_guard<std::mutex> lock(switcher->m);
std::swap(switcher->screenRegionSwitches[index],
switcher->screenRegionSwitches[index - 1]);
}
void AdvSceneSwitcher::on_screenRegionDown_clicked()
{
int index = ui->screenRegionSwitches->currentRow();
if (!listMoveDown(ui->screenRegionSwitches))
return;
ScreenRegionWidget *s1 =
(ScreenRegionWidget *)ui->screenRegionSwitches->itemWidget(
ui->screenRegionSwitches->item(index));
ScreenRegionWidget *s2 =
(ScreenRegionWidget *)ui->screenRegionSwitches->itemWidget(
ui->screenRegionSwitches->item(index + 1));
ScreenRegionWidget::swapSwitchData(s1, s2);
std::lock_guard<std::mutex> lock(switcher->m);
std::swap(switcher->screenRegionSwitches[index],
switcher->screenRegionSwitches[index + 1]);
}
void SwitcherData::checkScreenRegionSwitch(bool &match, OBSWeakSource &scene,
OBSWeakSource &transition)
{
std::pair<int, int> cursorPos = getCursorPos();
int minRegionSize = 99999;
for (auto &s : screenRegionSwitches) {
if (!s.initialized())
continue;
if (cursorPos.first >= s.minX && cursorPos.second >= s.minY &&
cursorPos.first <= s.maxX && cursorPos.second <= s.maxY) {
int regionSize = (s.maxX - s.minX) + (s.maxY - s.minY);
if (regionSize < minRegionSize) {
match = true;
scene = s.scene;
transition = s.transition;
minRegionSize = regionSize;
if (verbose)
s.logMatch();
break;
}
}
}
}
void AdvSceneSwitcher::updateScreenRegionCursorPos()
{
std::pair<int, int> position = getCursorPos();
ui->cursorXPosition->setText(QString::number(position.first));
ui->cursorYPosition->setText(QString::number(position.second));
}
void SwitcherData::saveScreenRegionSwitches(obs_data_t *obj)
{
obs_data_array_t *screenRegionArray = obs_data_array_create();
for (ScreenRegionSwitch &s : switcher->screenRegionSwitches) {
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, "screenRegionScene",
sceneName);
obs_data_set_string(array_obj, "transition",
transitionName);
obs_data_set_int(array_obj, "minX", s.minX);
obs_data_set_int(array_obj, "minY", s.minY);
obs_data_set_int(array_obj, "maxX", s.maxX);
obs_data_set_int(array_obj, "maxY", s.maxY);
obs_data_array_push_back(screenRegionArray, array_obj);
}
obs_source_release(source);
obs_source_release(transition);
obs_data_release(array_obj);
}
obs_data_set_array(obj, "screenRegion", screenRegionArray);
obs_data_array_release(screenRegionArray);
}
void SwitcherData::loadScreenRegionSwitches(obs_data_t *obj)
{
switcher->screenRegionSwitches.clear();
obs_data_array_t *screenRegionArray =
obs_data_get_array(obj, "screenRegion");
size_t count = obs_data_array_count(screenRegionArray);
for (size_t i = 0; i < count; i++) {
obs_data_t *array_obj =
obs_data_array_item(screenRegionArray, i);
const char *scene =
obs_data_get_string(array_obj, "screenRegionScene");
const char *transition =
obs_data_get_string(array_obj, "transition");
int minX = obs_data_get_int(array_obj, "minX");
int minY = obs_data_get_int(array_obj, "minY");
int maxX = obs_data_get_int(array_obj, "maxX");
int maxY = obs_data_get_int(array_obj, "maxY");
switcher->screenRegionSwitches.emplace_back(
GetWeakSourceByName(scene),
GetWeakTransitionByName(transition), minX, minY, maxX,
maxY);
obs_data_release(array_obj);
}
obs_data_array_release(screenRegionArray);
}
void AdvSceneSwitcher::setupRegionTab()
{
for (auto &s : switcher->screenRegionSwitches) {
QListWidgetItem *item;
item = new QListWidgetItem(ui->screenRegionSwitches);
ui->screenRegionSwitches->addItem(item);
ScreenRegionWidget *sw = new ScreenRegionWidget(&s);
item->setSizeHint(sw->minimumSizeHint());
ui->screenRegionSwitches->setItemWidget(item, sw);
}
if (switcher->screenRegionSwitches.size() == 0)
addPulse = PulseWidget(ui->screenRegionAdd, QColor(Qt::green));
// screen region cursor position
QTimer *screenRegionTimer = new QTimer(this);
connect(screenRegionTimer, SIGNAL(timeout()), this,
SLOT(updateScreenRegionCursorPos()));
screenRegionTimer->start(1000);
}
ScreenRegionWidget::ScreenRegionWidget(ScreenRegionSwitch *s)
: SwitchWidget(s, false)
{
minX = new QSpinBox();
minY = new QSpinBox();
maxX = new QSpinBox();
maxY = new QSpinBox();
cursorLabel = new QLabel("If cursor is in");
xLabel = new QLabel("x");
switchLabel = new QLabel("switch to");
usingLabel = new QLabel("using");
minX->setPrefix("Min X: ");
minY->setPrefix("Min Y: ");
maxX->setPrefix("Max X: ");
maxY->setPrefix("Max Y: ");
minX->setMinimum(-1000000);
minY->setMinimum(-1000000);
maxX->setMinimum(-1000000);
maxY->setMinimum(-1000000);
minX->setMaximum(1000000);
minY->setMaximum(1000000);
maxX->setMaximum(1000000);
maxY->setMaximum(1000000);
QWidget::connect(minX, SIGNAL(valueChanged(int)), this,
SLOT(MinXChanged(int)));
QWidget::connect(minY, SIGNAL(valueChanged(int)), this,
SLOT(MinYChanged(int)));
QWidget::connect(maxX, SIGNAL(valueChanged(int)), this,
SLOT(MaxXChanged(int)));
QWidget::connect(maxY, SIGNAL(valueChanged(int)), this,
SLOT(MaxYChanged(int)));
if (s) {
minX->setValue(s->minX);
minY->setValue(s->minY);
maxX->setValue(s->maxX);
maxY->setValue(s->maxY);
}
setStyleSheet("* { background-color: transparent; }");
QHBoxLayout *mainLayout = new QHBoxLayout;
mainLayout->addWidget(cursorLabel);
mainLayout->addWidget(minX);
mainLayout->addWidget(minY);
mainLayout->addWidget(xLabel);
mainLayout->addWidget(maxX);
mainLayout->addWidget(maxY);
mainLayout->addWidget(switchLabel);
mainLayout->addWidget(scenes);
mainLayout->addWidget(usingLabel);
mainLayout->addWidget(transitions);
mainLayout->addStretch();
setLayout(mainLayout);
switchData = s;
loading = false;
}
ScreenRegionSwitch *ScreenRegionWidget::getSwitchData()
{
return switchData;
}
void ScreenRegionWidget::setSwitchData(ScreenRegionSwitch *s)
{
switchData = s;
}
void ScreenRegionWidget::swapSwitchData(ScreenRegionWidget *s1,
ScreenRegionWidget *s2)
{
SwitchWidget::swapSwitchData(s1, s2);
ScreenRegionSwitch *t = s1->getSwitchData();
s1->setSwitchData(s2->getSwitchData());
s2->setSwitchData(t);
}
void ScreenRegionWidget::showFrame()
{
drawFrame();
helperFrame.show();
}
void ScreenRegionWidget::hideFrame()
{
helperFrame.hide();
}
void ScreenRegionWidget::MinXChanged(int pos)
{
if (loading || !switchData)
return;
std::lock_guard<std::mutex> lock(switcher->m);
switchData->minX = pos;
drawFrame();
}
void ScreenRegionWidget::MinYChanged(int pos)
{
if (loading || !switchData)
return;
std::lock_guard<std::mutex> lock(switcher->m);
switchData->minY = pos;
drawFrame();
}
void ScreenRegionWidget::MaxXChanged(int pos)
{
if (loading || !switchData)
return;
std::lock_guard<std::mutex> lock(switcher->m);
switchData->maxX = pos;
drawFrame();
}
void ScreenRegionWidget::MaxYChanged(int pos)
{
if (loading || !switchData)
return;
std::lock_guard<std::mutex> lock(switcher->m);
switchData->maxY = pos;
drawFrame();
}
void ScreenRegionWidget::drawFrame()
{
helperFrame.setFrameStyle(QFrame::Box | QFrame::Plain);
helperFrame.setWindowFlags(Qt::FramelessWindowHint | Qt::Tool |
Qt::WindowTransparentForInput |
Qt::WindowDoesNotAcceptFocus |
Qt::WindowStaysOnTopHint);
helperFrame.setAttribute(Qt::WA_TranslucentBackground, true);
if (switchData)
helperFrame.setGeometry(switchData->minX, switchData->minY,
switchData->maxX - switchData->minX,
switchData->maxY - switchData->minY);
}

419
src/switch-sequence.cpp Normal file
View File

@@ -0,0 +1,419 @@
#include <QFileDialog>
#include <QTextStream>
#include <QMessageBox>
#include "headers/advanced-scene-switcher.hpp"
#include "headers/utility.hpp"
static QMetaObject::Connection addPulse;
void AdvSceneSwitcher::on_sceneSequenceAdd_clicked()
{
ui->sceneSequenceAdd->disconnect(addPulse);
std::lock_guard<std::mutex> lock(switcher->m);
switcher->sceneSequenceSwitches.emplace_back();
QListWidgetItem *item;
item = new QListWidgetItem(ui->sceneSequenceSwitches);
ui->sceneSequenceSwitches->addItem(item);
SequenceWidget *sw =
new SequenceWidget(&switcher->sceneSequenceSwitches.back());
item->setSizeHint(sw->minimumSizeHint());
ui->sceneSequenceSwitches->setItemWidget(item, sw);
}
void AdvSceneSwitcher::on_sceneSequenceRemove_clicked()
{
QListWidgetItem *item = ui->sceneSequenceSwitches->currentItem();
if (!item)
return;
{
std::lock_guard<std::mutex> lock(switcher->m);
int idx = ui->sceneSequenceSwitches->currentRow();
auto &switches = switcher->sceneSequenceSwitches;
switches.erase(switches.begin() + idx);
}
delete item;
}
void AdvSceneSwitcher::on_sceneSequenceUp_clicked()
{
int index = ui->sceneSequenceSwitches->currentRow();
if (!listMoveUp(ui->sceneSequenceSwitches))
return;
SequenceWidget *s1 =
(SequenceWidget *)ui->sceneSequenceSwitches->itemWidget(
ui->sceneSequenceSwitches->item(index));
SequenceWidget *s2 =
(SequenceWidget *)ui->sceneSequenceSwitches->itemWidget(
ui->sceneSequenceSwitches->item(index - 1));
SequenceWidget::swapSwitchData(s1, s2);
std::lock_guard<std::mutex> lock(switcher->m);
std::swap(switcher->sceneSequenceSwitches[index],
switcher->sceneSequenceSwitches[index - 1]);
}
void AdvSceneSwitcher::on_sceneSequenceDown_clicked()
{
int index = ui->sceneSequenceSwitches->currentRow();
if (!listMoveDown(ui->sceneSequenceSwitches))
return;
SequenceWidget *s1 =
(SequenceWidget *)ui->sceneSequenceSwitches->itemWidget(
ui->sceneSequenceSwitches->item(index));
SequenceWidget *s2 =
(SequenceWidget *)ui->sceneSequenceSwitches->itemWidget(
ui->sceneSequenceSwitches->item(index + 1));
SequenceWidget::swapSwitchData(s1, s2);
std::lock_guard<std::mutex> lock(switcher->m);
std::swap(switcher->sceneSequenceSwitches[index],
switcher->sceneSequenceSwitches[index + 1]);
}
void AdvSceneSwitcher::on_sceneSequenceSave_clicked()
{
QString directory = QFileDialog::getSaveFileName(
this, tr("Save Scene Sequence to file ..."),
QDir::currentPath(), tr("Text files (*.txt)"));
if (directory.isEmpty())
return;
QFile file(directory);
if (!file.open(QIODevice::WriteOnly | QIODevice::Text))
return;
obs_data_t *obj = obs_data_create();
switcher->saveSceneSequenceSwitches(obj);
obs_data_save_json(obj, file.fileName().toUtf8().constData());
obs_data_release(obj);
}
void AdvSceneSwitcher::on_sceneSequenceLoad_clicked()
{
std::lock_guard<std::mutex> lock(switcher->m);
QString directory = QFileDialog::getOpenFileName(
this, tr("Select a file to read Scene Sequence from ..."),
QDir::currentPath(), tr("Text files (*.txt)"));
if (directory.isEmpty())
return;
QFile file(directory);
if (!file.open(QIODevice::ReadOnly | QIODevice::Text))
return;
obs_data_t *obj = obs_data_create_from_json_file(
file.fileName().toUtf8().constData());
if (!obj) {
QMessageBox Msgbox;
Msgbox.setText(
"Advanced Scene Switcher failed to import settings!");
Msgbox.exec();
return;
}
switcher->loadSceneSequenceSwitches(obj);
obs_data_release(obj);
QMessageBox Msgbox;
Msgbox.setText(
"Advanced Scene Switcher settings imported successfully!");
Msgbox.exec();
close();
}
void SwitcherData::checkSceneSequence(bool &match, OBSWeakSource &scene,
OBSWeakSource &transition,
std::unique_lock<std::mutex> &lock)
{
obs_source_t *currentSource = obs_frontend_get_current_scene();
obs_weak_source_t *ws = obs_source_get_weak_source(currentSource);
for (SceneSequenceSwitch &s : sceneSequenceSwitches) {
if (!s.initialized())
continue;
if (s.startScene == ws) {
int dur = s.delay * 1000 - interval;
if (dur > 0) {
waitScene = currentSource;
if (verbose)
s.logSleep(dur);
cv.wait_for(lock,
std::chrono::milliseconds(dur));
waitScene = nullptr;
}
obs_source_t *currentSource2 =
obs_frontend_get_current_scene();
// only switch if user hasn't changed scene manually
if (currentSource == currentSource2) {
match = true;
scene = (s.usePreviousScene) ? previousScene
: s.scene;
transition = s.transition;
if (verbose)
s.logMatch();
}
obs_source_release(currentSource2);
break;
}
}
obs_source_release(currentSource);
obs_weak_source_release(ws);
}
void SwitcherData::saveSceneSequenceSwitches(obs_data_t *obj)
{
obs_data_array_t *sceneSequenceArray = obs_data_array_create();
for (SceneSequenceSwitch &s : switcher->sceneSequenceSwitches) {
obs_data_t *array_obj = obs_data_create();
obs_source_t *source1 =
obs_weak_source_get_source(s.startScene);
obs_source_t *source2 = obs_weak_source_get_source(s.scene);
obs_source_t *transition =
obs_weak_source_get_source(s.transition);
if (source1 && (s.usePreviousScene || source2) && transition) {
const char *sceneName1 = obs_source_get_name(source1);
const char *sceneName2 = obs_source_get_name(source2);
const char *transitionName =
obs_source_get_name(transition);
obs_data_set_string(array_obj, "sceneRoundTripScene1",
sceneName1);
obs_data_set_string(array_obj, "sceneRoundTripScene2",
s.usePreviousScene
? previous_scene_name
: sceneName2);
obs_data_set_string(array_obj, "transition",
transitionName);
obs_data_set_double(array_obj, "delay", s.delay);
obs_data_set_int(array_obj, "delayMultiplier",
s.delayMultiplier);
obs_data_array_push_back(sceneSequenceArray, array_obj);
}
obs_source_release(source1);
obs_source_release(source2);
obs_source_release(transition);
obs_data_release(array_obj);
}
obs_data_set_array(obj, "sceneRoundTrip", sceneSequenceArray);
obs_data_array_release(sceneSequenceArray);
}
void SwitcherData::loadSceneSequenceSwitches(obs_data_t *obj)
{
switcher->sceneSequenceSwitches.clear();
obs_data_array_t *sceneSequenceArray =
obs_data_get_array(obj, "sceneRoundTrip");
size_t count = obs_data_array_count(sceneSequenceArray);
for (size_t i = 0; i < count; i++) {
obs_data_t *array_obj =
obs_data_array_item(sceneSequenceArray, i);
const char *scene1 =
obs_data_get_string(array_obj, "sceneRoundTripScene1");
const char *scene2 =
obs_data_get_string(array_obj, "sceneRoundTripScene2");
const char *transition =
obs_data_get_string(array_obj, "transition");
double delay = obs_data_get_double(array_obj, "delay");
int delayMultiplier =
obs_data_get_int(array_obj, "delayMultiplier");
if (delayMultiplier == 0 ||
(delayMultiplier != 1 && delayMultiplier % 60 != 0))
delayMultiplier = 1;
switcher->sceneSequenceSwitches.emplace_back(
GetWeakSourceByName(scene1),
GetWeakSourceByName(scene2),
GetWeakTransitionByName(transition), delay,
delayMultiplier,
(strcmp(scene2, previous_scene_name) == 0));
obs_data_release(array_obj);
}
obs_data_array_release(sceneSequenceArray);
}
void AdvSceneSwitcher::setupSequenceTab()
{
for (auto &s : switcher->sceneSequenceSwitches) {
QListWidgetItem *item;
item = new QListWidgetItem(ui->sceneSequenceSwitches);
ui->sceneSequenceSwitches->addItem(item);
SequenceWidget *sw = new SequenceWidget(&s);
item->setSizeHint(sw->minimumSizeHint());
ui->sceneSequenceSwitches->setItemWidget(item, sw);
}
if (switcher->sceneSequenceSwitches.size() == 0)
addPulse = PulseWidget(ui->sceneSequenceAdd, QColor(Qt::green));
}
bool SceneSequenceSwitch::initialized()
{
return SceneSwitcherEntry::initialized() && startScene;
}
bool SceneSequenceSwitch::valid()
{
return !initialized() ||
(SceneSwitcherEntry::valid() && WeakSourceValid(startScene));
}
void SceneSequenceSwitch::logSleep(int dur)
{
blog(LOG_INFO, "sequence sleep %d", dur);
}
void populateDelayUnits(QComboBox *list)
{
list->addItem("seconds");
list->addItem("minutes");
list->addItem("hours");
}
SequenceWidget::SequenceWidget(SceneSequenceSwitch *s) : SwitchWidget(s)
{
delay = new QDoubleSpinBox();
delayUnits = new QComboBox();
startScenes = new QComboBox();
whenLabel = new QLabel("When");
switchLabel = new QLabel("is active switch to");
afterLabel = new QLabel("after");
usingLabel = new QLabel("using");
QWidget::connect(delay, SIGNAL(valueChanged(double)), this,
SLOT(DelayChanged(double)));
QWidget::connect(delayUnits, SIGNAL(currentIndexChanged(int)), this,
SLOT(DelayUnitsChanged(int)));
QWidget::connect(startScenes,
SIGNAL(currentTextChanged(const QString &)), this,
SLOT(StartSceneChanged(const QString &)));
delay->setMaximum(99999.000000);
AdvSceneSwitcher::populateSceneSelection(startScenes, false);
populateDelayUnits(delayUnits);
if (s) {
switch (s->delayMultiplier) {
case 1:
delayUnits->setCurrentIndex(0);
break;
case 60:
delayUnits->setCurrentIndex(1);
break;
case 3600:
delayUnits->setCurrentIndex(2);
break;
default:
delayUnits->setCurrentIndex(0);
}
startScenes->setCurrentText(
GetWeakSourceName(s->startScene).c_str());
}
setStyleSheet("* { background-color: transparent; }");
QHBoxLayout *mainLayout = new QHBoxLayout;
mainLayout->addWidget(whenLabel);
mainLayout->addWidget(startScenes);
mainLayout->addWidget(switchLabel);
mainLayout->addWidget(scenes);
mainLayout->addWidget(afterLabel);
mainLayout->addWidget(delay);
mainLayout->addWidget(delayUnits);
mainLayout->addWidget(usingLabel);
mainLayout->addWidget(transitions);
mainLayout->addStretch();
setLayout(mainLayout);
switchData = s;
loading = false;
UpdateDelay();
}
SceneSequenceSwitch *SequenceWidget::getSwitchData()
{
return switchData;
}
void SequenceWidget::setSwitchData(SceneSequenceSwitch *s)
{
switchData = s;
}
void SequenceWidget::swapSwitchData(SequenceWidget *s1, SequenceWidget *s2)
{
SwitchWidget::swapSwitchData(s1, s2);
SceneSequenceSwitch *t = s1->getSwitchData();
s1->setSwitchData(s2->getSwitchData());
s2->setSwitchData(t);
}
void SequenceWidget::UpdateDelay()
{
if (switchData)
delay->setValue(switchData->delay /
switchData->delayMultiplier);
}
void SequenceWidget::DelayChanged(double delay)
{
if (loading || !switchData)
return;
switchData->delay = delay * switchData->delayMultiplier;
}
void SequenceWidget::DelayUnitsChanged(int idx)
{
if (loading || !switchData)
return;
delay_units unit = (delay_units)idx;
switch (unit) {
case SECONDS:
switchData->delayMultiplier = 1;
break;
case MINUTES:
switchData->delayMultiplier = 60;
break;
case HOURS:
switchData->delayMultiplier = 60 * 60;
break;
default:
break;
}
UpdateDelay();
}
void SequenceWidget::StartSceneChanged(const QString &text)
{
if (loading || !switchData)
return;
std::lock_guard<std::mutex> lock(switcher->m);
switchData->startScene = GetWeakSourceByQString(text);
}

309
src/switch-time.cpp Normal file
View File

@@ -0,0 +1,309 @@
#include "headers/advanced-scene-switcher.hpp"
#include "headers/utility.hpp"
static QMetaObject::Connection addPulse;
void AdvSceneSwitcher::on_timeAdd_clicked()
{
ui->timeAdd->disconnect(addPulse);
std::lock_guard<std::mutex> lock(switcher->m);
switcher->timeSwitches.emplace_back();
QListWidgetItem *item;
item = new QListWidgetItem(ui->timeSwitches);
ui->timeSwitches->addItem(item);
TimeSwitchWidget *sw =
new TimeSwitchWidget(&switcher->timeSwitches.back());
item->setSizeHint(sw->minimumSizeHint());
ui->timeSwitches->setItemWidget(item, sw);
}
void AdvSceneSwitcher::on_timeRemove_clicked()
{
QListWidgetItem *item = ui->timeSwitches->currentItem();
if (!item)
return;
{
std::lock_guard<std::mutex> lock(switcher->m);
int idx = ui->timeSwitches->currentRow();
auto &switches = switcher->timeSwitches;
switches.erase(switches.begin() + idx);
}
delete item;
}
void AdvSceneSwitcher::on_timeUp_clicked()
{
int index = ui->timeSwitches->currentRow();
if (!listMoveUp(ui->timeSwitches))
return;
TimeSwitchWidget *s1 = (TimeSwitchWidget *)ui->timeSwitches->itemWidget(
ui->timeSwitches->item(index));
TimeSwitchWidget *s2 = (TimeSwitchWidget *)ui->timeSwitches->itemWidget(
ui->timeSwitches->item(index - 1));
TimeSwitchWidget::swapSwitchData(s1, s2);
std::lock_guard<std::mutex> lock(switcher->m);
std::swap(switcher->timeSwitches[index],
switcher->timeSwitches[index - 1]);
}
void AdvSceneSwitcher::on_timeDown_clicked()
{
int index = ui->timeSwitches->currentRow();
if (!listMoveDown(ui->timeSwitches))
return;
TimeSwitchWidget *s1 = (TimeSwitchWidget *)ui->timeSwitches->itemWidget(
ui->timeSwitches->item(index));
TimeSwitchWidget *s2 = (TimeSwitchWidget *)ui->timeSwitches->itemWidget(
ui->timeSwitches->item(index + 1));
TimeSwitchWidget::swapSwitchData(s1, s2);
std::lock_guard<std::mutex> lock(switcher->m);
std::swap(switcher->timeSwitches[index],
switcher->timeSwitches[index + 1]);
}
bool timesAreInInterval(QTime &time1, QTime &time2, int &interval)
{
bool ret = false;
QTime validSwitchTimeWindow = time1.addMSecs(interval);
ret = time1 <= time2 && time2 <= validSwitchTimeWindow;
// check for overflow
if (!ret && validSwitchTimeWindow.msecsSinceStartOfDay() < interval) {
ret = time2 >= time1 || time2 <= validSwitchTimeWindow;
}
return ret;
}
bool checkLiveTime(TimeSwitch &s, QDateTime &start, int &interval)
{
if (start.isNull())
return false;
QDateTime now = QDateTime::currentDateTime();
QTime timePassed = QTime(0, 0).addMSecs(start.msecsTo(now));
return timesAreInInterval(s.time, timePassed, interval);
}
bool checkRegularTime(TimeSwitch &s, int &interval)
{
if (s.trigger != ANY_DAY &&
s.trigger != QDate::currentDate().dayOfWeek())
return false;
QTime now = QTime::currentTime();
return timesAreInInterval(s.time, now, interval);
}
void SwitcherData::checkTimeSwitch(bool &match, OBSWeakSource &scene,
OBSWeakSource &transition)
{
if (timeSwitches.size() == 0)
return;
for (TimeSwitch &s : timeSwitches) {
if (!s.initialized())
continue;
if (s.trigger == LIVE)
match = checkLiveTime(s, liveTime, interval);
else
match = checkRegularTime(s, interval);
if (match) {
scene = (s.usePreviousScene) ? previousScene : s.scene;
transition = s.transition;
match = true;
if (verbose)
s.logMatch();
break;
}
}
}
void SwitcherData::saveTimeSwitches(obs_data_t *obj)
{
obs_data_array_t *timeArray = obs_data_array_create();
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_int(array_obj, "trigger", s.trigger);
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);
}
obs_data_set_array(obj, "timeSwitches", timeArray);
obs_data_array_release(timeArray);
}
void SwitcherData::loadTimeSwitches(obs_data_t *obj)
{
switcher->timeSwitches.clear();
obs_data_array_t *timeArray = obs_data_get_array(obj, "timeSwitches");
size_t 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");
timeTrigger trigger =
(timeTrigger)obs_data_get_int(array_obj, "trigger");
QTime time = QTime::fromString(
obs_data_get_string(array_obj, "time"));
switcher->timeSwitches.emplace_back(
GetWeakSourceByName(scene),
GetWeakTransitionByName(transition), trigger, time,
(strcmp(scene, previous_scene_name) == 0));
obs_data_release(array_obj);
}
obs_data_array_release(timeArray);
}
void AdvSceneSwitcher::setupTimeTab()
{
for (auto &s : switcher->timeSwitches) {
QListWidgetItem *item;
item = new QListWidgetItem(ui->timeSwitches);
ui->timeSwitches->addItem(item);
TimeSwitchWidget *sw = new TimeSwitchWidget(&s);
item->setSizeHint(sw->minimumSizeHint());
ui->timeSwitches->setItemWidget(item, sw);
}
if (switcher->timeSwitches.size() == 0)
addPulse = PulseWidget(ui->timeAdd, QColor(Qt::green));
}
void populateTriggers(QComboBox *list)
{
list->addItem("On any day");
list->addItem("Mondays");
list->addItem("Tuesdays");
list->addItem("Wednesdays");
list->addItem("Thursdays");
list->addItem("Fridays");
list->addItem("Saturdays");
list->addItem("Sundays");
list->addItem("Atfer streaming/recording start");
list->setItemData(
8,
"The time relative to the start of streaming / recording will be used",
Qt::ToolTipRole);
}
TimeSwitchWidget::TimeSwitchWidget(TimeSwitch *s) : SwitchWidget(s)
{
triggers = new QComboBox();
time = new QTimeEdit();
atLabel = new QLabel("at");
switchLabel = new QLabel("switch to");
usingLabel = new QLabel("using");
QWidget::connect(triggers, SIGNAL(currentIndexChanged(int)), this,
SLOT(TriggerChanged(int)));
QWidget::connect(time, SIGNAL(timeChanged(const QTime &)), this,
SLOT(TimeChanged(const QTime &)));
populateTriggers(triggers);
time->setDisplayFormat("HH:mm:ss");
if (s) {
triggers->setCurrentIndex(s->trigger);
time->setTime(s->time);
}
setStyleSheet("* { background-color: transparent; }");
QHBoxLayout *mainLayout = new QHBoxLayout;
mainLayout->addWidget(triggers);
mainLayout->addWidget(atLabel);
mainLayout->addWidget(time);
mainLayout->addWidget(switchLabel);
mainLayout->addWidget(scenes);
mainLayout->addWidget(usingLabel);
mainLayout->addWidget(transitions);
mainLayout->addStretch();
setLayout(mainLayout);
switchData = s;
loading = false;
}
TimeSwitch *TimeSwitchWidget::getSwitchData()
{
return switchData;
}
void TimeSwitchWidget::setSwitchData(TimeSwitch *s)
{
switchData = s;
}
void TimeSwitchWidget::swapSwitchData(TimeSwitchWidget *s1,
TimeSwitchWidget *s2)
{
SwitchWidget::swapSwitchData(s1, s2);
TimeSwitch *t = s1->getSwitchData();
s1->setSwitchData(s2->getSwitchData());
s2->setSwitchData(t);
}
void TimeSwitchWidget::TriggerChanged(int index)
{
if (loading || !switchData)
return;
std::lock_guard<std::mutex> lock(switcher->m);
switchData->trigger = (timeTrigger)index;
}
void TimeSwitchWidget::TimeChanged(const QTime &time)
{
if (loading || !switchData)
return;
std::lock_guard<std::mutex> lock(switcher->m);
switchData->time = time;
}

519
src/switch-transitions.cpp Normal file
View File

@@ -0,0 +1,519 @@
#include "headers/advanced-scene-switcher.hpp"
#include "headers/utility.hpp"
void AdvSceneSwitcher::on_transitionsAdd_clicked()
{
std::lock_guard<std::mutex> lock(switcher->m);
switcher->sceneTransitions.emplace_back();
QListWidgetItem *item;
item = new QListWidgetItem(ui->sceneTransitions);
ui->sceneTransitions->addItem(item);
TransitionSwitchWidget *sw =
new TransitionSwitchWidget(&switcher->sceneTransitions.back());
item->setSizeHint(sw->minimumSizeHint());
ui->sceneTransitions->setItemWidget(item, sw);
}
void AdvSceneSwitcher::on_transitionsRemove_clicked()
{
QListWidgetItem *item = ui->sceneTransitions->currentItem();
if (!item)
return;
{
std::lock_guard<std::mutex> lock(switcher->m);
int idx = ui->sceneTransitions->currentRow();
auto &switches = switcher->sceneTransitions;
switches.erase(switches.begin() + idx);
}
delete item;
}
void AdvSceneSwitcher::on_transitionsUp_clicked()
{
int index = ui->sceneTransitions->currentRow();
if (!listMoveUp(ui->sceneTransitions))
return;
TransitionSwitchWidget *s1 =
(TransitionSwitchWidget *)ui->sceneTransitions->itemWidget(
ui->sceneTransitions->item(index));
TransitionSwitchWidget *s2 =
(TransitionSwitchWidget *)ui->sceneTransitions->itemWidget(
ui->sceneTransitions->item(index - 1));
TransitionSwitchWidget::swapSwitchData(s1, s2);
std::lock_guard<std::mutex> lock(switcher->m);
std::swap(switcher->sceneTransitions[index],
switcher->sceneTransitions[index - 1]);
}
void AdvSceneSwitcher::on_transitionsDown_clicked()
{
int index = ui->sceneTransitions->currentRow();
if (!listMoveDown(ui->sceneTransitions))
return;
TransitionSwitchWidget *s1 =
(TransitionSwitchWidget *)ui->sceneTransitions->itemWidget(
ui->sceneTransitions->item(index));
TransitionSwitchWidget *s2 =
(TransitionSwitchWidget *)ui->sceneTransitions->itemWidget(
ui->sceneTransitions->item(index + 1));
TransitionSwitchWidget::swapSwitchData(s1, s2);
std::lock_guard<std::mutex> lock(switcher->m);
std::swap(switcher->sceneTransitions[index],
switcher->sceneTransitions[index + 1]);
}
void AdvSceneSwitcher::on_defaultTransitionsAdd_clicked()
{
std::lock_guard<std::mutex> lock(switcher->m);
switcher->defaultSceneTransitions.emplace_back();
QListWidgetItem *item;
item = new QListWidgetItem(ui->defaultTransitions);
ui->defaultTransitions->addItem(item);
DefTransitionSwitchWidget *sw = new DefTransitionSwitchWidget(
&switcher->defaultSceneTransitions.back());
item->setSizeHint(sw->minimumSizeHint());
ui->defaultTransitions->setItemWidget(item, sw);
}
void AdvSceneSwitcher::on_defaultTransitionsRemove_clicked()
{
QListWidgetItem *item = ui->defaultTransitions->currentItem();
if (!item)
return;
{
std::lock_guard<std::mutex> lock(switcher->m);
int idx = ui->defaultTransitions->currentRow();
auto &switches = switcher->defaultSceneTransitions;
switches.erase(switches.begin() + idx);
}
delete item;
}
void AdvSceneSwitcher::on_defaultTransitionsUp_clicked()
{
int index = ui->defaultTransitions->currentRow();
if (!listMoveUp(ui->defaultTransitions))
return;
TransitionSwitchWidget *s1 =
(TransitionSwitchWidget *)ui->defaultTransitions->itemWidget(
ui->defaultTransitions->item(index));
TransitionSwitchWidget *s2 =
(TransitionSwitchWidget *)ui->defaultTransitions->itemWidget(
ui->defaultTransitions->item(index - 1));
TransitionSwitchWidget::swapSwitchData(s1, s2);
std::lock_guard<std::mutex> lock(switcher->m);
std::swap(switcher->defaultSceneTransitions[index],
switcher->defaultSceneTransitions[index - 1]);
}
void AdvSceneSwitcher::on_defaultTransitionsDown_clicked()
{
int index = ui->defaultTransitions->currentRow();
if (!listMoveDown(ui->defaultTransitions))
return;
DefTransitionSwitchWidget *s1 =
(DefTransitionSwitchWidget *)ui->defaultTransitions->itemWidget(
ui->defaultTransitions->item(index));
DefTransitionSwitchWidget *s2 =
(DefTransitionSwitchWidget *)ui->defaultTransitions->itemWidget(
ui->defaultTransitions->item(index + 1));
DefTransitionSwitchWidget::swapSwitchData(s1, s2);
std::lock_guard<std::mutex> lock(switcher->m);
std::swap(switcher->defaultSceneTransitions[index],
switcher->defaultSceneTransitions[index + 1]);
}
void SwitcherData::setDefaultSceneTransitions()
{
if (changedDefTransitionRecently)
return;
obs_source_t *currentSource = obs_frontend_get_current_scene();
obs_weak_source_t *ws = obs_source_get_weak_source(currentSource);
for (DefaultSceneTransition &s : defaultSceneTransitions) {
if (s.scene == ws) {
if (!s.initialized())
continue;
obs_source_t *transition =
obs_weak_source_get_source(s.transition);
//This might cancel the current transition
//There is no way to be sure when the previous transition finished
obs_frontend_set_current_transition(transition);
if (verbose)
s.logMatch();
obs_source_release(transition);
break;
}
}
obs_source_release(currentSource);
obs_weak_source_release(ws);
changedDefTransitionRecently = true;
}
void AdvSceneSwitcher::on_transitionOverridecheckBox_stateChanged(int state)
{
if (loading)
return;
std::lock_guard<std::mutex> lock(switcher->m);
if (!state) {
switcher->tansitionOverrideOverride = false;
} else {
switcher->tansitionOverrideOverride = true;
}
}
obs_weak_source_t *getNextTransition(obs_weak_source_t *scene1,
obs_weak_source_t *scene2)
{
obs_weak_source_t *ws = nullptr;
if (scene1 && scene2) {
for (SceneTransition &t : switcher->sceneTransitions) {
if (!t.initialized())
continue;
if (t.scene == scene1 && t.scene2 == scene2) {
ws = t.transition;
break;
}
}
obs_weak_source_addref(ws);
}
return ws;
}
void overwriteTransitionOverride(obs_weak_source_t *sceneWs,
obs_source_t *transition, transitionData &td)
{
obs_source_t *scene = obs_weak_source_get_source(sceneWs);
obs_data_t *data = obs_source_get_private_settings(scene);
td.name = obs_data_get_string(data, "transition");
td.duration = obs_data_get_int(data, "duration");
const char *name = obs_source_get_name(transition);
int duration = obs_frontend_get_transition_duration();
obs_data_set_string(data, "transition", name);
obs_data_set_int(data, "transition_duration", duration);
obs_data_release(data);
obs_source_release(scene);
}
void restoreTransitionOverride(obs_source_t *scene, transitionData td)
{
obs_data_t *data = obs_source_get_private_settings(scene);
obs_data_set_string(data, "transition", td.name.c_str());
obs_data_set_int(data, "transition_duration", td.duration);
obs_data_release(data);
}
void setNextTransition(OBSWeakSource &targetScene, obs_source_t *currentSource,
OBSWeakSource &transition,
bool &transitionOverrideOverride, transitionData &td)
{
obs_weak_source_t *currentScene =
obs_source_get_weak_source(currentSource);
obs_weak_source_t *nextTransitionWs =
getNextTransition(currentScene, targetScene);
obs_weak_source_release(currentScene);
obs_source_t *nextTransition = nullptr;
if (nextTransitionWs) {
nextTransition = obs_weak_source_get_source(nextTransitionWs);
} else if (transition) {
nextTransition = obs_weak_source_get_source(transition);
}
if (nextTransition) {
obs_frontend_set_current_transition(nextTransition);
}
if (transitionOverrideOverride)
overwriteTransitionOverride(targetScene, nextTransition, td);
obs_weak_source_release(nextTransitionWs);
obs_source_release(nextTransition);
}
void SwitcherData::saveSceneTransitions(obs_data_t *obj)
{
obs_data_array_t *sceneTransitionsArray = obs_data_array_create();
for (SceneTransition &s : switcher->sceneTransitions) {
obs_data_t *array_obj = obs_data_create();
obs_source_t *source1 = obs_weak_source_get_source(s.scene);
obs_source_t *source2 = obs_weak_source_get_source(s.scene2);
obs_source_t *transition =
obs_weak_source_get_source(s.transition);
if (source1 && source2 && transition) {
const char *sceneName1 = obs_source_get_name(source1);
const char *sceneName2 = obs_source_get_name(source2);
const char *transitionName =
obs_source_get_name(transition);
obs_data_set_string(array_obj, "Scene1", sceneName1);
obs_data_set_string(array_obj, "Scene2", sceneName2);
obs_data_set_string(array_obj, "transition",
transitionName);
obs_data_array_push_back(sceneTransitionsArray,
array_obj);
}
obs_source_release(source1);
obs_source_release(source2);
obs_source_release(transition);
obs_data_release(array_obj);
}
obs_data_set_array(obj, "sceneTransitions", sceneTransitionsArray);
obs_data_array_release(sceneTransitionsArray);
obs_data_array_t *defaultTransitionsArray = obs_data_array_create();
for (DefaultSceneTransition &s : switcher->defaultSceneTransitions) {
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_array_push_back(defaultTransitionsArray,
array_obj);
}
obs_source_release(source);
obs_source_release(transition);
obs_data_release(array_obj);
}
obs_data_set_array(obj, "defaultTransitions", defaultTransitionsArray);
obs_data_array_release(defaultTransitionsArray);
obs_data_set_bool(obj, "tansitionOverrideOverride",
switcher->tansitionOverrideOverride);
}
void SwitcherData::loadSceneTransitions(obs_data_t *obj)
{
switcher->sceneTransitions.clear();
obs_data_array_t *sceneTransitionsArray =
obs_data_get_array(obj, "sceneTransitions");
size_t count = obs_data_array_count(sceneTransitionsArray);
for (size_t i = 0; i < count; i++) {
obs_data_t *array_obj =
obs_data_array_item(sceneTransitionsArray, i);
const char *scene1 = obs_data_get_string(array_obj, "Scene1");
const char *scene2 = obs_data_get_string(array_obj, "Scene2");
const char *transition =
obs_data_get_string(array_obj, "transition");
switcher->sceneTransitions.emplace_back(
GetWeakSourceByName(scene1),
GetWeakSourceByName(scene2),
GetWeakTransitionByName(transition));
obs_data_release(array_obj);
}
obs_data_array_release(sceneTransitionsArray);
switcher->defaultSceneTransitions.clear();
obs_data_array_t *defaultTransitionsArray =
obs_data_get_array(obj, "defaultTransitions");
count = obs_data_array_count(defaultTransitionsArray);
for (size_t i = 0; i < count; i++) {
obs_data_t *array_obj =
obs_data_array_item(defaultTransitionsArray, i);
const char *scene = obs_data_get_string(array_obj, "Scene");
const char *transition =
obs_data_get_string(array_obj, "transition");
switcher->defaultSceneTransitions.emplace_back(
GetWeakSourceByName(scene),
GetWeakTransitionByName(transition));
obs_data_release(array_obj);
}
obs_data_array_release(defaultTransitionsArray);
switcher->tansitionOverrideOverride =
obs_data_get_bool(obj, "tansitionOverrideOverride");
}
void AdvSceneSwitcher::setupTransitionsTab()
{
for (auto &s : switcher->sceneTransitions) {
QListWidgetItem *item;
item = new QListWidgetItem(ui->sceneTransitions);
ui->sceneTransitions->addItem(item);
TransitionSwitchWidget *sw = new TransitionSwitchWidget(&s);
item->setSizeHint(sw->minimumSizeHint());
ui->sceneTransitions->setItemWidget(item, sw);
}
for (auto &s : switcher->defaultSceneTransitions) {
QListWidgetItem *item;
item = new QListWidgetItem(ui->defaultTransitions);
ui->defaultTransitions->addItem(item);
DefTransitionSwitchWidget *sw =
new DefTransitionSwitchWidget(&s);
item->setSizeHint(sw->minimumSizeHint());
ui->defaultTransitions->setItemWidget(item, sw);
}
ui->transitionOverridecheckBox->setChecked(
switcher->tansitionOverrideOverride);
}
bool SceneTransition::initialized()
{
return SceneSwitcherEntry::initialized() && scene2;
}
bool SceneTransition::valid()
{
return !initialized() ||
(SceneSwitcherEntry::valid() && WeakSourceValid(scene2));
}
TransitionSwitchWidget::TransitionSwitchWidget(SceneTransition *s)
: SwitchWidget(s, false)
{
scenes2 = new QComboBox();
switchLabel = new QLabel("Switch from scene");
toLabel = new QLabel("to scene");
usingLabel = new QLabel("using transition");
QWidget::connect(scenes2, SIGNAL(currentTextChanged(const QString &)),
this, SLOT(Scene2Changed(const QString &)));
AdvSceneSwitcher::populateSceneSelection(scenes2, false);
if (s) {
scenes2->setCurrentText(GetWeakSourceName(s->scene2).c_str());
}
setStyleSheet("* { background-color: transparent; }");
QHBoxLayout *mainLayout = new QHBoxLayout;
mainLayout->addWidget(switchLabel);
mainLayout->addWidget(scenes);
mainLayout->addWidget(toLabel);
mainLayout->addWidget(scenes2);
mainLayout->addWidget(usingLabel);
mainLayout->addWidget(transitions);
mainLayout->addStretch();
setLayout(mainLayout);
switchData = s;
loading = false;
}
SceneTransition *TransitionSwitchWidget::getSwitchData()
{
return switchData;
}
void TransitionSwitchWidget::setSwitchData(SceneTransition *s)
{
switchData = s;
}
void TransitionSwitchWidget::swapSwitchData(TransitionSwitchWidget *s1,
TransitionSwitchWidget *s2)
{
SwitchWidget::swapSwitchData(s1, s2);
SceneTransition *t = s1->getSwitchData();
s1->setSwitchData(s2->getSwitchData());
s2->setSwitchData(t);
}
void TransitionSwitchWidget::Scene2Changed(const QString &text)
{
if (loading || !switchData)
return;
std::lock_guard<std::mutex> lock(switcher->m);
switchData->scene2 = GetWeakSourceByQString(text);
}
DefTransitionSwitchWidget::DefTransitionSwitchWidget(DefaultSceneTransition *s)
: SwitchWidget(s, false)
{
whenLabel = new QLabel("When scene");
switchLabel =
new QLabel("is active change default scene transition to ");
setStyleSheet("* { background-color: transparent; }");
QHBoxLayout *mainLayout = new QHBoxLayout;
mainLayout->addWidget(whenLabel);
mainLayout->addWidget(scenes);
mainLayout->addWidget(switchLabel);
mainLayout->addWidget(transitions);
mainLayout->addStretch();
setLayout(mainLayout);
switchData = s;
loading = false;
}
DefaultSceneTransition *DefTransitionSwitchWidget::getSwitchData()
{
return switchData;
}
void DefTransitionSwitchWidget::setSwitchData(DefaultSceneTransition *s)
{
switchData = s;
}
void DefTransitionSwitchWidget::swapSwitchData(DefTransitionSwitchWidget *s1,
DefTransitionSwitchWidget *s2)
{
SwitchWidget::swapSwitchData(s1, s2);
DefaultSceneTransition *t = s1->getSwitchData();
s1->setSwitchData(s2->getSwitchData());
s2->setSwitchData(t);
}

View File

@@ -1,5 +1,166 @@
#include <obs-module.h>
#include "headers/advanced-scene-switcher.hpp"
#include "headers/utility.hpp"
static QMetaObject::Connection addPulse;
void AdvSceneSwitcher::on_windowAdd_clicked()
{
ui->windowAdd->disconnect(addPulse);
std::lock_guard<std::mutex> lock(switcher->m);
switcher->windowSwitches.emplace_back();
QListWidgetItem *item;
item = new QListWidgetItem(ui->windowSwitches);
ui->windowSwitches->addItem(item);
WindowSwitchWidget *sw =
new WindowSwitchWidget(&switcher->windowSwitches.back());
item->setSizeHint(sw->minimumSizeHint());
ui->windowSwitches->setItemWidget(item, sw);
}
void AdvSceneSwitcher::on_windowRemove_clicked()
{
QListWidgetItem *item = ui->windowSwitches->currentItem();
if (!item)
return;
{
std::lock_guard<std::mutex> lock(switcher->m);
int idx = ui->windowSwitches->currentRow();
auto &switches = switcher->windowSwitches;
switches.erase(switches.begin() + idx);
}
delete item;
}
void AdvSceneSwitcher::on_windowUp_clicked()
{
int index = ui->windowSwitches->currentRow();
if (!listMoveUp(ui->windowSwitches))
return;
WindowSwitchWidget *s1 =
(WindowSwitchWidget *)ui->windowSwitches->itemWidget(
ui->windowSwitches->item(index));
WindowSwitchWidget *s2 =
(WindowSwitchWidget *)ui->windowSwitches->itemWidget(
ui->windowSwitches->item(index - 1));
WindowSwitchWidget::swapSwitchData(s1, s2);
std::lock_guard<std::mutex> lock(switcher->m);
std::swap(switcher->windowSwitches[index],
switcher->windowSwitches[index - 1]);
}
void AdvSceneSwitcher::on_windowDown_clicked()
{
int index = ui->windowSwitches->currentRow();
if (!listMoveDown(ui->windowSwitches))
return;
WindowSwitchWidget *s1 =
(WindowSwitchWidget *)ui->windowSwitches->itemWidget(
ui->windowSwitches->item(index));
WindowSwitchWidget *s2 =
(WindowSwitchWidget *)ui->windowSwitches->itemWidget(
ui->windowSwitches->item(index + 1));
WindowSwitchWidget::swapSwitchData(s1, s2);
std::lock_guard<std::mutex> lock(switcher->m);
std::swap(switcher->windowSwitches[index],
switcher->windowSwitches[index + 1]);
}
void AdvSceneSwitcher::on_ignoreWindowsAdd_clicked()
{
QString windowName = ui->ignoreWindowsWindows->currentText();
if (windowName.isEmpty())
return;
QVariant v = QVariant::fromValue(windowName);
QList<QListWidgetItem *> items =
ui->ignoreWindows->findItems(windowName, Qt::MatchExactly);
if (items.size() == 0) {
QListWidgetItem *item =
new QListWidgetItem(windowName, ui->ignoreWindows);
item->setData(Qt::UserRole, v);
std::lock_guard<std::mutex> lock(switcher->m);
switcher->ignoreWindowsSwitches.emplace_back(
windowName.toUtf8().constData());
}
}
void AdvSceneSwitcher::on_ignoreWindowsRemove_clicked()
{
QListWidgetItem *item = ui->ignoreWindows->currentItem();
if (!item)
return;
QString windowName = item->data(Qt::UserRole).toString();
{
std::lock_guard<std::mutex> lock(switcher->m);
auto &switches = switcher->ignoreWindowsSwitches;
for (auto it = switches.begin(); it != switches.end(); ++it) {
auto &s = *it;
if (s == windowName.toUtf8().constData()) {
switches.erase(it);
break;
}
}
}
delete item;
}
int AdvSceneSwitcher::IgnoreWindowsFindByData(const QString &window)
{
int count = ui->ignoreWindows->count();
int idx = -1;
for (int i = 0; i < count; i++) {
QListWidgetItem *item = ui->ignoreWindows->item(i);
QString itemRegion = item->data(Qt::UserRole).toString();
if (itemRegion == window) {
idx = i;
break;
}
}
return idx;
}
void AdvSceneSwitcher::on_ignoreWindows_currentRowChanged(int idx)
{
if (loading)
return;
if (idx == -1)
return;
QListWidgetItem *item = ui->ignoreWindows->item(idx);
QString window = item->data(Qt::UserRole).toString();
std::lock_guard<std::mutex> lock(switcher->m);
for (auto &s : switcher->ignoreWindowsSwitches) {
if (window.compare(s.c_str()) == 0) {
ui->ignoreWindowsWindows->setCurrentText(s.c_str());
break;
}
}
}
bool isRunning(std::string &title)
{
@@ -29,242 +190,6 @@ bool isFocused(std::string &title)
return (equals || matches);
}
void SceneSwitcher::on_up_clicked()
{
int index = ui->switches->currentRow();
if (index != -1 && index != 0) {
ui->switches->insertItem(index - 1,
ui->switches->takeItem(index));
ui->switches->setCurrentRow(index - 1);
std::lock_guard<std::mutex> lock(switcher->m);
iter_swap(switcher->windowSwitches.begin() + index,
switcher->windowSwitches.begin() + index - 1);
}
}
void SceneSwitcher::on_down_clicked()
{
int index = ui->switches->currentRow();
if (index != -1 && index != ui->switches->count() - 1) {
ui->switches->insertItem(index + 1,
ui->switches->takeItem(index));
ui->switches->setCurrentRow(index + 1);
std::lock_guard<std::mutex> lock(switcher->m);
iter_swap(switcher->windowSwitches.begin() + index,
switcher->windowSwitches.begin() + index + 1);
}
}
void SceneSwitcher::on_add_clicked()
{
QString sceneName = ui->scenes->currentText();
QString windowName = ui->windows->currentText();
QString transitionName = ui->transitions->currentText();
bool fullscreen = ui->fullscreenCheckBox->isChecked();
bool focus = ui->focusCheckBox->isChecked();
if (windowName.isEmpty() || sceneName.isEmpty())
return;
OBSWeakSource source = GetWeakSourceByQString(sceneName);
OBSWeakSource transition = GetWeakTransitionByQString(transitionName);
QVariant v = QVariant::fromValue(windowName);
QString text = MakeSwitchName(sceneName, windowName, transitionName,
fullscreen, focus);
int idx = FindByData(windowName);
if (idx == -1) {
std::lock_guard<std::mutex> lock(switcher->m);
switcher->windowSwitches.emplace_back(
source, windowName.toUtf8().constData(), transition,
fullscreen, focus);
QListWidgetItem *item = new QListWidgetItem(text, ui->switches);
item->setData(Qt::UserRole, v);
} else {
QListWidgetItem *item = ui->switches->item(idx);
item->setText(text);
std::string window = windowName.toUtf8().constData();
{
std::lock_guard<std::mutex> lock(switcher->m);
for (auto &s : switcher->windowSwitches) {
if (s.window == window) {
s.scene = source;
s.transition = transition;
s.fullscreen = fullscreen;
s.focus = focus;
break;
}
}
}
}
}
void SceneSwitcher::on_remove_clicked()
{
QListWidgetItem *item = ui->switches->currentItem();
if (!item)
return;
std::string window =
item->data(Qt::UserRole).toString().toUtf8().constData();
{
std::lock_guard<std::mutex> lock(switcher->m);
auto &switches = switcher->windowSwitches;
for (auto it = switches.begin(); it != switches.end(); ++it) {
auto &s = *it;
if (s.window == window) {
switches.erase(it);
break;
}
}
}
delete item;
}
void SceneSwitcher::on_ignoreWindowsAdd_clicked()
{
QString windowName = ui->ignoreWindowsWindows->currentText();
if (windowName.isEmpty())
return;
QVariant v = QVariant::fromValue(windowName);
QList<QListWidgetItem *> items =
ui->ignoreWindows->findItems(windowName, Qt::MatchExactly);
if (items.size() == 0) {
QListWidgetItem *item =
new QListWidgetItem(windowName, ui->ignoreWindows);
item->setData(Qt::UserRole, v);
std::lock_guard<std::mutex> lock(switcher->m);
switcher->ignoreWindowsSwitches.emplace_back(
windowName.toUtf8().constData());
}
}
void SceneSwitcher::on_ignoreWindowsRemove_clicked()
{
QListWidgetItem *item = ui->ignoreWindows->currentItem();
if (!item)
return;
QString windowName = item->data(Qt::UserRole).toString();
{
std::lock_guard<std::mutex> lock(switcher->m);
auto &switches = switcher->ignoreWindowsSwitches;
for (auto it = switches.begin(); it != switches.end(); ++it) {
auto &s = *it;
if (s == windowName.toUtf8().constData()) {
switches.erase(it);
break;
}
}
}
delete item;
}
int SceneSwitcher::FindByData(const QString &window)
{
int count = ui->switches->count();
int idx = -1;
for (int i = 0; i < count; i++) {
QListWidgetItem *item = ui->switches->item(i);
QString itemWindow = item->data(Qt::UserRole).toString();
if (itemWindow == window) {
idx = i;
break;
}
}
return idx;
}
int SceneSwitcher::IgnoreWindowsFindByData(const QString &window)
{
int count = ui->ignoreWindows->count();
int idx = -1;
for (int i = 0; i < count; i++) {
QListWidgetItem *item = ui->ignoreWindows->item(i);
QString itemRegion = item->data(Qt::UserRole).toString();
if (itemRegion == window) {
idx = i;
break;
}
}
return idx;
}
void SceneSwitcher::on_switches_currentRowChanged(int idx)
{
if (loading)
return;
if (idx == -1)
return;
QListWidgetItem *item = ui->switches->item(idx);
QString window = item->data(Qt::UserRole).toString();
std::lock_guard<std::mutex> lock(switcher->m);
for (auto &s : switcher->windowSwitches) {
if (window.compare(s.window.c_str()) == 0) {
std::string name = GetWeakSourceName(s.scene);
std::string transitionName =
GetWeakSourceName(s.transition);
ui->scenes->setCurrentText(name.c_str());
ui->windows->setCurrentText(window);
ui->transitions->setCurrentText(transitionName.c_str());
ui->fullscreenCheckBox->setChecked(s.fullscreen);
ui->focusCheckBox->setChecked(s.focus);
break;
}
}
}
void SceneSwitcher::on_ignoreWindows_currentRowChanged(int idx)
{
if (loading)
return;
if (idx == -1)
return;
QListWidgetItem *item = ui->ignoreWindows->item(idx);
QString window = item->data(Qt::UserRole).toString();
std::lock_guard<std::mutex> lock(switcher->m);
for (auto &s : switcher->ignoreWindowsSwitches) {
if (window.compare(s.c_str()) == 0) {
ui->ignoreWindowsWindows->setCurrentText(s.c_str());
break;
}
}
}
void SwitcherData::checkWindowTitleSwitch(bool &match, OBSWeakSource &scene,
OBSWeakSource &transition)
{
@@ -290,9 +215,14 @@ void SwitcherData::checkWindowTitleSwitch(bool &match, OBSWeakSource &scene,
lastTitle = title;
// Check for match
for (WindowSceneSwitch &s : windowSwitches) {
for (WindowSwitch &s : windowSwitches) {
if (!s.initialized())
continue;
// True if fullscreen is disabled OR current window is fullscreen
bool fullscreen = (!s.fullscreen || isFullscreen(s.window));
// True if maximized is disabled OR current window is maximized
bool max = (!s.maximized || isMaximized(s.window));
// True if focus is disabled OR switch is focused
bool focus = (!s.focus || isFocused(s.window));
// True if current window is ignored AND switch equals OR matches last window
@@ -303,11 +233,14 @@ void SwitcherData::checkWindowTitleSwitch(bool &match, OBSWeakSource &scene,
QRegularExpression(
QString::fromStdString(s.window)))));
if (isRunning(s.window) && (fullscreen && (focus || ignore))) {
if (isRunning(s.window) &&
(fullscreen && (max && (focus || ignore)))) {
match = true;
scene = s.scene;
transition = s.transition;
if (verbose)
s.logMatch();
break;
}
}
@@ -316,7 +249,7 @@ void SwitcherData::checkWindowTitleSwitch(bool &match, OBSWeakSource &scene,
void SwitcherData::saveWindowTitleSwitches(obs_data_t *obj)
{
obs_data_array_t *windowTitleArray = obs_data_array_create();
for (WindowSceneSwitch &s : switcher->windowSwitches) {
for (WindowSwitch &s : switcher->windowSwitches) {
obs_data_t *array_obj = obs_data_create();
obs_source_t *source = obs_weak_source_get_source(s.scene);
@@ -333,11 +266,12 @@ void SwitcherData::saveWindowTitleSwitches(obs_data_t *obj)
s.window.c_str());
obs_data_set_bool(array_obj, "fullscreen",
s.fullscreen);
obs_data_set_bool(array_obj, "maximized", s.maximized);
obs_data_set_bool(array_obj, "focus", s.focus);
obs_data_array_push_back(windowTitleArray, array_obj);
obs_source_release(source);
obs_source_release(transition);
}
obs_source_release(source);
obs_source_release(transition);
obs_data_release(array_obj);
}
obs_data_set_array(obj, "switches", windowTitleArray);
@@ -372,12 +306,20 @@ void SwitcherData::loadWindowTitleSwitches(obs_data_t *obj)
const char *window =
obs_data_get_string(array_obj, "window_title");
bool fullscreen = obs_data_get_bool(array_obj, "fullscreen");
#if __APPLE__
// TODO:
// not implemented on MacOS as I cannot test it
bool maximized = false;
#else
bool maximized = obs_data_get_bool(array_obj, "maximized");
#endif
bool focus = obs_data_get_bool(array_obj, "focus") ||
!obs_data_has_user_value(array_obj, "focus");
switcher->windowSwitches.emplace_back(
GetWeakSourceByName(scene), window,
GetWeakTransitionByName(transition), fullscreen, focus);
GetWeakTransitionByName(transition), fullscreen,
maximized, focus);
obs_data_release(array_obj);
}
@@ -403,25 +345,22 @@ void SwitcherData::loadWindowTitleSwitches(obs_data_t *obj)
obs_data_array_release(ignoreWindowsArray);
}
void SceneSwitcher::setupTitleTab()
void AdvSceneSwitcher::setupTitleTab()
{
populateSceneSelection(ui->scenes, false);
populateTransitionSelection(ui->transitions);
populateWindowSelection(ui->windows);
populateWindowSelection(ui->ignoreWindowsWindows);
for (auto &s : switcher->windowSwitches) {
std::string sceneName = GetWeakSourceName(s.scene);
std::string transitionName = GetWeakSourceName(s.transition);
QString text = MakeSwitchName(sceneName.c_str(),
s.window.c_str(),
transitionName.c_str(),
s.fullscreen, s.focus);
QListWidgetItem *item = new QListWidgetItem(text, ui->switches);
item->setData(Qt::UserRole, s.window.c_str());
QListWidgetItem *item;
item = new QListWidgetItem(ui->windowSwitches);
ui->windowSwitches->addItem(item);
WindowSwitchWidget *sw = new WindowSwitchWidget(&s);
item->setSizeHint(sw->minimumSizeHint());
ui->windowSwitches->setItemWidget(item, sw);
}
if (switcher->windowSwitches.size() == 0)
addPulse = PulseWidget(ui->windowAdd, QColor(Qt::green));
populateWindowSelection(ui->ignoreWindowsWindows);
for (auto &window : switcher->ignoreWindowsSwitches) {
QString text = QString::fromStdString(window);
@@ -430,3 +369,109 @@ void SceneSwitcher::setupTitleTab()
item->setData(Qt::UserRole, text);
}
}
WindowSwitchWidget::WindowSwitchWidget(WindowSwitch *s) : SwitchWidget(s, false)
{
windows = new QComboBox();
fullscreen = new QCheckBox("if fullscreen");
maximized = new QCheckBox("if maximized");
focused = new QCheckBox("if focused");
QWidget::connect(windows, SIGNAL(currentTextChanged(const QString &)),
this, SLOT(WindowChanged(const QString &)));
QWidget::connect(fullscreen, SIGNAL(stateChanged(int)), this,
SLOT(FullscreenChanged(int)));
QWidget::connect(maximized, SIGNAL(stateChanged(int)), this,
SLOT(MaximizedChanged(int)));
QWidget::connect(focused, SIGNAL(stateChanged(int)), this,
SLOT(FocusChanged(int)));
AdvSceneSwitcher::populateWindowSelection(windows);
windows->setEditable(true);
windows->setMaxVisibleItems(20);
#if __APPLE__
// TODO:
// not implemented on MacOS as I cannot test it
maximized->setDisabled(true);
maximized->setVisible(false);
#endif
if (s) {
windows->setCurrentText(s->window.c_str());
fullscreen->setChecked(s->fullscreen);
maximized->setChecked(s->maximized);
focused->setChecked(s->focus);
}
setStyleSheet("* { background-color: transparent; }");
QHBoxLayout *mainLayout = new QHBoxLayout;
mainLayout->addWidget(windows);
mainLayout->addWidget(scenes);
mainLayout->addWidget(transitions);
mainLayout->addWidget(scenes);
mainLayout->addWidget(fullscreen);
mainLayout->addWidget(maximized);
mainLayout->addWidget(focused);
mainLayout->addStretch();
setLayout(mainLayout);
switchData = s;
loading = false;
}
WindowSwitch *WindowSwitchWidget::getSwitchData()
{
return switchData;
}
void WindowSwitchWidget::setSwitchData(WindowSwitch *s)
{
switchData = s;
}
void WindowSwitchWidget::swapSwitchData(WindowSwitchWidget *s1,
WindowSwitchWidget *s2)
{
SwitchWidget::swapSwitchData(s1, s2);
WindowSwitch *t = s1->getSwitchData();
s1->setSwitchData(s2->getSwitchData());
s2->setSwitchData(t);
}
void WindowSwitchWidget::WindowChanged(const QString &text)
{
if (loading || !switchData)
return;
std::lock_guard<std::mutex> lock(switcher->m);
switchData->window = text.toStdString();
}
void WindowSwitchWidget::FullscreenChanged(int state)
{
if (loading || !switchData)
return;
std::lock_guard<std::mutex> lock(switcher->m);
switchData->fullscreen = state;
}
void WindowSwitchWidget::MaximizedChanged(int state)
{
if (loading || !switchData)
return;
std::lock_guard<std::mutex> lock(switcher->m);
switchData->maximized = state;
}
void WindowSwitchWidget::FocusChanged(int state)
{
if (loading || !switchData)
return;
std::lock_guard<std::mutex> lock(switcher->m);
switchData->focus = state;
}

View File

@@ -0,0 +1,96 @@
#include "headers/switcher-data-structs.hpp"
#include "headers/utility.hpp"
void SwitcherData::Prune()
{
for (size_t i = 0; i < windowSwitches.size(); i++) {
WindowSwitch &s = windowSwitches[i];
if (!s.valid())
windowSwitches.erase(windowSwitches.begin() + i--);
}
if (nonMatchingScene && !WeakSourceValid(nonMatchingScene)) {
switchIfNotMatching = NO_SWITCH;
nonMatchingScene = nullptr;
}
for (size_t i = 0; i < randomSwitches.size(); i++) {
RandomSwitch &s = randomSwitches[i];
if (!s.valid())
randomSwitches.erase(randomSwitches.begin() + i--);
}
for (size_t i = 0; i < screenRegionSwitches.size(); i++) {
ScreenRegionSwitch &s = screenRegionSwitches[i];
if (!s.valid())
screenRegionSwitches.erase(
screenRegionSwitches.begin() + i--);
}
for (size_t i = 0; i < pauseScenesSwitches.size(); i++) {
OBSWeakSource &scene = pauseScenesSwitches[i];
if (!WeakSourceValid(scene))
pauseScenesSwitches.erase(pauseScenesSwitches.begin() +
i--);
}
for (size_t i = 0; i < sceneSequenceSwitches.size(); i++) {
SceneSequenceSwitch &s = sceneSequenceSwitches[i];
if (!s.valid())
sceneSequenceSwitches.erase(
sceneSequenceSwitches.begin() + i--);
}
if (!WeakSourceValid(autoStopScene)) {
autoStopScene = nullptr;
autoStopEnable = false;
}
for (size_t i = 0; i < sceneTransitions.size(); i++) {
SceneTransition &s = sceneTransitions[i];
if (!s.valid())
sceneTransitions.erase(sceneTransitions.begin() + i--);
}
for (size_t i = 0; i < defaultSceneTransitions.size(); i++) {
DefaultSceneTransition &s = defaultSceneTransitions[i];
if (!s.valid())
defaultSceneTransitions.erase(
defaultSceneTransitions.begin() + i--);
}
for (size_t i = 0; i < executableSwitches.size(); i++) {
ExecutableSwitch &s = executableSwitches[i];
if (!s.valid())
executableSwitches.erase(executableSwitches.begin() +
i--);
}
for (size_t i = 0; i < fileSwitches.size(); i++) {
FileSwitch &s = fileSwitches[i];
if (!s.valid())
fileSwitches.erase(fileSwitches.begin() + i--);
}
for (size_t i = 0; i < timeSwitches.size(); i++) {
TimeSwitch &s = timeSwitches[i];
if (!s.valid())
timeSwitches.erase(timeSwitches.begin() + i--);
}
if (!idleData.valid()) {
idleData.idleEnable = false;
}
for (size_t i = 0; i < mediaSwitches.size(); i++) {
MediaSwitch &s = mediaSwitches[i];
if (!s.valid())
mediaSwitches.erase(mediaSwitches.begin() + i--);
}
for (size_t i = 0; i < audioSwitches.size(); i++) {
AudioSwitch &s = audioSwitches[i];
if (!s.valid())
audioSwitches.erase(audioSwitches.begin() + i--);
}
}

View File

@@ -1,238 +0,0 @@
#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();
std::lock_guard<std::mutex> 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) {
std::lock_guard<std::mutex> 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);
item->setData(Qt::UserRole, v);
{
std::lock_guard<std::mutex> 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;
std::string text =
item->data(Qt::UserRole).toString().toUtf8().constData();
{
std::lock_guard<std::mutex> 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;
if (verbose)
blog(LOG_INFO,
"Advanced Scene Switcher time match");
break;
}
}
}
void SwitcherData::saveTimeSwitches(obs_data_t *obj)
{
obs_data_array_t *timeArray = obs_data_array_create();
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);
}
obs_data_set_array(obj, "timeSwitches", timeArray);
obs_data_array_release(timeArray);
}
void SwitcherData::loadTimeSwitches(obs_data_t *obj)
{
switcher->timeSwitches.clear();
obs_data_array_t *timeArray = obs_data_get_array(obj, "timeSwitches");
size_t 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"));
std::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);
}
obs_data_array_release(timeArray);
}
void SceneSwitcher::setupTimeTab()
{
populateSceneSelection(ui->timeScenes, true);
populateTransitionSelection(ui->timeTransitions);
for (auto &s : switcher->timeSwitches) {
std::string sceneName = (s.usePreviousScene)
? PREVIOUS_SCENE_NAME
: GetWeakSourceName(s.scene);
std::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);
}
}

1003
src/volume-control.cpp Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -3,13 +3,16 @@
#include "../headers/advanced-scene-switcher.hpp"
#include <TlHelp32.h>
#include <Psapi.h>
#include <locale>
#include <codecvt>
#include <string>
using namespace std;
#define MAX_SEARCH 1000
static bool GetWindowTitle(HWND window, string &title)
static bool GetWindowTitle(HWND window, std::string &title)
{
size_t len = (size_t)GetWindowTextLengthW(window);
wstring wtitle;
std::wstring wtitle;
wtitle.resize(len);
if (!GetWindowTextW(window, &wtitle[0], (int)len + 1))
@@ -23,8 +26,7 @@ static bool GetWindowTitle(HWND window, string &title)
static bool WindowValid(HWND window)
{
LONG_PTR styles, ex_styles;
RECT rect;
LONG_PTR styles;
DWORD id;
if (!IsWindowVisible(window))
@@ -33,48 +35,63 @@ static bool WindowValid(HWND window)
if (id == GetCurrentProcessId())
return false;
GetClientRect(window, &rect);
styles = GetWindowLongPtr(window, GWL_STYLE);
ex_styles = GetWindowLongPtr(window, GWL_EXSTYLE);
if (ex_styles & WS_EX_TOOLWINDOW)
return false;
if (styles & WS_CHILD)
return false;
return true;
}
void GetWindowList(vector<string> &windows)
BOOL CALLBACK GetTitleCB(HWND hwnd, LPARAM lParam)
{
windows.resize(0);
if (!WindowValid(hwnd)) {
return TRUE;
}
HWND window = GetWindow(GetDesktopWindow(), GW_CHILD);
std::string title;
GetWindowTitle(hwnd, title);
if (title.empty())
return TRUE;
while (window) {
string title;
if (WindowValid(window) && GetWindowTitle(window, title))
windows.emplace_back(title);
window = GetNextWindow(window, GW_HWNDNEXT);
std::vector<std::string> &titles =
*reinterpret_cast<std::vector<std::string> *>(lParam);
titles.push_back(title);
return TRUE;
}
VOID EnumWindowsWithMetro(__in WNDENUMPROC lpEnumFunc, __in LPARAM lParam)
{
HWND childWindow = NULL;
int i = 0;
while (i < MAX_SEARCH &&
(childWindow = FindWindowEx(NULL, childWindow, NULL, NULL))) {
if (!lpEnumFunc(childWindow, lParam))
return;
i++;
}
}
// Overloaded
void GetWindowList(std::vector<std::string> &windows)
{
windows.resize(0);
EnumWindowsWithMetro(GetTitleCB, reinterpret_cast<LPARAM>(&windows));
}
void GetWindowList(QStringList &windows)
{
windows.clear();
HWND window = GetWindow(GetDesktopWindow(), GW_CHILD);
while (window) {
string title;
if (WindowValid(window) && GetWindowTitle(window, title))
windows << QString::fromStdString(title);
window = GetNextWindow(window, GW_HWNDNEXT);
std::vector<std::string> w;
GetWindowList(w);
for (auto window : w) {
windows << QString::fromStdString(window);
}
}
void GetCurrentWindowTitle(string &title)
void GetCurrentWindowTitle(std::string &title)
{
HWND window = GetForegroundWindow();
DWORD pid;
@@ -102,22 +119,26 @@ std::pair<int, int> getCursorPos()
return pos;
}
bool isFullscreen(std::string &title)
HWND getHWNDfromTitle(std::string title)
{
HWND hwnd = NULL;
std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> converter;
std::wstring wTitle = converter.from_bytes(title);
hwnd = FindWindowEx(NULL, NULL, NULL, wTitle.c_str());
return hwnd;
}
bool isMaximized(std::string &title)
{
RECT appBounds;
MONITORINFO monitorInfo = {0};
HWND hwnd = NULL;
HWND hwnd = GetWindow(GetDesktopWindow(), GW_CHILD);
while (hwnd) {
string wtitle;
if (WindowValid(hwnd) && GetWindowTitle(hwnd, wtitle) &&
(wtitle == title ||
QString::fromStdString(wtitle).contains(QRegularExpression(
QString::fromStdString(title)))))
break;
hwnd = GetNextWindow(hwnd, GW_HWNDNEXT);
}
hwnd = getHWNDfromTitle(title);
if (!hwnd)
return false;
monitorInfo.cbSize = sizeof(MONITORINFO);
GetMonitorInfo(MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST),
@@ -135,6 +156,33 @@ bool isFullscreen(std::string &title)
return true;
}
}
return false;
}
bool isFullscreen(std::string &title)
{
RECT appBounds;
MONITORINFO monitorInfo = {0};
HWND hwnd = NULL;
hwnd = getHWNDfromTitle(title);
if (!hwnd)
return false;
monitorInfo.cbSize = sizeof(MONITORINFO);
GetMonitorInfo(MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST),
&monitorInfo);
if (hwnd && hwnd != GetDesktopWindow() && hwnd != GetShellWindow()) {
GetWindowRect(hwnd, &appBounds);
if (monitorInfo.rcMonitor.bottom == appBounds.bottom &&
monitorInfo.rcMonitor.top == appBounds.top &&
monitorInfo.rcMonitor.left == appBounds.left &&
monitorInfo.rcMonitor.right == appBounds.right) {
return true;
}
}
return false;
}