mirror of
https://github.com/WarmUpTill/SceneSwitcher.git
synced 2026-09-07 00:56:00 -05:00
Compare commits
47 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
dfccc4b16d | ||
|
|
b0ef1fee71 | ||
|
|
1ef8ce4b4a | ||
|
|
1bf4bff014 | ||
|
|
4b6e00b2b2 | ||
|
|
c99cbb0c0f | ||
|
|
2a87334d38 | ||
|
|
1a4724ff21 | ||
|
|
59d8fb5d28 | ||
|
|
8823c05aeb | ||
|
|
2d0a52f8d1 | ||
|
|
1273446ce5 | ||
|
|
177ca4aea2 | ||
|
|
7526cbe1a3 | ||
|
|
e4bb034303 | ||
|
|
78e14df620 | ||
|
|
34f07bfb07 | ||
|
|
ee74d39424 | ||
|
|
33667a1f23 | ||
|
|
7947ad2608 | ||
|
|
920c188213 | ||
|
|
28fbff1adc | ||
|
|
326617aad7 | ||
|
|
499ba7dd21 | ||
|
|
f4902933ab | ||
|
|
9aa0b31bff | ||
|
|
6d5086b971 | ||
|
|
f32f47db4b | ||
|
|
74c0cd1736 | ||
|
|
a74a952b1d | ||
|
|
5bbfef81e0 | ||
|
|
c31d1cd975 | ||
|
|
81d91a9139 | ||
|
|
a354daa71d | ||
|
|
a84a205608 | ||
|
|
a5f0eb8da8 | ||
|
|
35151ea134 | ||
|
|
1510a2c2fa | ||
|
|
8e8cb4bf3e | ||
|
|
a480be3ea0 | ||
|
|
27ae5107cd | ||
|
|
69d452f978 | ||
|
|
fe9457427f | ||
|
|
d989c2b570 | ||
|
|
1f4679433e | ||
|
|
b42de8d5c8 | ||
|
|
0950d93828 |
13
.github/workflows/build.yml
vendored
13
.github/workflows/build.yml
vendored
@@ -27,6 +27,7 @@ jobs:
|
|||||||
uses: actions/checkout@v2.3.4
|
uses: actions/checkout@v2.3.4
|
||||||
with:
|
with:
|
||||||
path: UI/frontend-plugins/${{ env.PLUGIN_NAME }}
|
path: UI/frontend-plugins/${{ env.PLUGIN_NAME }}
|
||||||
|
submodules: 'recursive'
|
||||||
- name: Fetch Git Tags
|
- name: Fetch Git Tags
|
||||||
run: |
|
run: |
|
||||||
cd UI/frontend-plugins/${{ env.PLUGIN_NAME }}
|
cd UI/frontend-plugins/${{ env.PLUGIN_NAME }}
|
||||||
@@ -75,8 +76,9 @@ jobs:
|
|||||||
if: success()
|
if: success()
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
curl -L -O https://s3-us-west-2.amazonaws.com/obs-nightly/Packages.pkg
|
curl -L -O http://s.sudre.free.fr/Software/files/Packages.dmg
|
||||||
sudo installer -pkg ./Packages.pkg -target /
|
sudo hdiutil attach ./Packages.dmg
|
||||||
|
sudo installer -pkg /Volumes/Packages\ 1.2.9/Install\ Packages.pkg -target /
|
||||||
- name: Package
|
- name: Package
|
||||||
if: success()
|
if: success()
|
||||||
shell: bash
|
shell: bash
|
||||||
@@ -102,7 +104,7 @@ jobs:
|
|||||||
path: ./nightly/*.pkg
|
path: ./nightly/*.pkg
|
||||||
ubuntu64:
|
ubuntu64:
|
||||||
name: 'Linux/Ubuntu 64-bit'
|
name: 'Linux/Ubuntu 64-bit'
|
||||||
runs-on: [ubuntu-latest]
|
runs-on: [ubuntu-18.04]
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v2.3.4
|
uses: actions/checkout@v2.3.4
|
||||||
@@ -113,6 +115,7 @@ jobs:
|
|||||||
uses: actions/checkout@v2.3.4
|
uses: actions/checkout@v2.3.4
|
||||||
with:
|
with:
|
||||||
path: UI/frontend-plugins/${{ env.PLUGIN_NAME }}
|
path: UI/frontend-plugins/${{ env.PLUGIN_NAME }}
|
||||||
|
submodules: 'recursive'
|
||||||
- name: Add plugin to obs cmake
|
- name: Add plugin to obs cmake
|
||||||
shell: bash
|
shell: bash
|
||||||
run: echo "add_subdirectory(${{ env.PLUGIN_NAME }})" >> UI/frontend-plugins/CMakeLists.txt
|
run: echo "add_subdirectory(${{ env.PLUGIN_NAME }})" >> UI/frontend-plugins/CMakeLists.txt
|
||||||
@@ -161,6 +164,7 @@ jobs:
|
|||||||
pkg-config \
|
pkg-config \
|
||||||
python3-dev \
|
python3-dev \
|
||||||
qtbase5-dev \
|
qtbase5-dev \
|
||||||
|
qtbase5-private-dev \
|
||||||
libqt5svg5-dev \
|
libqt5svg5-dev \
|
||||||
swig \
|
swig \
|
||||||
libxss-dev
|
libxss-dev
|
||||||
@@ -209,6 +213,7 @@ jobs:
|
|||||||
uses: actions/checkout@v2.3.4
|
uses: actions/checkout@v2.3.4
|
||||||
with:
|
with:
|
||||||
path: UI/frontend-plugins/${{ env.PLUGIN_NAME}}
|
path: UI/frontend-plugins/${{ env.PLUGIN_NAME}}
|
||||||
|
submodules: 'recursive'
|
||||||
- name: Add plugin to obs cmake
|
- name: Add plugin to obs cmake
|
||||||
shell: cmd
|
shell: cmd
|
||||||
run: echo add_subdirectory(${{ env.PLUGIN_NAME }}) >> UI/frontend-plugins/CMakeLists.txt
|
run: echo add_subdirectory(${{ env.PLUGIN_NAME }}) >> UI/frontend-plugins/CMakeLists.txt
|
||||||
@@ -270,7 +275,7 @@ jobs:
|
|||||||
release:
|
release:
|
||||||
needs: [macos64, ubuntu64, windows]
|
needs: [macos64, ubuntu64, windows]
|
||||||
name: 'Create Release'
|
name: 'Create Release'
|
||||||
runs-on: [ubuntu-latest]
|
runs-on: [ubuntu-18.04]
|
||||||
steps:
|
steps:
|
||||||
- name: "Checkout plugin"
|
- name: "Checkout plugin"
|
||||||
uses: actions/checkout@v2.3.4
|
uses: actions/checkout@v2.3.4
|
||||||
|
|||||||
15
.github/workflows/clang-format.yml
vendored
15
.github/workflows/clang-format.yml
vendored
@@ -8,23 +8,10 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v2
|
||||||
with:
|
|
||||||
submodules: 'recursive'
|
|
||||||
|
|
||||||
- name: Install clang format
|
- name: Install clang format
|
||||||
run: |
|
run: |
|
||||||
# gets us newer clang
|
sudo apt-get install -y clang-format-10
|
||||||
sudo bash -c "cat >> /etc/apt/sources.list" << LLVMAPT
|
|
||||||
# 3.8
|
|
||||||
deb http://apt.llvm.org/xenial/ llvm-toolchain-xenial-8 main
|
|
||||||
deb-src http://apt.llvm.org/xenial/ llvm-toolchain-xenial-8 main
|
|
||||||
LLVMAPT
|
|
||||||
|
|
||||||
wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key|sudo apt-key add -
|
|
||||||
|
|
||||||
sudo apt-get -qq update
|
|
||||||
|
|
||||||
sudo apt-get install -y clang-format-8
|
|
||||||
|
|
||||||
- name: Check the Formatting
|
- name: Check the Formatting
|
||||||
run: |
|
run: |
|
||||||
|
|||||||
6
.gitmodules
vendored
Normal file
6
.gitmodules
vendored
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
[submodule "deps/asio"]
|
||||||
|
path = deps/asio
|
||||||
|
url = https://github.com/chriskohlhoff/asio.git
|
||||||
|
[submodule "deps/websocketpp"]
|
||||||
|
path = deps/websocketpp
|
||||||
|
url = https://github.com/zaphoyd/websocketpp.git
|
||||||
@@ -6,6 +6,8 @@ set -o errexit
|
|||||||
set -o pipefail
|
set -o pipefail
|
||||||
set -o nounset
|
set -o nounset
|
||||||
|
|
||||||
|
SCRIPTPATH="$( cd -- "$(dirname "$0")" >/dev/null 2>&1 ; pwd -P )"
|
||||||
|
|
||||||
# Runs the Clang Formatter in parallel on the code base.
|
# Runs the Clang Formatter in parallel on the code base.
|
||||||
# Return codes:
|
# Return codes:
|
||||||
# - 1 there are files to be formatted
|
# - 1 there are files to be formatted
|
||||||
@@ -19,13 +21,15 @@ if [[ $OS = "Linux" || $OS = "Darwin" ]] ; then
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
# Discover clang-format
|
# Discover clang-format
|
||||||
if type clang-format-8 2> /dev/null ; then
|
if type clang-format-10 2> /dev/null ; then
|
||||||
|
CLANG_FORMAT=clang-format-10
|
||||||
|
elif type clang-format-8 2> /dev/null ; then
|
||||||
CLANG_FORMAT=clang-format-8
|
CLANG_FORMAT=clang-format-8
|
||||||
else
|
else
|
||||||
CLANG_FORMAT=clang-format
|
CLANG_FORMAT=clang-format
|
||||||
fi
|
fi
|
||||||
|
|
||||||
find .. -type d \( -path ./deps \
|
find $SCRIPTPATH/.. -type d \( -path $SCRIPTPATH/../deps \
|
||||||
-o -path ./cmake \
|
-o -path $SCRIPTPATH/../cmake \
|
||||||
-o -path ./build \) -prune -type f -o -name '*.h' -or -name '*.hpp' -or -name '*.m' -or -name '*.mm' -or -name '*.c' -or -name '*.cpp' \
|
-o -path $SCRIPTPATH/../build \) -prune -type f -o -name '*.h' -or -name '*.hpp' -or -name '*.m' -or -name '*.mm' -or -name '*.c' -or -name '*.cpp' \
|
||||||
| xargs -L100 -P${NPROC} ${CLANG_FORMAT} -i -style=file -fallback-style=none
|
| xargs -L100 -P${NPROC} ${CLANG_FORMAT} -i -style=file -fallback-style=none
|
||||||
|
|||||||
@@ -1,31 +1,38 @@
|
|||||||
Note that you will have to use OBS version 25 or newer!
|
Note that you will have to use OBS version 26.1 or newer!
|
||||||
|
|
||||||
--- WINDOWS ---
|
--- Windows ---
|
||||||
Recommended: Run the provided installer. (You might have to click 'More info' and select 'Run anyway' if it is blocked by Windows)
|
Recommended: Run the provided installer. (You might have to click 'More info' and select 'Run anyway' if it is blocked by Windows)
|
||||||
|
|
||||||
Alternatively manually copy the 'obs-plugins' and 'data' folders in the respective OBS Studio installation directory.
|
Alternatively manually copy the 'obs-plugins' and 'data' folders in the respective OBS Studio installation directory.
|
||||||
It is usually located at 'C:\Program Files (x86)\obs-studio\'.
|
It is usually located at 'C:\Program Files (x86)\obs-studio\'.
|
||||||
Remember to install the Visual C++ Redistributable for Visual Studio 2019, if you have not done so already. (See plugin overview page)
|
Remember to install the Visual C++ Redistributable for Visual Studio 2019, if you have not done so already. (See plugin overview page)
|
||||||
|
|
||||||
--- MAC OS ---
|
--- macOS ---
|
||||||
Recommended: Run the provided installer. (You might have to right click and select 'open' if it is blocked)
|
Recommended: Run the provided installer. (You might have to right-click or control-click and select 'open' if it is blocked)
|
||||||
|
|
||||||
Alternatively extract the *so file and data folder and either ...
|
Alternatively extract the *so file and data folder and either ...
|
||||||
|
|
||||||
... right click the OBS app inside your Applications folder and choose 'Show Package Contents'.
|
... right-click the OBS app inside your Applications folder and choose 'Show Package Contents'.
|
||||||
Copy the advanced-scene-switcher.so file to 'Contents/Plugins' and the 'data' folder to 'Contents/Resources'.
|
Copy the advanced-scene-switcher.so file to 'Contents/Plugins' and the 'data' folder to 'Contents/Resources'.
|
||||||
|
|
||||||
... or copy the advanced-scene-switcher.so file to Library/Application Support/obs-studio/plugins/advanced-scene-switcher/bin/ .
|
... or copy the advanced-scene-switcher.so file to Library/Application Support/obs-studio/plugins/advanced-scene-switcher/bin/ .
|
||||||
And the 'data' folder to 'Library/Application Support/obs-studio/plugins/advanced-scene-switcher/'.
|
And the 'data' folder to 'Library/Application Support/obs-studio/plugins/advanced-scene-switcher/'.
|
||||||
|
|
||||||
--- Linux ---
|
--- Linux ---
|
||||||
Copy the advanced-scene-switcher.so file and data folder into the OBS Studio plugin folder.
|
Copy the advanced-scene-switcher.so file and into the OBS Studio plugin folder.
|
||||||
The location of this folder can vary so you might have to look around a bit.
|
The location of this folder can vary, so you might have to look around a bit.
|
||||||
|
|
||||||
Examples are ...
|
Examples are ...
|
||||||
/usr/lib/obs-plugins/
|
/usr/lib/obs-plugins/
|
||||||
/usr/lib/x86_64-linux-gnu/obs-plugins/
|
/usr/lib/x86_64-linux-gnu/obs-plugins/
|
||||||
|
/usr/share/obs/obs-plugins/
|
||||||
~/.config/obs-studio/plugins/advanced-scene-switcher/bin/64bit/
|
~/.config/obs-studio/plugins/advanced-scene-switcher/bin/64bit/
|
||||||
|
|
||||||
|
Unfortunately the expected location of the locale, which can be found in the data folder, can vary also.
|
||||||
The data folder usually should be copied to:
|
The data folder usually should be copied to:
|
||||||
~/.config/obs-studio/plugins/advanced-scene-switcher/
|
~/.config/obs-studio/plugins/advanced-scene-switcher/
|
||||||
|
|
||||||
|
If this does not work you can try to copy the "locale" folder found inside the data folder to:
|
||||||
|
/usr/share/obs/obs-plugins/advanced-scene-switcher/locale
|
||||||
|
|
||||||
|
In doubt please check where other "en-US.ini" files are located on your system.
|
||||||
|
|||||||
@@ -1,12 +1,20 @@
|
|||||||
cmake_minimum_required(VERSION 3.5)
|
cmake_minimum_required(VERSION 3.5)
|
||||||
|
|
||||||
project(advanced-scene-switcher)
|
project(advanced-scene-switcher)
|
||||||
|
add_definitions(-DASIO_STANDALONE)
|
||||||
|
|
||||||
# generate version info
|
# generate version info
|
||||||
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake/")
|
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake/")
|
||||||
include(GetGitRevisionDescription)
|
include(GetGitRevisionDescription)
|
||||||
get_git_head_revision(GIT_REFSPEC GIT_SHA1)
|
get_git_head_revision(GIT_REFSPEC GIT_SHA1)
|
||||||
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/cmake/version.cpp.in" "${CMAKE_CURRENT_BINARY_DIR}/src/version.cpp" @ONLY)
|
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/cmake/version.cpp.in" "${CMAKE_CURRENT_BINARY_DIR}/src/version.cpp" @ONLY)
|
||||||
|
if(WIN32)
|
||||||
|
get_filename_component(ISS_PLUGIN_FILES_DIR "${CMAKE_BINARY_DIR}\\..\\package" ABSOLUTE)
|
||||||
|
file(TO_NATIVE_PATH "${ISS_PLUGIN_FILES_DIR}" ISS_PLUGIN_FILES_DIR)
|
||||||
|
get_filename_component(ISS_MSVC_REDIST_HELPER_DIR "${CMAKE_PACKAGE_PREFIX}\\.." ABSOLUTE)
|
||||||
|
file(TO_NATIVE_PATH "${ISS_MSVC_REDIST_HELPER_DIR}" ISS_MSVC_REDIST_HELPER_DIR)
|
||||||
|
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/CI/windows/setup.iss.in" "${CMAKE_CURRENT_BINARY_DIR}/CI/windows/setup.iss" @ONLY)
|
||||||
|
endif()
|
||||||
|
|
||||||
if(WIN32)
|
if(WIN32)
|
||||||
get_filename_component(ISS_PLUGIN_FILES_DIR "${CMAKE_BINARY_DIR}\\..\\package" ABSOLUTE)
|
get_filename_component(ISS_PLUGIN_FILES_DIR "${CMAKE_BINARY_DIR}\\..\\package" ABSOLUTE)
|
||||||
@@ -45,6 +53,11 @@ if(UNIX AND NOT APPLE)
|
|||||||
include_directories(${X11_INCLUDE_DIR})
|
include_directories(${X11_INCLUDE_DIR})
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
include_directories(
|
||||||
|
"${CMAKE_CURRENT_SOURCE_DIR}/deps/asio/asio/include"
|
||||||
|
"${CMAKE_CURRENT_SOURCE_DIR}/deps/websocketpp"
|
||||||
|
)
|
||||||
|
|
||||||
set(advanced-scene-switcher_HEADERS
|
set(advanced-scene-switcher_HEADERS
|
||||||
${advanced-scene-switcher_HEADERS}
|
${advanced-scene-switcher_HEADERS}
|
||||||
src/headers/advanced-scene-switcher.hpp
|
src/headers/advanced-scene-switcher.hpp
|
||||||
@@ -52,11 +65,14 @@ set(advanced-scene-switcher_HEADERS
|
|||||||
src/headers/utility.hpp
|
src/headers/utility.hpp
|
||||||
src/headers/curl-helper.hpp
|
src/headers/curl-helper.hpp
|
||||||
src/headers/volume-control.hpp
|
src/headers/volume-control.hpp
|
||||||
|
src/headers/scene-group.hpp
|
||||||
|
src/headers/scene-trigger.hpp
|
||||||
src/headers/switch-audio.hpp
|
src/headers/switch-audio.hpp
|
||||||
src/headers/switch-executable.hpp
|
src/headers/switch-executable.hpp
|
||||||
src/headers/switch-file.hpp
|
src/headers/switch-file.hpp
|
||||||
src/headers/switch-idle.hpp
|
src/headers/switch-idle.hpp
|
||||||
src/headers/switch-media.hpp
|
src/headers/switch-media.hpp
|
||||||
|
src/headers/switch-network.hpp
|
||||||
src/headers/switch-pause.hpp
|
src/headers/switch-pause.hpp
|
||||||
src/headers/switch-random.hpp
|
src/headers/switch-random.hpp
|
||||||
src/headers/switch-screen-region.hpp
|
src/headers/switch-screen-region.hpp
|
||||||
@@ -64,6 +80,7 @@ set(advanced-scene-switcher_HEADERS
|
|||||||
src/headers/switch-transitions.hpp
|
src/headers/switch-transitions.hpp
|
||||||
src/headers/switch-window.hpp
|
src/headers/switch-window.hpp
|
||||||
src/headers/switch-sequence.hpp
|
src/headers/switch-sequence.hpp
|
||||||
|
src/headers/switch-video.hpp
|
||||||
src/headers/switch-generic.hpp
|
src/headers/switch-generic.hpp
|
||||||
src/headers/version.h
|
src/headers/version.h
|
||||||
)
|
)
|
||||||
@@ -73,6 +90,8 @@ set(advanced-scene-switcher_SOURCES
|
|||||||
src/advanced-scene-switcher.cpp
|
src/advanced-scene-switcher.cpp
|
||||||
src/advanced-scene-switcher-module.c
|
src/advanced-scene-switcher-module.c
|
||||||
src/switcher-data-structs.cpp
|
src/switcher-data-structs.cpp
|
||||||
|
src/scene-group.cpp
|
||||||
|
src/scene-trigger.cpp
|
||||||
src/switch-transitions.cpp
|
src/switch-transitions.cpp
|
||||||
src/switch-screen-region.cpp
|
src/switch-screen-region.cpp
|
||||||
src/switch-priority.cpp
|
src/switch-priority.cpp
|
||||||
@@ -82,12 +101,14 @@ set(advanced-scene-switcher_SOURCES
|
|||||||
src/switch-file.cpp
|
src/switch-file.cpp
|
||||||
src/switch-window.cpp
|
src/switch-window.cpp
|
||||||
src/switch-media.cpp
|
src/switch-media.cpp
|
||||||
|
src/switch-network.cpp
|
||||||
src/hotkey.cpp
|
src/hotkey.cpp
|
||||||
src/general.cpp
|
src/general.cpp
|
||||||
src/switch-pause.cpp
|
src/switch-pause.cpp
|
||||||
src/switch-random.cpp
|
src/switch-random.cpp
|
||||||
src/switch-time.cpp
|
src/switch-time.cpp
|
||||||
src/switch-audio.cpp
|
src/switch-audio.cpp
|
||||||
|
src/switch-video.cpp
|
||||||
src/switch-generic.cpp
|
src/switch-generic.cpp
|
||||||
src/curl-helper.cpp
|
src/curl-helper.cpp
|
||||||
src/volume-control.cpp
|
src/volume-control.cpp
|
||||||
@@ -100,6 +121,8 @@ set(advanced-scene-switcher_UI
|
|||||||
)
|
)
|
||||||
|
|
||||||
if(WIN32)
|
if(WIN32)
|
||||||
|
add_definitions(-D_WEBSOCKETPP_CPP11_STL_)
|
||||||
|
|
||||||
set(advanced-scene-switcher_PLATFORM_SOURCES
|
set(advanced-scene-switcher_PLATFORM_SOURCES
|
||||||
src/win/advanced-scene-switcher-win.cpp)
|
src/win/advanced-scene-switcher-win.cpp)
|
||||||
elseif(APPLE)
|
elseif(APPLE)
|
||||||
|
|||||||
@@ -6,19 +6,19 @@ AdvSceneSwitcher.generalTab.title="Allgemein"
|
|||||||
AdvSceneSwitcher.generalTab.status="Status"
|
AdvSceneSwitcher.generalTab.status="Status"
|
||||||
AdvSceneSwitcher.generalTab.status.hotkeytips="Hotkeys können in den OBS Einstellungen konfiguriert werden"
|
AdvSceneSwitcher.generalTab.status.hotkeytips="Hotkeys können in den OBS Einstellungen konfiguriert werden"
|
||||||
AdvSceneSwitcher.generalTab.status.currentStatus="Erweiterter Automatischer Szenenwechsler ist:"
|
AdvSceneSwitcher.generalTab.status.currentStatus="Erweiterter Automatischer Szenenwechsler ist:"
|
||||||
AdvSceneSwitcher.generalTab.status.onStartup="Beim Start:"
|
AdvSceneSwitcher.generalTab.status.onStartup="Beim Start von OBS:"
|
||||||
AdvSceneSwitcher.generalTab.status.onStartup.asLastRun="Aktiviere den Szenenwechsler wenn er aktiv war"
|
AdvSceneSwitcher.generalTab.status.onStartup.asLastRun="Aktiviere den Szenenwechsler wenn er aktiv war"
|
||||||
AdvSceneSwitcher.generalTab.status.onStartup.alwaysStart="Aktiviere den Szenenwechsler"
|
AdvSceneSwitcher.generalTab.status.onStartup.alwaysStart="Aktiviere den Szenenwechsler"
|
||||||
AdvSceneSwitcher.generalTab.status.onStartup.doNotStart="Aktiviere den Szenenwechsler nicht"
|
AdvSceneSwitcher.generalTab.status.onStartup.doNotStart="Aktiviere den Szenenwechsler nicht"
|
||||||
AdvSceneSwitcher.generalTab.status.start="Start"
|
AdvSceneSwitcher.generalTab.status.start="Start"
|
||||||
AdvSceneSwitcher.generalTab.status.stop="Stop"
|
AdvSceneSwitcher.generalTab.status.stop="Stop"
|
||||||
|
AdvSceneSwitcher.generalTab.status.autoStart="Starte auotmatischen den Szenenwechsler beim:"
|
||||||
|
AdvSceneSwitcher.generalTab.status.autoStart.never="Niemals"
|
||||||
|
AdvSceneSwitcher.generalTab.status.autoStart.recording="Aufnehmen"
|
||||||
|
AdvSceneSwitcher.generalTab.status.autoStart.streaming="Streamen"
|
||||||
|
AdvSceneSwitcher.generalTab.status.autoStart.recordingAndStreaming="Aufnehmen oder Streamen"
|
||||||
AdvSceneSwitcher.generalTab.status.checkInterval="Teste Bedingungen alle"
|
AdvSceneSwitcher.generalTab.status.checkInterval="Teste Bedingungen alle"
|
||||||
AdvSceneSwitcher.generalTab.generalBehavior="Allgemeines Verhalten"
|
AdvSceneSwitcher.generalTab.generalBehavior="Allgemeines Verhalten"
|
||||||
AdvSceneSwitcher.generalTab.generalBehavior.automaticallyStart.recording="Aufnehmen"
|
|
||||||
AdvSceneSwitcher.generalTab.generalBehavior.automaticallyStart.streaming="Streamen"
|
|
||||||
AdvSceneSwitcher.generalTab.generalBehavior.automaticallyStart.recordingAndStreaming="Aufnehmen und Streamen"
|
|
||||||
AdvSceneSwitcher.generalTab.generalBehavior.automaticallyStart1="Starte"
|
|
||||||
AdvSceneSwitcher.generalTab.generalBehavior.automaticallyStart2=" auf Szene"
|
|
||||||
AdvSceneSwitcher.generalTab.generalBehavior.onNoMet="Wenn keine Bedingung erfüllt ist für "
|
AdvSceneSwitcher.generalTab.generalBehavior.onNoMet="Wenn keine Bedingung erfüllt ist für "
|
||||||
AdvSceneSwitcher.generalTab.generalBehavior.onNoMetDelayTooltip="Kann nur so genau sein wie das eingestellte Interval zum Testen der Bedingungen."
|
AdvSceneSwitcher.generalTab.generalBehavior.onNoMetDelayTooltip="Kann nur so genau sein wie das eingestellte Interval zum Testen der Bedingungen."
|
||||||
AdvSceneSwitcher.generalTab.generalBehavior.onNoMet.dontSwitch="Nicht wechseln"
|
AdvSceneSwitcher.generalTab.generalBehavior.onNoMet.dontSwitch="Nicht wechseln"
|
||||||
@@ -27,7 +27,6 @@ AdvSceneSwitcher.generalTab.generalBehavior.onNoMet.switchTo="Wechsle zu:"
|
|||||||
AdvSceneSwitcher.generalTab.generalBehavior.cooldown="Nach einem automatisierten Szenenwechsel wechsle nicht mehr für"
|
AdvSceneSwitcher.generalTab.generalBehavior.cooldown="Nach einem automatisierten Szenenwechsel wechsle nicht mehr für"
|
||||||
AdvSceneSwitcher.generalTab.generalBehavior.cooldownHint="In diesem Zeitraum werden potentielle erfüllte Bedingungen ignoriert!"
|
AdvSceneSwitcher.generalTab.generalBehavior.cooldownHint="In diesem Zeitraum werden potentielle erfüllte Bedingungen ignoriert!"
|
||||||
AdvSceneSwitcher.generalTab.generalBehavior.verboseLogging="Ausführliches Logging"
|
AdvSceneSwitcher.generalTab.generalBehavior.verboseLogging="Ausführliches Logging"
|
||||||
AdvSceneSwitcher.generalTab.generalBehavior.automaticallyStop="Stoppe Streamen/Aufnehmen auf Szene"
|
|
||||||
AdvSceneSwitcher.generalTab.generalBehavior.disableUIHints="Deaktiviere UI Tipps"
|
AdvSceneSwitcher.generalTab.generalBehavior.disableUIHints="Deaktiviere UI Tipps"
|
||||||
AdvSceneSwitcher.generalTab.priority="Priorität"
|
AdvSceneSwitcher.generalTab.priority="Priorität"
|
||||||
AdvSceneSwitcher.generalTab.priority.description="Szenenwechselmethoden sortiert nach Priorität"
|
AdvSceneSwitcher.generalTab.priority.description="Szenenwechselmethoden sortiert nach Priorität"
|
||||||
@@ -50,15 +49,19 @@ AdvSceneSwitcher.generalTab.priority.windowTitle="Fenstername"
|
|||||||
AdvSceneSwitcher.generalTab.priority.media="Medien"
|
AdvSceneSwitcher.generalTab.priority.media="Medien"
|
||||||
AdvSceneSwitcher.generalTab.priority.time="Zeit"
|
AdvSceneSwitcher.generalTab.priority.time="Zeit"
|
||||||
AdvSceneSwitcher.generalTab.priority.audio="Audio"
|
AdvSceneSwitcher.generalTab.priority.audio="Audio"
|
||||||
|
AdvSceneSwitcher.generalTab.priority.video="Video"
|
||||||
|
|
||||||
; Transition Tab
|
; Transition Tab
|
||||||
AdvSceneSwitcher.transitionTab.title="Szenenübergänge"
|
AdvSceneSwitcher.transitionTab.title="Szenenübergänge"
|
||||||
AdvSceneSwitcher.transitionTab.transitionOverride="Szenenübergänge, die im Szenenwechsler konfiguriert werden, haben Vorrang vor Übergangsüberschreibung"
|
AdvSceneSwitcher.transitionTab.transitionOverride="Szenenübergänge, die im Szenenwechsler konfiguriert werden, haben Vorrang vor Übergangsüberschreibung"
|
||||||
AdvSceneSwitcher.transitionTab.transitionForAToB="Szenenübergänge für automatisierte Szenenwechsel von Szene A zu Szene B"
|
AdvSceneSwitcher.transitionTab.transitionForAToB="Szenenübergänge für automatisierte Szenenwechsel von Szene A zu Szene B"
|
||||||
AdvSceneSwitcher.transitionTab.transitionForAToB.description="<html><head/><body><p>Diese Einstellungen beeinflussen <span style=\"font-style:italic;\">nur</span> vom Szenenwechsler ausgelöste Szenenübergänge - Siehe <a href=\"https://obsproject.com/forum/resources/transition-table.1174/\"><span style=\" text-decoration: underline; color:#268bd2;\">Transition Table</span></a> um auch manuelle Szenenübergänge zu konfigurieren.<br/>Einstellungen auf diesem Tab haben Vorrang vor denen welche auf den übrigen Tabs konfiguriert wurden.</p></body></html>"
|
AdvSceneSwitcher.transitionTab.transitionsHelp="<html><head/><body><p>Diese Einstellungen beeinflussen <span style=\"font-style:italic;\">nur</span> vom Szenenwechsler ausgelöste Szenenübergänge - Siehe <a href=\"https://obsproject.com/forum/resources/transition-table.1174/\"><span style=\" text-decoration: underline; color:#268bd2;\">Transition Table</span></a> um auch manuelle Szenenübergänge zu konfigurieren.<br/>Einstellungen auf diesem Tab haben Vorrang, vor denen welche auf den übrigen Tabs konfiguriert wurden.<br/><br/>Klicke auf das Plus Symbol, um einen neuen Eintrag hinzuzufügen.</p></body></html>"
|
||||||
AdvSceneSwitcher.transitionTab.defaultTransition="Ändere den Szenenübergang wenn eine Szene aktiv ist"
|
AdvSceneSwitcher.transitionTab.defaultTransition="Ändere den Szenenübergang wenn eine Szene aktiv ist"
|
||||||
AdvSceneSwitcher.transitionTab.entry="Wechsle von {{scenes}} zu {{scenes2}} mit {{transitions}}"
|
AdvSceneSwitcher.transitionTab.entry="Wechsle von {{scenes}} zu {{scenes2}} mit {{transitions}}"
|
||||||
AdvSceneSwitcher.transitionTab.defaultTransitionEntry="Wenn {{scenes}} aktiv ist ändere den Szenenübergang zu {{transitions}}"
|
AdvSceneSwitcher.transitionTab.defaultTransitionEntry="Wenn {{scenes}} aktiv ist ändere den Szenenübergang zu {{transitions}}"
|
||||||
|
AdvSceneSwitcher.transitionTab.defaultTransitionsHelp="Klicke auf das Plus Symbol, um einen neuen Eintrag hinzuzufügen."
|
||||||
|
AdvSceneSwitcher.transitionTab.defaultTransition.delay="Wechsle Szenenübergang {{defTransitionDelay}} nach Szenenwechsel."
|
||||||
|
AdvSceneSwitcher.transitionTab.defaultTransition.delay.help="Diese Verzögerung dient dazu Szenenwechsel zu unterbrechen.\nDies kann passieren, wenn der Szenenübergangtyp gewechselt wird während ein Szenenübergang grade noch im Gang ist."
|
||||||
|
|
||||||
; Pause Scenes Tab
|
; Pause Scenes Tab
|
||||||
AdvSceneSwitcher.pauseTab.title="Pause"
|
AdvSceneSwitcher.pauseTab.title="Pause"
|
||||||
@@ -69,6 +72,7 @@ AdvSceneSwitcher.pauseTab.pauseTypeScene="Szene aktiv ist"
|
|||||||
AdvSceneSwitcher.pauseTab.pauseTypeWindow="Fenster im Fokus ist"
|
AdvSceneSwitcher.pauseTab.pauseTypeWindow="Fenster im Fokus ist"
|
||||||
AdvSceneSwitcher.pauseTab.pauseTargetAll="alle"
|
AdvSceneSwitcher.pauseTab.pauseTargetAll="alle"
|
||||||
AdvSceneSwitcher.pauseTab.pauseEntry="Pausiere {{pauseTargets}} Überprüfungen wenn {{scenes}} {{windows}} {{pauseTypes}}"
|
AdvSceneSwitcher.pauseTab.pauseEntry="Pausiere {{pauseTargets}} Überprüfungen wenn {{scenes}} {{windows}} {{pauseTypes}}"
|
||||||
|
AdvSceneSwitcher.pauseTab.help="Auf diesem Tab können einzelne Szenenwechselmethoden pausiert werden, wenn eine Szene aktiv oder ein Fenster im Fokus ist.\n\nKlicke auf das markierte Plus Symbol, um einen neuen Eintrag hinzuzufügen."
|
||||||
|
|
||||||
; Window Title Tab
|
; Window Title Tab
|
||||||
AdvSceneSwitcher.windowTitleTab.title="Fenstername"
|
AdvSceneSwitcher.windowTitleTab.title="Fenstername"
|
||||||
@@ -79,19 +83,24 @@ AdvSceneSwitcher.windowTitleTab.fullscreen="wenn Vollbild"
|
|||||||
AdvSceneSwitcher.windowTitleTab.maximized="wenn maximiert"
|
AdvSceneSwitcher.windowTitleTab.maximized="wenn maximiert"
|
||||||
AdvSceneSwitcher.windowTitleTab.focused="wenn fokussiert"
|
AdvSceneSwitcher.windowTitleTab.focused="wenn fokussiert"
|
||||||
AdvSceneSwitcher.windowTitleTab.entry="{{windows}} {{scenes}} {{transitions}} {{fullscreen}} {{maximized}} {{focused}}"
|
AdvSceneSwitcher.windowTitleTab.entry="{{windows}} {{scenes}} {{transitions}} {{fullscreen}} {{maximized}} {{focused}}"
|
||||||
|
AdvSceneSwitcher.windowTitleTab.windowsHelp="Wechsle Szenen basiert auf dem Fensternamen eines laufenden Prozesses.\nFolgende zusätzliche Bedingungen können ausgewählt werden:\nDas Fenster ist im Vollbildmodus\nDas Fenster ist maximiert\nDas Fenster ist fokussiert\n\nKlicke auf das markierte Plus Symbol, um einen neuen Eintrag hinzuzufügen."
|
||||||
|
AdvSceneSwitcher.windowTitleTab.ignoreWindowsHelp="Ist ein Fenstername ignoriert so verhält sich der Szenenwechsler als ob das vorhergehende Fenster immer noch fokussiert ist.\nDies kann in Situationen nützlich sein, in den oft zwischen Fenster gewechselt wird, dies aber nicht immer einen Szenenwechsel auslösen soll.\n\nWähle oben ein Fenster aus oder gib einen Fensternamen ein und klicke auf das Plus Symbol um es zur Liste hinzuzufügen."
|
||||||
|
|
||||||
; Executable Tab
|
; Executable Tab
|
||||||
AdvSceneSwitcher.executableTab.title="Prozess"
|
AdvSceneSwitcher.executableTab.title="Prozess"
|
||||||
AdvSceneSwitcher.executableTab.implemented="Implementiert von dasOven"
|
AdvSceneSwitcher.executableTab.implemented="Implementiert von dasOven"
|
||||||
AdvSceneSwitcher.executableTab.requiresFocus="nur wenn dieser fokussiert ist"
|
AdvSceneSwitcher.executableTab.requiresFocus="nur wenn dieser fokussiert ist"
|
||||||
AdvSceneSwitcher.executableTab.entry="Wenn {{processes}} läuft wechsle zu {{scenes}} mit {{transitions}} {{requiresFocus}}"
|
AdvSceneSwitcher.executableTab.entry="Wenn {{processes}} läuft wechsle zu {{scenes}} mit {{transitions}} {{requiresFocus}}"
|
||||||
|
AdvSceneSwitcher.executableTab.help="Dieser Tab erlaubt es Szenen zu wechseln, wenn bestimmte Prozesse laufen.\nDas kann nützlich sein, wenn sich der Name eines Fensters ändern kann oder dieser nicht bekannt ist.\n\nKlicke auf das markierte Plus Symbol, um einen neuen Eintrag hinzuzufügen."
|
||||||
|
|
||||||
; Screen Region Tab
|
; Screen Region Tab
|
||||||
AdvSceneSwitcher.screenRegionTab.title="Bildschrimbereich"
|
AdvSceneSwitcher.screenRegionTab.title="Bildschrimbereich"
|
||||||
AdvSceneSwitcher.screenRegionTab.currentPosition="Mauszeiger ist bei:"
|
AdvSceneSwitcher.screenRegionTab.currentPosition="Mauszeiger ist bei:"
|
||||||
AdvSceneSwitcher.screenRegionTab.showGuideFrames="Hilfsrahmen einblenden"
|
AdvSceneSwitcher.screenRegionTab.showGuideFrames="Hilfsrahmen einblenden"
|
||||||
AdvSceneSwitcher.screenRegionTab.hideGuideFrames="Hilfsrahmen ausblenden"
|
AdvSceneSwitcher.screenRegionTab.hideGuideFrames="Hilfsrahmen ausblenden"
|
||||||
AdvSceneSwitcher.screenRegionTab.entry="Wenn der Mauszeiger in {{minX}} {{minY}} x {{maxX}} {{maxY}} ist wechsle zu {{scenes}} mit {{transitions}}"
|
AdvSceneSwitcher.screenRegionTab.excludeScenes.None="Keine Auswahl"
|
||||||
|
AdvSceneSwitcher.screenRegionTab.entry="Wenn der Mauszeiger in {{minX}} {{minY}} x {{maxX}} {{maxY}} ist wechsle zu {{scenes}} mit {{transitions}} außer wenn auf {{excludeScenes}}"
|
||||||
|
AdvSceneSwitcher.screenRegionTab.help="Dieser Tab ermöglicht es basierend auf der Position des Mauszeigers Szenen zu wechseln.\n\nKlicke auf das markierte Plus Symbol, um einen neuen Eintrag hinzuzufügen."
|
||||||
|
|
||||||
; Media Tab
|
; Media Tab
|
||||||
AdvSceneSwitcher.mediaTab.title="Medien"
|
AdvSceneSwitcher.mediaTab.title="Medien"
|
||||||
@@ -112,6 +121,7 @@ AdvSceneSwitcher.mediaTab.timeRestriction.longer="Dauer länger"
|
|||||||
AdvSceneSwitcher.mediaTab.timeRestriction.remainShorter="übrige Zeit kürzer"
|
AdvSceneSwitcher.mediaTab.timeRestriction.remainShorter="übrige Zeit kürzer"
|
||||||
AdvSceneSwitcher.mediaTab.timeRestriction.remainLonger="übrige Zeit länger"
|
AdvSceneSwitcher.mediaTab.timeRestriction.remainLonger="übrige Zeit länger"
|
||||||
AdvSceneSwitcher.mediaTab.entry="Wenn {{mediaSources}} im Status {{states}} ist und{{timeRestrictions}} {{time}} ist wechsle zu {{scenes}} mit {{transitions}}"
|
AdvSceneSwitcher.mediaTab.entry="Wenn {{mediaSources}} im Status {{states}} ist und{{timeRestrictions}} {{time}} ist wechsle zu {{scenes}} mit {{transitions}}"
|
||||||
|
AdvSceneSwitcher.mediaTab.help="Dieser Tab ermöglicht es basieren auf dem Status von Medienquellen Szenen zu wechseln.\nZum Beispiel kann automatisch zur vorhergehenden Szene gewechselt werden, sobald sich eine Medienquelle im Status Beendet befindet.\n\nKlicke auf das markierte Plus Symbol, um einen neuen Eintrag hinzuzufügen."
|
||||||
|
|
||||||
; File Tab
|
; File Tab
|
||||||
AdvSceneSwitcher.fileTab.title="Datei Inhalt"
|
AdvSceneSwitcher.fileTab.title="Datei Inhalt"
|
||||||
@@ -136,11 +146,13 @@ AdvSceneSwitcher.fileTab.checkfileContent="nur wenn sich der Inhalt geänder hat
|
|||||||
AdvSceneSwitcher.fileTab.entry="Wechsle zu {{scenes}} mit {{transitions}} wenn der Inhalt von {{fileType}} {{filePath}} {{browseButton}} passt zu:"
|
AdvSceneSwitcher.fileTab.entry="Wechsle zu {{scenes}} mit {{transitions}} wenn der Inhalt von {{fileType}} {{filePath}} {{browseButton}} passt zu:"
|
||||||
AdvSceneSwitcher.fileTab.entry2="{{matchText}}"
|
AdvSceneSwitcher.fileTab.entry2="{{matchText}}"
|
||||||
AdvSceneSwitcher.fileTab.entry3="{{useRegex}} {{checkModificationDate}} {{checkFileContent}}"
|
AdvSceneSwitcher.fileTab.entry3="{{useRegex}} {{checkModificationDate}} {{checkFileContent}}"
|
||||||
|
AdvSceneSwitcher.fileTab.help="Dieser Tab erlaubt es Szenen zu wechseln basierend auf dem Inhalt von lokalen oder remote Dateien.\n\nKlicke auf das markierte Plus Symbol, um einen neuen Eintrag hinzuzufügen."
|
||||||
|
|
||||||
; Random Tab
|
; Random Tab
|
||||||
AdvSceneSwitcher.randomTab.title="Zufall"
|
AdvSceneSwitcher.randomTab.title="Zufall"
|
||||||
AdvSceneSwitcher.randomTab.randomDisabledWarning="Funktionalität deaktiviert - Um diese zu aktivieren wähle \"Wenn keine Bedingung erfüllt ist wechsle zu einer Szene auf dem Zufall-Tab\" auf dem Allgemein-Tab aus"
|
AdvSceneSwitcher.randomTab.randomDisabledWarning="Funktionalität deaktiviert - Um diese zu aktivieren wähle \"Wenn keine Bedingung erfüllt ist wechsle zu einer Szene auf dem Zufall-Tab\" auf dem Allgemein-Tab aus"
|
||||||
AdvSceneSwitcher.randomTab.entry="Wenn keine Bedingung erfüllt ist wechsle zu {{scenes}} mit {{transitions}} für {{delay}}"
|
AdvSceneSwitcher.randomTab.entry="Wenn keine Bedingung erfüllt ist wechsle zu {{scenes}} mit {{transitions}} für {{delay}}"
|
||||||
|
AdvSceneSwitcher.randomTab.help="Der Szenenwechsler wählt zufällig einen Eintrag auf diesem Tab aus, zu dem er für die angegebene Zeit wechseln wird.\nDer gleiche Eintrag wird nicht zweimal nacheinander ausgewählt werden.\n\nKlicke auf das markierte Plus Symbol, um einen neuen Eintrag hinzuzufügen."
|
||||||
|
|
||||||
; Time Tab
|
; Time Tab
|
||||||
AdvSceneSwitcher.timeTab.title="Zeit"
|
AdvSceneSwitcher.timeTab.title="Zeit"
|
||||||
@@ -155,15 +167,12 @@ AdvSceneSwitcher.timeTab.sundays="Sonntags"
|
|||||||
AdvSceneSwitcher.timeTab.afterstart="Nach Streamen-/Aufnehmenstart"
|
AdvSceneSwitcher.timeTab.afterstart="Nach Streamen-/Aufnehmenstart"
|
||||||
AdvSceneSwitcher.timeTab.afterstart.tip="Hier wird die Zeit relativ zum Starten des Streamen/Aufnehmen gemessen"
|
AdvSceneSwitcher.timeTab.afterstart.tip="Hier wird die Zeit relativ zum Starten des Streamen/Aufnehmen gemessen"
|
||||||
AdvSceneSwitcher.timeTab.entry="{{triggers}} um {{time}} wechsle zu {{scenes}} mit {{transitions}}"
|
AdvSceneSwitcher.timeTab.entry="{{triggers}} um {{time}} wechsle zu {{scenes}} mit {{transitions}}"
|
||||||
|
AdvSceneSwitcher.timeTab.help="Dieser Tab ermöglicht es Szenen basierend auf der aktuellen Uhrzeit zu wechseln.\n\nDer Szenenwechsler versucht den Szenenwechsel nur zur eingestellten Uhrzeit auszulösen.\nBeachte, dass die Prioritätseinstellungen auf dem Allgemein Tab entsprechend konfiguriert sind, sodass keine andere Szenenwechselmethode hier konfiguriert Einstellungen zur gegebenen Uhrzeit übertrifft.\n\nKlicke auf das markierte Plus Symbol, um einen neuen Eintrag hinzuzufügen."
|
||||||
|
|
||||||
; Idle Tab
|
; Idle Tab
|
||||||
AdvSceneSwitcher.idleTab.title="Inaktivitätserkennung"
|
AdvSceneSwitcher.idleTab.title="Inaktivitätserkennung"
|
||||||
AdvSceneSwitcher.idleTab.enable="Aktiviere Inaktivitätserkennung"
|
AdvSceneSwitcher.idleTab.enable="Aktiviere Inaktivitätserkennung"
|
||||||
AdvSceneSwitcher.idleTab.condition1="Nach "
|
AdvSceneSwitcher.idleTab.idleswitch="After {{duration}} ohne Keyboard- oder Mauseingaben wechsle zu {{scenes}} mit {{transitions}}"
|
||||||
AdvSceneSwitcher.idleTab.condition2="ohne Keyboard- oder Mauseingaben"
|
|
||||||
AdvSceneSwitcher.idleTab.switchToScene="wechsle zu Szene"
|
|
||||||
AdvSceneSwitcher.idleTab.usingTransition1="mit"
|
|
||||||
AdvSceneSwitcher.idleTab.usingTransition2=" "
|
|
||||||
AdvSceneSwitcher.idleTab.dontSwitchIfFocus1="Wechsle nicht wenn"
|
AdvSceneSwitcher.idleTab.dontSwitchIfFocus1="Wechsle nicht wenn"
|
||||||
AdvSceneSwitcher.idleTab.dontSwitchIfFocus2="im Fokus ist"
|
AdvSceneSwitcher.idleTab.dontSwitchIfFocus2="im Fokus ist"
|
||||||
|
|
||||||
@@ -179,29 +188,115 @@ AdvSceneSwitcher.sceneSequenceTab.loadSuccess="Laden der Einstellungen war erfol
|
|||||||
AdvSceneSwitcher.sceneSequenceTab.fileType="Text Dateien (*.txt)"
|
AdvSceneSwitcher.sceneSequenceTab.fileType="Text Dateien (*.txt)"
|
||||||
AdvSceneSwitcher.sceneSequenceTab.interruptible="kann unterbrochen werden"
|
AdvSceneSwitcher.sceneSequenceTab.interruptible="kann unterbrochen werden"
|
||||||
AdvSceneSwitcher.sceneSequenceTab.interruptibleHint="Andere Szenenwechselmethoden können diese Sequenz unterbrechen"
|
AdvSceneSwitcher.sceneSequenceTab.interruptibleHint="Andere Szenenwechselmethoden können diese Sequenz unterbrechen"
|
||||||
|
AdvSceneSwitcher.sceneSequenceTab.extendEdit="Szenenfolge erweitern"
|
||||||
|
AdvSceneSwitcher.sceneSequenceTab.extendEntry="Nach {{delay}} {{delayUnits}} wechsle zu {{scenes}} mit {{transitions}}"
|
||||||
AdvSceneSwitcher.sceneSequenceTab.entry="Wenn {{startScenes}} aktiv ist wechsle zu {{scenes}} nach {{delay}} {{delayUnits}} mit {{transitions}} {{interruptible}}"
|
AdvSceneSwitcher.sceneSequenceTab.entry="Wenn {{startScenes}} aktiv ist wechsle zu {{scenes}} nach {{delay}} {{delayUnits}} mit {{transitions}} {{interruptible}}"
|
||||||
|
AdvSceneSwitcher.sceneSequenceTab.help="Dieser Tab ermöglicht es automatisiert zu einer anderen Szene zu wechseln, nachdem eine Szene für die konfigurierte Zeit aktiv war.\nZum Beispiel kann so automatisch zwischen zwei Szenen hin und her gewechselt werden.\n\nKlicke auf das markierte Plus Symbol, um einen neuen Eintrag hinzuzufügen."
|
||||||
|
|
||||||
; Audio Tab
|
; Audio Tab
|
||||||
AdvSceneSwitcher.audioTab.title="Audio"
|
AdvSceneSwitcher.audioTab.title="Audio"
|
||||||
AdvSceneSwitcher.audioTab.condition.above="über"
|
AdvSceneSwitcher.audioTab.condition.above="über"
|
||||||
AdvSceneSwitcher.audioTab.condition.below="unter"
|
AdvSceneSwitcher.audioTab.condition.below="unter"
|
||||||
AdvSceneSwitcher.audioTab.entry="Wenn die Lautstärke von {{audioSources}} {{condition}} {{volumeWidget}} ist für {{duration}} wechsle zu {{scenes}} mit {{transitions}}"
|
AdvSceneSwitcher.audioTab.ignoreInactiveSource="außer Audio Quelle ist inaktiv"
|
||||||
|
AdvSceneSwitcher.audioTab.entry="Wenn die Lautstärke von {{audioSources}} {{condition}} {{volumeWidget}} ist für {{duration}} wechsle zu {{scenes}} mit {{transitions}} {{ignoreInactiveSource}}"
|
||||||
AdvSceneSwitcher.audioTab.multiMatchfallbackCondition="Wenn mehrere Einträge zutreffen ..."
|
AdvSceneSwitcher.audioTab.multiMatchfallbackCondition="Wenn mehrere Einträge zutreffen ..."
|
||||||
AdvSceneSwitcher.audioTab.multiMatchfallback="... für {{duration}} wechsle zu {{scenes}} mit {{transitions}}"
|
AdvSceneSwitcher.audioTab.multiMatchfallback="... für {{duration}} wechsle zu {{scenes}} mit {{transitions}}"
|
||||||
|
AdvSceneSwitcher.audioTab.help="Dieser Tab ermöglicht es basierend auf der Lautstärke von Audioquellen Szenen zu wechseln.\nSo kann zum Beispiel automatisch zu einer Szene gewechselt werden, wenn die Lautstärke eines Mikrofons eine konfigurierte Schwelle überschreitet.\n\nKlicke auf das markierte Plus Symbol, um einen neuen Eintrag hinzuzufügen."
|
||||||
|
|
||||||
|
; Video Tab
|
||||||
|
AdvSceneSwitcher.videoTab.title="Video"
|
||||||
|
AdvSceneSwitcher.videoTab.getScreenshot="Screenshot für ausgewählten Eintrag erstellen"
|
||||||
|
AdvSceneSwitcher.videoTab.getScreenshotHelp="Erstellt einen Screenshot für die Video Quelle des ausgewählten Eintrag und setzt diesen als Ziel Bild"
|
||||||
|
AdvSceneSwitcher.videoTab.condition.match="genau übereinstimmt mit"
|
||||||
|
AdvSceneSwitcher.videoTab.condition.match.tooltip="Eine genaue Übereinstimmung setzt voraus, dass sowohl Zielquelle als auch das Bild die gleiche Auflösung besitzen.\nZusätzlich muss jeder einzelne Pixel zwischen beiden Bildquellen übereinstimmen weshalb Bildformate, welche das Bild komprimieren (z. B. .JPG) nicht zu empfehlen sind."
|
||||||
|
AdvSceneSwitcher.videoTab.condition.differ="nicht übereinstimmt mit"
|
||||||
|
AdvSceneSwitcher.videoTab.condition.hasNotChanged="sich nicht verändert"
|
||||||
|
AdvSceneSwitcher.videoTab.ignoreInactiveSource="außer Video Quelle ist inaktiv"
|
||||||
|
AdvSceneSwitcher.videoTab.entry="Wenn {{videoSources}} {{condition}} {{filePath}} {{browseButton}} für {{duration}} wechsle zu {{scenes}} mit {{transitions}} {{ignoreInactiveSource}}"
|
||||||
|
AdvSceneSwitcher.videoTab.help="<html><head/><body><p>Dieser Tab ermöglicht es basierend auf der Videoausgabe von Quellen scenen zu wechseln.<br/>Für eine noch bessere Implementierung dieser Funktionalität siehe <a href=\"https://obsproject.com/forum/resources/pixel-match-switcher.1202/\"><span style=\" text-decoration: underline; color:#268bd2;\">Pixel Match Switcher</span></a>.<br/><br/>Klicke auf das markierte Plus Symbol, um einen neuen Eintrag hinzuzufügen..</p></body></html>"
|
||||||
|
|
||||||
|
; Network Tab
|
||||||
|
AdvSceneSwitcher.networkTab.title="Netzwerk"
|
||||||
|
AdvSceneSwitcher.networkTab.warning="Die Verwendung des Servers außerhalb eines lokalen Netzwerks kann dazu führen, dass die aktive Szene von dritten Personen ausgelesen werden kann."
|
||||||
|
AdvSceneSwitcher.networkTab.server="Server starten (Sendet Szenenwechselnachrichten zu allen verbundenen Clients)"
|
||||||
|
AdvSceneSwitcher.networkTab.server.port="Port"
|
||||||
|
AdvSceneSwitcher.networkTab.server.lockToIPv4="Nur IPv4 verwenden (deaktiviert IPv6)"
|
||||||
|
AdvSceneSwitcher.networkTab.server.restrictSendToAutomatedSwitches="Sende nur Szenenwechselnachrichten für automatisierte Szenenwechsel"
|
||||||
|
AdvSceneSwitcher.networkTab.startFailed.message="Der WebSocket-Server konnte nicht gestartet werden, mögliche Gründe:\n - Der TCP-Port %1 wird möglicherweise gerade von einem anderen Programm verwendet. Versuchen Sie einen anderen Port in den Websocket-Servereinstellungen zu setzen oder alle Programme zu beenden, die den Port möglicherweise verwenden.\n - Fehler: %2"
|
||||||
|
AdvSceneSwitcher.networkTab.server.status.currentStatus="Aktueller Status"
|
||||||
|
AdvSceneSwitcher.networkTab.server.status.notRunning="Nicht gestartet"
|
||||||
|
AdvSceneSwitcher.networkTab.server.status.starting="Startet"
|
||||||
|
AdvSceneSwitcher.networkTab.server.status.running="Server läuft"
|
||||||
|
AdvSceneSwitcher.networkTab.server.restart="Server neu starten"
|
||||||
|
AdvSceneSwitcher.networkTab.client="Client starten (Empfängt Szenenwechselnachrichten )"
|
||||||
|
AdvSceneSwitcher.networkTab.client.address="Hostname oder IP-Adresse"
|
||||||
|
AdvSceneSwitcher.networkTab.client.port="Port"
|
||||||
|
AdvSceneSwitcher.networkTab.client.status.currentStatus="Aktueller status"
|
||||||
|
AdvSceneSwitcher.networkTab.client.status.disconnected="Verbindung getrennt"
|
||||||
|
AdvSceneSwitcher.networkTab.client.status.connecting="Verbinde"
|
||||||
|
AdvSceneSwitcher.networkTab.client.status.connected="Verbunden"
|
||||||
|
AdvSceneSwitcher.networkTab.client.reconnect="Erneutes Verbinden erzwingen"
|
||||||
|
|
||||||
|
; Scene Group Tab
|
||||||
|
AdvSceneSwitcher.sceneGroupTab.title="Szenengruppe"
|
||||||
|
AdvSceneSwitcher.sceneGroupTab.list="Szenengruppen"
|
||||||
|
AdvSceneSwitcher.sceneGroupTab.edit="Szenengruppe bearbeiten"
|
||||||
|
AdvSceneSwitcher.sceneGroupTab.edit.name="Name:"
|
||||||
|
AdvSceneSwitcher.sceneGroupTab.edit.type="Typ: {{type}}"
|
||||||
|
AdvSceneSwitcher.sceneGroupTab.type.count="Anzahl"
|
||||||
|
AdvSceneSwitcher.sceneGroupTab.type.time="Zeit"
|
||||||
|
AdvSceneSwitcher.sceneGroupTab.type.random="Zufall"
|
||||||
|
AdvSceneSwitcher.sceneGroupTab.edit.count="Schreite zur nächsten Szene in dieser Gruppe fort nach {{count}} Szenewechseln"
|
||||||
|
AdvSceneSwitcher.sceneGroupTab.edit.time="Schreite zur nächsten Szene in dieser Gruppe fort nach {{time}}"
|
||||||
|
AdvSceneSwitcher.sceneGroupTab.edit.random="Wähle eine zufällig Szene in der Liste aus"
|
||||||
|
AdvSceneSwitcher.sceneGroupTab.edit.repeat="Beginne von vorne wenn Ende der Szenenliste erreicht ist"
|
||||||
|
AdvSceneSwitcher.sceneGroupTab.edit.addScene="Szene hinzufügen"
|
||||||
|
AdvSceneSwitcher.sceneGroupTab.add="Szenengruppe hinzufügen"
|
||||||
|
AdvSceneSwitcher.sceneGroupTab.defaultname="Szenengruppe %1"
|
||||||
|
AdvSceneSwitcher.sceneGroupTab.exists="Szenengruppen- oder Szenenname existiert bereits"
|
||||||
|
AdvSceneSwitcher.sceneGroupTab.help="Szenengruppen können, genau wie reguläre Szenen, als Ziel eines Szenenwechsler Eintrags ausgewählt werden.\n\nSzenengruppen bestehen aus einer Liste von Szenen.\nDie aktive Szene der Szenengruppe schreitet abhängig von den konfigurierten Einstellungen durch die Liste der zugewiesenen Szenen fort.\n\nMögliche Einstellungen zum Fortschreiten der aktiven Szene sind:\nAnzahl der Szenenwechsel.\nZeit seit dem letzten Szenenwechsel in der Szenengruppe.\nOder zufällig.\n\nSo kann zum Beispiel eine Szenengruppe welche die folgenden Szenen enthält ...\nSzene 1\nSzene 2\nSzene 3 \n... beim erstem Mal zu \"Szene 1\" wechseln.\nBeim zweiten Mal zu \"Szene 2\".\nDie restlichem Male wechselt die Szenengruppe zu \"Szene 3\".\n\nKlicke auf das markierte Plus Symbol, um eine neuen Szenengruppe hinzuzufügen."
|
||||||
|
AdvSceneSwitcher.sceneGroupTab.scenes.help="Wähle die Szenengruppe, die du bearbeiten möchtest, auf der linken Seite aus.\n\nWähle oben eine Szene aus, die du zur Szenengruppe hinzufügen möchtest, und klicke das Plus Symbol.\n\nDie gleiche Szene kann mehrfach in derselben Szenengruppe auftauchen."
|
||||||
|
|
||||||
|
; Scene Trigger Tab
|
||||||
|
AdvSceneSwitcher.sceneTriggerTab.title="Szenen Trigger"
|
||||||
|
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerType.none="--Trigger auswählen--"
|
||||||
|
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerType.sceneActive="aktiv ist"
|
||||||
|
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerType.sceneInactive="nicht aktiv ist"
|
||||||
|
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerType.sceneLeave="verlassen wird"
|
||||||
|
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.none="--Aktion auswählen--"
|
||||||
|
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.startRecording="Aufnahme starten"
|
||||||
|
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.pauseRecording="Aufnahme pausieren"
|
||||||
|
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.unpauseRecording="Aufnahme fortsetzen"
|
||||||
|
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.stopRecording="Aufnahme stoppen"
|
||||||
|
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.stopStreaming="Streamen stoppen"
|
||||||
|
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.startStreaming="Streamen starten"
|
||||||
|
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.startReplayBuffer="Replay Buffer starten"
|
||||||
|
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.stopReplayBuffer="Replay Buffer stoppen"
|
||||||
|
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.muteSource="Audio Quelle stumm schalten"
|
||||||
|
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.unmuteSource="Audio Quelle aktiv schalten"
|
||||||
|
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.startSwitcher="Szenenwechsler starten"
|
||||||
|
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.stopSwitcher="Szenenwechsler stoppen"
|
||||||
|
AdvSceneSwitcher.sceneTriggerTab.entry="Wenn {{scenes}} {{triggers}} {{actions}} {{audioSources}} nach {{duration}} "
|
||||||
|
AdvSceneSwitcher.sceneTriggerTab.help="Dieser Tab ermöglicht es automatisiert Handlungen, wie etwa das Stoppen von einer Aufnahme oder des Streamens, bei einem Szenenwechsel auszuführen."
|
||||||
|
|
||||||
|
|
||||||
; Hotkey
|
; Hotkey
|
||||||
AdvSceneSwitcher.hotkey.startSwitcherHotkey="Starte den Erweiteren Szenenwechsler"
|
AdvSceneSwitcher.hotkey.startSwitcherHotkey="Starte den Erweiteren Szenenwechsler"
|
||||||
AdvSceneSwitcher.hotkey.stopSwitcherHotkey="Stoppe den Erweiteren Szenenwechsler"
|
AdvSceneSwitcher.hotkey.stopSwitcherHotkey="Stoppe den Erweiteren Szenenwechsler"
|
||||||
AdvSceneSwitcher.hotkey.startStopToggleSwitcherHotkey="Starte/Stoppe den Erweiteren Szenenwechsler"
|
AdvSceneSwitcher.hotkey.startStopToggleSwitcherHotkey="Starte/Stoppe den Erweiteren Szenenwechsler"
|
||||||
|
|
||||||
|
AdvSceneSwitcher.askBackup="Neue Version des Erweiterten Automatischen Szenenwechslers wurde erkannt.\nSoll ein Backup der alten Einstellungen angelegt werden?"
|
||||||
|
|
||||||
AdvSceneSwitcher.close="Schließen"
|
AdvSceneSwitcher.close="Schließen"
|
||||||
AdvSceneSwitcher.browse="Öffnen"
|
AdvSceneSwitcher.browse="Öffnen"
|
||||||
|
|
||||||
AdvSceneSwitcher.selectScene="--Szene auswählen--"
|
AdvSceneSwitcher.selectScene="--Szene auswählen--"
|
||||||
AdvSceneSwitcher.selectPreviousScene="Vorhergehende Szene"
|
AdvSceneSwitcher.selectPreviousScene="Vorhergehende Szene"
|
||||||
|
AdvSceneSwitcher.currentTransition="Aktueller Szenenübergang"
|
||||||
AdvSceneSwitcher.selectTransition="--Szenenübergang auswählen--"
|
AdvSceneSwitcher.selectTransition="--Szenenübergang auswählen--"
|
||||||
AdvSceneSwitcher.selectWindow="--Fenster auswählen--"
|
AdvSceneSwitcher.selectWindow="--Fenster auswählen--"
|
||||||
AdvSceneSwitcher.selectAudioSource="--Audio Quelle auswählen--"
|
AdvSceneSwitcher.selectAudioSource="--Audio Quelle auswählen--"
|
||||||
|
AdvSceneSwitcher.selectVideoSource="--Video Quelle auswählen--"
|
||||||
AdvSceneSwitcher.selectMediaSource="--Medien Quelle auswählen--"
|
AdvSceneSwitcher.selectMediaSource="--Medien Quelle auswählen--"
|
||||||
AdvSceneSwitcher.selectProcess="--Prozess auswählen--"
|
AdvSceneSwitcher.selectProcess="--Prozess auswählen--"
|
||||||
AdvSceneSwitcher.enterPath="--Pfad eingeben--"
|
AdvSceneSwitcher.enterPath="--Pfad eingeben--"
|
||||||
|
|||||||
@@ -6,19 +6,19 @@ AdvSceneSwitcher.generalTab.title="General"
|
|||||||
AdvSceneSwitcher.generalTab.status="Status"
|
AdvSceneSwitcher.generalTab.status="Status"
|
||||||
AdvSceneSwitcher.generalTab.status.hotkeytips="Hotkeys can be defined in the OBS settings"
|
AdvSceneSwitcher.generalTab.status.hotkeytips="Hotkeys can be defined in the OBS settings"
|
||||||
AdvSceneSwitcher.generalTab.status.currentStatus="Advanced Scene Switcher is:"
|
AdvSceneSwitcher.generalTab.status.currentStatus="Advanced Scene Switcher is:"
|
||||||
AdvSceneSwitcher.generalTab.status.onStartup="On startup:"
|
AdvSceneSwitcher.generalTab.status.onStartup="On startup of OBS:"
|
||||||
AdvSceneSwitcher.generalTab.status.onStartup.asLastRun="Start the scene switcher if it was running"
|
AdvSceneSwitcher.generalTab.status.onStartup.asLastRun="Start the scene switcher if it was running"
|
||||||
AdvSceneSwitcher.generalTab.status.onStartup.alwaysStart="Always start the scene switcher"
|
AdvSceneSwitcher.generalTab.status.onStartup.alwaysStart="Always start the scene switcher"
|
||||||
AdvSceneSwitcher.generalTab.status.onStartup.doNotStart="Do not start the scene switcher"
|
AdvSceneSwitcher.generalTab.status.onStartup.doNotStart="Do not start the scene switcher"
|
||||||
AdvSceneSwitcher.generalTab.status.start="Start"
|
AdvSceneSwitcher.generalTab.status.start="Start"
|
||||||
AdvSceneSwitcher.generalTab.status.stop="Stop"
|
AdvSceneSwitcher.generalTab.status.stop="Stop"
|
||||||
|
AdvSceneSwitcher.generalTab.status.autoStart="Automatically start the scene switcher when:"
|
||||||
|
AdvSceneSwitcher.generalTab.status.autoStart.never="Never"
|
||||||
|
AdvSceneSwitcher.generalTab.status.autoStart.recording="Recording"
|
||||||
|
AdvSceneSwitcher.generalTab.status.autoStart.streaming="Streaming"
|
||||||
|
AdvSceneSwitcher.generalTab.status.autoStart.recordingAndStreaming="Recording or Streaming"
|
||||||
AdvSceneSwitcher.generalTab.status.checkInterval="Check switch conditions every"
|
AdvSceneSwitcher.generalTab.status.checkInterval="Check switch conditions every"
|
||||||
AdvSceneSwitcher.generalTab.generalBehavior="General behavior"
|
AdvSceneSwitcher.generalTab.generalBehavior="General behavior"
|
||||||
AdvSceneSwitcher.generalTab.generalBehavior.automaticallyStart.recording="Recording"
|
|
||||||
AdvSceneSwitcher.generalTab.generalBehavior.automaticallyStart.streaming="Streaming"
|
|
||||||
AdvSceneSwitcher.generalTab.generalBehavior.automaticallyStart.recordingAndStreaming="Recording and Streaming"
|
|
||||||
AdvSceneSwitcher.generalTab.generalBehavior.automaticallyStart1="Automatically start"
|
|
||||||
AdvSceneSwitcher.generalTab.generalBehavior.automaticallyStart2="on scene"
|
|
||||||
AdvSceneSwitcher.generalTab.generalBehavior.onNoMet="If no switch condition is met for"
|
AdvSceneSwitcher.generalTab.generalBehavior.onNoMet="If no switch condition is met for"
|
||||||
AdvSceneSwitcher.generalTab.generalBehavior.onNoMetDelayTooltip="Will only ever be as accurate as the configured check interval."
|
AdvSceneSwitcher.generalTab.generalBehavior.onNoMetDelayTooltip="Will only ever be as accurate as the configured check interval."
|
||||||
AdvSceneSwitcher.generalTab.generalBehavior.onNoMet.dontSwitch="Don't switch"
|
AdvSceneSwitcher.generalTab.generalBehavior.onNoMet.dontSwitch="Don't switch"
|
||||||
@@ -27,7 +27,6 @@ AdvSceneSwitcher.generalTab.generalBehavior.onNoMet.switchTo="Switch to:"
|
|||||||
AdvSceneSwitcher.generalTab.generalBehavior.cooldown="After a match do not switch scenes for"
|
AdvSceneSwitcher.generalTab.generalBehavior.cooldown="After a match do not switch scenes for"
|
||||||
AdvSceneSwitcher.generalTab.generalBehavior.cooldownHint="During this time potential matches will be ignored!"
|
AdvSceneSwitcher.generalTab.generalBehavior.cooldownHint="During this time potential matches will be ignored!"
|
||||||
AdvSceneSwitcher.generalTab.generalBehavior.verboseLogging="Enable verbose logging"
|
AdvSceneSwitcher.generalTab.generalBehavior.verboseLogging="Enable verbose logging"
|
||||||
AdvSceneSwitcher.generalTab.generalBehavior.automaticallyStop="Automatically stop streaming/recording on scene"
|
|
||||||
AdvSceneSwitcher.generalTab.generalBehavior.disableUIHints="Disable UI hints"
|
AdvSceneSwitcher.generalTab.generalBehavior.disableUIHints="Disable UI hints"
|
||||||
AdvSceneSwitcher.generalTab.priority="Priority"
|
AdvSceneSwitcher.generalTab.priority="Priority"
|
||||||
AdvSceneSwitcher.generalTab.priority.description="Switching methods priority (Highest priority is at the top)"
|
AdvSceneSwitcher.generalTab.priority.description="Switching methods priority (Highest priority is at the top)"
|
||||||
@@ -50,15 +49,19 @@ AdvSceneSwitcher.generalTab.priority.windowTitle="Window Title"
|
|||||||
AdvSceneSwitcher.generalTab.priority.media="Media"
|
AdvSceneSwitcher.generalTab.priority.media="Media"
|
||||||
AdvSceneSwitcher.generalTab.priority.time="Time"
|
AdvSceneSwitcher.generalTab.priority.time="Time"
|
||||||
AdvSceneSwitcher.generalTab.priority.audio="Audio"
|
AdvSceneSwitcher.generalTab.priority.audio="Audio"
|
||||||
|
AdvSceneSwitcher.generalTab.priority.video="Video"
|
||||||
|
|
||||||
; Transition Tab
|
; Transition Tab
|
||||||
AdvSceneSwitcher.transitionTab.title="Transition"
|
AdvSceneSwitcher.transitionTab.title="Transition"
|
||||||
AdvSceneSwitcher.transitionTab.transitionOverride="Scene transitions defined in the scene switcher take priority over Transition Overrides defined per scene"
|
AdvSceneSwitcher.transitionTab.transitionOverride="Scene transitions defined in the scene switcher take priority over Transition Overrides defined per scene"
|
||||||
AdvSceneSwitcher.transitionTab.transitionForAToB="Use transition for automated scene switch from scene A to scene B"
|
AdvSceneSwitcher.transitionTab.transitionForAToB="Use transition for automated scene switch from scene A to scene B"
|
||||||
AdvSceneSwitcher.transitionTab.transitionForAToB.description="<html><head/><body><p>These settings <span style=\"font-style:italic;\">only</span> affect transitions caused by the scene switcher - Check out <a href=\"https://obsproject.com/forum/resources/transition-table.1174/\"><span style=\" text-decoration: underline; color:#268bd2;\">Transition Table</span></a> if you want to configure this for manual scene changes.<br/>Settings defined here take priority over transition settings configured elsewhere in the scene switcher.</p></body></html>"
|
AdvSceneSwitcher.transitionTab.transitionsHelp="<html><head/><body><p>These settings <span style=\"font-style:italic;\">only</span> affect transitions caused by the scene switcher - Check out <a href=\"https://obsproject.com/forum/resources/transition-table.1174/\"><span style=\" text-decoration: underline; color:#268bd2;\">Transition Table</span></a> if you want to configure this for manual scene changes.<br/>Settings defined here take priority over transition settings configured elsewhere in the scene switcher.<br/><br/>Click the plus symbol below to add a new entry.</p></body></html>"
|
||||||
AdvSceneSwitcher.transitionTab.defaultTransition="Change transition if scene is active"
|
AdvSceneSwitcher.transitionTab.defaultTransition="Change transition if scene is active"
|
||||||
AdvSceneSwitcher.transitionTab.entry="Switch from {{scenes}} to {{scenes2}} using {{transitions}}"
|
AdvSceneSwitcher.transitionTab.entry="Switch from {{scenes}} to {{scenes2}} using {{transitions}}"
|
||||||
AdvSceneSwitcher.transitionTab.defaultTransitionEntry="When scene {{scenes}} is active change default scene transition to {{transitions}}"
|
AdvSceneSwitcher.transitionTab.defaultTransitionEntry="When scene {{scenes}} is active change default scene transition to {{transitions}}"
|
||||||
|
AdvSceneSwitcher.transitionTab.defaultTransitionsHelp="Click on the plus symbol to add an entry."
|
||||||
|
AdvSceneSwitcher.transitionTab.defaultTransition.delay="Switch transition {{defTransitionDelay}} after scene change."
|
||||||
|
AdvSceneSwitcher.transitionTab.defaultTransition.delay.help="The delay is used to avoid cancelled scene switches, which can happen if the transition type is changed while a transition is still ongoing."
|
||||||
|
|
||||||
; Pause Scenes Tab
|
; Pause Scenes Tab
|
||||||
AdvSceneSwitcher.pauseTab.title="Pause"
|
AdvSceneSwitcher.pauseTab.title="Pause"
|
||||||
@@ -69,6 +72,7 @@ AdvSceneSwitcher.pauseTab.pauseTypeScene="scene is active"
|
|||||||
AdvSceneSwitcher.pauseTab.pauseTypeWindow="window is in focus"
|
AdvSceneSwitcher.pauseTab.pauseTypeWindow="window is in focus"
|
||||||
AdvSceneSwitcher.pauseTab.pauseTargetAll="all"
|
AdvSceneSwitcher.pauseTab.pauseTargetAll="all"
|
||||||
AdvSceneSwitcher.pauseTab.pauseEntry="Pause {{pauseTargets}} checks when {{pauseTypes}} {{scenes}} {{windows}}"
|
AdvSceneSwitcher.pauseTab.pauseEntry="Pause {{pauseTargets}} checks when {{pauseTypes}} {{scenes}} {{windows}}"
|
||||||
|
AdvSceneSwitcher.pauseTab.help="On this tab you can configure to pause individual switching methods if a scene is active or window is in focus.\n\nClick on the highlighted plus symbol to continue."
|
||||||
|
|
||||||
; Window Title Tab
|
; Window Title Tab
|
||||||
AdvSceneSwitcher.windowTitleTab.title="Title"
|
AdvSceneSwitcher.windowTitleTab.title="Title"
|
||||||
@@ -79,19 +83,24 @@ AdvSceneSwitcher.windowTitleTab.fullscreen="if fullscreen"
|
|||||||
AdvSceneSwitcher.windowTitleTab.maximized="if maximized"
|
AdvSceneSwitcher.windowTitleTab.maximized="if maximized"
|
||||||
AdvSceneSwitcher.windowTitleTab.focused="if focused"
|
AdvSceneSwitcher.windowTitleTab.focused="if focused"
|
||||||
AdvSceneSwitcher.windowTitleTab.entry="{{windows}} {{scenes}} {{transitions}} {{fullscreen}} {{maximized}} {{focused}}"
|
AdvSceneSwitcher.windowTitleTab.entry="{{windows}} {{scenes}} {{transitions}} {{fullscreen}} {{maximized}} {{focused}}"
|
||||||
|
AdvSceneSwitcher.windowTitleTab.windowsHelp="Switch scenes based on the window title of running applications.\nThe following additional conditions can be selected:\nThe window is Fullscreen\nThe window is maximized\nThe window is focused\n\nClick on the highlighted plus symbol to continue."
|
||||||
|
AdvSceneSwitcher.windowTitleTab.ignoreWindowsHelp="If a window title is ignored the scene switcher will act as if the previously selected window is still in focus.\nThis will allow you to avoid scene switches, if you frequently switch to a different window, which shall not trigger a scene change.\n\nChoose a window or enter a window title above and click on the plus symbol below to add it to the list."
|
||||||
|
|
||||||
; Executable Tab
|
; Executable Tab
|
||||||
AdvSceneSwitcher.executableTab.title="Executable"
|
AdvSceneSwitcher.executableTab.title="Executable"
|
||||||
AdvSceneSwitcher.executableTab.implemented="Implemented by dasOven"
|
AdvSceneSwitcher.executableTab.implemented="Implemented by dasOven"
|
||||||
AdvSceneSwitcher.executableTab.requiresFocus="only if focused"
|
AdvSceneSwitcher.executableTab.requiresFocus="only if focused"
|
||||||
AdvSceneSwitcher.executableTab.entry="When {{processes}} is running switch to {{scenes}} using {{transitions}} {{requiresFocus}}"
|
AdvSceneSwitcher.executableTab.entry="When {{processes}} is running switch to {{scenes}} using {{transitions}} {{requiresFocus}}"
|
||||||
|
AdvSceneSwitcher.executableTab.help="This tab will allow you to automatically switch scenes if a process is running.\nThis can be useful in situations where the window name could change or is not known.\n\nClick on the highlighted plus symbol to continue."
|
||||||
|
|
||||||
; Screen Region Tab
|
; Screen Region Tab
|
||||||
AdvSceneSwitcher.screenRegionTab.title="Region"
|
AdvSceneSwitcher.screenRegionTab.title="Region"
|
||||||
AdvSceneSwitcher.screenRegionTab.currentPosition="Cursor is currently at:"
|
AdvSceneSwitcher.screenRegionTab.currentPosition="Cursor is currently at:"
|
||||||
AdvSceneSwitcher.screenRegionTab.showGuideFrames="Show guide frames"
|
AdvSceneSwitcher.screenRegionTab.showGuideFrames="Show guide frames"
|
||||||
AdvSceneSwitcher.screenRegionTab.hideGuideFrames="Hide guide frames"
|
AdvSceneSwitcher.screenRegionTab.hideGuideFrames="Hide guide frames"
|
||||||
AdvSceneSwitcher.screenRegionTab.entry="If cursor is in {{minX}} {{minY}} x {{maxX}} {{maxY}} switch to {{scenes}} using {{transitions}}"
|
AdvSceneSwitcher.screenRegionTab.excludeScenes.None="No selection"
|
||||||
|
AdvSceneSwitcher.screenRegionTab.entry="If cursor is in {{minX}} {{minY}} x {{maxX}} {{maxY}} switch to {{scenes}} using {{transitions}} unless in {{excludeScenes}}"
|
||||||
|
AdvSceneSwitcher.screenRegionTab.help="This tab will allow you to automatically switch scenes based on the current position of your mouse cursor.\n\nClick on the highlighted plus symbol to continue."
|
||||||
|
|
||||||
; Media Tab
|
; Media Tab
|
||||||
AdvSceneSwitcher.mediaTab.title="Media"
|
AdvSceneSwitcher.mediaTab.title="Media"
|
||||||
@@ -112,6 +121,7 @@ AdvSceneSwitcher.mediaTab.timeRestriction.longer="Time longer"
|
|||||||
AdvSceneSwitcher.mediaTab.timeRestriction.remainShorter="Time remaining shorter"
|
AdvSceneSwitcher.mediaTab.timeRestriction.remainShorter="Time remaining shorter"
|
||||||
AdvSceneSwitcher.mediaTab.timeRestriction.remainLonger="Time remaining longer"
|
AdvSceneSwitcher.mediaTab.timeRestriction.remainLonger="Time remaining longer"
|
||||||
AdvSceneSwitcher.mediaTab.entry="When {{mediaSources}} state is {{states}} and {{timeRestrictions}} {{time}} switch to {{scenes}} using {{transitions}}"
|
AdvSceneSwitcher.mediaTab.entry="When {{mediaSources}} state is {{states}} and {{timeRestrictions}} {{time}} switch to {{scenes}} using {{transitions}}"
|
||||||
|
AdvSceneSwitcher.mediaTab.help="This tab will allow you to switch scenes based on the states of media sources.\nFor example, you can automatically switch back to the previous scene once the selected media sourced ended its playback.\n\nClick on the highlighted plus symbol to continue."
|
||||||
|
|
||||||
; File Tab
|
; File Tab
|
||||||
AdvSceneSwitcher.fileTab.title="File"
|
AdvSceneSwitcher.fileTab.title="File"
|
||||||
@@ -136,11 +146,13 @@ AdvSceneSwitcher.fileTab.checkfileContent="if content changed"
|
|||||||
AdvSceneSwitcher.fileTab.entry="Switch to {{scenes}} using {{transitions}} if content of {{fileType}} {{filePath}} {{browseButton}} matches:"
|
AdvSceneSwitcher.fileTab.entry="Switch to {{scenes}} using {{transitions}} if content of {{fileType}} {{filePath}} {{browseButton}} matches:"
|
||||||
AdvSceneSwitcher.fileTab.entry2="{{matchText}}"
|
AdvSceneSwitcher.fileTab.entry2="{{matchText}}"
|
||||||
AdvSceneSwitcher.fileTab.entry3="{{useRegex}} {{checkModificationDate}} {{checkFileContent}}"
|
AdvSceneSwitcher.fileTab.entry3="{{useRegex}} {{checkModificationDate}} {{checkFileContent}}"
|
||||||
|
AdvSceneSwitcher.fileTab.help="This tab will allow you to automatically switch scenes based on the content of remote or local files.\n\nClick on the highlighted plus symbol to continue."
|
||||||
|
|
||||||
; Random Tab
|
; Random Tab
|
||||||
AdvSceneSwitcher.randomTab.title="Random"
|
AdvSceneSwitcher.randomTab.title="Random"
|
||||||
AdvSceneSwitcher.randomTab.randomDisabledWarning="Functionality disabled - To activate select \"If no switch condition is met switch to any scene in Random tab\" on General tab"
|
AdvSceneSwitcher.randomTab.randomDisabledWarning="Functionality disabled - To activate select \"If no switch condition is met switch to any scene in Random tab\" on General tab"
|
||||||
AdvSceneSwitcher.randomTab.entry="If no switch condition is met switch to {{scenes}} using {{transitions}} for {{delay}}"
|
AdvSceneSwitcher.randomTab.entry="If no switch condition is met switch to {{scenes}} using {{transitions}} for {{delay}}"
|
||||||
|
AdvSceneSwitcher.randomTab.help="The scene switcher will randomly choose an entry on this tab to switch to for the configured time.\nNote that the same entry will not be chosen twice in a row.\n\nClick on the highlighted plus symbol to continue."
|
||||||
|
|
||||||
; Time Tab
|
; Time Tab
|
||||||
AdvSceneSwitcher.timeTab.title="Time"
|
AdvSceneSwitcher.timeTab.title="Time"
|
||||||
@@ -155,15 +167,12 @@ AdvSceneSwitcher.timeTab.sundays="Sundays"
|
|||||||
AdvSceneSwitcher.timeTab.afterstart="After streaming/recording start"
|
AdvSceneSwitcher.timeTab.afterstart="After streaming/recording start"
|
||||||
AdvSceneSwitcher.timeTab.afterstart.tip="The time relative to the start of streaming / recording will be used"
|
AdvSceneSwitcher.timeTab.afterstart.tip="The time relative to the start of streaming / recording will be used"
|
||||||
AdvSceneSwitcher.timeTab.entry="{{triggers}} at {{time}} switch to {{scenes}} using {{transitions}}"
|
AdvSceneSwitcher.timeTab.entry="{{triggers}} at {{time}} switch to {{scenes}} using {{transitions}}"
|
||||||
|
AdvSceneSwitcher.timeTab.help="This tab will allow you to automatically switch to a different scene based on the current local time.\n\nNote that the scene switcher will only switch scenes at the exact time you specified.\nMake sure you have configured the priority settings on the General tab to your liking so the selected time point will not be missed due to other switching methods having a higher priority.\n\nClick on the highlighted plus symbol to continue."
|
||||||
|
|
||||||
; Idle Tab
|
; Idle Tab
|
||||||
AdvSceneSwitcher.idleTab.title="Idle"
|
AdvSceneSwitcher.idleTab.title="Idle"
|
||||||
AdvSceneSwitcher.idleTab.enable="Enable Idle Detection"
|
AdvSceneSwitcher.idleTab.enable="Enable Idle Detection"
|
||||||
AdvSceneSwitcher.idleTab.condition1="After "
|
AdvSceneSwitcher.idleTab.idleswitch="After {{duration}} of no keyboard or mouse inputs switch to scene {{scenes}} using the {{transitions}}"
|
||||||
AdvSceneSwitcher.idleTab.condition2="of no keyboard or mouse inputs "
|
|
||||||
AdvSceneSwitcher.idleTab.switchToScene="switch to scene"
|
|
||||||
AdvSceneSwitcher.idleTab.usingTransition1="using the"
|
|
||||||
AdvSceneSwitcher.idleTab.usingTransition2="transition"
|
|
||||||
AdvSceneSwitcher.idleTab.dontSwitchIfFocus1="Do not switch if"
|
AdvSceneSwitcher.idleTab.dontSwitchIfFocus1="Do not switch if"
|
||||||
AdvSceneSwitcher.idleTab.dontSwitchIfFocus2="is in focus"
|
AdvSceneSwitcher.idleTab.dontSwitchIfFocus2="is in focus"
|
||||||
|
|
||||||
@@ -180,28 +189,113 @@ AdvSceneSwitcher.sceneSequenceTab.fileType="Text files (*.txt)"
|
|||||||
AdvSceneSwitcher.sceneSequenceTab.interruptible="interruptible"
|
AdvSceneSwitcher.sceneSequenceTab.interruptible="interruptible"
|
||||||
AdvSceneSwitcher.sceneSequenceTab.interruptibleHint="Other switching methods are allowed to interrupt this scene sequence"
|
AdvSceneSwitcher.sceneSequenceTab.interruptibleHint="Other switching methods are allowed to interrupt this scene sequence"
|
||||||
AdvSceneSwitcher.sceneSequenceTab.entry="When {{startScenes}} is active switch to {{scenes}} after {{delay}} {{delayUnits}} using {{transitions}} {{interruptible}}"
|
AdvSceneSwitcher.sceneSequenceTab.entry="When {{startScenes}} is active switch to {{scenes}} after {{delay}} {{delayUnits}} using {{transitions}} {{interruptible}}"
|
||||||
|
AdvSceneSwitcher.sceneSequenceTab.extendEdit="Extend Sequence"
|
||||||
|
AdvSceneSwitcher.sceneSequenceTab.extendEntry="After {{delay}} {{delayUnits}} switch to {{scenes}} using {{transitions}}"
|
||||||
|
AdvSceneSwitcher.sceneSequenceTab.help="This tab will allow you to automatically switch to a different scene if a scene was active for a configured period of time.\nFor example, you could automatically cycle back and forth between two scenes automatically.\n\nClick on the highlighted plus symbol to continue."
|
||||||
|
|
||||||
; Audio Tab
|
; Audio Tab
|
||||||
AdvSceneSwitcher.audioTab.title="Audio"
|
AdvSceneSwitcher.audioTab.title="Audio"
|
||||||
AdvSceneSwitcher.audioTab.condition.above="above"
|
AdvSceneSwitcher.audioTab.condition.above="above"
|
||||||
AdvSceneSwitcher.audioTab.condition.below="below"
|
AdvSceneSwitcher.audioTab.condition.below="below"
|
||||||
AdvSceneSwitcher.audioTab.entry="When the volume of {{audioSources}} is {{condition}} {{volumeWidget}} for {{duration}} switch to {{scenes}} using {{transitions}}"
|
AdvSceneSwitcher.audioTab.ignoreInactiveSource="unless source is inactive"
|
||||||
|
AdvSceneSwitcher.audioTab.entry="When the volume of {{audioSources}} is {{condition}} {{volumeWidget}} for {{duration}} switch to {{scenes}} using {{transitions}} {{ignoreInactiveSource}}"
|
||||||
AdvSceneSwitcher.audioTab.multiMatchfallbackCondition="If multiple entries match ..."
|
AdvSceneSwitcher.audioTab.multiMatchfallbackCondition="If multiple entries match ..."
|
||||||
AdvSceneSwitcher.audioTab.multiMatchfallback="... for {{duration}} switch to {{scenes}} using {{transitions}}"
|
AdvSceneSwitcher.audioTab.multiMatchfallback="... for {{duration}} switch to {{scenes}} using {{transitions}}"
|
||||||
|
AdvSceneSwitcher.audioTab.help="This tab will allow you to switch scenes based on the volume of sources.\nFor example, you could automatically switch to a different scene if the volume of your microphone reaches a certain threshold.\n\nClick on the highlighted plus symbol to continue."
|
||||||
|
|
||||||
|
; Video Tab
|
||||||
|
AdvSceneSwitcher.videoTab.title="Video"
|
||||||
|
AdvSceneSwitcher.videoTab.getScreenshot="Get screenshot for selected entry"
|
||||||
|
AdvSceneSwitcher.videoTab.getScreenshotHelp="Get Screenshot of the currently selected entry's video source and automatically set it as the target image"
|
||||||
|
AdvSceneSwitcher.videoTab.condition.match="exactly matches"
|
||||||
|
AdvSceneSwitcher.videoTab.condition.match.tooltip="An exact match requires the target and the source image to be of the same resolution.\nAdditionally every single pixel needs to match, which is why use of image formats which use compression (e.g. .JPG) is not recommended!"
|
||||||
|
AdvSceneSwitcher.videoTab.condition.differ="does not match"
|
||||||
|
AdvSceneSwitcher.videoTab.condition.hasNotChanged="has not changed"
|
||||||
|
AdvSceneSwitcher.videoTab.ignoreInactiveSource="unless source is inactive"
|
||||||
|
AdvSceneSwitcher.videoTab.entry="When {{videoSources}} {{condition}} {{filePath}} {{browseButton}} for {{duration}} switch to {{scenes}} using {{transitions}} {{ignoreInactiveSource}}"
|
||||||
|
AdvSceneSwitcher.videoTab.help="<html><head/><body><p>This tab will allow you to switch scenes based on the current video output of selected sources.<br/>Make sure to check out <a href=\"https://obsproject.com/forum/resources/pixel-match-switcher.1202/\"><span style=\" text-decoration: underline; color:#268bd2;\">Pixel Match Switcher</span></a> for an even better implementation of this functionality.<br/><br/> Click on the highlighted plus symbol to continue.</p></body></html>"
|
||||||
|
|
||||||
|
; Network Tab
|
||||||
|
AdvSceneSwitcher.networkTab.title="Network"
|
||||||
|
AdvSceneSwitcher.networkTab.warning="Running the server outside of a local network will allow third parties to read the active scene."
|
||||||
|
AdvSceneSwitcher.networkTab.server="Start server (Sends scene switch messages to all connected clients)"
|
||||||
|
AdvSceneSwitcher.networkTab.server.port="Port"
|
||||||
|
AdvSceneSwitcher.networkTab.server.lockToIPv4="Lock server to only using IPv4"
|
||||||
|
AdvSceneSwitcher.networkTab.server.restrictSendToAutomatedSwitches="Only send messages for automated scene switches"
|
||||||
|
AdvSceneSwitcher.networkTab.startFailed.message="The WebSockets server failed to start, maybe because:\n - TCP port %1 may currently be in use elsewhere on this system, possibly by another application. Try setting a different TCP port in the WebSocket server settings, or stop any application that could be using this port.\n - Error message: %2"
|
||||||
|
AdvSceneSwitcher.networkTab.server.status.currentStatus="Current status"
|
||||||
|
AdvSceneSwitcher.networkTab.server.status.notRunning="Not running"
|
||||||
|
AdvSceneSwitcher.networkTab.server.status.starting="Starting"
|
||||||
|
AdvSceneSwitcher.networkTab.server.status.running="Running"
|
||||||
|
AdvSceneSwitcher.networkTab.server.restart="Restart server"
|
||||||
|
AdvSceneSwitcher.networkTab.client="Start client (Receives scene switches messages)"
|
||||||
|
AdvSceneSwitcher.networkTab.client.address="Hostname or IP address"
|
||||||
|
AdvSceneSwitcher.networkTab.client.port="Port"
|
||||||
|
AdvSceneSwitcher.networkTab.client.status.currentStatus="Current status"
|
||||||
|
AdvSceneSwitcher.networkTab.client.status.disconnected="Disconnected"
|
||||||
|
AdvSceneSwitcher.networkTab.client.status.connecting="Connecting"
|
||||||
|
AdvSceneSwitcher.networkTab.client.status.connected="Connected"
|
||||||
|
AdvSceneSwitcher.networkTab.client.reconnect="Force reconnect"
|
||||||
|
|
||||||
|
; Scene Group Tab
|
||||||
|
AdvSceneSwitcher.sceneGroupTab.title="Scene Group"
|
||||||
|
AdvSceneSwitcher.sceneGroupTab.list="Scene Groups"
|
||||||
|
AdvSceneSwitcher.sceneGroupTab.edit="Edit Scene Groups"
|
||||||
|
AdvSceneSwitcher.sceneGroupTab.edit.name="Name:"
|
||||||
|
AdvSceneSwitcher.sceneGroupTab.edit.type="Type: {{type}}"
|
||||||
|
AdvSceneSwitcher.sceneGroupTab.type.count="Count"
|
||||||
|
AdvSceneSwitcher.sceneGroupTab.type.time="Time"
|
||||||
|
AdvSceneSwitcher.sceneGroupTab.type.random="Random"
|
||||||
|
AdvSceneSwitcher.sceneGroupTab.edit.count="Advance to next scene in list after {{count}} matches"
|
||||||
|
AdvSceneSwitcher.sceneGroupTab.edit.time="Advance to next scene in list after {{time}} has passed"
|
||||||
|
AdvSceneSwitcher.sceneGroupTab.edit.random="Choose next scene in list at random"
|
||||||
|
AdvSceneSwitcher.sceneGroupTab.edit.repeat="Start from beginning if end of scene list is reached"
|
||||||
|
AdvSceneSwitcher.sceneGroupTab.edit.addScene="Add scene"
|
||||||
|
AdvSceneSwitcher.sceneGroupTab.add="Add Scene Group"
|
||||||
|
AdvSceneSwitcher.sceneGroupTab.defaultname="Scene Group %1"
|
||||||
|
AdvSceneSwitcher.sceneGroupTab.exists="Scene Group or Scene name exists already"
|
||||||
|
AdvSceneSwitcher.sceneGroupTab.help="Scene Groups can be selected as a target just like a regular scene.\n\nAs the name suggests a scene group is a collection of multiple scenes.\nThe scene group will advance through the list of its assigned scenes depending on the configured settings, which can be found on the right side.\n\nYou can configure the scene group to advance to the next scene in the list:\nAfter a number of times the scene group is selected as a target.\nAfter a certain amount of time has passed.\nOr randomly.\n\nFor example, a scene group containing the scenes ...\nScene 1\nScene 2\nScene 3 \n... will activate \"Scene 1\" the first time it is selected as a target.\nThe second time it will activate \"Scene 2\".\nThe remaining times \"Scene 3\" will be activated.\n\nClick the highlighted plus symbol below to add a new scene group."
|
||||||
|
AdvSceneSwitcher.sceneGroupTab.scenes.help="Select the scene group you want to modify on the left.\n\nSelect a scene to add to this scene group by selecting the scene above and clicking the plus symbol below.\n\nA scene can be added multiple times to the same scene group."
|
||||||
|
|
||||||
|
; Scene Trigger Tab
|
||||||
|
AdvSceneSwitcher.sceneTriggerTab.title="Scene Triggers"
|
||||||
|
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerType.none="--select trigger--"
|
||||||
|
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerType.sceneActive="is active"
|
||||||
|
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerType.sceneInactive="is not active"
|
||||||
|
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerType.sceneLeave="switched away from"
|
||||||
|
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.none="--select action--"
|
||||||
|
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.startRecording="start recording"
|
||||||
|
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.pauseRecording="pause recording"
|
||||||
|
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.unpauseRecording="unpause recording"
|
||||||
|
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.stopRecording="stop recording"
|
||||||
|
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.stopStreaming="stop streaming"
|
||||||
|
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.startStreaming="start streaming"
|
||||||
|
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.startReplayBuffer="start replay buffer"
|
||||||
|
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.stopReplayBuffer="stop replay buffer"
|
||||||
|
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.muteSource="mute source"
|
||||||
|
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.unmuteSource="unmute source"
|
||||||
|
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.startSwitcher="start the scene switcher"
|
||||||
|
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.stopSwitcher="stop the scene switcher"
|
||||||
|
AdvSceneSwitcher.sceneTriggerTab.entry="When {{scenes}} {{triggers}} {{actions}} {{audioSources}} after {{duration}}"
|
||||||
|
AdvSceneSwitcher.sceneTriggerTab.help="This tab allows you to trigger actions on scene changes, like stopping recording or streaming."
|
||||||
|
|
||||||
; Hotkey
|
; Hotkey
|
||||||
AdvSceneSwitcher.hotkey.startSwitcherHotkey="Start the Advanced Scene Switcher"
|
AdvSceneSwitcher.hotkey.startSwitcherHotkey="Start the Advanced Scene Switcher"
|
||||||
AdvSceneSwitcher.hotkey.stopSwitcherHotkey="Stop the Advanced Scene Switcher"
|
AdvSceneSwitcher.hotkey.stopSwitcherHotkey="Stop the Advanced Scene Switcher"
|
||||||
AdvSceneSwitcher.hotkey.startStopToggleSwitcherHotkey="Toggle Start/Stop for the Advanced Scene Switcher"
|
AdvSceneSwitcher.hotkey.startStopToggleSwitcherHotkey="Toggle Start/Stop for the Advanced Scene Switcher"
|
||||||
|
|
||||||
|
AdvSceneSwitcher.askBackup="Detected a new version of the Advanced Scene Switcher.\nShould a backup of the old settings be created?"
|
||||||
|
|
||||||
AdvSceneSwitcher.close="Close"
|
AdvSceneSwitcher.close="Close"
|
||||||
AdvSceneSwitcher.browse="Browse"
|
AdvSceneSwitcher.browse="Browse"
|
||||||
|
|
||||||
AdvSceneSwitcher.selectScene="--select scene--"
|
AdvSceneSwitcher.selectScene="--select scene--"
|
||||||
AdvSceneSwitcher.selectPreviousScene="Previous Scene"
|
AdvSceneSwitcher.selectPreviousScene="Previous Scene"
|
||||||
|
AdvSceneSwitcher.currentTransition="Current Transition"
|
||||||
AdvSceneSwitcher.selectTransition="--select transition--"
|
AdvSceneSwitcher.selectTransition="--select transition--"
|
||||||
AdvSceneSwitcher.selectWindow="--select window--"
|
AdvSceneSwitcher.selectWindow="--select window--"
|
||||||
AdvSceneSwitcher.selectAudioSource="--select audio source--"
|
AdvSceneSwitcher.selectAudioSource="--select audio source--"
|
||||||
|
AdvSceneSwitcher.selectVideoSource="--select video source--"
|
||||||
AdvSceneSwitcher.selectMediaSource="--select media source--"
|
AdvSceneSwitcher.selectMediaSource="--select media source--"
|
||||||
AdvSceneSwitcher.selectProcess="--select process--"
|
AdvSceneSwitcher.selectProcess="--select process--"
|
||||||
AdvSceneSwitcher.enterPath="--enter path--"
|
AdvSceneSwitcher.enterPath="--enter path--"
|
||||||
|
|||||||
@@ -12,20 +12,17 @@ AdvSceneSwitcher.generalTab.status.onStartup.alwaysStart="总是自动启动"
|
|||||||
AdvSceneSwitcher.generalTab.status.onStartup.doNotStart="不要启动"
|
AdvSceneSwitcher.generalTab.status.onStartup.doNotStart="不要启动"
|
||||||
AdvSceneSwitcher.generalTab.status.start="启动"
|
AdvSceneSwitcher.generalTab.status.start="启动"
|
||||||
AdvSceneSwitcher.generalTab.status.stop="停止"
|
AdvSceneSwitcher.generalTab.status.stop="停止"
|
||||||
|
AdvSceneSwitcher.generalTab.status.autoStart.recording="录制"
|
||||||
|
AdvSceneSwitcher.generalTab.status.autoStart.streaming="推流"
|
||||||
|
AdvSceneSwitcher.generalTab.status.autoStart.recordingAndStreaming="录制和推流"
|
||||||
AdvSceneSwitcher.generalTab.status.checkInterval="检查切换条件时间间隔"
|
AdvSceneSwitcher.generalTab.status.checkInterval="检查切换条件时间间隔"
|
||||||
AdvSceneSwitcher.generalTab.generalBehavior="一般表现"
|
AdvSceneSwitcher.generalTab.generalBehavior="一般表现"
|
||||||
AdvSceneSwitcher.generalTab.generalBehavior.automaticallyStart.recording="录制"
|
|
||||||
AdvSceneSwitcher.generalTab.generalBehavior.automaticallyStart.streaming="推流"
|
|
||||||
AdvSceneSwitcher.generalTab.generalBehavior.automaticallyStart.recordingAndStreaming="录制和推流"
|
|
||||||
AdvSceneSwitcher.generalTab.generalBehavior.automaticallyStart1="自动开始"
|
|
||||||
AdvSceneSwitcher.generalTab.generalBehavior.automaticallyStart2="当切换到场景时"
|
|
||||||
AdvSceneSwitcher.generalTab.generalBehavior.onNoMet="如果不符合任何切换条件"
|
AdvSceneSwitcher.generalTab.generalBehavior.onNoMet="如果不符合任何切换条件"
|
||||||
AdvSceneSwitcher.generalTab.generalBehavior.onNoMetDelayTooltip="时间精度最高只能达到配置的轮询间隔"
|
AdvSceneSwitcher.generalTab.generalBehavior.onNoMetDelayTooltip="时间精度最高只能达到配置的轮询间隔"
|
||||||
AdvSceneSwitcher.generalTab.generalBehavior.onNoMet.dontSwitch="不切换"
|
AdvSceneSwitcher.generalTab.generalBehavior.onNoMet.dontSwitch="不切换"
|
||||||
AdvSceneSwitcher.generalTab.generalBehavior.onNoMet.switchToRandom="切换到随机场景列表中任意随机场景"
|
AdvSceneSwitcher.generalTab.generalBehavior.onNoMet.switchToRandom="切换到随机场景列表中任意随机场景"
|
||||||
AdvSceneSwitcher.generalTab.generalBehavior.onNoMet.switchTo="切换到"
|
AdvSceneSwitcher.generalTab.generalBehavior.onNoMet.switchTo="切换到"
|
||||||
AdvSceneSwitcher.generalTab.generalBehavior.verboseLogging="详细日志输出"
|
AdvSceneSwitcher.generalTab.generalBehavior.verboseLogging="详细日志输出"
|
||||||
AdvSceneSwitcher.generalTab.generalBehavior.automaticallyStop="当切换到场景时自动停止推流和录制"
|
|
||||||
AdvSceneSwitcher.generalTab.generalBehavior.disableUIHints="禁用UI提示"
|
AdvSceneSwitcher.generalTab.generalBehavior.disableUIHints="禁用UI提示"
|
||||||
AdvSceneSwitcher.generalTab.priority="优先级"
|
AdvSceneSwitcher.generalTab.priority="优先级"
|
||||||
AdvSceneSwitcher.generalTab.priority.description="切换场景优先级 (最上方的项优先级最高)"
|
AdvSceneSwitcher.generalTab.priority.description="切换场景优先级 (最上方的项优先级最高)"
|
||||||
@@ -53,7 +50,7 @@ AdvSceneSwitcher.generalTab.priority.audio="音频"
|
|||||||
AdvSceneSwitcher.transitionTab.title="转场特效"
|
AdvSceneSwitcher.transitionTab.title="转场特效"
|
||||||
AdvSceneSwitcher.transitionTab.transitionOverride="在场景切换器中设定的转场特效优先级高于场景设定的转场特效"
|
AdvSceneSwitcher.transitionTab.transitionOverride="在场景切换器中设定的转场特效优先级高于场景设定的转场特效"
|
||||||
AdvSceneSwitcher.transitionTab.transitionForAToB="当自动从场景A切换到场景B时使用的转场特效"
|
AdvSceneSwitcher.transitionTab.transitionForAToB="当自动从场景A切换到场景B时使用的转场特效"
|
||||||
AdvSceneSwitcher.transitionTab.transitionForAToB.description="<html><head/><body><p>这里的设定<span style=\"font-style:bold;\">只影响由场景切换器引起的转场有效</span>,不影响你手动的引起的转场。<br/>在这里设定的转场特效优先级高于场景切换器其他地方设置的</p></body></html>"
|
AdvSceneSwitcher.transitionTab.transitionsHelp="<html><head/><body><p>这里的设定<span style=\"font-style:bold;\">只影响由场景切换器引起的转场有效</span>,不影响你手动的引起的转场。<br/>在这里设定的转场特效优先级高于场景切换器其他地方设置的<br/><br/>Click the plus symbol below to continue.</p></body></html>"
|
||||||
AdvSceneSwitcher.transitionTab.defaultTransition="当切换到这个场景时修改默认转场特效"
|
AdvSceneSwitcher.transitionTab.defaultTransition="当切换到这个场景时修改默认转场特效"
|
||||||
AdvSceneSwitcher.transitionTab.switchDefaultLabel="活跃时切换默认转场特效为"
|
AdvSceneSwitcher.transitionTab.switchDefaultLabel="活跃时切换默认转场特效为"
|
||||||
AdvSceneSwitcher.transitionTab.entry="从场景 {{scenes}} 切换到场景 {{scenes2}} 时使用转场特效 {{transitions}}"
|
AdvSceneSwitcher.transitionTab.entry="从场景 {{scenes}} 切换到场景 {{scenes2}} 时使用转场特效 {{transitions}}"
|
||||||
@@ -86,7 +83,7 @@ AdvSceneSwitcher.screenRegionTab.title="屏幕区域"
|
|||||||
AdvSceneSwitcher.screenRegionTab.currentPosition="鼠标当前位置:"
|
AdvSceneSwitcher.screenRegionTab.currentPosition="鼠标当前位置:"
|
||||||
AdvSceneSwitcher.screenRegionTab.showGuideFrames="显示区域边界"
|
AdvSceneSwitcher.screenRegionTab.showGuideFrames="显示区域边界"
|
||||||
AdvSceneSwitcher.screenRegionTab.hideGuideFrames="隐藏区域边界"
|
AdvSceneSwitcher.screenRegionTab.hideGuideFrames="隐藏区域边界"
|
||||||
AdvSceneSwitcher.screenRegionTab.entry="如果鼠标在 {{minX}} {{minY}} x {{maxX}} {{maxY}} 使用转场特效 {{transitions}} 切换到场景 {{scenes}}"
|
AdvSceneSwitcher.screenRegionTab.entry="如果鼠标在 {{minX}} {{minY}} x {{maxX}} {{maxY}} 使用转场特效 {{transitions}} 切换到场景 {{scenes}} unless in {{excludeScenes}}"
|
||||||
|
|
||||||
; Media Tab
|
; Media Tab
|
||||||
AdvSceneSwitcher.mediaTab.title="媒体"
|
AdvSceneSwitcher.mediaTab.title="媒体"
|
||||||
|
|||||||
1
deps/asio
vendored
Submodule
1
deps/asio
vendored
Submodule
Submodule deps/asio added at 93f87fbdbf
1
deps/websocketpp
vendored
Submodule
1
deps/websocketpp
vendored
Submodule
Submodule deps/websocketpp added at 1b11fd3015
File diff suppressed because it is too large
Load Diff
@@ -15,9 +15,9 @@
|
|||||||
|
|
||||||
SwitcherData *switcher = nullptr;
|
SwitcherData *switcher = nullptr;
|
||||||
|
|
||||||
/********************************************************************************
|
/******************************************************************************
|
||||||
* Create the Advanced Scene Switcher settings window
|
* Create the Advanced Scene Switcher settings window
|
||||||
********************************************************************************/
|
******************************************************************************/
|
||||||
AdvSceneSwitcher::AdvSceneSwitcher(QWidget *parent)
|
AdvSceneSwitcher::AdvSceneSwitcher(QWidget *parent)
|
||||||
: QDialog(parent), ui(new Ui_AdvSceneSwitcher)
|
: QDialog(parent), ui(new Ui_AdvSceneSwitcher)
|
||||||
{
|
{
|
||||||
@@ -38,8 +38,9 @@ bool translationAvailable()
|
|||||||
void AdvSceneSwitcher::loadUI()
|
void AdvSceneSwitcher::loadUI()
|
||||||
{
|
{
|
||||||
if (!translationAvailable()) {
|
if (!translationAvailable()) {
|
||||||
DisplayMessage("Failed to find plug-in's 'data' directory.\n"
|
(void)DisplayMessage(
|
||||||
"Please check installation instructions!");
|
"Failed to find plug-in's 'data' directory.\n"
|
||||||
|
"Please check installation instructions!");
|
||||||
}
|
}
|
||||||
|
|
||||||
#if __APPLE__
|
#if __APPLE__
|
||||||
@@ -58,25 +59,44 @@ void AdvSceneSwitcher::loadUI()
|
|||||||
setupFileTab();
|
setupFileTab();
|
||||||
setupTimeTab();
|
setupTimeTab();
|
||||||
setupAudioTab();
|
setupAudioTab();
|
||||||
|
setupVideoTab();
|
||||||
|
setupNetworkTab();
|
||||||
|
setupSceneGroupTab();
|
||||||
|
setupTriggerTab();
|
||||||
|
|
||||||
setTabOrder();
|
setTabOrder();
|
||||||
|
|
||||||
loading = false;
|
loading = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/********************************************************************************
|
/******************************************************************************
|
||||||
* UI helpers
|
* UI helpers
|
||||||
********************************************************************************/
|
******************************************************************************/
|
||||||
void AdvSceneSwitcher::DisplayMessage(QString msg)
|
bool AdvSceneSwitcher::DisplayMessage(QString msg, bool question)
|
||||||
{
|
{
|
||||||
QMessageBox Msgbox;
|
if (question) {
|
||||||
Msgbox.setWindowTitle("Advanced Scene Switcher");
|
QMessageBox::StandardButton reply;
|
||||||
Msgbox.setText(msg);
|
reply = QMessageBox::question(
|
||||||
Msgbox.exec();
|
nullptr, "Advanced Scene Switcher", msg,
|
||||||
|
QMessageBox::Yes | QMessageBox::No);
|
||||||
|
if (reply == QMessageBox::Yes) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
QMessageBox Msgbox;
|
||||||
|
Msgbox.setWindowTitle("Advanced Scene Switcher");
|
||||||
|
Msgbox.setText(msg);
|
||||||
|
Msgbox.exec();
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void addSelectionEntry(QComboBox *sel, const char *description,
|
void AdvSceneSwitcher::addSelectionEntry(QComboBox *sel,
|
||||||
const char *tooltip = "")
|
const char *description,
|
||||||
|
bool selectable, const char *tooltip)
|
||||||
{
|
{
|
||||||
sel->addItem(description);
|
sel->addItem(description);
|
||||||
|
|
||||||
@@ -89,18 +109,30 @@ void addSelectionEntry(QComboBox *sel, const char *description,
|
|||||||
QModelIndex firstIndex =
|
QModelIndex firstIndex =
|
||||||
model->index(0, sel->modelColumn(), sel->rootModelIndex());
|
model->index(0, sel->modelColumn(), sel->rootModelIndex());
|
||||||
QStandardItem *firstItem = model->itemFromIndex(firstIndex);
|
QStandardItem *firstItem = model->itemFromIndex(firstIndex);
|
||||||
firstItem->setSelectable(false);
|
if (!selectable) {
|
||||||
firstItem->setEnabled(false);
|
firstItem->setSelectable(false);
|
||||||
|
firstItem->setEnabled(false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void AdvSceneSwitcher::populateSceneSelection(QComboBox *sel, bool addPrevious,
|
void AdvSceneSwitcher::populateSceneSelection(QComboBox *sel, bool addPrevious,
|
||||||
bool addSelect)
|
bool addSceneGroup,
|
||||||
|
bool addSelect,
|
||||||
|
std::string selectText,
|
||||||
|
bool selectable)
|
||||||
{
|
{
|
||||||
if (addSelect)
|
if (addSelect) {
|
||||||
addSelectionEntry(
|
if (selectText.empty()) {
|
||||||
sel, obs_module_text("AdvSceneSwitcher.selectScene"),
|
addSelectionEntry(
|
||||||
obs_module_text(
|
sel,
|
||||||
"AdvSceneSwitcher.invaildEntriesWillNotBeSaved"));
|
obs_module_text("AdvSceneSwitcher.selectScene"),
|
||||||
|
selectable,
|
||||||
|
obs_module_text(
|
||||||
|
"AdvSceneSwitcher.invaildEntriesWillNotBeSaved"));
|
||||||
|
} else {
|
||||||
|
addSelectionEntry(sel, selectText.c_str(), selectable);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
BPtr<char *> scenes = obs_frontend_get_scene_names();
|
BPtr<char *> scenes = obs_frontend_get_scene_names();
|
||||||
char **temp = scenes;
|
char **temp = scenes;
|
||||||
@@ -110,18 +142,34 @@ void AdvSceneSwitcher::populateSceneSelection(QComboBox *sel, bool addPrevious,
|
|||||||
temp++;
|
temp++;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (addPrevious)
|
if (addPrevious) {
|
||||||
sel->addItem(obs_module_text(
|
sel->addItem(obs_module_text(
|
||||||
"AdvSceneSwitcher.selectPreviousScene"));
|
"AdvSceneSwitcher.selectPreviousScene"));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (addSceneGroup) {
|
||||||
|
for (auto &sg : switcher->sceneGroups) {
|
||||||
|
sel->addItem(QString::fromStdString(sg.name));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void AdvSceneSwitcher::populateTransitionSelection(QComboBox *sel,
|
void AdvSceneSwitcher::populateTransitionSelection(QComboBox *sel,
|
||||||
bool addSelect)
|
bool addCurrent,
|
||||||
|
bool addSelect,
|
||||||
|
bool selectable)
|
||||||
{
|
{
|
||||||
if (addSelect)
|
if (addSelect) {
|
||||||
addSelectionEntry(
|
addSelectionEntry(
|
||||||
sel,
|
sel,
|
||||||
obs_module_text("AdvSceneSwitcher.selectTransition"));
|
obs_module_text("AdvSceneSwitcher.selectTransition"),
|
||||||
|
selectable);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (addCurrent) {
|
||||||
|
sel->addItem(
|
||||||
|
obs_module_text("AdvSceneSwitcher.currentTransition"));
|
||||||
|
}
|
||||||
|
|
||||||
obs_frontend_source_list *transitions = new obs_frontend_source_list();
|
obs_frontend_source_list *transitions = new obs_frontend_source_list();
|
||||||
obs_frontend_get_transitions(transitions);
|
obs_frontend_get_transitions(transitions);
|
||||||
@@ -137,9 +185,10 @@ void AdvSceneSwitcher::populateTransitionSelection(QComboBox *sel,
|
|||||||
|
|
||||||
void AdvSceneSwitcher::populateWindowSelection(QComboBox *sel, bool addSelect)
|
void AdvSceneSwitcher::populateWindowSelection(QComboBox *sel, bool addSelect)
|
||||||
{
|
{
|
||||||
if (addSelect)
|
if (addSelect) {
|
||||||
addSelectionEntry(
|
addSelectionEntry(
|
||||||
sel, obs_module_text("AdvSceneSwitcher.selectWindow"));
|
sel, obs_module_text("AdvSceneSwitcher.selectWindow"));
|
||||||
|
}
|
||||||
|
|
||||||
std::vector<std::string> windows;
|
std::vector<std::string> windows;
|
||||||
GetWindowList(windows);
|
GetWindowList(windows);
|
||||||
@@ -157,12 +206,14 @@ void AdvSceneSwitcher::populateWindowSelection(QComboBox *sel, bool addSelect)
|
|||||||
|
|
||||||
void AdvSceneSwitcher::populateAudioSelection(QComboBox *sel, bool addSelect)
|
void AdvSceneSwitcher::populateAudioSelection(QComboBox *sel, bool addSelect)
|
||||||
{
|
{
|
||||||
if (addSelect)
|
if (addSelect) {
|
||||||
addSelectionEntry(
|
addSelectionEntry(
|
||||||
sel,
|
sel,
|
||||||
obs_module_text("AdvSceneSwitcher.selectAudioSource"),
|
obs_module_text("AdvSceneSwitcher.selectAudioSource"),
|
||||||
|
false,
|
||||||
obs_module_text(
|
obs_module_text(
|
||||||
"AdvSceneSwitcher.invaildEntriesWillNotBeSaved"));
|
"AdvSceneSwitcher.invaildEntriesWillNotBeSaved"));
|
||||||
|
}
|
||||||
|
|
||||||
auto sourceEnum = [](void *data, obs_source_t *source) -> bool /* -- */
|
auto sourceEnum = [](void *data, obs_source_t *source) -> bool /* -- */
|
||||||
{
|
{
|
||||||
@@ -184,14 +235,52 @@ void AdvSceneSwitcher::populateAudioSelection(QComboBox *sel, bool addSelect)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void AdvSceneSwitcher::populateVideoSelection(QComboBox *sel, bool addScenes,
|
||||||
|
bool addSelect)
|
||||||
|
{
|
||||||
|
if (addSelect) {
|
||||||
|
addSelectionEntry(
|
||||||
|
sel,
|
||||||
|
obs_module_text("AdvSceneSwitcher.selectVideoSource"),
|
||||||
|
false,
|
||||||
|
obs_module_text(
|
||||||
|
"AdvSceneSwitcher.invaildEntriesWillNotBeSaved"));
|
||||||
|
}
|
||||||
|
|
||||||
|
auto sourceEnum = [](void *data, obs_source_t *source) -> bool /* -- */
|
||||||
|
{
|
||||||
|
std::vector<std::string> *list =
|
||||||
|
reinterpret_cast<std::vector<std::string> *>(data);
|
||||||
|
uint32_t flags = obs_source_get_output_flags(source);
|
||||||
|
std::string test = obs_source_get_name(source);
|
||||||
|
if ((flags & (OBS_SOURCE_VIDEO | OBS_SOURCE_ASYNC)) != 0) {
|
||||||
|
list->push_back(obs_source_get_name(source));
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
std::vector<std::string> videoSources;
|
||||||
|
obs_enum_sources(sourceEnum, &videoSources);
|
||||||
|
sort(videoSources.begin(), videoSources.end());
|
||||||
|
for (std::string &source : videoSources) {
|
||||||
|
sel->addItem(source.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (addScenes) {
|
||||||
|
populateSceneSelection(sel, false, false, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void AdvSceneSwitcher::populateMediaSelection(QComboBox *sel, bool addSelect)
|
void AdvSceneSwitcher::populateMediaSelection(QComboBox *sel, bool addSelect)
|
||||||
{
|
{
|
||||||
if (addSelect)
|
if (addSelect) {
|
||||||
addSelectionEntry(
|
addSelectionEntry(
|
||||||
sel,
|
sel,
|
||||||
obs_module_text("AdvSceneSwitcher.selectMediaSource"),
|
obs_module_text("AdvSceneSwitcher.selectMediaSource"),
|
||||||
|
false,
|
||||||
obs_module_text(
|
obs_module_text(
|
||||||
"AdvSceneSwitcher.invaildEntriesWillNotBeSaved"));
|
"AdvSceneSwitcher.invaildEntriesWillNotBeSaved"));
|
||||||
|
}
|
||||||
|
|
||||||
auto sourceEnum = [](void *data, obs_source_t *source) -> bool /* -- */
|
auto sourceEnum = [](void *data, obs_source_t *source) -> bool /* -- */
|
||||||
{
|
{
|
||||||
@@ -215,9 +304,10 @@ void AdvSceneSwitcher::populateMediaSelection(QComboBox *sel, bool addSelect)
|
|||||||
|
|
||||||
void AdvSceneSwitcher::populateProcessSelection(QComboBox *sel, bool addSelect)
|
void AdvSceneSwitcher::populateProcessSelection(QComboBox *sel, bool addSelect)
|
||||||
{
|
{
|
||||||
if (addSelect)
|
if (addSelect) {
|
||||||
addSelectionEntry(
|
addSelectionEntry(
|
||||||
sel, obs_module_text("AdvSceneSwitcher.selectProcess"));
|
sel, obs_module_text("AdvSceneSwitcher.selectProcess"));
|
||||||
|
}
|
||||||
|
|
||||||
QStringList processes;
|
QStringList processes;
|
||||||
GetProcessList(processes);
|
GetProcessList(processes);
|
||||||
@@ -237,8 +327,9 @@ void AdvSceneSwitcher::listAddClicked(QListWidget *list,
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (addButton && addHighlight)
|
if (addButton && addHighlight) {
|
||||||
addButton->disconnect(*addHighlight);
|
addButton->disconnect(*addHighlight);
|
||||||
|
}
|
||||||
|
|
||||||
QListWidgetItem *item;
|
QListWidgetItem *item;
|
||||||
item = new QListWidgetItem(list);
|
item = new QListWidgetItem(list);
|
||||||
@@ -252,8 +343,9 @@ void AdvSceneSwitcher::listAddClicked(QListWidget *list,
|
|||||||
bool AdvSceneSwitcher::listMoveUp(QListWidget *list)
|
bool AdvSceneSwitcher::listMoveUp(QListWidget *list)
|
||||||
{
|
{
|
||||||
int index = list->currentRow();
|
int index = list->currentRow();
|
||||||
if (index == -1 || index == 0)
|
if (index == -1 || index == 0) {
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
QWidget *row = list->itemWidget(list->currentItem());
|
QWidget *row = list->itemWidget(list->currentItem());
|
||||||
QListWidgetItem *itemN = list->currentItem()->clone();
|
QListWidgetItem *itemN = list->currentItem()->clone();
|
||||||
@@ -269,8 +361,9 @@ bool AdvSceneSwitcher::listMoveUp(QListWidget *list)
|
|||||||
bool AdvSceneSwitcher::listMoveDown(QListWidget *list)
|
bool AdvSceneSwitcher::listMoveDown(QListWidget *list)
|
||||||
{
|
{
|
||||||
int index = list->currentRow();
|
int index = list->currentRow();
|
||||||
if (index == -1 || index == list->count() - 1)
|
if (index == -1 || index == list->count() - 1) {
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
QWidget *row = list->itemWidget(list->currentItem());
|
QWidget *row = list->itemWidget(list->currentItem());
|
||||||
QListWidgetItem *itemN = list->currentItem()->clone();
|
QListWidgetItem *itemN = list->currentItem()->clone();
|
||||||
@@ -288,8 +381,9 @@ QMetaObject::Connection AdvSceneSwitcher::PulseWidget(QWidget *widget,
|
|||||||
QColor startColor,
|
QColor startColor,
|
||||||
QString specifier)
|
QString specifier)
|
||||||
{
|
{
|
||||||
if (switcher->disableHints)
|
if (switcher->disableHints) {
|
||||||
return QMetaObject::Connection();
|
return QMetaObject::Connection();
|
||||||
|
}
|
||||||
|
|
||||||
widget->setStyleSheet(specifier + "{ \
|
widget->setStyleSheet(specifier + "{ \
|
||||||
border-style: outset; \
|
border-style: outset; \
|
||||||
@@ -305,7 +399,7 @@ QMetaObject::Connection AdvSceneSwitcher::PulseWidget(QWidget *widget,
|
|||||||
paAnimation->setStartValue(startColor);
|
paAnimation->setStartValue(startColor);
|
||||||
paAnimation->setEndValue(endColor);
|
paAnimation->setEndValue(endColor);
|
||||||
paAnimation->setDuration(1000);
|
paAnimation->setDuration(1000);
|
||||||
// play backward to return to original state on timer end
|
// Play backwards to return to original state on timer end
|
||||||
paAnimation->setDirection(QAbstractAnimation::Backward);
|
paAnimation->setDirection(QAbstractAnimation::Backward);
|
||||||
|
|
||||||
auto con = QWidget::connect(
|
auto con = QWidget::connect(
|
||||||
@@ -320,9 +414,9 @@ QMetaObject::Connection AdvSceneSwitcher::PulseWidget(QWidget *widget,
|
|||||||
return con;
|
return con;
|
||||||
}
|
}
|
||||||
|
|
||||||
/********************************************************************************
|
/******************************************************************************
|
||||||
* Saving and loading
|
* Saving and loading
|
||||||
********************************************************************************/
|
******************************************************************************/
|
||||||
static void SaveSceneSwitcher(obs_data_t *save_data, bool saving, void *)
|
static void SaveSceneSwitcher(obs_data_t *save_data, bool saving, void *)
|
||||||
{
|
{
|
||||||
if (saving) {
|
if (saving) {
|
||||||
@@ -332,20 +426,7 @@ static void SaveSceneSwitcher(obs_data_t *save_data, bool saving, void *)
|
|||||||
|
|
||||||
obs_data_t *obj = obs_data_create();
|
obs_data_t *obj = obs_data_create();
|
||||||
|
|
||||||
switcher->saveWindowTitleSwitches(obj);
|
switcher->saveSettings(obj);
|
||||||
switcher->saveScreenRegionSwitches(obj);
|
|
||||||
switcher->savePauseSwitches(obj);
|
|
||||||
switcher->saveSceneSequenceSwitches(obj);
|
|
||||||
switcher->saveSceneTransitions(obj);
|
|
||||||
switcher->saveIdleSwitches(obj);
|
|
||||||
switcher->saveExecutableSwitches(obj);
|
|
||||||
switcher->saveRandomSwitches(obj);
|
|
||||||
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);
|
obs_data_set_obj(save_data, "advanced-scene-switcher", obj);
|
||||||
|
|
||||||
@@ -356,44 +437,37 @@ static void SaveSceneSwitcher(obs_data_t *save_data, bool saving, void *)
|
|||||||
obs_data_t *obj =
|
obs_data_t *obj =
|
||||||
obs_data_get_obj(save_data, "advanced-scene-switcher");
|
obs_data_get_obj(save_data, "advanced-scene-switcher");
|
||||||
|
|
||||||
if (!obj)
|
if (!obj) {
|
||||||
obj = obs_data_create();
|
obj = obs_data_create();
|
||||||
|
}
|
||||||
|
if (switcher->versionChanged(obj, g_GIT_SHA1)) {
|
||||||
|
AdvSceneSwitcher::AskBackup(obj);
|
||||||
|
}
|
||||||
|
|
||||||
switcher->loadWindowTitleSwitches(obj);
|
switcher->loadSettings(obj);
|
||||||
switcher->loadScreenRegionSwitches(obj);
|
|
||||||
switcher->loadPauseSwitches(obj);
|
|
||||||
switcher->loadSceneSequenceSwitches(obj);
|
|
||||||
switcher->loadSceneTransitions(obj);
|
|
||||||
switcher->loadIdleSwitches(obj);
|
|
||||||
switcher->loadExecutableSwitches(obj);
|
|
||||||
switcher->loadRandomSwitches(obj);
|
|
||||||
switcher->loadFileSwitches(obj);
|
|
||||||
switcher->loadMediaSwitches(obj);
|
|
||||||
switcher->loadTimeSwitches(obj);
|
|
||||||
switcher->loadAudioSwitches(obj);
|
|
||||||
switcher->loadGeneralSettings(obj);
|
|
||||||
switcher->loadHotkeys(obj);
|
|
||||||
|
|
||||||
obs_data_release(obj);
|
obs_data_release(obj);
|
||||||
|
|
||||||
switcher->m.unlock();
|
switcher->m.unlock();
|
||||||
|
|
||||||
// stop the scene switcher at least once
|
// Stop the scene switcher at least once to
|
||||||
// to avoid issues with scene collection changes
|
// avoid scene duplication issues with scene collection changes
|
||||||
bool start = !switcher->stop;
|
bool start = !switcher->stop;
|
||||||
switcher->Stop();
|
switcher->Stop();
|
||||||
if (start)
|
if (start) {
|
||||||
switcher->Start();
|
switcher->Start();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/********************************************************************************
|
/******************************************************************************
|
||||||
* Main switcher thread
|
* Main switcher thread
|
||||||
********************************************************************************/
|
******************************************************************************/
|
||||||
void SwitcherData::Thread()
|
void SwitcherData::Thread()
|
||||||
{
|
{
|
||||||
blog(LOG_INFO, "started");
|
blog(LOG_INFO, "started");
|
||||||
int sleep = 0;
|
int sleep = 0;
|
||||||
|
int linger = 0;
|
||||||
std::chrono::milliseconds duration;
|
std::chrono::milliseconds duration;
|
||||||
auto startTime = std::chrono::high_resolution_clock::now();
|
auto startTime = std::chrono::high_resolution_clock::now();
|
||||||
auto endTime = std::chrono::high_resolution_clock::now();
|
auto endTime = std::chrono::high_resolution_clock::now();
|
||||||
@@ -405,9 +479,6 @@ void SwitcherData::Thread()
|
|||||||
OBSWeakSource scene;
|
OBSWeakSource scene;
|
||||||
OBSWeakSource transition;
|
OBSWeakSource transition;
|
||||||
|
|
||||||
bool defTransitionMatch = false;
|
|
||||||
OBSWeakSource defTransition;
|
|
||||||
|
|
||||||
endTime = std::chrono::high_resolution_clock::now();
|
endTime = std::chrono::high_resolution_clock::now();
|
||||||
|
|
||||||
auto runTime =
|
auto runTime =
|
||||||
@@ -417,8 +488,8 @@ void SwitcherData::Thread()
|
|||||||
if (sleep) {
|
if (sleep) {
|
||||||
duration = std::chrono::milliseconds(sleep);
|
duration = std::chrono::milliseconds(sleep);
|
||||||
} else {
|
} else {
|
||||||
duration =
|
duration = std::chrono::milliseconds(interval) +
|
||||||
std::chrono::milliseconds(interval) - runTime;
|
std::chrono::milliseconds(linger) - runTime;
|
||||||
if (duration.count() < 1) {
|
if (duration.count() < 1) {
|
||||||
blog(LOG_INFO,
|
blog(LOG_INFO,
|
||||||
"detected busy loop - refusing to sleep less than 1ms");
|
"detected busy loop - refusing to sleep less than 1ms");
|
||||||
@@ -426,82 +497,47 @@ void SwitcherData::Thread()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//sleep for a bit
|
vblog(LOG_INFO, "try to sleep for %ld", duration.count());
|
||||||
if (verbose)
|
|
||||||
blog(LOG_INFO, "sleep for %ld", duration.count());
|
|
||||||
cv.wait_for(lock, duration);
|
cv.wait_for(lock, duration);
|
||||||
|
|
||||||
startTime = std::chrono::high_resolution_clock::now();
|
startTime = std::chrono::high_resolution_clock::now();
|
||||||
sleep = 0;
|
sleep = 0;
|
||||||
|
linger = 0;
|
||||||
|
|
||||||
switcher->Prune();
|
switcher->Prune();
|
||||||
|
|
||||||
if (switcher->stop) {
|
if (switcher->stop) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (autoStopEnable) {
|
|
||||||
autoStopStreamAndRecording();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (autoStartEnable) {
|
|
||||||
autoStartStreamRecording();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (checkPause()) {
|
if (checkPause()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
match = checkForMatch(scene, transition, linger);
|
||||||
|
if (switcher->stop) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
checkNoMatchSwitch(match, scene, transition, sleep);
|
||||||
|
checkSwitchCooldown(match);
|
||||||
|
|
||||||
checkDefaultSceneTransitions(defTransitionMatch, defTransition);
|
if (linger) {
|
||||||
|
duration = std::chrono::milliseconds(linger);
|
||||||
|
vblog(LOG_INFO, "sleep for %ld before switching scene",
|
||||||
|
duration.count());
|
||||||
|
|
||||||
for (int switchFuncName : functionNamesByPriority) {
|
cv.wait_for(lock, duration);
|
||||||
switch (switchFuncName) {
|
|
||||||
case read_file_func:
|
|
||||||
checkSwitchInfoFromFile(match, scene,
|
|
||||||
transition);
|
|
||||||
checkFileContent(match, scene, transition);
|
|
||||||
break;
|
|
||||||
case idle_func:
|
|
||||||
checkIdleSwitch(match, scene, transition);
|
|
||||||
break;
|
|
||||||
case exe_func:
|
|
||||||
checkExeSwitch(match, scene, transition);
|
|
||||||
break;
|
|
||||||
case screen_region_func:
|
|
||||||
checkScreenRegionSwitch(match, scene,
|
|
||||||
transition);
|
|
||||||
break;
|
|
||||||
case window_title_func:
|
|
||||||
checkWindowTitleSwitch(match, scene,
|
|
||||||
transition);
|
|
||||||
break;
|
|
||||||
case round_trip_func:
|
|
||||||
checkSceneSequence(match, scene, transition,
|
|
||||||
lock);
|
|
||||||
break;
|
|
||||||
case media_func:
|
|
||||||
checkMediaSwitch(match, scene, transition);
|
|
||||||
break;
|
|
||||||
case time_func:
|
|
||||||
checkTimeSwitch(match, scene, transition);
|
|
||||||
break;
|
|
||||||
case audio_func:
|
|
||||||
checkAudioSwitch(match, scene, transition);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (switcher->stop) {
|
if (switcher->stop) {
|
||||||
goto endLoop;
|
|
||||||
}
|
|
||||||
if (match) {
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (sceneChangedDuringWait()) {
|
||||||
|
vblog(LOG_INFO,
|
||||||
|
"scene was changed manually - ignoring match");
|
||||||
|
|
||||||
|
match = false;
|
||||||
|
linger = 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
checkNoMatchSwitch(match, scene, transition, sleep);
|
|
||||||
|
|
||||||
checkSwitchCooldown(match);
|
|
||||||
|
|
||||||
// After this point we will call frontend functions
|
// After this point we will call frontend functions
|
||||||
// obs_frontend_set_current_scene() and
|
// obs_frontend_set_current_scene() and
|
||||||
// obs_frontend_set_current_transition()
|
// obs_frontend_set_current_transition()
|
||||||
@@ -510,24 +546,85 @@ void SwitcherData::Thread()
|
|||||||
// leading to a deadlock, so we have to unlock()
|
// leading to a deadlock, so we have to unlock()
|
||||||
lock.unlock();
|
lock.unlock();
|
||||||
|
|
||||||
if (!match && defTransitionMatch) {
|
|
||||||
setCurrentDefTransition(defTransition);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (match) {
|
if (match) {
|
||||||
|
if (networkConfig.ServerEnabled) {
|
||||||
|
server.sendMessage(scene, transition);
|
||||||
|
}
|
||||||
switchScene(scene, transition,
|
switchScene(scene, transition,
|
||||||
tansitionOverrideOverride);
|
tansitionOverrideOverride);
|
||||||
}
|
}
|
||||||
|
|
||||||
writeSceneInfoToFile();
|
writeSceneInfoToFile();
|
||||||
}
|
}
|
||||||
endLoop:
|
|
||||||
blog(LOG_INFO, "stopped");
|
blog(LOG_INFO, "stopped");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool SwitcherData::checkForMatch(OBSWeakSource &scene,
|
||||||
|
OBSWeakSource &transition, int &linger)
|
||||||
|
{
|
||||||
|
bool match = false;
|
||||||
|
|
||||||
|
if (uninterruptibleSceneSequenceActive) {
|
||||||
|
checkSceneSequence(match, scene, transition, linger);
|
||||||
|
if (match) {
|
||||||
|
return match;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int switchFuncName : functionNamesByPriority) {
|
||||||
|
switch (switchFuncName) {
|
||||||
|
case read_file_func:
|
||||||
|
checkSwitchInfoFromFile(match, scene, transition);
|
||||||
|
checkFileContent(match, scene, transition);
|
||||||
|
break;
|
||||||
|
case idle_func:
|
||||||
|
checkIdleSwitch(match, scene, transition);
|
||||||
|
break;
|
||||||
|
case exe_func:
|
||||||
|
checkExeSwitch(match, scene, transition);
|
||||||
|
break;
|
||||||
|
case screen_region_func:
|
||||||
|
checkScreenRegionSwitch(match, scene, transition);
|
||||||
|
break;
|
||||||
|
case window_title_func:
|
||||||
|
checkWindowTitleSwitch(match, scene, transition);
|
||||||
|
break;
|
||||||
|
case round_trip_func:
|
||||||
|
checkSceneSequence(match, scene, transition, linger);
|
||||||
|
break;
|
||||||
|
case media_func:
|
||||||
|
checkMediaSwitch(match, scene, transition);
|
||||||
|
break;
|
||||||
|
case time_func:
|
||||||
|
checkTimeSwitch(match, scene, transition);
|
||||||
|
break;
|
||||||
|
case audio_func:
|
||||||
|
checkAudioSwitch(match, scene, transition);
|
||||||
|
break;
|
||||||
|
case video_func:
|
||||||
|
checkVideoSwitch(match, scene, transition);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (switcher->stop) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (match) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return match;
|
||||||
|
}
|
||||||
|
|
||||||
void switchScene(OBSWeakSource &scene, OBSWeakSource &transition,
|
void switchScene(OBSWeakSource &scene, OBSWeakSource &transition,
|
||||||
bool &transitionOverrideOverride)
|
bool &transitionOverrideOverride)
|
||||||
{
|
{
|
||||||
|
if (!scene && switcher->verbose) {
|
||||||
|
blog(LOG_INFO, "nothing to switch to");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
obs_source_t *source = obs_weak_source_get_source(scene);
|
obs_source_t *source = obs_weak_source_get_source(scene);
|
||||||
obs_source_t *currentSource = obs_frontend_get_current_scene();
|
obs_source_t *currentSource = obs_frontend_get_current_scene();
|
||||||
|
|
||||||
@@ -536,11 +633,13 @@ void switchScene(OBSWeakSource &scene, OBSWeakSource &transition,
|
|||||||
setNextTransition(scene, currentSource, transition,
|
setNextTransition(scene, currentSource, transition,
|
||||||
transitionOverrideOverride, td);
|
transitionOverrideOverride, td);
|
||||||
obs_frontend_set_current_scene(source);
|
obs_frontend_set_current_scene(source);
|
||||||
if (transitionOverrideOverride)
|
if (transitionOverrideOverride) {
|
||||||
restoreTransitionOverride(source, td);
|
restoreTransitionOverride(source, td);
|
||||||
|
}
|
||||||
|
|
||||||
if (switcher->verbose)
|
if (switcher->verbose) {
|
||||||
blog(LOG_INFO, "switched scene");
|
blog(LOG_INFO, "switched scene");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
obs_source_release(currentSource);
|
obs_source_release(currentSource);
|
||||||
obs_source_release(source);
|
obs_source_release(source);
|
||||||
@@ -557,6 +656,15 @@ void SwitcherData::Start()
|
|||||||
// Will be overwritten quickly but might be useful
|
// Will be overwritten quickly but might be useful
|
||||||
writeToStatusFile("Advanced Scene Switcher running");
|
writeToStatusFile("Advanced Scene Switcher running");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (networkConfig.ServerEnabled) {
|
||||||
|
server.start(networkConfig.ServerPort,
|
||||||
|
networkConfig.LockToIPv4);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (networkConfig.ClientEnabled) {
|
||||||
|
client.connect(networkConfig.GetClientUri());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void SwitcherData::Stop()
|
void SwitcherData::Stop()
|
||||||
@@ -571,25 +679,30 @@ void SwitcherData::Stop()
|
|||||||
|
|
||||||
writeToStatusFile("Advanced Scene Switcher stopped");
|
writeToStatusFile("Advanced Scene Switcher stopped");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
server.stop();
|
||||||
|
client.disconnect();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SwitcherData::sceneChangedDuringWait()
|
bool SwitcherData::sceneChangedDuringWait()
|
||||||
{
|
{
|
||||||
obs_source_t *currentSource = obs_frontend_get_current_scene();
|
obs_source_t *currentSource = obs_frontend_get_current_scene();
|
||||||
if (!currentSource)
|
if (!currentSource) {
|
||||||
return true;
|
return true;
|
||||||
|
}
|
||||||
obs_source_release(currentSource);
|
obs_source_release(currentSource);
|
||||||
return (waitScene && currentSource != waitScene);
|
return (waitScene && currentSource != waitScene);
|
||||||
}
|
}
|
||||||
|
|
||||||
/********************************************************************************
|
/******************************************************************************
|
||||||
* OBS module setup
|
* OBS module setup
|
||||||
********************************************************************************/
|
******************************************************************************/
|
||||||
extern "C" void FreeSceneSwitcher()
|
extern "C" void FreeSceneSwitcher()
|
||||||
{
|
{
|
||||||
if (loaded_curl_lib) {
|
if (loaded_curl_lib) {
|
||||||
if (switcher->curl && f_curl_cleanup)
|
if (switcher->curl && f_curl_cleanup) {
|
||||||
f_curl_cleanup(switcher->curl);
|
f_curl_cleanup(switcher->curl);
|
||||||
|
}
|
||||||
delete loaded_curl_lib;
|
delete loaded_curl_lib;
|
||||||
loaded_curl_lib = nullptr;
|
loaded_curl_lib = nullptr;
|
||||||
}
|
}
|
||||||
@@ -600,27 +713,28 @@ extern "C" void FreeSceneSwitcher()
|
|||||||
|
|
||||||
void handleSceneChange(SwitcherData *s)
|
void handleSceneChange(SwitcherData *s)
|
||||||
{
|
{
|
||||||
//stop waiting if scene was manually changed
|
// Stop waiting if scene was changed
|
||||||
if (s->sceneChangedDuringWait())
|
if (s->sceneChangedDuringWait()) {
|
||||||
s->cv.notify_one();
|
s->cv.notify_one();
|
||||||
|
}
|
||||||
|
|
||||||
//set previous scene
|
// Set previous scene
|
||||||
obs_source_t *source = obs_frontend_get_current_scene();
|
obs_source_t *source = obs_frontend_get_current_scene();
|
||||||
obs_weak_source_t *ws = obs_source_get_weak_source(source);
|
obs_weak_source_t *ws = obs_source_get_weak_source(source);
|
||||||
obs_source_release(source);
|
obs_source_release(source);
|
||||||
obs_weak_source_release(ws);
|
obs_weak_source_release(ws);
|
||||||
if (source && s->previousScene2 != ws) {
|
if (source && s->previousSceneHelper != ws) {
|
||||||
s->previousScene = s->previousScene2;
|
s->previousScene = s->previousSceneHelper;
|
||||||
s->previousScene2 = ws;
|
s->previousSceneHelper = ws;
|
||||||
}
|
}
|
||||||
|
|
||||||
//reset events only hanled on scene change
|
s->checkTriggers();
|
||||||
s->autoStartedRecently = false;
|
s->checkDefaultSceneTransitions();
|
||||||
}
|
|
||||||
|
|
||||||
void handleTransitionStop(SwitcherData *s)
|
if (switcher->networkConfig.ServerEnabled &&
|
||||||
{
|
switcher->networkConfig.SendAll) {
|
||||||
s->checkedDefTransition = false;
|
switcher->server.sendMessage(ws, nullptr);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void setLiveTime(SwitcherData *s)
|
void setLiveTime(SwitcherData *s)
|
||||||
@@ -633,6 +747,20 @@ void resetLiveTime(SwitcherData *s)
|
|||||||
s->liveTime = QDateTime();
|
s->liveTime = QDateTime();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void checkAutoStartRecording(SwitcherData *s)
|
||||||
|
{
|
||||||
|
if (s->autoStartEvent == AutoStartEvent::RECORDING ||
|
||||||
|
s->autoStartEvent == AutoStartEvent::RECORINDG_OR_STREAMING)
|
||||||
|
s->Start();
|
||||||
|
}
|
||||||
|
|
||||||
|
void checkAutoStartStreaming(SwitcherData *s)
|
||||||
|
{
|
||||||
|
if (s->autoStartEvent == AutoStartEvent::STREAMING ||
|
||||||
|
s->autoStartEvent == AutoStartEvent::RECORINDG_OR_STREAMING)
|
||||||
|
s->Start();
|
||||||
|
}
|
||||||
|
|
||||||
// Note to future self:
|
// Note to future self:
|
||||||
// be careful using switcher->m here as there is potential for deadlocks when using
|
// be careful using switcher->m here as there is potential for deadlocks when using
|
||||||
// frontend functions such as obs_frontend_set_current_scene()
|
// frontend functions such as obs_frontend_set_current_scene()
|
||||||
@@ -645,12 +773,13 @@ static void OBSEvent(enum obs_frontend_event event, void *switcher)
|
|||||||
case OBS_FRONTEND_EVENT_SCENE_CHANGED:
|
case OBS_FRONTEND_EVENT_SCENE_CHANGED:
|
||||||
handleSceneChange((SwitcherData *)switcher);
|
handleSceneChange((SwitcherData *)switcher);
|
||||||
break;
|
break;
|
||||||
case OBS_FRONTEND_EVENT_TRANSITION_STOPPED:
|
|
||||||
handleTransitionStop((SwitcherData *)switcher);
|
|
||||||
break;
|
|
||||||
case OBS_FRONTEND_EVENT_RECORDING_STARTED:
|
case OBS_FRONTEND_EVENT_RECORDING_STARTED:
|
||||||
|
setLiveTime((SwitcherData *)switcher);
|
||||||
|
checkAutoStartRecording((SwitcherData *)switcher);
|
||||||
|
break;
|
||||||
case OBS_FRONTEND_EVENT_STREAMING_STARTED:
|
case OBS_FRONTEND_EVENT_STREAMING_STARTED:
|
||||||
setLiveTime((SwitcherData *)switcher);
|
setLiveTime((SwitcherData *)switcher);
|
||||||
|
checkAutoStartStreaming((SwitcherData *)switcher);
|
||||||
break;
|
break;
|
||||||
case OBS_FRONTEND_EVENT_RECORDING_STOPPED:
|
case OBS_FRONTEND_EVENT_RECORDING_STOPPED:
|
||||||
case OBS_FRONTEND_EVENT_STREAMING_STOPPED:
|
case OBS_FRONTEND_EVENT_STREAMING_STOPPED:
|
||||||
|
|||||||
@@ -54,9 +54,9 @@ bool loadCurl()
|
|||||||
libFilePath.toUtf8().constData());
|
libFilePath.toUtf8().constData());
|
||||||
|
|
||||||
loaded_curl_lib = new QLibrary(libFilePath, nullptr);
|
loaded_curl_lib = new QLibrary(libFilePath, nullptr);
|
||||||
if (resolveCurl())
|
if (resolveCurl()) {
|
||||||
return true;
|
return true;
|
||||||
else {
|
} else {
|
||||||
delete loaded_curl_lib;
|
delete loaded_curl_lib;
|
||||||
loaded_curl_lib = nullptr;
|
loaded_curl_lib = nullptr;
|
||||||
}
|
}
|
||||||
|
|||||||
527
src/general.cpp
527
src/general.cpp
@@ -2,6 +2,9 @@
|
|||||||
|
|
||||||
#include "headers/advanced-scene-switcher.hpp"
|
#include "headers/advanced-scene-switcher.hpp"
|
||||||
#include "headers/utility.hpp"
|
#include "headers/utility.hpp"
|
||||||
|
#include "headers/version.h"
|
||||||
|
|
||||||
|
constexpr auto tab_count = 17;
|
||||||
|
|
||||||
QMetaObject::Connection inactivePluse;
|
QMetaObject::Connection inactivePluse;
|
||||||
|
|
||||||
@@ -23,8 +26,9 @@ void AdvSceneSwitcher::UpdateNonMatchingScene(const QString &name)
|
|||||||
|
|
||||||
void AdvSceneSwitcher::on_noMatchDontSwitch_clicked()
|
void AdvSceneSwitcher::on_noMatchDontSwitch_clicked()
|
||||||
{
|
{
|
||||||
if (loading)
|
if (loading) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
std::lock_guard<std::mutex> lock(switcher->m);
|
std::lock_guard<std::mutex> lock(switcher->m);
|
||||||
switcher->switchIfNotMatching = NO_SWITCH;
|
switcher->switchIfNotMatching = NO_SWITCH;
|
||||||
@@ -34,8 +38,9 @@ void AdvSceneSwitcher::on_noMatchDontSwitch_clicked()
|
|||||||
|
|
||||||
void AdvSceneSwitcher::on_noMatchSwitch_clicked()
|
void AdvSceneSwitcher::on_noMatchSwitch_clicked()
|
||||||
{
|
{
|
||||||
if (loading)
|
if (loading) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
std::lock_guard<std::mutex> lock(switcher->m);
|
std::lock_guard<std::mutex> lock(switcher->m);
|
||||||
switcher->switchIfNotMatching = SWITCH;
|
switcher->switchIfNotMatching = SWITCH;
|
||||||
@@ -46,8 +51,9 @@ void AdvSceneSwitcher::on_noMatchSwitch_clicked()
|
|||||||
|
|
||||||
void AdvSceneSwitcher::on_noMatchRandomSwitch_clicked()
|
void AdvSceneSwitcher::on_noMatchRandomSwitch_clicked()
|
||||||
{
|
{
|
||||||
if (loading)
|
if (loading) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
std::lock_guard<std::mutex> lock(switcher->m);
|
std::lock_guard<std::mutex> lock(switcher->m);
|
||||||
switcher->switchIfNotMatching = RANDOM_SWITCH;
|
switcher->switchIfNotMatching = RANDOM_SWITCH;
|
||||||
@@ -57,26 +63,40 @@ void AdvSceneSwitcher::on_noMatchRandomSwitch_clicked()
|
|||||||
|
|
||||||
void AdvSceneSwitcher::on_noMatchDelay_valueChanged(double i)
|
void AdvSceneSwitcher::on_noMatchDelay_valueChanged(double i)
|
||||||
{
|
{
|
||||||
if (loading)
|
if (loading) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
std::lock_guard<std::mutex> lock(switcher->m);
|
std::lock_guard<std::mutex> lock(switcher->m);
|
||||||
switcher->noMatchDelay = i;
|
switcher->noMatchDelay = i;
|
||||||
}
|
}
|
||||||
|
|
||||||
void AdvSceneSwitcher::on_startupBehavior_currentIndexChanged(int index)
|
void AdvSceneSwitcher::on_startupBehavior_currentIndexChanged(int index)
|
||||||
{
|
{
|
||||||
if (loading)
|
if (loading) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
std::lock_guard<std::mutex> lock(switcher->m);
|
std::lock_guard<std::mutex> lock(switcher->m);
|
||||||
switcher->startupBehavior = (StartupBehavior)index;
|
switcher->startupBehavior = (StartupBehavior)index;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void AdvSceneSwitcher::on_autoStartEvent_currentIndexChanged(int index)
|
||||||
|
{
|
||||||
|
if (loading) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::lock_guard<std::mutex> lock(switcher->m);
|
||||||
|
switcher->autoStartEvent = static_cast<AutoStartEvent>(index);
|
||||||
|
}
|
||||||
|
|
||||||
void AdvSceneSwitcher::on_noMatchSwitchScene_currentTextChanged(
|
void AdvSceneSwitcher::on_noMatchSwitchScene_currentTextChanged(
|
||||||
const QString &text)
|
const QString &text)
|
||||||
{
|
{
|
||||||
if (loading)
|
if (loading) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
std::lock_guard<std::mutex> lock(switcher->m);
|
std::lock_guard<std::mutex> lock(switcher->m);
|
||||||
UpdateNonMatchingScene(text);
|
UpdateNonMatchingScene(text);
|
||||||
@@ -84,16 +104,19 @@ void AdvSceneSwitcher::on_noMatchSwitchScene_currentTextChanged(
|
|||||||
|
|
||||||
void AdvSceneSwitcher::on_cooldownTime_valueChanged(double i)
|
void AdvSceneSwitcher::on_cooldownTime_valueChanged(double i)
|
||||||
{
|
{
|
||||||
if (loading)
|
if (loading) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
std::lock_guard<std::mutex> lock(switcher->m);
|
std::lock_guard<std::mutex> lock(switcher->m);
|
||||||
switcher->cooldown = i;
|
switcher->cooldown = i;
|
||||||
}
|
}
|
||||||
|
|
||||||
void AdvSceneSwitcher::on_checkInterval_valueChanged(int value)
|
void AdvSceneSwitcher::on_checkInterval_valueChanged(int value)
|
||||||
{
|
{
|
||||||
if (loading)
|
if (loading) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
std::lock_guard<std::mutex> lock(switcher->m);
|
std::lock_guard<std::mutex> lock(switcher->m);
|
||||||
switcher->interval = value;
|
switcher->interval = value;
|
||||||
@@ -106,6 +129,7 @@ void AdvSceneSwitcher::SetStarted()
|
|||||||
ui->pluginRunningText->setText(
|
ui->pluginRunningText->setText(
|
||||||
obs_module_text("AdvSceneSwitcher.status.active"));
|
obs_module_text("AdvSceneSwitcher.status.active"));
|
||||||
ui->pluginRunningText->disconnect(inactivePluse);
|
ui->pluginRunningText->disconnect(inactivePluse);
|
||||||
|
currentStatusActive = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void AdvSceneSwitcher::SetStopped()
|
void AdvSceneSwitcher::SetStopped()
|
||||||
@@ -116,6 +140,7 @@ void AdvSceneSwitcher::SetStopped()
|
|||||||
obs_module_text("AdvSceneSwitcher.status.inactive"));
|
obs_module_text("AdvSceneSwitcher.status.inactive"));
|
||||||
inactivePluse = PulseWidget(ui->pluginRunningText, QColor(Qt::red),
|
inactivePluse = PulseWidget(ui->pluginRunningText, QColor(Qt::red),
|
||||||
QColor(0, 0, 0, 0), "QLabel ");
|
QColor(0, 0, 0, 0), "QLabel ");
|
||||||
|
currentStatusActive = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void AdvSceneSwitcher::on_toggleStartButton_clicked()
|
void AdvSceneSwitcher::on_toggleStartButton_clicked()
|
||||||
@@ -134,192 +159,73 @@ void AdvSceneSwitcher::closeEvent(QCloseEvent *)
|
|||||||
obs_frontend_save();
|
obs_frontend_save();
|
||||||
}
|
}
|
||||||
|
|
||||||
void AdvSceneSwitcher::on_autoStopScenes_currentTextChanged(const QString &text)
|
|
||||||
{
|
|
||||||
if (loading)
|
|
||||||
return;
|
|
||||||
|
|
||||||
std::lock_guard<std::mutex> lock(switcher->m);
|
|
||||||
UpdateAutoStopScene(text);
|
|
||||||
}
|
|
||||||
|
|
||||||
void AdvSceneSwitcher::on_autoStopSceneCheckBox_stateChanged(int state)
|
|
||||||
{
|
|
||||||
if (loading)
|
|
||||||
return;
|
|
||||||
|
|
||||||
std::lock_guard<std::mutex> lock(switcher->m);
|
|
||||||
if (!state) {
|
|
||||||
ui->autoStopScenes->setDisabled(true);
|
|
||||||
switcher->autoStopEnable = false;
|
|
||||||
} else {
|
|
||||||
ui->autoStopScenes->setDisabled(false);
|
|
||||||
switcher->autoStopEnable = true;
|
|
||||||
if (!switcher->autoStopScene)
|
|
||||||
UpdateAutoStopScene(ui->autoStopScenes->currentText());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
|
|
||||||
switcher->autoStopScene = ws;
|
|
||||||
|
|
||||||
obs_weak_source_release(ws);
|
|
||||||
obs_source_release(scene);
|
|
||||||
}
|
|
||||||
|
|
||||||
void SwitcherData::autoStopStreamAndRecording()
|
|
||||||
{
|
|
||||||
obs_source_t *currentSource = obs_frontend_get_current_scene();
|
|
||||||
obs_weak_source_t *ws = obs_source_get_weak_source(currentSource);
|
|
||||||
|
|
||||||
if (ws && autoStopScene == ws) {
|
|
||||||
if (obs_frontend_streaming_active()) {
|
|
||||||
blog(LOG_INFO,
|
|
||||||
"Stopping stream because scene '%s' is active",
|
|
||||||
obs_source_get_name(currentSource));
|
|
||||||
obs_frontend_streaming_stop();
|
|
||||||
}
|
|
||||||
if (obs_frontend_recording_active()) {
|
|
||||||
blog(LOG_INFO,
|
|
||||||
"Stopping record because scene '%s' is active",
|
|
||||||
obs_source_get_name(currentSource));
|
|
||||||
obs_frontend_recording_stop();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
obs_source_release(currentSource);
|
|
||||||
obs_weak_source_release(ws);
|
|
||||||
}
|
|
||||||
|
|
||||||
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()) {
|
|
||||||
blog(LOG_INFO,
|
|
||||||
"Starting stream because scene '%s' is active",
|
|
||||||
obs_source_get_name(currentSource));
|
|
||||||
obs_frontend_streaming_start();
|
|
||||||
}
|
|
||||||
if ((switcher->autoStartType == RECORDING ||
|
|
||||||
switcher->autoStartType == RECORINDGSTREAMING) &&
|
|
||||||
!obs_frontend_recording_active()) {
|
|
||||||
blog(LOG_INFO,
|
|
||||||
"Starting record because scene '%s' is active",
|
|
||||||
obs_source_get_name(currentSource));
|
|
||||||
obs_frontend_recording_start();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
obs_source_release(currentSource);
|
|
||||||
obs_weak_source_release(ws);
|
|
||||||
|
|
||||||
autoStartedRecently = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void AdvSceneSwitcher::on_verboseLogging_stateChanged(int state)
|
void AdvSceneSwitcher::on_verboseLogging_stateChanged(int state)
|
||||||
{
|
{
|
||||||
if (loading)
|
if (loading) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
switcher->verbose = state;
|
switcher->verbose = state;
|
||||||
}
|
}
|
||||||
|
|
||||||
void AdvSceneSwitcher::on_uiHintsDisable_stateChanged(int state)
|
void AdvSceneSwitcher::on_uiHintsDisable_stateChanged(int state)
|
||||||
{
|
{
|
||||||
if (loading)
|
if (loading) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
switcher->disableHints = state;
|
switcher->disableHints = state;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void AdvSceneSwitcher::AskBackup(obs_data_t *obj)
|
||||||
|
{
|
||||||
|
bool backupSettings = DisplayMessage(
|
||||||
|
obs_module_text("AdvSceneSwitcher.askBackup"), true);
|
||||||
|
|
||||||
|
if (!backupSettings) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString directory = QFileDialog::getSaveFileName(
|
||||||
|
nullptr,
|
||||||
|
obs_module_text(
|
||||||
|
"AdvSceneSwitcher.generalTab.saveOrLoadsettings.importWindowTitle"),
|
||||||
|
QDir::currentPath(),
|
||||||
|
obs_module_text(
|
||||||
|
"AdvSceneSwitcher.generalTab.saveOrLoadsettings.textType"));
|
||||||
|
if (directory.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QFile file(directory);
|
||||||
|
if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
obs_data_save_json(obj, file.fileName().toUtf8().constData());
|
||||||
|
}
|
||||||
|
|
||||||
void AdvSceneSwitcher::on_exportSettings_clicked()
|
void AdvSceneSwitcher::on_exportSettings_clicked()
|
||||||
{
|
{
|
||||||
QString directory = QFileDialog::getSaveFileName(
|
QString directory = QFileDialog::getSaveFileName(
|
||||||
this,
|
this,
|
||||||
tr(obs_module_text(
|
tr(obs_module_text(
|
||||||
"AdvSceneSwitcher.generalTab.saveOrLoadsettings.importWindowTitle")),
|
"AdvSceneSwitcher.generalTab.saveOrLoadsettings.exportWindowTitle")),
|
||||||
QDir::currentPath(),
|
QDir::currentPath(),
|
||||||
tr(obs_module_text(
|
tr(obs_module_text(
|
||||||
"AdvSceneSwitcher.generalTab.saveOrLoadsettings.textType")));
|
"AdvSceneSwitcher.generalTab.saveOrLoadsettings.textType")));
|
||||||
if (directory.isEmpty())
|
if (directory.isEmpty()) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
QFile file(directory);
|
QFile file(directory);
|
||||||
if (!file.open(QIODevice::WriteOnly | QIODevice::Text))
|
if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
obs_data_t *obj = obs_data_create();
|
obs_data_t *obj = obs_data_create();
|
||||||
|
|
||||||
switcher->saveWindowTitleSwitches(obj);
|
switcher->saveSettings(obj);
|
||||||
switcher->saveScreenRegionSwitches(obj);
|
|
||||||
switcher->savePauseSwitches(obj);
|
|
||||||
switcher->saveSceneSequenceSwitches(obj);
|
|
||||||
switcher->saveSceneTransitions(obj);
|
|
||||||
switcher->saveIdleSwitches(obj);
|
|
||||||
switcher->saveExecutableSwitches(obj);
|
|
||||||
switcher->saveRandomSwitches(obj);
|
|
||||||
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_save_json(obj, file.fileName().toUtf8().constData());
|
||||||
|
|
||||||
@@ -328,6 +234,11 @@ void AdvSceneSwitcher::on_exportSettings_clicked()
|
|||||||
|
|
||||||
void AdvSceneSwitcher::on_importSettings_clicked()
|
void AdvSceneSwitcher::on_importSettings_clicked()
|
||||||
{
|
{
|
||||||
|
// Scene switcher could be stuck in a sequence
|
||||||
|
// so it needs to be stopped before importing new settings
|
||||||
|
bool start = !switcher->stop;
|
||||||
|
switcher->Stop();
|
||||||
|
|
||||||
std::lock_guard<std::mutex> lock(switcher->m);
|
std::lock_guard<std::mutex> lock(switcher->m);
|
||||||
|
|
||||||
QString directory = QFileDialog::getOpenFileName(
|
QString directory = QFileDialog::getOpenFileName(
|
||||||
@@ -337,42 +248,36 @@ void AdvSceneSwitcher::on_importSettings_clicked()
|
|||||||
QDir::currentPath(),
|
QDir::currentPath(),
|
||||||
tr(obs_module_text(
|
tr(obs_module_text(
|
||||||
"AdvSceneSwitcher.generalTab.saveOrLoadsettings.textType")));
|
"AdvSceneSwitcher.generalTab.saveOrLoadsettings.textType")));
|
||||||
if (directory.isEmpty())
|
if (directory.isEmpty()) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
QFile file(directory);
|
QFile file(directory);
|
||||||
if (!file.open(QIODevice::ReadOnly | QIODevice::Text))
|
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
obs_data_t *obj = obs_data_create_from_json_file(
|
obs_data_t *obj = obs_data_create_from_json_file(
|
||||||
file.fileName().toUtf8().constData());
|
file.fileName().toUtf8().constData());
|
||||||
|
|
||||||
if (!obj) {
|
if (!obj) {
|
||||||
DisplayMessage(obs_module_text(
|
(void)DisplayMessage(obs_module_text(
|
||||||
"AdvSceneSwitcher.generalTab.saveOrLoadsettings.loadFail"));
|
"AdvSceneSwitcher.generalTab.saveOrLoadsettings.loadFail"));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
switcher->loadWindowTitleSwitches(obj);
|
switcher->loadSettings(obj);
|
||||||
switcher->loadScreenRegionSwitches(obj);
|
|
||||||
switcher->loadPauseSwitches(obj);
|
|
||||||
switcher->loadSceneSequenceSwitches(obj);
|
|
||||||
switcher->loadSceneTransitions(obj);
|
|
||||||
switcher->loadIdleSwitches(obj);
|
|
||||||
switcher->loadExecutableSwitches(obj);
|
|
||||||
switcher->loadRandomSwitches(obj);
|
|
||||||
switcher->loadFileSwitches(obj);
|
|
||||||
switcher->loadMediaSwitches(obj);
|
|
||||||
switcher->loadTimeSwitches(obj);
|
|
||||||
switcher->loadAudioSwitches(obj);
|
|
||||||
switcher->loadGeneralSettings(obj);
|
|
||||||
switcher->loadHotkeys(obj);
|
|
||||||
|
|
||||||
obs_data_release(obj);
|
obs_data_release(obj);
|
||||||
|
|
||||||
DisplayMessage(obs_module_text(
|
(void)DisplayMessage(obs_module_text(
|
||||||
"AdvSceneSwitcher.generalTab.saveOrLoadsettings.loadSuccess"));
|
"AdvSceneSwitcher.generalTab.saveOrLoadsettings.loadSuccess"));
|
||||||
close();
|
close();
|
||||||
|
|
||||||
|
// Restart scene switcher if it was active
|
||||||
|
if (start) {
|
||||||
|
switcher->Start();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int findTabIndex(QTabWidget *tabWidget, int pos)
|
int findTabIndex(QTabWidget *tabWidget, int pos)
|
||||||
@@ -420,15 +325,28 @@ int findTabIndex(QTabWidget *tabWidget, int pos)
|
|||||||
case 12:
|
case 12:
|
||||||
tabName = "audioTab";
|
tabName = "audioTab";
|
||||||
break;
|
break;
|
||||||
|
case 13:
|
||||||
|
tabName = "videoTab";
|
||||||
|
break;
|
||||||
|
case 14:
|
||||||
|
tabName = "networkTab";
|
||||||
|
break;
|
||||||
|
case 15:
|
||||||
|
tabName = "sceneGroupTab";
|
||||||
|
break;
|
||||||
|
case 16:
|
||||||
|
tabName = "sceneTriggerTab";
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
QWidget *page = tabWidget->findChild<QWidget *>(tabName);
|
QWidget *page = tabWidget->findChild<QWidget *>(tabName);
|
||||||
if (page) {
|
if (page) {
|
||||||
at = tabWidget->indexOf(page);
|
at = tabWidget->indexOf(page);
|
||||||
}
|
}
|
||||||
if (at == -1)
|
if (at == -1) {
|
||||||
blog(LOG_INFO, "failed to find tab %s",
|
blog(LOG_INFO, "failed to find tab %s",
|
||||||
tabName.toUtf8().constData());
|
tabName.toUtf8().constData());
|
||||||
|
}
|
||||||
|
|
||||||
return at;
|
return at;
|
||||||
}
|
}
|
||||||
@@ -439,8 +357,9 @@ void AdvSceneSwitcher::setTabOrder()
|
|||||||
for (int i = 0; i < bar->count(); ++i) {
|
for (int i = 0; i < bar->count(); ++i) {
|
||||||
int curPos = findTabIndex(ui->tabWidget, switcher->tabOrder[i]);
|
int curPos = findTabIndex(ui->tabWidget, switcher->tabOrder[i]);
|
||||||
|
|
||||||
if (i != curPos && curPos != -1)
|
if (i != curPos && curPos != -1) {
|
||||||
bar->moveTab(curPos, i);
|
bar->moveTab(curPos, i);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
connect(bar, &QTabBar::tabMoved, this, &AdvSceneSwitcher::on_tabMoved);
|
connect(bar, &QTabBar::tabMoved, this, &AdvSceneSwitcher::on_tabMoved);
|
||||||
@@ -448,8 +367,10 @@ void AdvSceneSwitcher::setTabOrder()
|
|||||||
|
|
||||||
void AdvSceneSwitcher::on_tabMoved(int from, int to)
|
void AdvSceneSwitcher::on_tabMoved(int from, int to)
|
||||||
{
|
{
|
||||||
if (loading)
|
if (loading) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
std::swap(switcher->tabOrder[from], switcher->tabOrder[to]);
|
std::swap(switcher->tabOrder[from], switcher->tabOrder[to]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -462,6 +383,59 @@ void AdvSceneSwitcher::on_tabWidget_currentChanged(int index)
|
|||||||
SetShowFrames();
|
SetShowFrames();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SwitcherData::loadSettings(obs_data_t *obj)
|
||||||
|
{
|
||||||
|
if (!obj) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
loadSceneGroups(obj);
|
||||||
|
loadWindowTitleSwitches(obj);
|
||||||
|
loadScreenRegionSwitches(obj);
|
||||||
|
loadPauseSwitches(obj);
|
||||||
|
loadSceneSequenceSwitches(obj);
|
||||||
|
loadSceneTransitions(obj);
|
||||||
|
loadIdleSwitches(obj);
|
||||||
|
loadExecutableSwitches(obj);
|
||||||
|
loadRandomSwitches(obj);
|
||||||
|
loadFileSwitches(obj);
|
||||||
|
loadMediaSwitches(obj);
|
||||||
|
loadTimeSwitches(obj);
|
||||||
|
loadAudioSwitches(obj);
|
||||||
|
loadVideoSwitches(obj);
|
||||||
|
loadNetworkSettings(obj);
|
||||||
|
loadSceneTriggers(obj);
|
||||||
|
loadGeneralSettings(obj);
|
||||||
|
loadHotkeys(obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SwitcherData::saveSettings(obs_data_t *obj)
|
||||||
|
{
|
||||||
|
if (!obj) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
saveSceneGroups(obj);
|
||||||
|
saveWindowTitleSwitches(obj);
|
||||||
|
saveScreenRegionSwitches(obj);
|
||||||
|
savePauseSwitches(obj);
|
||||||
|
saveSceneSequenceSwitches(obj);
|
||||||
|
saveSceneTransitions(obj);
|
||||||
|
saveIdleSwitches(obj);
|
||||||
|
saveExecutableSwitches(obj);
|
||||||
|
saveRandomSwitches(obj);
|
||||||
|
saveFileSwitches(obj);
|
||||||
|
saveMediaSwitches(obj);
|
||||||
|
saveTimeSwitches(obj);
|
||||||
|
saveAudioSwitches(obj);
|
||||||
|
saveVideoSwitches(obj);
|
||||||
|
saveNetworkSwitches(obj);
|
||||||
|
saveSceneTriggers(obj);
|
||||||
|
saveGeneralSettings(obj);
|
||||||
|
saveHotkeys(obj);
|
||||||
|
saveVersion(obj, g_GIT_SHA1);
|
||||||
|
}
|
||||||
|
|
||||||
void SwitcherData::saveGeneralSettings(obs_data_t *obj)
|
void SwitcherData::saveGeneralSettings(obs_data_t *obj)
|
||||||
{
|
{
|
||||||
obs_data_set_int(obj, "interval", switcher->interval);
|
obs_data_set_int(obj, "interval", switcher->interval);
|
||||||
@@ -479,18 +453,8 @@ void SwitcherData::saveGeneralSettings(obs_data_t *obj)
|
|||||||
obs_data_set_bool(obj, "active", !switcher->stop);
|
obs_data_set_bool(obj, "active", !switcher->stop);
|
||||||
obs_data_set_int(obj, "startup_behavior", switcher->startupBehavior);
|
obs_data_set_int(obj, "startup_behavior", switcher->startupBehavior);
|
||||||
|
|
||||||
std::string autoStopSceneName =
|
obs_data_set_int(obj, "autoStartEvent",
|
||||||
GetWeakSourceName(switcher->autoStopScene);
|
static_cast<int>(switcher->autoStartEvent));
|
||||||
obs_data_set_bool(obj, "autoStopEnable", switcher->autoStopEnable);
|
|
||||||
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, "verbose", switcher->verbose);
|
||||||
obs_data_set_bool(obj, "disableHints", switcher->disableHints);
|
obs_data_set_bool(obj, "disableHints", switcher->disableHints);
|
||||||
@@ -513,15 +477,15 @@ void SwitcherData::saveGeneralSettings(obs_data_t *obj)
|
|||||||
switcher->functionNamesByPriority[7]);
|
switcher->functionNamesByPriority[7]);
|
||||||
obs_data_set_int(obj, "priority8",
|
obs_data_set_int(obj, "priority8",
|
||||||
switcher->functionNamesByPriority[8]);
|
switcher->functionNamesByPriority[8]);
|
||||||
|
obs_data_set_int(obj, "priority9",
|
||||||
|
switcher->functionNamesByPriority[9]);
|
||||||
|
|
||||||
obs_data_set_int(obj, "threadPriority", switcher->threadPriority);
|
obs_data_set_int(obj, "threadPriority", switcher->threadPriority);
|
||||||
|
|
||||||
// After fresh install of OBS the vector can be empty
|
// After fresh install of OBS the vector can be empty
|
||||||
// as save() might be called before first load()
|
// as save() might be called before first load()
|
||||||
if (switcher->tabOrder.size() < 13) {
|
if (tabOrder.size() < tab_count) {
|
||||||
switcher->tabOrder = std::vector<int>(13);
|
resetTabOrder();
|
||||||
std::iota(switcher->tabOrder.begin(), switcher->tabOrder.end(),
|
|
||||||
0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
obs_data_set_int(obj, "generalTabPos", switcher->tabOrder[0]);
|
obs_data_set_int(obj, "generalTabPos", switcher->tabOrder[0]);
|
||||||
@@ -537,6 +501,10 @@ void SwitcherData::saveGeneralSettings(obs_data_t *obj)
|
|||||||
obs_data_set_int(obj, "idleTabPos", switcher->tabOrder[10]);
|
obs_data_set_int(obj, "idleTabPos", switcher->tabOrder[10]);
|
||||||
obs_data_set_int(obj, "sequenceTabPos", switcher->tabOrder[11]);
|
obs_data_set_int(obj, "sequenceTabPos", switcher->tabOrder[11]);
|
||||||
obs_data_set_int(obj, "audioTabPos", switcher->tabOrder[12]);
|
obs_data_set_int(obj, "audioTabPos", switcher->tabOrder[12]);
|
||||||
|
obs_data_set_int(obj, "videoTabPos", switcher->tabOrder[13]);
|
||||||
|
obs_data_set_int(obj, "networkTabPos", switcher->tabOrder[14]);
|
||||||
|
obs_data_set_int(obj, "sceneGroupTabPos", switcher->tabOrder[15]);
|
||||||
|
obs_data_set_int(obj, "triggerTabPos", switcher->tabOrder[16]);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SwitcherData::loadGeneralSettings(obs_data_t *obj)
|
void SwitcherData::loadGeneralSettings(obs_data_t *obj)
|
||||||
@@ -558,22 +526,15 @@ void SwitcherData::loadGeneralSettings(obs_data_t *obj)
|
|||||||
switcher->stop = !obs_data_get_bool(obj, "active");
|
switcher->stop = !obs_data_get_bool(obj, "active");
|
||||||
switcher->startupBehavior =
|
switcher->startupBehavior =
|
||||||
(StartupBehavior)obs_data_get_int(obj, "startup_behavior");
|
(StartupBehavior)obs_data_get_int(obj, "startup_behavior");
|
||||||
if (switcher->startupBehavior == START)
|
if (switcher->startupBehavior == START) {
|
||||||
switcher->stop = false;
|
switcher->stop = false;
|
||||||
if (switcher->startupBehavior == STOP)
|
}
|
||||||
|
if (switcher->startupBehavior == STOP) {
|
||||||
switcher->stop = true;
|
switcher->stop = true;
|
||||||
|
}
|
||||||
|
|
||||||
std::string autoStopScene =
|
switcher->autoStartEvent = static_cast<AutoStartEvent>(
|
||||||
obs_data_get_string(obj, "autoStopSceneName");
|
obs_data_get_int(obj, "autoStartEvent"));
|
||||||
switcher->autoStopEnable = obs_data_get_bool(obj, "autoStopEnable");
|
|
||||||
switcher->autoStopScene = GetWeakSourceByName(autoStopScene.c_str());
|
|
||||||
|
|
||||||
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());
|
|
||||||
|
|
||||||
switcher->verbose = obs_data_get_bool(obj, "verbose");
|
switcher->verbose = obs_data_get_bool(obj, "verbose");
|
||||||
switcher->disableHints = obs_data_get_bool(obj, "disableHints");
|
switcher->disableHints = obs_data_get_bool(obj, "disableHints");
|
||||||
@@ -606,6 +567,8 @@ void SwitcherData::loadGeneralSettings(obs_data_t *obj)
|
|||||||
(obs_data_get_int(obj, "priority7"));
|
(obs_data_get_int(obj, "priority7"));
|
||||||
switcher->functionNamesByPriority[8] =
|
switcher->functionNamesByPriority[8] =
|
||||||
(obs_data_get_int(obj, "priority8"));
|
(obs_data_get_int(obj, "priority8"));
|
||||||
|
switcher->functionNamesByPriority[9] =
|
||||||
|
(obs_data_get_int(obj, "priority9"));
|
||||||
if (!switcher->prioFuncsValid()) {
|
if (!switcher->prioFuncsValid()) {
|
||||||
switcher->functionNamesByPriority[0] = (default_priority_0);
|
switcher->functionNamesByPriority[0] = (default_priority_0);
|
||||||
switcher->functionNamesByPriority[1] = (default_priority_1);
|
switcher->functionNamesByPriority[1] = (default_priority_1);
|
||||||
@@ -616,6 +579,7 @@ void SwitcherData::loadGeneralSettings(obs_data_t *obj)
|
|||||||
switcher->functionNamesByPriority[6] = (default_priority_6);
|
switcher->functionNamesByPriority[6] = (default_priority_6);
|
||||||
switcher->functionNamesByPriority[7] = (default_priority_7);
|
switcher->functionNamesByPriority[7] = (default_priority_7);
|
||||||
switcher->functionNamesByPriority[8] = (default_priority_8);
|
switcher->functionNamesByPriority[8] = (default_priority_8);
|
||||||
|
switcher->functionNamesByPriority[9] = (default_priority_9);
|
||||||
}
|
}
|
||||||
|
|
||||||
obs_data_set_default_int(obj, "threadPriority",
|
obs_data_set_default_int(obj, "threadPriority",
|
||||||
@@ -635,6 +599,10 @@ void SwitcherData::loadGeneralSettings(obs_data_t *obj)
|
|||||||
obs_data_set_default_int(obj, "idleTabPos", 10);
|
obs_data_set_default_int(obj, "idleTabPos", 10);
|
||||||
obs_data_set_default_int(obj, "sequenceTabPos", 11);
|
obs_data_set_default_int(obj, "sequenceTabPos", 11);
|
||||||
obs_data_set_default_int(obj, "audioTabPos", 12);
|
obs_data_set_default_int(obj, "audioTabPos", 12);
|
||||||
|
obs_data_set_default_int(obj, "videoTabPos", 13);
|
||||||
|
obs_data_set_default_int(obj, "networkTabPos", 14);
|
||||||
|
obs_data_set_default_int(obj, "sceneGroupTabPos", 15);
|
||||||
|
obs_data_set_default_int(obj, "triggerTabPos", 16);
|
||||||
|
|
||||||
switcher->tabOrder.emplace_back(
|
switcher->tabOrder.emplace_back(
|
||||||
(int)(obs_data_get_int(obj, "generalTabPos")));
|
(int)(obs_data_get_int(obj, "generalTabPos")));
|
||||||
@@ -662,6 +630,32 @@ void SwitcherData::loadGeneralSettings(obs_data_t *obj)
|
|||||||
(int)(obs_data_get_int(obj, "sequenceTabPos")));
|
(int)(obs_data_get_int(obj, "sequenceTabPos")));
|
||||||
switcher->tabOrder.emplace_back(
|
switcher->tabOrder.emplace_back(
|
||||||
(int)(obs_data_get_int(obj, "audioTabPos")));
|
(int)(obs_data_get_int(obj, "audioTabPos")));
|
||||||
|
switcher->tabOrder.emplace_back(
|
||||||
|
(int)(obs_data_get_int(obj, "videoTabPos")));
|
||||||
|
switcher->tabOrder.emplace_back(
|
||||||
|
(int)(obs_data_get_int(obj, "networkTabPos")));
|
||||||
|
switcher->tabOrder.emplace_back(
|
||||||
|
(int)(obs_data_get_int(obj, "sceneGroupTabPos")));
|
||||||
|
switcher->tabOrder.emplace_back(
|
||||||
|
(int)(obs_data_get_int(obj, "triggerTabPos")));
|
||||||
|
|
||||||
|
if (!tabOrderValid()) {
|
||||||
|
resetTabOrder();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SwitcherData::tabOrderValid()
|
||||||
|
{
|
||||||
|
auto tmp = tabOrder;
|
||||||
|
std::sort(tmp.begin(), tmp.end());
|
||||||
|
auto it = std::unique(tmp.begin(), tmp.end());
|
||||||
|
return it == tmp.end();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SwitcherData::resetTabOrder()
|
||||||
|
{
|
||||||
|
tabOrder = std::vector<int>(tab_count);
|
||||||
|
std::iota(switcher->tabOrder.begin(), switcher->tabOrder.end(), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SwitcherData::checkNoMatchSwitch(bool &match, OBSWeakSource &scene,
|
void SwitcherData::checkNoMatchSwitch(bool &match, OBSWeakSource &scene,
|
||||||
@@ -703,15 +697,49 @@ void SwitcherData::checkSwitchCooldown(bool &match)
|
|||||||
}
|
}
|
||||||
|
|
||||||
match = false;
|
match = false;
|
||||||
if (verbose)
|
vblog(LOG_INFO, "cooldown active - ignoring match");
|
||||||
blog(LOG_INFO, "cooldown active - ignoring match");
|
}
|
||||||
|
|
||||||
|
void populateStartupBehavior(QComboBox *cb)
|
||||||
|
{
|
||||||
|
cb->addItem(obs_module_text(
|
||||||
|
"AdvSceneSwitcher.generalTab.status.onStartup.asLastRun"));
|
||||||
|
cb->addItem(obs_module_text(
|
||||||
|
"AdvSceneSwitcher.generalTab.status.onStartup.alwaysStart"));
|
||||||
|
cb->addItem(obs_module_text(
|
||||||
|
"AdvSceneSwitcher.generalTab.status.onStartup.doNotStart"));
|
||||||
|
}
|
||||||
|
|
||||||
|
void populateAutoStartEventSelection(QComboBox *cb)
|
||||||
|
{
|
||||||
|
cb->addItem(obs_module_text(
|
||||||
|
"AdvSceneSwitcher.generalTab.status.autoStart.never"));
|
||||||
|
cb->addItem(obs_module_text(
|
||||||
|
"AdvSceneSwitcher.generalTab.status.autoStart.recording"));
|
||||||
|
cb->addItem(obs_module_text(
|
||||||
|
"AdvSceneSwitcher.generalTab.status.autoStart.streaming"));
|
||||||
|
cb->addItem(obs_module_text(
|
||||||
|
"AdvSceneSwitcher.generalTab.status.autoStart.recordingAndStreaming"));
|
||||||
|
}
|
||||||
|
|
||||||
|
void AdvSceneSwitcher::updateStatus()
|
||||||
|
{
|
||||||
|
if (switcher->th && switcher->th->isRunning()) {
|
||||||
|
if (currentStatusActive) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
SetStarted();
|
||||||
|
} else {
|
||||||
|
if (!currentStatusActive) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
SetStopped();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void AdvSceneSwitcher::setupGeneralTab()
|
void AdvSceneSwitcher::setupGeneralTab()
|
||||||
{
|
{
|
||||||
populateSceneSelection(ui->noMatchSwitchScene, false);
|
populateSceneSelection(ui->noMatchSwitchScene, false);
|
||||||
populateSceneSelection(ui->autoStopScenes, false);
|
|
||||||
populateSceneSelection(ui->autoStartScenes, false);
|
|
||||||
|
|
||||||
if (switcher->switchIfNotMatching == SWITCH) {
|
if (switcher->switchIfNotMatching == SWITCH) {
|
||||||
ui->noMatchSwitch->setChecked(true);
|
ui->noMatchSwitch->setChecked(true);
|
||||||
@@ -734,36 +762,6 @@ void AdvSceneSwitcher::setupGeneralTab()
|
|||||||
ui->cooldownTime->setToolTip(obs_module_text(
|
ui->cooldownTime->setToolTip(obs_module_text(
|
||||||
"AdvSceneSwitcher.generalTab.generalBehavior.cooldownHint"));
|
"AdvSceneSwitcher.generalTab.generalBehavior.cooldownHint"));
|
||||||
|
|
||||||
ui->autoStopSceneCheckBox->setChecked(switcher->autoStopEnable);
|
|
||||||
ui->autoStopScenes->setCurrentText(
|
|
||||||
GetWeakSourceName(switcher->autoStopScene).c_str());
|
|
||||||
|
|
||||||
if (ui->autoStopSceneCheckBox->checkState()) {
|
|
||||||
ui->autoStopScenes->setDisabled(false);
|
|
||||||
} else {
|
|
||||||
ui->autoStopScenes->setDisabled(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
ui->autoStartType->addItem(obs_module_text(
|
|
||||||
"AdvSceneSwitcher.generalTab.generalBehavior.automaticallyStart.recording"));
|
|
||||||
ui->autoStartType->addItem(obs_module_text(
|
|
||||||
"AdvSceneSwitcher.generalTab.generalBehavior.automaticallyStart.streaming"));
|
|
||||||
ui->autoStartType->addItem(obs_module_text(
|
|
||||||
"AdvSceneSwitcher.generalTab.generalBehavior.automaticallyStart.recordingAndStreaming"));
|
|
||||||
|
|
||||||
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->verboseLogging->setChecked(switcher->verbose);
|
||||||
ui->uiHintsDisable->setChecked(switcher->disableHints);
|
ui->uiHintsDisable->setChecked(switcher->disableHints);
|
||||||
|
|
||||||
@@ -806,6 +804,10 @@ void AdvSceneSwitcher::setupGeneralTab()
|
|||||||
s = obs_module_text(
|
s = obs_module_text(
|
||||||
"AdvSceneSwitcher.generalTab.priority.audio");
|
"AdvSceneSwitcher.generalTab.priority.audio");
|
||||||
break;
|
break;
|
||||||
|
case video_func:
|
||||||
|
s = obs_module_text(
|
||||||
|
"AdvSceneSwitcher.generalTab.priority.video");
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
QString text(s.c_str());
|
QString text(s.c_str());
|
||||||
QListWidgetItem *item =
|
QListWidgetItem *item =
|
||||||
@@ -826,17 +828,22 @@ void AdvSceneSwitcher::setupGeneralTab()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ui->startupBehavior->addItem(obs_module_text(
|
populateStartupBehavior(ui->startupBehavior);
|
||||||
"AdvSceneSwitcher.generalTab.status.onStartup.asLastRun"));
|
|
||||||
ui->startupBehavior->addItem(obs_module_text(
|
|
||||||
"AdvSceneSwitcher.generalTab.status.onStartup.alwaysStart"));
|
|
||||||
ui->startupBehavior->addItem(obs_module_text(
|
|
||||||
"AdvSceneSwitcher.generalTab.status.onStartup.doNotStart"));
|
|
||||||
|
|
||||||
ui->startupBehavior->setCurrentIndex(switcher->startupBehavior);
|
ui->startupBehavior->setCurrentIndex(switcher->startupBehavior);
|
||||||
|
|
||||||
if (switcher->th && switcher->th->isRunning())
|
populateAutoStartEventSelection(ui->autoStartEvent);
|
||||||
|
ui->autoStartEvent->setCurrentIndex(
|
||||||
|
static_cast<int>(switcher->autoStartEvent));
|
||||||
|
|
||||||
|
if (switcher->th && switcher->th->isRunning()) {
|
||||||
SetStarted();
|
SetStarted();
|
||||||
else
|
} else {
|
||||||
SetStopped();
|
SetStopped();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Updates the UI status element if the status changed externally
|
||||||
|
// (e.g. via hotkeys)
|
||||||
|
QTimer *statusTimer = new QTimer(this);
|
||||||
|
connect(statusTimer, SIGNAL(timeout()), this, SLOT(updateStatus()));
|
||||||
|
statusTimer->start(1000);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,10 @@
|
|||||||
#include "switcher-data-structs.hpp"
|
#include "switcher-data-structs.hpp"
|
||||||
|
|
||||||
#define blog(level, msg, ...) blog(level, "[adv-ss] " msg, ##__VA_ARGS__)
|
#define blog(level, msg, ...) blog(level, "[adv-ss] " msg, ##__VA_ARGS__)
|
||||||
|
#define vblog(level, msg, ...) \
|
||||||
|
if (verbose) { \
|
||||||
|
blog(level, msg, ##__VA_ARGS__); \
|
||||||
|
}
|
||||||
|
|
||||||
class QCloseEvent;
|
class QCloseEvent;
|
||||||
|
|
||||||
@@ -19,6 +23,7 @@ class AdvSceneSwitcher : public QDialog {
|
|||||||
public:
|
public:
|
||||||
std::unique_ptr<Ui_AdvSceneSwitcher> ui;
|
std::unique_ptr<Ui_AdvSceneSwitcher> ui;
|
||||||
bool loading = true;
|
bool loading = true;
|
||||||
|
bool currentStatusActive = false;
|
||||||
|
|
||||||
AdvSceneSwitcher(QWidget *parent);
|
AdvSceneSwitcher(QWidget *parent);
|
||||||
|
|
||||||
@@ -35,10 +40,8 @@ public:
|
|||||||
int IgnoreIdleWindowsFindByData(const QString &window);
|
int IgnoreIdleWindowsFindByData(const QString &window);
|
||||||
|
|
||||||
void UpdateNonMatchingScene(const QString &name);
|
void UpdateNonMatchingScene(const QString &name);
|
||||||
void UpdateAutoStopScene(const QString &name);
|
void OpenSequenceExtendEdit(SequenceWidget *sw);
|
||||||
void UpdateAutoStartScene(const QString &name);
|
void SetEditSceneGroup(SceneGroup &sg);
|
||||||
void UpdateIdleDataTransition(const QString &name);
|
|
||||||
void UpdateIdleDataScene(const QString &name);
|
|
||||||
|
|
||||||
void loadUI();
|
void loadUI();
|
||||||
void setupGeneralTab();
|
void setupGeneralTab();
|
||||||
@@ -54,18 +57,36 @@ public:
|
|||||||
void setupFileTab();
|
void setupFileTab();
|
||||||
void setupTimeTab();
|
void setupTimeTab();
|
||||||
void setupAudioTab();
|
void setupAudioTab();
|
||||||
|
void setupSceneGroupTab();
|
||||||
|
void setupTriggerTab();
|
||||||
|
void setupVideoTab();
|
||||||
|
void setupNetworkTab();
|
||||||
void setTabOrder();
|
void setTabOrder();
|
||||||
|
|
||||||
void DisplayMessage(QString msg);
|
static bool DisplayMessage(QString msg, bool question = false);
|
||||||
|
static void AskBackup(obs_data_t *obj);
|
||||||
|
|
||||||
|
static void addSelectionEntry(QComboBox *sel, const char *description,
|
||||||
|
bool selectable = false,
|
||||||
|
const char *tooltip = "");
|
||||||
static void populateSceneSelection(QComboBox *sel,
|
static void populateSceneSelection(QComboBox *sel,
|
||||||
bool addPrevious = false,
|
bool addPrevious = false,
|
||||||
bool addSelect = true);
|
bool addSceneGroup = false,
|
||||||
|
bool addSelect = true,
|
||||||
|
std::string selectText = "",
|
||||||
|
bool selectable = false);
|
||||||
|
|
||||||
static void populateTransitionSelection(QComboBox *sel,
|
static void populateTransitionSelection(QComboBox *sel,
|
||||||
bool addSelect = true);
|
bool addCurrent = true,
|
||||||
|
bool addSelect = true,
|
||||||
|
bool selectable = false);
|
||||||
static void populateWindowSelection(QComboBox *sel,
|
static void populateWindowSelection(QComboBox *sel,
|
||||||
bool addSelect = true);
|
bool addSelect = true);
|
||||||
static void populateAudioSelection(QComboBox *sel,
|
static void populateAudioSelection(QComboBox *sel,
|
||||||
bool addSelect = true);
|
bool addSelect = true);
|
||||||
|
static void populateVideoSelection(QComboBox *sel,
|
||||||
|
bool addScenes = true,
|
||||||
|
bool addSelect = true);
|
||||||
static void populateMediaSelection(QComboBox *sel,
|
static void populateMediaSelection(QComboBox *sel,
|
||||||
bool addSelect = true);
|
bool addSelect = true);
|
||||||
static void populateProcessSelection(QComboBox *sel,
|
static void populateProcessSelection(QComboBox *sel,
|
||||||
@@ -80,6 +101,11 @@ public:
|
|||||||
bool listMoveUp(QListWidget *list);
|
bool listMoveUp(QListWidget *list);
|
||||||
bool listMoveDown(QListWidget *list);
|
bool listMoveDown(QListWidget *list);
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void SceneGroupAdded(const QString &name);
|
||||||
|
void SceneGroupRemoved(const QString &name);
|
||||||
|
void SceneGroupRenamed(const QString &oldName, const QString newName);
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void on_windowUp_clicked();
|
void on_windowUp_clicked();
|
||||||
void on_windowDown_clicked();
|
void on_windowDown_clicked();
|
||||||
@@ -91,6 +117,7 @@ public slots:
|
|||||||
void on_noMatchDelay_valueChanged(double i);
|
void on_noMatchDelay_valueChanged(double i);
|
||||||
void on_cooldownTime_valueChanged(double i);
|
void on_cooldownTime_valueChanged(double i);
|
||||||
void on_startupBehavior_currentIndexChanged(int index);
|
void on_startupBehavior_currentIndexChanged(int index);
|
||||||
|
void on_autoStartEvent_currentIndexChanged(int index);
|
||||||
void on_noMatchSwitchScene_currentTextChanged(const QString &text);
|
void on_noMatchSwitchScene_currentTextChanged(const QString &text);
|
||||||
void on_checkInterval_valueChanged(int value);
|
void on_checkInterval_valueChanged(int value);
|
||||||
void on_toggleStartButton_clicked();
|
void on_toggleStartButton_clicked();
|
||||||
@@ -115,17 +142,12 @@ public slots:
|
|||||||
|
|
||||||
void on_sceneSequenceAdd_clicked();
|
void on_sceneSequenceAdd_clicked();
|
||||||
void on_sceneSequenceRemove_clicked();
|
void on_sceneSequenceRemove_clicked();
|
||||||
void on_sceneSequenceSave_clicked();
|
|
||||||
void on_sceneSequenceLoad_clicked();
|
|
||||||
void on_sceneSequenceUp_clicked();
|
void on_sceneSequenceUp_clicked();
|
||||||
void on_sceneSequenceDown_clicked();
|
void on_sceneSequenceDown_clicked();
|
||||||
|
void on_sceneSequenceSave_clicked();
|
||||||
void on_autoStopSceneCheckBox_stateChanged(int state);
|
void on_sceneSequenceLoad_clicked();
|
||||||
void on_autoStopScenes_currentTextChanged(const QString &text);
|
void on_sequenceEdit_clicked();
|
||||||
|
void on_sceneSequenceSwitches_itemDoubleClicked(QListWidgetItem *item);
|
||||||
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_verboseLogging_stateChanged(int state);
|
||||||
void on_uiHintsDisable_stateChanged(int state);
|
void on_uiHintsDisable_stateChanged(int state);
|
||||||
@@ -142,6 +164,7 @@ public slots:
|
|||||||
void on_defaultTransitionsUp_clicked();
|
void on_defaultTransitionsUp_clicked();
|
||||||
void on_defaultTransitionsDown_clicked();
|
void on_defaultTransitionsDown_clicked();
|
||||||
void on_transitionOverridecheckBox_stateChanged(int state);
|
void on_transitionOverridecheckBox_stateChanged(int state);
|
||||||
|
void defTransitionDelayValueChanged(int value);
|
||||||
|
|
||||||
void on_browseButton_clicked();
|
void on_browseButton_clicked();
|
||||||
void on_readFileCheckBox_stateChanged(int state);
|
void on_readFileCheckBox_stateChanged(int state);
|
||||||
@@ -155,9 +178,6 @@ public slots:
|
|||||||
void on_executableRemove_clicked();
|
void on_executableRemove_clicked();
|
||||||
|
|
||||||
void on_idleCheckBox_stateChanged(int state);
|
void on_idleCheckBox_stateChanged(int state);
|
||||||
void on_idleTransitions_currentTextChanged(const QString &text);
|
|
||||||
void on_idleScenes_currentTextChanged(const QString &text);
|
|
||||||
void on_idleSpinBox_valueChanged(int i);
|
|
||||||
void on_ignoreIdleWindows_currentRowChanged(int idx);
|
void on_ignoreIdleWindows_currentRowChanged(int idx);
|
||||||
void on_ignoreIdleAdd_clicked();
|
void on_ignoreIdleAdd_clicked();
|
||||||
void on_ignoreIdleRemove_clicked();
|
void on_ignoreIdleRemove_clicked();
|
||||||
@@ -187,45 +207,81 @@ public slots:
|
|||||||
void on_audioDown_clicked();
|
void on_audioDown_clicked();
|
||||||
void on_audioFallback_toggled(bool on);
|
void on_audioFallback_toggled(bool on);
|
||||||
|
|
||||||
|
void on_videoAdd_clicked();
|
||||||
|
void on_videoRemove_clicked();
|
||||||
|
void on_videoUp_clicked();
|
||||||
|
void on_videoDown_clicked();
|
||||||
|
void on_getScreenshot_clicked();
|
||||||
|
|
||||||
|
void on_serverSettings_toggled(bool on);
|
||||||
|
void on_serverPort_valueChanged(int value);
|
||||||
|
void on_lockToIPv4_stateChanged(int state);
|
||||||
|
void on_serverRestart_clicked();
|
||||||
|
void updateServerStatus();
|
||||||
|
void on_clientSettings_toggled(bool on);
|
||||||
|
void on_clientHostname_textChanged(const QString &text);
|
||||||
|
void on_clientPort_valueChanged(int value);
|
||||||
|
void on_restrictSend_stateChanged(int state);
|
||||||
|
void on_clientReconnect_clicked();
|
||||||
|
void updateClientStatus();
|
||||||
|
|
||||||
|
void on_sceneGroupAdd_clicked();
|
||||||
|
void on_sceneGroupRemove_clicked();
|
||||||
|
void on_sceneGroupUp_clicked();
|
||||||
|
void on_sceneGroupDown_clicked();
|
||||||
|
void on_sceneGroupName_editingFinished();
|
||||||
|
void on_sceneGroups_currentRowChanged(int idx);
|
||||||
|
|
||||||
|
void on_sceneGroupSceneAdd_clicked();
|
||||||
|
void on_sceneGroupSceneRemove_clicked();
|
||||||
|
void on_sceneGroupSceneUp_clicked();
|
||||||
|
void on_sceneGroupSceneDown_clicked();
|
||||||
|
|
||||||
|
void on_triggerAdd_clicked();
|
||||||
|
void on_triggerRemove_clicked();
|
||||||
|
void on_triggerUp_clicked();
|
||||||
|
void on_triggerDown_clicked();
|
||||||
|
|
||||||
void on_priorityUp_clicked();
|
void on_priorityUp_clicked();
|
||||||
void on_priorityDown_clicked();
|
void on_priorityDown_clicked();
|
||||||
void on_threadPriority_currentTextChanged(const QString &text);
|
void on_threadPriority_currentTextChanged(const QString &text);
|
||||||
|
|
||||||
void updateScreenRegionCursorPos();
|
void updateScreenRegionCursorPos();
|
||||||
|
void updateStatus();
|
||||||
|
|
||||||
void on_close_clicked();
|
void on_close_clicked();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
};
|
};
|
||||||
|
|
||||||
/********************************************************************************
|
/******************************************************************************
|
||||||
* Windowtitle helper
|
* Windowtitle helper
|
||||||
********************************************************************************/
|
******************************************************************************/
|
||||||
void GetWindowList(std::vector<std::string> &windows);
|
void GetWindowList(std::vector<std::string> &windows);
|
||||||
void GetWindowList(QStringList &windows); // Overloaded
|
void GetWindowList(QStringList &windows);
|
||||||
void GetCurrentWindowTitle(std::string &title);
|
void GetCurrentWindowTitle(std::string &title);
|
||||||
bool isFullscreen(std::string &title);
|
bool isFullscreen(std::string &title);
|
||||||
bool isMaximized(std::string &title);
|
bool isMaximized(std::string &title);
|
||||||
|
|
||||||
/********************************************************************************
|
/******************************************************************************
|
||||||
* Screenregion helper
|
* Screenregion helper
|
||||||
********************************************************************************/
|
******************************************************************************/
|
||||||
std::pair<int, int> getCursorPos();
|
std::pair<int, int> getCursorPos();
|
||||||
|
|
||||||
/********************************************************************************
|
/******************************************************************************
|
||||||
* Idle detection helper
|
* Idle detection helper
|
||||||
********************************************************************************/
|
******************************************************************************/
|
||||||
int secondsSinceLastInput();
|
int secondsSinceLastInput();
|
||||||
|
|
||||||
/********************************************************************************
|
/******************************************************************************
|
||||||
* Executable helper
|
* Executable helper
|
||||||
********************************************************************************/
|
******************************************************************************/
|
||||||
void GetProcessList(QStringList &processes);
|
void GetProcessList(QStringList &processes);
|
||||||
bool isInFocus(const QString &executable);
|
bool isInFocus(const QString &executable);
|
||||||
|
|
||||||
/********************************************************************************
|
/******************************************************************************
|
||||||
* Sceneswitch helper
|
* Sceneswitch helper
|
||||||
********************************************************************************/
|
******************************************************************************/
|
||||||
|
|
||||||
void setNextTransition(OBSWeakSource &targetScene, obs_source_t *currentSource,
|
void setNextTransition(OBSWeakSource &targetScene, obs_source_t *currentSource,
|
||||||
OBSWeakSource &transition,
|
OBSWeakSource &transition,
|
||||||
@@ -236,8 +292,8 @@ void restoreTransitionOverride(obs_source_t *scene, transitionData td);
|
|||||||
void switchScene(OBSWeakSource &scene, OBSWeakSource &transition,
|
void switchScene(OBSWeakSource &scene, OBSWeakSource &transition,
|
||||||
bool &transitionOverrideOverride);
|
bool &transitionOverrideOverride);
|
||||||
|
|
||||||
/********************************************************************************
|
/******************************************************************************
|
||||||
* Main SwitcherData
|
* Main SwitcherData
|
||||||
********************************************************************************/
|
******************************************************************************/
|
||||||
struct SwitcherData;
|
struct SwitcherData;
|
||||||
extern SwitcherData *switcher;
|
extern SwitcherData *switcher;
|
||||||
|
|||||||
108
src/headers/scene-group.hpp
Normal file
108
src/headers/scene-group.hpp
Normal file
@@ -0,0 +1,108 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
#include <QDialog>
|
||||||
|
#include <QLabel>
|
||||||
|
#include <QLineEdit>
|
||||||
|
#include <QComboBox>
|
||||||
|
#include <QSpinBox>
|
||||||
|
#include <QDoubleSpinBox>
|
||||||
|
#include <QCheckBox>
|
||||||
|
#include <obs.hpp>
|
||||||
|
|
||||||
|
auto constexpr invalid_scene_group_name = "invalid-scene-group";
|
||||||
|
|
||||||
|
enum class AdvanceCondition {
|
||||||
|
Count,
|
||||||
|
Time,
|
||||||
|
Random,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct SceneGroup {
|
||||||
|
OBSWeakSource getCurrentScene();
|
||||||
|
OBSWeakSource getNextScene();
|
||||||
|
OBSWeakSource getNextSceneCount();
|
||||||
|
OBSWeakSource getNextSceneTime();
|
||||||
|
OBSWeakSource getNextSceneRandom();
|
||||||
|
void advanceIdx();
|
||||||
|
|
||||||
|
std::string name = invalid_scene_group_name;
|
||||||
|
AdvanceCondition type = AdvanceCondition::Count;
|
||||||
|
std::vector<OBSWeakSource> scenes = {};
|
||||||
|
int count = 1;
|
||||||
|
double time = 0;
|
||||||
|
bool repeat = false;
|
||||||
|
|
||||||
|
size_t currentIdx = 0;
|
||||||
|
|
||||||
|
int currentCount = -1;
|
||||||
|
std::chrono::high_resolution_clock::time_point lastAdvTime;
|
||||||
|
int lastRandomScene = -1;
|
||||||
|
|
||||||
|
inline SceneGroup(){};
|
||||||
|
inline SceneGroup(std::string name_) : name(name_){};
|
||||||
|
inline SceneGroup(std::string name_, AdvanceCondition type_,
|
||||||
|
std::vector<OBSWeakSource> scenes_, int count_,
|
||||||
|
double time_, bool repeat_)
|
||||||
|
: name(name_),
|
||||||
|
type(type_),
|
||||||
|
scenes(scenes_),
|
||||||
|
count(count_),
|
||||||
|
time(time_),
|
||||||
|
repeat(repeat_)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class SwitchTargetType {
|
||||||
|
Scene,
|
||||||
|
SceneGroup,
|
||||||
|
};
|
||||||
|
|
||||||
|
class SceneGroupEditWidget : public QWidget {
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
SceneGroupEditWidget();
|
||||||
|
void SetEditSceneGroup(SceneGroup *sg);
|
||||||
|
void ShowCurrentTypeEdit();
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void TypeChanged(int type);
|
||||||
|
void CountChanged(int count);
|
||||||
|
void TimeChanged(double time);
|
||||||
|
void RepeatChanged(int state);
|
||||||
|
|
||||||
|
private:
|
||||||
|
QComboBox *type;
|
||||||
|
|
||||||
|
QWidget *timeEdit;
|
||||||
|
QWidget *countEdit;
|
||||||
|
|
||||||
|
QSpinBox *count;
|
||||||
|
QDoubleSpinBox *time;
|
||||||
|
QLabel *random;
|
||||||
|
|
||||||
|
QCheckBox *repeat;
|
||||||
|
|
||||||
|
SceneGroup *sceneGroup = nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Based on OBS's NameDialog
|
||||||
|
class SGNameDialog : public QDialog {
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
SGNameDialog(QWidget *parent);
|
||||||
|
|
||||||
|
// Returns true if user clicks OK, false otherwise
|
||||||
|
// userTextInput returns string that user typed into dialog
|
||||||
|
static bool AskForName(QWidget *parent, const QString &title,
|
||||||
|
const QString &text, std::string &userTextInput,
|
||||||
|
const QString &placeHolder = QString(""),
|
||||||
|
int maxSize = 170);
|
||||||
|
|
||||||
|
private:
|
||||||
|
QLabel *label;
|
||||||
|
QLineEdit *userText;
|
||||||
|
};
|
||||||
73
src/headers/scene-trigger.hpp
Normal file
73
src/headers/scene-trigger.hpp
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "switch-generic.hpp"
|
||||||
|
|
||||||
|
enum class sceneTriggerType {
|
||||||
|
NONE = 0,
|
||||||
|
SCENE_ACTIVE = 1,
|
||||||
|
SCENE_INACTIVE = 2,
|
||||||
|
SCENE_LEAVE = 3,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class sceneTriggerAction {
|
||||||
|
NONE = 0,
|
||||||
|
START_RECORDING,
|
||||||
|
PAUSE_RECORDING,
|
||||||
|
UNPAUSE_RECORDING,
|
||||||
|
STOP_RECORDING,
|
||||||
|
|
||||||
|
START_STREAMING,
|
||||||
|
STOP_STREAMING,
|
||||||
|
|
||||||
|
START_REPLAY_BUFFER,
|
||||||
|
STOP_REPLAY_BUFFER,
|
||||||
|
|
||||||
|
MUTE_SOURCE,
|
||||||
|
UNMUTE_SOURCE,
|
||||||
|
|
||||||
|
START_SWITCHER,
|
||||||
|
STOP_SWITCHER,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct SceneTrigger : SceneSwitcherEntry {
|
||||||
|
static bool pause;
|
||||||
|
sceneTriggerType triggerType = sceneTriggerType::NONE;
|
||||||
|
sceneTriggerAction triggerAction = sceneTriggerAction::NONE;
|
||||||
|
double duration = 0;
|
||||||
|
OBSWeakSource audioSource = nullptr;
|
||||||
|
|
||||||
|
const char *getType() { return "trigger"; }
|
||||||
|
void save(obs_data_t *obj);
|
||||||
|
void load(obs_data_t *obj);
|
||||||
|
|
||||||
|
bool checkMatch(OBSWeakSource currentScene,
|
||||||
|
OBSWeakSource previousScene);
|
||||||
|
void performAction();
|
||||||
|
void logMatch();
|
||||||
|
};
|
||||||
|
|
||||||
|
class SceneTriggerWidget : public SwitchWidget {
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
SceneTriggerWidget(QWidget *parent, SceneTrigger *s);
|
||||||
|
SceneTrigger *getSwitchData();
|
||||||
|
void setSwitchData(SceneTrigger *s);
|
||||||
|
|
||||||
|
static void swapSwitchData(SceneTriggerWidget *s1,
|
||||||
|
SceneTriggerWidget *s2);
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void TriggerTypeChanged(int index);
|
||||||
|
void TriggerActionChanged(int index);
|
||||||
|
void DurationChanged(double dur);
|
||||||
|
void AudioSourceChanged(const QString &text);
|
||||||
|
|
||||||
|
private:
|
||||||
|
QComboBox *triggers;
|
||||||
|
QComboBox *actions;
|
||||||
|
QDoubleSpinBox *duration;
|
||||||
|
QComboBox *audioSources;
|
||||||
|
|
||||||
|
SceneTrigger *switchData;
|
||||||
|
};
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include <QSpinBox>
|
#include <QSpinBox>
|
||||||
|
#include <limits>
|
||||||
|
|
||||||
#include "switch-generic.hpp"
|
#include "switch-generic.hpp"
|
||||||
#include "volume-control.hpp"
|
#include "volume-control.hpp"
|
||||||
@@ -18,13 +19,16 @@ struct AudioSwitch : virtual SceneSwitcherEntry {
|
|||||||
int volumeThreshold = 0;
|
int volumeThreshold = 0;
|
||||||
audioCondition condition = ABOVE;
|
audioCondition condition = ABOVE;
|
||||||
double duration = 0;
|
double duration = 0;
|
||||||
|
bool ignoreInactiveSource = true;
|
||||||
unsigned int matchCount = 0;
|
unsigned int matchCount = 0;
|
||||||
float peak = -1;
|
float peak = -std::numeric_limits<float>::infinity();
|
||||||
obs_volmeter_t *volmeter = nullptr;
|
obs_volmeter_t *volmeter = nullptr;
|
||||||
|
|
||||||
const char *getType() { return "audio"; }
|
const char *getType() { return "audio"; }
|
||||||
bool initialized();
|
bool initialized();
|
||||||
bool valid();
|
bool valid();
|
||||||
|
void save(obs_data_t *obj);
|
||||||
|
void load(obs_data_t *obj);
|
||||||
static void setVolumeLevel(void *data,
|
static void setVolumeLevel(void *data,
|
||||||
const float magnitude[MAX_AUDIO_CHANNELS],
|
const float magnitude[MAX_AUDIO_CHANNELS],
|
||||||
const float peak[MAX_AUDIO_CHANNELS],
|
const float peak[MAX_AUDIO_CHANNELS],
|
||||||
@@ -32,10 +36,6 @@ struct AudioSwitch : virtual SceneSwitcherEntry {
|
|||||||
void resetVolmeter();
|
void resetVolmeter();
|
||||||
|
|
||||||
AudioSwitch(){};
|
AudioSwitch(){};
|
||||||
AudioSwitch(OBSWeakSource scene_, OBSWeakSource transition_,
|
|
||||||
OBSWeakSource audioSource_, int volumeThreshold_,
|
|
||||||
audioCondition condition_, double duration_,
|
|
||||||
bool usePreviousScene_);
|
|
||||||
AudioSwitch(const AudioSwitch &other);
|
AudioSwitch(const AudioSwitch &other);
|
||||||
AudioSwitch(AudioSwitch &&other);
|
AudioSwitch(AudioSwitch &&other);
|
||||||
~AudioSwitch();
|
~AudioSwitch();
|
||||||
@@ -46,6 +46,9 @@ struct AudioSwitch : virtual SceneSwitcherEntry {
|
|||||||
|
|
||||||
struct AudioSwitchFallback : virtual SceneSwitcherEntry {
|
struct AudioSwitchFallback : virtual SceneSwitcherEntry {
|
||||||
const char *getType() { return "audio_fallback"; }
|
const char *getType() { return "audio_fallback"; }
|
||||||
|
void save(obs_data_t *obj);
|
||||||
|
void load(obs_data_t *obj);
|
||||||
|
|
||||||
bool enable = false;
|
bool enable = false;
|
||||||
double duration = 0;
|
double duration = 0;
|
||||||
unsigned int matchCount = 0;
|
unsigned int matchCount = 0;
|
||||||
@@ -55,7 +58,7 @@ class AudioSwitchWidget : public SwitchWidget {
|
|||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
AudioSwitchWidget(AudioSwitch *s);
|
AudioSwitchWidget(QWidget *parent, AudioSwitch *s);
|
||||||
void UpdateVolmeterSource();
|
void UpdateVolmeterSource();
|
||||||
AudioSwitch *getSwitchData();
|
AudioSwitch *getSwitchData();
|
||||||
void setSwitchData(AudioSwitch *s);
|
void setSwitchData(AudioSwitch *s);
|
||||||
@@ -68,12 +71,14 @@ private slots:
|
|||||||
void VolumeThresholdChanged(int vol);
|
void VolumeThresholdChanged(int vol);
|
||||||
void ConditionChanged(int cond);
|
void ConditionChanged(int cond);
|
||||||
void DurationChanged(double dur);
|
void DurationChanged(double dur);
|
||||||
|
void IgnoreInactiveChanged(int state);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QComboBox *audioSources;
|
QComboBox *audioSources;
|
||||||
QComboBox *condition;
|
QComboBox *condition;
|
||||||
QSpinBox *audioVolumeThreshold;
|
QSpinBox *audioVolumeThreshold;
|
||||||
QDoubleSpinBox *duration;
|
QDoubleSpinBox *duration;
|
||||||
|
QCheckBox *ignoreInactiveSource;
|
||||||
VolControl *volMeter;
|
VolControl *volMeter;
|
||||||
|
|
||||||
AudioSwitch *switchData;
|
AudioSwitch *switchData;
|
||||||
@@ -83,7 +88,7 @@ class AudioSwitchFallbackWidget : public SwitchWidget {
|
|||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
AudioSwitchFallbackWidget(AudioSwitchFallback *s);
|
AudioSwitchFallbackWidget(QWidget *parent, AudioSwitchFallback *s);
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void DurationChanged(double dur);
|
void DurationChanged(double dur);
|
||||||
|
|||||||
@@ -11,22 +11,15 @@ struct ExecutableSwitch : SceneSwitcherEntry {
|
|||||||
bool inFocus = false;
|
bool inFocus = false;
|
||||||
|
|
||||||
const char *getType() { return "exec"; }
|
const char *getType() { return "exec"; }
|
||||||
|
void save(obs_data_t *obj);
|
||||||
inline ExecutableSwitch(){};
|
void load(obs_data_t *obj);
|
||||||
inline ExecutableSwitch(OBSWeakSource scene_, OBSWeakSource transition_,
|
|
||||||
const QString &exe_, bool inFocus_)
|
|
||||||
: SceneSwitcherEntry(scene_, transition_),
|
|
||||||
exe(exe_),
|
|
||||||
inFocus(inFocus_)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class ExecutableSwitchWidget : public SwitchWidget {
|
class ExecutableSwitchWidget : public SwitchWidget {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
ExecutableSwitchWidget(ExecutableSwitch *s);
|
ExecutableSwitchWidget(QWidget *parent, ExecutableSwitch *s);
|
||||||
ExecutableSwitch *getSwitchData();
|
ExecutableSwitch *getSwitchData();
|
||||||
void setSwitchData(ExecutableSwitch *s);
|
void setSwitchData(ExecutableSwitch *s);
|
||||||
|
|
||||||
|
|||||||
@@ -23,29 +23,15 @@ struct FileSwitch : SceneSwitcherEntry {
|
|||||||
size_t lastHash = 0;
|
size_t lastHash = 0;
|
||||||
|
|
||||||
const char *getType() { return "file"; }
|
const char *getType() { return "file"; }
|
||||||
|
void save(obs_data_t *obj);
|
||||||
inline FileSwitch(){};
|
void load(obs_data_t *obj);
|
||||||
inline FileSwitch(OBSWeakSource scene_, OBSWeakSource transition_,
|
|
||||||
const char *file_, const char *text_, bool remote_,
|
|
||||||
bool useRegex_, bool useTime_,
|
|
||||||
bool onlyMatchIfChanged_)
|
|
||||||
: SceneSwitcherEntry(scene_, transition_),
|
|
||||||
file(file_),
|
|
||||||
text(text_),
|
|
||||||
remote(remote_),
|
|
||||||
useRegex(useRegex_),
|
|
||||||
useTime(useTime_),
|
|
||||||
onlyMatchIfChanged(onlyMatchIfChanged_),
|
|
||||||
lastMod()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class FileSwitchWidget : public SwitchWidget {
|
class FileSwitchWidget : public SwitchWidget {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
FileSwitchWidget(FileSwitch *s);
|
FileSwitchWidget(QWidget *parent, FileSwitch *s);
|
||||||
FileSwitch *getSwitchData();
|
FileSwitch *getSwitchData();
|
||||||
void setSwitchData(FileSwitch *s);
|
void setSwitchData(FileSwitch *s);
|
||||||
|
|
||||||
|
|||||||
@@ -3,14 +3,28 @@
|
|||||||
#include <QComboBox>
|
#include <QComboBox>
|
||||||
|
|
||||||
struct SceneSwitcherEntry {
|
struct SceneSwitcherEntry {
|
||||||
|
SwitchTargetType targetType = SwitchTargetType::Scene;
|
||||||
|
SceneGroup *group = nullptr;
|
||||||
OBSWeakSource scene = nullptr;
|
OBSWeakSource scene = nullptr;
|
||||||
OBSWeakSource transition = nullptr;
|
OBSWeakSource transition = nullptr;
|
||||||
bool usePreviousScene = false;
|
bool usePreviousScene = false;
|
||||||
|
bool useCurrentTransition = false;
|
||||||
|
|
||||||
virtual const char *getType() = 0;
|
virtual const char *getType() = 0;
|
||||||
virtual bool initialized();
|
virtual bool initialized();
|
||||||
virtual bool valid();
|
virtual bool valid();
|
||||||
|
virtual void logMatchScene();
|
||||||
|
virtual void logMatchSceneGroup();
|
||||||
virtual void logMatch();
|
virtual void logMatch();
|
||||||
|
virtual OBSWeakSource getScene();
|
||||||
|
virtual void save(obs_data_t *obj,
|
||||||
|
const char *targetTypeSaveName = "targetType",
|
||||||
|
const char *targetSaveName = "target",
|
||||||
|
const char *transitionSaveName = "transition");
|
||||||
|
virtual void load(obs_data_t *obj,
|
||||||
|
const char *targetTypeLoadName = "targetType",
|
||||||
|
const char *targetLoadName = "target",
|
||||||
|
const char *transitionLoadName = "transition");
|
||||||
|
|
||||||
inline SceneSwitcherEntry() {}
|
inline SceneSwitcherEntry() {}
|
||||||
|
|
||||||
@@ -23,6 +37,26 @@ struct SceneSwitcherEntry {
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline SceneSwitcherEntry(SceneGroup *group_, OBSWeakSource transition_,
|
||||||
|
bool usePreviousScene_ = false)
|
||||||
|
: group(group_),
|
||||||
|
transition(transition_),
|
||||||
|
usePreviousScene(usePreviousScene_)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
inline SceneSwitcherEntry(SwitchTargetType targetType_,
|
||||||
|
SceneGroup *group_, OBSWeakSource scene_,
|
||||||
|
OBSWeakSource transition_,
|
||||||
|
bool usePreviousScene_ = false)
|
||||||
|
: targetType(targetType_),
|
||||||
|
group(group_),
|
||||||
|
scene(scene_),
|
||||||
|
transition(transition_),
|
||||||
|
usePreviousScene(usePreviousScene_)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
virtual ~SceneSwitcherEntry() {}
|
virtual ~SceneSwitcherEntry() {}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -30,15 +64,21 @@ class SwitchWidget : public QWidget {
|
|||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
SwitchWidget(SceneSwitcherEntry *s, bool usePreviousScene = true);
|
SwitchWidget(QWidget *parent, SceneSwitcherEntry *s,
|
||||||
|
bool usePreviousScene = true, bool addSceneGroup = false,
|
||||||
|
bool addCurrentTransition = true);
|
||||||
virtual SceneSwitcherEntry *getSwitchData();
|
virtual SceneSwitcherEntry *getSwitchData();
|
||||||
virtual void setSwitchData(SceneSwitcherEntry *s);
|
virtual void setSwitchData(SceneSwitcherEntry *s);
|
||||||
|
void showSwitchData();
|
||||||
|
|
||||||
static void swapSwitchData(SwitchWidget *s1, SwitchWidget *s2);
|
static void swapSwitchData(SwitchWidget *s1, SwitchWidget *s2);
|
||||||
|
|
||||||
private slots:
|
protected slots:
|
||||||
void SceneChanged(const QString &text);
|
void SceneChanged(const QString &text);
|
||||||
void TransitionChanged(const QString &text);
|
void TransitionChanged(const QString &text);
|
||||||
|
void SceneGroupAdd(const QString &name);
|
||||||
|
void SceneGroupRemove(const QString &name);
|
||||||
|
void SceneGroupRename(const QString &oldName, const QString &newName);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
bool loading = true;
|
bool loading = true;
|
||||||
|
|||||||
@@ -12,4 +12,21 @@ struct IdleData : SceneSwitcherEntry {
|
|||||||
bool alreadySwitched = false;
|
bool alreadySwitched = false;
|
||||||
|
|
||||||
const char *getType() { return "idle"; }
|
const char *getType() { return "idle"; }
|
||||||
|
void save(obs_data_t *obj);
|
||||||
|
void load(obs_data_t *obj);
|
||||||
|
};
|
||||||
|
|
||||||
|
class IdleWidget : public SwitchWidget {
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
IdleWidget(QWidget *parent, IdleData *s);
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void DurationChanged(int dur);
|
||||||
|
|
||||||
|
private:
|
||||||
|
QSpinBox *duration;
|
||||||
|
|
||||||
|
IdleData *switchData;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -20,13 +20,13 @@ struct MediaSwitch : SceneSwitcherEntry {
|
|||||||
time_restriction restriction = TIME_RESTRICTION_NONE;
|
time_restriction restriction = TIME_RESTRICTION_NONE;
|
||||||
int64_t time = 0;
|
int64_t time = 0;
|
||||||
|
|
||||||
// trigger scene change only once even if media state might trigger repeatedly
|
// Trigger scene change only once even if media state might trigger repeatedly
|
||||||
bool matched = false;
|
bool matched = false;
|
||||||
|
|
||||||
std::atomic_bool stopped = {false};
|
std::atomic_bool stopped = {false};
|
||||||
std::atomic_bool ended = {false};
|
std::atomic_bool ended = {false};
|
||||||
|
|
||||||
// workaround to enable use of "ended" to specify end of VLC playlist
|
// Workaround to enable use of "ended" to specify end of VLC playlist
|
||||||
bool previousStateEnded = false;
|
bool previousStateEnded = false;
|
||||||
|
|
||||||
bool playedToEnd = false;
|
bool playedToEnd = false;
|
||||||
@@ -34,6 +34,8 @@ struct MediaSwitch : SceneSwitcherEntry {
|
|||||||
const char *getType() { return "media"; }
|
const char *getType() { return "media"; }
|
||||||
bool initialized();
|
bool initialized();
|
||||||
bool valid();
|
bool valid();
|
||||||
|
void save(obs_data_t *obj);
|
||||||
|
void load(obs_data_t *obj);
|
||||||
|
|
||||||
void clearSignalHandler();
|
void clearSignalHandler();
|
||||||
void resetSignalHandler();
|
void resetSignalHandler();
|
||||||
@@ -41,10 +43,7 @@ struct MediaSwitch : SceneSwitcherEntry {
|
|||||||
static void MediaEnded(void *data, calldata_t *);
|
static void MediaEnded(void *data, calldata_t *);
|
||||||
|
|
||||||
inline MediaSwitch(){};
|
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(const MediaSwitch &other);
|
||||||
MediaSwitch(MediaSwitch &&other);
|
MediaSwitch(MediaSwitch &&other);
|
||||||
~MediaSwitch();
|
~MediaSwitch();
|
||||||
@@ -57,7 +56,7 @@ class MediaSwitchWidget : public SwitchWidget {
|
|||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
MediaSwitchWidget(MediaSwitch *s);
|
MediaSwitchWidget(QWidget *parent, MediaSwitch *s);
|
||||||
MediaSwitch *getSwitchData();
|
MediaSwitch *getSwitchData();
|
||||||
void setSwitchData(MediaSwitch *s);
|
void setSwitchData(MediaSwitch *s);
|
||||||
|
|
||||||
|
|||||||
111
src/headers/switch-network.hpp
Normal file
111
src/headers/switch-network.hpp
Normal file
@@ -0,0 +1,111 @@
|
|||||||
|
/*
|
||||||
|
Most of this code is based on https://github.com/Palakis/obs-websocket
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <set>
|
||||||
|
#include <QtCore/QObject>
|
||||||
|
#include <QtCore/QMutex>
|
||||||
|
#include <QtCore/QSharedPointer>
|
||||||
|
#include <QtCore/QVariantHash>
|
||||||
|
#include <QtCore/QThreadPool>
|
||||||
|
#include <mutex>
|
||||||
|
#include <condition_variable>
|
||||||
|
|
||||||
|
#include <websocketpp/config/asio_no_tls_client.hpp>
|
||||||
|
#include <websocketpp/config/asio_no_tls.hpp>
|
||||||
|
#include <websocketpp/server.hpp>
|
||||||
|
#include <websocketpp/client.hpp>
|
||||||
|
|
||||||
|
using websocketpp::connection_hdl;
|
||||||
|
typedef websocketpp::server<websocketpp::config::asio> server;
|
||||||
|
typedef websocketpp::client<websocketpp::config::asio_client> client;
|
||||||
|
|
||||||
|
class NetworkConfig {
|
||||||
|
public:
|
||||||
|
NetworkConfig();
|
||||||
|
void Load(obs_data_t *obj);
|
||||||
|
void Save(obs_data_t *obj);
|
||||||
|
void SetDefaults(obs_data_t *obj);
|
||||||
|
|
||||||
|
std::string GetClientUri();
|
||||||
|
|
||||||
|
// Server
|
||||||
|
bool ServerEnabled;
|
||||||
|
uint64_t ServerPort;
|
||||||
|
bool LockToIPv4;
|
||||||
|
|
||||||
|
// Client
|
||||||
|
bool ClientEnabled;
|
||||||
|
std::string Address;
|
||||||
|
uint64_t ClientPort;
|
||||||
|
bool SendAll;
|
||||||
|
};
|
||||||
|
|
||||||
|
class WSServer : public QObject {
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit WSServer();
|
||||||
|
virtual ~WSServer();
|
||||||
|
void start(quint16 port, bool lockToIPv4);
|
||||||
|
void stop();
|
||||||
|
void sendMessage(OBSWeakSource scene, OBSWeakSource transition);
|
||||||
|
QThreadPool *threadPool() { return &_threadPool; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
void onOpen(connection_hdl hdl);
|
||||||
|
void onMessage(connection_hdl hdl, server::message_ptr message);
|
||||||
|
void onClose(connection_hdl hdl);
|
||||||
|
|
||||||
|
QString getRemoteEndpoint(connection_hdl hdl);
|
||||||
|
|
||||||
|
server _server;
|
||||||
|
quint16 _serverPort;
|
||||||
|
bool _lockToIPv4;
|
||||||
|
std::set<connection_hdl, std::owner_less<connection_hdl>> _connections;
|
||||||
|
QMutex _clMutex;
|
||||||
|
QThreadPool _threadPool;
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class ServerStatus {
|
||||||
|
NOT_RUNNING,
|
||||||
|
STARTING,
|
||||||
|
RUNNING,
|
||||||
|
};
|
||||||
|
|
||||||
|
class WSClient : public QObject {
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit WSClient();
|
||||||
|
virtual ~WSClient();
|
||||||
|
void connect(std::string uri);
|
||||||
|
void disconnect();
|
||||||
|
std::string getFail() { return _failMsg; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
void onOpen(connection_hdl hdl);
|
||||||
|
void onFail(connection_hdl hdl);
|
||||||
|
void onMessage(connection_hdl hdl, client::message_ptr message);
|
||||||
|
void onClose(connection_hdl hdl);
|
||||||
|
void connectThread();
|
||||||
|
|
||||||
|
client _client;
|
||||||
|
std::string _uri;
|
||||||
|
connection_hdl _connection;
|
||||||
|
std::thread _thread;
|
||||||
|
bool _retry = false;
|
||||||
|
std::atomic_bool _connected = {false};
|
||||||
|
std::mutex _waitMtx;
|
||||||
|
std::condition_variable _cv;
|
||||||
|
std::string _failMsg;
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class ClientStatus {
|
||||||
|
DISCONNECTED,
|
||||||
|
CONNECTING,
|
||||||
|
CONNECTED,
|
||||||
|
FAIL,
|
||||||
|
};
|
||||||
@@ -19,6 +19,7 @@ enum class PauseTarget {
|
|||||||
Idle,
|
Idle,
|
||||||
Sequence,
|
Sequence,
|
||||||
Audio,
|
Audio,
|
||||||
|
Video,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct PauseEntry : SceneSwitcherEntry {
|
struct PauseEntry : SceneSwitcherEntry {
|
||||||
@@ -44,7 +45,7 @@ class PauseEntryWidget : public SwitchWidget {
|
|||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
PauseEntryWidget(PauseEntry *s);
|
PauseEntryWidget(QWidget *parent, PauseEntry *s);
|
||||||
PauseEntry *getSwitchData();
|
PauseEntry *getSwitchData();
|
||||||
void setSwitchData(PauseEntry *s);
|
void setSwitchData(PauseEntry *s);
|
||||||
|
|
||||||
|
|||||||
@@ -6,20 +6,15 @@ struct RandomSwitch : SceneSwitcherEntry {
|
|||||||
double delay = 0.0;
|
double delay = 0.0;
|
||||||
|
|
||||||
const char *getType() { return "random"; }
|
const char *getType() { return "random"; }
|
||||||
|
void save(obs_data_t *obj);
|
||||||
inline RandomSwitch() {}
|
void load(obs_data_t *obj);
|
||||||
inline RandomSwitch(OBSWeakSource scene_, OBSWeakSource transition_,
|
|
||||||
double delay_)
|
|
||||||
: SceneSwitcherEntry(scene_, transition_), delay(delay_)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class RandomSwitchWidget : public SwitchWidget {
|
class RandomSwitchWidget : public SwitchWidget {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
RandomSwitchWidget(RandomSwitch *s);
|
RandomSwitchWidget(QWidget *parent, RandomSwitch *s);
|
||||||
RandomSwitch *getSwitchData();
|
RandomSwitch *getSwitchData();
|
||||||
void setSwitchData(RandomSwitch *s);
|
void setSwitchData(RandomSwitch *s);
|
||||||
|
|
||||||
|
|||||||
@@ -7,28 +7,19 @@ constexpr auto default_priority_4 = screen_region_func;
|
|||||||
|
|
||||||
struct ScreenRegionSwitch : SceneSwitcherEntry {
|
struct ScreenRegionSwitch : SceneSwitcherEntry {
|
||||||
static bool pause;
|
static bool pause;
|
||||||
|
OBSWeakSource excludeScene = nullptr;
|
||||||
int minX = 0, minY = 0, maxX = 0, maxY = 0;
|
int minX = 0, minY = 0, maxX = 0, maxY = 0;
|
||||||
|
|
||||||
const char *getType() { return "region"; }
|
const char *getType() { return "region"; }
|
||||||
|
void save(obs_data_t *obj);
|
||||||
inline ScreenRegionSwitch(){};
|
void load(obs_data_t *obj);
|
||||||
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 {
|
class ScreenRegionWidget : public SwitchWidget {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
ScreenRegionWidget(ScreenRegionSwitch *s);
|
ScreenRegionWidget(QWidget *parent, ScreenRegionSwitch *s);
|
||||||
ScreenRegionSwitch *getSwitchData();
|
ScreenRegionSwitch *getSwitchData();
|
||||||
void setSwitchData(ScreenRegionSwitch *s);
|
void setSwitchData(ScreenRegionSwitch *s);
|
||||||
|
|
||||||
@@ -39,12 +30,14 @@ public:
|
|||||||
void hideFrame();
|
void hideFrame();
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
|
void ExcludeSceneChanged(const QString &text);
|
||||||
void MinXChanged(int pos);
|
void MinXChanged(int pos);
|
||||||
void MinYChanged(int pos);
|
void MinYChanged(int pos);
|
||||||
void MaxXChanged(int pos);
|
void MaxXChanged(int pos);
|
||||||
void MaxYChanged(int pos);
|
void MaxYChanged(int pos);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
QComboBox *excludeScenes;
|
||||||
QSpinBox *minX;
|
QSpinBox *minX;
|
||||||
QSpinBox *minY;
|
QSpinBox *minY;
|
||||||
QSpinBox *maxX;
|
QSpinBox *maxX;
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
#include <QVBoxLayout>
|
||||||
|
|
||||||
#include "switch-generic.hpp"
|
#include "switch-generic.hpp"
|
||||||
|
|
||||||
constexpr auto round_trip_func = 1;
|
constexpr auto round_trip_func = 1;
|
||||||
@@ -12,55 +14,77 @@ typedef enum {
|
|||||||
|
|
||||||
struct SceneSequenceSwitch : SceneSwitcherEntry {
|
struct SceneSequenceSwitch : SceneSwitcherEntry {
|
||||||
static bool pause;
|
static bool pause;
|
||||||
|
SwitchTargetType startTargetType = SwitchTargetType::Scene;
|
||||||
OBSWeakSource startScene = nullptr;
|
OBSWeakSource startScene = nullptr;
|
||||||
double delay = 0;
|
double delay = 0;
|
||||||
int delayMultiplier = 1;
|
int delayMultiplier = 1;
|
||||||
bool interruptible = false;
|
bool interruptible = false;
|
||||||
unsigned int matchCount = 0;
|
unsigned int matchCount = 0;
|
||||||
|
|
||||||
|
// nullptr marks start point and reaching end of extended sequence
|
||||||
|
SceneSequenceSwitch *activeSequence = nullptr;
|
||||||
|
|
||||||
|
std::unique_ptr<SceneSequenceSwitch> extendedSequence = nullptr;
|
||||||
|
|
||||||
const char *getType() { return "sequence"; }
|
const char *getType() { return "sequence"; }
|
||||||
bool initialized();
|
bool initialized();
|
||||||
bool valid();
|
bool valid();
|
||||||
void logSleep(int dur);
|
void save(obs_data_t *obj, bool saveExt = true);
|
||||||
|
void load(obs_data_t *obj, bool saveExt = true);
|
||||||
|
|
||||||
inline SceneSequenceSwitch(){};
|
bool reduce();
|
||||||
inline SceneSequenceSwitch(OBSWeakSource startScene_,
|
SceneSequenceSwitch *extend();
|
||||||
OBSWeakSource scene_,
|
|
||||||
OBSWeakSource transition_, double delay_,
|
bool checkMatch(OBSWeakSource currentScene, int &linger,
|
||||||
int delayMultiplier_, bool interruptible_,
|
SceneSequenceSwitch *root = nullptr);
|
||||||
bool usePreviousScene_)
|
bool checkDurationMatchInterruptible();
|
||||||
: SceneSwitcherEntry(scene_, transition_, usePreviousScene_),
|
void prepareUninterruptibleMatch(OBSWeakSource currentScene,
|
||||||
startScene(startScene_),
|
int &linger);
|
||||||
delay(delay_),
|
void advanceActiveSequence();
|
||||||
delayMultiplier(delayMultiplier_),
|
void logAdvanceSequence();
|
||||||
interruptible(interruptible_)
|
void logSequenceCanceled();
|
||||||
{
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class SequenceWidget : public SwitchWidget {
|
class SequenceWidget : public SwitchWidget {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
SequenceWidget(SceneSequenceSwitch *s);
|
SequenceWidget(QWidget *parent, SceneSequenceSwitch *s,
|
||||||
|
bool extendSequence = false, bool editExtendMode = false,
|
||||||
|
bool showExtendText = true);
|
||||||
SceneSequenceSwitch *getSwitchData();
|
SceneSequenceSwitch *getSwitchData();
|
||||||
void setSwitchData(SceneSequenceSwitch *s);
|
void setSwitchData(SceneSequenceSwitch *s);
|
||||||
|
|
||||||
static void swapSwitchData(SequenceWidget *s1, SequenceWidget *s2);
|
static void swapSwitchData(SequenceWidget *s1, SequenceWidget *s2);
|
||||||
|
|
||||||
void UpdateDelay();
|
void UpdateDelay();
|
||||||
|
void UpdateWidgetStatus(bool showExtendText);
|
||||||
|
void setExtendedSequenceStartScene();
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
|
void SceneChanged(const QString &text);
|
||||||
void DelayChanged(double delay);
|
void DelayChanged(double delay);
|
||||||
void DelayUnitsChanged(int idx);
|
void DelayUnitsChanged(int idx);
|
||||||
void StartSceneChanged(const QString &text);
|
void StartSceneChanged(const QString &text);
|
||||||
void InterruptibleChanged(int state);
|
void InterruptibleChanged(int state);
|
||||||
|
void ExtendClicked();
|
||||||
|
void ReduceClicked();
|
||||||
|
|
||||||
private:
|
protected:
|
||||||
QDoubleSpinBox *delay;
|
QDoubleSpinBox *delay;
|
||||||
QComboBox *delayUnits;
|
QComboBox *delayUnits;
|
||||||
QComboBox *startScenes;
|
QComboBox *startScenes;
|
||||||
QCheckBox *interruptible;
|
QCheckBox *interruptible;
|
||||||
|
QVBoxLayout *extendSequenceLayout;
|
||||||
|
QPushButton *extend;
|
||||||
|
QPushButton *reduce;
|
||||||
|
|
||||||
|
// I would prefer having a list of only widgets of type editExtendMode
|
||||||
|
// but I am not sure how to best implement that using a QListWidget.
|
||||||
|
//
|
||||||
|
// So use edit button to bring up edit widget and
|
||||||
|
// add a label to disaplay current extended sequence state.
|
||||||
|
QLabel *extendText;
|
||||||
|
|
||||||
SceneSequenceSwitch *switchData;
|
SceneSequenceSwitch *switchData;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -24,23 +24,15 @@ struct TimeSwitch : SceneSwitcherEntry {
|
|||||||
QTime time = QTime(0, 0);
|
QTime time = QTime(0, 0);
|
||||||
|
|
||||||
const char *getType() { return "time"; }
|
const char *getType() { return "time"; }
|
||||||
|
void save(obs_data_t *obj);
|
||||||
inline TimeSwitch(){};
|
void load(obs_data_t *obj);
|
||||||
inline TimeSwitch(OBSWeakSource scene_, OBSWeakSource transition_,
|
|
||||||
timeTrigger trigger_, QTime time_,
|
|
||||||
bool usePreviousScene_)
|
|
||||||
: SceneSwitcherEntry(scene_, transition_, usePreviousScene_),
|
|
||||||
trigger(trigger_),
|
|
||||||
time(time_)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class TimeSwitchWidget : public SwitchWidget {
|
class TimeSwitchWidget : public SwitchWidget {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
TimeSwitchWidget(TimeSwitch *s);
|
TimeSwitchWidget(QWidget *parent, TimeSwitch *s);
|
||||||
TimeSwitch *getSwitchData();
|
TimeSwitch *getSwitchData();
|
||||||
void setSwitchData(TimeSwitch *s);
|
void setSwitchData(TimeSwitch *s);
|
||||||
|
|
||||||
|
|||||||
@@ -18,8 +18,11 @@ struct SceneTransition : SceneSwitcherEntry {
|
|||||||
|
|
||||||
struct DefaultSceneTransition : SceneSwitcherEntry {
|
struct DefaultSceneTransition : SceneSwitcherEntry {
|
||||||
static bool pause;
|
static bool pause;
|
||||||
|
static unsigned int delay;
|
||||||
|
|
||||||
const char *getType() { return "def_transition"; }
|
const char *getType() { return "def_transition"; }
|
||||||
|
bool checkMatch(OBSWeakSource currentScene);
|
||||||
|
void setTransition();
|
||||||
|
|
||||||
inline DefaultSceneTransition(){};
|
inline DefaultSceneTransition(){};
|
||||||
inline DefaultSceneTransition(OBSWeakSource scene_,
|
inline DefaultSceneTransition(OBSWeakSource scene_,
|
||||||
@@ -33,7 +36,7 @@ class TransitionSwitchWidget : public SwitchWidget {
|
|||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
TransitionSwitchWidget(SceneTransition *s);
|
TransitionSwitchWidget(QWidget *parent, SceneTransition *s);
|
||||||
SceneTransition *getSwitchData();
|
SceneTransition *getSwitchData();
|
||||||
void setSwitchData(SceneTransition *s);
|
void setSwitchData(SceneTransition *s);
|
||||||
|
|
||||||
@@ -53,7 +56,7 @@ class DefTransitionSwitchWidget : public SwitchWidget {
|
|||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
DefTransitionSwitchWidget(DefaultSceneTransition *s);
|
DefTransitionSwitchWidget(QWidget *parent, DefaultSceneTransition *s);
|
||||||
DefaultSceneTransition *getSwitchData();
|
DefaultSceneTransition *getSwitchData();
|
||||||
void setSwitchData(DefaultSceneTransition *s);
|
void setSwitchData(DefaultSceneTransition *s);
|
||||||
|
|
||||||
|
|||||||
98
src/headers/switch-video.hpp
Normal file
98
src/headers/switch-video.hpp
Normal file
@@ -0,0 +1,98 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <QSpinBox>
|
||||||
|
|
||||||
|
#include "switch-generic.hpp"
|
||||||
|
|
||||||
|
constexpr auto video_func = 9;
|
||||||
|
constexpr auto default_priority_9 = video_func;
|
||||||
|
|
||||||
|
enum class videoSwitchType {
|
||||||
|
MATCH,
|
||||||
|
DIFFER,
|
||||||
|
HAS_NOT_CHANGED,
|
||||||
|
};
|
||||||
|
|
||||||
|
class AdvSSScreenshotObj {
|
||||||
|
public:
|
||||||
|
AdvSSScreenshotObj(obs_source_t *source);
|
||||||
|
~AdvSSScreenshotObj();
|
||||||
|
|
||||||
|
void Screenshot();
|
||||||
|
void Download();
|
||||||
|
void Copy();
|
||||||
|
void MarkDone();
|
||||||
|
|
||||||
|
gs_texrender_t *texrender = nullptr;
|
||||||
|
gs_stagesurf_t *stagesurf = nullptr;
|
||||||
|
OBSWeakSource weakSource;
|
||||||
|
std::string path;
|
||||||
|
QImage image;
|
||||||
|
uint32_t cx = 0;
|
||||||
|
uint32_t cy = 0;
|
||||||
|
|
||||||
|
int stage = 0;
|
||||||
|
|
||||||
|
bool done = false;
|
||||||
|
std::chrono::high_resolution_clock::time_point time;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct VideoSwitch : virtual SceneSwitcherEntry {
|
||||||
|
static bool pause;
|
||||||
|
|
||||||
|
videoSwitchType condition = videoSwitchType::MATCH;
|
||||||
|
OBSWeakSource videoSource = nullptr;
|
||||||
|
std::string file = obs_module_text("AdvSceneSwitcher.enterPath");
|
||||||
|
double duration = 0;
|
||||||
|
bool ignoreInactiveSource = false;
|
||||||
|
|
||||||
|
std::unique_ptr<AdvSSScreenshotObj> screenshotData = nullptr;
|
||||||
|
std::chrono::high_resolution_clock::time_point previousTime{};
|
||||||
|
QImage matchImage;
|
||||||
|
|
||||||
|
std::chrono::milliseconds currentMatchDuration{};
|
||||||
|
|
||||||
|
const char *getType() { return "video"; }
|
||||||
|
bool initialized();
|
||||||
|
bool valid();
|
||||||
|
void save(obs_data_t *obj);
|
||||||
|
void load(obs_data_t *obj);
|
||||||
|
void getScreenshot();
|
||||||
|
bool loadImageFromFile();
|
||||||
|
bool checkMatch();
|
||||||
|
|
||||||
|
VideoSwitch(){};
|
||||||
|
friend void swap(VideoSwitch &first, VideoSwitch &second);
|
||||||
|
};
|
||||||
|
|
||||||
|
class VideoSwitchWidget : public SwitchWidget {
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
VideoSwitchWidget(QWidget *parent, VideoSwitch *s);
|
||||||
|
VideoSwitch *getSwitchData();
|
||||||
|
void setSwitchData(VideoSwitch *s);
|
||||||
|
|
||||||
|
static void swapSwitchData(VideoSwitchWidget *as1,
|
||||||
|
VideoSwitchWidget *as2);
|
||||||
|
|
||||||
|
void UpdatePreviewTooltip();
|
||||||
|
void SetFilePath(const QString &text);
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void SourceChanged(const QString &text);
|
||||||
|
void ConditionChanged(int cond);
|
||||||
|
void DurationChanged(double dur);
|
||||||
|
void FilePathChanged();
|
||||||
|
void BrowseButtonClicked();
|
||||||
|
void IgnoreInactiveChanged(int state);
|
||||||
|
|
||||||
|
private:
|
||||||
|
QComboBox *videoSources;
|
||||||
|
QComboBox *condition;
|
||||||
|
QDoubleSpinBox *duration;
|
||||||
|
QLineEdit *filePath;
|
||||||
|
QPushButton *browseButton;
|
||||||
|
QCheckBox *ignoreInactiveSource;
|
||||||
|
|
||||||
|
VideoSwitch *switchData;
|
||||||
|
};
|
||||||
@@ -12,25 +12,15 @@ struct WindowSwitch : SceneSwitcherEntry {
|
|||||||
bool focus = true;
|
bool focus = true;
|
||||||
|
|
||||||
const char *getType() { return "window"; }
|
const char *getType() { return "window"; }
|
||||||
|
void save(obs_data_t *obj);
|
||||||
inline WindowSwitch() {}
|
void load(obs_data_t *obj);
|
||||||
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 {
|
class WindowSwitchWidget : public SwitchWidget {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
WindowSwitchWidget(WindowSwitch *s);
|
WindowSwitchWidget(QWidget *parent, WindowSwitch *s);
|
||||||
WindowSwitch *getSwitchData();
|
WindowSwitch *getSwitchData();
|
||||||
void setSwitchData(WindowSwitch *s);
|
void setSwitchData(WindowSwitch *s);
|
||||||
|
|
||||||
|
|||||||
@@ -7,6 +7,8 @@
|
|||||||
#include <QThread>
|
#include <QThread>
|
||||||
#include <curl/curl.h>
|
#include <curl/curl.h>
|
||||||
|
|
||||||
|
#include "scene-group.hpp"
|
||||||
|
#include "scene-trigger.hpp"
|
||||||
#include "switch-audio.hpp"
|
#include "switch-audio.hpp"
|
||||||
#include "switch-executable.hpp"
|
#include "switch-executable.hpp"
|
||||||
#include "switch-file.hpp"
|
#include "switch-file.hpp"
|
||||||
@@ -19,17 +21,22 @@
|
|||||||
#include "switch-transitions.hpp"
|
#include "switch-transitions.hpp"
|
||||||
#include "switch-window.hpp"
|
#include "switch-window.hpp"
|
||||||
#include "switch-sequence.hpp"
|
#include "switch-sequence.hpp"
|
||||||
|
#include "switch-video.hpp"
|
||||||
|
#include "switch-network.hpp"
|
||||||
|
|
||||||
constexpr auto default_interval = 300;
|
constexpr auto default_interval = 300;
|
||||||
constexpr auto previous_scene_name = "Previous Scene";
|
constexpr auto previous_scene_name = "Previous Scene";
|
||||||
|
constexpr auto current_transition_name = "Current Transition";
|
||||||
|
|
||||||
typedef enum { NO_SWITCH = 0, SWITCH = 1, RANDOM_SWITCH = 2 } NoMatch;
|
typedef enum { NO_SWITCH = 0, SWITCH = 1, RANDOM_SWITCH = 2 } NoMatch;
|
||||||
typedef enum { PERSIST = 0, START = 1, STOP = 2 } StartupBehavior;
|
typedef enum { PERSIST = 0, START = 1, STOP = 2 } StartupBehavior;
|
||||||
typedef enum {
|
|
||||||
RECORDING = 0,
|
enum class AutoStartEvent {
|
||||||
STREAMING = 1,
|
NEVER,
|
||||||
RECORINDGSTREAMING = 2
|
RECORDING,
|
||||||
} AutoStartType;
|
STREAMING,
|
||||||
|
RECORINDG_OR_STREAMING,
|
||||||
|
};
|
||||||
|
|
||||||
typedef struct transitionData {
|
typedef struct transitionData {
|
||||||
std::string name = "";
|
std::string name = "";
|
||||||
@@ -60,13 +67,15 @@ struct SwitcherData {
|
|||||||
|
|
||||||
obs_source_t *waitScene = nullptr;
|
obs_source_t *waitScene = nullptr;
|
||||||
OBSWeakSource previousScene = nullptr;
|
OBSWeakSource previousScene = nullptr;
|
||||||
OBSWeakSource previousScene2 = nullptr;
|
OBSWeakSource previousSceneHelper = nullptr;
|
||||||
OBSWeakSource lastRandomScene;
|
OBSWeakSource lastRandomScene;
|
||||||
|
SceneGroup *lastRandomSceneGroup;
|
||||||
OBSWeakSource nonMatchingScene;
|
OBSWeakSource nonMatchingScene;
|
||||||
NoMatch switchIfNotMatching = NO_SWITCH;
|
NoMatch switchIfNotMatching = NO_SWITCH;
|
||||||
double noMatchDelay;
|
double noMatchDelay = 0;
|
||||||
double noMatchCount = 0;
|
double noMatchCount = 0;
|
||||||
StartupBehavior startupBehavior = PERSIST;
|
StartupBehavior startupBehavior = PERSIST;
|
||||||
|
AutoStartEvent autoStartEvent = AutoStartEvent::NEVER;
|
||||||
|
|
||||||
double cooldown = 0.;
|
double cooldown = 0.;
|
||||||
std::chrono::high_resolution_clock::time_point lastMatchTime;
|
std::chrono::high_resolution_clock::time_point lastMatchTime;
|
||||||
@@ -79,6 +88,7 @@ struct SwitcherData {
|
|||||||
|
|
||||||
std::vector<std::string> ignoreWindowsSwitches;
|
std::vector<std::string> ignoreWindowsSwitches;
|
||||||
|
|
||||||
|
bool uninterruptibleSceneSequenceActive = false;
|
||||||
std::deque<SceneSequenceSwitch> sceneSequenceSwitches;
|
std::deque<SceneSequenceSwitch> sceneSequenceSwitches;
|
||||||
|
|
||||||
std::deque<RandomSwitch> randomSwitches;
|
std::deque<RandomSwitch> randomSwitches;
|
||||||
@@ -91,17 +101,10 @@ struct SwitcherData {
|
|||||||
|
|
||||||
std::deque<ExecutableSwitch> executableSwitches;
|
std::deque<ExecutableSwitch> executableSwitches;
|
||||||
|
|
||||||
bool autoStopEnable = false;
|
std::deque<SceneTrigger> sceneTriggers;
|
||||||
OBSWeakSource autoStopScene;
|
|
||||||
|
|
||||||
bool autoStartEnable = false;
|
|
||||||
AutoStartType autoStartType = RECORDING;
|
|
||||||
OBSWeakSource autoStartScene;
|
|
||||||
bool autoStartedRecently = false;
|
|
||||||
|
|
||||||
std::deque<SceneTransition> sceneTransitions;
|
std::deque<SceneTransition> sceneTransitions;
|
||||||
std::deque<DefaultSceneTransition> defaultSceneTransitions;
|
std::deque<DefaultSceneTransition> defaultSceneTransitions;
|
||||||
bool checkedDefTransition = false;
|
|
||||||
|
|
||||||
std::deque<MediaSwitch> mediaSwitches;
|
std::deque<MediaSwitch> mediaSwitches;
|
||||||
|
|
||||||
@@ -113,10 +116,21 @@ struct SwitcherData {
|
|||||||
std::deque<AudioSwitch> audioSwitches;
|
std::deque<AudioSwitch> audioSwitches;
|
||||||
AudioSwitchFallback audioFallback;
|
AudioSwitchFallback audioFallback;
|
||||||
|
|
||||||
|
WSServer server;
|
||||||
|
ServerStatus serverStatus = ServerStatus::NOT_RUNNING;
|
||||||
|
WSClient client;
|
||||||
|
ClientStatus clientStatus = ClientStatus::DISCONNECTED;
|
||||||
|
NetworkConfig networkConfig;
|
||||||
|
|
||||||
|
std::deque<VideoSwitch> videoSwitches;
|
||||||
|
|
||||||
|
std::deque<SceneGroup> sceneGroups;
|
||||||
|
|
||||||
std::vector<int> functionNamesByPriority = std::vector<int>{
|
std::vector<int> functionNamesByPriority = std::vector<int>{
|
||||||
default_priority_0, default_priority_1, default_priority_2,
|
default_priority_0, default_priority_1, default_priority_2,
|
||||||
default_priority_3, default_priority_4, default_priority_5,
|
default_priority_3, default_priority_4, default_priority_5,
|
||||||
default_priority_6, default_priority_7, default_priority_8};
|
default_priority_6, default_priority_7, default_priority_8,
|
||||||
|
default_priority_9};
|
||||||
|
|
||||||
struct ThreadPrio {
|
struct ThreadPrio {
|
||||||
std::string name;
|
std::string name;
|
||||||
@@ -152,23 +166,26 @@ struct SwitcherData {
|
|||||||
obs_hotkey_id stopHotkey = 0;
|
obs_hotkey_id stopHotkey = 0;
|
||||||
obs_hotkey_id toggleHotkey = 0;
|
obs_hotkey_id toggleHotkey = 0;
|
||||||
|
|
||||||
|
bool versionChanged(obs_data_t *obj, std::string currentVersion);
|
||||||
|
|
||||||
void Thread();
|
void Thread();
|
||||||
void Start();
|
void Start();
|
||||||
void Stop();
|
void Stop();
|
||||||
|
|
||||||
bool sceneChangedDuringWait();
|
bool sceneChangedDuringWait();
|
||||||
|
|
||||||
bool prioFuncsValid();
|
bool prioFuncsValid();
|
||||||
|
|
||||||
|
bool tabOrderValid();
|
||||||
|
void resetTabOrder();
|
||||||
|
|
||||||
void writeSceneInfoToFile();
|
void writeSceneInfoToFile();
|
||||||
void writeToStatusFile(QString status);
|
void writeToStatusFile(QString status);
|
||||||
void autoStopStreamAndRecording();
|
|
||||||
void autoStartStreamRecording();
|
bool checkForMatch(OBSWeakSource &scene, OBSWeakSource &transition,
|
||||||
bool checkPause();
|
int &linger);
|
||||||
void checkDefaultSceneTransitions(bool &match,
|
|
||||||
OBSWeakSource &transition);
|
|
||||||
void setCurrentDefTransition(OBSWeakSource &transition);
|
|
||||||
void checkSceneSequence(bool &match, OBSWeakSource &scene,
|
void checkSceneSequence(bool &match, OBSWeakSource &scene,
|
||||||
OBSWeakSource &transition,
|
OBSWeakSource &transition, int &linger);
|
||||||
std::unique_lock<std::mutex> &lock);
|
|
||||||
void checkIdleSwitch(bool &match, OBSWeakSource &scene,
|
void checkIdleSwitch(bool &match, OBSWeakSource &scene,
|
||||||
OBSWeakSource &transition);
|
OBSWeakSource &transition);
|
||||||
void checkWindowTitleSwitch(bool &match, OBSWeakSource &scene,
|
void checkWindowTitleSwitch(bool &match, OBSWeakSource &scene,
|
||||||
@@ -191,10 +208,16 @@ struct SwitcherData {
|
|||||||
OBSWeakSource &transition);
|
OBSWeakSource &transition);
|
||||||
void checkAudioSwitchFallback(OBSWeakSource &scene,
|
void checkAudioSwitchFallback(OBSWeakSource &scene,
|
||||||
OBSWeakSource &transition);
|
OBSWeakSource &transition);
|
||||||
|
void checkVideoSwitch(bool &match, OBSWeakSource &scene,
|
||||||
|
OBSWeakSource &transition);
|
||||||
void checkNoMatchSwitch(bool &match, OBSWeakSource &scene,
|
void checkNoMatchSwitch(bool &match, OBSWeakSource &scene,
|
||||||
OBSWeakSource &transition, int &sleep);
|
OBSWeakSource &transition, int &sleep);
|
||||||
void checkSwitchCooldown(bool &match);
|
void checkSwitchCooldown(bool &match);
|
||||||
|
void checkTriggers();
|
||||||
|
bool checkPause();
|
||||||
|
void checkDefaultSceneTransitions();
|
||||||
|
|
||||||
|
void saveSettings(obs_data_t *obj);
|
||||||
void saveWindowTitleSwitches(obs_data_t *obj);
|
void saveWindowTitleSwitches(obs_data_t *obj);
|
||||||
void saveScreenRegionSwitches(obs_data_t *obj);
|
void saveScreenRegionSwitches(obs_data_t *obj);
|
||||||
void savePauseSwitches(obs_data_t *obj);
|
void savePauseSwitches(obs_data_t *obj);
|
||||||
@@ -207,9 +230,15 @@ struct SwitcherData {
|
|||||||
void saveMediaSwitches(obs_data_t *obj);
|
void saveMediaSwitches(obs_data_t *obj);
|
||||||
void saveTimeSwitches(obs_data_t *obj);
|
void saveTimeSwitches(obs_data_t *obj);
|
||||||
void saveAudioSwitches(obs_data_t *obj);
|
void saveAudioSwitches(obs_data_t *obj);
|
||||||
|
void saveSceneGroups(obs_data_t *obj);
|
||||||
|
void saveSceneTriggers(obs_data_t *obj);
|
||||||
|
void saveVideoSwitches(obs_data_t *obj);
|
||||||
|
void saveNetworkSwitches(obs_data_t *obj);
|
||||||
void saveGeneralSettings(obs_data_t *obj);
|
void saveGeneralSettings(obs_data_t *obj);
|
||||||
void saveHotkeys(obs_data_t *obj);
|
void saveHotkeys(obs_data_t *obj);
|
||||||
|
void saveVersion(obs_data_t *obj, std::string currentVersion);
|
||||||
|
|
||||||
|
void loadSettings(obs_data_t *obj);
|
||||||
void loadWindowTitleSwitches(obs_data_t *obj);
|
void loadWindowTitleSwitches(obs_data_t *obj);
|
||||||
void loadScreenRegionSwitches(obs_data_t *obj);
|
void loadScreenRegionSwitches(obs_data_t *obj);
|
||||||
void loadPauseSwitches(obs_data_t *obj);
|
void loadPauseSwitches(obs_data_t *obj);
|
||||||
@@ -223,6 +252,10 @@ struct SwitcherData {
|
|||||||
void loadMediaSwitches(obs_data_t *obj);
|
void loadMediaSwitches(obs_data_t *obj);
|
||||||
void loadTimeSwitches(obs_data_t *obj);
|
void loadTimeSwitches(obs_data_t *obj);
|
||||||
void loadAudioSwitches(obs_data_t *obj);
|
void loadAudioSwitches(obs_data_t *obj);
|
||||||
|
void loadSceneGroups(obs_data_t *obj);
|
||||||
|
void loadSceneTriggers(obs_data_t *obj);
|
||||||
|
void loadVideoSwitches(obs_data_t *obj);
|
||||||
|
void loadNetworkSettings(obs_data_t *obj);
|
||||||
void loadGeneralSettings(obs_data_t *obj);
|
void loadGeneralSettings(obs_data_t *obj);
|
||||||
void loadHotkeys(obs_data_t *obj);
|
void loadHotkeys(obs_data_t *obj);
|
||||||
|
|
||||||
|
|||||||
@@ -9,11 +9,20 @@
|
|||||||
#include <obs-module.h>
|
#include <obs-module.h>
|
||||||
#include <obs-frontend-api.h>
|
#include <obs-frontend-api.h>
|
||||||
|
|
||||||
|
static inline bool SceneGroupValid(SceneGroup *group)
|
||||||
|
{
|
||||||
|
if (group) {
|
||||||
|
return group->name != invalid_scene_group_name;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
static inline bool WeakSourceValid(obs_weak_source_t *ws)
|
static inline bool WeakSourceValid(obs_weak_source_t *ws)
|
||||||
{
|
{
|
||||||
obs_source_t *source = obs_weak_source_get_source(ws);
|
obs_source_t *source = obs_weak_source_get_source(ws);
|
||||||
if (source)
|
if (source) {
|
||||||
obs_source_release(source);
|
obs_source_release(source);
|
||||||
|
}
|
||||||
return !!source;
|
return !!source;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -30,6 +39,26 @@ static inline std::string GetWeakSourceName(obs_weak_source_t *weak_source)
|
|||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline SceneGroup *GetSceneGroupByName(const char *name)
|
||||||
|
{
|
||||||
|
if (!switcher) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (SceneGroup &sg : switcher->sceneGroups) {
|
||||||
|
if (sg.name == name) {
|
||||||
|
return &sg;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline SceneGroup *GetSceneGroupByQString(const QString &name)
|
||||||
|
{
|
||||||
|
return GetSceneGroupByName(name.toUtf8().constData());
|
||||||
|
}
|
||||||
|
|
||||||
static inline OBSWeakSource GetWeakSourceByName(const char *name)
|
static inline OBSWeakSource GetWeakSourceByName(const char *name)
|
||||||
{
|
{
|
||||||
OBSWeakSource weak;
|
OBSWeakSource weak;
|
||||||
@@ -104,8 +133,9 @@ getNextDelim(std::string text,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pos == std::string::npos)
|
if (pos == std::string::npos) {
|
||||||
return "";
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
@@ -141,11 +171,14 @@ placeWidgets(std::string text, QBoxLayout *layout,
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (auto &lw : labelsWidgetsPairs) {
|
for (auto &lw : labelsWidgetsPairs) {
|
||||||
if (lw.first != "")
|
if (lw.first != "") {
|
||||||
layout->addWidget(new QLabel(lw.first.c_str()));
|
layout->addWidget(new QLabel(lw.first.c_str()));
|
||||||
if (lw.second)
|
}
|
||||||
|
if (lw.second) {
|
||||||
layout->addWidget(lw.second);
|
layout->addWidget(lw.second);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (addStretch)
|
if (addStretch) {
|
||||||
layout->addStretch();
|
layout->addStretch();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,8 +11,9 @@ void startHotkeyFunc(void *data, obs_hotkey_id id, obs_hotkey_t *hotkey,
|
|||||||
UNUSED_PARAMETER(hotkey);
|
UNUSED_PARAMETER(hotkey);
|
||||||
|
|
||||||
if (pressed) {
|
if (pressed) {
|
||||||
if (!(switcher->th && switcher->th->isRunning()))
|
if (!(switcher->th && switcher->th->isRunning())) {
|
||||||
switcher->Start();
|
switcher->Start();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -24,8 +25,9 @@ void stopHotkeyFunc(void *data, obs_hotkey_id id, obs_hotkey_t *hotkey,
|
|||||||
UNUSED_PARAMETER(hotkey);
|
UNUSED_PARAMETER(hotkey);
|
||||||
|
|
||||||
if (pressed) {
|
if (pressed) {
|
||||||
if (switcher->th && switcher->th->isRunning())
|
if (switcher->th && switcher->th->isRunning()) {
|
||||||
switcher->Stop();
|
switcher->Stop();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -37,10 +39,11 @@ void startStopToggleHotkeyFunc(void *data, obs_hotkey_id id,
|
|||||||
UNUSED_PARAMETER(hotkey);
|
UNUSED_PARAMETER(hotkey);
|
||||||
|
|
||||||
if (pressed) {
|
if (pressed) {
|
||||||
if (switcher->th && switcher->th->isRunning())
|
if (switcher->th && switcher->th->isRunning()) {
|
||||||
switcher->Stop();
|
switcher->Stop();
|
||||||
else
|
} else {
|
||||||
switcher->Start();
|
switcher->Start();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -84,8 +87,9 @@ void SwitcherData::saveHotkeys(obs_data_t *obj)
|
|||||||
|
|
||||||
void SwitcherData::loadHotkeys(obs_data_t *obj)
|
void SwitcherData::loadHotkeys(obs_data_t *obj)
|
||||||
{
|
{
|
||||||
if (!switcher->hotkeysRegistered)
|
if (!switcher->hotkeysRegistered) {
|
||||||
registerHotkeys();
|
registerHotkeys();
|
||||||
|
}
|
||||||
|
|
||||||
obs_data_array_t *startHotkeyArrray =
|
obs_data_array_t *startHotkeyArrray =
|
||||||
obs_data_get_array(obj, "startHotkey");
|
obs_data_get_array(obj, "startHotkey");
|
||||||
|
|||||||
743
src/scene-group.cpp
Normal file
743
src/scene-group.cpp
Normal file
@@ -0,0 +1,743 @@
|
|||||||
|
#include <QVBoxLayout>
|
||||||
|
#include <QDialogButtonBox>
|
||||||
|
#include <random>
|
||||||
|
#include "headers/advanced-scene-switcher.hpp"
|
||||||
|
#include "headers/utility.hpp"
|
||||||
|
|
||||||
|
static QMetaObject::Connection addPulse;
|
||||||
|
SceneGroupEditWidget *typeEdit = nullptr;
|
||||||
|
|
||||||
|
void SceneGroup::advanceIdx()
|
||||||
|
{
|
||||||
|
currentIdx++;
|
||||||
|
|
||||||
|
if (currentIdx >= scenes.size()) {
|
||||||
|
if (repeat) {
|
||||||
|
currentIdx = 0;
|
||||||
|
} else {
|
||||||
|
currentIdx = scenes.size() - 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
OBSWeakSource SceneGroup::getNextSceneCount()
|
||||||
|
{
|
||||||
|
currentCount++;
|
||||||
|
if (currentCount >= count) {
|
||||||
|
advanceIdx();
|
||||||
|
currentCount = 0;
|
||||||
|
}
|
||||||
|
return scenes[currentIdx];
|
||||||
|
}
|
||||||
|
|
||||||
|
OBSWeakSource SceneGroup::getNextSceneTime()
|
||||||
|
{
|
||||||
|
if (lastAdvTime.time_since_epoch().count() == 0) {
|
||||||
|
lastAdvTime = std::chrono::high_resolution_clock::now();
|
||||||
|
}
|
||||||
|
|
||||||
|
auto now = std::chrono::high_resolution_clock::now();
|
||||||
|
auto passedTime = std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||||
|
now - lastAdvTime);
|
||||||
|
|
||||||
|
if (passedTime.count() >= time * 1000) {
|
||||||
|
advanceIdx();
|
||||||
|
lastAdvTime = now;
|
||||||
|
}
|
||||||
|
|
||||||
|
return scenes[currentIdx];
|
||||||
|
}
|
||||||
|
|
||||||
|
OBSWeakSource SceneGroup::getNextSceneRandom()
|
||||||
|
{
|
||||||
|
if (scenes.size() == 1) {
|
||||||
|
currentIdx = 0;
|
||||||
|
return scenes[currentIdx];
|
||||||
|
}
|
||||||
|
|
||||||
|
int rIdx = lastRandomScene;
|
||||||
|
do {
|
||||||
|
rIdx = rand() % scenes.size();
|
||||||
|
} while (rIdx == lastRandomScene);
|
||||||
|
|
||||||
|
lastRandomScene = rIdx;
|
||||||
|
currentIdx = rIdx;
|
||||||
|
|
||||||
|
return scenes[currentIdx];
|
||||||
|
}
|
||||||
|
|
||||||
|
OBSWeakSource SceneGroup::getCurrentScene()
|
||||||
|
{
|
||||||
|
if (scenes.size() < currentIdx + 1) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
return scenes[currentIdx];
|
||||||
|
}
|
||||||
|
|
||||||
|
OBSWeakSource SceneGroup::getNextScene()
|
||||||
|
{
|
||||||
|
if (scenes.empty()) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (type) {
|
||||||
|
case AdvanceCondition::Count:
|
||||||
|
return getNextSceneCount();
|
||||||
|
break;
|
||||||
|
case AdvanceCondition::Time:
|
||||||
|
return getNextSceneTime();
|
||||||
|
break;
|
||||||
|
case AdvanceCondition::Random:
|
||||||
|
return getNextSceneRandom();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
blog(LOG_INFO, "unknown scene group type!");
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool sceneGroupNameExists(std::string name)
|
||||||
|
{
|
||||||
|
obs_source_t *source = obs_get_source_by_name(name.c_str());
|
||||||
|
if (source) {
|
||||||
|
obs_source_release(source);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (SceneGroup &sg : switcher->sceneGroups) {
|
||||||
|
if (sg.name == name) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (name == obs_module_text(
|
||||||
|
"AdvSceneSwitcher.selectPreviousScene") ||
|
||||||
|
(name == invalid_scene_group_name));
|
||||||
|
}
|
||||||
|
|
||||||
|
void AdvSceneSwitcher::on_sceneGroupAdd_clicked()
|
||||||
|
{
|
||||||
|
std::string name;
|
||||||
|
QString format{
|
||||||
|
obs_module_text("AdvSceneSwitcher.sceneGroupTab.defaultname")};
|
||||||
|
|
||||||
|
int i = 1;
|
||||||
|
QString placeHolderText = format.arg(i);
|
||||||
|
while ((sceneGroupNameExists(placeHolderText.toUtf8().constData()))) {
|
||||||
|
placeHolderText = format.arg(++i);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool accepted = SGNameDialog::AskForName(
|
||||||
|
this, obs_module_text("AdvSceneSwitcher.sceneGroupTab.add"),
|
||||||
|
obs_module_text("AdvSceneSwitcher.sceneGroupTab.add"), name,
|
||||||
|
placeHolderText);
|
||||||
|
|
||||||
|
if (!accepted) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (name.empty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sceneGroupNameExists(name)) {
|
||||||
|
DisplayMessage(obs_module_text(
|
||||||
|
"AdvSceneSwitcher.sceneGroupTab.exists"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(switcher->m);
|
||||||
|
switcher->sceneGroups.emplace_back(name);
|
||||||
|
}
|
||||||
|
QString text = QString::fromStdString(name);
|
||||||
|
|
||||||
|
QListWidgetItem *item = new QListWidgetItem(text, ui->sceneGroups);
|
||||||
|
item->setData(Qt::UserRole, text);
|
||||||
|
ui->sceneGroups->setCurrentItem(item);
|
||||||
|
|
||||||
|
ui->sceneGroupAdd->disconnect(addPulse);
|
||||||
|
ui->sceneGroupHelp->setVisible(false);
|
||||||
|
|
||||||
|
emit SceneGroupAdded(QString::fromStdString(name));
|
||||||
|
}
|
||||||
|
|
||||||
|
void AdvSceneSwitcher::on_sceneGroupRemove_clicked()
|
||||||
|
{
|
||||||
|
QListWidgetItem *item = ui->sceneGroups->currentItem();
|
||||||
|
if (!item) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString name;
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(switcher->m);
|
||||||
|
int idx = ui->sceneGroups->currentRow();
|
||||||
|
auto &sg = switcher->sceneGroups;
|
||||||
|
name = QString::fromStdString(sg[idx].name);
|
||||||
|
sg.erase(sg.begin() + idx);
|
||||||
|
}
|
||||||
|
|
||||||
|
delete item;
|
||||||
|
|
||||||
|
emit SceneGroupRemoved(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AdvSceneSwitcher::on_sceneGroupUp_clicked()
|
||||||
|
{
|
||||||
|
int index = ui->sceneGroups->currentRow();
|
||||||
|
if (index != -1 && index != 0) {
|
||||||
|
ui->sceneGroups->insertItem(index - 1,
|
||||||
|
ui->sceneGroups->takeItem(index));
|
||||||
|
ui->sceneGroups->setCurrentRow(index - 1);
|
||||||
|
|
||||||
|
std::lock_guard<std::mutex> lock(switcher->m);
|
||||||
|
|
||||||
|
iter_swap(switcher->sceneGroups.begin() + index,
|
||||||
|
switcher->sceneGroups.begin() + index - 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AdvSceneSwitcher::on_sceneGroupDown_clicked()
|
||||||
|
{
|
||||||
|
int index = ui->sceneGroups->currentRow();
|
||||||
|
if (index != -1 && index != ui->sceneGroups->count() - 1) {
|
||||||
|
ui->sceneGroups->insertItem(index + 1,
|
||||||
|
ui->sceneGroups->takeItem(index));
|
||||||
|
ui->sceneGroups->setCurrentRow(index + 1);
|
||||||
|
|
||||||
|
std::lock_guard<std::mutex> lock(switcher->m);
|
||||||
|
|
||||||
|
iter_swap(switcher->sceneGroups.begin() + index,
|
||||||
|
switcher->sceneGroups.begin() + index + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SceneGroup *getSelectedSG(Ui_AdvSceneSwitcher *ui)
|
||||||
|
{
|
||||||
|
SceneGroup *currentSG = nullptr;
|
||||||
|
QListWidgetItem *sgItem = ui->sceneGroups->currentItem();
|
||||||
|
|
||||||
|
if (!sgItem) {
|
||||||
|
return currentSG;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString sgName = sgItem->data(Qt::UserRole).toString();
|
||||||
|
for (auto &sg : switcher->sceneGroups) {
|
||||||
|
if (sgName.compare(sg.name.c_str()) == 0) {
|
||||||
|
currentSG = &sg;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return currentSG;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AdvSceneSwitcher::on_sceneGroupName_editingFinished()
|
||||||
|
{
|
||||||
|
bool nameValid = true;
|
||||||
|
|
||||||
|
SceneGroup *currentSG = getSelectedSG(ui.get());
|
||||||
|
if (!currentSG) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString newName = ui->sceneGroupName->text();
|
||||||
|
QString oldName = QString::fromStdString(currentSG->name);
|
||||||
|
|
||||||
|
if (newName.isEmpty() || newName == oldName) {
|
||||||
|
nameValid = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nameValid && sceneGroupNameExists(newName.toUtf8().constData())) {
|
||||||
|
DisplayMessage(obs_module_text(
|
||||||
|
"AdvSceneSwitcher.sceneGroupTab.exists"));
|
||||||
|
nameValid = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(switcher->m);
|
||||||
|
if (nameValid) {
|
||||||
|
currentSG->name = newName.toUtf8().constData();
|
||||||
|
QListWidgetItem *sgItem =
|
||||||
|
ui->sceneGroups->currentItem();
|
||||||
|
sgItem->setData(Qt::UserRole, newName);
|
||||||
|
sgItem->setText(newName);
|
||||||
|
} else {
|
||||||
|
ui->sceneGroupName->setText(oldName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
emit SceneGroupRenamed(oldName, newName);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AdvSceneSwitcher::SetEditSceneGroup(SceneGroup &sg)
|
||||||
|
{
|
||||||
|
ui->sceneGroupName->setText(sg.name.c_str());
|
||||||
|
ui->sceneGroupScenes->clear();
|
||||||
|
|
||||||
|
for (auto &s : sg.scenes) {
|
||||||
|
QString sceneName =
|
||||||
|
QString::fromStdString(GetWeakSourceName(s));
|
||||||
|
QVariant v = QVariant::fromValue(sceneName);
|
||||||
|
QListWidgetItem *item =
|
||||||
|
new QListWidgetItem(sceneName, ui->sceneGroupScenes);
|
||||||
|
item->setData(Qt::UserRole, v);
|
||||||
|
}
|
||||||
|
|
||||||
|
ui->sceneGroupEdit->setDisabled(false);
|
||||||
|
typeEdit->SetEditSceneGroup(&sg);
|
||||||
|
|
||||||
|
if (sg.scenes.size() == 0) {
|
||||||
|
ui->sceneGroupScenesHelp->setVisible(true);
|
||||||
|
} else {
|
||||||
|
ui->sceneGroupScenesHelp->setVisible(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AdvSceneSwitcher::on_sceneGroups_currentRowChanged(int idx)
|
||||||
|
{
|
||||||
|
if (loading) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (idx == -1) {
|
||||||
|
ui->sceneGroupEdit->setDisabled(true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QListWidgetItem *item = ui->sceneGroups->item(idx);
|
||||||
|
QString sgName = item->data(Qt::UserRole).toString();
|
||||||
|
|
||||||
|
for (auto &sg : switcher->sceneGroups) {
|
||||||
|
if (sgName.compare(sg.name.c_str()) == 0) {
|
||||||
|
SetEditSceneGroup(sg);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AdvSceneSwitcher::on_sceneGroupSceneAdd_clicked()
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(switcher->m);
|
||||||
|
SceneGroup *currentSG = getSelectedSG(ui.get());
|
||||||
|
if (!currentSG) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString sceneName = ui->sceneGroupSceneSelection->currentText();
|
||||||
|
|
||||||
|
if (sceneName.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
OBSWeakSource source = GetWeakSourceByQString(sceneName);
|
||||||
|
if (!source) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QVariant v = QVariant::fromValue(sceneName);
|
||||||
|
QListWidgetItem *item =
|
||||||
|
new QListWidgetItem(sceneName, ui->sceneGroupScenes);
|
||||||
|
item->setData(Qt::UserRole, v);
|
||||||
|
|
||||||
|
currentSG->scenes.emplace_back(source);
|
||||||
|
|
||||||
|
ui->sceneGroupScenesHelp->setVisible(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AdvSceneSwitcher::on_sceneGroupSceneRemove_clicked()
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(switcher->m);
|
||||||
|
SceneGroup *currentSG = getSelectedSG(ui.get());
|
||||||
|
if (!currentSG) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int idx = ui->sceneGroupScenes->currentRow();
|
||||||
|
if (idx == -1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto &scenes = currentSG->scenes;
|
||||||
|
scenes.erase(scenes.begin() + idx);
|
||||||
|
|
||||||
|
auto item = ui->sceneGroupScenes->currentItem();
|
||||||
|
delete item;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AdvSceneSwitcher::on_sceneGroupSceneUp_clicked()
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(switcher->m);
|
||||||
|
SceneGroup *currentSG = getSelectedSG(ui.get());
|
||||||
|
if (!currentSG) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int index = ui->sceneGroupScenes->currentRow();
|
||||||
|
if (index != -1 && index != 0) {
|
||||||
|
ui->sceneGroupScenes->insertItem(
|
||||||
|
index - 1, ui->sceneGroupScenes->takeItem(index));
|
||||||
|
ui->sceneGroupScenes->setCurrentRow(index - 1);
|
||||||
|
|
||||||
|
iter_swap(currentSG->scenes.begin() + index,
|
||||||
|
currentSG->scenes.begin() + index - 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AdvSceneSwitcher::on_sceneGroupSceneDown_clicked()
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(switcher->m);
|
||||||
|
SceneGroup *currentSG = getSelectedSG(ui.get());
|
||||||
|
if (!currentSG) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int index = ui->sceneGroupScenes->currentRow();
|
||||||
|
if (index != -1 && index != ui->sceneGroupScenes->count() - 1) {
|
||||||
|
ui->sceneGroupScenes->insertItem(
|
||||||
|
index + 1, ui->sceneGroupScenes->takeItem(index));
|
||||||
|
ui->sceneGroupScenes->setCurrentRow(index + 1);
|
||||||
|
|
||||||
|
iter_swap(currentSG->scenes.begin() + index,
|
||||||
|
currentSG->scenes.begin() + index + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SwitcherData::saveSceneGroups(obs_data_t *obj)
|
||||||
|
{
|
||||||
|
obs_data_array_t *sceneGroupArray = obs_data_array_create();
|
||||||
|
for (SceneGroup &sg : switcher->sceneGroups) {
|
||||||
|
obs_data_t *array_obj = obs_data_create();
|
||||||
|
|
||||||
|
obs_data_set_string(array_obj, "name", sg.name.c_str());
|
||||||
|
obs_data_set_int(array_obj, "type", static_cast<int>(sg.type));
|
||||||
|
|
||||||
|
obs_data_array_t *scenesArray = obs_data_array_create();
|
||||||
|
|
||||||
|
for (OBSWeakSource s : sg.scenes) {
|
||||||
|
obs_data_t *sceneArray_obj = obs_data_create();
|
||||||
|
obs_source_t *source = obs_weak_source_get_source(s);
|
||||||
|
|
||||||
|
if (source) {
|
||||||
|
const char *name = obs_source_get_name(source);
|
||||||
|
obs_data_set_string(sceneArray_obj, "scene",
|
||||||
|
name);
|
||||||
|
}
|
||||||
|
|
||||||
|
obs_source_release(source);
|
||||||
|
|
||||||
|
obs_data_array_push_back(scenesArray, sceneArray_obj);
|
||||||
|
obs_data_release(sceneArray_obj);
|
||||||
|
}
|
||||||
|
obs_data_set_array(array_obj, "scenes", scenesArray);
|
||||||
|
obs_data_array_release(scenesArray);
|
||||||
|
|
||||||
|
obs_data_set_int(array_obj, "count", sg.count);
|
||||||
|
obs_data_set_double(array_obj, "time", sg.time);
|
||||||
|
obs_data_set_bool(array_obj, "repeat", sg.repeat);
|
||||||
|
|
||||||
|
obs_data_array_push_back(sceneGroupArray, array_obj);
|
||||||
|
obs_data_release(array_obj);
|
||||||
|
}
|
||||||
|
obs_data_set_array(obj, "sceneGroups", sceneGroupArray);
|
||||||
|
obs_data_array_release(sceneGroupArray);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SwitcherData::loadSceneGroups(obs_data_t *obj)
|
||||||
|
{
|
||||||
|
switcher->sceneGroups.clear();
|
||||||
|
|
||||||
|
obs_data_array_t *sceneGroupArray =
|
||||||
|
obs_data_get_array(obj, "sceneGroups");
|
||||||
|
size_t count = obs_data_array_count(sceneGroupArray);
|
||||||
|
|
||||||
|
for (size_t i = 0; i < count; i++) {
|
||||||
|
obs_data_t *array_obj = obs_data_array_item(sceneGroupArray, i);
|
||||||
|
|
||||||
|
const char *name = obs_data_get_string(array_obj, "name");
|
||||||
|
AdvanceCondition type = static_cast<AdvanceCondition>(
|
||||||
|
obs_data_get_int(array_obj, "type"));
|
||||||
|
|
||||||
|
std::vector<OBSWeakSource> scenes;
|
||||||
|
obs_data_array_t *scenesArray =
|
||||||
|
obs_data_get_array(array_obj, "scenes");
|
||||||
|
size_t scenesCount = obs_data_array_count(scenesArray);
|
||||||
|
for (size_t j = 0; j < scenesCount; j++) {
|
||||||
|
obs_data_t *scenesArray_obj =
|
||||||
|
obs_data_array_item(scenesArray, j);
|
||||||
|
const char *scene =
|
||||||
|
obs_data_get_string(scenesArray_obj, "scene");
|
||||||
|
scenes.emplace_back(GetWeakSourceByName(scene));
|
||||||
|
obs_data_release(scenesArray_obj);
|
||||||
|
}
|
||||||
|
obs_data_array_release(scenesArray);
|
||||||
|
|
||||||
|
int count = obs_data_get_int(array_obj, "count");
|
||||||
|
double time = obs_data_get_double(array_obj, "time");
|
||||||
|
bool repeat = obs_data_get_bool(array_obj, "repeat");
|
||||||
|
|
||||||
|
switcher->sceneGroups.emplace_back(name, type, scenes, count,
|
||||||
|
time, repeat);
|
||||||
|
|
||||||
|
obs_data_release(array_obj);
|
||||||
|
}
|
||||||
|
obs_data_array_release(sceneGroupArray);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AdvSceneSwitcher::setupSceneGroupTab()
|
||||||
|
{
|
||||||
|
populateSceneSelection(ui->sceneGroupSceneSelection);
|
||||||
|
|
||||||
|
for (auto &sg : switcher->sceneGroups) {
|
||||||
|
QString text = QString::fromStdString(sg.name);
|
||||||
|
|
||||||
|
QListWidgetItem *item =
|
||||||
|
new QListWidgetItem(text, ui->sceneGroups);
|
||||||
|
item->setData(Qt::UserRole, text);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (switcher->sceneGroups.size() == 0) {
|
||||||
|
addPulse = PulseWidget(ui->sceneGroupAdd, QColor(Qt::green));
|
||||||
|
ui->sceneGroupHelp->setVisible(true);
|
||||||
|
} else {
|
||||||
|
ui->sceneGroupHelp->setVisible(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
typeEdit = new SceneGroupEditWidget();
|
||||||
|
ui->sceneGroupTypeEdit->addWidget(typeEdit);
|
||||||
|
|
||||||
|
ui->sceneGroupEdit->setDisabled(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
SGNameDialog::SGNameDialog(QWidget *parent) : QDialog(parent)
|
||||||
|
{
|
||||||
|
setModal(true);
|
||||||
|
setWindowModality(Qt::WindowModality::WindowModal);
|
||||||
|
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
|
||||||
|
setFixedWidth(555);
|
||||||
|
setMinimumHeight(100);
|
||||||
|
QVBoxLayout *layout = new QVBoxLayout;
|
||||||
|
setLayout(layout);
|
||||||
|
|
||||||
|
label = new QLabel(this);
|
||||||
|
layout->addWidget(label);
|
||||||
|
label->setText("Set Text");
|
||||||
|
|
||||||
|
userText = new QLineEdit(this);
|
||||||
|
layout->addWidget(userText);
|
||||||
|
|
||||||
|
QDialogButtonBox *buttonbox = new QDialogButtonBox(
|
||||||
|
QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
|
||||||
|
layout->addWidget(buttonbox);
|
||||||
|
buttonbox->setCenterButtons(true);
|
||||||
|
connect(buttonbox, &QDialogButtonBox::accepted, this, &QDialog::accept);
|
||||||
|
connect(buttonbox, &QDialogButtonBox::rejected, this, &QDialog::reject);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool IsWhitespace(char ch)
|
||||||
|
{
|
||||||
|
return ch == ' ' || ch == '\t';
|
||||||
|
}
|
||||||
|
|
||||||
|
static void CleanWhitespace(std::string &str)
|
||||||
|
{
|
||||||
|
while (str.size() && IsWhitespace(str.back()))
|
||||||
|
str.erase(str.end() - 1);
|
||||||
|
while (str.size() && IsWhitespace(str.front()))
|
||||||
|
str.erase(str.begin());
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SGNameDialog::AskForName(QWidget *parent, const QString &title,
|
||||||
|
const QString &text, std::string &userTextInput,
|
||||||
|
const QString &placeHolder, int maxSize)
|
||||||
|
{
|
||||||
|
if (maxSize <= 0 || maxSize > 32767) {
|
||||||
|
maxSize = 170;
|
||||||
|
}
|
||||||
|
|
||||||
|
SGNameDialog dialog(parent);
|
||||||
|
dialog.setWindowTitle(title);
|
||||||
|
|
||||||
|
dialog.label->setText(text);
|
||||||
|
dialog.userText->setMaxLength(maxSize);
|
||||||
|
dialog.userText->setText(placeHolder);
|
||||||
|
dialog.userText->selectAll();
|
||||||
|
|
||||||
|
if (dialog.exec() != DialogCode::Accepted) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
userTextInput = dialog.userText->text().toUtf8().constData();
|
||||||
|
CleanWhitespace(userTextInput);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void populateTypeSelection(QComboBox *list)
|
||||||
|
{
|
||||||
|
list->addItem(
|
||||||
|
obs_module_text("AdvSceneSwitcher.sceneGroupTab.type.count"));
|
||||||
|
list->addItem(
|
||||||
|
obs_module_text("AdvSceneSwitcher.sceneGroupTab.type.time"));
|
||||||
|
list->addItem(
|
||||||
|
obs_module_text("AdvSceneSwitcher.sceneGroupTab.type.random"));
|
||||||
|
}
|
||||||
|
|
||||||
|
SceneGroupEditWidget::SceneGroupEditWidget()
|
||||||
|
{
|
||||||
|
type = new QComboBox();
|
||||||
|
populateTypeSelection(type);
|
||||||
|
|
||||||
|
QWidget::connect(type, SIGNAL(currentIndexChanged(int)), this,
|
||||||
|
SLOT(TypeChanged(int)));
|
||||||
|
|
||||||
|
QHBoxLayout *typeLayout = new QHBoxLayout();
|
||||||
|
typeLayout->setContentsMargins(0, 0, 0, 0);
|
||||||
|
|
||||||
|
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
|
||||||
|
{"{{type}}", type}};
|
||||||
|
placeWidgets(
|
||||||
|
obs_module_text("AdvSceneSwitcher.sceneGroupTab.edit.type"),
|
||||||
|
typeLayout, widgetPlaceholders);
|
||||||
|
|
||||||
|
countEdit = new QWidget();
|
||||||
|
count = new QSpinBox();
|
||||||
|
|
||||||
|
count->setMinimum(1);
|
||||||
|
count->setMaximum(999);
|
||||||
|
|
||||||
|
QWidget::connect(count, SIGNAL(valueChanged(int)), this,
|
||||||
|
SLOT(CountChanged(int)));
|
||||||
|
|
||||||
|
QHBoxLayout *countLayout = new QHBoxLayout(countEdit);
|
||||||
|
countLayout->setContentsMargins(0, 0, 0, 0);
|
||||||
|
|
||||||
|
widgetPlaceholders = {{"{{count}}", count}};
|
||||||
|
placeWidgets(
|
||||||
|
obs_module_text("AdvSceneSwitcher.sceneGroupTab.edit.count"),
|
||||||
|
countLayout, widgetPlaceholders);
|
||||||
|
|
||||||
|
timeEdit = new QWidget();
|
||||||
|
time = new QDoubleSpinBox();
|
||||||
|
|
||||||
|
time->setMinimum(0.0);
|
||||||
|
time->setMaximum(99999999.00);
|
||||||
|
time->setSuffix("s");
|
||||||
|
|
||||||
|
QWidget::connect(time, SIGNAL(valueChanged(double)), this,
|
||||||
|
SLOT(TimeChanged(double)));
|
||||||
|
|
||||||
|
QHBoxLayout *timeLayout = new QHBoxLayout(timeEdit);
|
||||||
|
timeLayout->setContentsMargins(0, 0, 0, 0);
|
||||||
|
|
||||||
|
widgetPlaceholders = {{"{{time}}", time}};
|
||||||
|
placeWidgets(
|
||||||
|
obs_module_text("AdvSceneSwitcher.sceneGroupTab.edit.time"),
|
||||||
|
timeLayout, widgetPlaceholders);
|
||||||
|
|
||||||
|
repeat = new QCheckBox(
|
||||||
|
obs_module_text("AdvSceneSwitcher.sceneGroupTab.edit.repeat"));
|
||||||
|
|
||||||
|
QWidget::connect(repeat, SIGNAL(stateChanged(int)), this,
|
||||||
|
SLOT(RepeatChanged(int)));
|
||||||
|
|
||||||
|
random = new QLabel(
|
||||||
|
obs_module_text("AdvSceneSwitcher.sceneGroupTab.edit.random"));
|
||||||
|
|
||||||
|
QVBoxLayout *mainLayout = new QVBoxLayout;
|
||||||
|
mainLayout->setContentsMargins(0, 0, 0, 0);
|
||||||
|
mainLayout->addLayout(typeLayout);
|
||||||
|
mainLayout->addWidget(countEdit);
|
||||||
|
mainLayout->addWidget(timeEdit);
|
||||||
|
mainLayout->addWidget(repeat);
|
||||||
|
mainLayout->addWidget(random);
|
||||||
|
|
||||||
|
setLayout(mainLayout);
|
||||||
|
|
||||||
|
countEdit->setVisible(false);
|
||||||
|
timeEdit->setVisible(false);
|
||||||
|
repeat->setVisible(false);
|
||||||
|
random->setVisible(false);
|
||||||
|
|
||||||
|
sceneGroup = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SceneGroupEditWidget::ShowCurrentTypeEdit()
|
||||||
|
{
|
||||||
|
if (!sceneGroup) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
countEdit->setVisible(false);
|
||||||
|
timeEdit->setVisible(false);
|
||||||
|
repeat->setVisible(false);
|
||||||
|
random->setVisible(false);
|
||||||
|
|
||||||
|
switch (sceneGroup->type) {
|
||||||
|
case AdvanceCondition::Count:
|
||||||
|
countEdit->setVisible(true);
|
||||||
|
repeat->setVisible(true);
|
||||||
|
break;
|
||||||
|
case AdvanceCondition::Time:
|
||||||
|
timeEdit->setVisible(true);
|
||||||
|
repeat->setVisible(true);
|
||||||
|
break;
|
||||||
|
case AdvanceCondition::Random:
|
||||||
|
random->setVisible(true);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SceneGroupEditWidget::SetEditSceneGroup(SceneGroup *sg)
|
||||||
|
{
|
||||||
|
if (!sg) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
sceneGroup = sg;
|
||||||
|
type->setCurrentIndex(static_cast<int>(sg->type));
|
||||||
|
count->setValue(sg->count);
|
||||||
|
time->setValue(sg->time);
|
||||||
|
repeat->setChecked(sg->repeat);
|
||||||
|
ShowCurrentTypeEdit();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SceneGroupEditWidget::TypeChanged(int type)
|
||||||
|
{
|
||||||
|
if (!sceneGroup) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::lock_guard<std::mutex> lock(switcher->m);
|
||||||
|
sceneGroup->type = static_cast<AdvanceCondition>(type);
|
||||||
|
|
||||||
|
ShowCurrentTypeEdit();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SceneGroupEditWidget::CountChanged(int count)
|
||||||
|
{
|
||||||
|
if (!sceneGroup) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::lock_guard<std::mutex> lock(switcher->m);
|
||||||
|
sceneGroup->count = count;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SceneGroupEditWidget::TimeChanged(double time)
|
||||||
|
{
|
||||||
|
if (!sceneGroup) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::lock_guard<std::mutex> lock(switcher->m);
|
||||||
|
sceneGroup->time = time;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SceneGroupEditWidget::RepeatChanged(int state)
|
||||||
|
{
|
||||||
|
if (!sceneGroup) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::lock_guard<std::mutex> lock(switcher->m);
|
||||||
|
sceneGroup->repeat = state;
|
||||||
|
}
|
||||||
638
src/scene-trigger.cpp
Normal file
638
src/scene-trigger.cpp
Normal file
@@ -0,0 +1,638 @@
|
|||||||
|
#include <thread>
|
||||||
|
|
||||||
|
#include "headers/advanced-scene-switcher.hpp"
|
||||||
|
#include "headers/utility.hpp"
|
||||||
|
|
||||||
|
bool SceneTrigger::pause = false;
|
||||||
|
static QMetaObject::Connection addPulse;
|
||||||
|
|
||||||
|
void AdvSceneSwitcher::on_triggerAdd_clicked()
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(switcher->m);
|
||||||
|
switcher->sceneTriggers.emplace_back();
|
||||||
|
|
||||||
|
listAddClicked(ui->sceneTriggers,
|
||||||
|
new SceneTriggerWidget(this,
|
||||||
|
&switcher->sceneTriggers.back()),
|
||||||
|
ui->triggerAdd, &addPulse);
|
||||||
|
|
||||||
|
ui->triggerHelp->setVisible(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AdvSceneSwitcher::on_triggerRemove_clicked()
|
||||||
|
{
|
||||||
|
QListWidgetItem *item = ui->sceneTriggers->currentItem();
|
||||||
|
if (!item) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(switcher->m);
|
||||||
|
int idx = ui->sceneTriggers->currentRow();
|
||||||
|
auto &switches = switcher->sceneTriggers;
|
||||||
|
switches.erase(switches.begin() + idx);
|
||||||
|
}
|
||||||
|
|
||||||
|
delete item;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AdvSceneSwitcher::on_triggerUp_clicked()
|
||||||
|
{
|
||||||
|
int index = ui->sceneTriggers->currentRow();
|
||||||
|
if (!listMoveUp(ui->sceneTriggers)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
SceneTriggerWidget *s1 =
|
||||||
|
(SceneTriggerWidget *)ui->sceneTriggers->itemWidget(
|
||||||
|
ui->sceneTriggers->item(index));
|
||||||
|
SceneTriggerWidget *s2 =
|
||||||
|
(SceneTriggerWidget *)ui->sceneTriggers->itemWidget(
|
||||||
|
ui->sceneTriggers->item(index - 1));
|
||||||
|
SceneTriggerWidget::swapSwitchData(s1, s2);
|
||||||
|
|
||||||
|
std::lock_guard<std::mutex> lock(switcher->m);
|
||||||
|
|
||||||
|
std::swap(switcher->sceneTriggers[index],
|
||||||
|
switcher->sceneTriggers[index - 1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AdvSceneSwitcher::on_triggerDown_clicked()
|
||||||
|
{
|
||||||
|
int index = ui->sceneTriggers->currentRow();
|
||||||
|
|
||||||
|
if (!listMoveDown(ui->sceneTriggers)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
SceneTriggerWidget *s1 =
|
||||||
|
(SceneTriggerWidget *)ui->sceneTriggers->itemWidget(
|
||||||
|
ui->sceneTriggers->item(index));
|
||||||
|
SceneTriggerWidget *s2 =
|
||||||
|
(SceneTriggerWidget *)ui->sceneTriggers->itemWidget(
|
||||||
|
ui->sceneTriggers->item(index + 1));
|
||||||
|
SceneTriggerWidget::swapSwitchData(s1, s2);
|
||||||
|
|
||||||
|
std::lock_guard<std::mutex> lock(switcher->m);
|
||||||
|
|
||||||
|
std::swap(switcher->sceneTriggers[index],
|
||||||
|
switcher->sceneTriggers[index + 1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SceneTrigger::logMatch()
|
||||||
|
{
|
||||||
|
std::string sceneName = "";
|
||||||
|
std::string statusName = "";
|
||||||
|
std::string actionName = "";
|
||||||
|
|
||||||
|
switch (triggerType) {
|
||||||
|
case sceneTriggerType::NONE:
|
||||||
|
statusName = "NONE";
|
||||||
|
break;
|
||||||
|
case sceneTriggerType::SCENE_ACTIVE:
|
||||||
|
statusName = "SCENE ACTIVE";
|
||||||
|
break;
|
||||||
|
case sceneTriggerType::SCENE_INACTIVE:
|
||||||
|
statusName = "SCENE INACTIVE";
|
||||||
|
break;
|
||||||
|
case sceneTriggerType::SCENE_LEAVE:
|
||||||
|
statusName = "SCENE LEAVE";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (triggerAction) {
|
||||||
|
case sceneTriggerAction::NONE:
|
||||||
|
actionName = "NONE";
|
||||||
|
break;
|
||||||
|
case sceneTriggerAction::START_RECORDING:
|
||||||
|
actionName = "START RECORDING";
|
||||||
|
break;
|
||||||
|
case sceneTriggerAction::PAUSE_RECORDING:
|
||||||
|
actionName = "PAUSE RECORDING";
|
||||||
|
break;
|
||||||
|
case sceneTriggerAction::UNPAUSE_RECORDING:
|
||||||
|
actionName = "UNPAUSE RECORDING";
|
||||||
|
break;
|
||||||
|
case sceneTriggerAction::STOP_RECORDING:
|
||||||
|
actionName = "STOP RECORDING";
|
||||||
|
break;
|
||||||
|
case sceneTriggerAction::START_STREAMING:
|
||||||
|
actionName = "START STREAMING";
|
||||||
|
break;
|
||||||
|
case sceneTriggerAction::STOP_STREAMING:
|
||||||
|
actionName = "STOP STREAMING";
|
||||||
|
break;
|
||||||
|
case sceneTriggerAction::START_REPLAY_BUFFER:
|
||||||
|
actionName = "START REPLAY BUFFER";
|
||||||
|
break;
|
||||||
|
case sceneTriggerAction::STOP_REPLAY_BUFFER:
|
||||||
|
actionName = "STOP REPLAY BUFFER";
|
||||||
|
break;
|
||||||
|
case sceneTriggerAction::MUTE_SOURCE:
|
||||||
|
actionName = "MUTE (" + GetWeakSourceName(audioSource) + ")";
|
||||||
|
break;
|
||||||
|
case sceneTriggerAction::UNMUTE_SOURCE:
|
||||||
|
actionName = "UNMUTE (" + GetWeakSourceName(audioSource) + ")";
|
||||||
|
break;
|
||||||
|
case sceneTriggerAction::START_SWITCHER:
|
||||||
|
actionName = "START SCENE SWITCHER";
|
||||||
|
break;
|
||||||
|
case sceneTriggerAction::STOP_SWITCHER:
|
||||||
|
actionName = "STOP SCENE SWITCHER";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
actionName = "UNKOWN";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
blog(LOG_INFO,
|
||||||
|
"scene '%s' in status '%s' triggering action '%s' after %f seconds",
|
||||||
|
GetWeakSourceName(scene).c_str(), statusName.c_str(),
|
||||||
|
actionName.c_str(), duration);
|
||||||
|
}
|
||||||
|
|
||||||
|
void frontEndActionThread(sceneTriggerAction action, double delay)
|
||||||
|
{
|
||||||
|
long long mil = delay * 1000;
|
||||||
|
std::this_thread::sleep_for(std::chrono::milliseconds(mil));
|
||||||
|
|
||||||
|
switch (action) {
|
||||||
|
case sceneTriggerAction::NONE:
|
||||||
|
break;
|
||||||
|
case sceneTriggerAction::START_RECORDING:
|
||||||
|
obs_frontend_recording_start();
|
||||||
|
break;
|
||||||
|
case sceneTriggerAction::PAUSE_RECORDING:
|
||||||
|
obs_frontend_recording_pause(true);
|
||||||
|
break;
|
||||||
|
case sceneTriggerAction::UNPAUSE_RECORDING:
|
||||||
|
obs_frontend_recording_pause(false);
|
||||||
|
break;
|
||||||
|
case sceneTriggerAction::STOP_RECORDING:
|
||||||
|
obs_frontend_recording_stop();
|
||||||
|
break;
|
||||||
|
case sceneTriggerAction::START_STREAMING:
|
||||||
|
obs_frontend_streaming_start();
|
||||||
|
break;
|
||||||
|
case sceneTriggerAction::STOP_STREAMING:
|
||||||
|
obs_frontend_streaming_stop();
|
||||||
|
break;
|
||||||
|
case sceneTriggerAction::START_REPLAY_BUFFER:
|
||||||
|
obs_frontend_replay_buffer_start();
|
||||||
|
break;
|
||||||
|
case sceneTriggerAction::STOP_REPLAY_BUFFER:
|
||||||
|
obs_frontend_replay_buffer_stop();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
blog(LOG_WARNING, "ignoring unexpected frontend action '%d'",
|
||||||
|
static_cast<int>(action));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void muteThread(OBSWeakSource source, double delay, bool mute)
|
||||||
|
{
|
||||||
|
long long mil = delay * 1000;
|
||||||
|
std::this_thread::sleep_for(std::chrono::milliseconds(mil));
|
||||||
|
|
||||||
|
auto s = obs_weak_source_get_source(source);
|
||||||
|
obs_source_set_muted(s, mute);
|
||||||
|
obs_source_release(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
void statusThread(double delay, bool stop)
|
||||||
|
{
|
||||||
|
long long mil = delay * 1000;
|
||||||
|
std::this_thread::sleep_for(std::chrono::milliseconds(mil));
|
||||||
|
|
||||||
|
if (stop) {
|
||||||
|
switcher->Stop();
|
||||||
|
} else {
|
||||||
|
switcher->Start();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isFrontendAction(sceneTriggerAction triggerAction)
|
||||||
|
{
|
||||||
|
return triggerAction == sceneTriggerAction::START_RECORDING ||
|
||||||
|
triggerAction == sceneTriggerAction::PAUSE_RECORDING ||
|
||||||
|
triggerAction == sceneTriggerAction::UNPAUSE_RECORDING ||
|
||||||
|
triggerAction == sceneTriggerAction::STOP_RECORDING ||
|
||||||
|
triggerAction == sceneTriggerAction::START_STREAMING ||
|
||||||
|
triggerAction == sceneTriggerAction::STOP_STREAMING ||
|
||||||
|
triggerAction == sceneTriggerAction::START_REPLAY_BUFFER ||
|
||||||
|
triggerAction == sceneTriggerAction::STOP_REPLAY_BUFFER;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isAudioAction(sceneTriggerAction t)
|
||||||
|
{
|
||||||
|
return t == sceneTriggerAction::MUTE_SOURCE ||
|
||||||
|
t == sceneTriggerAction::UNMUTE_SOURCE;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isSwitcherStatusAction(sceneTriggerAction t)
|
||||||
|
{
|
||||||
|
return t == sceneTriggerAction::START_SWITCHER ||
|
||||||
|
t == sceneTriggerAction::STOP_SWITCHER;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SceneTrigger::performAction()
|
||||||
|
{
|
||||||
|
if (triggerAction == sceneTriggerAction::NONE) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::thread t;
|
||||||
|
|
||||||
|
if (isFrontendAction(triggerAction)) {
|
||||||
|
t = std::thread(frontEndActionThread, triggerAction, duration);
|
||||||
|
} else if (isAudioAction(triggerAction)) {
|
||||||
|
bool mute = triggerAction == sceneTriggerAction::MUTE_SOURCE;
|
||||||
|
t = std::thread(muteThread, audioSource, duration, mute);
|
||||||
|
} else if (isSwitcherStatusAction(triggerAction)) {
|
||||||
|
bool stop = triggerAction == sceneTriggerAction::STOP_SWITCHER;
|
||||||
|
t = std::thread(statusThread, duration, stop);
|
||||||
|
} else {
|
||||||
|
blog(LOG_WARNING, "ignoring unkown action '%d'",
|
||||||
|
static_cast<int>(triggerAction));
|
||||||
|
}
|
||||||
|
|
||||||
|
t.detach();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SceneTrigger::checkMatch(OBSWeakSource currentScene,
|
||||||
|
OBSWeakSource previousScene)
|
||||||
|
{
|
||||||
|
switch (triggerType) {
|
||||||
|
case sceneTriggerType::NONE:
|
||||||
|
return false;
|
||||||
|
case sceneTriggerType::SCENE_ACTIVE:
|
||||||
|
return currentScene == scene;
|
||||||
|
case sceneTriggerType::SCENE_INACTIVE:
|
||||||
|
return currentScene != scene;
|
||||||
|
case sceneTriggerType::SCENE_LEAVE:
|
||||||
|
return previousScene == scene;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SwitcherData::checkTriggers()
|
||||||
|
{
|
||||||
|
if (SceneTrigger::pause) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
OBSSource source = obs_frontend_get_current_scene();
|
||||||
|
OBSWeakSource currentScene = obs_source_get_weak_source(source);
|
||||||
|
|
||||||
|
for (auto &t : sceneTriggers) {
|
||||||
|
if (stop && !isSwitcherStatusAction(t.triggerAction)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (t.checkMatch(currentScene, previousScene)) {
|
||||||
|
t.logMatch();
|
||||||
|
t.performAction();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
obs_source_release(source);
|
||||||
|
obs_weak_source_release(currentScene);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SwitcherData::saveSceneTriggers(obs_data_t *obj)
|
||||||
|
{
|
||||||
|
obs_data_array_t *triggerArray = obs_data_array_create();
|
||||||
|
for (auto &s : switcher->sceneTriggers) {
|
||||||
|
obs_data_t *array_obj = obs_data_create();
|
||||||
|
|
||||||
|
s.save(array_obj);
|
||||||
|
obs_data_array_push_back(triggerArray, array_obj);
|
||||||
|
|
||||||
|
obs_data_release(array_obj);
|
||||||
|
}
|
||||||
|
obs_data_set_array(obj, "triggers", triggerArray);
|
||||||
|
obs_data_array_release(triggerArray);
|
||||||
|
}
|
||||||
|
|
||||||
|
// To be removed in future version
|
||||||
|
void loadOldAutoStopStart(obs_data_t *obj)
|
||||||
|
{
|
||||||
|
typedef enum {
|
||||||
|
RECORDING = 0,
|
||||||
|
STREAMING = 1,
|
||||||
|
RECORINDGSTREAMING = 2
|
||||||
|
} AutoStartStopType;
|
||||||
|
|
||||||
|
if (obs_data_get_bool(obj, "autoStopEnable")) {
|
||||||
|
std::string autoStopScene =
|
||||||
|
obs_data_get_string(obj, "autoStopSceneName");
|
||||||
|
int action = obs_data_get_int(obj, "autoStopType");
|
||||||
|
|
||||||
|
if (action == RECORDING) {
|
||||||
|
switcher->sceneTriggers.emplace_back();
|
||||||
|
auto &s = switcher->sceneTriggers.back();
|
||||||
|
s.scene = GetWeakSourceByName(autoStopScene.c_str());
|
||||||
|
s.triggerType = sceneTriggerType::SCENE_ACTIVE;
|
||||||
|
s.triggerAction = sceneTriggerAction::STOP_RECORDING;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (action == STREAMING) {
|
||||||
|
switcher->sceneTriggers.emplace_back();
|
||||||
|
auto &s = switcher->sceneTriggers.back();
|
||||||
|
s.scene = GetWeakSourceByName(autoStopScene.c_str());
|
||||||
|
s.triggerType = sceneTriggerType::SCENE_ACTIVE;
|
||||||
|
s.triggerAction = sceneTriggerAction::STOP_STREAMING;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (action == RECORINDGSTREAMING) {
|
||||||
|
switcher->sceneTriggers.emplace_back();
|
||||||
|
auto &s = switcher->sceneTriggers.back();
|
||||||
|
s.scene = GetWeakSourceByName(autoStopScene.c_str());
|
||||||
|
s.triggerType = sceneTriggerType::SCENE_ACTIVE;
|
||||||
|
s.triggerAction = sceneTriggerAction::STOP_RECORDING;
|
||||||
|
|
||||||
|
switcher->sceneTriggers.emplace_back();
|
||||||
|
auto &s2 = switcher->sceneTriggers.back();
|
||||||
|
s2.scene = GetWeakSourceByName(autoStopScene.c_str());
|
||||||
|
s2.triggerType = sceneTriggerType::SCENE_ACTIVE;
|
||||||
|
s2.triggerAction = sceneTriggerAction::STOP_STREAMING;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (obs_data_get_bool(obj, "autoStartEnable")) {
|
||||||
|
std::string autoStartScene =
|
||||||
|
obs_data_get_string(obj, "autoStartSceneName");
|
||||||
|
int action = obs_data_get_int(obj, "autoStartType");
|
||||||
|
|
||||||
|
if (action == RECORDING) {
|
||||||
|
switcher->sceneTriggers.emplace_back();
|
||||||
|
auto &s = switcher->sceneTriggers.back();
|
||||||
|
s.scene = GetWeakSourceByName(autoStartScene.c_str());
|
||||||
|
s.triggerType = sceneTriggerType::SCENE_ACTIVE;
|
||||||
|
s.triggerAction = sceneTriggerAction::START_RECORDING;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (action == STREAMING) {
|
||||||
|
switcher->sceneTriggers.emplace_back();
|
||||||
|
auto &s = switcher->sceneTriggers.back();
|
||||||
|
s.scene = GetWeakSourceByName(autoStartScene.c_str());
|
||||||
|
s.triggerType = sceneTriggerType::SCENE_ACTIVE;
|
||||||
|
s.triggerAction = sceneTriggerAction::START_STREAMING;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (action == RECORINDGSTREAMING) {
|
||||||
|
switcher->sceneTriggers.emplace_back();
|
||||||
|
auto &s = switcher->sceneTriggers.back();
|
||||||
|
s.scene = GetWeakSourceByName(autoStartScene.c_str());
|
||||||
|
s.triggerType = sceneTriggerType::SCENE_ACTIVE;
|
||||||
|
s.triggerAction = sceneTriggerAction::START_RECORDING;
|
||||||
|
|
||||||
|
switcher->sceneTriggers.emplace_back();
|
||||||
|
auto &s2 = switcher->sceneTriggers.back();
|
||||||
|
s2.scene = GetWeakSourceByName(autoStartScene.c_str());
|
||||||
|
s2.triggerType = sceneTriggerType::SCENE_ACTIVE;
|
||||||
|
s2.triggerAction = sceneTriggerAction::START_STREAMING;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SwitcherData::loadSceneTriggers(obs_data_t *obj)
|
||||||
|
{
|
||||||
|
switcher->sceneTriggers.clear();
|
||||||
|
|
||||||
|
loadOldAutoStopStart(obj);
|
||||||
|
|
||||||
|
obs_data_array_t *triggerArray = obs_data_get_array(obj, "triggers");
|
||||||
|
size_t count = obs_data_array_count(triggerArray);
|
||||||
|
|
||||||
|
for (size_t i = 0; i < count; i++) {
|
||||||
|
obs_data_t *array_obj = obs_data_array_item(triggerArray, i);
|
||||||
|
|
||||||
|
switcher->sceneTriggers.emplace_back();
|
||||||
|
sceneTriggers.back().load(array_obj);
|
||||||
|
|
||||||
|
obs_data_release(array_obj);
|
||||||
|
}
|
||||||
|
obs_data_array_release(triggerArray);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AdvSceneSwitcher::setupTriggerTab()
|
||||||
|
{
|
||||||
|
for (auto &s : switcher->sceneTriggers) {
|
||||||
|
QListWidgetItem *item;
|
||||||
|
item = new QListWidgetItem(ui->sceneTriggers);
|
||||||
|
ui->sceneTriggers->addItem(item);
|
||||||
|
SceneTriggerWidget *sw = new SceneTriggerWidget(this, &s);
|
||||||
|
item->setSizeHint(sw->minimumSizeHint());
|
||||||
|
ui->sceneTriggers->setItemWidget(item, sw);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (switcher->sceneTriggers.size() == 0) {
|
||||||
|
addPulse = PulseWidget(ui->triggerAdd, QColor(Qt::green));
|
||||||
|
ui->triggerHelp->setVisible(true);
|
||||||
|
} else {
|
||||||
|
ui->triggerHelp->setVisible(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SceneTrigger::save(obs_data_t *obj)
|
||||||
|
{
|
||||||
|
obs_data_set_string(obj, "scene", GetWeakSourceName(scene).c_str());
|
||||||
|
obs_data_set_int(obj, "triggerType", static_cast<int>(triggerType));
|
||||||
|
obs_data_set_int(obj, "triggerAction", static_cast<int>(triggerAction));
|
||||||
|
obs_data_set_double(obj, "duration", duration);
|
||||||
|
obs_data_set_string(obj, "audioSource",
|
||||||
|
GetWeakSourceName(audioSource).c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
void SceneTrigger::load(obs_data_t *obj)
|
||||||
|
{
|
||||||
|
const char *sceneName = obs_data_get_string(obj, "scene");
|
||||||
|
scene = GetWeakSourceByName(sceneName);
|
||||||
|
|
||||||
|
triggerType = static_cast<sceneTriggerType>(
|
||||||
|
obs_data_get_int(obj, "triggerType"));
|
||||||
|
triggerAction = static_cast<sceneTriggerAction>(
|
||||||
|
obs_data_get_int(obj, "triggerAction"));
|
||||||
|
duration = obs_data_get_double(obj, "duration");
|
||||||
|
|
||||||
|
const char *audioSourceName = obs_data_get_string(obj, "audioSource");
|
||||||
|
audioSource = GetWeakSourceByName(audioSourceName);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void populateTriggers(QComboBox *list)
|
||||||
|
{
|
||||||
|
AdvSceneSwitcher::addSelectionEntry(
|
||||||
|
list,
|
||||||
|
obs_module_text(
|
||||||
|
"AdvSceneSwitcher.sceneTriggerTab.sceneTriggerType.none"));
|
||||||
|
|
||||||
|
list->addItem(obs_module_text(
|
||||||
|
"AdvSceneSwitcher.sceneTriggerTab.sceneTriggerType.sceneActive"));
|
||||||
|
list->addItem(obs_module_text(
|
||||||
|
"AdvSceneSwitcher.sceneTriggerTab.sceneTriggerType.sceneInactive"));
|
||||||
|
list->addItem(obs_module_text(
|
||||||
|
"AdvSceneSwitcher.sceneTriggerTab.sceneTriggerType.sceneLeave"));
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void populateActions(QComboBox *list)
|
||||||
|
{
|
||||||
|
AdvSceneSwitcher::addSelectionEntry(
|
||||||
|
list,
|
||||||
|
obs_module_text(
|
||||||
|
"AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.none"));
|
||||||
|
|
||||||
|
list->addItem(obs_module_text(
|
||||||
|
"AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.startRecording"));
|
||||||
|
list->addItem(obs_module_text(
|
||||||
|
"AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.pauseRecording"));
|
||||||
|
list->addItem(obs_module_text(
|
||||||
|
"AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.unpauseRecording"));
|
||||||
|
list->addItem(obs_module_text(
|
||||||
|
"AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.stopRecording"));
|
||||||
|
list->addItem(obs_module_text(
|
||||||
|
"AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.startStreaming"));
|
||||||
|
list->addItem(obs_module_text(
|
||||||
|
"AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.stopStreaming"));
|
||||||
|
list->addItem(obs_module_text(
|
||||||
|
"AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.startReplayBuffer"));
|
||||||
|
list->addItem(obs_module_text(
|
||||||
|
"AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.stopReplayBuffer"));
|
||||||
|
list->addItem(obs_module_text(
|
||||||
|
"AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.muteSource"));
|
||||||
|
list->addItem(obs_module_text(
|
||||||
|
"AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.unmuteSource"));
|
||||||
|
list->addItem(obs_module_text(
|
||||||
|
"AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.startSwitcher"));
|
||||||
|
list->addItem(obs_module_text(
|
||||||
|
"AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.stopSwitcher"));
|
||||||
|
}
|
||||||
|
|
||||||
|
SceneTriggerWidget::SceneTriggerWidget(QWidget *parent, SceneTrigger *s)
|
||||||
|
: SwitchWidget(parent, s, false, false)
|
||||||
|
{
|
||||||
|
triggers = new QComboBox();
|
||||||
|
actions = new QComboBox();
|
||||||
|
duration = new QDoubleSpinBox();
|
||||||
|
audioSources = new QComboBox();
|
||||||
|
|
||||||
|
duration->setMinimum(0.0);
|
||||||
|
duration->setMaximum(99.000000);
|
||||||
|
duration->setSuffix("s");
|
||||||
|
|
||||||
|
QWidget::connect(triggers, SIGNAL(currentIndexChanged(int)), this,
|
||||||
|
SLOT(TriggerTypeChanged(int)));
|
||||||
|
QWidget::connect(actions, SIGNAL(currentIndexChanged(int)), this,
|
||||||
|
SLOT(TriggerActionChanged(int)));
|
||||||
|
QWidget::connect(duration, SIGNAL(valueChanged(double)), this,
|
||||||
|
SLOT(DurationChanged(double)));
|
||||||
|
QWidget::connect(audioSources,
|
||||||
|
SIGNAL(currentTextChanged(const QString &)), this,
|
||||||
|
SLOT(AudioSourceChanged(const QString &)));
|
||||||
|
|
||||||
|
populateTriggers(triggers);
|
||||||
|
populateActions(actions);
|
||||||
|
AdvSceneSwitcher::populateAudioSelection(audioSources);
|
||||||
|
|
||||||
|
if (s) {
|
||||||
|
triggers->setCurrentIndex(static_cast<int>(s->triggerType));
|
||||||
|
actions->setCurrentIndex(static_cast<int>(s->triggerAction));
|
||||||
|
duration->setValue(s->duration);
|
||||||
|
|
||||||
|
audioSources->setCurrentText(
|
||||||
|
GetWeakSourceName(s->audioSource).c_str());
|
||||||
|
|
||||||
|
if (isAudioAction(s->triggerAction)) {
|
||||||
|
audioSources->show();
|
||||||
|
} else {
|
||||||
|
audioSources->hide();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QHBoxLayout *mainLayout = new QHBoxLayout;
|
||||||
|
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
|
||||||
|
{"{{triggers}}", triggers},
|
||||||
|
{"{{actions}}", actions},
|
||||||
|
{"{{audioSources}}", audioSources},
|
||||||
|
{"{{duration}}", duration},
|
||||||
|
{"{{scenes}}", scenes}};
|
||||||
|
placeWidgets(obs_module_text("AdvSceneSwitcher.sceneTriggerTab.entry"),
|
||||||
|
mainLayout, widgetPlaceholders);
|
||||||
|
setLayout(mainLayout);
|
||||||
|
|
||||||
|
switchData = s;
|
||||||
|
|
||||||
|
loading = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
SceneTrigger *SceneTriggerWidget::getSwitchData()
|
||||||
|
{
|
||||||
|
return switchData;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SceneTriggerWidget::setSwitchData(SceneTrigger *s)
|
||||||
|
{
|
||||||
|
switchData = s;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SceneTriggerWidget::swapSwitchData(SceneTriggerWidget *s1,
|
||||||
|
SceneTriggerWidget *s2)
|
||||||
|
{
|
||||||
|
SwitchWidget::swapSwitchData(s1, s2);
|
||||||
|
|
||||||
|
SceneTrigger *t = s1->getSwitchData();
|
||||||
|
s1->setSwitchData(s2->getSwitchData());
|
||||||
|
s2->setSwitchData(t);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SceneTriggerWidget::TriggerTypeChanged(int index)
|
||||||
|
{
|
||||||
|
if (loading || !switchData) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::lock_guard<std::mutex> lock(switcher->m);
|
||||||
|
switchData->triggerType = static_cast<sceneTriggerType>(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SceneTriggerWidget::TriggerActionChanged(int index)
|
||||||
|
{
|
||||||
|
if (loading || !switchData) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(switcher->m);
|
||||||
|
switchData->triggerAction =
|
||||||
|
static_cast<sceneTriggerAction>(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isAudioAction(switchData->triggerAction)) {
|
||||||
|
audioSources->show();
|
||||||
|
} else {
|
||||||
|
audioSources->hide();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SceneTriggerWidget::DurationChanged(double dur)
|
||||||
|
{
|
||||||
|
if (loading || !switchData) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::lock_guard<std::mutex> lock(switcher->m);
|
||||||
|
switchData->duration = dur;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SceneTriggerWidget::AudioSourceChanged(const QString &text)
|
||||||
|
{
|
||||||
|
if (loading || !switchData) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::lock_guard<std::mutex> lock(switcher->m);
|
||||||
|
switchData->audioSource = GetWeakSourceByQString(text);
|
||||||
|
}
|
||||||
@@ -12,16 +12,20 @@ void AdvSceneSwitcher::on_audioAdd_clicked()
|
|||||||
std::lock_guard<std::mutex> lock(switcher->m);
|
std::lock_guard<std::mutex> lock(switcher->m);
|
||||||
switcher->audioSwitches.emplace_back();
|
switcher->audioSwitches.emplace_back();
|
||||||
|
|
||||||
listAddClicked(ui->audioSwitches,
|
AudioSwitchWidget *sw =
|
||||||
new AudioSwitchWidget(&switcher->audioSwitches.back()),
|
new AudioSwitchWidget(this, &switcher->audioSwitches.back());
|
||||||
ui->audioAdd, &addPulse);
|
|
||||||
|
listAddClicked(ui->audioSwitches, sw, ui->audioAdd, &addPulse);
|
||||||
|
|
||||||
|
ui->audioHelp->setVisible(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AdvSceneSwitcher::on_audioRemove_clicked()
|
void AdvSceneSwitcher::on_audioRemove_clicked()
|
||||||
{
|
{
|
||||||
QListWidgetItem *item = ui->audioSwitches->currentItem();
|
QListWidgetItem *item = ui->audioSwitches->currentItem();
|
||||||
if (!item)
|
if (!item) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> lock(switcher->m);
|
std::lock_guard<std::mutex> lock(switcher->m);
|
||||||
@@ -36,8 +40,9 @@ void AdvSceneSwitcher::on_audioRemove_clicked()
|
|||||||
void AdvSceneSwitcher::on_audioUp_clicked()
|
void AdvSceneSwitcher::on_audioUp_clicked()
|
||||||
{
|
{
|
||||||
int index = ui->audioSwitches->currentRow();
|
int index = ui->audioSwitches->currentRow();
|
||||||
if (!listMoveUp(ui->audioSwitches))
|
if (!listMoveUp(ui->audioSwitches)) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
AudioSwitchWidget *s1 =
|
AudioSwitchWidget *s1 =
|
||||||
(AudioSwitchWidget *)ui->audioSwitches->itemWidget(
|
(AudioSwitchWidget *)ui->audioSwitches->itemWidget(
|
||||||
@@ -57,8 +62,9 @@ void AdvSceneSwitcher::on_audioDown_clicked()
|
|||||||
{
|
{
|
||||||
int index = ui->audioSwitches->currentRow();
|
int index = ui->audioSwitches->currentRow();
|
||||||
|
|
||||||
if (!listMoveDown(ui->audioSwitches))
|
if (!listMoveDown(ui->audioSwitches)) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
AudioSwitchWidget *s1 =
|
AudioSwitchWidget *s1 =
|
||||||
(AudioSwitchWidget *)ui->audioSwitches->itemWidget(
|
(AudioSwitchWidget *)ui->audioSwitches->itemWidget(
|
||||||
@@ -76,8 +82,10 @@ void AdvSceneSwitcher::on_audioDown_clicked()
|
|||||||
|
|
||||||
void AdvSceneSwitcher::on_audioFallback_toggled(bool on)
|
void AdvSceneSwitcher::on_audioFallback_toggled(bool on)
|
||||||
{
|
{
|
||||||
if (loading || !switcher)
|
if (loading || !switcher) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
std::lock_guard<std::mutex> lock(switcher->m);
|
std::lock_guard<std::mutex> lock(switcher->m);
|
||||||
switcher->audioFallback.enable = on;
|
switcher->audioFallback.enable = on;
|
||||||
}
|
}
|
||||||
@@ -92,12 +100,12 @@ void SwitcherData::checkAudioSwitchFallback(OBSWeakSource &scene,
|
|||||||
|
|
||||||
if (durationReached) {
|
if (durationReached) {
|
||||||
|
|
||||||
scene = (audioFallback.usePreviousScene) ? previousScene
|
scene = audioFallback.getScene();
|
||||||
: audioFallback.scene;
|
|
||||||
transition = audioFallback.transition;
|
transition = audioFallback.transition;
|
||||||
|
|
||||||
if (verbose)
|
if (verbose) {
|
||||||
audioFallback.logMatch();
|
audioFallback.logMatch();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
audioFallback.matchCount++;
|
audioFallback.matchCount++;
|
||||||
@@ -106,30 +114,40 @@ void SwitcherData::checkAudioSwitchFallback(OBSWeakSource &scene,
|
|||||||
void SwitcherData::checkAudioSwitch(bool &match, OBSWeakSource &scene,
|
void SwitcherData::checkAudioSwitch(bool &match, OBSWeakSource &scene,
|
||||||
OBSWeakSource &transition)
|
OBSWeakSource &transition)
|
||||||
{
|
{
|
||||||
if (AudioSwitch::pause)
|
if (AudioSwitch::pause) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
bool fallbackChecked = false; // false if one or no match
|
bool fallbackChecked = false; // false if only one or no match
|
||||||
|
|
||||||
for (AudioSwitch &s : audioSwitches) {
|
for (AudioSwitch &s : audioSwitches) {
|
||||||
if (!s.initialized())
|
if (!s.initialized()) {
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
obs_source_t *as = obs_weak_source_get_source(s.audioSource);
|
if (s.ignoreInactiveSource) {
|
||||||
bool audioActive = obs_source_active(as);
|
obs_source_t *as =
|
||||||
obs_source_release(as);
|
obs_weak_source_get_source(s.audioSource);
|
||||||
|
bool audioActive = obs_source_active(as);
|
||||||
|
obs_source_release(as);
|
||||||
|
|
||||||
|
if (!audioActive) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// peak will have a value from -60 db to 0 db
|
// peak will have a value from -60 db to 0 db
|
||||||
bool volumeThresholdreached = false;
|
bool volumeThresholdreached = false;
|
||||||
|
|
||||||
if (s.condition == ABOVE)
|
if (s.condition == ABOVE) {
|
||||||
volumeThresholdreached = ((double)s.peak + 60) * 1.7 >
|
volumeThresholdreached = ((double)s.peak + 60) * 1.7 >
|
||||||
s.volumeThreshold;
|
s.volumeThreshold;
|
||||||
else
|
} else {
|
||||||
volumeThresholdreached = ((double)s.peak + 60) * 1.7 <
|
volumeThresholdreached = ((double)s.peak + 60) * 1.7 <
|
||||||
s.volumeThreshold;
|
s.volumeThreshold;
|
||||||
|
}
|
||||||
|
|
||||||
// reset for next check
|
// Reset for next check
|
||||||
s.peak = -FLT_MAX;
|
s.peak = -FLT_MAX;
|
||||||
|
|
||||||
if (volumeThresholdreached) {
|
if (volumeThresholdreached) {
|
||||||
@@ -143,52 +161,30 @@ void SwitcherData::checkAudioSwitch(bool &match, OBSWeakSource &scene,
|
|||||||
1000.0 >=
|
1000.0 >=
|
||||||
s.duration;
|
s.duration;
|
||||||
|
|
||||||
if (volumeThresholdreached && durationReached && audioActive) {
|
if (volumeThresholdreached && durationReached) {
|
||||||
if (match) {
|
if (match) {
|
||||||
checkAudioSwitchFallback(scene, transition);
|
checkAudioSwitchFallback(scene, transition);
|
||||||
fallbackChecked = true;
|
fallbackChecked = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
scene = (s.usePreviousScene) ? previousScene : s.scene;
|
scene = s.getScene();
|
||||||
transition = s.transition;
|
transition = s.transition;
|
||||||
match = true;
|
match = true;
|
||||||
|
|
||||||
if (verbose)
|
if (verbose) {
|
||||||
s.logMatch();
|
s.logMatch();
|
||||||
|
}
|
||||||
|
|
||||||
if (!audioFallback.enable)
|
if (!audioFallback.enable) {
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!fallbackChecked)
|
if (!fallbackChecked) {
|
||||||
audioFallback.matchCount = 0;
|
audioFallback.matchCount = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void saveAudioFallback(obs_data_t *obj, AudioSwitchFallback &audioFallback)
|
|
||||||
{
|
|
||||||
obs_source_t *fallbackSceneSource =
|
|
||||||
obs_weak_source_get_source(audioFallback.scene);
|
|
||||||
obs_source_t *fallbackTransition =
|
|
||||||
obs_weak_source_get_source(audioFallback.transition);
|
|
||||||
|
|
||||||
const char *fallbackSceneName =
|
|
||||||
obs_source_get_name(fallbackSceneSource);
|
|
||||||
const char *fallbackTransitionName =
|
|
||||||
obs_source_get_name(fallbackTransition);
|
|
||||||
|
|
||||||
obs_data_set_bool(obj, "audioFallbackEnable", audioFallback.enable);
|
|
||||||
obs_data_set_string(obj, "audioFallbackScene",
|
|
||||||
audioFallback.usePreviousScene ? previous_scene_name
|
|
||||||
: fallbackSceneName);
|
|
||||||
obs_data_set_string(obj, "audioFallbackTransition",
|
|
||||||
fallbackTransitionName);
|
|
||||||
obs_data_set_double(obj, "audioFallbackDuration",
|
|
||||||
audioFallback.duration);
|
|
||||||
|
|
||||||
obs_source_release(fallbackSceneSource);
|
|
||||||
obs_source_release(fallbackTransition);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void SwitcherData::saveAudioSwitches(obs_data_t *obj)
|
void SwitcherData::saveAudioSwitches(obs_data_t *obj)
|
||||||
@@ -197,60 +193,15 @@ void SwitcherData::saveAudioSwitches(obs_data_t *obj)
|
|||||||
for (AudioSwitch &s : switcher->audioSwitches) {
|
for (AudioSwitch &s : switcher->audioSwitches) {
|
||||||
obs_data_t *array_obj = obs_data_create();
|
obs_data_t *array_obj = obs_data_create();
|
||||||
|
|
||||||
obs_source_t *sceneSource = obs_weak_source_get_source(s.scene);
|
s.save(array_obj);
|
||||||
obs_source_t *transition =
|
obs_data_array_push_back(audioArray, array_obj);
|
||||||
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_set_int(array_obj, "condition", s.condition);
|
|
||||||
obs_data_set_double(array_obj, "duration", s.duration);
|
|
||||||
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_release(array_obj);
|
||||||
}
|
}
|
||||||
obs_data_set_array(obj, "audioSwitches", audioArray);
|
obs_data_set_array(obj, "audioSwitches", audioArray);
|
||||||
obs_data_array_release(audioArray);
|
obs_data_array_release(audioArray);
|
||||||
|
|
||||||
saveAudioFallback(obj, audioFallback);
|
audioFallback.save(obj);
|
||||||
}
|
|
||||||
|
|
||||||
void loadAudioFallback(obs_data_t *obj, AudioSwitchFallback &audioFallback)
|
|
||||||
{
|
|
||||||
const char *fallbackSceneName =
|
|
||||||
obs_data_get_string(obj, "audioFallbackScene");
|
|
||||||
const char *fallbackTransitionName =
|
|
||||||
obs_data_get_string(obj, "audioFallbackTransition");
|
|
||||||
|
|
||||||
audioFallback.enable = obs_data_get_bool(obj, "audioFallbackEnable");
|
|
||||||
audioFallback.duration =
|
|
||||||
obs_data_get_double(obj, "audioFallbackDuration");
|
|
||||||
audioFallback.scene = GetWeakSourceByName(fallbackSceneName);
|
|
||||||
audioFallback.transition =
|
|
||||||
GetWeakTransitionByName(fallbackTransitionName);
|
|
||||||
audioFallback.usePreviousScene =
|
|
||||||
strcmp(fallbackSceneName, previous_scene_name) == 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void SwitcherData::loadAudioSwitches(obs_data_t *obj)
|
void SwitcherData::loadAudioSwitches(obs_data_t *obj)
|
||||||
@@ -263,27 +214,14 @@ void SwitcherData::loadAudioSwitches(obs_data_t *obj)
|
|||||||
for (size_t i = 0; i < count; i++) {
|
for (size_t i = 0; i < count; i++) {
|
||||||
obs_data_t *array_obj = obs_data_array_item(audioArray, i);
|
obs_data_t *array_obj = obs_data_array_item(audioArray, i);
|
||||||
|
|
||||||
const char *scene = obs_data_get_string(array_obj, "scene");
|
switcher->audioSwitches.emplace_back();
|
||||||
const char *transition =
|
audioSwitches.back().load(array_obj);
|
||||||
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");
|
|
||||||
audioCondition condition = (audioCondition)obs_data_get_int(
|
|
||||||
array_obj, "condition");
|
|
||||||
double duration = obs_data_get_double(array_obj, "duration");
|
|
||||||
|
|
||||||
switcher->audioSwitches.emplace_back(
|
|
||||||
GetWeakSourceByName(scene),
|
|
||||||
GetWeakTransitionByName(transition),
|
|
||||||
GetWeakSourceByName(audioSource), vol, condition,
|
|
||||||
duration, (strcmp(scene, previous_scene_name) == 0));
|
|
||||||
|
|
||||||
obs_data_release(array_obj);
|
obs_data_release(array_obj);
|
||||||
}
|
}
|
||||||
obs_data_array_release(audioArray);
|
obs_data_array_release(audioArray);
|
||||||
|
|
||||||
loadAudioFallback(obj, audioFallback);
|
audioFallback.load(obj);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AdvSceneSwitcher::setupAudioTab()
|
void AdvSceneSwitcher::setupAudioTab()
|
||||||
@@ -292,16 +230,20 @@ void AdvSceneSwitcher::setupAudioTab()
|
|||||||
QListWidgetItem *item;
|
QListWidgetItem *item;
|
||||||
item = new QListWidgetItem(ui->audioSwitches);
|
item = new QListWidgetItem(ui->audioSwitches);
|
||||||
ui->audioSwitches->addItem(item);
|
ui->audioSwitches->addItem(item);
|
||||||
AudioSwitchWidget *sw = new AudioSwitchWidget(&s);
|
AudioSwitchWidget *sw = new AudioSwitchWidget(this, &s);
|
||||||
item->setSizeHint(sw->minimumSizeHint());
|
item->setSizeHint(sw->minimumSizeHint());
|
||||||
ui->audioSwitches->setItemWidget(item, sw);
|
ui->audioSwitches->setItemWidget(item, sw);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (switcher->audioSwitches.size() == 0)
|
if (switcher->audioSwitches.size() == 0) {
|
||||||
addPulse = PulseWidget(ui->audioAdd, QColor(Qt::green));
|
addPulse = PulseWidget(ui->audioAdd, QColor(Qt::green));
|
||||||
|
ui->audioHelp->setVisible(true);
|
||||||
|
} else {
|
||||||
|
ui->audioHelp->setVisible(false);
|
||||||
|
}
|
||||||
|
|
||||||
AudioSwitchFallbackWidget *fb =
|
AudioSwitchFallbackWidget *fb =
|
||||||
new AudioSwitchFallbackWidget(&switcher->audioFallback);
|
new AudioSwitchFallbackWidget(this, &switcher->audioFallback);
|
||||||
ui->audioFallbackLayout->addWidget(fb);
|
ui->audioFallbackLayout->addWidget(fb);
|
||||||
ui->audioFallback->setChecked(switcher->audioFallback.enable);
|
ui->audioFallback->setChecked(switcher->audioFallback.enable);
|
||||||
}
|
}
|
||||||
@@ -315,9 +257,11 @@ void AudioSwitch::setVolumeLevel(void *data,
|
|||||||
UNUSED_PARAMETER(inputPeak);
|
UNUSED_PARAMETER(inputPeak);
|
||||||
AudioSwitch *s = static_cast<AudioSwitch *>(data);
|
AudioSwitch *s = static_cast<AudioSwitch *>(data);
|
||||||
|
|
||||||
for (int i = 1; i < MAX_AUDIO_CHANNELS; i++)
|
for (int i = 0; i < MAX_AUDIO_CHANNELS; i++) {
|
||||||
if (peak[i] > s->peak)
|
if (peak[i] > s->peak) {
|
||||||
s->peak = peak[i];
|
s->peak = peak[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
obs_volmeter_t *AddVolmeterToSource(AudioSwitch *entry, obs_weak_source *source)
|
obs_volmeter_t *AddVolmeterToSource(AudioSwitch *entry, obs_weak_source *source)
|
||||||
@@ -354,22 +298,90 @@ bool AudioSwitch::valid()
|
|||||||
(SceneSwitcherEntry::valid() && WeakSourceValid(audioSource));
|
(SceneSwitcherEntry::valid() && WeakSourceValid(audioSource));
|
||||||
}
|
}
|
||||||
|
|
||||||
AudioSwitch::AudioSwitch(OBSWeakSource scene_, OBSWeakSource transition_,
|
void AudioSwitch::save(obs_data_t *obj)
|
||||||
OBSWeakSource audioSource_, int volumeThreshold_,
|
|
||||||
audioCondition condition_, double duration_,
|
|
||||||
bool usePreviousScene_)
|
|
||||||
: SceneSwitcherEntry(scene_, transition_, usePreviousScene_),
|
|
||||||
audioSource(audioSource_),
|
|
||||||
volumeThreshold(volumeThreshold_),
|
|
||||||
condition(condition_),
|
|
||||||
duration(duration_)
|
|
||||||
{
|
{
|
||||||
|
SceneSwitcherEntry::save(obj);
|
||||||
|
|
||||||
|
obs_data_set_string(obj, "audioSource",
|
||||||
|
GetWeakSourceName(audioSource).c_str());
|
||||||
|
|
||||||
|
obs_data_set_int(obj, "volume", volumeThreshold);
|
||||||
|
obs_data_set_int(obj, "condition", condition);
|
||||||
|
obs_data_set_double(obj, "duration", duration);
|
||||||
|
obs_data_set_bool(obj, "ignoreInactiveSource", ignoreInactiveSource);
|
||||||
|
}
|
||||||
|
|
||||||
|
// To be removed in future version
|
||||||
|
bool loadOldAudio(obs_data_t *obj, AudioSwitch *s)
|
||||||
|
{
|
||||||
|
if (!s) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *scene = obs_data_get_string(obj, "scene");
|
||||||
|
|
||||||
|
if (strcmp(scene, "") == 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
s->scene = GetWeakSourceByName(scene);
|
||||||
|
|
||||||
|
const char *transition = obs_data_get_string(obj, "transition");
|
||||||
|
s->transition = GetWeakTransitionByName(transition);
|
||||||
|
|
||||||
|
const char *audioSource = obs_data_get_string(obj, "audioSource");
|
||||||
|
s->audioSource = GetWeakSourceByName(audioSource);
|
||||||
|
|
||||||
|
s->volumeThreshold = obs_data_get_int(obj, "volume");
|
||||||
|
s->condition = (audioCondition)obs_data_get_int(obj, "condition");
|
||||||
|
s->duration = obs_data_get_double(obj, "duration");
|
||||||
|
s->usePreviousScene = strcmp(scene, previous_scene_name) == 0;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AudioSwitch::load(obs_data_t *obj)
|
||||||
|
{
|
||||||
|
if (loadOldAudio(obj, this)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
SceneSwitcherEntry::load(obj);
|
||||||
|
|
||||||
|
const char *audioSourceName = obs_data_get_string(obj, "audioSource");
|
||||||
|
audioSource = GetWeakSourceByName(audioSourceName);
|
||||||
|
|
||||||
|
volumeThreshold = obs_data_get_int(obj, "volume");
|
||||||
|
condition = (audioCondition)obs_data_get_int(obj, "condition");
|
||||||
|
duration = obs_data_get_double(obj, "duration");
|
||||||
|
ignoreInactiveSource = obs_data_get_bool(obj, "ignoreInactiveSource");
|
||||||
|
|
||||||
volmeter = AddVolmeterToSource(this, audioSource);
|
volmeter = AddVolmeterToSource(this, audioSource);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void AudioSwitchFallback::save(obs_data_t *obj)
|
||||||
|
{
|
||||||
|
SceneSwitcherEntry::save(obj, "audioFallbackTargetType",
|
||||||
|
"audioFallbackScene",
|
||||||
|
"audioFallbackTransition");
|
||||||
|
|
||||||
|
obs_data_set_bool(obj, "audioFallbackEnable", enable);
|
||||||
|
obs_data_set_double(obj, "audioFallbackDuration", duration);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AudioSwitchFallback::load(obs_data_t *obj)
|
||||||
|
{
|
||||||
|
SceneSwitcherEntry::load(obj, "audioFallbackTargetType",
|
||||||
|
"audioFallbackScene",
|
||||||
|
"audioFallbackTransition");
|
||||||
|
|
||||||
|
enable = obs_data_get_bool(obj, "audioFallbackEnable");
|
||||||
|
duration = obs_data_get_double(obj, "audioFallbackDuration");
|
||||||
|
}
|
||||||
|
|
||||||
AudioSwitch::AudioSwitch(const AudioSwitch &other)
|
AudioSwitch::AudioSwitch(const AudioSwitch &other)
|
||||||
: SceneSwitcherEntry(other.scene, other.transition,
|
: SceneSwitcherEntry(other.targetType, other.group, other.scene,
|
||||||
other.usePreviousScene),
|
other.transition, other.usePreviousScene),
|
||||||
audioSource(other.audioSource),
|
audioSource(other.audioSource),
|
||||||
volumeThreshold(other.volumeThreshold),
|
volumeThreshold(other.volumeThreshold),
|
||||||
condition(other.condition),
|
condition(other.condition),
|
||||||
@@ -379,8 +391,8 @@ AudioSwitch::AudioSwitch(const AudioSwitch &other)
|
|||||||
}
|
}
|
||||||
|
|
||||||
AudioSwitch::AudioSwitch(AudioSwitch &&other)
|
AudioSwitch::AudioSwitch(AudioSwitch &&other)
|
||||||
: SceneSwitcherEntry(other.scene, other.transition,
|
: SceneSwitcherEntry(other.targetType, other.group, other.scene,
|
||||||
other.usePreviousScene),
|
other.transition, other.usePreviousScene),
|
||||||
audioSource(other.audioSource),
|
audioSource(other.audioSource),
|
||||||
volumeThreshold(other.volumeThreshold),
|
volumeThreshold(other.volumeThreshold),
|
||||||
condition(other.condition),
|
condition(other.condition),
|
||||||
@@ -420,6 +432,8 @@ AudioSwitch &AudioSwitch::operator=(AudioSwitch &&other) noexcept
|
|||||||
|
|
||||||
void swap(AudioSwitch &first, AudioSwitch &second)
|
void swap(AudioSwitch &first, AudioSwitch &second)
|
||||||
{
|
{
|
||||||
|
std::swap(first.targetType, second.targetType);
|
||||||
|
std::swap(first.group, second.group);
|
||||||
std::swap(first.scene, second.scene);
|
std::swap(first.scene, second.scene);
|
||||||
std::swap(first.transition, second.transition);
|
std::swap(first.transition, second.transition);
|
||||||
std::swap(first.usePreviousScene, second.usePreviousScene);
|
std::swap(first.usePreviousScene, second.usePreviousScene);
|
||||||
@@ -433,7 +447,7 @@ void swap(AudioSwitch &first, AudioSwitch &second)
|
|||||||
second.resetVolmeter();
|
second.resetVolmeter();
|
||||||
}
|
}
|
||||||
|
|
||||||
void populateConditionSelection(QComboBox *list)
|
static inline void populateConditionSelection(QComboBox *list)
|
||||||
{
|
{
|
||||||
list->addItem(
|
list->addItem(
|
||||||
obs_module_text("AdvSceneSwitcher.audioTab.condition.above"));
|
obs_module_text("AdvSceneSwitcher.audioTab.condition.above"));
|
||||||
@@ -441,18 +455,22 @@ void populateConditionSelection(QComboBox *list)
|
|||||||
obs_module_text("AdvSceneSwitcher.audioTab.condition.below"));
|
obs_module_text("AdvSceneSwitcher.audioTab.condition.below"));
|
||||||
}
|
}
|
||||||
|
|
||||||
AudioSwitchWidget::AudioSwitchWidget(AudioSwitch *s) : SwitchWidget(s)
|
AudioSwitchWidget::AudioSwitchWidget(QWidget *parent, AudioSwitch *s)
|
||||||
|
: SwitchWidget(parent, s, true, true)
|
||||||
{
|
{
|
||||||
audioSources = new QComboBox();
|
audioSources = new QComboBox();
|
||||||
condition = new QComboBox();
|
condition = new QComboBox();
|
||||||
audioVolumeThreshold = new QSpinBox();
|
audioVolumeThreshold = new QSpinBox();
|
||||||
duration = new QDoubleSpinBox();
|
duration = new QDoubleSpinBox();
|
||||||
|
ignoreInactiveSource = new QCheckBox(obs_module_text(
|
||||||
|
"AdvSceneSwitcher.audioTab.ignoreInactiveSource"));
|
||||||
|
|
||||||
obs_source_t *soruce = nullptr;
|
obs_source_t *source = nullptr;
|
||||||
if (s)
|
if (s) {
|
||||||
soruce = obs_weak_source_get_source(s->audioSource);
|
source = obs_weak_source_get_source(s->audioSource);
|
||||||
volMeter = new VolControl(soruce);
|
}
|
||||||
obs_source_release(soruce);
|
volMeter = new VolControl(source);
|
||||||
|
obs_source_release(source);
|
||||||
|
|
||||||
audioVolumeThreshold->setSuffix("%");
|
audioVolumeThreshold->setSuffix("%");
|
||||||
audioVolumeThreshold->setMaximum(100);
|
audioVolumeThreshold->setMaximum(100);
|
||||||
@@ -475,6 +493,8 @@ AudioSwitchWidget::AudioSwitchWidget(AudioSwitch *s) : SwitchWidget(s)
|
|||||||
QWidget::connect(audioSources,
|
QWidget::connect(audioSources,
|
||||||
SIGNAL(currentTextChanged(const QString &)), this,
|
SIGNAL(currentTextChanged(const QString &)), this,
|
||||||
SLOT(SourceChanged(const QString &)));
|
SLOT(SourceChanged(const QString &)));
|
||||||
|
QWidget::connect(ignoreInactiveSource, SIGNAL(stateChanged(int)), this,
|
||||||
|
SLOT(IgnoreInactiveChanged(int)));
|
||||||
|
|
||||||
AdvSceneSwitcher::populateAudioSelection(audioSources);
|
AdvSceneSwitcher::populateAudioSelection(audioSources);
|
||||||
populateConditionSelection(condition);
|
populateConditionSelection(condition);
|
||||||
@@ -485,6 +505,7 @@ AudioSwitchWidget::AudioSwitchWidget(AudioSwitch *s) : SwitchWidget(s)
|
|||||||
audioVolumeThreshold->setValue(s->volumeThreshold);
|
audioVolumeThreshold->setValue(s->volumeThreshold);
|
||||||
condition->setCurrentIndex(s->condition);
|
condition->setCurrentIndex(s->condition);
|
||||||
duration->setValue(s->duration);
|
duration->setValue(s->duration);
|
||||||
|
ignoreInactiveSource->setChecked(s->ignoreInactiveSource);
|
||||||
}
|
}
|
||||||
|
|
||||||
QHBoxLayout *switchLayout = new QHBoxLayout;
|
QHBoxLayout *switchLayout = new QHBoxLayout;
|
||||||
@@ -493,6 +514,7 @@ AudioSwitchWidget::AudioSwitchWidget(AudioSwitch *s) : SwitchWidget(s)
|
|||||||
{"{{volumeWidget}}", audioVolumeThreshold},
|
{"{{volumeWidget}}", audioVolumeThreshold},
|
||||||
{"{{condition}}", condition},
|
{"{{condition}}", condition},
|
||||||
{"{{duration}}", duration},
|
{"{{duration}}", duration},
|
||||||
|
{"{{ignoreInactiveSource}}", ignoreInactiveSource},
|
||||||
{"{{scenes}}", scenes},
|
{"{{scenes}}", scenes},
|
||||||
{"{{transitions}}", transitions}};
|
{"{{transitions}}", transitions}};
|
||||||
placeWidgets(obs_module_text("AdvSceneSwitcher.audioTab.entry"),
|
placeWidgets(obs_module_text("AdvSceneSwitcher.audioTab.entry"),
|
||||||
@@ -552,8 +574,10 @@ void AudioSwitchWidget::UpdateVolmeterSource()
|
|||||||
|
|
||||||
void AudioSwitchWidget::SourceChanged(const QString &text)
|
void AudioSwitchWidget::SourceChanged(const QString &text)
|
||||||
{
|
{
|
||||||
if (loading || !switchData)
|
if (loading || !switchData) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
std::lock_guard<std::mutex> lock(switcher->m);
|
std::lock_guard<std::mutex> lock(switcher->m);
|
||||||
switchData->audioSource = GetWeakSourceByQString(text);
|
switchData->audioSource = GetWeakSourceByQString(text);
|
||||||
switchData->resetVolmeter();
|
switchData->resetVolmeter();
|
||||||
@@ -562,30 +586,47 @@ void AudioSwitchWidget::SourceChanged(const QString &text)
|
|||||||
|
|
||||||
void AudioSwitchWidget::VolumeThresholdChanged(int vol)
|
void AudioSwitchWidget::VolumeThresholdChanged(int vol)
|
||||||
{
|
{
|
||||||
if (loading || !switchData)
|
if (loading || !switchData) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
std::lock_guard<std::mutex> lock(switcher->m);
|
std::lock_guard<std::mutex> lock(switcher->m);
|
||||||
switchData->volumeThreshold = vol;
|
switchData->volumeThreshold = vol;
|
||||||
}
|
}
|
||||||
|
|
||||||
void AudioSwitchWidget::ConditionChanged(int cond)
|
void AudioSwitchWidget::ConditionChanged(int cond)
|
||||||
{
|
{
|
||||||
if (loading || !switchData)
|
if (loading || !switchData) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
std::lock_guard<std::mutex> lock(switcher->m);
|
std::lock_guard<std::mutex> lock(switcher->m);
|
||||||
switchData->condition = (audioCondition)cond;
|
switchData->condition = (audioCondition)cond;
|
||||||
}
|
}
|
||||||
|
|
||||||
void AudioSwitchWidget::DurationChanged(double dur)
|
void AudioSwitchWidget::DurationChanged(double dur)
|
||||||
{
|
{
|
||||||
if (loading || !switchData)
|
if (loading || !switchData) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
std::lock_guard<std::mutex> lock(switcher->m);
|
std::lock_guard<std::mutex> lock(switcher->m);
|
||||||
switchData->duration = dur;
|
switchData->duration = dur;
|
||||||
}
|
}
|
||||||
|
|
||||||
AudioSwitchFallbackWidget::AudioSwitchFallbackWidget(AudioSwitchFallback *s)
|
void AudioSwitchWidget::IgnoreInactiveChanged(int state)
|
||||||
: SwitchWidget(s)
|
{
|
||||||
|
if (loading || !switchData) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::lock_guard<std::mutex> lock(switcher->m);
|
||||||
|
switchData->ignoreInactiveSource = state;
|
||||||
|
}
|
||||||
|
|
||||||
|
AudioSwitchFallbackWidget::AudioSwitchFallbackWidget(QWidget *parent,
|
||||||
|
AudioSwitchFallback *s)
|
||||||
|
: SwitchWidget(parent, s, true, true)
|
||||||
{
|
{
|
||||||
duration = new QDoubleSpinBox();
|
duration = new QDoubleSpinBox();
|
||||||
|
|
||||||
@@ -617,8 +658,10 @@ AudioSwitchFallbackWidget::AudioSwitchFallbackWidget(AudioSwitchFallback *s)
|
|||||||
|
|
||||||
void AudioSwitchFallbackWidget::DurationChanged(double dur)
|
void AudioSwitchFallbackWidget::DurationChanged(double dur)
|
||||||
{
|
{
|
||||||
if (loading || !switchData)
|
if (loading || !switchData) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
std::lock_guard<std::mutex> lock(switcher->m);
|
std::lock_guard<std::mutex> lock(switcher->m);
|
||||||
switchData->duration = dur;
|
switchData->duration = dur;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,15 +11,18 @@ void AdvSceneSwitcher::on_executableAdd_clicked()
|
|||||||
|
|
||||||
listAddClicked(ui->executables,
|
listAddClicked(ui->executables,
|
||||||
new ExecutableSwitchWidget(
|
new ExecutableSwitchWidget(
|
||||||
&switcher->executableSwitches.back()),
|
this, &switcher->executableSwitches.back()),
|
||||||
ui->executableAdd, &addPulse);
|
ui->executableAdd, &addPulse);
|
||||||
|
|
||||||
|
ui->exeHelp->setVisible(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AdvSceneSwitcher::on_executableRemove_clicked()
|
void AdvSceneSwitcher::on_executableRemove_clicked()
|
||||||
{
|
{
|
||||||
QListWidgetItem *item = ui->executables->currentItem();
|
QListWidgetItem *item = ui->executables->currentItem();
|
||||||
if (!item)
|
if (!item) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> lock(switcher->m);
|
std::lock_guard<std::mutex> lock(switcher->m);
|
||||||
@@ -34,8 +37,9 @@ void AdvSceneSwitcher::on_executableRemove_clicked()
|
|||||||
void AdvSceneSwitcher::on_executableUp_clicked()
|
void AdvSceneSwitcher::on_executableUp_clicked()
|
||||||
{
|
{
|
||||||
int index = ui->executables->currentRow();
|
int index = ui->executables->currentRow();
|
||||||
if (!listMoveUp(ui->executables))
|
if (!listMoveUp(ui->executables)) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
ExecutableSwitchWidget *s1 =
|
ExecutableSwitchWidget *s1 =
|
||||||
(ExecutableSwitchWidget *)ui->executables->itemWidget(
|
(ExecutableSwitchWidget *)ui->executables->itemWidget(
|
||||||
@@ -55,8 +59,9 @@ void AdvSceneSwitcher::on_executableDown_clicked()
|
|||||||
{
|
{
|
||||||
int index = ui->executables->currentRow();
|
int index = ui->executables->currentRow();
|
||||||
|
|
||||||
if (!listMoveDown(ui->executables))
|
if (!listMoveDown(ui->executables)) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
ExecutableSwitchWidget *s1 =
|
ExecutableSwitchWidget *s1 =
|
||||||
(ExecutableSwitchWidget *)ui->executables->itemWidget(
|
(ExecutableSwitchWidget *)ui->executables->itemWidget(
|
||||||
@@ -75,8 +80,9 @@ void AdvSceneSwitcher::on_executableDown_clicked()
|
|||||||
void SwitcherData::checkExeSwitch(bool &match, OBSWeakSource &scene,
|
void SwitcherData::checkExeSwitch(bool &match, OBSWeakSource &scene,
|
||||||
OBSWeakSource &transition)
|
OBSWeakSource &transition)
|
||||||
{
|
{
|
||||||
if (executableSwitches.size() == 0 || ExecutableSwitch::pause)
|
if (executableSwitches.size() == 0 || ExecutableSwitch::pause) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
std::string title;
|
std::string title;
|
||||||
QStringList runningProcesses;
|
QStringList runningProcesses;
|
||||||
@@ -103,15 +109,15 @@ void SwitcherData::checkExeSwitch(bool &match, OBSWeakSource &scene,
|
|||||||
// Check for match
|
// Check for match
|
||||||
GetProcessList(runningProcesses);
|
GetProcessList(runningProcesses);
|
||||||
for (ExecutableSwitch &s : executableSwitches) {
|
for (ExecutableSwitch &s : executableSwitches) {
|
||||||
if (!s.initialized())
|
if (!s.initialized()) {
|
||||||
continue;
|
continue;
|
||||||
// True if executable switch is running (direct)
|
}
|
||||||
|
|
||||||
bool equals = runningProcesses.contains(s.exe);
|
bool equals = runningProcesses.contains(s.exe);
|
||||||
// True if executable switch is running (regex)
|
|
||||||
bool matches = (runningProcesses.indexOf(
|
bool matches = (runningProcesses.indexOf(
|
||||||
QRegularExpression(s.exe)) != -1);
|
QRegularExpression(s.exe)) != -1);
|
||||||
// True if focus is disabled OR switch is focused
|
|
||||||
bool focus = (!s.inFocus || isInFocus(s.exe));
|
bool focus = (!s.inFocus || isInFocus(s.exe));
|
||||||
|
|
||||||
// True if current window is ignored AND switch equals OR matches last window
|
// True if current window is ignored AND switch equals OR matches last window
|
||||||
bool ignore =
|
bool ignore =
|
||||||
(ignored && (title == s.exe.toStdString() ||
|
(ignored && (title == s.exe.toStdString() ||
|
||||||
@@ -120,11 +126,12 @@ void SwitcherData::checkExeSwitch(bool &match, OBSWeakSource &scene,
|
|||||||
|
|
||||||
if ((equals || matches) && (focus || ignore)) {
|
if ((equals || matches) && (focus || ignore)) {
|
||||||
match = true;
|
match = true;
|
||||||
scene = s.scene;
|
scene = s.getScene();
|
||||||
transition = s.transition;
|
transition = s.transition;
|
||||||
|
|
||||||
if (verbose)
|
if (verbose) {
|
||||||
s.logMatch();
|
s.logMatch();
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -136,24 +143,9 @@ void SwitcherData::saveExecutableSwitches(obs_data_t *obj)
|
|||||||
for (ExecutableSwitch &s : switcher->executableSwitches) {
|
for (ExecutableSwitch &s : switcher->executableSwitches) {
|
||||||
obs_data_t *array_obj = obs_data_create();
|
obs_data_t *array_obj = obs_data_create();
|
||||||
|
|
||||||
obs_source_t *source = obs_weak_source_get_source(s.scene);
|
s.save(array_obj);
|
||||||
obs_source_t *transition =
|
obs_data_array_push_back(executableArray, array_obj);
|
||||||
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_release(array_obj);
|
||||||
}
|
}
|
||||||
obs_data_set_array(obj, "executableSwitches", executableArray);
|
obs_data_set_array(obj, "executableSwitches", executableArray);
|
||||||
@@ -171,15 +163,8 @@ void SwitcherData::loadExecutableSwitches(obs_data_t *obj)
|
|||||||
for (size_t i = 0; i < count; i++) {
|
for (size_t i = 0; i < count; i++) {
|
||||||
obs_data_t *array_obj = obs_data_array_item(executableArray, i);
|
obs_data_t *array_obj = obs_data_array_item(executableArray, i);
|
||||||
|
|
||||||
const char *scene = obs_data_get_string(array_obj, "scene");
|
switcher->executableSwitches.emplace_back();
|
||||||
const char *transition =
|
executableSwitches.back().load(array_obj);
|
||||||
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_release(array_obj);
|
||||||
}
|
}
|
||||||
@@ -192,17 +177,68 @@ void AdvSceneSwitcher::setupExecutableTab()
|
|||||||
QListWidgetItem *item;
|
QListWidgetItem *item;
|
||||||
item = new QListWidgetItem(ui->executables);
|
item = new QListWidgetItem(ui->executables);
|
||||||
ui->executables->addItem(item);
|
ui->executables->addItem(item);
|
||||||
ExecutableSwitchWidget *sw = new ExecutableSwitchWidget(&s);
|
ExecutableSwitchWidget *sw =
|
||||||
|
new ExecutableSwitchWidget(this, &s);
|
||||||
item->setSizeHint(sw->minimumSizeHint());
|
item->setSizeHint(sw->minimumSizeHint());
|
||||||
ui->executables->setItemWidget(item, sw);
|
ui->executables->setItemWidget(item, sw);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (switcher->executableSwitches.size() == 0)
|
if (switcher->executableSwitches.size() == 0) {
|
||||||
addPulse = PulseWidget(ui->executableAdd, QColor(Qt::green));
|
addPulse = PulseWidget(ui->executableAdd, QColor(Qt::green));
|
||||||
|
ui->exeHelp->setVisible(true);
|
||||||
|
} else {
|
||||||
|
ui->exeHelp->setVisible(false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ExecutableSwitchWidget::ExecutableSwitchWidget(ExecutableSwitch *s)
|
void ExecutableSwitch::save(obs_data_t *obj)
|
||||||
: SwitchWidget(s, false)
|
{
|
||||||
|
SceneSwitcherEntry::save(obj);
|
||||||
|
|
||||||
|
obs_data_set_string(obj, "exefile", exe.toUtf8());
|
||||||
|
obs_data_set_bool(obj, "infocus", inFocus);
|
||||||
|
}
|
||||||
|
|
||||||
|
// To be removed in future version
|
||||||
|
bool loadOldExe(obs_data_t *obj, ExecutableSwitch *s)
|
||||||
|
{
|
||||||
|
if (!s) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *scene = obs_data_get_string(obj, "scene");
|
||||||
|
|
||||||
|
if (strcmp(scene, "") == 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
s->scene = GetWeakSourceByName(scene);
|
||||||
|
|
||||||
|
const char *transition = obs_data_get_string(obj, "transition");
|
||||||
|
s->transition = GetWeakTransitionByName(transition);
|
||||||
|
|
||||||
|
s->exe = obs_data_get_string(obj, "exefile");
|
||||||
|
s->inFocus = obs_data_get_bool(obj, "infocus");
|
||||||
|
s->usePreviousScene = strcmp(scene, previous_scene_name) == 0;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ExecutableSwitch::load(obs_data_t *obj)
|
||||||
|
{
|
||||||
|
if (loadOldExe(obj, this)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
SceneSwitcherEntry::load(obj);
|
||||||
|
|
||||||
|
exe = obs_data_get_string(obj, "exefile");
|
||||||
|
inFocus = obs_data_get_bool(obj, "infocus");
|
||||||
|
}
|
||||||
|
|
||||||
|
ExecutableSwitchWidget::ExecutableSwitchWidget(QWidget *parent,
|
||||||
|
ExecutableSwitch *s)
|
||||||
|
: SwitchWidget(parent, s, true, true)
|
||||||
{
|
{
|
||||||
processes = new QComboBox();
|
processes = new QComboBox();
|
||||||
requiresFocus = new QCheckBox(obs_module_text(
|
requiresFocus = new QCheckBox(obs_module_text(
|
||||||
@@ -260,16 +296,20 @@ void ExecutableSwitchWidget::swapSwitchData(ExecutableSwitchWidget *s1,
|
|||||||
|
|
||||||
void ExecutableSwitchWidget::ProcessChanged(const QString &text)
|
void ExecutableSwitchWidget::ProcessChanged(const QString &text)
|
||||||
{
|
{
|
||||||
if (loading || !switchData)
|
if (loading || !switchData) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
std::lock_guard<std::mutex> lock(switcher->m);
|
std::lock_guard<std::mutex> lock(switcher->m);
|
||||||
switchData->exe = text;
|
switchData->exe = text;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ExecutableSwitchWidget::FocusChanged(int state)
|
void ExecutableSwitchWidget::FocusChanged(int state)
|
||||||
{
|
{
|
||||||
if (loading || !switchData)
|
if (loading || !switchData) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
std::lock_guard<std::mutex> lock(switcher->m);
|
std::lock_guard<std::mutex> lock(switcher->m);
|
||||||
switchData->inFocus = state;
|
switchData->inFocus = state;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,14 +19,16 @@ void AdvSceneSwitcher::on_browseButton_clicked()
|
|||||||
tr(obs_module_text("AdvSceneSwitcher.fileTab.selectWrite")),
|
tr(obs_module_text("AdvSceneSwitcher.fileTab.selectWrite")),
|
||||||
QDir::currentPath(),
|
QDir::currentPath(),
|
||||||
tr(obs_module_text("AdvSceneSwitcher.fileTab.textFileType")));
|
tr(obs_module_text("AdvSceneSwitcher.fileTab.textFileType")));
|
||||||
if (!path.isEmpty())
|
if (!path.isEmpty()) {
|
||||||
ui->writePathLineEdit->setText(path);
|
ui->writePathLineEdit->setText(path);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void AdvSceneSwitcher::on_readFileCheckBox_stateChanged(int state)
|
void AdvSceneSwitcher::on_readFileCheckBox_stateChanged(int state)
|
||||||
{
|
{
|
||||||
if (loading)
|
if (loading) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
std::lock_guard<std::mutex> lock(switcher->m);
|
std::lock_guard<std::mutex> lock(switcher->m);
|
||||||
if (!state) {
|
if (!state) {
|
||||||
@@ -42,8 +44,9 @@ void AdvSceneSwitcher::on_readFileCheckBox_stateChanged(int state)
|
|||||||
|
|
||||||
void AdvSceneSwitcher::on_readPathLineEdit_textChanged(const QString &text)
|
void AdvSceneSwitcher::on_readPathLineEdit_textChanged(const QString &text)
|
||||||
{
|
{
|
||||||
if (loading)
|
if (loading) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
std::lock_guard<std::mutex> lock(switcher->m);
|
std::lock_guard<std::mutex> lock(switcher->m);
|
||||||
if (text.isEmpty()) {
|
if (text.isEmpty()) {
|
||||||
@@ -56,8 +59,9 @@ void AdvSceneSwitcher::on_readPathLineEdit_textChanged(const QString &text)
|
|||||||
|
|
||||||
void AdvSceneSwitcher::on_writePathLineEdit_textChanged(const QString &text)
|
void AdvSceneSwitcher::on_writePathLineEdit_textChanged(const QString &text)
|
||||||
{
|
{
|
||||||
if (loading)
|
if (loading) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
std::lock_guard<std::mutex> lock(switcher->m);
|
std::lock_guard<std::mutex> lock(switcher->m);
|
||||||
if (text.isEmpty()) {
|
if (text.isEmpty()) {
|
||||||
@@ -75,14 +79,16 @@ void AdvSceneSwitcher::on_browseButton_2_clicked()
|
|||||||
tr(obs_module_text("AdvSceneSwitcher.fileTab.selectRead")),
|
tr(obs_module_text("AdvSceneSwitcher.fileTab.selectRead")),
|
||||||
QDir::currentPath(),
|
QDir::currentPath(),
|
||||||
tr(obs_module_text("AdvSceneSwitcher.fileTab.anyFileType")));
|
tr(obs_module_text("AdvSceneSwitcher.fileTab.anyFileType")));
|
||||||
if (!path.isEmpty())
|
if (!path.isEmpty()) {
|
||||||
ui->readPathLineEdit->setText(path);
|
ui->readPathLineEdit->setText(path);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void SwitcherData::writeSceneInfoToFile()
|
void SwitcherData::writeSceneInfoToFile()
|
||||||
{
|
{
|
||||||
if (!fileIO.writeEnabled || fileIO.writePath.empty())
|
if (!fileIO.writeEnabled || fileIO.writePath.empty()) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
obs_source_t *currentSource = obs_frontend_get_current_scene();
|
obs_source_t *currentSource = obs_frontend_get_current_scene();
|
||||||
|
|
||||||
@@ -97,8 +103,9 @@ void SwitcherData::writeSceneInfoToFile()
|
|||||||
|
|
||||||
void SwitcherData::writeToStatusFile(QString msg)
|
void SwitcherData::writeToStatusFile(QString msg)
|
||||||
{
|
{
|
||||||
if (!fileIO.writeEnabled || fileIO.writePath.empty())
|
if (!fileIO.writeEnabled || fileIO.writePath.empty()) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
QFile file(QString::fromStdString(fileIO.writePath));
|
QFile file(QString::fromStdString(fileIO.writePath));
|
||||||
if (file.open(QIODevice::ReadWrite)) {
|
if (file.open(QIODevice::ReadWrite)) {
|
||||||
@@ -111,28 +118,37 @@ void SwitcherData::writeToStatusFile(QString msg)
|
|||||||
void SwitcherData::checkSwitchInfoFromFile(bool &match, OBSWeakSource &scene,
|
void SwitcherData::checkSwitchInfoFromFile(bool &match, OBSWeakSource &scene,
|
||||||
OBSWeakSource &transition)
|
OBSWeakSource &transition)
|
||||||
{
|
{
|
||||||
if (!fileIO.readEnabled || fileIO.readPath.empty())
|
if (!fileIO.readEnabled || fileIO.readPath.empty() ||
|
||||||
|
FileSwitch::pause) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
QFile file(QString::fromStdString(fileIO.readPath));
|
QFile file(QString::fromStdString(fileIO.readPath));
|
||||||
if (file.open(QIODevice::ReadOnly)) {
|
if (!file.open(QIODevice::ReadOnly)) {
|
||||||
QTextStream in(&file);
|
return;
|
||||||
QString sceneStr = in.readLine();
|
|
||||||
obs_source_t *sceneRead =
|
|
||||||
obs_get_source_by_name(sceneStr.toUtf8().constData());
|
|
||||||
if (sceneRead) {
|
|
||||||
obs_weak_source_t *sceneReadWs =
|
|
||||||
obs_source_get_weak_source(sceneRead);
|
|
||||||
|
|
||||||
match = true;
|
|
||||||
scene = sceneReadWs;
|
|
||||||
transition = nullptr;
|
|
||||||
|
|
||||||
obs_weak_source_release(sceneReadWs);
|
|
||||||
obs_source_release(sceneRead);
|
|
||||||
}
|
|
||||||
file.close();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QTextStream in(&file);
|
||||||
|
|
||||||
|
QString sceneStr = in.readLine();
|
||||||
|
OBSWeakSource sceneRead = GetWeakSourceByQString(sceneStr);
|
||||||
|
|
||||||
|
QString transitionStr = in.readLine();
|
||||||
|
OBSWeakSource transitionRead =
|
||||||
|
GetWeakTransitionByQString(transitionStr);
|
||||||
|
|
||||||
|
if (sceneRead) {
|
||||||
|
match = true;
|
||||||
|
scene = sceneRead;
|
||||||
|
transition = transitionRead;
|
||||||
|
|
||||||
|
vblog(LOG_INFO,
|
||||||
|
"match for 'file' - read scene '%s' and transition '%s' from file '%s'",
|
||||||
|
sceneStr.toUtf8().constData(),
|
||||||
|
transitionStr.toUtf8().constData(),
|
||||||
|
fileIO.readPath.c_str());
|
||||||
|
}
|
||||||
|
file.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
static size_t WriteCallback(void *contents, size_t size, size_t nmemb,
|
static size_t WriteCallback(void *contents, size_t size, size_t nmemb,
|
||||||
@@ -175,8 +191,9 @@ bool compareIgnoringLineEnding(QString &s1, QString &s2)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!s1stream.atEnd() && !s2stream.atEnd())
|
if (!s1stream.atEnd() && !s2stream.atEnd()) {
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -185,8 +202,9 @@ bool matchFileContent(QString &filedata, FileSwitch &s)
|
|||||||
{
|
{
|
||||||
if (s.onlyMatchIfChanged) {
|
if (s.onlyMatchIfChanged) {
|
||||||
size_t newHash = strHash(filedata.toUtf8().constData());
|
size_t newHash = strHash(filedata.toUtf8().constData());
|
||||||
if (newHash == s.lastHash)
|
if (newHash == s.lastHash) {
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
s.lastHash = newHash;
|
s.lastHash = newHash;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -210,13 +228,15 @@ bool checkLocalFileContent(FileSwitch &s)
|
|||||||
{
|
{
|
||||||
QString t = QString::fromStdString(s.text);
|
QString t = QString::fromStdString(s.text);
|
||||||
QFile file(QString::fromStdString(s.file));
|
QFile file(QString::fromStdString(s.file));
|
||||||
if (s.file.empty() || !file.open(QIODevice::ReadOnly))
|
if (s.file.empty() || !file.open(QIODevice::ReadOnly)) {
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (s.useTime) {
|
if (s.useTime) {
|
||||||
QDateTime newLastMod = QFileInfo(file).lastModified();
|
QDateTime newLastMod = QFileInfo(file).lastModified();
|
||||||
if (s.lastMod == newLastMod)
|
if (s.lastMod == newLastMod) {
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
s.lastMod = newLastMod;
|
s.lastMod = newLastMod;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -230,12 +250,14 @@ bool checkLocalFileContent(FileSwitch &s)
|
|||||||
void SwitcherData::checkFileContent(bool &match, OBSWeakSource &scene,
|
void SwitcherData::checkFileContent(bool &match, OBSWeakSource &scene,
|
||||||
OBSWeakSource &transition)
|
OBSWeakSource &transition)
|
||||||
{
|
{
|
||||||
if (FileSwitch::pause)
|
if (FileSwitch::pause) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
for (FileSwitch &s : fileSwitches) {
|
for (FileSwitch &s : fileSwitches) {
|
||||||
if (!s.initialized())
|
if (!s.initialized()) {
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
bool equal = false;
|
bool equal = false;
|
||||||
if (s.remote) {
|
if (s.remote) {
|
||||||
@@ -245,12 +267,13 @@ void SwitcherData::checkFileContent(bool &match, OBSWeakSource &scene,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (equal) {
|
if (equal) {
|
||||||
scene = s.scene;
|
scene = s.getScene();
|
||||||
transition = s.transition;
|
transition = s.transition;
|
||||||
match = true;
|
match = true;
|
||||||
|
|
||||||
if (verbose)
|
if (verbose) {
|
||||||
s.logMatch();
|
s.logMatch();
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -262,15 +285,19 @@ void AdvSceneSwitcher::on_fileAdd_clicked()
|
|||||||
switcher->fileSwitches.emplace_back();
|
switcher->fileSwitches.emplace_back();
|
||||||
|
|
||||||
listAddClicked(ui->fileSwitches,
|
listAddClicked(ui->fileSwitches,
|
||||||
new FileSwitchWidget(&switcher->fileSwitches.back()),
|
new FileSwitchWidget(this,
|
||||||
|
&switcher->fileSwitches.back()),
|
||||||
ui->fileAdd, &addPulse);
|
ui->fileAdd, &addPulse);
|
||||||
|
|
||||||
|
ui->fileHelp->setVisible(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AdvSceneSwitcher::on_fileRemove_clicked()
|
void AdvSceneSwitcher::on_fileRemove_clicked()
|
||||||
{
|
{
|
||||||
QListWidgetItem *item = ui->fileSwitches->currentItem();
|
QListWidgetItem *item = ui->fileSwitches->currentItem();
|
||||||
if (!item)
|
if (!item) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> lock(switcher->m);
|
std::lock_guard<std::mutex> lock(switcher->m);
|
||||||
@@ -284,27 +311,32 @@ void AdvSceneSwitcher::on_fileRemove_clicked()
|
|||||||
|
|
||||||
void AdvSceneSwitcher::on_fileSwitches_currentRowChanged(int idx)
|
void AdvSceneSwitcher::on_fileSwitches_currentRowChanged(int idx)
|
||||||
{
|
{
|
||||||
if (loading)
|
if (loading) {
|
||||||
return;
|
return;
|
||||||
if (idx == -1)
|
}
|
||||||
|
if (idx == -1) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
std::lock_guard<std::mutex> lock(switcher->m);
|
std::lock_guard<std::mutex> lock(switcher->m);
|
||||||
|
|
||||||
if ((int)switcher->fileSwitches.size() <= idx)
|
if ((int)switcher->fileSwitches.size() <= idx) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
FileSwitch s = switcher->fileSwitches[idx];
|
FileSwitch s = switcher->fileSwitches[idx];
|
||||||
if (s.remote)
|
if (s.remote) {
|
||||||
ui->remoteFileWarningLabel->show();
|
ui->remoteFileWarningLabel->show();
|
||||||
else
|
} else {
|
||||||
ui->remoteFileWarningLabel->hide();
|
ui->remoteFileWarningLabel->hide();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void AdvSceneSwitcher::on_fileUp_clicked()
|
void AdvSceneSwitcher::on_fileUp_clicked()
|
||||||
{
|
{
|
||||||
int index = ui->fileSwitches->currentRow();
|
int index = ui->fileSwitches->currentRow();
|
||||||
if (!listMoveUp(ui->fileSwitches))
|
if (!listMoveUp(ui->fileSwitches)) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
FileSwitchWidget *s1 = (FileSwitchWidget *)ui->fileSwitches->itemWidget(
|
FileSwitchWidget *s1 = (FileSwitchWidget *)ui->fileSwitches->itemWidget(
|
||||||
ui->fileSwitches->item(index));
|
ui->fileSwitches->item(index));
|
||||||
@@ -322,8 +354,9 @@ void AdvSceneSwitcher::on_fileDown_clicked()
|
|||||||
{
|
{
|
||||||
int index = ui->fileSwitches->currentRow();
|
int index = ui->fileSwitches->currentRow();
|
||||||
|
|
||||||
if (!listMoveDown(ui->fileSwitches))
|
if (!listMoveDown(ui->fileSwitches)) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
FileSwitchWidget *s1 = (FileSwitchWidget *)ui->fileSwitches->itemWidget(
|
FileSwitchWidget *s1 = (FileSwitchWidget *)ui->fileSwitches->itemWidget(
|
||||||
ui->fileSwitches->item(index));
|
ui->fileSwitches->item(index));
|
||||||
@@ -343,28 +376,9 @@ void SwitcherData::saveFileSwitches(obs_data_t *obj)
|
|||||||
for (FileSwitch &s : switcher->fileSwitches) {
|
for (FileSwitch &s : switcher->fileSwitches) {
|
||||||
obs_data_t *array_obj = obs_data_create();
|
obs_data_t *array_obj = obs_data_create();
|
||||||
|
|
||||||
obs_source_t *source = obs_weak_source_get_source(s.scene);
|
s.save(array_obj);
|
||||||
obs_source_t *transition =
|
obs_data_array_push_back(fileArray, array_obj);
|
||||||
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, "file", s.file.c_str());
|
|
||||||
obs_data_set_string(array_obj, "text", s.text.c_str());
|
|
||||||
obs_data_set_bool(array_obj, "remote", s.remote);
|
|
||||||
obs_data_set_bool(array_obj, "useRegex", s.useRegex);
|
|
||||||
obs_data_set_bool(array_obj, "useTime", s.useTime);
|
|
||||||
obs_data_set_bool(array_obj, "onlyMatchIfChanged",
|
|
||||||
s.onlyMatchIfChanged);
|
|
||||||
obs_data_array_push_back(fileArray, array_obj);
|
|
||||||
}
|
|
||||||
obs_source_release(source);
|
|
||||||
obs_source_release(transition);
|
|
||||||
obs_data_release(array_obj);
|
obs_data_release(array_obj);
|
||||||
}
|
}
|
||||||
obs_data_set_array(obj, "fileSwitches", fileArray);
|
obs_data_set_array(obj, "fileSwitches", fileArray);
|
||||||
@@ -386,21 +400,8 @@ void SwitcherData::loadFileSwitches(obs_data_t *obj)
|
|||||||
for (size_t i = 0; i < count; i++) {
|
for (size_t i = 0; i < count; i++) {
|
||||||
obs_data_t *array_obj = obs_data_array_item(fileArray, i);
|
obs_data_t *array_obj = obs_data_array_item(fileArray, i);
|
||||||
|
|
||||||
const char *scene = obs_data_get_string(array_obj, "scene");
|
switcher->fileSwitches.emplace_back();
|
||||||
const char *transition =
|
fileSwitches.back().load(array_obj);
|
||||||
obs_data_get_string(array_obj, "transition");
|
|
||||||
const char *file = obs_data_get_string(array_obj, "file");
|
|
||||||
const char *text = obs_data_get_string(array_obj, "text");
|
|
||||||
bool remote = obs_data_get_bool(array_obj, "remote");
|
|
||||||
bool useRegex = obs_data_get_bool(array_obj, "useRegex");
|
|
||||||
bool useTime = obs_data_get_bool(array_obj, "useTime");
|
|
||||||
bool onlyMatchIfChanged =
|
|
||||||
obs_data_get_bool(array_obj, "onlyMatchIfChanged");
|
|
||||||
|
|
||||||
switcher->fileSwitches.emplace_back(
|
|
||||||
GetWeakSourceByName(scene),
|
|
||||||
GetWeakTransitionByName(transition), file, text, remote,
|
|
||||||
useRegex, useTime, onlyMatchIfChanged);
|
|
||||||
|
|
||||||
obs_data_release(array_obj);
|
obs_data_release(array_obj);
|
||||||
}
|
}
|
||||||
@@ -422,20 +423,25 @@ void AdvSceneSwitcher::setupFileTab()
|
|||||||
obs_module_text("AdvSceneSwitcher.fileTab.remoteFileWarning2"));
|
obs_module_text("AdvSceneSwitcher.fileTab.remoteFileWarning2"));
|
||||||
ui->remoteFileWarningLabel->hide();
|
ui->remoteFileWarningLabel->hide();
|
||||||
|
|
||||||
if (switcher->curl)
|
if (switcher->curl) {
|
||||||
ui->libcurlWarning->setVisible(false);
|
ui->libcurlWarning->setVisible(false);
|
||||||
|
}
|
||||||
|
|
||||||
for (auto &s : switcher->fileSwitches) {
|
for (auto &s : switcher->fileSwitches) {
|
||||||
QListWidgetItem *item;
|
QListWidgetItem *item;
|
||||||
item = new QListWidgetItem(ui->fileSwitches);
|
item = new QListWidgetItem(ui->fileSwitches);
|
||||||
ui->fileSwitches->addItem(item);
|
ui->fileSwitches->addItem(item);
|
||||||
FileSwitchWidget *sw = new FileSwitchWidget(&s);
|
FileSwitchWidget *sw = new FileSwitchWidget(this, &s);
|
||||||
item->setSizeHint(sw->minimumSizeHint());
|
item->setSizeHint(sw->minimumSizeHint());
|
||||||
ui->fileSwitches->setItemWidget(item, sw);
|
ui->fileSwitches->setItemWidget(item, sw);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (switcher->fileSwitches.size() == 0)
|
if (switcher->fileSwitches.size() == 0) {
|
||||||
addPulse = PulseWidget(ui->fileAdd, QColor(Qt::green));
|
addPulse = PulseWidget(ui->fileAdd, QColor(Qt::green));
|
||||||
|
ui->fileHelp->setVisible(true);
|
||||||
|
} else {
|
||||||
|
ui->fileHelp->setVisible(false);
|
||||||
|
}
|
||||||
|
|
||||||
ui->readPathLineEdit->setText(
|
ui->readPathLineEdit->setText(
|
||||||
QString::fromStdString(switcher->fileIO.readPath.c_str()));
|
QString::fromStdString(switcher->fileIO.readPath.c_str()));
|
||||||
@@ -452,7 +458,65 @@ void AdvSceneSwitcher::setupFileTab()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
FileSwitchWidget::FileSwitchWidget(FileSwitch *s) : SwitchWidget(s, false)
|
void FileSwitch::save(obs_data_t *obj)
|
||||||
|
{
|
||||||
|
SceneSwitcherEntry::save(obj);
|
||||||
|
|
||||||
|
obs_data_set_string(obj, "file", file.c_str());
|
||||||
|
obs_data_set_string(obj, "text", text.c_str());
|
||||||
|
obs_data_set_bool(obj, "remote", remote);
|
||||||
|
obs_data_set_bool(obj, "useRegex", useRegex);
|
||||||
|
obs_data_set_bool(obj, "useTime", useTime);
|
||||||
|
obs_data_set_bool(obj, "onlyMatchIfChanged", onlyMatchIfChanged);
|
||||||
|
}
|
||||||
|
|
||||||
|
// To be removed in future version
|
||||||
|
bool loadOldFile(obs_data_t *obj, FileSwitch *s)
|
||||||
|
{
|
||||||
|
if (!s) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *scene = obs_data_get_string(obj, "scene");
|
||||||
|
|
||||||
|
if (strcmp(scene, "") == 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
s->scene = GetWeakSourceByName(scene);
|
||||||
|
|
||||||
|
const char *transition = obs_data_get_string(obj, "transition");
|
||||||
|
s->transition = GetWeakTransitionByName(transition);
|
||||||
|
|
||||||
|
s->file = obs_data_get_string(obj, "file");
|
||||||
|
s->text = obs_data_get_string(obj, "text");
|
||||||
|
s->remote = obs_data_get_bool(obj, "remote");
|
||||||
|
s->useRegex = obs_data_get_bool(obj, "useRegex");
|
||||||
|
s->useTime = obs_data_get_bool(obj, "useTime");
|
||||||
|
s->onlyMatchIfChanged = obs_data_get_bool(obj, "onlyMatchIfChanged");
|
||||||
|
s->usePreviousScene = strcmp(scene, previous_scene_name) == 0;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FileSwitch::load(obs_data_t *obj)
|
||||||
|
{
|
||||||
|
if (loadOldFile(obj, this)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
SceneSwitcherEntry::load(obj);
|
||||||
|
|
||||||
|
file = obs_data_get_string(obj, "file");
|
||||||
|
text = obs_data_get_string(obj, "text");
|
||||||
|
remote = obs_data_get_bool(obj, "remote");
|
||||||
|
useRegex = obs_data_get_bool(obj, "useRegex");
|
||||||
|
useTime = obs_data_get_bool(obj, "useTime");
|
||||||
|
onlyMatchIfChanged = obs_data_get_bool(obj, "onlyMatchIfChanged");
|
||||||
|
}
|
||||||
|
|
||||||
|
FileSwitchWidget::FileSwitchWidget(QWidget *parent, FileSwitch *s)
|
||||||
|
: SwitchWidget(parent, s, true, true)
|
||||||
{
|
{
|
||||||
fileType = new QComboBox();
|
fileType = new QComboBox();
|
||||||
filePath = new QLineEdit();
|
filePath = new QLineEdit();
|
||||||
@@ -486,10 +550,11 @@ FileSwitchWidget::FileSwitchWidget(FileSwitch *s) : SwitchWidget(s, false)
|
|||||||
fileType->addItem(obs_module_text("AdvSceneSwitcher.fileTab.remote"));
|
fileType->addItem(obs_module_text("AdvSceneSwitcher.fileTab.remote"));
|
||||||
|
|
||||||
if (s) {
|
if (s) {
|
||||||
if (s->remote)
|
if (s->remote) {
|
||||||
fileType->setCurrentIndex(1);
|
fileType->setCurrentIndex(1);
|
||||||
else
|
} else {
|
||||||
fileType->setCurrentIndex(0);
|
fileType->setCurrentIndex(0);
|
||||||
|
}
|
||||||
filePath->setText(QString::fromStdString(s->file));
|
filePath->setText(QString::fromStdString(s->file));
|
||||||
matchText->setPlainText(QString::fromStdString(s->text));
|
matchText->setPlainText(QString::fromStdString(s->text));
|
||||||
useRegex->setChecked(s->useRegex);
|
useRegex->setChecked(s->useRegex);
|
||||||
@@ -551,8 +616,9 @@ void FileSwitchWidget::swapSwitchData(FileSwitchWidget *s1,
|
|||||||
|
|
||||||
void FileSwitchWidget::FileTypeChanged(int index)
|
void FileSwitchWidget::FileTypeChanged(int index)
|
||||||
{
|
{
|
||||||
if (loading || !switchData)
|
if (loading || !switchData) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if ((file_type)index == LOCAL) {
|
if ((file_type)index == LOCAL) {
|
||||||
browseButton->setDisabled(false);
|
browseButton->setDisabled(false);
|
||||||
@@ -568,24 +634,28 @@ void FileSwitchWidget::FileTypeChanged(int index)
|
|||||||
|
|
||||||
void FileSwitchWidget::FilePathChanged()
|
void FileSwitchWidget::FilePathChanged()
|
||||||
{
|
{
|
||||||
if (loading || !switchData)
|
if (loading || !switchData) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
std::lock_guard<std::mutex> lock(switcher->m);
|
std::lock_guard<std::mutex> lock(switcher->m);
|
||||||
switchData->file = filePath->text().toUtf8().constData();
|
switchData->file = filePath->text().toUtf8().constData();
|
||||||
}
|
}
|
||||||
|
|
||||||
void FileSwitchWidget::BrowseButtonClicked()
|
void FileSwitchWidget::BrowseButtonClicked()
|
||||||
{
|
{
|
||||||
if (loading || !switchData)
|
if (loading || !switchData) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
QString path = QFileDialog::getOpenFileName(
|
QString path = QFileDialog::getOpenFileName(
|
||||||
this,
|
this,
|
||||||
tr(obs_module_text("AdvSceneSwitcher.fileTab.selectRead")),
|
tr(obs_module_text("AdvSceneSwitcher.fileTab.selectRead")),
|
||||||
QDir::currentPath(),
|
QDir::currentPath(),
|
||||||
tr(obs_module_text("AdvSceneSwitcher.fileTab.anyFileType")));
|
tr(obs_module_text("AdvSceneSwitcher.fileTab.anyFileType")));
|
||||||
if (path.isEmpty())
|
if (path.isEmpty()) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
filePath->setText(path);
|
filePath->setText(path);
|
||||||
FilePathChanged();
|
FilePathChanged();
|
||||||
@@ -593,32 +663,40 @@ void FileSwitchWidget::BrowseButtonClicked()
|
|||||||
|
|
||||||
void FileSwitchWidget::MatchTextChanged()
|
void FileSwitchWidget::MatchTextChanged()
|
||||||
{
|
{
|
||||||
if (loading || !switchData)
|
if (loading || !switchData) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
std::lock_guard<std::mutex> lock(switcher->m);
|
std::lock_guard<std::mutex> lock(switcher->m);
|
||||||
switchData->text = matchText->toPlainText().toUtf8().constData();
|
switchData->text = matchText->toPlainText().toUtf8().constData();
|
||||||
}
|
}
|
||||||
|
|
||||||
void FileSwitchWidget::UseRegexChanged(int state)
|
void FileSwitchWidget::UseRegexChanged(int state)
|
||||||
{
|
{
|
||||||
if (loading || !switchData)
|
if (loading || !switchData) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
std::lock_guard<std::mutex> lock(switcher->m);
|
std::lock_guard<std::mutex> lock(switcher->m);
|
||||||
switchData->useRegex = state;
|
switchData->useRegex = state;
|
||||||
}
|
}
|
||||||
|
|
||||||
void FileSwitchWidget::CheckModificationDateChanged(int state)
|
void FileSwitchWidget::CheckModificationDateChanged(int state)
|
||||||
{
|
{
|
||||||
if (loading || !switchData)
|
if (loading || !switchData) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
std::lock_guard<std::mutex> lock(switcher->m);
|
std::lock_guard<std::mutex> lock(switcher->m);
|
||||||
switchData->useTime = state;
|
switchData->useTime = state;
|
||||||
}
|
}
|
||||||
|
|
||||||
void FileSwitchWidget::CheckFileContentChanged(int state)
|
void FileSwitchWidget::CheckFileContentChanged(int state)
|
||||||
{
|
{
|
||||||
if (loading || !switchData)
|
if (loading || !switchData) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
std::lock_guard<std::mutex> lock(switcher->m);
|
std::lock_guard<std::mutex> lock(switcher->m);
|
||||||
switchData->onlyMatchIfChanged = state;
|
switchData->onlyMatchIfChanged = state;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,29 +3,178 @@
|
|||||||
|
|
||||||
bool SceneSwitcherEntry::initialized()
|
bool SceneSwitcherEntry::initialized()
|
||||||
{
|
{
|
||||||
return (usePreviousScene || WeakSourceValid(scene)) && transition;
|
return (usePreviousScene || WeakSourceValid(scene) ||
|
||||||
|
SceneGroupValid(group)) &&
|
||||||
|
(useCurrentTransition || transition);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SceneSwitcherEntry::valid()
|
bool SceneSwitcherEntry::valid()
|
||||||
{
|
{
|
||||||
return !initialized() ||
|
return !initialized() ||
|
||||||
((usePreviousScene || WeakSourceValid(scene)) &&
|
((usePreviousScene || WeakSourceValid(scene) ||
|
||||||
WeakSourceValid(transition));
|
SceneGroupValid(group)) &&
|
||||||
|
(useCurrentTransition || WeakSourceValid(transition)));
|
||||||
|
}
|
||||||
|
|
||||||
|
void SceneSwitcherEntry::logMatchScene()
|
||||||
|
{
|
||||||
|
std::string sceneName = previous_scene_name;
|
||||||
|
if (!usePreviousScene) {
|
||||||
|
sceneName = GetWeakSourceName(scene);
|
||||||
|
}
|
||||||
|
blog(LOG_INFO, "match for '%s' - switch to scene '%s'", getType(),
|
||||||
|
sceneName.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
void SceneSwitcherEntry::logMatchSceneGroup()
|
||||||
|
{
|
||||||
|
if (group->scenes.empty()) {
|
||||||
|
blog(LOG_INFO,
|
||||||
|
"match for '%s' - but no scenes specified in '%s'",
|
||||||
|
getType(), group->name.c_str());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string sceneName = GetWeakSourceName(group->getCurrentScene());
|
||||||
|
|
||||||
|
blog(LOG_INFO, "match for '%s' - switch to scene '%s' using '%s'",
|
||||||
|
getType(), sceneName.c_str(), group->name.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
void SceneSwitcherEntry::logMatch()
|
void SceneSwitcherEntry::logMatch()
|
||||||
{
|
{
|
||||||
const char *sceneName = previous_scene_name;
|
if (targetType == SwitchTargetType::Scene) {
|
||||||
if (!usePreviousScene) {
|
logMatchScene();
|
||||||
obs_source_t *s = obs_weak_source_get_source(scene);
|
} else if (targetType == SwitchTargetType::SceneGroup) {
|
||||||
sceneName = obs_source_get_name(s);
|
logMatchSceneGroup();
|
||||||
obs_source_release(s);
|
|
||||||
}
|
}
|
||||||
blog(LOG_INFO, "match for '%s' - switch to scene '%s'", getType(),
|
|
||||||
sceneName);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SwitchWidget::SwitchWidget(SceneSwitcherEntry *s, bool usePreviousScene)
|
OBSWeakSource SceneSwitcherEntry::getScene()
|
||||||
|
{
|
||||||
|
if (targetType == SwitchTargetType::Scene) {
|
||||||
|
if (usePreviousScene && switcher) {
|
||||||
|
return switcher->previousScene;
|
||||||
|
}
|
||||||
|
return scene;
|
||||||
|
} else if (targetType == SwitchTargetType::SceneGroup) {
|
||||||
|
return group->getNextScene();
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SceneSwitcherEntry::save(obs_data_t *obj, const char *targetTypeSaveName,
|
||||||
|
const char *targetSaveName,
|
||||||
|
const char *transitionSaveName)
|
||||||
|
{
|
||||||
|
obs_data_set_int(obj, targetTypeSaveName, static_cast<int>(targetType));
|
||||||
|
|
||||||
|
std::string targetName = "";
|
||||||
|
|
||||||
|
if (targetType == SwitchTargetType::Scene) {
|
||||||
|
if (usePreviousScene) {
|
||||||
|
targetName = previous_scene_name;
|
||||||
|
} else {
|
||||||
|
targetName = GetWeakSourceName(scene);
|
||||||
|
}
|
||||||
|
} else if (targetType == SwitchTargetType::SceneGroup) {
|
||||||
|
targetName = group->name.c_str();
|
||||||
|
}
|
||||||
|
|
||||||
|
obs_data_set_string(obj, targetSaveName, targetName.c_str());
|
||||||
|
|
||||||
|
std::string transitionName = current_transition_name;
|
||||||
|
if (!useCurrentTransition) {
|
||||||
|
transitionName = GetWeakSourceName(transition);
|
||||||
|
}
|
||||||
|
obs_data_set_string(obj, transitionSaveName, transitionName.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
void SceneSwitcherEntry::load(obs_data_t *obj, const char *targetTypeLoadName,
|
||||||
|
const char *targetLoadName,
|
||||||
|
const char *transitionLoadName)
|
||||||
|
{
|
||||||
|
targetType = static_cast<SwitchTargetType>(
|
||||||
|
obs_data_get_int(obj, targetTypeLoadName));
|
||||||
|
|
||||||
|
const char *targetName = obs_data_get_string(obj, targetLoadName);
|
||||||
|
|
||||||
|
if (targetType == SwitchTargetType::Scene) {
|
||||||
|
usePreviousScene = strcmp(targetName, previous_scene_name) == 0;
|
||||||
|
if (!usePreviousScene) {
|
||||||
|
scene = GetWeakSourceByName(targetName);
|
||||||
|
}
|
||||||
|
} else if (targetType == SwitchTargetType::SceneGroup) {
|
||||||
|
group = GetSceneGroupByName(targetName);
|
||||||
|
}
|
||||||
|
|
||||||
|
usePreviousScene = strcmp(targetName, previous_scene_name) == 0;
|
||||||
|
|
||||||
|
const char *transitionName =
|
||||||
|
obs_data_get_string(obj, transitionLoadName);
|
||||||
|
transition = GetWeakTransitionByName(transitionName);
|
||||||
|
|
||||||
|
useCurrentTransition =
|
||||||
|
strcmp(transitionName, current_transition_name) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SwitchWidget::SceneGroupAdd(const QString &name)
|
||||||
|
{
|
||||||
|
if (!scenes) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
scenes->addItem(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SwitchWidget::SceneGroupRemove(const QString &name)
|
||||||
|
{
|
||||||
|
if (!scenes) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int idx = scenes->findText(name);
|
||||||
|
|
||||||
|
if (idx == -1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
scenes->removeItem(idx);
|
||||||
|
|
||||||
|
if (switchData && switchData->group == GetSceneGroupByQString(name)) {
|
||||||
|
std::lock_guard<std::mutex> lock(switcher->m);
|
||||||
|
switchData->targetType = SwitchTargetType::Scene;
|
||||||
|
switchData->scene = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
scenes->setCurrentIndex(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SwitchWidget::SceneGroupRename(const QString &oldName,
|
||||||
|
const QString &newName)
|
||||||
|
{
|
||||||
|
if (!scenes) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool renameSelected = scenes->currentText() == oldName;
|
||||||
|
int idx = scenes->findText(oldName);
|
||||||
|
|
||||||
|
if (idx == -1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
scenes->removeItem(idx);
|
||||||
|
scenes->insertItem(idx, newName);
|
||||||
|
|
||||||
|
if (renameSelected) {
|
||||||
|
scenes->setCurrentIndex(scenes->findText(newName));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SwitchWidget::SwitchWidget(QWidget *parent, SceneSwitcherEntry *s,
|
||||||
|
bool usePreviousScene, bool addSceneGroup,
|
||||||
|
bool addCurrentTransition)
|
||||||
{
|
{
|
||||||
scenes = new QComboBox();
|
scenes = new QComboBox();
|
||||||
transitions = new QComboBox();
|
transitions = new QComboBox();
|
||||||
@@ -42,21 +191,22 @@ SwitchWidget::SwitchWidget(SceneSwitcherEntry *s, bool usePreviousScene)
|
|||||||
SIGNAL(currentTextChanged(const QString &)), this,
|
SIGNAL(currentTextChanged(const QString &)), this,
|
||||||
SLOT(TransitionChanged(const QString &)));
|
SLOT(TransitionChanged(const QString &)));
|
||||||
|
|
||||||
AdvSceneSwitcher::populateSceneSelection(scenes, usePreviousScene);
|
QWidget::connect(parent, SIGNAL(SceneGroupAdded(const QString &)), this,
|
||||||
AdvSceneSwitcher::populateTransitionSelection(transitions);
|
SLOT(SceneGroupAdd(const QString &)));
|
||||||
|
QWidget::connect(parent, SIGNAL(SceneGroupRemoved(const QString &)),
|
||||||
|
this, SLOT(SceneGroupRemove(const QString &)));
|
||||||
|
QWidget::connect(
|
||||||
|
parent,
|
||||||
|
SIGNAL(SceneGroupRenamed(const QString &, const QString &)),
|
||||||
|
this, SLOT(SceneGroupRename(const QString &, const QString &)));
|
||||||
|
|
||||||
if (s) {
|
AdvSceneSwitcher::populateSceneSelection(scenes, usePreviousScene,
|
||||||
if (s->usePreviousScene)
|
addSceneGroup);
|
||||||
scenes->setCurrentText(obs_module_text(
|
AdvSceneSwitcher::populateTransitionSelection(transitions,
|
||||||
"AdvSceneSwitcher.selectPreviousScene"));
|
addCurrentTransition);
|
||||||
else
|
|
||||||
scenes->setCurrentText(
|
|
||||||
GetWeakSourceName(s->scene).c_str());
|
|
||||||
transitions->setCurrentText(
|
|
||||||
GetWeakSourceName(s->transition).c_str());
|
|
||||||
}
|
|
||||||
|
|
||||||
switchData = s;
|
switchData = s;
|
||||||
|
showSwitchData();
|
||||||
}
|
}
|
||||||
|
|
||||||
SceneSwitcherEntry *SwitchWidget::getSwitchData()
|
SceneSwitcherEntry *SwitchWidget::getSwitchData()
|
||||||
@@ -69,6 +219,34 @@ void SwitchWidget::setSwitchData(SceneSwitcherEntry *s)
|
|||||||
switchData = s;
|
switchData = s;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SwitchWidget::showSwitchData()
|
||||||
|
{
|
||||||
|
if (!switchData) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
transitions->setCurrentText(
|
||||||
|
GetWeakSourceName(switchData->transition).c_str());
|
||||||
|
if (switchData->useCurrentTransition) {
|
||||||
|
transitions->setCurrentText(
|
||||||
|
obs_module_text("AdvSceneSwitcher.currentTransition"));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (switchData->usePreviousScene) {
|
||||||
|
scenes->setCurrentText(obs_module_text(
|
||||||
|
"AdvSceneSwitcher.selectPreviousScene"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
scenes->setCurrentText(GetWeakSourceName(switchData->scene).c_str());
|
||||||
|
|
||||||
|
if (switchData->group &&
|
||||||
|
switchData->targetType == SwitchTargetType::SceneGroup) {
|
||||||
|
scenes->setCurrentText(
|
||||||
|
QString::fromStdString(switchData->group->name));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void SwitchWidget::swapSwitchData(SwitchWidget *s1, SwitchWidget *s2)
|
void SwitchWidget::swapSwitchData(SwitchWidget *s1, SwitchWidget *s2)
|
||||||
{
|
{
|
||||||
SceneSwitcherEntry *t = s1->getSwitchData();
|
SceneSwitcherEntry *t = s1->getSwitchData();
|
||||||
@@ -84,19 +262,34 @@ bool isPreviousScene(const QString &text)
|
|||||||
|
|
||||||
void SwitchWidget::SceneChanged(const QString &text)
|
void SwitchWidget::SceneChanged(const QString &text)
|
||||||
{
|
{
|
||||||
if (loading || !switchData)
|
if (loading || !switchData) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
std::lock_guard<std::mutex> lock(switcher->m);
|
std::lock_guard<std::mutex> lock(switcher->m);
|
||||||
|
|
||||||
switchData->usePreviousScene = isPreviousScene(text);
|
switchData->usePreviousScene = isPreviousScene(text);
|
||||||
if (switchData->usePreviousScene)
|
if (switchData->usePreviousScene) {
|
||||||
|
switchData->targetType = SwitchTargetType::Scene;
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
switchData->scene = GetWeakSourceByQString(text);
|
switchData->scene = GetWeakSourceByQString(text);
|
||||||
|
switchData->targetType = SwitchTargetType::Scene;
|
||||||
|
|
||||||
|
if (!switchData->scene) {
|
||||||
|
switchData->group = GetSceneGroupByQString(text);
|
||||||
|
switchData->targetType = SwitchTargetType::SceneGroup;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void SwitchWidget::TransitionChanged(const QString &text)
|
void SwitchWidget::TransitionChanged(const QString &text)
|
||||||
{
|
{
|
||||||
if (loading || !switchData)
|
if (loading || !switchData) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
std::lock_guard<std::mutex> lock(switcher->m);
|
std::lock_guard<std::mutex> lock(switcher->m);
|
||||||
switchData->transition = GetWeakTransitionByQString(text);
|
switchData->transition = GetWeakTransitionByQString(text);
|
||||||
|
switchData->useCurrentTransition = switchData->transition == nullptr;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,18 +4,18 @@
|
|||||||
#include "headers/utility.hpp"
|
#include "headers/utility.hpp"
|
||||||
|
|
||||||
bool IdleData::pause = false;
|
bool IdleData::pause = false;
|
||||||
|
IdleWidget *idleWidget = nullptr;
|
||||||
|
|
||||||
void SwitcherData::checkIdleSwitch(bool &match, OBSWeakSource &scene,
|
void SwitcherData::checkIdleSwitch(bool &match, OBSWeakSource &scene,
|
||||||
OBSWeakSource &transition)
|
OBSWeakSource &transition)
|
||||||
{
|
{
|
||||||
if (!idleData.idleEnable || IdleData::pause)
|
if (!idleData.idleEnable || IdleData::pause) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
std::string title;
|
std::string title;
|
||||||
bool ignoreIdle = false;
|
bool ignoreIdle = false;
|
||||||
//lock.unlock();
|
|
||||||
GetCurrentWindowTitle(title);
|
GetCurrentWindowTitle(title);
|
||||||
//lock.lock();
|
|
||||||
|
|
||||||
for (std::string &window : ignoreIdleWindows) {
|
for (std::string &window : ignoreIdleWindows) {
|
||||||
if (window == title) {
|
if (window == title) {
|
||||||
@@ -39,96 +39,45 @@ void SwitcherData::checkIdleSwitch(bool &match, OBSWeakSource &scene,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!ignoreIdle && secondsSinceLastInput() > idleData.time) {
|
if (!ignoreIdle && secondsSinceLastInput() > idleData.time) {
|
||||||
if (idleData.alreadySwitched)
|
if (idleData.alreadySwitched) {
|
||||||
return;
|
return;
|
||||||
scene = (idleData.usePreviousScene) ? previousScene
|
}
|
||||||
: idleData.scene;
|
scene = idleData.getScene();
|
||||||
transition = idleData.transition;
|
transition = idleData.transition;
|
||||||
match = true;
|
match = true;
|
||||||
idleData.alreadySwitched = true;
|
idleData.alreadySwitched = true;
|
||||||
|
|
||||||
if (verbose)
|
if (verbose) {
|
||||||
idleData.logMatch();
|
idleData.logMatch();
|
||||||
|
}
|
||||||
} else
|
} else
|
||||||
idleData.alreadySwitched = false;
|
idleData.alreadySwitched = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void AdvSceneSwitcher::on_idleCheckBox_stateChanged(int state)
|
void AdvSceneSwitcher::on_idleCheckBox_stateChanged(int state)
|
||||||
{
|
{
|
||||||
if (loading)
|
if (loading) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
std::lock_guard<std::mutex> lock(switcher->m);
|
std::lock_guard<std::mutex> lock(switcher->m);
|
||||||
if (!state) {
|
if (!state) {
|
||||||
ui->idleScenes->setDisabled(true);
|
|
||||||
ui->idleSpinBox->setDisabled(true);
|
|
||||||
ui->idleTransitions->setDisabled(true);
|
|
||||||
|
|
||||||
switcher->idleData.idleEnable = false;
|
switcher->idleData.idleEnable = false;
|
||||||
|
idleWidget->setDisabled(true);
|
||||||
} else {
|
} else {
|
||||||
ui->idleScenes->setDisabled(false);
|
|
||||||
ui->idleSpinBox->setDisabled(false);
|
|
||||||
ui->idleTransitions->setDisabled(false);
|
|
||||||
|
|
||||||
switcher->idleData.idleEnable = true;
|
switcher->idleData.idleEnable = true;
|
||||||
|
idleWidget->setDisabled(false);
|
||||||
UpdateIdleDataTransition(ui->idleTransitions->currentText());
|
|
||||||
UpdateIdleDataScene(ui->idleScenes->currentText());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void AdvSceneSwitcher::UpdateIdleDataTransition(const QString &name)
|
|
||||||
{
|
|
||||||
obs_weak_source_t *transition = GetWeakTransitionByQString(name);
|
|
||||||
switcher->idleData.transition = transition;
|
|
||||||
}
|
|
||||||
|
|
||||||
void AdvSceneSwitcher::UpdateIdleDataScene(const QString &name)
|
|
||||||
{
|
|
||||||
switcher->idleData.usePreviousScene =
|
|
||||||
(name ==
|
|
||||||
obs_module_text("AdvSceneSwitcher.selectPreviousScene"));
|
|
||||||
obs_source_t *scene = obs_get_source_by_name(name.toUtf8().constData());
|
|
||||||
obs_weak_source_t *ws = obs_source_get_weak_source(scene);
|
|
||||||
|
|
||||||
switcher->idleData.scene = ws;
|
|
||||||
|
|
||||||
obs_weak_source_release(ws);
|
|
||||||
obs_source_release(scene);
|
|
||||||
}
|
|
||||||
|
|
||||||
void AdvSceneSwitcher::on_idleTransitions_currentTextChanged(const QString &text)
|
|
||||||
{
|
|
||||||
if (loading)
|
|
||||||
return;
|
|
||||||
|
|
||||||
std::lock_guard<std::mutex> lock(switcher->m);
|
|
||||||
UpdateIdleDataTransition(text);
|
|
||||||
}
|
|
||||||
|
|
||||||
void AdvSceneSwitcher::on_idleScenes_currentTextChanged(const QString &text)
|
|
||||||
{
|
|
||||||
if (loading)
|
|
||||||
return;
|
|
||||||
|
|
||||||
std::lock_guard<std::mutex> lock(switcher->m);
|
|
||||||
UpdateIdleDataScene(text);
|
|
||||||
}
|
|
||||||
|
|
||||||
void AdvSceneSwitcher::on_idleSpinBox_valueChanged(int i)
|
|
||||||
{
|
|
||||||
if (loading)
|
|
||||||
return;
|
|
||||||
std::lock_guard<std::mutex> lock(switcher->m);
|
|
||||||
switcher->idleData.time = i;
|
|
||||||
}
|
|
||||||
|
|
||||||
void AdvSceneSwitcher::on_ignoreIdleWindows_currentRowChanged(int idx)
|
void AdvSceneSwitcher::on_ignoreIdleWindows_currentRowChanged(int idx)
|
||||||
{
|
{
|
||||||
if (loading)
|
if (loading) {
|
||||||
return;
|
return;
|
||||||
if (idx == -1)
|
}
|
||||||
|
if (idx == -1) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
QListWidgetItem *item = ui->ignoreIdleWindows->item(idx);
|
QListWidgetItem *item = ui->ignoreIdleWindows->item(idx);
|
||||||
|
|
||||||
@@ -147,8 +96,9 @@ void AdvSceneSwitcher::on_ignoreIdleAdd_clicked()
|
|||||||
{
|
{
|
||||||
QString windowName = ui->ignoreIdleWindowsWindows->currentText();
|
QString windowName = ui->ignoreIdleWindowsWindows->currentText();
|
||||||
|
|
||||||
if (windowName.isEmpty())
|
if (windowName.isEmpty()) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
QVariant v = QVariant::fromValue(windowName);
|
QVariant v = QVariant::fromValue(windowName);
|
||||||
|
|
||||||
@@ -170,8 +120,9 @@ void AdvSceneSwitcher::on_ignoreIdleAdd_clicked()
|
|||||||
void AdvSceneSwitcher::on_ignoreIdleRemove_clicked()
|
void AdvSceneSwitcher::on_ignoreIdleRemove_clicked()
|
||||||
{
|
{
|
||||||
QListWidgetItem *item = ui->ignoreIdleWindows->currentItem();
|
QListWidgetItem *item = ui->ignoreIdleWindows->currentItem();
|
||||||
if (!item)
|
if (!item) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
QString windowName = item->data(Qt::UserRole).toString();
|
QString windowName = item->data(Qt::UserRole).toString();
|
||||||
|
|
||||||
@@ -222,17 +173,7 @@ void SwitcherData::saveIdleSwitches(obs_data_t *obj)
|
|||||||
obs_data_set_array(obj, "ignoreIdleWindows", ignoreIdleWindowsArray);
|
obs_data_set_array(obj, "ignoreIdleWindows", ignoreIdleWindowsArray);
|
||||||
obs_data_array_release(ignoreIdleWindowsArray);
|
obs_data_array_release(ignoreIdleWindowsArray);
|
||||||
|
|
||||||
std::string idleSceneName = GetWeakSourceName(switcher->idleData.scene);
|
idleData.save(obj);
|
||||||
std::string idleTransitionName =
|
|
||||||
GetWeakSourceName(switcher->idleData.transition);
|
|
||||||
obs_data_set_bool(obj, "idleEnable", switcher->idleData.idleEnable);
|
|
||||||
obs_data_set_string(obj, "idleSceneName",
|
|
||||||
switcher->idleData.usePreviousScene
|
|
||||||
? previous_scene_name
|
|
||||||
: idleSceneName.c_str());
|
|
||||||
obs_data_set_string(obj, "idleTransitionName",
|
|
||||||
idleTransitionName.c_str());
|
|
||||||
obs_data_set_int(obj, "idleTime", switcher->idleData.time);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void SwitcherData::loadIdleSwitches(obs_data_t *obj)
|
void SwitcherData::loadIdleSwitches(obs_data_t *obj)
|
||||||
@@ -255,24 +196,14 @@ void SwitcherData::loadIdleSwitches(obs_data_t *obj)
|
|||||||
}
|
}
|
||||||
obs_data_array_release(ignoreIdleWindowsArray);
|
obs_data_array_release(ignoreIdleWindowsArray);
|
||||||
|
|
||||||
std::string idleSceneName = obs_data_get_string(obj, "idleSceneName");
|
|
||||||
std::string idleTransitionName =
|
|
||||||
obs_data_get_string(obj, "idleTransitionName");
|
|
||||||
switcher->idleData.scene = GetWeakSourceByName(idleSceneName.c_str());
|
|
||||||
switcher->idleData.transition =
|
|
||||||
GetWeakTransitionByName(idleTransitionName.c_str());
|
|
||||||
obs_data_set_default_bool(obj, "idleEnable", false);
|
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 =
|
idleData.load(obj);
|
||||||
(idleSceneName == previous_scene_name);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void AdvSceneSwitcher::setupIdleTab()
|
void AdvSceneSwitcher::setupIdleTab()
|
||||||
{
|
{
|
||||||
populateSceneSelection(ui->idleScenes, true);
|
|
||||||
populateTransitionSelection(ui->idleTransitions);
|
|
||||||
populateWindowSelection(ui->ignoreIdleWindowsWindows);
|
populateWindowSelection(ui->ignoreIdleWindowsWindows);
|
||||||
|
|
||||||
for (auto &window : switcher->ignoreIdleWindows) {
|
for (auto &window : switcher->ignoreIdleWindows) {
|
||||||
@@ -283,25 +214,71 @@ void AdvSceneSwitcher::setupIdleTab()
|
|||||||
item->setData(Qt::UserRole, text);
|
item->setData(Qt::UserRole, text);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
idleWidget = new IdleWidget(this, &switcher->idleData);
|
||||||
|
ui->idleWidgetLayout->addWidget(idleWidget);
|
||||||
ui->idleCheckBox->setChecked(switcher->idleData.idleEnable);
|
ui->idleCheckBox->setChecked(switcher->idleData.idleEnable);
|
||||||
if (switcher->idleData.usePreviousScene) {
|
|
||||||
ui->idleScenes->setCurrentText(obs_module_text(
|
|
||||||
"AdvSceneSwitcher.selectPreviousScene"));
|
|
||||||
} else {
|
|
||||||
ui->idleScenes->setCurrentText(
|
|
||||||
GetWeakSourceName(switcher->idleData.scene).c_str());
|
|
||||||
}
|
|
||||||
ui->idleTransitions->setCurrentText(
|
|
||||||
GetWeakSourceName(switcher->idleData.transition).c_str());
|
|
||||||
ui->idleSpinBox->setValue(switcher->idleData.time);
|
|
||||||
|
|
||||||
if (ui->idleCheckBox->checkState()) {
|
if (ui->idleCheckBox->checkState()) {
|
||||||
ui->idleScenes->setDisabled(false);
|
idleWidget->setDisabled(false);
|
||||||
ui->idleSpinBox->setDisabled(false);
|
|
||||||
ui->idleTransitions->setDisabled(false);
|
|
||||||
} else {
|
} else {
|
||||||
ui->idleScenes->setDisabled(true);
|
idleWidget->setDisabled(true);
|
||||||
ui->idleSpinBox->setDisabled(true);
|
|
||||||
ui->idleTransitions->setDisabled(true);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void IdleData::save(obs_data_t *obj)
|
||||||
|
{
|
||||||
|
SceneSwitcherEntry::save(obj, "idleTargetType", "idleSceneName",
|
||||||
|
"idleTransitionName");
|
||||||
|
|
||||||
|
obs_data_set_bool(obj, "idleEnable", idleEnable);
|
||||||
|
obs_data_set_int(obj, "idleTime", time);
|
||||||
|
}
|
||||||
|
|
||||||
|
void IdleData::load(obs_data_t *obj)
|
||||||
|
{
|
||||||
|
SceneSwitcherEntry::load(obj, "idleTargetType", "idleSceneName",
|
||||||
|
"idleTransitionName");
|
||||||
|
|
||||||
|
idleEnable = obs_data_get_bool(obj, "idleEnable");
|
||||||
|
time = obs_data_get_int(obj, "idleTime");
|
||||||
|
}
|
||||||
|
|
||||||
|
IdleWidget::IdleWidget(QWidget *parent, IdleData *s)
|
||||||
|
: SwitchWidget(parent, s, true, true)
|
||||||
|
{
|
||||||
|
duration = new QSpinBox();
|
||||||
|
|
||||||
|
duration->setMinimum(0);
|
||||||
|
duration->setMaximum(1000000);
|
||||||
|
duration->setSuffix("s");
|
||||||
|
|
||||||
|
QWidget::connect(duration, SIGNAL(valueChanged(int)), this,
|
||||||
|
SLOT(DurationChanged(int)));
|
||||||
|
|
||||||
|
if (s) {
|
||||||
|
duration->setValue(s->time);
|
||||||
|
}
|
||||||
|
|
||||||
|
QHBoxLayout *mainLayout = new QHBoxLayout;
|
||||||
|
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
|
||||||
|
{"{{duration}}", duration},
|
||||||
|
{"{{scenes}}", scenes},
|
||||||
|
{"{{transitions}}", transitions}};
|
||||||
|
placeWidgets(obs_module_text("AdvSceneSwitcher.idleTab.idleswitch"),
|
||||||
|
mainLayout, widgetPlaceholders);
|
||||||
|
setLayout(mainLayout);
|
||||||
|
|
||||||
|
switchData = s;
|
||||||
|
|
||||||
|
loading = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void IdleWidget::DurationChanged(int dur)
|
||||||
|
{
|
||||||
|
if (loading) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::lock_guard<std::mutex> lock(switcher->m);
|
||||||
|
switcher->idleData.time = dur;
|
||||||
|
}
|
||||||
|
|||||||
@@ -13,15 +13,19 @@ void AdvSceneSwitcher::on_mediaAdd_clicked()
|
|||||||
switcher->mediaSwitches.emplace_back();
|
switcher->mediaSwitches.emplace_back();
|
||||||
|
|
||||||
listAddClicked(ui->mediaSwitches,
|
listAddClicked(ui->mediaSwitches,
|
||||||
new MediaSwitchWidget(&switcher->mediaSwitches.back()),
|
new MediaSwitchWidget(this,
|
||||||
|
&switcher->mediaSwitches.back()),
|
||||||
ui->mediaAdd, &addPulse);
|
ui->mediaAdd, &addPulse);
|
||||||
|
|
||||||
|
ui->mediaHelp->setVisible(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AdvSceneSwitcher::on_mediaRemove_clicked()
|
void AdvSceneSwitcher::on_mediaRemove_clicked()
|
||||||
{
|
{
|
||||||
QListWidgetItem *item = ui->mediaSwitches->currentItem();
|
QListWidgetItem *item = ui->mediaSwitches->currentItem();
|
||||||
if (!item)
|
if (!item) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> lock(switcher->m);
|
std::lock_guard<std::mutex> lock(switcher->m);
|
||||||
@@ -36,8 +40,9 @@ void AdvSceneSwitcher::on_mediaRemove_clicked()
|
|||||||
void AdvSceneSwitcher::on_mediaUp_clicked()
|
void AdvSceneSwitcher::on_mediaUp_clicked()
|
||||||
{
|
{
|
||||||
int index = ui->mediaSwitches->currentRow();
|
int index = ui->mediaSwitches->currentRow();
|
||||||
if (!listMoveUp(ui->mediaSwitches))
|
if (!listMoveUp(ui->mediaSwitches)) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
MediaSwitchWidget *s1 =
|
MediaSwitchWidget *s1 =
|
||||||
(MediaSwitchWidget *)ui->mediaSwitches->itemWidget(
|
(MediaSwitchWidget *)ui->mediaSwitches->itemWidget(
|
||||||
@@ -57,8 +62,9 @@ void AdvSceneSwitcher::on_mediaDown_clicked()
|
|||||||
{
|
{
|
||||||
int index = ui->mediaSwitches->currentRow();
|
int index = ui->mediaSwitches->currentRow();
|
||||||
|
|
||||||
if (!listMoveDown(ui->mediaSwitches))
|
if (!listMoveDown(ui->mediaSwitches)) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
MediaSwitchWidget *s1 =
|
MediaSwitchWidget *s1 =
|
||||||
(MediaSwitchWidget *)ui->mediaSwitches->itemWidget(
|
(MediaSwitchWidget *)ui->mediaSwitches->itemWidget(
|
||||||
@@ -77,12 +83,14 @@ void AdvSceneSwitcher::on_mediaDown_clicked()
|
|||||||
void SwitcherData::checkMediaSwitch(bool &match, OBSWeakSource &scene,
|
void SwitcherData::checkMediaSwitch(bool &match, OBSWeakSource &scene,
|
||||||
OBSWeakSource &transition)
|
OBSWeakSource &transition)
|
||||||
{
|
{
|
||||||
if (MediaSwitch::pause)
|
if (MediaSwitch::pause) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
for (MediaSwitch &mediaSwitch : mediaSwitches) {
|
for (MediaSwitch &mediaSwitch : mediaSwitches) {
|
||||||
if (!mediaSwitch.initialized())
|
if (!mediaSwitch.initialized()) {
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
obs_source_t *source =
|
obs_source_t *source =
|
||||||
obs_weak_source_get_source(mediaSwitch.source);
|
obs_weak_source_get_source(mediaSwitch.source);
|
||||||
@@ -135,8 +143,9 @@ void SwitcherData::checkMediaSwitch(bool &match, OBSWeakSource &scene,
|
|||||||
(duration - time <= interval * 2);
|
(duration - time <= interval * 2);
|
||||||
|
|
||||||
// reset
|
// reset
|
||||||
if (ended)
|
if (ended) {
|
||||||
mediaSwitch.playedToEnd = false;
|
mediaSwitch.playedToEnd = false;
|
||||||
|
}
|
||||||
|
|
||||||
// reset for next check
|
// reset for next check
|
||||||
mediaSwitch.stopped = false;
|
mediaSwitch.stopped = false;
|
||||||
@@ -173,19 +182,19 @@ void SwitcherData::checkMediaSwitch(bool &match, OBSWeakSource &scene,
|
|||||||
|
|
||||||
if (matched && !mediaSwitch.matched) {
|
if (matched && !mediaSwitch.matched) {
|
||||||
match = true;
|
match = true;
|
||||||
scene = (mediaSwitch.usePreviousScene)
|
scene = mediaSwitch.getScene();
|
||||||
? previousScene
|
|
||||||
: mediaSwitch.scene;
|
|
||||||
transition = mediaSwitch.transition;
|
transition = mediaSwitch.transition;
|
||||||
|
|
||||||
if (verbose)
|
if (verbose) {
|
||||||
mediaSwitch.logMatch();
|
mediaSwitch.logMatch();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mediaSwitch.matched = matched;
|
mediaSwitch.matched = matched;
|
||||||
|
|
||||||
if (match)
|
if (match) {
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -195,33 +204,8 @@ void SwitcherData::saveMediaSwitches(obs_data_t *obj)
|
|||||||
for (MediaSwitch &s : switcher->mediaSwitches) {
|
for (MediaSwitch &s : switcher->mediaSwitches) {
|
||||||
obs_data_t *array_obj = obs_data_create();
|
obs_data_t *array_obj = obs_data_create();
|
||||||
|
|
||||||
obs_source_t *source = obs_weak_source_get_source(s.source);
|
s.save(array_obj);
|
||||||
obs_source_t *sceneSource = obs_weak_source_get_source(s.scene);
|
obs_data_array_push_back(mediaArray, array_obj);
|
||||||
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_release(array_obj);
|
||||||
}
|
}
|
||||||
@@ -238,21 +222,8 @@ void SwitcherData::loadMediaSwitches(obs_data_t *obj)
|
|||||||
for (size_t i = 0; i < count; i++) {
|
for (size_t i = 0; i < count; i++) {
|
||||||
obs_data_t *array_obj = obs_data_array_item(mediaArray, i);
|
obs_data_t *array_obj = obs_data_array_item(mediaArray, i);
|
||||||
|
|
||||||
const char *source = obs_data_get_string(array_obj, "source");
|
switcher->mediaSwitches.emplace_back();
|
||||||
const char *scene = obs_data_get_string(array_obj, "scene");
|
mediaSwitches.back().load(array_obj);
|
||||||
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_release(array_obj);
|
||||||
}
|
}
|
||||||
@@ -265,13 +236,17 @@ void AdvSceneSwitcher::setupMediaTab()
|
|||||||
QListWidgetItem *item;
|
QListWidgetItem *item;
|
||||||
item = new QListWidgetItem(ui->mediaSwitches);
|
item = new QListWidgetItem(ui->mediaSwitches);
|
||||||
ui->mediaSwitches->addItem(item);
|
ui->mediaSwitches->addItem(item);
|
||||||
MediaSwitchWidget *sw = new MediaSwitchWidget(&s);
|
MediaSwitchWidget *sw = new MediaSwitchWidget(this, &s);
|
||||||
item->setSizeHint(sw->minimumSizeHint());
|
item->setSizeHint(sw->minimumSizeHint());
|
||||||
ui->mediaSwitches->setItemWidget(item, sw);
|
ui->mediaSwitches->setItemWidget(item, sw);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (switcher->mediaSwitches.size() == 0)
|
if (switcher->mediaSwitches.size() == 0) {
|
||||||
addPulse = PulseWidget(ui->mediaAdd, QColor(Qt::green));
|
addPulse = PulseWidget(ui->mediaAdd, QColor(Qt::green));
|
||||||
|
ui->mediaHelp->setVisible(true);
|
||||||
|
} else {
|
||||||
|
ui->mediaHelp->setVisible(false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool MediaSwitch::initialized()
|
bool MediaSwitch::initialized()
|
||||||
@@ -285,6 +260,70 @@ bool MediaSwitch::valid()
|
|||||||
(SceneSwitcherEntry::valid() && WeakSourceValid(source));
|
(SceneSwitcherEntry::valid() && WeakSourceValid(source));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MediaSwitch::save(obs_data_t *obj)
|
||||||
|
{
|
||||||
|
SceneSwitcherEntry::save(obj);
|
||||||
|
|
||||||
|
obs_data_set_string(obj, "source", GetWeakSourceName(source).c_str());
|
||||||
|
|
||||||
|
obs_data_set_int(obj, "state", state);
|
||||||
|
obs_data_set_int(obj, "restriction", restriction);
|
||||||
|
obs_data_set_int(obj, "time", time);
|
||||||
|
}
|
||||||
|
|
||||||
|
// To be removed in future version
|
||||||
|
bool loadOldMedia(obs_data_t *obj, MediaSwitch *s)
|
||||||
|
{
|
||||||
|
if (!s) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *scene = obs_data_get_string(obj, "scene");
|
||||||
|
|
||||||
|
if (strcmp(scene, "") == 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
s->scene = GetWeakSourceByName(scene);
|
||||||
|
|
||||||
|
const char *transition = obs_data_get_string(obj, "transition");
|
||||||
|
s->transition = GetWeakTransitionByName(transition);
|
||||||
|
|
||||||
|
const char *source = obs_data_get_string(obj, "source");
|
||||||
|
s->source = GetWeakSourceByName(source);
|
||||||
|
|
||||||
|
s->state = (obs_media_state)obs_data_get_int(obj, "state");
|
||||||
|
s->restriction = (time_restriction)obs_data_get_int(obj, "restriction");
|
||||||
|
s->time = obs_data_get_int(obj, "time");
|
||||||
|
s->usePreviousScene = strcmp(scene, previous_scene_name) == 0;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MediaSwitch::load(obs_data_t *obj)
|
||||||
|
{
|
||||||
|
|
||||||
|
if (loadOldMedia(obj, this)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
SceneSwitcherEntry::load(obj);
|
||||||
|
|
||||||
|
const char *sourceName = obs_data_get_string(obj, "source");
|
||||||
|
source = GetWeakSourceByName(sourceName);
|
||||||
|
|
||||||
|
state = (obs_media_state)obs_data_get_int(obj, "state");
|
||||||
|
restriction = (time_restriction)obs_data_get_int(obj, "restriction");
|
||||||
|
time = obs_data_get_int(obj, "time");
|
||||||
|
|
||||||
|
anyState = state == media_any_idx;
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
void MediaSwitch::clearSignalHandler()
|
void MediaSwitch::clearSignalHandler()
|
||||||
{
|
{
|
||||||
obs_source_t *mediasource = obs_weak_source_get_source(source);
|
obs_source_t *mediasource = obs_weak_source_get_source(source);
|
||||||
@@ -317,28 +356,9 @@ void MediaSwitch::MediaEnded(void *data, calldata_t *)
|
|||||||
media->ended = true;
|
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 == media_any_idx;
|
|
||||||
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)
|
MediaSwitch::MediaSwitch(const MediaSwitch &other)
|
||||||
: SceneSwitcherEntry(other.scene, other.transition,
|
: SceneSwitcherEntry(other.targetType, other.group, other.scene,
|
||||||
other.usePreviousScene),
|
other.transition, other.usePreviousScene),
|
||||||
source(other.source),
|
source(other.source),
|
||||||
state(other.state),
|
state(other.state),
|
||||||
restriction(other.restriction),
|
restriction(other.restriction),
|
||||||
@@ -353,8 +373,8 @@ MediaSwitch::MediaSwitch(const MediaSwitch &other)
|
|||||||
}
|
}
|
||||||
|
|
||||||
MediaSwitch::MediaSwitch(MediaSwitch &&other)
|
MediaSwitch::MediaSwitch(MediaSwitch &&other)
|
||||||
: SceneSwitcherEntry(other.scene, other.transition,
|
: SceneSwitcherEntry(other.targetType, other.group, other.scene,
|
||||||
other.usePreviousScene),
|
other.transition, other.usePreviousScene),
|
||||||
source(other.source),
|
source(other.source),
|
||||||
state(other.state),
|
state(other.state),
|
||||||
anyState(other.anyState),
|
anyState(other.anyState),
|
||||||
@@ -398,6 +418,8 @@ MediaSwitch &MediaSwitch::operator=(MediaSwitch &&other) noexcept
|
|||||||
|
|
||||||
void swap(MediaSwitch &first, MediaSwitch &second)
|
void swap(MediaSwitch &first, MediaSwitch &second)
|
||||||
{
|
{
|
||||||
|
std::swap(first.targetType, second.targetType);
|
||||||
|
std::swap(first.group, second.group);
|
||||||
std::swap(first.scene, second.scene);
|
std::swap(first.scene, second.scene);
|
||||||
std::swap(first.transition, second.transition);
|
std::swap(first.transition, second.transition);
|
||||||
std::swap(first.usePreviousScene, second.usePreviousScene);
|
std::swap(first.usePreviousScene, second.usePreviousScene);
|
||||||
@@ -446,7 +468,8 @@ void populateTimeRestrictions(QComboBox *list)
|
|||||||
"AdvSceneSwitcher.mediaTab.timeRestriction.remainLonger"));
|
"AdvSceneSwitcher.mediaTab.timeRestriction.remainLonger"));
|
||||||
}
|
}
|
||||||
|
|
||||||
MediaSwitchWidget::MediaSwitchWidget(MediaSwitch *s) : SwitchWidget(s)
|
MediaSwitchWidget::MediaSwitchWidget(QWidget *parent, MediaSwitch *s)
|
||||||
|
: SwitchWidget(parent, s, true, true)
|
||||||
{
|
{
|
||||||
mediaSources = new QComboBox();
|
mediaSources = new QComboBox();
|
||||||
states = new QComboBox();
|
states = new QComboBox();
|
||||||
@@ -477,8 +500,9 @@ MediaSwitchWidget::MediaSwitchWidget(MediaSwitch *s) : SwitchWidget(s)
|
|||||||
states->setCurrentIndex(s->state);
|
states->setCurrentIndex(s->state);
|
||||||
timeRestrictions->setCurrentIndex(s->restriction);
|
timeRestrictions->setCurrentIndex(s->restriction);
|
||||||
time->setValue(s->time);
|
time->setValue(s->time);
|
||||||
if (s->restriction == TIME_RESTRICTION_NONE)
|
if (s->restriction == TIME_RESTRICTION_NONE) {
|
||||||
time->setDisabled(true);
|
time->setDisabled(true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QHBoxLayout *mainLayout = new QHBoxLayout;
|
QHBoxLayout *mainLayout = new QHBoxLayout;
|
||||||
@@ -520,8 +544,10 @@ void MediaSwitchWidget::swapSwitchData(MediaSwitchWidget *s1,
|
|||||||
|
|
||||||
void MediaSwitchWidget::SourceChanged(const QString &text)
|
void MediaSwitchWidget::SourceChanged(const QString &text)
|
||||||
{
|
{
|
||||||
if (loading || !switchData)
|
if (loading || !switchData) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
std::lock_guard<std::mutex> lock(switcher->m);
|
std::lock_guard<std::mutex> lock(switcher->m);
|
||||||
switchData->clearSignalHandler();
|
switchData->clearSignalHandler();
|
||||||
switchData->source = GetWeakSourceByQString(text);
|
switchData->source = GetWeakSourceByQString(text);
|
||||||
@@ -530,8 +556,10 @@ void MediaSwitchWidget::SourceChanged(const QString &text)
|
|||||||
|
|
||||||
void MediaSwitchWidget::StateChanged(int index)
|
void MediaSwitchWidget::StateChanged(int index)
|
||||||
{
|
{
|
||||||
if (loading || !switchData)
|
if (loading || !switchData) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
std::lock_guard<std::mutex> lock(switcher->m);
|
std::lock_guard<std::mutex> lock(switcher->m);
|
||||||
switchData->state = (obs_media_state)index;
|
switchData->state = (obs_media_state)index;
|
||||||
switchData->anyState = switchData->state == media_any_idx;
|
switchData->anyState = switchData->state == media_any_idx;
|
||||||
@@ -539,13 +567,15 @@ void MediaSwitchWidget::StateChanged(int index)
|
|||||||
|
|
||||||
void MediaSwitchWidget::TimeRestrictionChanged(int index)
|
void MediaSwitchWidget::TimeRestrictionChanged(int index)
|
||||||
{
|
{
|
||||||
if (loading || !switchData)
|
if (loading || !switchData) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if ((time_restriction)index == TIME_RESTRICTION_NONE)
|
if ((time_restriction)index == TIME_RESTRICTION_NONE) {
|
||||||
time->setDisabled(true);
|
time->setDisabled(true);
|
||||||
else
|
} else {
|
||||||
time->setDisabled(false);
|
time->setDisabled(false);
|
||||||
|
}
|
||||||
|
|
||||||
std::lock_guard<std::mutex> lock(switcher->m);
|
std::lock_guard<std::mutex> lock(switcher->m);
|
||||||
switchData->restriction = (time_restriction)index;
|
switchData->restriction = (time_restriction)index;
|
||||||
@@ -553,8 +583,10 @@ void MediaSwitchWidget::TimeRestrictionChanged(int index)
|
|||||||
|
|
||||||
void MediaSwitchWidget::TimeChanged(int time)
|
void MediaSwitchWidget::TimeChanged(int time)
|
||||||
{
|
{
|
||||||
if (loading || !switchData)
|
if (loading || !switchData) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
std::lock_guard<std::mutex> lock(switcher->m);
|
std::lock_guard<std::mutex> lock(switcher->m);
|
||||||
switchData->time = time;
|
switchData->time = time;
|
||||||
}
|
}
|
||||||
|
|||||||
633
src/switch-network.cpp
Normal file
633
src/switch-network.cpp
Normal file
@@ -0,0 +1,633 @@
|
|||||||
|
/*
|
||||||
|
Most of this code is based on https://github.com/Palakis/obs-websocket
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <QtWidgets/QMainWindow>
|
||||||
|
#include <QtConcurrent/QtConcurrent>
|
||||||
|
#include <QTime>
|
||||||
|
#include <QMessageBox>
|
||||||
|
|
||||||
|
#include "headers/advanced-scene-switcher.hpp"
|
||||||
|
#include "headers/utility.hpp"
|
||||||
|
|
||||||
|
#define PARAM_SERVER_ENABLE "ServerEnabled"
|
||||||
|
#define PARAM_SERVER_PORT "ServerPort"
|
||||||
|
#define PARAM_LOCKTOIPV4 "LockToIPv4"
|
||||||
|
|
||||||
|
#define PARAM_CLIENT_ENABLE "ClientEnabled"
|
||||||
|
#define PARAM_CLIENT_PORT "ClientPort"
|
||||||
|
#define PARAM_ADDRESS "Address"
|
||||||
|
#define PARAM_CLIENT_SENDALL "SendAll"
|
||||||
|
|
||||||
|
#define RECONNECT_DELAY 10
|
||||||
|
|
||||||
|
#define SCENE_ENTRY "scene"
|
||||||
|
#define TRANSITION_ENTRY "transition"
|
||||||
|
|
||||||
|
using websocketpp::lib::placeholders::_1;
|
||||||
|
using websocketpp::lib::placeholders::_2;
|
||||||
|
using websocketpp::lib::bind;
|
||||||
|
|
||||||
|
NetworkConfig::NetworkConfig()
|
||||||
|
: ServerEnabled(false),
|
||||||
|
ServerPort(55555),
|
||||||
|
LockToIPv4(false),
|
||||||
|
ClientEnabled(false),
|
||||||
|
Address(""),
|
||||||
|
ClientPort(55555),
|
||||||
|
SendAll(true)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void NetworkConfig::Load(obs_data_t *obj)
|
||||||
|
{
|
||||||
|
SetDefaults(obj);
|
||||||
|
|
||||||
|
ServerEnabled = obs_data_get_bool(obj, PARAM_SERVER_ENABLE);
|
||||||
|
ServerPort = obs_data_get_int(obj, PARAM_SERVER_PORT);
|
||||||
|
LockToIPv4 = obs_data_get_bool(obj, PARAM_LOCKTOIPV4);
|
||||||
|
|
||||||
|
ClientEnabled = obs_data_get_bool(obj, PARAM_CLIENT_ENABLE);
|
||||||
|
Address = obs_data_get_string(obj, PARAM_ADDRESS);
|
||||||
|
ClientPort = obs_data_get_int(obj, PARAM_CLIENT_PORT);
|
||||||
|
SendAll = obs_data_get_bool(obj, PARAM_CLIENT_SENDALL);
|
||||||
|
}
|
||||||
|
|
||||||
|
void NetworkConfig::Save(obs_data_t *obj)
|
||||||
|
{
|
||||||
|
obs_data_set_bool(obj, PARAM_SERVER_ENABLE, ServerEnabled);
|
||||||
|
obs_data_set_int(obj, PARAM_SERVER_PORT, ServerPort);
|
||||||
|
obs_data_set_bool(obj, PARAM_LOCKTOIPV4, LockToIPv4);
|
||||||
|
|
||||||
|
obs_data_set_bool(obj, PARAM_CLIENT_ENABLE, ClientEnabled);
|
||||||
|
obs_data_set_string(obj, PARAM_ADDRESS, Address.c_str());
|
||||||
|
obs_data_set_int(obj, PARAM_CLIENT_PORT, ClientPort);
|
||||||
|
obs_data_set_bool(obj, PARAM_CLIENT_SENDALL, SendAll);
|
||||||
|
}
|
||||||
|
|
||||||
|
void NetworkConfig::SetDefaults(obs_data_t *obj)
|
||||||
|
{
|
||||||
|
obs_data_set_default_bool(obj, PARAM_SERVER_ENABLE, ServerEnabled);
|
||||||
|
obs_data_set_default_int(obj, PARAM_SERVER_PORT, ServerPort);
|
||||||
|
obs_data_set_default_bool(obj, PARAM_LOCKTOIPV4, LockToIPv4);
|
||||||
|
|
||||||
|
obs_data_set_default_bool(obj, PARAM_CLIENT_ENABLE, ClientEnabled);
|
||||||
|
obs_data_set_default_string(obj, PARAM_ADDRESS, Address.c_str());
|
||||||
|
obs_data_set_default_int(obj, PARAM_CLIENT_PORT, ClientPort);
|
||||||
|
obs_data_set_default_bool(obj, PARAM_CLIENT_SENDALL, SendAll);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string NetworkConfig::GetClientUri()
|
||||||
|
{
|
||||||
|
return "ws://" + Address + ":" + std::to_string(ClientPort);
|
||||||
|
}
|
||||||
|
|
||||||
|
WSServer::WSServer()
|
||||||
|
: QObject(nullptr), _connections(), _clMutex(QMutex::Recursive)
|
||||||
|
{
|
||||||
|
_server.get_alog().clear_channels(
|
||||||
|
websocketpp::log::alevel::frame_header |
|
||||||
|
websocketpp::log::alevel::frame_payload |
|
||||||
|
websocketpp::log::alevel::control);
|
||||||
|
_server.init_asio();
|
||||||
|
#ifndef _WIN32
|
||||||
|
_server.set_reuse_addr(true);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
_server.set_open_handler(bind(&WSServer::onOpen, this, ::_1));
|
||||||
|
_server.set_close_handler(bind(&WSServer::onClose, this, ::_1));
|
||||||
|
_server.set_message_handler(
|
||||||
|
bind(&WSServer::onMessage, this, ::_1, ::_2));
|
||||||
|
}
|
||||||
|
|
||||||
|
WSServer::~WSServer()
|
||||||
|
{
|
||||||
|
stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
void WSServer::start(quint16 port, bool lockToIPv4)
|
||||||
|
{
|
||||||
|
if (_server.is_listening() &&
|
||||||
|
(port == _serverPort && _lockToIPv4 == lockToIPv4)) {
|
||||||
|
blog(LOG_INFO,
|
||||||
|
"WSServer::start: server already on this port and protocol mode. no restart needed");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_server.is_listening()) {
|
||||||
|
stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
_server.reset();
|
||||||
|
|
||||||
|
_serverPort = port;
|
||||||
|
_lockToIPv4 = lockToIPv4;
|
||||||
|
|
||||||
|
websocketpp::lib::error_code errorCode;
|
||||||
|
if (lockToIPv4) {
|
||||||
|
blog(LOG_INFO, "WSServer::start: Locked to IPv4 bindings");
|
||||||
|
_server.listen(websocketpp::lib::asio::ip::tcp::v4(),
|
||||||
|
_serverPort, errorCode);
|
||||||
|
} else {
|
||||||
|
blog(LOG_INFO, "WSServer::start: Not locked to IPv4 bindings");
|
||||||
|
_server.listen(_serverPort, errorCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (errorCode) {
|
||||||
|
std::string errorCodeMessage = errorCode.message();
|
||||||
|
blog(LOG_INFO, "server: listen failed: %s",
|
||||||
|
errorCodeMessage.c_str());
|
||||||
|
|
||||||
|
obs_frontend_push_ui_translation(obs_module_get_string);
|
||||||
|
QString errorTitle = tr("AdvSceneSwitcher.windowTitle");
|
||||||
|
QString errorMessage =
|
||||||
|
tr("AdvSceneSwitcher.networkTab.startFailed.message")
|
||||||
|
.arg(_serverPort)
|
||||||
|
.arg(errorCodeMessage.c_str());
|
||||||
|
obs_frontend_pop_ui_translation();
|
||||||
|
|
||||||
|
QMainWindow *mainWindow = reinterpret_cast<QMainWindow *>(
|
||||||
|
obs_frontend_get_main_window());
|
||||||
|
QMessageBox::warning(mainWindow, errorTitle, errorMessage);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
switcher->serverStatus = ServerStatus::STARTING;
|
||||||
|
|
||||||
|
_server.start_accept();
|
||||||
|
|
||||||
|
QtConcurrent::run([=]() {
|
||||||
|
blog(LOG_INFO, "WSServer::start: io thread started");
|
||||||
|
_server.run();
|
||||||
|
blog(LOG_INFO, "WSServer::start: io thread exited");
|
||||||
|
});
|
||||||
|
|
||||||
|
switcher->serverStatus = ServerStatus::RUNNING;
|
||||||
|
blog(LOG_INFO,
|
||||||
|
"WSServer::start: server started successfully on port %d",
|
||||||
|
_serverPort);
|
||||||
|
}
|
||||||
|
|
||||||
|
void WSServer::stop()
|
||||||
|
{
|
||||||
|
if (!_server.is_listening()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_server.stop_listening();
|
||||||
|
for (connection_hdl hdl : _connections) {
|
||||||
|
websocketpp::lib::error_code ec;
|
||||||
|
_server.close(hdl, websocketpp::close::status::going_away,
|
||||||
|
"Server stopping", ec);
|
||||||
|
}
|
||||||
|
|
||||||
|
_threadPool.waitForDone();
|
||||||
|
|
||||||
|
while (_connections.size() > 0) {
|
||||||
|
std::this_thread::sleep_for(std::chrono::milliseconds(10));
|
||||||
|
}
|
||||||
|
|
||||||
|
switcher->serverStatus = ServerStatus::NOT_RUNNING;
|
||||||
|
blog(LOG_INFO, "server stopped successfully");
|
||||||
|
}
|
||||||
|
|
||||||
|
void WSServer::sendMessage(OBSWeakSource scene, OBSWeakSource transition)
|
||||||
|
{
|
||||||
|
if (!scene) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
OBSData data = obs_data_create();
|
||||||
|
obs_data_set_string(data, SCENE_ENTRY,
|
||||||
|
GetWeakSourceName(scene).c_str());
|
||||||
|
obs_data_set_string(data, TRANSITION_ENTRY,
|
||||||
|
GetWeakSourceName(transition).c_str());
|
||||||
|
std::string message = obs_data_get_json(data);
|
||||||
|
obs_data_release(data);
|
||||||
|
|
||||||
|
for (connection_hdl hdl : _connections) {
|
||||||
|
websocketpp::lib::error_code ec;
|
||||||
|
_server.send(hdl, message, websocketpp::frame::opcode::text,
|
||||||
|
ec);
|
||||||
|
if (ec) {
|
||||||
|
std::string errorCodeMessage = ec.message();
|
||||||
|
blog(LOG_INFO, "server: send failed: %s",
|
||||||
|
errorCodeMessage.c_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (switcher->verbose) {
|
||||||
|
blog(LOG_INFO, "server sent message:\n%s", message.c_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void WSServer::onOpen(connection_hdl hdl)
|
||||||
|
{
|
||||||
|
QMutexLocker locker(&_clMutex);
|
||||||
|
_connections.insert(hdl);
|
||||||
|
locker.unlock();
|
||||||
|
|
||||||
|
QString clientIp = getRemoteEndpoint(hdl);
|
||||||
|
blog(LOG_INFO, "new client connection from %s",
|
||||||
|
clientIp.toUtf8().constData());
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string processMessage(std::string payload)
|
||||||
|
{
|
||||||
|
auto config = switcher->networkConfig;
|
||||||
|
std::string msgContainer(payload);
|
||||||
|
const char *msg = msgContainer.c_str();
|
||||||
|
|
||||||
|
OBSData data = obs_data_create_from_json(msg);
|
||||||
|
if (!data) {
|
||||||
|
blog(LOG_ERROR, "invalid JSON payload received for '%s'", msg);
|
||||||
|
return "invalid JSON payload";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!obs_data_has_user_value(data, SCENE_ENTRY) ||
|
||||||
|
!obs_data_has_user_value(data, TRANSITION_ENTRY)) {
|
||||||
|
return "missing request parameters";
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string sceneName = obs_data_get_string(data, SCENE_ENTRY);
|
||||||
|
std::string transitionName =
|
||||||
|
obs_data_get_string(data, TRANSITION_ENTRY);
|
||||||
|
|
||||||
|
obs_data_release(data);
|
||||||
|
|
||||||
|
auto scene = GetWeakSourceByName(sceneName.c_str());
|
||||||
|
if (!scene) {
|
||||||
|
return "ignoring request - unknown scene '" + sceneName + "'";
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string ret = "message ok";
|
||||||
|
|
||||||
|
auto transition = GetWeakTransitionByName(transitionName.c_str());
|
||||||
|
if (switcher->verbose && !transition) {
|
||||||
|
ret += " - ignoring invalid transition: '" + transitionName +
|
||||||
|
"'";
|
||||||
|
}
|
||||||
|
|
||||||
|
switchScene(scene, transition, switcher->tansitionOverrideOverride);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
void WSServer::onMessage(connection_hdl hdl, server::message_ptr message)
|
||||||
|
{
|
||||||
|
UNUSED_PARAMETER(hdl);
|
||||||
|
|
||||||
|
auto opcode = message->get_opcode();
|
||||||
|
if (opcode != websocketpp::frame::opcode::text) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QtConcurrent::run(&_threadPool, [=]() {
|
||||||
|
if (message->get_payload() != "message ok") {
|
||||||
|
blog(LOG_WARNING, "received response: %s",
|
||||||
|
message->get_payload().c_str());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void WSServer::onClose(connection_hdl hdl)
|
||||||
|
{
|
||||||
|
QMutexLocker locker(&_clMutex);
|
||||||
|
_connections.erase(hdl);
|
||||||
|
locker.unlock();
|
||||||
|
|
||||||
|
auto conn = _server.get_con_from_hdl(hdl);
|
||||||
|
auto localCloseCode = conn->get_local_close_code();
|
||||||
|
|
||||||
|
if (localCloseCode != websocketpp::close::status::going_away) {
|
||||||
|
QString clientIp = getRemoteEndpoint(hdl);
|
||||||
|
blog(LOG_INFO, "client %s disconnected",
|
||||||
|
clientIp.toUtf8().constData());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QString WSServer::getRemoteEndpoint(connection_hdl hdl)
|
||||||
|
{
|
||||||
|
auto conn = _server.get_con_from_hdl(hdl);
|
||||||
|
return QString::fromStdString(conn->get_remote_endpoint());
|
||||||
|
}
|
||||||
|
|
||||||
|
WSClient::WSClient() : QObject(nullptr)
|
||||||
|
{
|
||||||
|
_client.get_alog().clear_channels(
|
||||||
|
websocketpp::log::alevel::frame_header |
|
||||||
|
websocketpp::log::alevel::frame_payload |
|
||||||
|
websocketpp::log::alevel::control);
|
||||||
|
_client.init_asio();
|
||||||
|
#ifndef _WIN32
|
||||||
|
_client.set_reuse_addr(true);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
_client.set_open_handler(bind(&WSClient::onOpen, this, ::_1));
|
||||||
|
_client.set_fail_handler(bind(&WSClient::onFail, this, ::_1));
|
||||||
|
_client.set_message_handler(
|
||||||
|
bind(&WSClient::onMessage, this, ::_1, ::_2));
|
||||||
|
_client.set_close_handler(bind(&WSClient::onClose, this, ::_1));
|
||||||
|
}
|
||||||
|
|
||||||
|
WSClient::~WSClient()
|
||||||
|
{
|
||||||
|
disconnect();
|
||||||
|
}
|
||||||
|
|
||||||
|
void WSClient::connectThread()
|
||||||
|
{
|
||||||
|
while (_retry) {
|
||||||
|
_client.reset();
|
||||||
|
switcher->clientStatus = ClientStatus::CONNECTING;
|
||||||
|
// Create a connection to the given URI and queue it for connection once
|
||||||
|
// the event loop starts
|
||||||
|
websocketpp::lib::error_code ec;
|
||||||
|
client::connection_ptr con = _client.get_connection(_uri, ec);
|
||||||
|
if (ec) {
|
||||||
|
_failMsg = ec.message();
|
||||||
|
blog(LOG_INFO, "client: connect failed: %s",
|
||||||
|
_failMsg.c_str());
|
||||||
|
switcher->clientStatus = ClientStatus::FAIL;
|
||||||
|
} else {
|
||||||
|
_client.connect(con);
|
||||||
|
_connection = connection_hdl(con);
|
||||||
|
|
||||||
|
// Start the ASIO io_service run loop
|
||||||
|
blog(LOG_INFO, "WSClient::connect: io thread started");
|
||||||
|
_connected = true;
|
||||||
|
_client.run();
|
||||||
|
_connected = false;
|
||||||
|
blog(LOG_INFO, "WSClient::connect: io thread exited");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_retry) {
|
||||||
|
std::unique_lock<std::mutex> lck(_waitMtx);
|
||||||
|
blog(LOG_INFO,
|
||||||
|
"trying to reconnect to %s in %d seconds.",
|
||||||
|
_uri.c_str(), RECONNECT_DELAY);
|
||||||
|
_cv.wait_for(lck,
|
||||||
|
std::chrono::seconds(RECONNECT_DELAY));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void WSClient::connect(std::string uri)
|
||||||
|
{
|
||||||
|
disconnect();
|
||||||
|
_uri = uri;
|
||||||
|
_retry = true;
|
||||||
|
|
||||||
|
_thread = std::thread(&WSClient::connectThread, this);
|
||||||
|
|
||||||
|
switcher->clientStatus = ClientStatus::DISCONNECTED;
|
||||||
|
blog(LOG_INFO, "WSClient::connect: exited");
|
||||||
|
}
|
||||||
|
|
||||||
|
void WSClient::disconnect()
|
||||||
|
{
|
||||||
|
_retry = false;
|
||||||
|
websocketpp::lib::error_code ec;
|
||||||
|
_client.close(_connection, websocketpp::close::status::normal,
|
||||||
|
"Client stopping", ec);
|
||||||
|
|
||||||
|
{
|
||||||
|
std::unique_lock<std::mutex> waitLck(_waitMtx);
|
||||||
|
blog(LOG_INFO, "trying to reconnect to %s in %d seconds.",
|
||||||
|
_uri.c_str(), RECONNECT_DELAY);
|
||||||
|
_cv.notify_all();
|
||||||
|
}
|
||||||
|
|
||||||
|
while (_connected) {
|
||||||
|
std::this_thread::sleep_for(std::chrono::milliseconds(10));
|
||||||
|
_client.close(_connection, websocketpp::close::status::normal,
|
||||||
|
"Client stopping", ec);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_thread.joinable()) {
|
||||||
|
_thread.join();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void WSClient::onOpen(connection_hdl hdl)
|
||||||
|
{
|
||||||
|
UNUSED_PARAMETER(hdl);
|
||||||
|
blog(LOG_INFO, "connection to %s opened", _uri.c_str());
|
||||||
|
switcher->clientStatus = ClientStatus::CONNECTED;
|
||||||
|
}
|
||||||
|
|
||||||
|
void WSClient::onFail(connection_hdl hdl)
|
||||||
|
{
|
||||||
|
UNUSED_PARAMETER(hdl);
|
||||||
|
blog(LOG_INFO, "connection to %s failed", _uri.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
void WSClient::onMessage(connection_hdl hdl, client::message_ptr message)
|
||||||
|
{
|
||||||
|
auto opcode = message->get_opcode();
|
||||||
|
if (opcode != websocketpp::frame::opcode::text) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string payload = message->get_payload();
|
||||||
|
std::string response = processMessage(payload);
|
||||||
|
websocketpp::lib::error_code errorCode;
|
||||||
|
_client.send(hdl, response, websocketpp::frame::opcode::text,
|
||||||
|
errorCode);
|
||||||
|
|
||||||
|
if (errorCode) {
|
||||||
|
std::string errorCodeMessage = errorCode.message();
|
||||||
|
blog(LOG_INFO, "client(response): send failed: %s",
|
||||||
|
errorCodeMessage.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (switcher->verbose) {
|
||||||
|
blog(LOG_INFO, "client sent message:\n%s", response.c_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void WSClient::onClose(connection_hdl hdl)
|
||||||
|
{
|
||||||
|
UNUSED_PARAMETER(hdl);
|
||||||
|
blog(LOG_INFO, "client-connection to %s closed.", _uri.c_str());
|
||||||
|
switcher->clientStatus = ClientStatus::DISCONNECTED;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SwitcherData::loadNetworkSettings(obs_data_t *obj)
|
||||||
|
{
|
||||||
|
networkConfig.Load(obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SwitcherData::saveNetworkSwitches(obs_data_t *obj)
|
||||||
|
{
|
||||||
|
networkConfig.Save(obj);
|
||||||
|
if (!networkConfig.ServerEnabled) {
|
||||||
|
switcher->server.stop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AdvSceneSwitcher::setupNetworkTab()
|
||||||
|
{
|
||||||
|
ui->serverSettings->setChecked(switcher->networkConfig.ServerEnabled);
|
||||||
|
ui->serverPort->setValue(switcher->networkConfig.ServerPort);
|
||||||
|
ui->lockToIPv4->setChecked(switcher->networkConfig.LockToIPv4);
|
||||||
|
|
||||||
|
ui->clientSettings->setChecked(switcher->networkConfig.ClientEnabled);
|
||||||
|
ui->clientHostname->setText(switcher->networkConfig.Address.c_str());
|
||||||
|
ui->clientPort->setValue(switcher->networkConfig.ClientPort);
|
||||||
|
ui->restrictSend->setChecked(!switcher->networkConfig.SendAll);
|
||||||
|
|
||||||
|
QTimer *statusTimer = new QTimer(this);
|
||||||
|
connect(statusTimer, SIGNAL(timeout()), this,
|
||||||
|
SLOT(updateClientStatus()));
|
||||||
|
connect(statusTimer, SIGNAL(timeout()), this,
|
||||||
|
SLOT(updateServerStatus()));
|
||||||
|
statusTimer->start(500);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AdvSceneSwitcher::on_serverSettings_toggled(bool on)
|
||||||
|
{
|
||||||
|
if (loading) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::lock_guard<std::mutex> lock(switcher->m);
|
||||||
|
switcher->networkConfig.ServerEnabled = on;
|
||||||
|
if (on) {
|
||||||
|
switcher->server.start(switcher->networkConfig.ServerPort,
|
||||||
|
switcher->networkConfig.LockToIPv4);
|
||||||
|
} else {
|
||||||
|
switcher->server.stop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AdvSceneSwitcher::on_serverPort_valueChanged(int value)
|
||||||
|
{
|
||||||
|
if (loading) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::lock_guard<std::mutex> lock(switcher->m);
|
||||||
|
switcher->networkConfig.ServerPort = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AdvSceneSwitcher::on_lockToIPv4_stateChanged(int state)
|
||||||
|
{
|
||||||
|
if (loading) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::lock_guard<std::mutex> lock(switcher->m);
|
||||||
|
switcher->networkConfig.LockToIPv4 = state;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AdvSceneSwitcher::on_serverRestart_clicked()
|
||||||
|
{
|
||||||
|
if (loading) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::lock_guard<std::mutex> lock(switcher->m);
|
||||||
|
switcher->server.start(switcher->networkConfig.ServerPort,
|
||||||
|
switcher->networkConfig.LockToIPv4);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AdvSceneSwitcher::updateServerStatus()
|
||||||
|
{
|
||||||
|
switch (switcher->serverStatus) {
|
||||||
|
case ServerStatus::NOT_RUNNING:
|
||||||
|
ui->serverStatus->setText(obs_module_text(
|
||||||
|
"AdvSceneSwitcher.networkTab.server.status.notRunning"));
|
||||||
|
break;
|
||||||
|
case ServerStatus::STARTING:
|
||||||
|
ui->serverStatus->setText(obs_module_text(
|
||||||
|
"AdvSceneSwitcher.networkTab.server.status.starting"));
|
||||||
|
break;
|
||||||
|
case ServerStatus::RUNNING:
|
||||||
|
ui->serverStatus->setText(obs_module_text(
|
||||||
|
"AdvSceneSwitcher.networkTab.server.status.running"));
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AdvSceneSwitcher::on_clientSettings_toggled(bool on)
|
||||||
|
{
|
||||||
|
if (loading) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::lock_guard<std::mutex> lock(switcher->m);
|
||||||
|
switcher->networkConfig.ClientEnabled = on;
|
||||||
|
|
||||||
|
if (on) {
|
||||||
|
switcher->client.connect(
|
||||||
|
switcher->networkConfig.GetClientUri());
|
||||||
|
} else {
|
||||||
|
switcher->client.disconnect();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AdvSceneSwitcher::on_clientHostname_textChanged(const QString &text)
|
||||||
|
{
|
||||||
|
if (loading) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::lock_guard<std::mutex> lock(switcher->m);
|
||||||
|
switcher->networkConfig.Address = text.toUtf8().constData();
|
||||||
|
}
|
||||||
|
|
||||||
|
void AdvSceneSwitcher::on_clientPort_valueChanged(int value)
|
||||||
|
{
|
||||||
|
if (loading) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::lock_guard<std::mutex> lock(switcher->m);
|
||||||
|
switcher->networkConfig.ClientPort = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AdvSceneSwitcher::on_restrictSend_stateChanged(int state)
|
||||||
|
{
|
||||||
|
if (loading) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::lock_guard<std::mutex> lock(switcher->m);
|
||||||
|
switcher->networkConfig.SendAll = !state;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AdvSceneSwitcher::on_clientReconnect_clicked()
|
||||||
|
{
|
||||||
|
if (loading) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::lock_guard<std::mutex> lock(switcher->m);
|
||||||
|
switcher->client.connect(switcher->networkConfig.GetClientUri());
|
||||||
|
}
|
||||||
|
|
||||||
|
void AdvSceneSwitcher::updateClientStatus()
|
||||||
|
{
|
||||||
|
switch (switcher->clientStatus) {
|
||||||
|
case ClientStatus::DISCONNECTED:
|
||||||
|
ui->clientStatus->setText(obs_module_text(
|
||||||
|
"AdvSceneSwitcher.networkTab.client.status.disconnected"));
|
||||||
|
break;
|
||||||
|
case ClientStatus::CONNECTING:
|
||||||
|
ui->clientStatus->setText(obs_module_text(
|
||||||
|
"AdvSceneSwitcher.networkTab.client.status.connecting"));
|
||||||
|
break;
|
||||||
|
case ClientStatus::CONNECTED:
|
||||||
|
ui->clientStatus->setText(obs_module_text(
|
||||||
|
"AdvSceneSwitcher.networkTab.client.status.connected"));
|
||||||
|
break;
|
||||||
|
case ClientStatus::FAIL:
|
||||||
|
ui->clientStatus->setText(QString("Error: ") +
|
||||||
|
switcher->client.getFail().c_str());
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -11,15 +11,19 @@ void AdvSceneSwitcher::on_pauseAdd_clicked()
|
|||||||
switcher->pauseEntries.emplace_back();
|
switcher->pauseEntries.emplace_back();
|
||||||
|
|
||||||
listAddClicked(ui->pauseEntries,
|
listAddClicked(ui->pauseEntries,
|
||||||
new PauseEntryWidget(&switcher->pauseEntries.back()),
|
new PauseEntryWidget(this,
|
||||||
|
&switcher->pauseEntries.back()),
|
||||||
ui->pauseAdd, &addPulse);
|
ui->pauseAdd, &addPulse);
|
||||||
|
|
||||||
|
ui->pauseHelp->setVisible(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AdvSceneSwitcher::on_pauseRemove_clicked()
|
void AdvSceneSwitcher::on_pauseRemove_clicked()
|
||||||
{
|
{
|
||||||
QListWidgetItem *item = ui->pauseEntries->currentItem();
|
QListWidgetItem *item = ui->pauseEntries->currentItem();
|
||||||
if (!item)
|
if (!item) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> lock(switcher->m);
|
std::lock_guard<std::mutex> lock(switcher->m);
|
||||||
@@ -34,8 +38,9 @@ void AdvSceneSwitcher::on_pauseRemove_clicked()
|
|||||||
void AdvSceneSwitcher::on_pauseUp_clicked()
|
void AdvSceneSwitcher::on_pauseUp_clicked()
|
||||||
{
|
{
|
||||||
int index = ui->pauseEntries->currentRow();
|
int index = ui->pauseEntries->currentRow();
|
||||||
if (!listMoveUp(ui->pauseEntries))
|
if (!listMoveUp(ui->pauseEntries)) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
PauseEntryWidget *s1 = (PauseEntryWidget *)ui->pauseEntries->itemWidget(
|
PauseEntryWidget *s1 = (PauseEntryWidget *)ui->pauseEntries->itemWidget(
|
||||||
ui->pauseEntries->item(index));
|
ui->pauseEntries->item(index));
|
||||||
@@ -53,8 +58,9 @@ void AdvSceneSwitcher::on_pauseDown_clicked()
|
|||||||
{
|
{
|
||||||
int index = ui->pauseEntries->currentRow();
|
int index = ui->pauseEntries->currentRow();
|
||||||
|
|
||||||
if (!listMoveDown(ui->pauseEntries))
|
if (!listMoveDown(ui->pauseEntries)) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
PauseEntryWidget *s1 = (PauseEntryWidget *)ui->pauseEntries->itemWidget(
|
PauseEntryWidget *s1 = (PauseEntryWidget *)ui->pauseEntries->itemWidget(
|
||||||
ui->pauseEntries->item(index));
|
ui->pauseEntries->item(index));
|
||||||
@@ -81,94 +87,85 @@ void resetPause()
|
|||||||
IdleData::pause = false;
|
IdleData::pause = false;
|
||||||
SceneSequenceSwitch::pause = false;
|
SceneSequenceSwitch::pause = false;
|
||||||
AudioSwitch::pause = false;
|
AudioSwitch::pause = false;
|
||||||
|
VideoSwitch::pause = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void setPauseTarget(PauseTarget &target, bool &verbose)
|
void setPauseTarget(PauseTarget &target, bool &verbose)
|
||||||
{
|
{
|
||||||
switch (target) {
|
switch (target) {
|
||||||
case PauseTarget::All:
|
case PauseTarget::All:
|
||||||
if (verbose)
|
vblog(LOG_INFO, "pause all switching");
|
||||||
blog(LOG_INFO, "pause all switching");
|
|
||||||
break;
|
break;
|
||||||
case PauseTarget::Transition:
|
case PauseTarget::Transition:
|
||||||
if (verbose)
|
vblog(LOG_INFO, "pause def_transition switching");
|
||||||
blog(LOG_INFO, "pause def_transition switching");
|
|
||||||
DefaultSceneTransition::pause = true;
|
DefaultSceneTransition::pause = true;
|
||||||
break;
|
break;
|
||||||
case PauseTarget::Window:
|
case PauseTarget::Window:
|
||||||
if (verbose)
|
vblog(LOG_INFO, "pause window switching");
|
||||||
blog(LOG_INFO, "pause window switching");
|
|
||||||
WindowSwitch::pause = true;
|
WindowSwitch::pause = true;
|
||||||
break;
|
break;
|
||||||
case PauseTarget::Executable:
|
case PauseTarget::Executable:
|
||||||
if (verbose)
|
vblog(LOG_INFO, "pause exec switching");
|
||||||
blog(LOG_INFO, "pause exec switching");
|
|
||||||
ExecutableSwitch::pause = true;
|
ExecutableSwitch::pause = true;
|
||||||
break;
|
break;
|
||||||
case PauseTarget::Region:
|
case PauseTarget::Region:
|
||||||
if (verbose)
|
vblog(LOG_INFO, "pause region switching");
|
||||||
blog(LOG_INFO, "pause region switching");
|
|
||||||
ScreenRegionSwitch::pause = true;
|
ScreenRegionSwitch::pause = true;
|
||||||
break;
|
break;
|
||||||
case PauseTarget::Media:
|
case PauseTarget::Media:
|
||||||
if (verbose)
|
vblog(LOG_INFO, "pause media switching");
|
||||||
blog(LOG_INFO, "pause media switching");
|
|
||||||
MediaSwitch::pause = true;
|
MediaSwitch::pause = true;
|
||||||
break;
|
break;
|
||||||
case PauseTarget::File:
|
case PauseTarget::File:
|
||||||
if (verbose)
|
vblog(LOG_INFO, "pause file switching");
|
||||||
blog(LOG_INFO, "pause file switching");
|
|
||||||
FileSwitch::pause = true;
|
FileSwitch::pause = true;
|
||||||
break;
|
break;
|
||||||
case PauseTarget::Random:
|
case PauseTarget::Random:
|
||||||
if (verbose)
|
vblog(LOG_INFO, "pause random switching");
|
||||||
blog(LOG_INFO, "pause random switching");
|
|
||||||
RandomSwitch::pause = true;
|
RandomSwitch::pause = true;
|
||||||
break;
|
break;
|
||||||
case PauseTarget::Time:
|
case PauseTarget::Time:
|
||||||
if (verbose)
|
vblog(LOG_INFO, "pause time switching");
|
||||||
blog(LOG_INFO, "pause time switching");
|
|
||||||
TimeSwitch::pause = true;
|
TimeSwitch::pause = true;
|
||||||
break;
|
break;
|
||||||
case PauseTarget::Idle:
|
case PauseTarget::Idle:
|
||||||
if (verbose)
|
vblog(LOG_INFO, "pause idle switching");
|
||||||
blog(LOG_INFO, "pause idle switching");
|
|
||||||
IdleData::pause = true;
|
IdleData::pause = true;
|
||||||
break;
|
break;
|
||||||
case PauseTarget::Sequence:
|
case PauseTarget::Sequence:
|
||||||
if (verbose)
|
vblog(LOG_INFO, "pause sequence switching");
|
||||||
blog(LOG_INFO, "pause sequence switching");
|
|
||||||
SceneSequenceSwitch::pause = true;
|
SceneSequenceSwitch::pause = true;
|
||||||
break;
|
break;
|
||||||
case PauseTarget::Audio:
|
case PauseTarget::Audio:
|
||||||
if (verbose)
|
vblog(LOG_INFO, "pause audio switching");
|
||||||
blog(LOG_INFO, "pause audio switching");
|
|
||||||
AudioSwitch::pause = true;
|
AudioSwitch::pause = true;
|
||||||
break;
|
break;
|
||||||
|
case PauseTarget::Video:
|
||||||
|
vblog(LOG_INFO, "pause video switching");
|
||||||
|
VideoSwitch::pause = true;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool checkPauseScene(obs_weak_source_t *currentScene, obs_weak_source_t *scene,
|
bool checkPauseScene(obs_weak_source_t *currentScene, obs_weak_source_t *scene,
|
||||||
PauseTarget &target, bool &verbose)
|
PauseTarget &target, bool &verbose)
|
||||||
{
|
{
|
||||||
if (currentScene != scene)
|
if (currentScene != scene) {
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
setPauseTarget(target, verbose);
|
setPauseTarget(target, verbose);
|
||||||
if (target == PauseTarget::All)
|
return (target == PauseTarget::All);
|
||||||
return true;
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
bool checkPauseWindow(std::string ¤tTitle, std::string &title,
|
bool checkPauseWindow(std::string ¤tTitle, std::string &title,
|
||||||
PauseTarget &target, bool &verbose)
|
PauseTarget &target, bool &verbose)
|
||||||
{
|
{
|
||||||
if (currentTitle != title)
|
if (currentTitle != title) {
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
setPauseTarget(target, verbose);
|
setPauseTarget(target, verbose);
|
||||||
if (target == PauseTarget::All)
|
return (target == PauseTarget::All);
|
||||||
return true;
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SwitcherData::checkPause()
|
bool SwitcherData::checkPause()
|
||||||
@@ -184,14 +181,16 @@ bool SwitcherData::checkPause()
|
|||||||
obs_weak_source_t *ws = obs_source_get_weak_source(currentSource);
|
obs_weak_source_t *ws = obs_source_get_weak_source(currentSource);
|
||||||
|
|
||||||
for (PauseEntry &s : pauseEntries) {
|
for (PauseEntry &s : pauseEntries) {
|
||||||
if (s.pauseType == PauseType::Scene)
|
if (s.pauseType == PauseType::Scene) {
|
||||||
pauseAll = checkPauseScene(ws, s.scene, s.pauseTarget,
|
pauseAll = checkPauseScene(ws, s.scene, s.pauseTarget,
|
||||||
verbose);
|
verbose);
|
||||||
else
|
} else {
|
||||||
pauseAll = checkPauseWindow(title, s.window,
|
pauseAll = checkPauseWindow(title, s.window,
|
||||||
s.pauseTarget, verbose);
|
s.pauseTarget, verbose);
|
||||||
if (pauseAll)
|
}
|
||||||
|
if (pauseAll) {
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
obs_source_release(currentSource);
|
obs_source_release(currentSource);
|
||||||
@@ -260,8 +259,9 @@ void SwitcherData::loadPauseSwitches(obs_data_t *obj)
|
|||||||
|
|
||||||
void SwitcherData::loadOldPauseSwitches(obs_data_t *obj)
|
void SwitcherData::loadOldPauseSwitches(obs_data_t *obj)
|
||||||
{
|
{
|
||||||
if (obs_data_get_int(obj, "oldPauseValuesImported"))
|
if (obs_data_get_int(obj, "oldPauseValuesImported")) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
obs_data_array_t *pauseScenesArray =
|
obs_data_array_t *pauseScenesArray =
|
||||||
obs_data_get_array(obj, "pauseScenes");
|
obs_data_get_array(obj, "pauseScenes");
|
||||||
@@ -307,13 +307,17 @@ void AdvSceneSwitcher::setupPauseTab()
|
|||||||
QListWidgetItem *item;
|
QListWidgetItem *item;
|
||||||
item = new QListWidgetItem(ui->pauseEntries);
|
item = new QListWidgetItem(ui->pauseEntries);
|
||||||
ui->pauseEntries->addItem(item);
|
ui->pauseEntries->addItem(item);
|
||||||
PauseEntryWidget *sw = new PauseEntryWidget(&s);
|
PauseEntryWidget *sw = new PauseEntryWidget(this, &s);
|
||||||
item->setSizeHint(sw->minimumSizeHint());
|
item->setSizeHint(sw->minimumSizeHint());
|
||||||
ui->pauseEntries->setItemWidget(item, sw);
|
ui->pauseEntries->setItemWidget(item, sw);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (switcher->pauseEntries.size() == 0)
|
if (switcher->pauseEntries.size() == 0) {
|
||||||
addPulse = PulseWidget(ui->pauseAdd, QColor(Qt::green));
|
addPulse = PulseWidget(ui->pauseAdd, QColor(Qt::green));
|
||||||
|
ui->pauseHelp->setVisible(true);
|
||||||
|
} else {
|
||||||
|
ui->pauseHelp->setVisible(false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void populatePauseTypes(QComboBox *list)
|
void populatePauseTypes(QComboBox *list)
|
||||||
@@ -341,9 +345,11 @@ void populatePauseTargets(QComboBox *list)
|
|||||||
list->addItem(
|
list->addItem(
|
||||||
obs_module_text("AdvSceneSwitcher.sceneSequenceTab.title"));
|
obs_module_text("AdvSceneSwitcher.sceneSequenceTab.title"));
|
||||||
list->addItem(obs_module_text("AdvSceneSwitcher.audioTab.title"));
|
list->addItem(obs_module_text("AdvSceneSwitcher.audioTab.title"));
|
||||||
|
list->addItem(obs_module_text("AdvSceneSwitcher.VideoTab.title"));
|
||||||
}
|
}
|
||||||
|
|
||||||
PauseEntryWidget::PauseEntryWidget(PauseEntry *s) : SwitchWidget(s, false)
|
PauseEntryWidget::PauseEntryWidget(QWidget *parent, PauseEntry *s)
|
||||||
|
: SwitchWidget(parent, s, false, false)
|
||||||
{
|
{
|
||||||
pauseTypes = new QComboBox();
|
pauseTypes = new QComboBox();
|
||||||
pauseTargets = new QComboBox();
|
pauseTargets = new QComboBox();
|
||||||
@@ -415,8 +421,10 @@ void PauseEntryWidget::swapSwitchData(PauseEntryWidget *s1,
|
|||||||
|
|
||||||
void PauseEntryWidget::PauseTypeChanged(int index)
|
void PauseEntryWidget::PauseTypeChanged(int index)
|
||||||
{
|
{
|
||||||
if (loading || !switchData)
|
if (loading || !switchData) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
std::lock_guard<std::mutex> lock(switcher->m);
|
std::lock_guard<std::mutex> lock(switcher->m);
|
||||||
switchData->pauseType = static_cast<PauseType>(index);
|
switchData->pauseType = static_cast<PauseType>(index);
|
||||||
|
|
||||||
@@ -435,16 +443,20 @@ void PauseEntryWidget::PauseTypeChanged(int index)
|
|||||||
|
|
||||||
void PauseEntryWidget::PauseTargetChanged(int index)
|
void PauseEntryWidget::PauseTargetChanged(int index)
|
||||||
{
|
{
|
||||||
if (loading || !switchData)
|
if (loading || !switchData) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
std::lock_guard<std::mutex> lock(switcher->m);
|
std::lock_guard<std::mutex> lock(switcher->m);
|
||||||
switchData->pauseTarget = static_cast<PauseTarget>(index);
|
switchData->pauseTarget = static_cast<PauseTarget>(index);
|
||||||
}
|
}
|
||||||
|
|
||||||
void PauseEntryWidget::WindowChanged(const QString &text)
|
void PauseEntryWidget::WindowChanged(const QString &text)
|
||||||
{
|
{
|
||||||
if (loading || !switchData)
|
if (loading || !switchData) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
std::lock_guard<std::mutex> lock(switcher->m);
|
std::lock_guard<std::mutex> lock(switcher->m);
|
||||||
switchData->window = text.toStdString();
|
switchData->window = text.toStdString();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -55,15 +55,20 @@ void AdvSceneSwitcher::on_priorityDown_clicked()
|
|||||||
|
|
||||||
bool SwitcherData::prioFuncsValid()
|
bool SwitcherData::prioFuncsValid()
|
||||||
{
|
{
|
||||||
auto it = std::unique(functionNamesByPriority.begin(),
|
auto fNBPCopy = functionNamesByPriority;
|
||||||
functionNamesByPriority.end());
|
|
||||||
bool wasUnique = (it == functionNamesByPriority.end());
|
std::sort(fNBPCopy.begin(), fNBPCopy.end());
|
||||||
if (!wasUnique)
|
auto it = std::unique(fNBPCopy.begin(), fNBPCopy.end());
|
||||||
|
bool wasUnique = (it == fNBPCopy.end());
|
||||||
|
|
||||||
|
if (!wasUnique) {
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
for (int p : functionNamesByPriority) {
|
for (int p : functionNamesByPriority) {
|
||||||
if (p < 0 || p > 8)
|
if (p < 0 || p > 9) {
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,15 +12,19 @@ void AdvSceneSwitcher::on_randomAdd_clicked()
|
|||||||
switcher->randomSwitches.emplace_back();
|
switcher->randomSwitches.emplace_back();
|
||||||
|
|
||||||
listAddClicked(ui->randomSwitches,
|
listAddClicked(ui->randomSwitches,
|
||||||
new RandomSwitchWidget(&switcher->randomSwitches.back()),
|
new RandomSwitchWidget(this,
|
||||||
|
&switcher->randomSwitches.back()),
|
||||||
ui->randomAdd, &addPulse);
|
ui->randomAdd, &addPulse);
|
||||||
|
|
||||||
|
ui->randomHelp->setVisible(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AdvSceneSwitcher::on_randomRemove_clicked()
|
void AdvSceneSwitcher::on_randomRemove_clicked()
|
||||||
{
|
{
|
||||||
QListWidgetItem *item = ui->randomSwitches->currentItem();
|
QListWidgetItem *item = ui->randomSwitches->currentItem();
|
||||||
if (!item)
|
if (!item) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> lock(switcher->m);
|
std::lock_guard<std::mutex> lock(switcher->m);
|
||||||
@@ -35,27 +39,40 @@ void AdvSceneSwitcher::on_randomRemove_clicked()
|
|||||||
void SwitcherData::checkRandom(bool &match, OBSWeakSource &scene,
|
void SwitcherData::checkRandom(bool &match, OBSWeakSource &scene,
|
||||||
OBSWeakSource &transition, int &delay)
|
OBSWeakSource &transition, int &delay)
|
||||||
{
|
{
|
||||||
if (randomSwitches.size() == 0 || RandomSwitch::pause)
|
if (randomSwitches.size() == 0 || RandomSwitch::pause) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
std::deque<RandomSwitch> rs(randomSwitches);
|
std::deque<RandomSwitch> rs(randomSwitches);
|
||||||
std::random_device rng;
|
std::random_device rng;
|
||||||
std::mt19937 urng(rng());
|
std::mt19937 urng(rng());
|
||||||
std::shuffle(rs.begin(), rs.end(), urng);
|
std::shuffle(rs.begin(), rs.end(), urng);
|
||||||
for (RandomSwitch &r : rs) {
|
for (RandomSwitch &r : rs) {
|
||||||
if (!r.initialized())
|
if (!r.initialized()) {
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (r.scene == lastRandomScene && randomSwitches.size() != 1)
|
if (randomSwitches.size() != 1) {
|
||||||
continue;
|
if (r.targetType == SwitchTargetType::Scene &&
|
||||||
scene = r.scene;
|
r.scene == lastRandomScene) {
|
||||||
|
continue;
|
||||||
|
} else if (r.targetType ==
|
||||||
|
SwitchTargetType::SceneGroup &&
|
||||||
|
r.group && r.group == lastRandomSceneGroup) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
scene = r.getScene();
|
||||||
transition = r.transition;
|
transition = r.transition;
|
||||||
delay = (int)r.delay * 1000;
|
delay = (int)r.delay * 1000;
|
||||||
match = true;
|
match = true;
|
||||||
lastRandomScene = r.scene;
|
lastRandomScene = r.scene;
|
||||||
|
lastRandomSceneGroup = r.group;
|
||||||
|
|
||||||
if (verbose)
|
if (verbose) {
|
||||||
r.logMatch();
|
r.logMatch();
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -66,22 +83,9 @@ void SwitcherData::saveRandomSwitches(obs_data_t *obj)
|
|||||||
for (RandomSwitch &s : switcher->randomSwitches) {
|
for (RandomSwitch &s : switcher->randomSwitches) {
|
||||||
obs_data_t *array_obj = obs_data_create();
|
obs_data_t *array_obj = obs_data_create();
|
||||||
|
|
||||||
obs_source_t *source = obs_weak_source_get_source(s.scene);
|
s.save(array_obj);
|
||||||
obs_source_t *transition =
|
obs_data_array_push_back(randomArray, array_obj);
|
||||||
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_release(array_obj);
|
||||||
}
|
}
|
||||||
obs_data_set_array(obj, "randomSwitches", randomArray);
|
obs_data_set_array(obj, "randomSwitches", randomArray);
|
||||||
@@ -99,14 +103,8 @@ void SwitcherData::loadRandomSwitches(obs_data_t *obj)
|
|||||||
for (size_t i = 0; i < count; i++) {
|
for (size_t i = 0; i < count; i++) {
|
||||||
obs_data_t *array_obj = obs_data_array_item(randomArray, i);
|
obs_data_t *array_obj = obs_data_array_item(randomArray, i);
|
||||||
|
|
||||||
const char *scene = obs_data_get_string(array_obj, "scene");
|
switcher->randomSwitches.emplace_back();
|
||||||
const char *transition =
|
randomSwitches.back().load(array_obj);
|
||||||
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_release(array_obj);
|
||||||
}
|
}
|
||||||
@@ -119,22 +117,40 @@ void AdvSceneSwitcher::setupRandomTab()
|
|||||||
QListWidgetItem *item;
|
QListWidgetItem *item;
|
||||||
item = new QListWidgetItem(ui->randomSwitches);
|
item = new QListWidgetItem(ui->randomSwitches);
|
||||||
ui->randomSwitches->addItem(item);
|
ui->randomSwitches->addItem(item);
|
||||||
RandomSwitchWidget *sw = new RandomSwitchWidget(&s);
|
RandomSwitchWidget *sw = new RandomSwitchWidget(this, &s);
|
||||||
item->setSizeHint(sw->minimumSizeHint());
|
item->setSizeHint(sw->minimumSizeHint());
|
||||||
ui->randomSwitches->setItemWidget(item, sw);
|
ui->randomSwitches->setItemWidget(item, sw);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (switcher->randomSwitches.size() == 0)
|
if (switcher->randomSwitches.size() == 0) {
|
||||||
addPulse = PulseWidget(ui->randomAdd, QColor(Qt::green));
|
addPulse = PulseWidget(ui->randomAdd, QColor(Qt::green));
|
||||||
|
ui->randomHelp->setVisible(true);
|
||||||
|
} else {
|
||||||
|
ui->randomHelp->setVisible(false);
|
||||||
|
}
|
||||||
|
|
||||||
if (switcher->switchIfNotMatching != RANDOM_SWITCH)
|
if (switcher->switchIfNotMatching != RANDOM_SWITCH) {
|
||||||
PulseWidget(ui->randomDisabledWarning, QColor(Qt::red),
|
PulseWidget(ui->randomDisabledWarning, QColor(Qt::red),
|
||||||
QColor(0, 0, 0, 0), "QLabel ");
|
QColor(0, 0, 0, 0), "QLabel ");
|
||||||
else
|
} else {
|
||||||
ui->randomDisabledWarning->setVisible(false);
|
ui->randomDisabledWarning->setVisible(false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
RandomSwitchWidget::RandomSwitchWidget(RandomSwitch *s) : SwitchWidget(s, false)
|
void RandomSwitch::save(obs_data_t *obj)
|
||||||
|
{
|
||||||
|
SceneSwitcherEntry::save(obj, "targetType", "scene");
|
||||||
|
obs_data_set_double(obj, "delay", delay);
|
||||||
|
}
|
||||||
|
|
||||||
|
void RandomSwitch::load(obs_data_t *obj)
|
||||||
|
{
|
||||||
|
SceneSwitcherEntry::load(obj, "targetType", "scene");
|
||||||
|
delay = obs_data_get_double(obj, "delay");
|
||||||
|
}
|
||||||
|
|
||||||
|
RandomSwitchWidget::RandomSwitchWidget(QWidget *parent, RandomSwitch *s)
|
||||||
|
: SwitchWidget(parent, s, false, true)
|
||||||
{
|
{
|
||||||
delay = new QDoubleSpinBox();
|
delay = new QDoubleSpinBox();
|
||||||
|
|
||||||
@@ -184,8 +200,10 @@ void RandomSwitchWidget::swapSwitchData(RandomSwitchWidget *s1,
|
|||||||
|
|
||||||
void RandomSwitchWidget::DelayChanged(double d)
|
void RandomSwitchWidget::DelayChanged(double d)
|
||||||
{
|
{
|
||||||
if (loading || !switchData)
|
if (loading || !switchData) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
std::lock_guard<std::mutex> lock(switcher->m);
|
std::lock_guard<std::mutex> lock(switcher->m);
|
||||||
switchData->delay = d;
|
switchData->delay = d;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,8 +19,9 @@ void showCurrentFrame(QListWidget *list)
|
|||||||
{
|
{
|
||||||
QListWidgetItem *item = list->currentItem();
|
QListWidgetItem *item = list->currentItem();
|
||||||
|
|
||||||
if (!item)
|
if (!item) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
ScreenRegionWidget *sw = (ScreenRegionWidget *)list->itemWidget(item);
|
ScreenRegionWidget *sw = (ScreenRegionWidget *)list->itemWidget(item);
|
||||||
sw->showFrame();
|
sw->showFrame();
|
||||||
@@ -53,8 +54,9 @@ void AdvSceneSwitcher::on_showFrame_clicked()
|
|||||||
|
|
||||||
void AdvSceneSwitcher::on_screenRegionSwitches_currentRowChanged(int idx)
|
void AdvSceneSwitcher::on_screenRegionSwitches_currentRowChanged(int idx)
|
||||||
{
|
{
|
||||||
if (loading || idx == -1)
|
if (loading || idx == -1) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (switcher->showFrame) {
|
if (switcher->showFrame) {
|
||||||
clearFrames(ui->screenRegionSwitches);
|
clearFrames(ui->screenRegionSwitches);
|
||||||
@@ -67,17 +69,20 @@ void AdvSceneSwitcher::on_screenRegionAdd_clicked()
|
|||||||
std::lock_guard<std::mutex> lock(switcher->m);
|
std::lock_guard<std::mutex> lock(switcher->m);
|
||||||
switcher->screenRegionSwitches.emplace_back();
|
switcher->screenRegionSwitches.emplace_back();
|
||||||
|
|
||||||
listAddClicked(
|
listAddClicked(ui->screenRegionSwitches,
|
||||||
ui->screenRegionSwitches,
|
new ScreenRegionWidget(
|
||||||
new ScreenRegionWidget(&switcher->screenRegionSwitches.back()),
|
this, &switcher->screenRegionSwitches.back()),
|
||||||
ui->screenRegionAdd, &addPulse);
|
ui->screenRegionAdd, &addPulse);
|
||||||
|
|
||||||
|
ui->regionHelp->setVisible(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AdvSceneSwitcher::on_screenRegionRemove_clicked()
|
void AdvSceneSwitcher::on_screenRegionRemove_clicked()
|
||||||
{
|
{
|
||||||
QListWidgetItem *item = ui->screenRegionSwitches->currentItem();
|
QListWidgetItem *item = ui->screenRegionSwitches->currentItem();
|
||||||
if (!item)
|
if (!item) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> lock(switcher->m);
|
std::lock_guard<std::mutex> lock(switcher->m);
|
||||||
@@ -92,8 +97,9 @@ void AdvSceneSwitcher::on_screenRegionRemove_clicked()
|
|||||||
void AdvSceneSwitcher::on_screenRegionUp_clicked()
|
void AdvSceneSwitcher::on_screenRegionUp_clicked()
|
||||||
{
|
{
|
||||||
int index = ui->screenRegionSwitches->currentRow();
|
int index = ui->screenRegionSwitches->currentRow();
|
||||||
if (!listMoveUp(ui->screenRegionSwitches))
|
if (!listMoveUp(ui->screenRegionSwitches)) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
ScreenRegionWidget *s1 =
|
ScreenRegionWidget *s1 =
|
||||||
(ScreenRegionWidget *)ui->screenRegionSwitches->itemWidget(
|
(ScreenRegionWidget *)ui->screenRegionSwitches->itemWidget(
|
||||||
@@ -113,8 +119,9 @@ void AdvSceneSwitcher::on_screenRegionDown_clicked()
|
|||||||
{
|
{
|
||||||
int index = ui->screenRegionSwitches->currentRow();
|
int index = ui->screenRegionSwitches->currentRow();
|
||||||
|
|
||||||
if (!listMoveDown(ui->screenRegionSwitches))
|
if (!listMoveDown(ui->screenRegionSwitches)) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
ScreenRegionWidget *s1 =
|
ScreenRegionWidget *s1 =
|
||||||
(ScreenRegionWidget *)ui->screenRegionSwitches->itemWidget(
|
(ScreenRegionWidget *)ui->screenRegionSwitches->itemWidget(
|
||||||
@@ -130,30 +137,52 @@ void AdvSceneSwitcher::on_screenRegionDown_clicked()
|
|||||||
switcher->screenRegionSwitches[index + 1]);
|
switcher->screenRegionSwitches[index + 1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool shouldIgnoreSceneSwitch(ScreenRegionSwitch &matchingRegion)
|
||||||
|
{
|
||||||
|
if (!matchingRegion.excludeScene) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
obs_source_t *currentScene = obs_frontend_get_current_scene();
|
||||||
|
OBSWeakSource ws = obs_source_get_weak_source(currentScene);
|
||||||
|
obs_weak_source_release(ws);
|
||||||
|
obs_source_release(currentScene);
|
||||||
|
|
||||||
|
return matchingRegion.excludeScene == ws;
|
||||||
|
}
|
||||||
|
|
||||||
void SwitcherData::checkScreenRegionSwitch(bool &match, OBSWeakSource &scene,
|
void SwitcherData::checkScreenRegionSwitch(bool &match, OBSWeakSource &scene,
|
||||||
OBSWeakSource &transition)
|
OBSWeakSource &transition)
|
||||||
{
|
{
|
||||||
if (ScreenRegionSwitch::pause)
|
if (ScreenRegionSwitch::pause) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
std::pair<int, int> cursorPos = getCursorPos();
|
std::pair<int, int> cursorPos = getCursorPos();
|
||||||
int minRegionSize = 99999;
|
int minRegionSize = 99999;
|
||||||
|
|
||||||
for (auto &s : screenRegionSwitches) {
|
for (auto &s : screenRegionSwitches) {
|
||||||
if (!s.initialized())
|
if (!s.initialized()) {
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (cursorPos.first >= s.minX && cursorPos.second >= s.minY &&
|
if (cursorPos.first >= s.minX && cursorPos.second >= s.minY &&
|
||||||
cursorPos.first <= s.maxX && cursorPos.second <= s.maxY) {
|
cursorPos.first <= s.maxX && cursorPos.second <= s.maxY) {
|
||||||
int regionSize = (s.maxX - s.minX) + (s.maxY - s.minY);
|
int regionSize = (s.maxX - s.minX) + (s.maxY - s.minY);
|
||||||
if (regionSize < minRegionSize) {
|
if (regionSize < minRegionSize) {
|
||||||
|
if (shouldIgnoreSceneSwitch(s)) {
|
||||||
|
// We technically have a match.
|
||||||
|
// But just ignore it.
|
||||||
|
return;
|
||||||
|
}
|
||||||
match = true;
|
match = true;
|
||||||
scene = s.scene;
|
scene = s.getScene();
|
||||||
transition = s.transition;
|
transition = s.transition;
|
||||||
minRegionSize = regionSize;
|
minRegionSize = regionSize;
|
||||||
|
|
||||||
if (verbose)
|
if (verbose) {
|
||||||
s.logMatch();
|
s.logMatch();
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -173,25 +202,9 @@ void SwitcherData::saveScreenRegionSwitches(obs_data_t *obj)
|
|||||||
for (ScreenRegionSwitch &s : switcher->screenRegionSwitches) {
|
for (ScreenRegionSwitch &s : switcher->screenRegionSwitches) {
|
||||||
obs_data_t *array_obj = obs_data_create();
|
obs_data_t *array_obj = obs_data_create();
|
||||||
|
|
||||||
obs_source_t *source = obs_weak_source_get_source(s.scene);
|
s.save(array_obj);
|
||||||
obs_source_t *transition =
|
obs_data_array_push_back(screenRegionArray, array_obj);
|
||||||
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_release(array_obj);
|
||||||
}
|
}
|
||||||
obs_data_set_array(obj, "screenRegion", screenRegionArray);
|
obs_data_set_array(obj, "screenRegion", screenRegionArray);
|
||||||
@@ -210,19 +223,8 @@ void SwitcherData::loadScreenRegionSwitches(obs_data_t *obj)
|
|||||||
obs_data_t *array_obj =
|
obs_data_t *array_obj =
|
||||||
obs_data_array_item(screenRegionArray, i);
|
obs_data_array_item(screenRegionArray, i);
|
||||||
|
|
||||||
const char *scene =
|
switcher->screenRegionSwitches.emplace_back();
|
||||||
obs_data_get_string(array_obj, "screenRegionScene");
|
screenRegionSwitches.back().load(array_obj);
|
||||||
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_release(array_obj);
|
||||||
}
|
}
|
||||||
@@ -235,13 +237,17 @@ void AdvSceneSwitcher::setupRegionTab()
|
|||||||
QListWidgetItem *item;
|
QListWidgetItem *item;
|
||||||
item = new QListWidgetItem(ui->screenRegionSwitches);
|
item = new QListWidgetItem(ui->screenRegionSwitches);
|
||||||
ui->screenRegionSwitches->addItem(item);
|
ui->screenRegionSwitches->addItem(item);
|
||||||
ScreenRegionWidget *sw = new ScreenRegionWidget(&s);
|
ScreenRegionWidget *sw = new ScreenRegionWidget(this, &s);
|
||||||
item->setSizeHint(sw->minimumSizeHint());
|
item->setSizeHint(sw->minimumSizeHint());
|
||||||
ui->screenRegionSwitches->setItemWidget(item, sw);
|
ui->screenRegionSwitches->setItemWidget(item, sw);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (switcher->screenRegionSwitches.size() == 0)
|
if (switcher->screenRegionSwitches.size() == 0) {
|
||||||
addPulse = PulseWidget(ui->screenRegionAdd, QColor(Qt::green));
|
addPulse = PulseWidget(ui->screenRegionAdd, QColor(Qt::green));
|
||||||
|
ui->regionHelp->setVisible(true);
|
||||||
|
} else {
|
||||||
|
ui->regionHelp->setVisible(false);
|
||||||
|
}
|
||||||
|
|
||||||
// screen region cursor position
|
// screen region cursor position
|
||||||
QTimer *screenRegionTimer = new QTimer(this);
|
QTimer *screenRegionTimer = new QTimer(this);
|
||||||
@@ -250,9 +256,68 @@ void AdvSceneSwitcher::setupRegionTab()
|
|||||||
screenRegionTimer->start(1000);
|
screenRegionTimer->start(1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
ScreenRegionWidget::ScreenRegionWidget(ScreenRegionSwitch *s)
|
void ScreenRegionSwitch::save(obs_data_t *obj)
|
||||||
: SwitchWidget(s, false)
|
|
||||||
{
|
{
|
||||||
|
SceneSwitcherEntry::save(obj);
|
||||||
|
|
||||||
|
obs_data_set_string(obj, "excludeScene",
|
||||||
|
GetWeakSourceName(excludeScene).c_str());
|
||||||
|
|
||||||
|
obs_data_set_int(obj, "minX", minX);
|
||||||
|
obs_data_set_int(obj, "minY", minY);
|
||||||
|
obs_data_set_int(obj, "maxX", maxX);
|
||||||
|
obs_data_set_int(obj, "maxY", maxY);
|
||||||
|
}
|
||||||
|
|
||||||
|
// To be removed in future version
|
||||||
|
bool loadOldRegion(obs_data_t *obj, ScreenRegionSwitch *s)
|
||||||
|
{
|
||||||
|
if (!s) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *scene = obs_data_get_string(obj, "screenRegionScene");
|
||||||
|
|
||||||
|
if (strcmp(scene, "") == 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
s->scene = GetWeakSourceByName(scene);
|
||||||
|
|
||||||
|
const char *transition = obs_data_get_string(obj, "transition");
|
||||||
|
s->transition = GetWeakTransitionByName(transition);
|
||||||
|
|
||||||
|
s->minX = obs_data_get_int(obj, "minX");
|
||||||
|
s->minY = obs_data_get_int(obj, "minY");
|
||||||
|
s->maxX = obs_data_get_int(obj, "maxX");
|
||||||
|
s->maxY = obs_data_get_int(obj, "maxY");
|
||||||
|
|
||||||
|
s->usePreviousScene = strcmp(scene, previous_scene_name) == 0;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ScreenRegionSwitch::load(obs_data_t *obj)
|
||||||
|
{
|
||||||
|
if (loadOldRegion(obj, this)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
SceneSwitcherEntry::load(obj);
|
||||||
|
|
||||||
|
const char *excludeSceneName = obs_data_get_string(obj, "excludeScene");
|
||||||
|
excludeScene = GetWeakSourceByName(excludeSceneName);
|
||||||
|
|
||||||
|
minX = obs_data_get_int(obj, "minX");
|
||||||
|
minY = obs_data_get_int(obj, "minY");
|
||||||
|
maxX = obs_data_get_int(obj, "maxX");
|
||||||
|
maxY = obs_data_get_int(obj, "maxY");
|
||||||
|
}
|
||||||
|
|
||||||
|
ScreenRegionWidget::ScreenRegionWidget(QWidget *parent, ScreenRegionSwitch *s)
|
||||||
|
: SwitchWidget(parent, s, true, true)
|
||||||
|
{
|
||||||
|
excludeScenes = new QComboBox();
|
||||||
minX = new QSpinBox();
|
minX = new QSpinBox();
|
||||||
minY = new QSpinBox();
|
minY = new QSpinBox();
|
||||||
maxX = new QSpinBox();
|
maxX = new QSpinBox();
|
||||||
@@ -273,6 +338,9 @@ ScreenRegionWidget::ScreenRegionWidget(ScreenRegionSwitch *s)
|
|||||||
maxX->setMaximum(1000000);
|
maxX->setMaximum(1000000);
|
||||||
maxY->setMaximum(1000000);
|
maxY->setMaximum(1000000);
|
||||||
|
|
||||||
|
QWidget::connect(excludeScenes,
|
||||||
|
SIGNAL(currentTextChanged(const QString &)), this,
|
||||||
|
SLOT(ExcludeSceneChanged(const QString &)));
|
||||||
QWidget::connect(minX, SIGNAL(valueChanged(int)), this,
|
QWidget::connect(minX, SIGNAL(valueChanged(int)), this,
|
||||||
SLOT(MinXChanged(int)));
|
SLOT(MinXChanged(int)));
|
||||||
QWidget::connect(minY, SIGNAL(valueChanged(int)), this,
|
QWidget::connect(minY, SIGNAL(valueChanged(int)), this,
|
||||||
@@ -282,7 +350,15 @@ ScreenRegionWidget::ScreenRegionWidget(ScreenRegionSwitch *s)
|
|||||||
QWidget::connect(maxY, SIGNAL(valueChanged(int)), this,
|
QWidget::connect(maxY, SIGNAL(valueChanged(int)), this,
|
||||||
SLOT(MaxYChanged(int)));
|
SLOT(MaxYChanged(int)));
|
||||||
|
|
||||||
|
AdvSceneSwitcher::populateSceneSelection(
|
||||||
|
excludeScenes, false, false, true,
|
||||||
|
obs_module_text(
|
||||||
|
"AdvSceneSwitcher.screenRegionTab.excludeScenes.None"),
|
||||||
|
true);
|
||||||
|
|
||||||
if (s) {
|
if (s) {
|
||||||
|
excludeScenes->setCurrentText(
|
||||||
|
GetWeakSourceName(s->excludeScene).c_str());
|
||||||
minX->setValue(s->minX);
|
minX->setValue(s->minX);
|
||||||
minY->setValue(s->minY);
|
minY->setValue(s->minY);
|
||||||
maxX->setValue(s->maxX);
|
maxX->setValue(s->maxX);
|
||||||
@@ -291,9 +367,13 @@ ScreenRegionWidget::ScreenRegionWidget(ScreenRegionSwitch *s)
|
|||||||
|
|
||||||
QHBoxLayout *mainLayout = new QHBoxLayout;
|
QHBoxLayout *mainLayout = new QHBoxLayout;
|
||||||
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
|
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
|
||||||
{"{{minX}}", minX}, {"{{minY}}", minY},
|
{"{{minX}}", minX},
|
||||||
{"{{maxX}}", maxX}, {"{{maxY}}", maxY},
|
{"{{minY}}", minY},
|
||||||
{"{{scenes}}", scenes}, {"{{transitions}}", transitions}};
|
{"{{maxX}}", maxX},
|
||||||
|
{"{{maxY}}", maxY},
|
||||||
|
{"{{scenes}}", scenes},
|
||||||
|
{"{{transitions}}", transitions},
|
||||||
|
{"{{excludeScenes}}", excludeScenes}};
|
||||||
placeWidgets(obs_module_text("AdvSceneSwitcher.screenRegionTab.entry"),
|
placeWidgets(obs_module_text("AdvSceneSwitcher.screenRegionTab.entry"),
|
||||||
mainLayout, widgetPlaceholders);
|
mainLayout, widgetPlaceholders);
|
||||||
setLayout(mainLayout);
|
setLayout(mainLayout);
|
||||||
@@ -334,10 +414,22 @@ void ScreenRegionWidget::hideFrame()
|
|||||||
helperFrame.hide();
|
helperFrame.hide();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ScreenRegionWidget::ExcludeSceneChanged(const QString &text)
|
||||||
|
{
|
||||||
|
if (loading || !switchData) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::lock_guard<std::mutex> lock(switcher->m);
|
||||||
|
switchData->excludeScene = GetWeakSourceByQString(text);
|
||||||
|
}
|
||||||
|
|
||||||
void ScreenRegionWidget::MinXChanged(int pos)
|
void ScreenRegionWidget::MinXChanged(int pos)
|
||||||
{
|
{
|
||||||
if (loading || !switchData)
|
if (loading || !switchData) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
std::lock_guard<std::mutex> lock(switcher->m);
|
std::lock_guard<std::mutex> lock(switcher->m);
|
||||||
switchData->minX = pos;
|
switchData->minX = pos;
|
||||||
|
|
||||||
@@ -346,8 +438,10 @@ void ScreenRegionWidget::MinXChanged(int pos)
|
|||||||
|
|
||||||
void ScreenRegionWidget::MinYChanged(int pos)
|
void ScreenRegionWidget::MinYChanged(int pos)
|
||||||
{
|
{
|
||||||
if (loading || !switchData)
|
if (loading || !switchData) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
std::lock_guard<std::mutex> lock(switcher->m);
|
std::lock_guard<std::mutex> lock(switcher->m);
|
||||||
switchData->minY = pos;
|
switchData->minY = pos;
|
||||||
|
|
||||||
@@ -356,8 +450,10 @@ void ScreenRegionWidget::MinYChanged(int pos)
|
|||||||
|
|
||||||
void ScreenRegionWidget::MaxXChanged(int pos)
|
void ScreenRegionWidget::MaxXChanged(int pos)
|
||||||
{
|
{
|
||||||
if (loading || !switchData)
|
if (loading || !switchData) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
std::lock_guard<std::mutex> lock(switcher->m);
|
std::lock_guard<std::mutex> lock(switcher->m);
|
||||||
switchData->maxX = pos;
|
switchData->maxX = pos;
|
||||||
|
|
||||||
@@ -366,8 +462,10 @@ void ScreenRegionWidget::MaxXChanged(int pos)
|
|||||||
|
|
||||||
void ScreenRegionWidget::MaxYChanged(int pos)
|
void ScreenRegionWidget::MaxYChanged(int pos)
|
||||||
{
|
{
|
||||||
if (loading || !switchData)
|
if (loading || !switchData) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
std::lock_guard<std::mutex> lock(switcher->m);
|
std::lock_guard<std::mutex> lock(switcher->m);
|
||||||
switchData->maxY = pos;
|
switchData->maxY = pos;
|
||||||
|
|
||||||
@@ -383,8 +481,9 @@ void ScreenRegionWidget::drawFrame()
|
|||||||
Qt::WindowStaysOnTopHint);
|
Qt::WindowStaysOnTopHint);
|
||||||
helperFrame.setAttribute(Qt::WA_TranslucentBackground, true);
|
helperFrame.setAttribute(Qt::WA_TranslucentBackground, true);
|
||||||
|
|
||||||
if (switchData)
|
if (switchData) {
|
||||||
helperFrame.setGeometry(switchData->minX, switchData->minY,
|
helperFrame.setGeometry(switchData->minX, switchData->minY,
|
||||||
switchData->maxX - switchData->minX,
|
switchData->maxX - switchData->minX,
|
||||||
switchData->maxY - switchData->minY);
|
switchData->maxY - switchData->minY);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,8 @@
|
|||||||
#include "headers/advanced-scene-switcher.hpp"
|
#include "headers/advanced-scene-switcher.hpp"
|
||||||
#include "headers/utility.hpp"
|
#include "headers/utility.hpp"
|
||||||
|
|
||||||
|
constexpr auto max_extend_text_size = 150;
|
||||||
|
|
||||||
bool SceneSequenceSwitch::pause = false;
|
bool SceneSequenceSwitch::pause = false;
|
||||||
static QMetaObject::Connection addPulse;
|
static QMetaObject::Connection addPulse;
|
||||||
|
|
||||||
@@ -12,32 +14,26 @@ void AdvSceneSwitcher::on_sceneSequenceAdd_clicked()
|
|||||||
std::lock_guard<std::mutex> lock(switcher->m);
|
std::lock_guard<std::mutex> lock(switcher->m);
|
||||||
switcher->sceneSequenceSwitches.emplace_back();
|
switcher->sceneSequenceSwitches.emplace_back();
|
||||||
|
|
||||||
listAddClicked(
|
listAddClicked(ui->sceneSequenceSwitches,
|
||||||
ui->sceneSequenceSwitches,
|
new SequenceWidget(
|
||||||
new SequenceWidget(&switcher->sceneSequenceSwitches.back()),
|
this, &switcher->sceneSequenceSwitches.back()),
|
||||||
ui->sceneSequenceAdd, &addPulse);
|
ui->sceneSequenceAdd, &addPulse);
|
||||||
|
|
||||||
|
ui->sequenceHelp->setVisible(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AdvSceneSwitcher::on_sceneSequenceRemove_clicked()
|
void AdvSceneSwitcher::on_sceneSequenceRemove_clicked()
|
||||||
{
|
{
|
||||||
QListWidgetItem *item = ui->sceneSequenceSwitches->currentItem();
|
QListWidgetItem *item = ui->sceneSequenceSwitches->currentItem();
|
||||||
if (!item)
|
if (!item) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
// might be in waiting state of sequence
|
|
||||||
// causing invalid access after wakeup
|
|
||||||
// thus we need to stop the main thread before delete
|
|
||||||
bool wasRunning = !switcher->stop;
|
|
||||||
switcher->Stop();
|
|
||||||
|
|
||||||
std::lock_guard<std::mutex> lock(switcher->m);
|
std::lock_guard<std::mutex> lock(switcher->m);
|
||||||
int idx = ui->sceneSequenceSwitches->currentRow();
|
int idx = ui->sceneSequenceSwitches->currentRow();
|
||||||
auto &switches = switcher->sceneSequenceSwitches;
|
auto &switches = switcher->sceneSequenceSwitches;
|
||||||
switches.erase(switches.begin() + idx);
|
switches.erase(switches.begin() + idx);
|
||||||
|
|
||||||
if (wasRunning)
|
|
||||||
switcher->Start();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
delete item;
|
delete item;
|
||||||
@@ -46,8 +42,9 @@ void AdvSceneSwitcher::on_sceneSequenceRemove_clicked()
|
|||||||
void AdvSceneSwitcher::on_sceneSequenceUp_clicked()
|
void AdvSceneSwitcher::on_sceneSequenceUp_clicked()
|
||||||
{
|
{
|
||||||
int index = ui->sceneSequenceSwitches->currentRow();
|
int index = ui->sceneSequenceSwitches->currentRow();
|
||||||
if (!listMoveUp(ui->sceneSequenceSwitches))
|
if (!listMoveUp(ui->sceneSequenceSwitches)) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
SequenceWidget *s1 =
|
SequenceWidget *s1 =
|
||||||
(SequenceWidget *)ui->sceneSequenceSwitches->itemWidget(
|
(SequenceWidget *)ui->sceneSequenceSwitches->itemWidget(
|
||||||
@@ -67,8 +64,9 @@ void AdvSceneSwitcher::on_sceneSequenceDown_clicked()
|
|||||||
{
|
{
|
||||||
int index = ui->sceneSequenceSwitches->currentRow();
|
int index = ui->sceneSequenceSwitches->currentRow();
|
||||||
|
|
||||||
if (!listMoveDown(ui->sceneSequenceSwitches))
|
if (!listMoveDown(ui->sceneSequenceSwitches)) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
SequenceWidget *s1 =
|
SequenceWidget *s1 =
|
||||||
(SequenceWidget *)ui->sceneSequenceSwitches->itemWidget(
|
(SequenceWidget *)ui->sceneSequenceSwitches->itemWidget(
|
||||||
@@ -93,11 +91,13 @@ void AdvSceneSwitcher::on_sceneSequenceSave_clicked()
|
|||||||
QDir::currentPath(),
|
QDir::currentPath(),
|
||||||
tr(obs_module_text(
|
tr(obs_module_text(
|
||||||
"AdvSceneSwitcher.sceneSequenceTab.fileType")));
|
"AdvSceneSwitcher.sceneSequenceTab.fileType")));
|
||||||
if (directory.isEmpty())
|
if (directory.isEmpty()) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
QFile file(directory);
|
QFile file(directory);
|
||||||
if (!file.open(QIODevice::WriteOnly | QIODevice::Text))
|
if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
obs_data_t *obj = obs_data_create();
|
obs_data_t *obj = obs_data_create();
|
||||||
switcher->saveSceneSequenceSwitches(obj);
|
switcher->saveSceneSequenceSwitches(obj);
|
||||||
@@ -114,12 +114,14 @@ void AdvSceneSwitcher::on_sceneSequenceLoad_clicked()
|
|||||||
QDir::currentPath(),
|
QDir::currentPath(),
|
||||||
tr(obs_module_text(
|
tr(obs_module_text(
|
||||||
"AdvSceneSwitcher.sceneSequenceTab.fileType")));
|
"AdvSceneSwitcher.sceneSequenceTab.fileType")));
|
||||||
if (directory.isEmpty())
|
if (directory.isEmpty()) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
QFile file(directory);
|
QFile file(directory);
|
||||||
if (!file.open(QIODevice::ReadOnly | QIODevice::Text))
|
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
obs_data_t *obj = obs_data_create_from_json_file(
|
obs_data_t *obj = obs_data_create_from_json_file(
|
||||||
file.fileName().toUtf8().constData());
|
file.fileName().toUtf8().constData());
|
||||||
@@ -130,17 +132,8 @@ void AdvSceneSwitcher::on_sceneSequenceLoad_clicked()
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// might be in waiting state of sequence
|
|
||||||
// causing invalid access after wakeup
|
|
||||||
// thus we need to stop the main thread before delete
|
|
||||||
bool wasRunning = !switcher->stop;
|
|
||||||
switcher->Stop();
|
|
||||||
|
|
||||||
switcher->loadSceneSequenceSwitches(obj);
|
switcher->loadSceneSequenceSwitches(obj);
|
||||||
|
|
||||||
if (wasRunning)
|
|
||||||
switcher->Start();
|
|
||||||
|
|
||||||
obs_data_release(obj);
|
obs_data_release(obj);
|
||||||
|
|
||||||
DisplayMessage(obs_module_text(
|
DisplayMessage(obs_module_text(
|
||||||
@@ -148,90 +141,98 @@ void AdvSceneSwitcher::on_sceneSequenceLoad_clicked()
|
|||||||
close();
|
close();
|
||||||
}
|
}
|
||||||
|
|
||||||
void matchInterruptible(SwitcherData *switcher, SceneSequenceSwitch &s,
|
void AdvSceneSwitcher::OpenSequenceExtendEdit(SequenceWidget *sw)
|
||||||
bool &match, OBSWeakSource &scene,
|
|
||||||
OBSWeakSource &transition)
|
|
||||||
{
|
{
|
||||||
bool durationReached = s.matchCount * (switcher->interval / 1000.0) >=
|
QDialog edit;
|
||||||
s.delay;
|
SequenceWidget editWidget(this, sw->getSwitchData(), false, true,
|
||||||
|
false);
|
||||||
|
QHBoxLayout layout;
|
||||||
|
layout.setSizeConstraint(QLayout::SetFixedSize);
|
||||||
|
layout.addWidget(&editWidget);
|
||||||
|
edit.setLayout(&layout);
|
||||||
|
edit.setWindowTitle(obs_module_text(
|
||||||
|
"AdvSceneSwitcher.sceneSequenceTab.extendEdit"));
|
||||||
|
edit.exec();
|
||||||
|
|
||||||
s.matchCount++;
|
sw->UpdateWidgetStatus(true);
|
||||||
|
|
||||||
if (durationReached) {
|
|
||||||
match = true;
|
|
||||||
scene = (s.usePreviousScene) ? switcher->previousScene
|
|
||||||
: s.scene;
|
|
||||||
transition = s.transition;
|
|
||||||
if (switcher->verbose)
|
|
||||||
s.logMatch();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void matchUninterruptible(SwitcherData *switcher, SceneSequenceSwitch &s,
|
void AdvSceneSwitcher::on_sequenceEdit_clicked()
|
||||||
obs_source_t *currentSource,
|
|
||||||
std::unique_lock<std::mutex> &lock, bool &match,
|
|
||||||
OBSWeakSource &scene, OBSWeakSource &transition)
|
|
||||||
{
|
{
|
||||||
// scene was already active for the previous cycle so remove this time
|
int index = ui->sceneSequenceSwitches->currentRow();
|
||||||
int dur = s.delay * 1000 - switcher->interval;
|
if (index == -1) {
|
||||||
if (dur > 0) {
|
return;
|
||||||
switcher->waitScene = currentSource;
|
|
||||||
|
|
||||||
if (switcher->verbose)
|
|
||||||
s.logSleep(dur);
|
|
||||||
|
|
||||||
switcher->cv.wait_for(lock, std::chrono::milliseconds(dur));
|
|
||||||
switcher->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) ? switcher->previousScene
|
|
||||||
: s.scene;
|
|
||||||
transition = s.transition;
|
|
||||||
if (switcher->verbose)
|
|
||||||
s.logMatch();
|
|
||||||
} else if (switcher->verbose) {
|
|
||||||
blog(LOG_INFO, "sequence canceled");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
obs_source_release(currentSource2);
|
SequenceWidget *currentWidget =
|
||||||
|
(SequenceWidget *)ui->sceneSequenceSwitches->itemWidget(
|
||||||
|
ui->sceneSequenceSwitches->item(index));
|
||||||
|
|
||||||
|
OpenSequenceExtendEdit(currentWidget);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AdvSceneSwitcher::on_sceneSequenceSwitches_itemDoubleClicked(
|
||||||
|
QListWidgetItem *item)
|
||||||
|
{
|
||||||
|
SequenceWidget *currentWidget =
|
||||||
|
(SequenceWidget *)ui->sceneSequenceSwitches->itemWidget(item);
|
||||||
|
OpenSequenceExtendEdit(currentWidget);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SwitcherData::checkSceneSequence(bool &match, OBSWeakSource &scene,
|
void SwitcherData::checkSceneSequence(bool &match, OBSWeakSource &scene,
|
||||||
OBSWeakSource &transition,
|
OBSWeakSource &transition, int &linger)
|
||||||
std::unique_lock<std::mutex> &lock)
|
|
||||||
{
|
{
|
||||||
if (SceneSequenceSwitch::pause)
|
if (SceneSequenceSwitch::pause) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
obs_source_t *currentSource = obs_frontend_get_current_scene();
|
obs_source_t *currentSceneSource = obs_frontend_get_current_scene();
|
||||||
obs_weak_source_t *ws = obs_source_get_weak_source(currentSource);
|
obs_weak_source_t *currentScene =
|
||||||
|
obs_source_get_weak_source(currentSceneSource);
|
||||||
|
|
||||||
for (SceneSequenceSwitch &s : sceneSequenceSwitches) {
|
for (SceneSequenceSwitch &s : sceneSequenceSwitches) {
|
||||||
if (!s.initialized())
|
// Continue the active uninterruptible sequence and skip others
|
||||||
|
if (uninterruptibleSceneSequenceActive &&
|
||||||
|
s.activeSequence == nullptr) {
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (s.startScene == ws) {
|
bool matched = s.checkMatch(currentScene, linger);
|
||||||
if (!match) {
|
|
||||||
if (s.interruptible) {
|
if (!match && matched) {
|
||||||
matchInterruptible(switcher, s, match,
|
match = matched;
|
||||||
scene, transition);
|
|
||||||
} else {
|
if (s.activeSequence) {
|
||||||
matchUninterruptible(switcher, s,
|
scene = s.activeSequence->getScene();
|
||||||
currentSource,
|
transition = s.activeSequence->transition;
|
||||||
lock, match, scene,
|
} else {
|
||||||
transition);
|
scene = s.getScene();
|
||||||
|
transition = s.transition;
|
||||||
|
if (verbose) {
|
||||||
|
s.logMatch();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
s.matchCount = 0;
|
s.advanceActiveSequence();
|
||||||
|
if (verbose) {
|
||||||
|
s.logAdvanceSequence();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ignore other switching methods if sequence is not
|
||||||
|
// interruptible and has not reached its end
|
||||||
|
if (s.activeSequence) {
|
||||||
|
uninterruptibleSceneSequenceActive =
|
||||||
|
!s.interruptible;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
obs_source_release(currentSource);
|
|
||||||
obs_weak_source_release(ws);
|
if (!match) {
|
||||||
|
uninterruptibleSceneSequenceActive = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
obs_source_release(currentSceneSource);
|
||||||
|
obs_weak_source_release(currentScene);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SwitcherData::saveSceneSequenceSwitches(obs_data_t *obj)
|
void SwitcherData::saveSceneSequenceSwitches(obs_data_t *obj)
|
||||||
@@ -240,35 +241,9 @@ void SwitcherData::saveSceneSequenceSwitches(obs_data_t *obj)
|
|||||||
for (SceneSequenceSwitch &s : switcher->sceneSequenceSwitches) {
|
for (SceneSequenceSwitch &s : switcher->sceneSequenceSwitches) {
|
||||||
obs_data_t *array_obj = obs_data_create();
|
obs_data_t *array_obj = obs_data_create();
|
||||||
|
|
||||||
obs_source_t *source1 =
|
s.save(array_obj);
|
||||||
obs_weak_source_get_source(s.startScene);
|
obs_data_array_push_back(sceneSequenceArray, array_obj);
|
||||||
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_set_bool(array_obj, "interruptible",
|
|
||||||
s.interruptible);
|
|
||||||
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_release(array_obj);
|
||||||
}
|
}
|
||||||
obs_data_set_array(obj, "sceneRoundTrip", sceneSequenceArray);
|
obs_data_set_array(obj, "sceneRoundTrip", sceneSequenceArray);
|
||||||
@@ -287,27 +262,8 @@ void SwitcherData::loadSceneSequenceSwitches(obs_data_t *obj)
|
|||||||
obs_data_t *array_obj =
|
obs_data_t *array_obj =
|
||||||
obs_data_array_item(sceneSequenceArray, i);
|
obs_data_array_item(sceneSequenceArray, i);
|
||||||
|
|
||||||
const char *scene1 =
|
switcher->sceneSequenceSwitches.emplace_back();
|
||||||
obs_data_get_string(array_obj, "sceneRoundTripScene1");
|
sceneSequenceSwitches.back().load(array_obj);
|
||||||
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;
|
|
||||||
bool interruptible =
|
|
||||||
obs_data_get_bool(array_obj, "interruptible");
|
|
||||||
|
|
||||||
switcher->sceneSequenceSwitches.emplace_back(
|
|
||||||
GetWeakSourceByName(scene1),
|
|
||||||
GetWeakSourceByName(scene2),
|
|
||||||
GetWeakTransitionByName(transition), delay,
|
|
||||||
delayMultiplier, interruptible,
|
|
||||||
(strcmp(scene2, previous_scene_name) == 0));
|
|
||||||
|
|
||||||
obs_data_release(array_obj);
|
obs_data_release(array_obj);
|
||||||
}
|
}
|
||||||
@@ -320,13 +276,17 @@ void AdvSceneSwitcher::setupSequenceTab()
|
|||||||
QListWidgetItem *item;
|
QListWidgetItem *item;
|
||||||
item = new QListWidgetItem(ui->sceneSequenceSwitches);
|
item = new QListWidgetItem(ui->sceneSequenceSwitches);
|
||||||
ui->sceneSequenceSwitches->addItem(item);
|
ui->sceneSequenceSwitches->addItem(item);
|
||||||
SequenceWidget *sw = new SequenceWidget(&s);
|
SequenceWidget *sw = new SequenceWidget(this, &s);
|
||||||
item->setSizeHint(sw->minimumSizeHint());
|
item->setSizeHint(sw->minimumSizeHint());
|
||||||
ui->sceneSequenceSwitches->setItemWidget(item, sw);
|
ui->sceneSequenceSwitches->setItemWidget(item, sw);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (switcher->sceneSequenceSwitches.size() == 0)
|
if (switcher->sceneSequenceSwitches.size() == 0) {
|
||||||
addPulse = PulseWidget(ui->sceneSequenceAdd, QColor(Qt::green));
|
addPulse = PulseWidget(ui->sceneSequenceAdd, QColor(Qt::green));
|
||||||
|
ui->sequenceHelp->setVisible(true);
|
||||||
|
} else {
|
||||||
|
ui->sequenceHelp->setVisible(false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SceneSequenceSwitch::initialized()
|
bool SceneSequenceSwitch::initialized()
|
||||||
@@ -340,9 +300,304 @@ bool SceneSequenceSwitch::valid()
|
|||||||
(SceneSwitcherEntry::valid() && WeakSourceValid(startScene));
|
(SceneSwitcherEntry::valid() && WeakSourceValid(startScene));
|
||||||
}
|
}
|
||||||
|
|
||||||
void SceneSequenceSwitch::logSleep(int dur)
|
void SceneSequenceSwitch::save(obs_data_t *obj, bool saveExt)
|
||||||
{
|
{
|
||||||
blog(LOG_INFO, "sequence sleep %d", dur);
|
SceneSwitcherEntry::save(obj);
|
||||||
|
|
||||||
|
obs_data_set_int(obj, "startTargetType",
|
||||||
|
static_cast<int>(startTargetType));
|
||||||
|
obs_data_set_string(obj, "startScene",
|
||||||
|
GetWeakSourceName(startScene).c_str());
|
||||||
|
obs_data_set_double(obj, "delay", delay);
|
||||||
|
obs_data_set_int(obj, "delayMultiplier", delayMultiplier);
|
||||||
|
obs_data_set_bool(obj, "interruptible", interruptible);
|
||||||
|
|
||||||
|
if (saveExt) {
|
||||||
|
auto cur = extendedSequence.get();
|
||||||
|
|
||||||
|
obs_data_array_t *extendScenes = obs_data_array_create();
|
||||||
|
while (cur) {
|
||||||
|
obs_data_t *array_obj = obs_data_create();
|
||||||
|
cur->save(array_obj, false);
|
||||||
|
obs_data_array_push_back(extendScenes, array_obj);
|
||||||
|
obs_data_release(array_obj);
|
||||||
|
cur = cur->extendedSequence.get();
|
||||||
|
}
|
||||||
|
obs_data_set_array(obj, "extendScenes", extendScenes);
|
||||||
|
obs_data_array_release(extendScenes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// To be removed in future version
|
||||||
|
bool loadOldScequence(obs_data_t *obj, SceneSequenceSwitch *s)
|
||||||
|
{
|
||||||
|
if (!s) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *scene1 = obs_data_get_string(obj, "sceneRoundTripScene1");
|
||||||
|
|
||||||
|
if (strcmp(scene1, "") == 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
s->startScene = GetWeakSourceByName(scene1);
|
||||||
|
|
||||||
|
const char *scene2 = obs_data_get_string(obj, "sceneRoundTripScene2");
|
||||||
|
s->scene = GetWeakSourceByName(scene2);
|
||||||
|
|
||||||
|
const char *transition = obs_data_get_string(obj, "transition");
|
||||||
|
s->transition = GetWeakTransitionByName(transition);
|
||||||
|
|
||||||
|
s->delay = obs_data_get_double(obj, "delay");
|
||||||
|
|
||||||
|
int delayMultiplier = obs_data_get_int(obj, "delayMultiplier");
|
||||||
|
if (delayMultiplier == 0 ||
|
||||||
|
(delayMultiplier != 1 && delayMultiplier % 60 != 0))
|
||||||
|
delayMultiplier = 1;
|
||||||
|
s->delayMultiplier = delayMultiplier;
|
||||||
|
|
||||||
|
s->interruptible = obs_data_get_bool(obj, "interruptible");
|
||||||
|
|
||||||
|
s->usePreviousScene = strcmp(scene2, previous_scene_name) == 0;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SceneSequenceSwitch::load(obs_data_t *obj, bool saveExt)
|
||||||
|
{
|
||||||
|
if (loadOldScequence(obj, this)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
SceneSwitcherEntry::load(obj);
|
||||||
|
startTargetType = static_cast<SwitchTargetType>(
|
||||||
|
obs_data_get_int(obj, "startTargetType"));
|
||||||
|
const char *scene = obs_data_get_string(obj, "startScene");
|
||||||
|
startScene = GetWeakSourceByName(scene);
|
||||||
|
|
||||||
|
delay = obs_data_get_double(obj, "delay");
|
||||||
|
|
||||||
|
delayMultiplier = obs_data_get_int(obj, "delayMultiplier");
|
||||||
|
if (delayMultiplier == 0 ||
|
||||||
|
(delayMultiplier != 1 && delayMultiplier % 60 != 0))
|
||||||
|
delayMultiplier = 1;
|
||||||
|
|
||||||
|
interruptible = obs_data_get_bool(obj, "interruptible");
|
||||||
|
|
||||||
|
if (saveExt) {
|
||||||
|
auto cur = this;
|
||||||
|
|
||||||
|
obs_data_array_t *extendScenes =
|
||||||
|
obs_data_get_array(obj, "extendScenes");
|
||||||
|
size_t count = obs_data_array_count(extendScenes);
|
||||||
|
for (size_t i = 0; i < count; i++) {
|
||||||
|
obs_data_t *array_obj =
|
||||||
|
obs_data_array_item(extendScenes, i);
|
||||||
|
|
||||||
|
cur->extendedSequence =
|
||||||
|
std::make_unique<SceneSequenceSwitch>();
|
||||||
|
|
||||||
|
cur->extendedSequence->load(array_obj, false);
|
||||||
|
|
||||||
|
cur = cur->extendedSequence.get();
|
||||||
|
obs_data_release(array_obj);
|
||||||
|
}
|
||||||
|
obs_data_array_release(extendScenes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SceneSequenceSwitch::reduce()
|
||||||
|
{
|
||||||
|
// Reset activeSequence just in case it was one of the deleted entries
|
||||||
|
activeSequence = nullptr;
|
||||||
|
|
||||||
|
if (!extendedSequence) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (extendedSequence->reduce()) {
|
||||||
|
extendedSequence.reset(nullptr);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
SceneSequenceSwitch *SceneSequenceSwitch::extend()
|
||||||
|
{
|
||||||
|
if (extendedSequence) {
|
||||||
|
return extendedSequence->extend();
|
||||||
|
}
|
||||||
|
extendedSequence = std::make_unique<SceneSequenceSwitch>();
|
||||||
|
extendedSequence->startScene = scene;
|
||||||
|
return extendedSequence.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SceneSequenceSwitch::checkMatch(OBSWeakSource currentScene, int &linger,
|
||||||
|
SceneSequenceSwitch *root)
|
||||||
|
{
|
||||||
|
if (!initialized()) {
|
||||||
|
if (root) {
|
||||||
|
root->activeSequence = nullptr;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool match = false;
|
||||||
|
|
||||||
|
if (activeSequence) {
|
||||||
|
return activeSequence->checkMatch(currentScene, linger, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (startScene == currentScene) {
|
||||||
|
if (interruptible) {
|
||||||
|
match = checkDurationMatchInterruptible();
|
||||||
|
} else {
|
||||||
|
match = true;
|
||||||
|
prepareUninterruptibleMatch(currentScene, linger);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
matchCount = 0;
|
||||||
|
|
||||||
|
if (root) {
|
||||||
|
root->activeSequence = nullptr;
|
||||||
|
logSequenceCanceled();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return match;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SceneSequenceSwitch::checkDurationMatchInterruptible()
|
||||||
|
{
|
||||||
|
bool durationReached = matchCount * (switcher->interval / 1000.0) >=
|
||||||
|
delay;
|
||||||
|
matchCount++;
|
||||||
|
if (durationReached) {
|
||||||
|
matchCount = 0;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SceneSequenceSwitch::prepareUninterruptibleMatch(
|
||||||
|
OBSWeakSource currentScene, int &linger)
|
||||||
|
{
|
||||||
|
int dur = delay * 1000;
|
||||||
|
if (dur > 0) {
|
||||||
|
switcher->waitScene = obs_weak_source_get_source(currentScene);
|
||||||
|
obs_source_release(switcher->waitScene);
|
||||||
|
linger = dur;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SceneSequenceSwitch::advanceActiveSequence()
|
||||||
|
{
|
||||||
|
// Set start Scene
|
||||||
|
OBSWeakSource currentSceneGroupScene = nullptr;
|
||||||
|
if (targetType == SwitchTargetType::SceneGroup && group) {
|
||||||
|
currentSceneGroupScene = group->getCurrentScene();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (activeSequence) {
|
||||||
|
activeSequence = activeSequence->extendedSequence.get();
|
||||||
|
} else {
|
||||||
|
activeSequence = extendedSequence.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (activeSequence) {
|
||||||
|
if (activeSequence->startTargetType ==
|
||||||
|
SwitchTargetType::SceneGroup) {
|
||||||
|
activeSequence->startScene = currentSceneGroupScene;
|
||||||
|
}
|
||||||
|
if (activeSequence->targetType == SwitchTargetType::Scene &&
|
||||||
|
!activeSequence->scene) {
|
||||||
|
blog(LOG_WARNING,
|
||||||
|
"cannot advance sequence - null scene set");
|
||||||
|
activeSequence = nullptr;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (activeSequence->targetType ==
|
||||||
|
SwitchTargetType::SceneGroup &&
|
||||||
|
activeSequence->group &&
|
||||||
|
activeSequence->group->scenes.empty()) {
|
||||||
|
blog(LOG_WARNING,
|
||||||
|
"cannot advance sequence - no scenes specified in '%s'",
|
||||||
|
activeSequence->group->name.c_str());
|
||||||
|
activeSequence = nullptr;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reinit old matchCount value in case it was previously set
|
||||||
|
activeSequence->matchCount = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SceneSequenceSwitch::logAdvanceSequence()
|
||||||
|
{
|
||||||
|
if (activeSequence) {
|
||||||
|
std::string targetName =
|
||||||
|
GetWeakSourceName(activeSequence->scene);
|
||||||
|
|
||||||
|
if (activeSequence->targetType ==
|
||||||
|
SwitchTargetType::SceneGroup &&
|
||||||
|
activeSequence->group) {
|
||||||
|
targetName = activeSequence->group->name;
|
||||||
|
}
|
||||||
|
|
||||||
|
blog(LOG_INFO, "continuing sequence with '%s' -> '%s'",
|
||||||
|
GetWeakSourceName(activeSequence->startScene).c_str(),
|
||||||
|
targetName.c_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SceneSequenceSwitch::logSequenceCanceled()
|
||||||
|
{
|
||||||
|
blog(LOG_INFO, "unexpected scene change - cancel sequence");
|
||||||
|
}
|
||||||
|
|
||||||
|
QString delayMultiplierToString(int delayMultiplier)
|
||||||
|
{
|
||||||
|
switch (delayMultiplier) {
|
||||||
|
case 1:
|
||||||
|
return obs_module_text("AdvSceneSwitcher.unit.secends");
|
||||||
|
case 60:
|
||||||
|
return obs_module_text("AdvSceneSwitcher.unit.minutes");
|
||||||
|
case 60 * 60:
|
||||||
|
return obs_module_text("AdvSceneSwitcher.unit.hours");
|
||||||
|
}
|
||||||
|
return "???";
|
||||||
|
}
|
||||||
|
|
||||||
|
QString makeExtendText(SceneSequenceSwitch *s, int curLen = 0)
|
||||||
|
{
|
||||||
|
if (!s) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
QString ext = "";
|
||||||
|
|
||||||
|
ext = QString::number(s->delay / s->delayMultiplier) + " ";
|
||||||
|
ext += delayMultiplierToString(s->delayMultiplier);
|
||||||
|
|
||||||
|
QString sceneName = GetWeakSourceName(s->scene).c_str();
|
||||||
|
if (s->targetType == SwitchTargetType::SceneGroup && s->group) {
|
||||||
|
sceneName = QString::fromStdString(s->group->name);
|
||||||
|
}
|
||||||
|
if (sceneName.isEmpty()) {
|
||||||
|
sceneName = obs_module_text("AdvSceneSwitcher.selectScene");
|
||||||
|
}
|
||||||
|
ext += " -> [" + sceneName + "]";
|
||||||
|
|
||||||
|
if (ext.length() + curLen > max_extend_text_size) {
|
||||||
|
return "...";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (s->extendedSequence.get()) {
|
||||||
|
return ext +=
|
||||||
|
" | " + makeExtendText(s->extendedSequence.get(),
|
||||||
|
curLen + ext.length());
|
||||||
|
} else {
|
||||||
|
return ext;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void populateDelayUnits(QComboBox *list)
|
void populateDelayUnits(QComboBox *list)
|
||||||
@@ -352,13 +607,36 @@ void populateDelayUnits(QComboBox *list)
|
|||||||
list->addItem(obs_module_text("AdvSceneSwitcher.unit.hours"));
|
list->addItem(obs_module_text("AdvSceneSwitcher.unit.hours"));
|
||||||
}
|
}
|
||||||
|
|
||||||
SequenceWidget::SequenceWidget(SceneSequenceSwitch *s) : SwitchWidget(s)
|
SequenceWidget::SequenceWidget(QWidget *parent, SceneSequenceSwitch *s,
|
||||||
|
bool extendSequence, bool editExtendMode,
|
||||||
|
bool showExtendText)
|
||||||
|
: SwitchWidget(parent, s, !extendSequence, true)
|
||||||
{
|
{
|
||||||
|
this->setParent(parent);
|
||||||
|
|
||||||
delay = new QDoubleSpinBox();
|
delay = new QDoubleSpinBox();
|
||||||
delayUnits = new QComboBox();
|
delayUnits = new QComboBox();
|
||||||
startScenes = new QComboBox();
|
startScenes = new QComboBox();
|
||||||
interruptible = new QCheckBox(obs_module_text(
|
interruptible = new QCheckBox(obs_module_text(
|
||||||
"AdvSceneSwitcher.sceneSequenceTab.interruptible"));
|
"AdvSceneSwitcher.sceneSequenceTab.interruptible"));
|
||||||
|
extendText = new QLabel();
|
||||||
|
extend = new QPushButton();
|
||||||
|
reduce = new QPushButton();
|
||||||
|
|
||||||
|
extend->setProperty("themeID",
|
||||||
|
QVariant(QStringLiteral("addIconSmall")));
|
||||||
|
reduce->setProperty("themeID",
|
||||||
|
QVariant(QStringLiteral("removeIconSmall")));
|
||||||
|
|
||||||
|
extend->setMaximumSize(22, 22);
|
||||||
|
reduce->setMaximumSize(22, 22);
|
||||||
|
|
||||||
|
// We need to extend the generic SwitchWidget::SceneChanged()
|
||||||
|
// with our own SequenceWidget::SceneChanged()
|
||||||
|
// so the old singal / slot needs to be disconnected
|
||||||
|
scenes->disconnect();
|
||||||
|
QWidget::connect(scenes, SIGNAL(currentTextChanged(const QString &)),
|
||||||
|
this, SLOT(SceneChanged(const QString &)));
|
||||||
|
|
||||||
QWidget::connect(delay, SIGNAL(valueChanged(double)), this,
|
QWidget::connect(delay, SIGNAL(valueChanged(double)), this,
|
||||||
SLOT(DelayChanged(double)));
|
SLOT(DelayChanged(double)));
|
||||||
@@ -370,48 +648,80 @@ SequenceWidget::SequenceWidget(SceneSequenceSwitch *s) : SwitchWidget(s)
|
|||||||
QWidget::connect(interruptible, SIGNAL(stateChanged(int)), this,
|
QWidget::connect(interruptible, SIGNAL(stateChanged(int)), this,
|
||||||
SLOT(InterruptibleChanged(int)));
|
SLOT(InterruptibleChanged(int)));
|
||||||
|
|
||||||
|
QWidget::connect(extend, SIGNAL(clicked()), this,
|
||||||
|
SLOT(ExtendClicked()));
|
||||||
|
QWidget::connect(reduce, SIGNAL(clicked()), this,
|
||||||
|
SLOT(ReduceClicked()));
|
||||||
|
|
||||||
delay->setMaximum(99999.000000);
|
delay->setMaximum(99999.000000);
|
||||||
AdvSceneSwitcher::populateSceneSelection(startScenes, false);
|
AdvSceneSwitcher::populateSceneSelection(startScenes, false);
|
||||||
populateDelayUnits(delayUnits);
|
populateDelayUnits(delayUnits);
|
||||||
interruptible->setToolTip(obs_module_text(
|
interruptible->setToolTip(obs_module_text(
|
||||||
"AdvSceneSwitcher.sceneSequenceTab.interruptibleHint"));
|
"AdvSceneSwitcher.sceneSequenceTab.interruptibleHint"));
|
||||||
|
|
||||||
if (s) {
|
// The extended sequence widgets never exist on their own and are always
|
||||||
switch (s->delayMultiplier) {
|
// place inside a non-extend sequence widget
|
||||||
case 1:
|
if (extendSequence) {
|
||||||
delayUnits->setCurrentIndex(0);
|
QHBoxLayout *mainLayout = new QHBoxLayout;
|
||||||
break;
|
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
|
||||||
case 60:
|
{"{{scenes}}", scenes},
|
||||||
delayUnits->setCurrentIndex(1);
|
{"{{delay}}", delay},
|
||||||
break;
|
{"{{delayUnits}}", delayUnits},
|
||||||
case 3600:
|
{"{{transitions}}", transitions}};
|
||||||
delayUnits->setCurrentIndex(2);
|
placeWidgets(
|
||||||
break;
|
obs_module_text(
|
||||||
default:
|
"AdvSceneSwitcher.sceneSequenceTab.extendEntry"),
|
||||||
delayUnits->setCurrentIndex(0);
|
mainLayout, widgetPlaceholders);
|
||||||
|
setLayout(mainLayout);
|
||||||
|
} else {
|
||||||
|
QHBoxLayout *startSequence = new QHBoxLayout;
|
||||||
|
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
|
||||||
|
{"{{startScenes}}", startScenes},
|
||||||
|
{"{{scenes}}", scenes},
|
||||||
|
{"{{delay}}", delay},
|
||||||
|
{"{{delayUnits}}", delayUnits},
|
||||||
|
{"{{transitions}}", transitions},
|
||||||
|
{"{{interruptible}}", interruptible}};
|
||||||
|
placeWidgets(obs_module_text(
|
||||||
|
"AdvSceneSwitcher.sceneSequenceTab.entry"),
|
||||||
|
startSequence, widgetPlaceholders);
|
||||||
|
|
||||||
|
// The exetend widgets placed here
|
||||||
|
extendSequenceLayout = new QVBoxLayout;
|
||||||
|
if (s) {
|
||||||
|
if (!editExtendMode) {
|
||||||
|
extendText->setText(makeExtendText(
|
||||||
|
s->extendedSequence.get()));
|
||||||
|
} else {
|
||||||
|
auto cur = s->extendedSequence.get();
|
||||||
|
while (cur != nullptr) {
|
||||||
|
extendSequenceLayout->addWidget(
|
||||||
|
new SequenceWidget(parent, cur,
|
||||||
|
true, true));
|
||||||
|
cur = cur->extendedSequence.get();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
startScenes->setCurrentText(
|
|
||||||
GetWeakSourceName(s->startScene).c_str());
|
QHBoxLayout *extendSequenceControlsLayout = new QHBoxLayout;
|
||||||
interruptible->setChecked(s->interruptible);
|
if (editExtendMode) {
|
||||||
|
extendSequenceControlsLayout->addWidget(extend);
|
||||||
|
extendSequenceControlsLayout->addWidget(reduce);
|
||||||
|
}
|
||||||
|
extendSequenceControlsLayout->addStretch();
|
||||||
|
|
||||||
|
QVBoxLayout *mainLayout = new QVBoxLayout;
|
||||||
|
mainLayout->addLayout(startSequence);
|
||||||
|
mainLayout->addLayout(extendSequenceLayout);
|
||||||
|
mainLayout->addWidget(extendText);
|
||||||
|
mainLayout->addLayout(extendSequenceControlsLayout);
|
||||||
|
setLayout(mainLayout);
|
||||||
}
|
}
|
||||||
|
|
||||||
QHBoxLayout *mainLayout = new QHBoxLayout;
|
|
||||||
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
|
|
||||||
{"{{startScenes}}", startScenes},
|
|
||||||
{"{{scenes}}", scenes},
|
|
||||||
{"{{delay}}", delay},
|
|
||||||
{"{{delayUnits}}", delayUnits},
|
|
||||||
{"{{transitions}}", transitions},
|
|
||||||
{"{{interruptible}}", interruptible}};
|
|
||||||
placeWidgets(obs_module_text("AdvSceneSwitcher.sceneSequenceTab.entry"),
|
|
||||||
mainLayout, widgetPlaceholders);
|
|
||||||
setLayout(mainLayout);
|
|
||||||
|
|
||||||
switchData = s;
|
switchData = s;
|
||||||
|
UpdateWidgetStatus(showExtendText);
|
||||||
|
|
||||||
loading = false;
|
loading = false;
|
||||||
|
|
||||||
UpdateDelay();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SceneSequenceSwitch *SequenceWidget::getSwitchData()
|
SceneSequenceSwitch *SequenceWidget::getSwitchData()
|
||||||
@@ -435,22 +745,26 @@ void SequenceWidget::swapSwitchData(SequenceWidget *s1, SequenceWidget *s2)
|
|||||||
|
|
||||||
void SequenceWidget::UpdateDelay()
|
void SequenceWidget::UpdateDelay()
|
||||||
{
|
{
|
||||||
if (switchData)
|
if (switchData) {
|
||||||
delay->setValue(switchData->delay /
|
delay->setValue(switchData->delay /
|
||||||
switchData->delayMultiplier);
|
switchData->delayMultiplier);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void SequenceWidget::DelayChanged(double delay)
|
void SequenceWidget::DelayChanged(double delay)
|
||||||
{
|
{
|
||||||
if (loading || !switchData)
|
if (loading || !switchData) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
switchData->delay = delay * switchData->delayMultiplier;
|
switchData->delay = delay * switchData->delayMultiplier;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SequenceWidget::DelayUnitsChanged(int idx)
|
void SequenceWidget::DelayUnitsChanged(int idx)
|
||||||
{
|
{
|
||||||
if (loading || !switchData)
|
if (loading || !switchData) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
delay_units unit = (delay_units)idx;
|
delay_units unit = (delay_units)idx;
|
||||||
|
|
||||||
@@ -471,18 +785,113 @@ void SequenceWidget::DelayUnitsChanged(int idx)
|
|||||||
UpdateDelay();
|
UpdateDelay();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SequenceWidget::setExtendedSequenceStartScene()
|
||||||
|
{
|
||||||
|
switchData->extendedSequence->startScene = switchData->scene;
|
||||||
|
switchData->extendedSequence->startTargetType = SwitchTargetType::Scene;
|
||||||
|
|
||||||
|
if (switchData->targetType == SwitchTargetType::SceneGroup) {
|
||||||
|
switchData->extendedSequence->startScene = nullptr;
|
||||||
|
switchData->extendedSequence->startTargetType =
|
||||||
|
SwitchTargetType::SceneGroup;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SequenceWidget::SceneChanged(const QString &text)
|
||||||
|
{
|
||||||
|
if (loading || !switchData) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
SwitchWidget::SceneChanged(text);
|
||||||
|
std::lock_guard<std::mutex> lock(switcher->m);
|
||||||
|
|
||||||
|
if (switchData->extendedSequence) {
|
||||||
|
setExtendedSequenceStartScene();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void SequenceWidget::StartSceneChanged(const QString &text)
|
void SequenceWidget::StartSceneChanged(const QString &text)
|
||||||
{
|
{
|
||||||
if (loading || !switchData)
|
if (loading || !switchData) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
std::lock_guard<std::mutex> lock(switcher->m);
|
std::lock_guard<std::mutex> lock(switcher->m);
|
||||||
switchData->startScene = GetWeakSourceByQString(text);
|
switchData->startScene = GetWeakSourceByQString(text);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SequenceWidget::InterruptibleChanged(int state)
|
void SequenceWidget::InterruptibleChanged(int state)
|
||||||
{
|
{
|
||||||
if (loading || !switchData)
|
if (loading || !switchData) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
std::lock_guard<std::mutex> lock(switcher->m);
|
std::lock_guard<std::mutex> lock(switcher->m);
|
||||||
switchData->interruptible = state;
|
switchData->interruptible = state;
|
||||||
|
|
||||||
|
auto cur = switchData->extendedSequence.get();
|
||||||
|
while (cur != nullptr) {
|
||||||
|
cur->interruptible = state;
|
||||||
|
cur = cur->extendedSequence.get();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SequenceWidget::UpdateWidgetStatus(bool showExtendText)
|
||||||
|
{
|
||||||
|
if (showExtendText) {
|
||||||
|
extendText->setText(
|
||||||
|
makeExtendText(switchData->extendedSequence.get()));
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (switchData->delayMultiplier) {
|
||||||
|
case 1:
|
||||||
|
delayUnits->setCurrentIndex(0);
|
||||||
|
break;
|
||||||
|
case 60:
|
||||||
|
delayUnits->setCurrentIndex(1);
|
||||||
|
break;
|
||||||
|
case 3600:
|
||||||
|
delayUnits->setCurrentIndex(2);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
delayUnits->setCurrentIndex(0);
|
||||||
|
}
|
||||||
|
UpdateDelay();
|
||||||
|
|
||||||
|
startScenes->setCurrentText(
|
||||||
|
GetWeakSourceName(switchData->startScene).c_str());
|
||||||
|
interruptible->setChecked(switchData->interruptible);
|
||||||
|
|
||||||
|
SwitchWidget::showSwitchData();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SequenceWidget::ExtendClicked()
|
||||||
|
{
|
||||||
|
if (loading || !switchData) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::lock_guard<std::mutex> lock(switcher->m);
|
||||||
|
auto es = switchData->extend();
|
||||||
|
|
||||||
|
SequenceWidget *ew = new SequenceWidget(this->parentWidget(), es, true);
|
||||||
|
extendSequenceLayout->addWidget(ew);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SequenceWidget::ReduceClicked()
|
||||||
|
{
|
||||||
|
if (loading || !switchData) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::lock_guard<std::mutex> lock(switcher->m);
|
||||||
|
switchData->reduce();
|
||||||
|
|
||||||
|
int count = extendSequenceLayout->count();
|
||||||
|
auto item = extendSequenceLayout->takeAt(count - 1);
|
||||||
|
if (item) {
|
||||||
|
item->widget()->setVisible(false);
|
||||||
|
delete item;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,15 +10,19 @@ void AdvSceneSwitcher::on_timeAdd_clicked()
|
|||||||
switcher->timeSwitches.emplace_back();
|
switcher->timeSwitches.emplace_back();
|
||||||
|
|
||||||
listAddClicked(ui->timeSwitches,
|
listAddClicked(ui->timeSwitches,
|
||||||
new TimeSwitchWidget(&switcher->timeSwitches.back()),
|
new TimeSwitchWidget(this,
|
||||||
|
&switcher->timeSwitches.back()),
|
||||||
ui->timeAdd, &addPulse);
|
ui->timeAdd, &addPulse);
|
||||||
|
|
||||||
|
ui->timeHelp->setVisible(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AdvSceneSwitcher::on_timeRemove_clicked()
|
void AdvSceneSwitcher::on_timeRemove_clicked()
|
||||||
{
|
{
|
||||||
QListWidgetItem *item = ui->timeSwitches->currentItem();
|
QListWidgetItem *item = ui->timeSwitches->currentItem();
|
||||||
if (!item)
|
if (!item) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> lock(switcher->m);
|
std::lock_guard<std::mutex> lock(switcher->m);
|
||||||
@@ -33,8 +37,9 @@ void AdvSceneSwitcher::on_timeRemove_clicked()
|
|||||||
void AdvSceneSwitcher::on_timeUp_clicked()
|
void AdvSceneSwitcher::on_timeUp_clicked()
|
||||||
{
|
{
|
||||||
int index = ui->timeSwitches->currentRow();
|
int index = ui->timeSwitches->currentRow();
|
||||||
if (!listMoveUp(ui->timeSwitches))
|
if (!listMoveUp(ui->timeSwitches)) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
TimeSwitchWidget *s1 = (TimeSwitchWidget *)ui->timeSwitches->itemWidget(
|
TimeSwitchWidget *s1 = (TimeSwitchWidget *)ui->timeSwitches->itemWidget(
|
||||||
ui->timeSwitches->item(index));
|
ui->timeSwitches->item(index));
|
||||||
@@ -52,8 +57,9 @@ void AdvSceneSwitcher::on_timeDown_clicked()
|
|||||||
{
|
{
|
||||||
int index = ui->timeSwitches->currentRow();
|
int index = ui->timeSwitches->currentRow();
|
||||||
|
|
||||||
if (!listMoveDown(ui->timeSwitches))
|
if (!listMoveDown(ui->timeSwitches)) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
TimeSwitchWidget *s1 = (TimeSwitchWidget *)ui->timeSwitches->itemWidget(
|
TimeSwitchWidget *s1 = (TimeSwitchWidget *)ui->timeSwitches->itemWidget(
|
||||||
ui->timeSwitches->item(index));
|
ui->timeSwitches->item(index));
|
||||||
@@ -69,14 +75,15 @@ void AdvSceneSwitcher::on_timeDown_clicked()
|
|||||||
|
|
||||||
bool timesAreInInterval(QTime &time1, QTime &time2, int &interval)
|
bool timesAreInInterval(QTime &time1, QTime &time2, int &interval)
|
||||||
{
|
{
|
||||||
if (time1.isNull() || time2.isNull())
|
if (time1.isNull() || time2.isNull()) {
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
bool ret = false;
|
bool ret = false;
|
||||||
QTime validSwitchTimeWindow = time1.addMSecs(interval);
|
QTime validSwitchTimeWindow = time1.addMSecs(interval);
|
||||||
|
|
||||||
ret = time1 <= time2 && time2 <= validSwitchTimeWindow;
|
ret = time1 <= time2 && time2 <= validSwitchTimeWindow;
|
||||||
// check for overflow
|
// Check for overflow
|
||||||
if (!ret && validSwitchTimeWindow.msecsSinceStartOfDay() < interval) {
|
if (!ret && validSwitchTimeWindow.msecsSinceStartOfDay() < interval) {
|
||||||
ret = time2 >= time1 || time2 <= validSwitchTimeWindow;
|
ret = time2 >= time1 || time2 <= validSwitchTimeWindow;
|
||||||
}
|
}
|
||||||
@@ -85,8 +92,9 @@ bool timesAreInInterval(QTime &time1, QTime &time2, int &interval)
|
|||||||
|
|
||||||
bool checkLiveTime(TimeSwitch &s, QDateTime &start, int &interval)
|
bool checkLiveTime(TimeSwitch &s, QDateTime &start, int &interval)
|
||||||
{
|
{
|
||||||
if (start.isNull())
|
if (start.isNull()) {
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
QDateTime now = QDateTime::currentDateTime();
|
QDateTime now = QDateTime::currentDateTime();
|
||||||
QTime timePassed = QTime(0, 0).addMSecs(start.msecsTo(now));
|
QTime timePassed = QTime(0, 0).addMSecs(start.msecsTo(now));
|
||||||
@@ -108,25 +116,29 @@ bool checkRegularTime(TimeSwitch &s, int &interval)
|
|||||||
void SwitcherData::checkTimeSwitch(bool &match, OBSWeakSource &scene,
|
void SwitcherData::checkTimeSwitch(bool &match, OBSWeakSource &scene,
|
||||||
OBSWeakSource &transition)
|
OBSWeakSource &transition)
|
||||||
{
|
{
|
||||||
if (TimeSwitch::pause)
|
if (TimeSwitch::pause) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
for (TimeSwitch &s : timeSwitches) {
|
for (TimeSwitch &s : timeSwitches) {
|
||||||
if (!s.initialized())
|
if (!s.initialized()) {
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (s.trigger == LIVE)
|
if (s.trigger == LIVE) {
|
||||||
match = checkLiveTime(s, liveTime, interval);
|
match = checkLiveTime(s, liveTime, interval);
|
||||||
else
|
} else {
|
||||||
match = checkRegularTime(s, interval);
|
match = checkRegularTime(s, interval);
|
||||||
|
}
|
||||||
|
|
||||||
if (match) {
|
if (match) {
|
||||||
scene = (s.usePreviousScene) ? previousScene : s.scene;
|
scene = s.getScene();
|
||||||
transition = s.transition;
|
transition = s.transition;
|
||||||
match = true;
|
match = true;
|
||||||
|
|
||||||
if (verbose)
|
if (verbose) {
|
||||||
s.logMatch();
|
s.logMatch();
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -138,28 +150,8 @@ void SwitcherData::saveTimeSwitches(obs_data_t *obj)
|
|||||||
for (TimeSwitch &s : switcher->timeSwitches) {
|
for (TimeSwitch &s : switcher->timeSwitches) {
|
||||||
obs_data_t *array_obj = obs_data_create();
|
obs_data_t *array_obj = obs_data_create();
|
||||||
|
|
||||||
obs_source_t *sceneSource = obs_weak_source_get_source(s.scene);
|
s.save(array_obj);
|
||||||
obs_source_t *transition =
|
obs_data_array_push_back(timeArray, array_obj);
|
||||||
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_release(array_obj);
|
||||||
}
|
}
|
||||||
@@ -177,18 +169,8 @@ void SwitcherData::loadTimeSwitches(obs_data_t *obj)
|
|||||||
for (size_t i = 0; i < count; i++) {
|
for (size_t i = 0; i < count; i++) {
|
||||||
obs_data_t *array_obj = obs_data_array_item(timeArray, i);
|
obs_data_t *array_obj = obs_data_array_item(timeArray, i);
|
||||||
|
|
||||||
const char *scene = obs_data_get_string(array_obj, "scene");
|
switcher->timeSwitches.emplace_back();
|
||||||
const char *transition =
|
timeSwitches.back().load(array_obj);
|
||||||
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_release(array_obj);
|
||||||
}
|
}
|
||||||
@@ -201,16 +183,65 @@ void AdvSceneSwitcher::setupTimeTab()
|
|||||||
QListWidgetItem *item;
|
QListWidgetItem *item;
|
||||||
item = new QListWidgetItem(ui->timeSwitches);
|
item = new QListWidgetItem(ui->timeSwitches);
|
||||||
ui->timeSwitches->addItem(item);
|
ui->timeSwitches->addItem(item);
|
||||||
TimeSwitchWidget *sw = new TimeSwitchWidget(&s);
|
TimeSwitchWidget *sw = new TimeSwitchWidget(this, &s);
|
||||||
item->setSizeHint(sw->minimumSizeHint());
|
item->setSizeHint(sw->minimumSizeHint());
|
||||||
ui->timeSwitches->setItemWidget(item, sw);
|
ui->timeSwitches->setItemWidget(item, sw);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (switcher->timeSwitches.size() == 0)
|
if (switcher->timeSwitches.size() == 0) {
|
||||||
addPulse = PulseWidget(ui->timeAdd, QColor(Qt::green));
|
addPulse = PulseWidget(ui->timeAdd, QColor(Qt::green));
|
||||||
|
ui->timeHelp->setVisible(true);
|
||||||
|
} else {
|
||||||
|
ui->timeHelp->setVisible(false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void populateTriggers(QComboBox *list)
|
void TimeSwitch::save(obs_data_t *obj)
|
||||||
|
{
|
||||||
|
SceneSwitcherEntry::save(obj);
|
||||||
|
|
||||||
|
obs_data_set_int(obj, "trigger", trigger);
|
||||||
|
obs_data_set_string(obj, "time", time.toString().toStdString().c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
// To be removed in future version
|
||||||
|
bool loadOldTime(obs_data_t *obj, TimeSwitch *s)
|
||||||
|
{
|
||||||
|
if (!s) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *scene = obs_data_get_string(obj, "scene");
|
||||||
|
|
||||||
|
if (strcmp(scene, "") == 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
s->scene = GetWeakSourceByName(scene);
|
||||||
|
|
||||||
|
const char *transition = obs_data_get_string(obj, "transition");
|
||||||
|
s->transition = GetWeakTransitionByName(transition);
|
||||||
|
|
||||||
|
s->trigger = (timeTrigger)obs_data_get_int(obj, "trigger");
|
||||||
|
s->time = QTime::fromString(obs_data_get_string(obj, "time"));
|
||||||
|
s->usePreviousScene = strcmp(scene, previous_scene_name) == 0;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TimeSwitch::load(obs_data_t *obj)
|
||||||
|
{
|
||||||
|
if (loadOldTime(obj, this)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
SceneSwitcherEntry::load(obj);
|
||||||
|
|
||||||
|
trigger = (timeTrigger)obs_data_get_int(obj, "trigger");
|
||||||
|
time = QTime::fromString(obs_data_get_string(obj, "time"));
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void populateTriggers(QComboBox *list)
|
||||||
{
|
{
|
||||||
list->addItem(obs_module_text("AdvSceneSwitcher.timeTab.anyDay"));
|
list->addItem(obs_module_text("AdvSceneSwitcher.timeTab.anyDay"));
|
||||||
list->addItem(obs_module_text("AdvSceneSwitcher.timeTab.mondays"));
|
list->addItem(obs_module_text("AdvSceneSwitcher.timeTab.mondays"));
|
||||||
@@ -227,7 +258,8 @@ void populateTriggers(QComboBox *list)
|
|||||||
Qt::ToolTipRole);
|
Qt::ToolTipRole);
|
||||||
}
|
}
|
||||||
|
|
||||||
TimeSwitchWidget::TimeSwitchWidget(TimeSwitch *s) : SwitchWidget(s)
|
TimeSwitchWidget::TimeSwitchWidget(QWidget *parent, TimeSwitch *s)
|
||||||
|
: SwitchWidget(parent, s, true, true)
|
||||||
{
|
{
|
||||||
triggers = new QComboBox();
|
triggers = new QComboBox();
|
||||||
time = new QTimeEdit();
|
time = new QTimeEdit();
|
||||||
@@ -282,16 +314,20 @@ void TimeSwitchWidget::swapSwitchData(TimeSwitchWidget *s1,
|
|||||||
|
|
||||||
void TimeSwitchWidget::TriggerChanged(int index)
|
void TimeSwitchWidget::TriggerChanged(int index)
|
||||||
{
|
{
|
||||||
if (loading || !switchData)
|
if (loading || !switchData) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
std::lock_guard<std::mutex> lock(switcher->m);
|
std::lock_guard<std::mutex> lock(switcher->m);
|
||||||
switchData->trigger = (timeTrigger)index;
|
switchData->trigger = (timeTrigger)index;
|
||||||
}
|
}
|
||||||
|
|
||||||
void TimeSwitchWidget::TimeChanged(const QTime &time)
|
void TimeSwitchWidget::TimeChanged(const QTime &time)
|
||||||
{
|
{
|
||||||
if (loading || !switchData)
|
if (loading || !switchData) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
std::lock_guard<std::mutex> lock(switcher->m);
|
std::lock_guard<std::mutex> lock(switcher->m);
|
||||||
switchData->time = time;
|
switchData->time = time;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,23 +1,30 @@
|
|||||||
|
#include <thread>
|
||||||
|
|
||||||
#include "headers/advanced-scene-switcher.hpp"
|
#include "headers/advanced-scene-switcher.hpp"
|
||||||
#include "headers/utility.hpp"
|
#include "headers/utility.hpp"
|
||||||
|
|
||||||
|
constexpr auto default_def_transition_dealy = 50;
|
||||||
bool DefaultSceneTransition::pause = false;
|
bool DefaultSceneTransition::pause = false;
|
||||||
|
unsigned int DefaultSceneTransition::delay = default_def_transition_dealy;
|
||||||
|
|
||||||
void AdvSceneSwitcher::on_transitionsAdd_clicked()
|
void AdvSceneSwitcher::on_transitionsAdd_clicked()
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> lock(switcher->m);
|
std::lock_guard<std::mutex> lock(switcher->m);
|
||||||
switcher->sceneTransitions.emplace_back();
|
switcher->sceneTransitions.emplace_back();
|
||||||
|
|
||||||
listAddClicked(
|
listAddClicked(ui->sceneTransitions,
|
||||||
ui->sceneTransitions,
|
new TransitionSwitchWidget(
|
||||||
new TransitionSwitchWidget(&switcher->sceneTransitions.back()));
|
this, &switcher->sceneTransitions.back()));
|
||||||
|
|
||||||
|
ui->transitionHelp->setVisible(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AdvSceneSwitcher::on_transitionsRemove_clicked()
|
void AdvSceneSwitcher::on_transitionsRemove_clicked()
|
||||||
{
|
{
|
||||||
QListWidgetItem *item = ui->sceneTransitions->currentItem();
|
QListWidgetItem *item = ui->sceneTransitions->currentItem();
|
||||||
if (!item)
|
if (!item) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> lock(switcher->m);
|
std::lock_guard<std::mutex> lock(switcher->m);
|
||||||
@@ -32,8 +39,9 @@ void AdvSceneSwitcher::on_transitionsRemove_clicked()
|
|||||||
void AdvSceneSwitcher::on_transitionsUp_clicked()
|
void AdvSceneSwitcher::on_transitionsUp_clicked()
|
||||||
{
|
{
|
||||||
int index = ui->sceneTransitions->currentRow();
|
int index = ui->sceneTransitions->currentRow();
|
||||||
if (!listMoveUp(ui->sceneTransitions))
|
if (!listMoveUp(ui->sceneTransitions)) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
TransitionSwitchWidget *s1 =
|
TransitionSwitchWidget *s1 =
|
||||||
(TransitionSwitchWidget *)ui->sceneTransitions->itemWidget(
|
(TransitionSwitchWidget *)ui->sceneTransitions->itemWidget(
|
||||||
@@ -53,8 +61,9 @@ void AdvSceneSwitcher::on_transitionsDown_clicked()
|
|||||||
{
|
{
|
||||||
int index = ui->sceneTransitions->currentRow();
|
int index = ui->sceneTransitions->currentRow();
|
||||||
|
|
||||||
if (!listMoveDown(ui->sceneTransitions))
|
if (!listMoveDown(ui->sceneTransitions)) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
TransitionSwitchWidget *s1 =
|
TransitionSwitchWidget *s1 =
|
||||||
(TransitionSwitchWidget *)ui->sceneTransitions->itemWidget(
|
(TransitionSwitchWidget *)ui->sceneTransitions->itemWidget(
|
||||||
@@ -77,14 +86,18 @@ void AdvSceneSwitcher::on_defaultTransitionsAdd_clicked()
|
|||||||
|
|
||||||
listAddClicked(ui->defaultTransitions,
|
listAddClicked(ui->defaultTransitions,
|
||||||
new DefTransitionSwitchWidget(
|
new DefTransitionSwitchWidget(
|
||||||
|
this,
|
||||||
&switcher->defaultSceneTransitions.back()));
|
&switcher->defaultSceneTransitions.back()));
|
||||||
|
|
||||||
|
ui->defaultTransitionHelp->setVisible(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AdvSceneSwitcher::on_defaultTransitionsRemove_clicked()
|
void AdvSceneSwitcher::on_defaultTransitionsRemove_clicked()
|
||||||
{
|
{
|
||||||
QListWidgetItem *item = ui->defaultTransitions->currentItem();
|
QListWidgetItem *item = ui->defaultTransitions->currentItem();
|
||||||
if (!item)
|
if (!item) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> lock(switcher->m);
|
std::lock_guard<std::mutex> lock(switcher->m);
|
||||||
@@ -99,8 +112,9 @@ void AdvSceneSwitcher::on_defaultTransitionsRemove_clicked()
|
|||||||
void AdvSceneSwitcher::on_defaultTransitionsUp_clicked()
|
void AdvSceneSwitcher::on_defaultTransitionsUp_clicked()
|
||||||
{
|
{
|
||||||
int index = ui->defaultTransitions->currentRow();
|
int index = ui->defaultTransitions->currentRow();
|
||||||
if (!listMoveUp(ui->defaultTransitions))
|
if (!listMoveUp(ui->defaultTransitions)) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
TransitionSwitchWidget *s1 =
|
TransitionSwitchWidget *s1 =
|
||||||
(TransitionSwitchWidget *)ui->defaultTransitions->itemWidget(
|
(TransitionSwitchWidget *)ui->defaultTransitions->itemWidget(
|
||||||
@@ -120,8 +134,9 @@ void AdvSceneSwitcher::on_defaultTransitionsDown_clicked()
|
|||||||
{
|
{
|
||||||
int index = ui->defaultTransitions->currentRow();
|
int index = ui->defaultTransitions->currentRow();
|
||||||
|
|
||||||
if (!listMoveDown(ui->defaultTransitions))
|
if (!listMoveDown(ui->defaultTransitions)) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
DefTransitionSwitchWidget *s1 =
|
DefTransitionSwitchWidget *s1 =
|
||||||
(DefTransitionSwitchWidget *)ui->defaultTransitions->itemWidget(
|
(DefTransitionSwitchWidget *)ui->defaultTransitions->itemWidget(
|
||||||
@@ -137,45 +152,35 @@ void AdvSceneSwitcher::on_defaultTransitionsDown_clicked()
|
|||||||
switcher->defaultSceneTransitions[index + 1]);
|
switcher->defaultSceneTransitions[index + 1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SwitcherData::checkDefaultSceneTransitions(bool &match,
|
void SwitcherData::checkDefaultSceneTransitions()
|
||||||
OBSWeakSource &transition)
|
|
||||||
{
|
{
|
||||||
if (checkedDefTransition || DefaultSceneTransition::pause)
|
if (DefaultSceneTransition::pause || stop) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
obs_source_t *currentSource = obs_frontend_get_current_scene();
|
obs_source_t *currentSceneSource = obs_frontend_get_current_scene();
|
||||||
obs_weak_source_t *ws = obs_source_get_weak_source(currentSource);
|
obs_weak_source_t *currentScene =
|
||||||
|
obs_source_get_weak_source(currentSceneSource);
|
||||||
|
|
||||||
for (DefaultSceneTransition &s : defaultSceneTransitions) {
|
for (auto &t : defaultSceneTransitions) {
|
||||||
if (s.scene == ws) {
|
if (t.checkMatch(currentScene)) {
|
||||||
if (!s.initialized())
|
if (verbose) {
|
||||||
continue;
|
t.logMatch();
|
||||||
|
}
|
||||||
match = true;
|
t.setTransition();
|
||||||
transition = s.transition;
|
|
||||||
|
|
||||||
if (verbose)
|
|
||||||
s.logMatch();
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
obs_source_release(currentSource);
|
|
||||||
obs_weak_source_release(ws);
|
|
||||||
|
|
||||||
checkedDefTransition = true;
|
obs_weak_source_release(currentScene);
|
||||||
}
|
obs_source_release(currentSceneSource);
|
||||||
|
|
||||||
void SwitcherData::setCurrentDefTransition(OBSWeakSource &transition)
|
|
||||||
{
|
|
||||||
obs_source_t *transitionSource = obs_weak_source_get_source(transition);
|
|
||||||
obs_frontend_set_current_transition(transitionSource);
|
|
||||||
obs_source_release(transitionSource);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void AdvSceneSwitcher::on_transitionOverridecheckBox_stateChanged(int state)
|
void AdvSceneSwitcher::on_transitionOverridecheckBox_stateChanged(int state)
|
||||||
{
|
{
|
||||||
if (loading)
|
if (loading) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
std::lock_guard<std::mutex> lock(switcher->m);
|
std::lock_guard<std::mutex> lock(switcher->m);
|
||||||
if (!state) {
|
if (!state) {
|
||||||
@@ -185,14 +190,25 @@ void AdvSceneSwitcher::on_transitionOverridecheckBox_stateChanged(int state)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void AdvSceneSwitcher::defTransitionDelayValueChanged(int value)
|
||||||
|
{
|
||||||
|
if (loading) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::lock_guard<std::mutex> lock(switcher->m);
|
||||||
|
DefaultSceneTransition::delay = value;
|
||||||
|
}
|
||||||
|
|
||||||
obs_weak_source_t *getNextTransition(obs_weak_source_t *scene1,
|
obs_weak_source_t *getNextTransition(obs_weak_source_t *scene1,
|
||||||
obs_weak_source_t *scene2)
|
obs_weak_source_t *scene2)
|
||||||
{
|
{
|
||||||
obs_weak_source_t *ws = nullptr;
|
obs_weak_source_t *ws = nullptr;
|
||||||
if (scene1 && scene2) {
|
if (scene1 && scene2) {
|
||||||
for (SceneTransition &t : switcher->sceneTransitions) {
|
for (SceneTransition &t : switcher->sceneTransitions) {
|
||||||
if (!t.initialized())
|
if (!t.initialized()) {
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (t.scene == scene1 && t.scene2 == scene2) {
|
if (t.scene == scene1 && t.scene2 == scene2) {
|
||||||
ws = t.transition;
|
ws = t.transition;
|
||||||
@@ -254,8 +270,9 @@ void setNextTransition(OBSWeakSource &targetScene, obs_source_t *currentSource,
|
|||||||
obs_frontend_set_current_transition(nextTransition);
|
obs_frontend_set_current_transition(nextTransition);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (transitionOverrideOverride)
|
if (transitionOverrideOverride) {
|
||||||
overwriteTransitionOverride(targetScene, nextTransition, td);
|
overwriteTransitionOverride(targetScene, nextTransition, td);
|
||||||
|
}
|
||||||
|
|
||||||
obs_weak_source_release(nextTransitionWs);
|
obs_weak_source_release(nextTransitionWs);
|
||||||
obs_source_release(nextTransition);
|
obs_source_release(nextTransition);
|
||||||
@@ -317,6 +334,10 @@ void SwitcherData::saveSceneTransitions(obs_data_t *obj)
|
|||||||
|
|
||||||
obs_data_set_bool(obj, "tansitionOverrideOverride",
|
obs_data_set_bool(obj, "tansitionOverrideOverride",
|
||||||
switcher->tansitionOverrideOverride);
|
switcher->tansitionOverrideOverride);
|
||||||
|
obs_data_set_default_int(obj, "defTransitionDelay",
|
||||||
|
default_def_transition_dealy);
|
||||||
|
obs_data_set_int(obj, "defTransitionDelay",
|
||||||
|
DefaultSceneTransition::delay);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SwitcherData::loadSceneTransitions(obs_data_t *obj)
|
void SwitcherData::loadSceneTransitions(obs_data_t *obj)
|
||||||
@@ -369,6 +390,8 @@ void SwitcherData::loadSceneTransitions(obs_data_t *obj)
|
|||||||
|
|
||||||
switcher->tansitionOverrideOverride =
|
switcher->tansitionOverrideOverride =
|
||||||
obs_data_get_bool(obj, "tansitionOverrideOverride");
|
obs_data_get_bool(obj, "tansitionOverrideOverride");
|
||||||
|
DefaultSceneTransition::delay =
|
||||||
|
obs_data_get_int(obj, "defTransitionDelay");
|
||||||
}
|
}
|
||||||
|
|
||||||
void AdvSceneSwitcher::setupTransitionsTab()
|
void AdvSceneSwitcher::setupTransitionsTab()
|
||||||
@@ -377,23 +400,54 @@ void AdvSceneSwitcher::setupTransitionsTab()
|
|||||||
QListWidgetItem *item;
|
QListWidgetItem *item;
|
||||||
item = new QListWidgetItem(ui->sceneTransitions);
|
item = new QListWidgetItem(ui->sceneTransitions);
|
||||||
ui->sceneTransitions->addItem(item);
|
ui->sceneTransitions->addItem(item);
|
||||||
TransitionSwitchWidget *sw = new TransitionSwitchWidget(&s);
|
TransitionSwitchWidget *sw =
|
||||||
|
new TransitionSwitchWidget(this, &s);
|
||||||
item->setSizeHint(sw->minimumSizeHint());
|
item->setSizeHint(sw->minimumSizeHint());
|
||||||
ui->sceneTransitions->setItemWidget(item, sw);
|
ui->sceneTransitions->setItemWidget(item, sw);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (switcher->sceneTransitions.size() == 0) {
|
||||||
|
ui->transitionHelp->setVisible(true);
|
||||||
|
} else {
|
||||||
|
ui->transitionHelp->setVisible(false);
|
||||||
|
}
|
||||||
|
|
||||||
for (auto &s : switcher->defaultSceneTransitions) {
|
for (auto &s : switcher->defaultSceneTransitions) {
|
||||||
QListWidgetItem *item;
|
QListWidgetItem *item;
|
||||||
item = new QListWidgetItem(ui->defaultTransitions);
|
item = new QListWidgetItem(ui->defaultTransitions);
|
||||||
ui->defaultTransitions->addItem(item);
|
ui->defaultTransitions->addItem(item);
|
||||||
DefTransitionSwitchWidget *sw =
|
DefTransitionSwitchWidget *sw =
|
||||||
new DefTransitionSwitchWidget(&s);
|
new DefTransitionSwitchWidget(this, &s);
|
||||||
item->setSizeHint(sw->minimumSizeHint());
|
item->setSizeHint(sw->minimumSizeHint());
|
||||||
ui->defaultTransitions->setItemWidget(item, sw);
|
ui->defaultTransitions->setItemWidget(item, sw);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (switcher->defaultSceneTransitions.size() == 0) {
|
||||||
|
ui->defaultTransitionHelp->setVisible(true);
|
||||||
|
} else {
|
||||||
|
ui->defaultTransitionHelp->setVisible(false);
|
||||||
|
}
|
||||||
|
|
||||||
ui->transitionOverridecheckBox->setChecked(
|
ui->transitionOverridecheckBox->setChecked(
|
||||||
switcher->tansitionOverrideOverride);
|
switcher->tansitionOverrideOverride);
|
||||||
|
|
||||||
|
QSpinBox *defTransitionDelay = new QSpinBox();
|
||||||
|
defTransitionDelay->setSuffix("ms");
|
||||||
|
defTransitionDelay->setMinimum(50);
|
||||||
|
defTransitionDelay->setMaximum(10000);
|
||||||
|
defTransitionDelay->setValue(DefaultSceneTransition::delay);
|
||||||
|
defTransitionDelay->setToolTip(obs_module_text(
|
||||||
|
"AdvSceneSwitcher.transitionTab.defaultTransition.delay.help"));
|
||||||
|
|
||||||
|
QWidget::connect(defTransitionDelay, SIGNAL(valueChanged(int)), this,
|
||||||
|
SLOT(defTransitionDelayValueChanged(int)));
|
||||||
|
|
||||||
|
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
|
||||||
|
{"{{defTransitionDelay}}", defTransitionDelay}};
|
||||||
|
placeWidgets(
|
||||||
|
obs_module_text(
|
||||||
|
"AdvSceneSwitcher.transitionTab.defaultTransition.delay"),
|
||||||
|
ui->defTransitionDelayLayout, widgetPlaceholders);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SceneTransition::initialized()
|
bool SceneTransition::initialized()
|
||||||
@@ -407,8 +461,9 @@ bool SceneTransition::valid()
|
|||||||
(SceneSwitcherEntry::valid() && WeakSourceValid(scene2));
|
(SceneSwitcherEntry::valid() && WeakSourceValid(scene2));
|
||||||
}
|
}
|
||||||
|
|
||||||
TransitionSwitchWidget::TransitionSwitchWidget(SceneTransition *s)
|
TransitionSwitchWidget::TransitionSwitchWidget(QWidget *parent,
|
||||||
: SwitchWidget(s, false)
|
SceneTransition *s)
|
||||||
|
: SwitchWidget(parent, s, false, false, false)
|
||||||
{
|
{
|
||||||
scenes2 = new QComboBox();
|
scenes2 = new QComboBox();
|
||||||
|
|
||||||
@@ -457,14 +512,17 @@ void TransitionSwitchWidget::swapSwitchData(TransitionSwitchWidget *s1,
|
|||||||
|
|
||||||
void TransitionSwitchWidget::Scene2Changed(const QString &text)
|
void TransitionSwitchWidget::Scene2Changed(const QString &text)
|
||||||
{
|
{
|
||||||
if (loading || !switchData)
|
if (loading || !switchData) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
std::lock_guard<std::mutex> lock(switcher->m);
|
std::lock_guard<std::mutex> lock(switcher->m);
|
||||||
switchData->scene2 = GetWeakSourceByQString(text);
|
switchData->scene2 = GetWeakSourceByQString(text);
|
||||||
}
|
}
|
||||||
|
|
||||||
DefTransitionSwitchWidget::DefTransitionSwitchWidget(DefaultSceneTransition *s)
|
DefTransitionSwitchWidget::DefTransitionSwitchWidget(QWidget *parent,
|
||||||
: SwitchWidget(s, false)
|
DefaultSceneTransition *s)
|
||||||
|
: SwitchWidget(parent, s, false, false, false)
|
||||||
{
|
{
|
||||||
QHBoxLayout *mainLayout = new QHBoxLayout;
|
QHBoxLayout *mainLayout = new QHBoxLayout;
|
||||||
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
|
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
|
||||||
@@ -499,3 +557,37 @@ void DefTransitionSwitchWidget::swapSwitchData(DefTransitionSwitchWidget *s1,
|
|||||||
s1->setSwitchData(s2->getSwitchData());
|
s1->setSwitchData(s2->getSwitchData());
|
||||||
s2->setSwitchData(t);
|
s2->setSwitchData(t);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool DefaultSceneTransition::checkMatch(OBSWeakSource currentScene)
|
||||||
|
{
|
||||||
|
return scene == currentScene;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setTransitionDelayed(OBSWeakSource transition, unsigned int delay)
|
||||||
|
{
|
||||||
|
// A hardcoded delay of 50 ms before switching transition type is
|
||||||
|
// necessary due to OBS_FRONTEND_EVENT_SCENE_CHANGED seemingly firing a
|
||||||
|
// bit too early and thus leading to canceled transitions.
|
||||||
|
//
|
||||||
|
// The same is to be the case for OBS_FRONTEND_EVENT_TRANSITION_STOPPED.
|
||||||
|
//
|
||||||
|
// 50 ms was chosen as a default value as it seems to avoid the problem
|
||||||
|
// mentioned above and becuase that is the minimum value which can be
|
||||||
|
// chosen for the scene switcher's check interval.
|
||||||
|
// Thus it can be made sure that the delayed setting of the transition
|
||||||
|
// does not interfere with any new scene changes triggered by the scene
|
||||||
|
// switcher
|
||||||
|
|
||||||
|
std::this_thread::sleep_for(std::chrono::milliseconds(delay));
|
||||||
|
|
||||||
|
obs_source_t *transitionSource = obs_weak_source_get_source(transition);
|
||||||
|
obs_frontend_set_current_transition(transitionSource);
|
||||||
|
obs_source_release(transitionSource);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DefaultSceneTransition::setTransition()
|
||||||
|
{
|
||||||
|
std::thread t;
|
||||||
|
t = std::thread(setTransitionDelayed, transition, delay);
|
||||||
|
t.detach();
|
||||||
|
}
|
||||||
|
|||||||
698
src/switch-video.cpp
Normal file
698
src/switch-video.cpp
Normal file
@@ -0,0 +1,698 @@
|
|||||||
|
#include <QFileDialog>
|
||||||
|
#include <QBuffer>
|
||||||
|
#include <QToolTip>
|
||||||
|
#include <thread>
|
||||||
|
|
||||||
|
#include "headers/advanced-scene-switcher.hpp"
|
||||||
|
#include "headers/utility.hpp"
|
||||||
|
|
||||||
|
bool VideoSwitch::pause = false;
|
||||||
|
static QMetaObject::Connection addPulse;
|
||||||
|
|
||||||
|
static void ScreenshotTick(void *param, float);
|
||||||
|
|
||||||
|
AdvSSScreenshotObj::AdvSSScreenshotObj(obs_source_t *source)
|
||||||
|
: weakSource(OBSGetWeakRef(source))
|
||||||
|
{
|
||||||
|
obs_add_tick_callback(ScreenshotTick, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
AdvSSScreenshotObj::~AdvSSScreenshotObj()
|
||||||
|
{
|
||||||
|
obs_enter_graphics();
|
||||||
|
gs_stagesurface_destroy(stagesurf);
|
||||||
|
gs_texrender_destroy(texrender);
|
||||||
|
obs_leave_graphics();
|
||||||
|
|
||||||
|
obs_remove_tick_callback(ScreenshotTick, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AdvSSScreenshotObj::Screenshot()
|
||||||
|
{
|
||||||
|
OBSSource source = OBSGetStrongRef(weakSource);
|
||||||
|
|
||||||
|
if (source) {
|
||||||
|
cx = obs_source_get_base_width(source);
|
||||||
|
cy = obs_source_get_base_height(source);
|
||||||
|
} else {
|
||||||
|
obs_video_info ovi;
|
||||||
|
obs_get_video_info(&ovi);
|
||||||
|
cx = ovi.base_width;
|
||||||
|
cy = ovi.base_height;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!cx || !cy) {
|
||||||
|
blog(LOG_WARNING, "Cannot screenshot, invalid target size");
|
||||||
|
obs_remove_tick_callback(ScreenshotTick, this);
|
||||||
|
done = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
texrender = gs_texrender_create(GS_RGBA, GS_ZS_NONE);
|
||||||
|
stagesurf = gs_stagesurface_create(cx, cy, GS_RGBA);
|
||||||
|
|
||||||
|
gs_texrender_reset(texrender);
|
||||||
|
if (gs_texrender_begin(texrender, cx, cy)) {
|
||||||
|
vec4 zero;
|
||||||
|
vec4_zero(&zero);
|
||||||
|
|
||||||
|
gs_clear(GS_CLEAR_COLOR, &zero, 0.0f, 0);
|
||||||
|
gs_ortho(0.0f, (float)cx, 0.0f, (float)cy, -100.0f, 100.0f);
|
||||||
|
|
||||||
|
gs_blend_state_push();
|
||||||
|
gs_blend_function(GS_BLEND_ONE, GS_BLEND_ZERO);
|
||||||
|
|
||||||
|
if (source) {
|
||||||
|
obs_source_inc_showing(source);
|
||||||
|
obs_source_video_render(source);
|
||||||
|
obs_source_dec_showing(source);
|
||||||
|
} else {
|
||||||
|
obs_render_main_texture();
|
||||||
|
}
|
||||||
|
|
||||||
|
gs_blend_state_pop();
|
||||||
|
gs_texrender_end(texrender);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AdvSSScreenshotObj::Download()
|
||||||
|
{
|
||||||
|
gs_stage_texture(stagesurf, gs_texrender_get_texture(texrender));
|
||||||
|
}
|
||||||
|
|
||||||
|
void AdvSSScreenshotObj::Copy()
|
||||||
|
{
|
||||||
|
uint8_t *videoData = nullptr;
|
||||||
|
uint32_t videoLinesize = 0;
|
||||||
|
|
||||||
|
image = QImage(cx, cy, QImage::Format::Format_RGBX8888);
|
||||||
|
|
||||||
|
if (gs_stagesurface_map(stagesurf, &videoData, &videoLinesize)) {
|
||||||
|
int linesize = image.bytesPerLine();
|
||||||
|
for (int y = 0; y < (int)cy; y++)
|
||||||
|
memcpy(image.scanLine(y),
|
||||||
|
videoData + (y * videoLinesize), linesize);
|
||||||
|
|
||||||
|
gs_stagesurface_unmap(stagesurf);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AdvSSScreenshotObj::MarkDone()
|
||||||
|
{
|
||||||
|
time = std::chrono::high_resolution_clock::now();
|
||||||
|
done = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define STAGE_SCREENSHOT 0
|
||||||
|
#define STAGE_DOWNLOAD 1
|
||||||
|
#define STAGE_COPY_AND_SAVE 2
|
||||||
|
#define STAGE_FINISH 3
|
||||||
|
|
||||||
|
static void ScreenshotTick(void *param, float)
|
||||||
|
{
|
||||||
|
AdvSSScreenshotObj *data =
|
||||||
|
reinterpret_cast<AdvSSScreenshotObj *>(param);
|
||||||
|
|
||||||
|
if (data->stage == STAGE_FINISH) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
obs_enter_graphics();
|
||||||
|
|
||||||
|
switch (data->stage) {
|
||||||
|
case STAGE_SCREENSHOT:
|
||||||
|
data->Screenshot();
|
||||||
|
break;
|
||||||
|
case STAGE_DOWNLOAD:
|
||||||
|
data->Download();
|
||||||
|
break;
|
||||||
|
case STAGE_COPY_AND_SAVE:
|
||||||
|
data->Copy();
|
||||||
|
data->MarkDone();
|
||||||
|
|
||||||
|
obs_remove_tick_callback(ScreenshotTick, data);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
obs_leave_graphics();
|
||||||
|
|
||||||
|
data->stage++;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AdvSceneSwitcher::on_videoAdd_clicked()
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(switcher->m);
|
||||||
|
switcher->videoSwitches.emplace_back();
|
||||||
|
|
||||||
|
VideoSwitchWidget *sw =
|
||||||
|
new VideoSwitchWidget(this, &switcher->videoSwitches.back());
|
||||||
|
|
||||||
|
listAddClicked(ui->videoSwitches, sw, ui->videoAdd, &addPulse);
|
||||||
|
|
||||||
|
ui->videoHelp->setVisible(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AdvSceneSwitcher::on_videoRemove_clicked()
|
||||||
|
{
|
||||||
|
QListWidgetItem *item = ui->videoSwitches->currentItem();
|
||||||
|
if (!item) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(switcher->m);
|
||||||
|
int idx = ui->videoSwitches->currentRow();
|
||||||
|
auto &switches = switcher->videoSwitches;
|
||||||
|
switches.erase(switches.begin() + idx);
|
||||||
|
}
|
||||||
|
|
||||||
|
delete item;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AdvSceneSwitcher::on_videoUp_clicked()
|
||||||
|
{
|
||||||
|
int index = ui->videoSwitches->currentRow();
|
||||||
|
if (!listMoveUp(ui->videoSwitches)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
VideoSwitchWidget *s1 =
|
||||||
|
(VideoSwitchWidget *)ui->videoSwitches->itemWidget(
|
||||||
|
ui->videoSwitches->item(index));
|
||||||
|
VideoSwitchWidget *s2 =
|
||||||
|
(VideoSwitchWidget *)ui->videoSwitches->itemWidget(
|
||||||
|
ui->videoSwitches->item(index - 1));
|
||||||
|
VideoSwitchWidget::swapSwitchData(s1, s2);
|
||||||
|
|
||||||
|
std::lock_guard<std::mutex> lock(switcher->m);
|
||||||
|
|
||||||
|
std::swap(switcher->videoSwitches[index],
|
||||||
|
switcher->videoSwitches[index - 1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AdvSceneSwitcher::on_videoDown_clicked()
|
||||||
|
{
|
||||||
|
int index = ui->videoSwitches->currentRow();
|
||||||
|
|
||||||
|
if (!listMoveDown(ui->videoSwitches)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
VideoSwitchWidget *s1 =
|
||||||
|
(VideoSwitchWidget *)ui->videoSwitches->itemWidget(
|
||||||
|
ui->videoSwitches->item(index));
|
||||||
|
VideoSwitchWidget *s2 =
|
||||||
|
(VideoSwitchWidget *)ui->videoSwitches->itemWidget(
|
||||||
|
ui->videoSwitches->item(index + 1));
|
||||||
|
VideoSwitchWidget::swapSwitchData(s1, s2);
|
||||||
|
|
||||||
|
std::lock_guard<std::mutex> lock(switcher->m);
|
||||||
|
|
||||||
|
std::swap(switcher->videoSwitches[index],
|
||||||
|
switcher->videoSwitches[index + 1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AdvSceneSwitcher::on_getScreenshot_clicked()
|
||||||
|
{
|
||||||
|
QListWidgetItem *item = ui->videoSwitches->currentItem();
|
||||||
|
|
||||||
|
if (!item) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
VideoSwitchWidget *sw =
|
||||||
|
(VideoSwitchWidget *)ui->videoSwitches->itemWidget(item);
|
||||||
|
auto s = sw->getSwitchData();
|
||||||
|
if (!s || !s->videoSource) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto source = obs_weak_source_get_source(s->videoSource);
|
||||||
|
auto screenshotData = std::make_unique<AdvSSScreenshotObj>(source);
|
||||||
|
obs_source_release(source);
|
||||||
|
|
||||||
|
QString filePath = QFileDialog::getSaveFileName(this);
|
||||||
|
if (filePath.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QFile file(filePath);
|
||||||
|
if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// During selection of the save path enough time should usually have
|
||||||
|
// passed already
|
||||||
|
// Add this just in case ...
|
||||||
|
if (!screenshotData->done) {
|
||||||
|
std::this_thread::sleep_for(std::chrono::seconds(1));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!screenshotData->done) {
|
||||||
|
DisplayMessage("Failed to get screenshot of source!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
screenshotData->image.save(file.fileName());
|
||||||
|
sw->SetFilePath(file.fileName());
|
||||||
|
}
|
||||||
|
|
||||||
|
void SwitcherData::checkVideoSwitch(bool &match, OBSWeakSource &scene,
|
||||||
|
OBSWeakSource &transition)
|
||||||
|
{
|
||||||
|
if (VideoSwitch::pause) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto &s : videoSwitches) {
|
||||||
|
bool matched = s.checkMatch();
|
||||||
|
if (!match && matched) {
|
||||||
|
match = true;
|
||||||
|
scene = s.getScene();
|
||||||
|
transition = s.transition;
|
||||||
|
if (verbose) {
|
||||||
|
s.logMatch();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SwitcherData::saveVideoSwitches(obs_data_t *obj)
|
||||||
|
{
|
||||||
|
obs_data_array_t *videoArray = obs_data_array_create();
|
||||||
|
for (VideoSwitch &s : videoSwitches) {
|
||||||
|
obs_data_t *array_obj = obs_data_create();
|
||||||
|
|
||||||
|
s.save(array_obj);
|
||||||
|
obs_data_array_push_back(videoArray, array_obj);
|
||||||
|
|
||||||
|
obs_data_release(array_obj);
|
||||||
|
}
|
||||||
|
obs_data_set_array(obj, "videoSwitches", videoArray);
|
||||||
|
obs_data_array_release(videoArray);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SwitcherData::loadVideoSwitches(obs_data_t *obj)
|
||||||
|
{
|
||||||
|
switcher->videoSwitches.clear();
|
||||||
|
|
||||||
|
obs_data_array_t *videoArray = obs_data_get_array(obj, "videoSwitches");
|
||||||
|
size_t count = obs_data_array_count(videoArray);
|
||||||
|
|
||||||
|
for (size_t i = 0; i < count; i++) {
|
||||||
|
obs_data_t *array_obj = obs_data_array_item(videoArray, i);
|
||||||
|
|
||||||
|
switcher->videoSwitches.emplace_back();
|
||||||
|
videoSwitches.back().load(array_obj);
|
||||||
|
|
||||||
|
obs_data_release(array_obj);
|
||||||
|
}
|
||||||
|
obs_data_array_release(videoArray);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AdvSceneSwitcher::setupVideoTab()
|
||||||
|
{
|
||||||
|
for (auto &s : switcher->videoSwitches) {
|
||||||
|
QListWidgetItem *item;
|
||||||
|
item = new QListWidgetItem(ui->videoSwitches);
|
||||||
|
ui->videoSwitches->addItem(item);
|
||||||
|
VideoSwitchWidget *sw = new VideoSwitchWidget(this, &s);
|
||||||
|
item->setSizeHint(sw->minimumSizeHint());
|
||||||
|
ui->videoSwitches->setItemWidget(item, sw);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (switcher->videoSwitches.size() == 0) {
|
||||||
|
addPulse = PulseWidget(ui->videoAdd, QColor(Qt::green));
|
||||||
|
ui->videoHelp->setVisible(true);
|
||||||
|
} else {
|
||||||
|
ui->videoHelp->setVisible(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
ui->getScreenshot->setToolTip(
|
||||||
|
obs_module_text("AdvSceneSwitcher.videoTab.getScreenshotHelp"));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool VideoSwitch::initialized()
|
||||||
|
{
|
||||||
|
return SceneSwitcherEntry::initialized() && videoSource;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool VideoSwitch::valid()
|
||||||
|
{
|
||||||
|
return !initialized() ||
|
||||||
|
(SceneSwitcherEntry::valid() && WeakSourceValid(videoSource));
|
||||||
|
}
|
||||||
|
|
||||||
|
void VideoSwitch::save(obs_data_t *obj)
|
||||||
|
{
|
||||||
|
SceneSwitcherEntry::save(obj);
|
||||||
|
|
||||||
|
obs_data_set_string(obj, "videoSource",
|
||||||
|
GetWeakSourceName(videoSource).c_str());
|
||||||
|
obs_data_set_int(obj, "condition", static_cast<int>(condition));
|
||||||
|
obs_data_set_double(obj, "duration", duration);
|
||||||
|
obs_data_set_string(obj, "filePath", file.c_str());
|
||||||
|
obs_data_set_bool(obj, "ignoreInactiveSource", ignoreInactiveSource);
|
||||||
|
}
|
||||||
|
|
||||||
|
void VideoSwitch::load(obs_data_t *obj)
|
||||||
|
{
|
||||||
|
SceneSwitcherEntry::load(obj);
|
||||||
|
|
||||||
|
const char *videoSourceName = obs_data_get_string(obj, "videoSource");
|
||||||
|
videoSource = GetWeakSourceByName(videoSourceName);
|
||||||
|
condition = static_cast<videoSwitchType>(
|
||||||
|
obs_data_get_int(obj, "condition"));
|
||||||
|
duration = obs_data_get_double(obj, "duration");
|
||||||
|
file = obs_data_get_string(obj, "filePath");
|
||||||
|
ignoreInactiveSource = obs_data_get_bool(obj, "ignoreInactiveSource");
|
||||||
|
|
||||||
|
if (condition != videoSwitchType::HAS_NOT_CHANGED) {
|
||||||
|
(void)loadImageFromFile();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void VideoSwitch::getScreenshot()
|
||||||
|
{
|
||||||
|
auto source = obs_weak_source_get_source(videoSource);
|
||||||
|
screenshotData = std::make_unique<AdvSSScreenshotObj>(source);
|
||||||
|
obs_source_release(source);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool VideoSwitch::loadImageFromFile()
|
||||||
|
{
|
||||||
|
if (!matchImage.load(QString::fromStdString(file))) {
|
||||||
|
blog(LOG_WARNING, "Cannot load image data from file '%s'",
|
||||||
|
file.c_str());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
matchImage =
|
||||||
|
matchImage.convertToFormat(QImage::Format::Format_RGBX8888);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool VideoSwitch::checkMatch()
|
||||||
|
{
|
||||||
|
if (ignoreInactiveSource) {
|
||||||
|
obs_source_t *vs = obs_weak_source_get_source(videoSource);
|
||||||
|
bool videoActive = obs_source_active(vs);
|
||||||
|
obs_source_release(vs);
|
||||||
|
|
||||||
|
if (!videoActive) {
|
||||||
|
screenshotData.reset(nullptr);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool match = false;
|
||||||
|
|
||||||
|
if (screenshotData) {
|
||||||
|
if (screenshotData->done) {
|
||||||
|
bool conditionMatch = false;
|
||||||
|
|
||||||
|
switch (condition) {
|
||||||
|
case videoSwitchType::MATCH:
|
||||||
|
conditionMatch = screenshotData->image ==
|
||||||
|
matchImage;
|
||||||
|
break;
|
||||||
|
case videoSwitchType::DIFFER:
|
||||||
|
conditionMatch = screenshotData->image !=
|
||||||
|
matchImage;
|
||||||
|
break;
|
||||||
|
case videoSwitchType::HAS_NOT_CHANGED:
|
||||||
|
conditionMatch = screenshotData->image ==
|
||||||
|
matchImage;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (conditionMatch) {
|
||||||
|
currentMatchDuration +=
|
||||||
|
std::chrono::duration_cast<
|
||||||
|
std::chrono::milliseconds>(
|
||||||
|
screenshotData->time -
|
||||||
|
previousTime);
|
||||||
|
} else {
|
||||||
|
currentMatchDuration = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
bool durationMatch = currentMatchDuration.count() >=
|
||||||
|
duration * 1000;
|
||||||
|
|
||||||
|
if (conditionMatch && durationMatch) {
|
||||||
|
match = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (condition == videoSwitchType::HAS_NOT_CHANGED) {
|
||||||
|
matchImage = std::move(screenshotData->image);
|
||||||
|
}
|
||||||
|
previousTime = std::move(screenshotData->time);
|
||||||
|
|
||||||
|
screenshotData.reset(nullptr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getScreenshot();
|
||||||
|
return match;
|
||||||
|
}
|
||||||
|
|
||||||
|
void swap(VideoSwitch &first, VideoSwitch &second)
|
||||||
|
{
|
||||||
|
std::swap(first.targetType, second.targetType);
|
||||||
|
std::swap(first.group, second.group);
|
||||||
|
std::swap(first.scene, second.scene);
|
||||||
|
std::swap(first.transition, second.transition);
|
||||||
|
std::swap(first.usePreviousScene, second.usePreviousScene);
|
||||||
|
std::swap(first.videoSource, second.videoSource);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void populateConditionSelection(QComboBox *list)
|
||||||
|
{
|
||||||
|
list->addItem(
|
||||||
|
obs_module_text("AdvSceneSwitcher.videoTab.condition.match"));
|
||||||
|
list->setItemData(
|
||||||
|
0,
|
||||||
|
obs_module_text(
|
||||||
|
"AdvSceneSwitcher.videoTab.condition.match.tooltip"),
|
||||||
|
Qt::ToolTipRole);
|
||||||
|
list->addItem(
|
||||||
|
obs_module_text("AdvSceneSwitcher.videoTab.condition.differ"));
|
||||||
|
list->addItem(obs_module_text(
|
||||||
|
"AdvSceneSwitcher.videoTab.condition.hasNotChanged"));
|
||||||
|
}
|
||||||
|
|
||||||
|
VideoSwitchWidget::VideoSwitchWidget(QWidget *parent, VideoSwitch *s)
|
||||||
|
: SwitchWidget(parent, s, true, true)
|
||||||
|
{
|
||||||
|
videoSources = new QComboBox();
|
||||||
|
condition = new QComboBox();
|
||||||
|
duration = new QDoubleSpinBox();
|
||||||
|
filePath = new QLineEdit();
|
||||||
|
browseButton =
|
||||||
|
new QPushButton(obs_module_text("AdvSceneSwitcher.browse"));
|
||||||
|
ignoreInactiveSource = new QCheckBox(obs_module_text(
|
||||||
|
"AdvSceneSwitcher.videoTab.ignoreInactiveSource"));
|
||||||
|
|
||||||
|
filePath->setFixedWidth(100);
|
||||||
|
|
||||||
|
browseButton->setStyleSheet("border:1px solid gray;");
|
||||||
|
|
||||||
|
duration->setMinimum(0.0);
|
||||||
|
duration->setMaximum(99.000000);
|
||||||
|
duration->setSuffix("s");
|
||||||
|
|
||||||
|
QWidget::connect(videoSources,
|
||||||
|
SIGNAL(currentTextChanged(const QString &)), this,
|
||||||
|
SLOT(SourceChanged(const QString &)));
|
||||||
|
QWidget::connect(condition, SIGNAL(currentIndexChanged(int)), this,
|
||||||
|
SLOT(ConditionChanged(int)));
|
||||||
|
QWidget::connect(duration, SIGNAL(valueChanged(double)), this,
|
||||||
|
SLOT(DurationChanged(double)));
|
||||||
|
QWidget::connect(filePath, SIGNAL(editingFinished()), this,
|
||||||
|
SLOT(FilePathChanged()));
|
||||||
|
QWidget::connect(browseButton, SIGNAL(clicked()), this,
|
||||||
|
SLOT(BrowseButtonClicked()));
|
||||||
|
QWidget::connect(ignoreInactiveSource, SIGNAL(stateChanged(int)), this,
|
||||||
|
SLOT(IgnoreInactiveChanged(int)));
|
||||||
|
|
||||||
|
// TODO:
|
||||||
|
// Figure out why scene do not work for "match exactly".
|
||||||
|
// Until then do not allow selecting scenes
|
||||||
|
AdvSceneSwitcher::populateVideoSelection(videoSources, false);
|
||||||
|
populateConditionSelection(condition);
|
||||||
|
|
||||||
|
if (s) {
|
||||||
|
videoSources->setCurrentText(
|
||||||
|
GetWeakSourceName(s->videoSource).c_str());
|
||||||
|
condition->setCurrentIndex(static_cast<int>(s->condition));
|
||||||
|
duration->setValue(s->duration);
|
||||||
|
filePath->setText(QString::fromStdString(s->file));
|
||||||
|
ignoreInactiveSource->setChecked(s->ignoreInactiveSource);
|
||||||
|
|
||||||
|
if (s->condition == videoSwitchType::HAS_NOT_CHANGED) {
|
||||||
|
filePath->hide();
|
||||||
|
browseButton->hide();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QHBoxLayout *switchLayout = new QHBoxLayout;
|
||||||
|
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
|
||||||
|
{"{{videoSources}}", videoSources},
|
||||||
|
{"{{condition}}", condition},
|
||||||
|
{"{{duration}}", duration},
|
||||||
|
{"{{filePath}}", filePath},
|
||||||
|
{"{{browseButton}}", browseButton},
|
||||||
|
{"{{ignoreInactiveSource}}", ignoreInactiveSource},
|
||||||
|
{"{{scenes}}", scenes},
|
||||||
|
{"{{transitions}}", transitions}};
|
||||||
|
placeWidgets(obs_module_text("AdvSceneSwitcher.videoTab.entry"),
|
||||||
|
switchLayout, widgetPlaceholders);
|
||||||
|
|
||||||
|
QVBoxLayout *mainLayout = new QVBoxLayout;
|
||||||
|
|
||||||
|
mainLayout->addLayout(switchLayout);
|
||||||
|
setLayout(mainLayout);
|
||||||
|
|
||||||
|
switchData = s;
|
||||||
|
UpdatePreviewTooltip();
|
||||||
|
|
||||||
|
loading = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
VideoSwitch *VideoSwitchWidget::getSwitchData()
|
||||||
|
{
|
||||||
|
return switchData;
|
||||||
|
}
|
||||||
|
|
||||||
|
void VideoSwitchWidget::setSwitchData(VideoSwitch *s)
|
||||||
|
{
|
||||||
|
switchData = s;
|
||||||
|
}
|
||||||
|
|
||||||
|
void VideoSwitchWidget::swapSwitchData(VideoSwitchWidget *s1,
|
||||||
|
VideoSwitchWidget *s2)
|
||||||
|
{
|
||||||
|
SwitchWidget::swapSwitchData(s1, s2);
|
||||||
|
|
||||||
|
VideoSwitch *t = s1->getSwitchData();
|
||||||
|
s1->setSwitchData(s2->getSwitchData());
|
||||||
|
s2->setSwitchData(t);
|
||||||
|
}
|
||||||
|
|
||||||
|
void VideoSwitchWidget::UpdatePreviewTooltip()
|
||||||
|
{
|
||||||
|
if (!switchData ||
|
||||||
|
switchData->condition == videoSwitchType::HAS_NOT_CHANGED) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QImage preview =
|
||||||
|
switchData->matchImage.scaled({300, 300}, Qt::KeepAspectRatio);
|
||||||
|
|
||||||
|
QByteArray data;
|
||||||
|
QBuffer buffer(&data);
|
||||||
|
if (!preview.save(&buffer, "PNG")) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString html =
|
||||||
|
QString("<html><img src='data:image/png;base64, %0'/></html>")
|
||||||
|
.arg(QString(data.toBase64()));
|
||||||
|
this->setToolTip(html);
|
||||||
|
}
|
||||||
|
|
||||||
|
void VideoSwitchWidget::SetFilePath(const QString &text)
|
||||||
|
{
|
||||||
|
filePath->setText(text);
|
||||||
|
FilePathChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
void VideoSwitchWidget::SourceChanged(const QString &text)
|
||||||
|
{
|
||||||
|
if (loading || !switchData) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::lock_guard<std::mutex> lock(switcher->m);
|
||||||
|
switchData->videoSource = GetWeakSourceByQString(text);
|
||||||
|
}
|
||||||
|
|
||||||
|
void VideoSwitchWidget::ConditionChanged(int cond)
|
||||||
|
{
|
||||||
|
if (loading || !switchData) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::lock_guard<std::mutex> lock(switcher->m);
|
||||||
|
switchData->condition = static_cast<videoSwitchType>(cond);
|
||||||
|
|
||||||
|
if (switchData->condition == videoSwitchType::HAS_NOT_CHANGED) {
|
||||||
|
filePath->hide();
|
||||||
|
browseButton->hide();
|
||||||
|
} else {
|
||||||
|
filePath->show();
|
||||||
|
browseButton->show();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reload image data to avoid incorrect matches.
|
||||||
|
//
|
||||||
|
// Condition type HAS_NOT_CHANGED will use matchImage to store previous
|
||||||
|
// frame of video source, which will differ from the image stored at
|
||||||
|
// specified file location.
|
||||||
|
if (switchData->loadImageFromFile()) {
|
||||||
|
UpdatePreviewTooltip();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void VideoSwitchWidget::DurationChanged(double dur)
|
||||||
|
{
|
||||||
|
if (loading || !switchData) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::lock_guard<std::mutex> lock(switcher->m);
|
||||||
|
switchData->duration = dur;
|
||||||
|
}
|
||||||
|
|
||||||
|
void VideoSwitchWidget::FilePathChanged()
|
||||||
|
{
|
||||||
|
if (loading || !switchData) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::lock_guard<std::mutex> lock(switcher->m);
|
||||||
|
switchData->file = filePath->text().toUtf8().constData();
|
||||||
|
if (switchData->loadImageFromFile()) {
|
||||||
|
UpdatePreviewTooltip();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void VideoSwitchWidget::BrowseButtonClicked()
|
||||||
|
{
|
||||||
|
if (loading || !switchData) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString path = QFileDialog::getOpenFileName(
|
||||||
|
this,
|
||||||
|
tr(obs_module_text("AdvSceneSwitcher.fileTab.selectRead")),
|
||||||
|
QDir::currentPath(),
|
||||||
|
tr(obs_module_text("AdvSceneSwitcher.fileTab.anyFileType")));
|
||||||
|
if (path.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
filePath->setText(path);
|
||||||
|
FilePathChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
void VideoSwitchWidget::IgnoreInactiveChanged(int state)
|
||||||
|
{
|
||||||
|
if (loading || !switchData) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::lock_guard<std::mutex> lock(switcher->m);
|
||||||
|
switchData->ignoreInactiveSource = state;
|
||||||
|
}
|
||||||
@@ -1,3 +1,5 @@
|
|||||||
|
#include <regex>
|
||||||
|
|
||||||
#include "headers/advanced-scene-switcher.hpp"
|
#include "headers/advanced-scene-switcher.hpp"
|
||||||
#include "headers/utility.hpp"
|
#include "headers/utility.hpp"
|
||||||
|
|
||||||
@@ -10,15 +12,19 @@ void AdvSceneSwitcher::on_windowAdd_clicked()
|
|||||||
switcher->windowSwitches.emplace_back();
|
switcher->windowSwitches.emplace_back();
|
||||||
|
|
||||||
listAddClicked(ui->windowSwitches,
|
listAddClicked(ui->windowSwitches,
|
||||||
new WindowSwitchWidget(&switcher->windowSwitches.back()),
|
new WindowSwitchWidget(this,
|
||||||
|
&switcher->windowSwitches.back()),
|
||||||
ui->windowAdd, &addPulse);
|
ui->windowAdd, &addPulse);
|
||||||
|
|
||||||
|
ui->windowHelp->setVisible(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AdvSceneSwitcher::on_windowRemove_clicked()
|
void AdvSceneSwitcher::on_windowRemove_clicked()
|
||||||
{
|
{
|
||||||
QListWidgetItem *item = ui->windowSwitches->currentItem();
|
QListWidgetItem *item = ui->windowSwitches->currentItem();
|
||||||
if (!item)
|
if (!item) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> lock(switcher->m);
|
std::lock_guard<std::mutex> lock(switcher->m);
|
||||||
@@ -33,8 +39,9 @@ void AdvSceneSwitcher::on_windowRemove_clicked()
|
|||||||
void AdvSceneSwitcher::on_windowUp_clicked()
|
void AdvSceneSwitcher::on_windowUp_clicked()
|
||||||
{
|
{
|
||||||
int index = ui->windowSwitches->currentRow();
|
int index = ui->windowSwitches->currentRow();
|
||||||
if (!listMoveUp(ui->windowSwitches))
|
if (!listMoveUp(ui->windowSwitches)) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
WindowSwitchWidget *s1 =
|
WindowSwitchWidget *s1 =
|
||||||
(WindowSwitchWidget *)ui->windowSwitches->itemWidget(
|
(WindowSwitchWidget *)ui->windowSwitches->itemWidget(
|
||||||
@@ -54,8 +61,9 @@ void AdvSceneSwitcher::on_windowDown_clicked()
|
|||||||
{
|
{
|
||||||
int index = ui->windowSwitches->currentRow();
|
int index = ui->windowSwitches->currentRow();
|
||||||
|
|
||||||
if (!listMoveDown(ui->windowSwitches))
|
if (!listMoveDown(ui->windowSwitches)) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
WindowSwitchWidget *s1 =
|
WindowSwitchWidget *s1 =
|
||||||
(WindowSwitchWidget *)ui->windowSwitches->itemWidget(
|
(WindowSwitchWidget *)ui->windowSwitches->itemWidget(
|
||||||
@@ -75,8 +83,9 @@ void AdvSceneSwitcher::on_ignoreWindowsAdd_clicked()
|
|||||||
{
|
{
|
||||||
QString windowName = ui->ignoreWindowsWindows->currentText();
|
QString windowName = ui->ignoreWindowsWindows->currentText();
|
||||||
|
|
||||||
if (windowName.isEmpty())
|
if (windowName.isEmpty()) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
QVariant v = QVariant::fromValue(windowName);
|
QVariant v = QVariant::fromValue(windowName);
|
||||||
|
|
||||||
@@ -92,13 +101,16 @@ void AdvSceneSwitcher::on_ignoreWindowsAdd_clicked()
|
|||||||
switcher->ignoreWindowsSwitches.emplace_back(
|
switcher->ignoreWindowsSwitches.emplace_back(
|
||||||
windowName.toUtf8().constData());
|
windowName.toUtf8().constData());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ui->ignoreWindowHelp->setVisible(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AdvSceneSwitcher::on_ignoreWindowsRemove_clicked()
|
void AdvSceneSwitcher::on_ignoreWindowsRemove_clicked()
|
||||||
{
|
{
|
||||||
QListWidgetItem *item = ui->ignoreWindows->currentItem();
|
QListWidgetItem *item = ui->ignoreWindows->currentItem();
|
||||||
if (!item)
|
if (!item) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
QString windowName = item->data(Qt::UserRole).toString();
|
QString windowName = item->data(Qt::UserRole).toString();
|
||||||
|
|
||||||
@@ -139,10 +151,12 @@ int AdvSceneSwitcher::IgnoreWindowsFindByData(const QString &window)
|
|||||||
|
|
||||||
void AdvSceneSwitcher::on_ignoreWindows_currentRowChanged(int idx)
|
void AdvSceneSwitcher::on_ignoreWindows_currentRowChanged(int idx)
|
||||||
{
|
{
|
||||||
if (loading)
|
if (loading) {
|
||||||
return;
|
return;
|
||||||
if (idx == -1)
|
}
|
||||||
|
if (idx == -1) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
QListWidgetItem *item = ui->ignoreWindows->item(idx);
|
QListWidgetItem *item = ui->ignoreWindows->item(idx);
|
||||||
|
|
||||||
@@ -157,88 +171,100 @@ void AdvSceneSwitcher::on_ignoreWindows_currentRowChanged(int idx)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isRunning(std::string &title)
|
void checkWindowTitleSwitchDirect(WindowSwitch &s,
|
||||||
|
std::string ¤tWindowTitle, bool &match,
|
||||||
|
OBSWeakSource &scene,
|
||||||
|
OBSWeakSource &transition)
|
||||||
{
|
{
|
||||||
QStringList windows;
|
bool focus = (!s.focus || s.window == currentWindowTitle);
|
||||||
|
bool fullscreen = (!s.fullscreen || isFullscreen(s.window));
|
||||||
|
bool max = (!s.maximized || isMaximized(s.window));
|
||||||
|
|
||||||
GetWindowList(windows);
|
if (focus && fullscreen && max) {
|
||||||
// True if switch is running (direct)
|
match = true;
|
||||||
bool equals = windows.contains(QString::fromStdString(title));
|
scene = s.getScene();
|
||||||
// True if switch is running (regex)
|
transition = s.transition;
|
||||||
bool matches = (windows.indexOf(QRegularExpression(
|
}
|
||||||
QString::fromStdString(title))) != -1);
|
|
||||||
|
|
||||||
return (equals || matches);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isFocused(std::string &title)
|
void checkWindowTitleSwitchRegex(WindowSwitch &s,
|
||||||
|
std::string ¤tWindowTitle,
|
||||||
|
std::vector<std::string> windowList,
|
||||||
|
bool &match, OBSWeakSource &scene,
|
||||||
|
OBSWeakSource &transition)
|
||||||
{
|
{
|
||||||
std::string current;
|
for (auto &window : windowList) {
|
||||||
|
try {
|
||||||
|
std::regex expr(s.window);
|
||||||
|
if (!std::regex_match(window, expr)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
} catch (const std::regex_error &) {
|
||||||
|
}
|
||||||
|
|
||||||
GetCurrentWindowTitle(current);
|
bool focus = (!s.focus || window == currentWindowTitle);
|
||||||
// True if switch equals current window
|
bool fullscreen = (!s.fullscreen || isFullscreen(window));
|
||||||
bool equals = (title == current);
|
bool max = (!s.maximized || isMaximized(window));
|
||||||
// True if switch matches current window
|
|
||||||
bool matches = QString::fromStdString(current).contains(
|
|
||||||
QRegularExpression(QString::fromStdString(title)));
|
|
||||||
|
|
||||||
return (equals || matches);
|
if (focus && fullscreen && max) {
|
||||||
|
match = true;
|
||||||
|
scene = s.getScene();
|
||||||
|
transition = s.transition;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void SwitcherData::checkWindowTitleSwitch(bool &match, OBSWeakSource &scene,
|
void SwitcherData::checkWindowTitleSwitch(bool &match, OBSWeakSource &scene,
|
||||||
OBSWeakSource &transition)
|
OBSWeakSource &transition)
|
||||||
{
|
{
|
||||||
if (WindowSwitch::pause)
|
if (WindowSwitch::pause) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
std::string title;
|
std::string currentWindowTitle;
|
||||||
bool ignored = false;
|
GetCurrentWindowTitle(currentWindowTitle);
|
||||||
|
|
||||||
// Check if current window is ignored
|
// Check if current window is ignored
|
||||||
GetCurrentWindowTitle(title);
|
|
||||||
for (auto &window : ignoreWindowsSwitches) {
|
for (auto &window : ignoreWindowsSwitches) {
|
||||||
// True if ignored switch equals current window
|
bool equals = (currentWindowTitle == window);
|
||||||
bool equals = (title == window);
|
|
||||||
// True if ignored switch matches current window
|
|
||||||
bool matches = QString::fromStdString(title).contains(
|
|
||||||
QRegularExpression(QString::fromStdString(window)));
|
|
||||||
|
|
||||||
if (equals || matches) {
|
try {
|
||||||
ignored = true;
|
std::regex expr(window);
|
||||||
title = lastTitle;
|
bool matches =
|
||||||
|
std::regex_match(currentWindowTitle, expr);
|
||||||
|
|
||||||
break;
|
if (equals || matches) {
|
||||||
|
currentWindowTitle = lastTitle;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} catch (const std::regex_error &) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
lastTitle = title;
|
|
||||||
|
|
||||||
// Check for match
|
lastTitle = currentWindowTitle;
|
||||||
|
|
||||||
|
std::vector<std::string> windowList;
|
||||||
|
GetWindowList(windowList);
|
||||||
|
|
||||||
for (WindowSwitch &s : windowSwitches) {
|
for (WindowSwitch &s : windowSwitches) {
|
||||||
if (!s.initialized())
|
if (!s.initialized()) {
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
// True if fullscreen is disabled OR current window is fullscreen
|
if (std::find(windowList.begin(), windowList.end(), s.window) !=
|
||||||
bool fullscreen = (!s.fullscreen || isFullscreen(s.window));
|
windowList.end()) {
|
||||||
// True if maximized is disabled OR current window is maximized
|
checkWindowTitleSwitchDirect(s, currentWindowTitle,
|
||||||
bool max = (!s.maximized || isMaximized(s.window));
|
match, scene, transition);
|
||||||
// True if focus is disabled OR switch is focused
|
} else {
|
||||||
bool focus = (!s.focus || isFocused(s.window));
|
checkWindowTitleSwitchRegex(s, currentWindowTitle,
|
||||||
// True if current window is ignored AND switch equals OR matches last window
|
windowList, match, scene,
|
||||||
bool ignore =
|
transition);
|
||||||
(ignored &&
|
}
|
||||||
(title == s.window ||
|
|
||||||
QString::fromStdString(title).contains(
|
|
||||||
QRegularExpression(
|
|
||||||
QString::fromStdString(s.window)))));
|
|
||||||
|
|
||||||
if (isRunning(s.window) &&
|
if (match) {
|
||||||
(fullscreen && (max && (focus || ignore)))) {
|
if (verbose) {
|
||||||
match = true;
|
|
||||||
scene = s.scene;
|
|
||||||
transition = s.transition;
|
|
||||||
|
|
||||||
if (verbose)
|
|
||||||
s.logMatch();
|
s.logMatch();
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -250,26 +276,9 @@ void SwitcherData::saveWindowTitleSwitches(obs_data_t *obj)
|
|||||||
for (WindowSwitch &s : switcher->windowSwitches) {
|
for (WindowSwitch &s : switcher->windowSwitches) {
|
||||||
obs_data_t *array_obj = obs_data_create();
|
obs_data_t *array_obj = obs_data_create();
|
||||||
|
|
||||||
obs_source_t *source = obs_weak_source_get_source(s.scene);
|
s.save(array_obj);
|
||||||
obs_source_t *transition =
|
obs_data_array_push_back(windowTitleArray, array_obj);
|
||||||
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, "window_title",
|
|
||||||
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_data_release(array_obj);
|
obs_data_release(array_obj);
|
||||||
}
|
}
|
||||||
obs_data_set_array(obj, "switches", windowTitleArray);
|
obs_data_set_array(obj, "switches", windowTitleArray);
|
||||||
@@ -298,26 +307,8 @@ void SwitcherData::loadWindowTitleSwitches(obs_data_t *obj)
|
|||||||
obs_data_t *array_obj =
|
obs_data_t *array_obj =
|
||||||
obs_data_array_item(windowTitleArray, i);
|
obs_data_array_item(windowTitleArray, i);
|
||||||
|
|
||||||
const char *scene = obs_data_get_string(array_obj, "scene");
|
switcher->windowSwitches.emplace_back();
|
||||||
const char *transition =
|
windowSwitches.back().load(array_obj);
|
||||||
obs_data_get_string(array_obj, "transition");
|
|
||||||
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,
|
|
||||||
maximized, focus);
|
|
||||||
|
|
||||||
obs_data_release(array_obj);
|
obs_data_release(array_obj);
|
||||||
}
|
}
|
||||||
@@ -349,13 +340,17 @@ void AdvSceneSwitcher::setupTitleTab()
|
|||||||
QListWidgetItem *item;
|
QListWidgetItem *item;
|
||||||
item = new QListWidgetItem(ui->windowSwitches);
|
item = new QListWidgetItem(ui->windowSwitches);
|
||||||
ui->windowSwitches->addItem(item);
|
ui->windowSwitches->addItem(item);
|
||||||
WindowSwitchWidget *sw = new WindowSwitchWidget(&s);
|
WindowSwitchWidget *sw = new WindowSwitchWidget(this, &s);
|
||||||
item->setSizeHint(sw->minimumSizeHint());
|
item->setSizeHint(sw->minimumSizeHint());
|
||||||
ui->windowSwitches->setItemWidget(item, sw);
|
ui->windowSwitches->setItemWidget(item, sw);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (switcher->windowSwitches.size() == 0)
|
if (switcher->windowSwitches.size() == 0) {
|
||||||
addPulse = PulseWidget(ui->windowAdd, QColor(Qt::green));
|
addPulse = PulseWidget(ui->windowAdd, QColor(Qt::green));
|
||||||
|
ui->windowHelp->setVisible(true);
|
||||||
|
} else {
|
||||||
|
ui->windowHelp->setVisible(false);
|
||||||
|
}
|
||||||
|
|
||||||
populateWindowSelection(ui->ignoreWindowsWindows);
|
populateWindowSelection(ui->ignoreWindowsWindows);
|
||||||
|
|
||||||
@@ -366,9 +361,81 @@ void AdvSceneSwitcher::setupTitleTab()
|
|||||||
new QListWidgetItem(text, ui->ignoreWindows);
|
new QListWidgetItem(text, ui->ignoreWindows);
|
||||||
item->setData(Qt::UserRole, text);
|
item->setData(Qt::UserRole, text);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (switcher->ignoreWindowsSwitches.size() == 0) {
|
||||||
|
ui->ignoreWindowHelp->setVisible(true);
|
||||||
|
} else {
|
||||||
|
ui->ignoreWindowHelp->setVisible(false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
WindowSwitchWidget::WindowSwitchWidget(WindowSwitch *s) : SwitchWidget(s, false)
|
void WindowSwitch::save(obs_data_t *obj)
|
||||||
|
{
|
||||||
|
SceneSwitcherEntry::save(obj);
|
||||||
|
|
||||||
|
obs_data_set_string(obj, "windowTitle", window.c_str());
|
||||||
|
obs_data_set_bool(obj, "fullscreen", fullscreen);
|
||||||
|
obs_data_set_bool(obj, "maximized", maximized);
|
||||||
|
obs_data_set_bool(obj, "focus", focus);
|
||||||
|
}
|
||||||
|
|
||||||
|
// To be removed in future version
|
||||||
|
bool loadOldWindow(obs_data_t *obj, WindowSwitch *s)
|
||||||
|
{
|
||||||
|
if (!s) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *scene = obs_data_get_string(obj, "scene");
|
||||||
|
|
||||||
|
if (strcmp(scene, "") == 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
s->scene = GetWeakSourceByName(scene);
|
||||||
|
|
||||||
|
const char *transition = obs_data_get_string(obj, "transition");
|
||||||
|
s->transition = GetWeakTransitionByName(transition);
|
||||||
|
|
||||||
|
s->window = obs_data_get_string(obj, "window_title");
|
||||||
|
s->fullscreen = obs_data_get_bool(obj, "fullscreen");
|
||||||
|
#if __APPLE__
|
||||||
|
// TODO:
|
||||||
|
// not implemented on MacOS as I cannot test it
|
||||||
|
s->maximized = false;
|
||||||
|
#else
|
||||||
|
s->maximized = obs_data_get_bool(obj, "maximized");
|
||||||
|
#endif
|
||||||
|
s->focus = obs_data_get_bool(obj, "focus") ||
|
||||||
|
!obs_data_has_user_value(obj, "focus");
|
||||||
|
s->usePreviousScene = strcmp(scene, previous_scene_name) == 0;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void WindowSwitch::load(obs_data_t *obj)
|
||||||
|
{
|
||||||
|
if (loadOldWindow(obj, this)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
SceneSwitcherEntry::load(obj);
|
||||||
|
|
||||||
|
window = obs_data_get_string(obj, "windowTitle");
|
||||||
|
fullscreen = obs_data_get_bool(obj, "fullscreen");
|
||||||
|
#if __APPLE__
|
||||||
|
// TODO:
|
||||||
|
// not implemented on MacOS as I cannot test it
|
||||||
|
maximized = false;
|
||||||
|
#else
|
||||||
|
maximized = obs_data_get_bool(obj, "maximized");
|
||||||
|
#endif
|
||||||
|
focus = obs_data_get_bool(obj, "focus") ||
|
||||||
|
!obs_data_has_user_value(obj, "focus");
|
||||||
|
}
|
||||||
|
|
||||||
|
WindowSwitchWidget::WindowSwitchWidget(QWidget *parent, WindowSwitch *s)
|
||||||
|
: SwitchWidget(parent, s, true, true)
|
||||||
{
|
{
|
||||||
windows = new QComboBox();
|
windows = new QComboBox();
|
||||||
fullscreen = new QCheckBox(
|
fullscreen = new QCheckBox(
|
||||||
@@ -444,32 +511,40 @@ void WindowSwitchWidget::swapSwitchData(WindowSwitchWidget *s1,
|
|||||||
|
|
||||||
void WindowSwitchWidget::WindowChanged(const QString &text)
|
void WindowSwitchWidget::WindowChanged(const QString &text)
|
||||||
{
|
{
|
||||||
if (loading || !switchData)
|
if (loading || !switchData) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
std::lock_guard<std::mutex> lock(switcher->m);
|
std::lock_guard<std::mutex> lock(switcher->m);
|
||||||
switchData->window = text.toStdString();
|
switchData->window = text.toStdString();
|
||||||
}
|
}
|
||||||
|
|
||||||
void WindowSwitchWidget::FullscreenChanged(int state)
|
void WindowSwitchWidget::FullscreenChanged(int state)
|
||||||
{
|
{
|
||||||
if (loading || !switchData)
|
if (loading || !switchData) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
std::lock_guard<std::mutex> lock(switcher->m);
|
std::lock_guard<std::mutex> lock(switcher->m);
|
||||||
switchData->fullscreen = state;
|
switchData->fullscreen = state;
|
||||||
}
|
}
|
||||||
|
|
||||||
void WindowSwitchWidget::MaximizedChanged(int state)
|
void WindowSwitchWidget::MaximizedChanged(int state)
|
||||||
{
|
{
|
||||||
if (loading || !switchData)
|
if (loading || !switchData) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
std::lock_guard<std::mutex> lock(switcher->m);
|
std::lock_guard<std::mutex> lock(switcher->m);
|
||||||
switchData->maximized = state;
|
switchData->maximized = state;
|
||||||
}
|
}
|
||||||
|
|
||||||
void WindowSwitchWidget::FocusChanged(int state)
|
void WindowSwitchWidget::FocusChanged(int state)
|
||||||
{
|
{
|
||||||
if (loading || !switchData)
|
if (loading || !switchData) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
std::lock_guard<std::mutex> lock(switcher->m);
|
std::lock_guard<std::mutex> lock(switcher->m);
|
||||||
switchData->focus = state;
|
switchData->focus = state;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,8 +5,9 @@ void SwitcherData::Prune()
|
|||||||
{
|
{
|
||||||
for (size_t i = 0; i < windowSwitches.size(); i++) {
|
for (size_t i = 0; i < windowSwitches.size(); i++) {
|
||||||
WindowSwitch &s = windowSwitches[i];
|
WindowSwitch &s = windowSwitches[i];
|
||||||
if (!s.valid())
|
if (!s.valid()) {
|
||||||
windowSwitches.erase(windowSwitches.begin() + i--);
|
windowSwitches.erase(windowSwitches.begin() + i--);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (nonMatchingScene && !WeakSourceValid(nonMatchingScene)) {
|
if (nonMatchingScene && !WeakSourceValid(nonMatchingScene)) {
|
||||||
@@ -16,65 +17,79 @@ void SwitcherData::Prune()
|
|||||||
|
|
||||||
for (size_t i = 0; i < randomSwitches.size(); i++) {
|
for (size_t i = 0; i < randomSwitches.size(); i++) {
|
||||||
RandomSwitch &s = randomSwitches[i];
|
RandomSwitch &s = randomSwitches[i];
|
||||||
if (!s.valid())
|
if (!s.valid()) {
|
||||||
randomSwitches.erase(randomSwitches.begin() + i--);
|
randomSwitches.erase(randomSwitches.begin() + i--);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (size_t i = 0; i < screenRegionSwitches.size(); i++) {
|
for (size_t i = 0; i < screenRegionSwitches.size(); i++) {
|
||||||
ScreenRegionSwitch &s = screenRegionSwitches[i];
|
ScreenRegionSwitch &s = screenRegionSwitches[i];
|
||||||
if (!s.valid())
|
if (!s.valid()) {
|
||||||
screenRegionSwitches.erase(
|
screenRegionSwitches.erase(
|
||||||
screenRegionSwitches.begin() + i--);
|
screenRegionSwitches.begin() + i--);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (size_t i = 0; i < pauseEntries.size(); i++) {
|
for (size_t i = 0; i < pauseEntries.size(); i++) {
|
||||||
PauseEntry &s = pauseEntries[i];
|
PauseEntry &s = pauseEntries[i];
|
||||||
if (!s.valid())
|
if (!s.valid()) {
|
||||||
pauseEntries.erase(pauseEntries.begin() + i--);
|
pauseEntries.erase(pauseEntries.begin() + i--);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (size_t i = 0; i < sceneSequenceSwitches.size(); i++) {
|
for (size_t i = 0; i < sceneSequenceSwitches.size(); i++) {
|
||||||
SceneSequenceSwitch &s = sceneSequenceSwitches[i];
|
SceneSequenceSwitch &s = sceneSequenceSwitches[i];
|
||||||
if (!s.valid())
|
if (!s.valid()) {
|
||||||
sceneSequenceSwitches.erase(
|
sceneSequenceSwitches.erase(
|
||||||
sceneSequenceSwitches.begin() + i--);
|
sceneSequenceSwitches.begin() + i--);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!WeakSourceValid(autoStopScene)) {
|
auto cur = &s;
|
||||||
autoStopScene = nullptr;
|
while (cur != nullptr) {
|
||||||
autoStopEnable = false;
|
if (cur->extendedSequence &&
|
||||||
|
!cur->extendedSequence->valid()) {
|
||||||
|
cur->extendedSequence.reset(nullptr);
|
||||||
|
s.activeSequence = nullptr;
|
||||||
|
}
|
||||||
|
cur = cur->extendedSequence.get();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (size_t i = 0; i < sceneTransitions.size(); i++) {
|
for (size_t i = 0; i < sceneTransitions.size(); i++) {
|
||||||
SceneTransition &s = sceneTransitions[i];
|
SceneTransition &s = sceneTransitions[i];
|
||||||
if (!s.valid())
|
if (!s.valid()) {
|
||||||
sceneTransitions.erase(sceneTransitions.begin() + i--);
|
sceneTransitions.erase(sceneTransitions.begin() + i--);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (size_t i = 0; i < defaultSceneTransitions.size(); i++) {
|
for (size_t i = 0; i < defaultSceneTransitions.size(); i++) {
|
||||||
DefaultSceneTransition &s = defaultSceneTransitions[i];
|
DefaultSceneTransition &s = defaultSceneTransitions[i];
|
||||||
if (!s.valid())
|
if (!s.valid()) {
|
||||||
defaultSceneTransitions.erase(
|
defaultSceneTransitions.erase(
|
||||||
defaultSceneTransitions.begin() + i--);
|
defaultSceneTransitions.begin() + i--);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (size_t i = 0; i < executableSwitches.size(); i++) {
|
for (size_t i = 0; i < executableSwitches.size(); i++) {
|
||||||
ExecutableSwitch &s = executableSwitches[i];
|
ExecutableSwitch &s = executableSwitches[i];
|
||||||
if (!s.valid())
|
if (!s.valid()) {
|
||||||
executableSwitches.erase(executableSwitches.begin() +
|
executableSwitches.erase(executableSwitches.begin() +
|
||||||
i--);
|
i--);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (size_t i = 0; i < fileSwitches.size(); i++) {
|
for (size_t i = 0; i < fileSwitches.size(); i++) {
|
||||||
FileSwitch &s = fileSwitches[i];
|
FileSwitch &s = fileSwitches[i];
|
||||||
if (!s.valid())
|
if (!s.valid()) {
|
||||||
fileSwitches.erase(fileSwitches.begin() + i--);
|
fileSwitches.erase(fileSwitches.begin() + i--);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (size_t i = 0; i < timeSwitches.size(); i++) {
|
for (size_t i = 0; i < timeSwitches.size(); i++) {
|
||||||
TimeSwitch &s = timeSwitches[i];
|
TimeSwitch &s = timeSwitches[i];
|
||||||
if (!s.valid())
|
if (!s.valid()) {
|
||||||
timeSwitches.erase(timeSwitches.begin() + i--);
|
timeSwitches.erase(timeSwitches.begin() + i--);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!idleData.valid()) {
|
if (!idleData.valid()) {
|
||||||
@@ -83,13 +98,33 @@ void SwitcherData::Prune()
|
|||||||
|
|
||||||
for (size_t i = 0; i < mediaSwitches.size(); i++) {
|
for (size_t i = 0; i < mediaSwitches.size(); i++) {
|
||||||
MediaSwitch &s = mediaSwitches[i];
|
MediaSwitch &s = mediaSwitches[i];
|
||||||
if (!s.valid())
|
if (!s.valid()) {
|
||||||
mediaSwitches.erase(mediaSwitches.begin() + i--);
|
mediaSwitches.erase(mediaSwitches.begin() + i--);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (size_t i = 0; i < audioSwitches.size(); i++) {
|
for (size_t i = 0; i < audioSwitches.size(); i++) {
|
||||||
AudioSwitch &s = audioSwitches[i];
|
AudioSwitch &s = audioSwitches[i];
|
||||||
if (!s.valid())
|
if (!s.valid()) {
|
||||||
audioSwitches.erase(audioSwitches.begin() + i--);
|
audioSwitches.erase(audioSwitches.begin() + i--);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto &sg : sceneGroups) {
|
||||||
|
for (size_t i = 0; i < sg.scenes.size(); i++)
|
||||||
|
if (!WeakSourceValid(sg.scenes[i])) {
|
||||||
|
sg.scenes.erase(sg.scenes.begin() + i--);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool SwitcherData::versionChanged(obs_data_t *obj, std::string currentVersion)
|
||||||
|
{
|
||||||
|
std::string previousVersion = obs_data_get_string(obj, "version");
|
||||||
|
return previousVersion != currentVersion;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SwitcherData::saveVersion(obs_data_t *obj, std::string currentVersion)
|
||||||
|
{
|
||||||
|
obs_data_set_string(obj, "version", currentVersion.c_str());
|
||||||
|
}
|
||||||
|
|||||||
@@ -82,7 +82,10 @@ VolControl::VolControl(OBSSource source_, bool vertical)
|
|||||||
nameLabel = new QLabel();
|
nameLabel = new QLabel();
|
||||||
volLabel = new QLabel();
|
volLabel = new QLabel();
|
||||||
|
|
||||||
QString sourceName = obs_source_get_name(source);
|
QString sourceName = "";
|
||||||
|
if (source) {
|
||||||
|
obs_source_get_name(source);
|
||||||
|
}
|
||||||
setObjectName(sourceName);
|
setObjectName(sourceName);
|
||||||
|
|
||||||
QVBoxLayout *mainLayout = new QVBoxLayout;
|
QVBoxLayout *mainLayout = new QVBoxLayout;
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
#define WIN32_LEAN_AND_MEAN
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
#include <util/platform.h>
|
#include <util/platform.h>
|
||||||
#include "../headers/advanced-scene-switcher.hpp"
|
#include "../headers/advanced-scene-switcher.hpp"
|
||||||
@@ -15,8 +16,9 @@ static bool GetWindowTitle(HWND window, std::string &title)
|
|||||||
std::wstring wtitle;
|
std::wstring wtitle;
|
||||||
|
|
||||||
wtitle.resize(len);
|
wtitle.resize(len);
|
||||||
if (!GetWindowTextW(window, &wtitle[0], (int)len + 1))
|
if (!GetWindowTextW(window, &wtitle[0], (int)len + 1)) {
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
len = os_wcs_to_utf8(wtitle.c_str(), 0, nullptr, 0);
|
len = os_wcs_to_utf8(wtitle.c_str(), 0, nullptr, 0);
|
||||||
title.resize(len);
|
title.resize(len);
|
||||||
@@ -29,16 +31,19 @@ static bool WindowValid(HWND window)
|
|||||||
LONG_PTR styles;
|
LONG_PTR styles;
|
||||||
DWORD id;
|
DWORD id;
|
||||||
|
|
||||||
if (!IsWindowVisible(window))
|
if (!IsWindowVisible(window)) {
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
GetWindowThreadProcessId(window, &id);
|
GetWindowThreadProcessId(window, &id);
|
||||||
if (id == GetCurrentProcessId())
|
if (id == GetCurrentProcessId()) {
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
styles = GetWindowLongPtr(window, GWL_STYLE);
|
styles = GetWindowLongPtr(window, GWL_STYLE);
|
||||||
|
|
||||||
if (styles & WS_CHILD)
|
if (styles & WS_CHILD) {
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -51,8 +56,9 @@ BOOL CALLBACK GetTitleCB(HWND hwnd, LPARAM lParam)
|
|||||||
|
|
||||||
std::string title;
|
std::string title;
|
||||||
GetWindowTitle(hwnd, title);
|
GetWindowTitle(hwnd, title);
|
||||||
if (title.empty())
|
if (title.empty()) {
|
||||||
return TRUE;
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
std::vector<std::string> &titles =
|
std::vector<std::string> &titles =
|
||||||
*reinterpret_cast<std::vector<std::string> *>(lParam);
|
*reinterpret_cast<std::vector<std::string> *>(lParam);
|
||||||
@@ -68,8 +74,9 @@ VOID EnumWindowsWithMetro(__in WNDENUMPROC lpEnumFunc, __in LPARAM lParam)
|
|||||||
|
|
||||||
while (i < MAX_SEARCH &&
|
while (i < MAX_SEARCH &&
|
||||||
(childWindow = FindWindowEx(NULL, childWindow, NULL, NULL))) {
|
(childWindow = FindWindowEx(NULL, childWindow, NULL, NULL))) {
|
||||||
if (!lpEnumFunc(childWindow, lParam))
|
if (!lpEnumFunc(childWindow, lParam)) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -140,8 +147,9 @@ bool isMaximized(std::string &title)
|
|||||||
HWND hwnd = NULL;
|
HWND hwnd = NULL;
|
||||||
|
|
||||||
hwnd = getHWNDfromTitle(title);
|
hwnd = getHWNDfromTitle(title);
|
||||||
if (!hwnd)
|
if (!hwnd) {
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
monitorInfo.cbSize = sizeof(MONITORINFO);
|
monitorInfo.cbSize = sizeof(MONITORINFO);
|
||||||
GetMonitorInfo(MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST),
|
GetMonitorInfo(MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST),
|
||||||
@@ -170,8 +178,9 @@ bool isFullscreen(std::string &title)
|
|||||||
HWND hwnd = NULL;
|
HWND hwnd = NULL;
|
||||||
hwnd = getHWNDfromTitle(title);
|
hwnd = getHWNDfromTitle(title);
|
||||||
|
|
||||||
if (!hwnd)
|
if (!hwnd) {
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
monitorInfo.cbSize = sizeof(MONITORINFO);
|
monitorInfo.cbSize = sizeof(MONITORINFO);
|
||||||
GetMonitorInfo(MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST),
|
GetMonitorInfo(MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST),
|
||||||
@@ -197,8 +206,9 @@ void GetProcessList(QStringList &processes)
|
|||||||
PROCESSENTRY32 procEntry;
|
PROCESSENTRY32 procEntry;
|
||||||
|
|
||||||
procSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
|
procSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
|
||||||
if (procSnapshot == INVALID_HANDLE_VALUE)
|
if (procSnapshot == INVALID_HANDLE_VALUE) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
procEntry.dwSize = sizeof(PROCESSENTRY32);
|
procEntry.dwSize = sizeof(PROCESSENTRY32);
|
||||||
|
|
||||||
@@ -209,12 +219,15 @@ void GetProcessList(QStringList &processes)
|
|||||||
|
|
||||||
do {
|
do {
|
||||||
QString tempexe = QString::fromWCharArray(procEntry.szExeFile);
|
QString tempexe = QString::fromWCharArray(procEntry.szExeFile);
|
||||||
if (tempexe == "System")
|
if (tempexe == "System") {
|
||||||
continue;
|
continue;
|
||||||
if (tempexe == "[System Process]")
|
}
|
||||||
|
if (tempexe == "[System Process]") {
|
||||||
continue;
|
continue;
|
||||||
if (processes.contains(tempexe))
|
}
|
||||||
|
if (processes.contains(tempexe)) {
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
processes.append(tempexe);
|
processes.append(tempexe);
|
||||||
} while (Process32Next(procSnapshot, &procEntry));
|
} while (Process32Next(procSnapshot, &procEntry));
|
||||||
|
|
||||||
@@ -231,8 +244,9 @@ bool isInFocus(const QString &executable)
|
|||||||
|
|
||||||
HANDLE process = OpenProcess(
|
HANDLE process = OpenProcess(
|
||||||
PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, processId);
|
PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, processId);
|
||||||
if (process == NULL)
|
if (process == NULL) {
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
WCHAR executablePath[600];
|
WCHAR executablePath[600];
|
||||||
GetModuleFileNameEx(process, 0, executablePath, 600);
|
GetModuleFileNameEx(process, 0, executablePath, 600);
|
||||||
@@ -254,8 +268,9 @@ int getLastInputTime()
|
|||||||
{
|
{
|
||||||
LASTINPUTINFO lastInputInfo;
|
LASTINPUTINFO lastInputInfo;
|
||||||
lastInputInfo.cbSize = sizeof(LASTINPUTINFO);
|
lastInputInfo.cbSize = sizeof(LASTINPUTINFO);
|
||||||
if (GetLastInputInfo(&lastInputInfo))
|
if (GetLastInputInfo(&lastInputInfo)) {
|
||||||
return lastInputInfo.dwTime;
|
return lastInputInfo.dwTime;
|
||||||
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user