mirror of
https://github.com/WarmUpTill/SceneSwitcher.git
synced 2026-08-29 04:36:21 -05:00
Compare commits
153 Commits
1.9
...
virtual-de
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7d381b9ba1 | ||
|
|
d66a2dc408 | ||
|
|
047ca93486 | ||
|
|
bd00a3f97e | ||
|
|
72f67705ed | ||
|
|
38161f63a3 | ||
|
|
b9f6e47a49 | ||
|
|
c82c56dbb7 | ||
|
|
326cc0c46f | ||
|
|
d9997d374d | ||
|
|
c18d799f4a | ||
|
|
fdb72629db | ||
|
|
7fda5e3820 | ||
|
|
78966e456a | ||
|
|
49a042039e | ||
|
|
adb266927f | ||
|
|
703f24b8fc | ||
|
|
de6120c401 | ||
|
|
b1f7a255d5 | ||
|
|
d112e92d33 | ||
|
|
5c4dd6dae1 | ||
|
|
546a83ee07 | ||
|
|
09ab29b4ba | ||
|
|
86d4e59dc2 | ||
|
|
bf03edd391 | ||
|
|
aa7714210a | ||
|
|
2454ea8fa5 | ||
|
|
a5edbc8b88 | ||
|
|
ec6cffcd6a | ||
|
|
89849a0792 | ||
|
|
48e6bcbb7a | ||
|
|
6893d9539e | ||
|
|
4a84bede37 | ||
|
|
77642c54d8 | ||
|
|
069b0f89d0 | ||
|
|
e517fa7b39 | ||
|
|
9301713c49 | ||
|
|
66285a0d3d | ||
|
|
e83a9e8d94 | ||
|
|
881e197cb2 | ||
|
|
e7f031641f | ||
|
|
198a5dc1ec | ||
|
|
5496c256c0 | ||
|
|
a93cec2bb9 | ||
|
|
32101627a6 | ||
|
|
c2d2a27d44 | ||
|
|
5eb8378612 | ||
|
|
961ea655fd | ||
|
|
c1704f2164 | ||
|
|
e4b060976b | ||
|
|
186198bfd4 | ||
|
|
283025f1a8 | ||
|
|
5b4ddfa669 | ||
|
|
572b974b82 | ||
|
|
ae71b24c60 | ||
|
|
e803ba75f9 | ||
|
|
72bd905e7c | ||
|
|
51680adc20 | ||
|
|
4b84850f00 | ||
|
|
6a998934d0 | ||
|
|
b80c16a446 | ||
|
|
f4d11dd6e6 | ||
|
|
4248b86705 | ||
|
|
305ce96ad0 | ||
|
|
74dfd9af30 | ||
|
|
c911862d8c | ||
|
|
2a73583793 | ||
|
|
8f16d05a28 | ||
|
|
2ac1a226e2 | ||
|
|
f56b0b56a7 | ||
|
|
2ce0b35921 | ||
|
|
33d2def126 | ||
|
|
c44b0a2ac2 | ||
|
|
3abeb936b3 | ||
|
|
6c1eb3ffab | ||
|
|
e9d6067db2 | ||
|
|
d691492a31 | ||
|
|
950dfe8688 | ||
|
|
fb38f60609 | ||
|
|
3c283a0818 | ||
|
|
271bbcbe95 | ||
|
|
cef513f775 | ||
|
|
a66961eac1 | ||
|
|
92c8767e1a | ||
|
|
5b12468a46 | ||
|
|
675021c284 | ||
|
|
6bf122d0e8 | ||
|
|
8b8e479098 | ||
|
|
717d41a527 | ||
|
|
ad5587e084 | ||
|
|
f37f384b5a | ||
|
|
dad36689c4 | ||
|
|
0bd7d004ad | ||
|
|
f8c589c7a2 | ||
|
|
e714df7ad0 | ||
|
|
6277da1bbd | ||
|
|
1bef309264 | ||
|
|
2e1d660e17 | ||
|
|
3f492f468a | ||
|
|
c86bba84b3 | ||
|
|
c468b0698b | ||
|
|
2af82fe22a | ||
|
|
5d720219bf | ||
|
|
484e1aa191 | ||
|
|
0fdfe757b4 | ||
|
|
bc5a04651f | ||
|
|
d9dfc1913d | ||
|
|
1310d0b3f3 | ||
|
|
139df8a641 | ||
|
|
acf1ae63aa | ||
|
|
041a9e7f19 | ||
|
|
427ed34a1b | ||
|
|
45fa4dac8f | ||
|
|
e4bf5fa749 | ||
|
|
eb346da6f4 | ||
|
|
866480cde6 | ||
|
|
10e1c7b3a3 | ||
|
|
beb482bd5a | ||
|
|
e561d378dd | ||
|
|
73ce88e2f0 | ||
|
|
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 |
19
.github/workflows/build.yml
vendored
19
.github/workflows/build.yml
vendored
@@ -27,6 +27,7 @@ jobs:
|
||||
uses: actions/checkout@v2.3.4
|
||||
with:
|
||||
path: UI/frontend-plugins/${{ env.PLUGIN_NAME }}
|
||||
submodules: 'recursive'
|
||||
- name: Fetch Git Tags
|
||||
run: |
|
||||
cd UI/frontend-plugins/${{ env.PLUGIN_NAME }}
|
||||
@@ -62,7 +63,7 @@ jobs:
|
||||
echo "add_subdirectory(${{ env.PLUGIN_NAME }})" >> UI/frontend-plugins/CMakeLists.txt
|
||||
mkdir ./build
|
||||
cd ./build
|
||||
cmake -DCMAKE_OSX_DEPLOYMENT_TARGET=10.13 -DDISABLE_PYTHON=ON -DDepsPath="/tmp/obsdeps" -DQTDIR="/tmp/obsdeps" ..
|
||||
cmake -DBUILD_BROWSER=OFF -DCMAKE_OSX_DEPLOYMENT_TARGET=10.13 -DDISABLE_PYTHON=ON -DDepsPath="/tmp/obsdeps" -DQTDIR="/tmp/obsdeps" ..
|
||||
cd -
|
||||
- name: Build
|
||||
shell: bash
|
||||
@@ -75,8 +76,9 @@ jobs:
|
||||
if: success()
|
||||
shell: bash
|
||||
run: |
|
||||
curl -L -O https://s3-us-west-2.amazonaws.com/obs-nightly/Packages.pkg
|
||||
sudo installer -pkg ./Packages.pkg -target /
|
||||
curl -L -O http://s.sudre.free.fr/Software/files/Packages.dmg
|
||||
sudo hdiutil attach ./Packages.dmg
|
||||
sudo installer -pkg /Volumes/Packages\ 1.2.9/Install\ Packages.pkg -target /
|
||||
- name: Package
|
||||
if: success()
|
||||
shell: bash
|
||||
@@ -102,7 +104,7 @@ jobs:
|
||||
path: ./nightly/*.pkg
|
||||
ubuntu64:
|
||||
name: 'Linux/Ubuntu 64-bit'
|
||||
runs-on: [ubuntu-latest]
|
||||
runs-on: [ubuntu-18.04]
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2.3.4
|
||||
@@ -113,6 +115,7 @@ jobs:
|
||||
uses: actions/checkout@v2.3.4
|
||||
with:
|
||||
path: UI/frontend-plugins/${{ env.PLUGIN_NAME }}
|
||||
submodules: 'recursive'
|
||||
- name: Add plugin to obs cmake
|
||||
shell: bash
|
||||
run: echo "add_subdirectory(${{ env.PLUGIN_NAME }})" >> UI/frontend-plugins/CMakeLists.txt
|
||||
@@ -161,6 +164,7 @@ jobs:
|
||||
pkg-config \
|
||||
python3-dev \
|
||||
qtbase5-dev \
|
||||
qtbase5-private-dev \
|
||||
libqt5svg5-dev \
|
||||
swig \
|
||||
libxss-dev
|
||||
@@ -169,7 +173,7 @@ jobs:
|
||||
run: |
|
||||
mkdir ./build
|
||||
cd ./build
|
||||
cmake -DUNIX_STRUCTURE=0 -DCMAKE_INSTALL_PREFIX="${{ github.workspace }}/obs-studio-portable" -DWITH_RTMPS=OFF -DBUILD_BROWSER=OFF ..
|
||||
cmake -DENABLE_PIPEWIRE=OFF -DUNIX_STRUCTURE=0 -DCMAKE_INSTALL_PREFIX="${{ github.workspace }}/obs-studio-portable" -DWITH_RTMPS=OFF -DBUILD_BROWSER=OFF ..
|
||||
- name: 'Build'
|
||||
shell: bash
|
||||
working-directory: ${{ github.workspace }}/build
|
||||
@@ -196,7 +200,7 @@ jobs:
|
||||
QT_VERSION: 5.10.1
|
||||
CMAKE_GENERATOR: "Visual Studio 16 2019"
|
||||
CMAKE_SYSTEM_VERSION: "10.0.18363.657"
|
||||
WINDOWS_DEPS_VERSION: '2017'
|
||||
WINDOWS_DEPS_VERSION: '2019'
|
||||
steps:
|
||||
- name: Add msbuild to PATH
|
||||
uses: microsoft/setup-msbuild@v1.0.2
|
||||
@@ -209,6 +213,7 @@ jobs:
|
||||
uses: actions/checkout@v2.3.4
|
||||
with:
|
||||
path: UI/frontend-plugins/${{ env.PLUGIN_NAME}}
|
||||
submodules: 'recursive'
|
||||
- name: Add plugin to obs cmake
|
||||
shell: cmd
|
||||
run: echo add_subdirectory(${{ env.PLUGIN_NAME }}) >> UI/frontend-plugins/CMakeLists.txt
|
||||
@@ -270,7 +275,7 @@ jobs:
|
||||
release:
|
||||
needs: [macos64, ubuntu64, windows]
|
||||
name: 'Create Release'
|
||||
runs-on: [ubuntu-latest]
|
||||
runs-on: [ubuntu-18.04]
|
||||
steps:
|
||||
- name: "Checkout plugin"
|
||||
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:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
submodules: 'recursive'
|
||||
|
||||
- name: Install clang format
|
||||
run: |
|
||||
# gets us newer clang
|
||||
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
|
||||
sudo apt-get install -y clang-format-10
|
||||
|
||||
- name: Check the Formatting
|
||||
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 nounset
|
||||
|
||||
SCRIPTPATH="$( cd -- "$(dirname "$0")" >/dev/null 2>&1 ; pwd -P )"
|
||||
|
||||
# Runs the Clang Formatter in parallel on the code base.
|
||||
# Return codes:
|
||||
# - 1 there are files to be formatted
|
||||
@@ -19,13 +21,15 @@ if [[ $OS = "Linux" || $OS = "Darwin" ]] ; then
|
||||
fi
|
||||
|
||||
# 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
|
||||
else
|
||||
CLANG_FORMAT=clang-format
|
||||
fi
|
||||
|
||||
find .. -type d \( -path ./deps \
|
||||
-o -path ./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' \
|
||||
find $SCRIPTPATH/.. -type d \( -path $SCRIPTPATH/../deps \
|
||||
-o -path $SCRIPTPATH/../cmake \
|
||||
-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
|
||||
|
||||
@@ -27,6 +27,8 @@ Examples are ...
|
||||
/usr/lib/x86_64-linux-gnu/obs-plugins/
|
||||
/usr/share/obs/obs-plugins/
|
||||
~/.config/obs-studio/plugins/advanced-scene-switcher/bin/64bit/
|
||||
~/.local/share/flatpak/app/com.obsproject.Studio/x86_64/stable/active/files/lib/obs-plugins/
|
||||
/var/lib/flatpak/app/com.obsproject.Studio/x86_64/stable/active/files/lib/obs-plugins/
|
||||
|
||||
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:
|
||||
@@ -34,5 +36,7 @@ The data folder usually should be copied to:
|
||||
|
||||
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
|
||||
~/.local/share/flatpak/app/com.obsproject.Studio/x86_64/stable/active/files/share/obs/obs-plugins/advanced-scene-switcher/locale
|
||||
/var/lib/flatpak/app/com.obsproject.Studio/x86_64/stable/active/files/share/obs/obs-plugins/advanced-scene-switcher/locale
|
||||
|
||||
In doubt please check where other "en-US.ini" files are located on your system.
|
||||
|
||||
102
CMakeLists.txt
102
CMakeLists.txt
@@ -1,12 +1,20 @@
|
||||
cmake_minimum_required(VERSION 3.5)
|
||||
|
||||
project(advanced-scene-switcher)
|
||||
add_definitions(-DASIO_STANDALONE)
|
||||
|
||||
# generate version info
|
||||
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake/")
|
||||
include(GetGitRevisionDescription)
|
||||
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)
|
||||
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)
|
||||
get_filename_component(ISS_PLUGIN_FILES_DIR "${CMAKE_BINARY_DIR}\\..\\package" ABSOLUTE)
|
||||
@@ -45,13 +53,15 @@ if(UNIX AND NOT APPLE)
|
||||
include_directories(${X11_INCLUDE_DIR})
|
||||
endif()
|
||||
|
||||
include_directories(
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/deps/asio/asio/include"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/deps/websocketpp"
|
||||
)
|
||||
|
||||
set(advanced-scene-switcher_HEADERS
|
||||
${advanced-scene-switcher_HEADERS}
|
||||
src/headers/advanced-scene-switcher.hpp
|
||||
src/headers/switcher-data-structs.hpp
|
||||
src/headers/utility.hpp
|
||||
src/headers/curl-helper.hpp
|
||||
src/headers/volume-control.hpp
|
||||
src/headers/scene-group.hpp
|
||||
src/headers/scene-trigger.hpp
|
||||
src/headers/switch-audio.hpp
|
||||
@@ -59,6 +69,7 @@ set(advanced-scene-switcher_HEADERS
|
||||
src/headers/switch-file.hpp
|
||||
src/headers/switch-idle.hpp
|
||||
src/headers/switch-media.hpp
|
||||
src/headers/switch-network.hpp
|
||||
src/headers/switch-pause.hpp
|
||||
src/headers/switch-random.hpp
|
||||
src/headers/switch-screen-region.hpp
|
||||
@@ -66,7 +77,49 @@ set(advanced-scene-switcher_HEADERS
|
||||
src/headers/switch-transitions.hpp
|
||||
src/headers/switch-window.hpp
|
||||
src/headers/switch-sequence.hpp
|
||||
src/headers/switch-video.hpp
|
||||
src/headers/switch-generic.hpp
|
||||
src/headers/macro-action-edit.hpp
|
||||
src/headers/macro-action-audio.hpp
|
||||
src/headers/macro-action-filter.hpp
|
||||
src/headers/macro-action-macro.hpp
|
||||
src/headers/macro-action-media.hpp
|
||||
src/headers/macro-action-plugin-state.hpp
|
||||
src/headers/macro-action-recording.hpp
|
||||
src/headers/macro-action-replay-buffer.hpp
|
||||
src/headers/macro-action-run.hpp
|
||||
src/headers/macro-action-scene-switch.hpp
|
||||
src/headers/macro-action-scene-visibility.hpp
|
||||
src/headers/macro-action-source.hpp
|
||||
src/headers/macro-action-streaming.hpp
|
||||
src/headers/macro-action-wait.hpp
|
||||
src/headers/macro-condition-edit.hpp
|
||||
src/headers/macro-condition-audio.hpp
|
||||
src/headers/macro-condition-counter.hpp
|
||||
src/headers/macro-condition-file.hpp
|
||||
src/headers/macro-condition-idle.hpp
|
||||
src/headers/macro-condition-interval.hpp
|
||||
src/headers/macro-condition-media.hpp
|
||||
src/headers/macro-condition-plugin-state.hpp
|
||||
src/headers/macro-condition-process.hpp
|
||||
src/headers/macro-condition-recording.hpp
|
||||
src/headers/macro-condition-region.hpp
|
||||
src/headers/macro-condition-scene.hpp
|
||||
src/headers/macro-condition-source.hpp
|
||||
src/headers/macro-condition-streaming.hpp
|
||||
src/headers/macro-condition-video.hpp
|
||||
src/headers/macro-condition-virt-desktop.hpp
|
||||
src/headers/macro-condition-window.hpp
|
||||
src/headers/macro.hpp
|
||||
src/headers/macro-selection.hpp
|
||||
src/headers/curl-helper.hpp
|
||||
src/headers/screenshot-helper.hpp
|
||||
src/headers/name-dialog.hpp
|
||||
src/headers/duration-control.hpp
|
||||
src/headers/section.hpp
|
||||
src/headers/platform-funcs.hpp
|
||||
src/headers/utility.hpp
|
||||
src/headers/volume-control.hpp
|
||||
src/headers/version.h
|
||||
)
|
||||
|
||||
@@ -86,14 +139,55 @@ set(advanced-scene-switcher_SOURCES
|
||||
src/switch-file.cpp
|
||||
src/switch-window.cpp
|
||||
src/switch-media.cpp
|
||||
src/switch-network.cpp
|
||||
src/hotkey.cpp
|
||||
src/general.cpp
|
||||
src/switch-pause.cpp
|
||||
src/switch-random.cpp
|
||||
src/switch-time.cpp
|
||||
src/switch-audio.cpp
|
||||
src/switch-video.cpp
|
||||
src/switch-generic.cpp
|
||||
src/macro-action-edit.cpp
|
||||
src/macro-action-audio.cpp
|
||||
src/macro-action-filter.cpp
|
||||
src/macro-action-macro.cpp
|
||||
src/macro-action-media.cpp
|
||||
src/macro-action-plugin-state.cpp
|
||||
src/macro-action-recording.cpp
|
||||
src/macro-action-replay-buffer.cpp
|
||||
src/macro-action-run.cpp
|
||||
src/macro-action-scene-switch.cpp
|
||||
src/macro-action-scene-visibility.cpp
|
||||
src/macro-action-source.cpp
|
||||
src/macro-action-streaming.cpp
|
||||
src/macro-action-wait.cpp
|
||||
src/macro-condition-edit.cpp
|
||||
src/macro-condition-audio.cpp
|
||||
src/macro-condition-counter.cpp
|
||||
src/macro-condition-file.cpp
|
||||
src/macro-condition-idle.cpp
|
||||
src/macro-condition-interval.cpp
|
||||
src/macro-condition-media.cpp
|
||||
src/macro-condition-plugin-state.cpp
|
||||
src/macro-condition-process.cpp
|
||||
src/macro-condition-recording.cpp
|
||||
src/macro-condition-region.cpp
|
||||
src/macro-condition-scene.cpp
|
||||
src/macro-condition-source.cpp
|
||||
src/macro-condition-streaming.cpp
|
||||
src/macro-condition-video.cpp
|
||||
src/macro-condition-virt-desktop.cpp
|
||||
src/macro-condition-window.cpp
|
||||
src/macro.cpp
|
||||
src/macro-selection.cpp
|
||||
src/macro-tab.cpp
|
||||
src/curl-helper.cpp
|
||||
src/screenshot-helper.cpp
|
||||
src/name-dialog.cpp
|
||||
src/duration-control.cpp
|
||||
src/section.cpp
|
||||
src/utility.cpp
|
||||
src/volume-control.cpp
|
||||
src/version.cpp
|
||||
)
|
||||
@@ -104,6 +198,8 @@ set(advanced-scene-switcher_UI
|
||||
)
|
||||
|
||||
if(WIN32)
|
||||
add_definitions(-D_WEBSOCKETPP_CPP11_STL_)
|
||||
|
||||
set(advanced-scene-switcher_PLATFORM_SOURCES
|
||||
src/win/advanced-scene-switcher-win.cpp)
|
||||
elseif(APPLE)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# SceneSwitcher
|
||||
An automated scene switcher for OBS Studio
|
||||
|
||||
More information can be found on https://obsproject.com/forum/resources/automatic-scene-switching.395/
|
||||
More information can be found on https://obsproject.com/forum/resources/automatic-scene-switching.395/ and see https://github.com/WarmUpTill/SceneSwitcher/blob/master/CI/release/README.txt for installing releases.
|
||||
|
||||
## Compiling in tree (recommended)
|
||||
Add the "SceneSwitcher" source directory to your obs-studio source directory under obs-studio/UI/frontend-plugins/:
|
||||
|
||||
@@ -27,6 +27,7 @@ AdvSceneSwitcher.generalTab.generalBehavior.onNoMet.switchTo="Wechsle zu:"
|
||||
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.verboseLogging="Ausführliches Logging"
|
||||
AdvSceneSwitcher.generalTab.generalBehavior.saveWindowGeo="Fensterposition und -größe speichern"
|
||||
AdvSceneSwitcher.generalTab.generalBehavior.disableUIHints="Deaktiviere UI Tipps"
|
||||
AdvSceneSwitcher.generalTab.priority="Priorität"
|
||||
AdvSceneSwitcher.generalTab.priority.description="Szenenwechselmethoden sortiert nach Priorität"
|
||||
@@ -49,16 +50,22 @@ AdvSceneSwitcher.generalTab.priority.windowTitle="Fenstername"
|
||||
AdvSceneSwitcher.generalTab.priority.media="Medien"
|
||||
AdvSceneSwitcher.generalTab.priority.time="Zeit"
|
||||
AdvSceneSwitcher.generalTab.priority.audio="Audio"
|
||||
AdvSceneSwitcher.generalTab.priority.video="Video"
|
||||
|
||||
; Transition Tab
|
||||
AdvSceneSwitcher.transitionTab.title="Szenenübergänge"
|
||||
AdvSceneSwitcher.transitionTab.transitionOverride="Szenenübergänge, die im Szenenwechsler konfiguriert werden, haben Vorrang vor Übergangsüberschreibung"
|
||||
AdvSceneSwitcher.transitionTab.setTransitionBy="Beim Ändern des Szenenübergangs:"
|
||||
AdvSceneSwitcher.transitionTab.transitionOverride="Verwende Übergangsüberschreibungen"
|
||||
AdvSceneSwitcher.transitionTab.adjustActiveTransitionType="Wechsle den aktiven Szenenübergangtyp"
|
||||
AdvSceneSwitcher.transitionTab.transitionBehaviorSelectionError="Mindestens eine Option muss aktiv sein:\n\n - Verwende Übergangsüberschreibungen\n\n - Wechsle den aktiven Szenenübergangtyp"
|
||||
AdvSceneSwitcher.transitionTab.transitionForAToB="Szenenübergänge für automatisierte Szenenwechsel von Szene A zu Szene B"
|
||||
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.entry="Wechsle von {{scenes}} zu {{scenes2}} mit {{transitions}}"
|
||||
AdvSceneSwitcher.transitionTab.entry="Wechsle von {{scenes}} zu {{scenes2}} mit {{transitions}} mit einer Dauer von {{duration}}"
|
||||
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
|
||||
AdvSceneSwitcher.pauseTab.title="Pause"
|
||||
@@ -185,18 +192,59 @@ AdvSceneSwitcher.sceneSequenceTab.loadSuccess="Laden der Einstellungen war erfol
|
||||
AdvSceneSwitcher.sceneSequenceTab.fileType="Text Dateien (*.txt)"
|
||||
AdvSceneSwitcher.sceneSequenceTab.interruptible="kann unterbrochen werden"
|
||||
AdvSceneSwitcher.sceneSequenceTab.interruptibleHint="Andere Szenenwechselmethoden können diese Sequenz unterbrechen"
|
||||
AdvSceneSwitcher.sceneSequenceTab.entry="Wenn {{startScenes}} aktiv ist wechsle zu {{scenes}} nach {{delay}} {{delayUnits}} mit {{transitions}} {{interruptible}}"
|
||||
AdvSceneSwitcher.sceneSequenceTab.extendEdit="Szenenfolge erweitern"
|
||||
AdvSceneSwitcher.sceneSequenceTab.extendEntry="Nach {{delay}} wechsle zu {{scenes}} mit {{transitions}}"
|
||||
AdvSceneSwitcher.sceneSequenceTab.entry="Wenn {{startScenes}} aktiv ist wechsle zu {{scenes}} nach {{delay}} 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
|
||||
AdvSceneSwitcher.audioTab.title="Audio"
|
||||
AdvSceneSwitcher.audioTab.condition.above="über"
|
||||
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}} Sekunden wechsle zu {{scenes}} mit {{transitions}} {{ignoreInactiveSource}}"
|
||||
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}} Sekunden 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.condition.hasNotChanged="sich 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.Disabledwarning="Diese Funktionalität musste unter macOS leider aufgrund von Bibliotheksinkompatibilitäten mit dem obs-websocket plugin deaktiviert werden."
|
||||
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.sendSceneChange="Sende Nachrichten für Szenenwechsel"
|
||||
AdvSceneSwitcher.networkTab.server.restrictSendToAutomatedSwitches="Sende nur Szenenwechselnachrichten für automatisierte Szenenwechsel"
|
||||
AdvSceneSwitcher.networkTab.server.sendPreview="Sende Nachrichten für Vorschau Szenenwechsel im Studio-Modus"
|
||||
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"
|
||||
@@ -232,7 +280,10 @@ AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.stopStreaming="Streamen stop
|
||||
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.startStreaming="Streamen starten"
|
||||
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.startReplayBuffer="Replay Buffer starten"
|
||||
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.stopReplayBuffer="Replay Buffer stoppen"
|
||||
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.muteSource="Quelle stumm schalten"
|
||||
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.muteSource="Audio Quelle stumm schalten"
|
||||
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.unmuteSource="Audio Quelle aktiv schalten"
|
||||
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.startSwitcher="Szenenwechsler starten"
|
||||
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.stopSwitcher="Szenenwechsler stoppen"
|
||||
AdvSceneSwitcher.sceneTriggerTab.entry="Wenn {{scenes}} {{triggers}} {{actions}} {{audioSources}} nach {{duration}} "
|
||||
AdvSceneSwitcher.sceneTriggerTab.help="Dieser Tab ermöglicht es automatisiert Handlungen, wie etwa das Stoppen von einer Aufnahme oder des Streamens, bei einem Szenenwechsel auszuführen."
|
||||
|
||||
@@ -249,9 +300,11 @@ AdvSceneSwitcher.browse="Öffnen"
|
||||
|
||||
AdvSceneSwitcher.selectScene="--Szene auswählen--"
|
||||
AdvSceneSwitcher.selectPreviousScene="Vorhergehende Szene"
|
||||
AdvSceneSwitcher.currentTransition="Aktueller Szenenübergang"
|
||||
AdvSceneSwitcher.selectTransition="--Szenenübergang auswählen--"
|
||||
AdvSceneSwitcher.selectWindow="--Fenster auswählen--"
|
||||
AdvSceneSwitcher.selectAudioSource="--Audio Quelle auswählen--"
|
||||
AdvSceneSwitcher.selectVideoSource="--Video Quelle auswählen--"
|
||||
AdvSceneSwitcher.selectMediaSource="--Medien Quelle auswählen--"
|
||||
AdvSceneSwitcher.selectProcess="--Prozess auswählen--"
|
||||
AdvSceneSwitcher.enterPath="--Pfad eingeben--"
|
||||
|
||||
@@ -27,6 +27,7 @@ AdvSceneSwitcher.generalTab.generalBehavior.onNoMet.switchTo="Switch to:"
|
||||
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.verboseLogging="Enable verbose logging"
|
||||
AdvSceneSwitcher.generalTab.generalBehavior.saveWindowGeo="Save window position and size"
|
||||
AdvSceneSwitcher.generalTab.generalBehavior.disableUIHints="Disable UI hints"
|
||||
AdvSceneSwitcher.generalTab.priority="Priority"
|
||||
AdvSceneSwitcher.generalTab.priority.description="Switching methods priority (Highest priority is at the top)"
|
||||
@@ -49,16 +50,181 @@ AdvSceneSwitcher.generalTab.priority.windowTitle="Window Title"
|
||||
AdvSceneSwitcher.generalTab.priority.media="Media"
|
||||
AdvSceneSwitcher.generalTab.priority.time="Time"
|
||||
AdvSceneSwitcher.generalTab.priority.audio="Audio"
|
||||
AdvSceneSwitcher.generalTab.priority.video="Video"
|
||||
AdvSceneSwitcher.generalTab.priority.macro="Macro"
|
||||
|
||||
; Macro Tab
|
||||
AdvSceneSwitcher.macroTab.title="Macro"
|
||||
AdvSceneSwitcher.macroTab.macros="Macros"
|
||||
AdvSceneSwitcher.macroTab.help="Macros allow you to execute a string of actions depending on multiple conditions.\n\nClick on the highlighted plus symbol to add a new Macro."
|
||||
AdvSceneSwitcher.macroTab.editConditionHelp="This section allows you to define Macro conditions.\n\nSelect an existing or add a new Macro on the left.\nThen click the plus button below to add a new condition."
|
||||
AdvSceneSwitcher.macroTab.editActionHelp="This section allows you to define Macro actions.\n\nSelect an existing or add a new Macro on the left.\nThen click the plus button below to add a new action."
|
||||
AdvSceneSwitcher.macroTab.edit="Edit macro"
|
||||
AdvSceneSwitcher.macroTab.edit.logic="Logic type:"
|
||||
AdvSceneSwitcher.macroTab.edit.condition="Condition type:"
|
||||
AdvSceneSwitcher.macroTab.edit.action="Action type:"
|
||||
AdvSceneSwitcher.macroTab.add="Add new macro"
|
||||
AdvSceneSwitcher.macroTab.name="Name:"
|
||||
AdvSceneSwitcher.macroTab.defaultname="Macro %1"
|
||||
AdvSceneSwitcher.macroTab.exists="Macro name exists already"
|
||||
AdvSceneSwitcher.macroTab.copy="Create copy"
|
||||
|
||||
; Macro Logic
|
||||
AdvSceneSwitcher.logic.none="Ignore entry"
|
||||
AdvSceneSwitcher.logic.and="And"
|
||||
AdvSceneSwitcher.logic.or="Or"
|
||||
AdvSceneSwitcher.logic.andNot="And not"
|
||||
AdvSceneSwitcher.logic.orNot="Or not"
|
||||
AdvSceneSwitcher.logic.rootNone="If"
|
||||
AdvSceneSwitcher.logic.not="If not"
|
||||
|
||||
; Macro Conditions
|
||||
AdvSceneSwitcher.condition.audio="Audio"
|
||||
AdvSceneSwitcher.condition.audio.state.below="Below"
|
||||
AdvSceneSwitcher.condition.audio.state.above="Above"
|
||||
AdvSceneSwitcher.condition.audio.entry="Volume of {{audioSources}} is {{condition}} {{volume}}"
|
||||
AdvSceneSwitcher.condition.region="Screen region"
|
||||
AdvSceneSwitcher.condition.region.entry="Cursor is in {{minX}} {{minY}} x {{maxX}} {{maxY}}"
|
||||
AdvSceneSwitcher.condition.scene="Scene"
|
||||
AdvSceneSwitcher.condition.scene.type.current="Current"
|
||||
AdvSceneSwitcher.condition.scene.type.previous="Previous"
|
||||
AdvSceneSwitcher.condition.scene.entry="{{sceneType}} scene is {{scenes}}"
|
||||
AdvSceneSwitcher.condition.window="Window"
|
||||
AdvSceneSwitcher.condition.window.entry.line1="{{windows}} exist and ..."
|
||||
AdvSceneSwitcher.condition.window.entry.line2="... is {{fullscreen}} fullscreen {{maximized}} maximized {{focused}} focused"
|
||||
AdvSceneSwitcher.condition.file="File"
|
||||
AdvSceneSwitcher.condition.file.entry.line1="Content of {{fileType}} {{filePath}} {{browseButton}} matches:"
|
||||
AdvSceneSwitcher.condition.file.entry.line2="{{matchText}}"
|
||||
AdvSceneSwitcher.condition.file.entry.line3="{{useRegex}} {{checkModificationDate}} {{checkFileContent}}"
|
||||
AdvSceneSwitcher.condition.media="Media"
|
||||
AdvSceneSwitcher.condition.media.entry="{{mediaSources}} state is {{states}} and {{timeRestrictions}} {{time}}"
|
||||
AdvSceneSwitcher.condition.video="Video"
|
||||
AdvSceneSwitcher.condition.video.condition.match="exactly matches"
|
||||
AdvSceneSwitcher.condition.video.condition.differ="does not match"
|
||||
AdvSceneSwitcher.condition.video.condition.hasChanged="has changed"
|
||||
AdvSceneSwitcher.condition.video.condition.hasNotChanged="has not changed"
|
||||
AdvSceneSwitcher.condition.video.condition.noImage="has no output"
|
||||
AdvSceneSwitcher.condition.video.askFileAction="Do you want to use an existing file or create a screenshot of the currently selected source?"
|
||||
AdvSceneSwitcher.condition.video.askFileAction.file="Use existing file"
|
||||
AdvSceneSwitcher.condition.video.askFileAction.screenshot="Create screenshot"
|
||||
AdvSceneSwitcher.condition.video.entry="{{videoSources}} {{condition}} {{filePath}} {{browseButton}}"
|
||||
AdvSceneSwitcher.condition.stream="Streaming"
|
||||
AdvSceneSwitcher.condition.stream.state.start="Stream running"
|
||||
AdvSceneSwitcher.condition.stream.state.stop="Stream stopped"
|
||||
AdvSceneSwitcher.condition.stream.entry="{{streamState}}"
|
||||
AdvSceneSwitcher.condition.record="Recording"
|
||||
AdvSceneSwitcher.condition.record.state.start="Recording running"
|
||||
AdvSceneSwitcher.condition.record.state.pause="Recording paused"
|
||||
AdvSceneSwitcher.condition.record.state.stop="Recording stopped"
|
||||
AdvSceneSwitcher.condition.record.entry="{{recordState}}"
|
||||
AdvSceneSwitcher.condition.process="Process"
|
||||
AdvSceneSwitcher.condition.process.entry="{{processes}} is running {{focused}} and is focused"
|
||||
AdvSceneSwitcher.condition.idle="Idle"
|
||||
AdvSceneSwitcher.condition.idle.entry="No keyboard or mouse inputs for {{duration}}"
|
||||
AdvSceneSwitcher.condition.pluginState="Plugin state"
|
||||
AdvSceneSwitcher.condition.pluginState.state.sceneSwitched="Automated scene change was triggered in this interval"
|
||||
AdvSceneSwitcher.condition.pluginState.entry="{{condition}}"
|
||||
AdvSceneSwitcher.condition.virtDesktop="Virtual desktop"
|
||||
AdvSceneSwitcher.condition.virtDesktop.notAvailable="Not availbale"
|
||||
AdvSceneSwitcher.condition.virtDesktop.entry.line1="Active virtual desktop is {{virtDesktops}}"
|
||||
AdvSceneSwitcher.condition.virtDesktop.entry.line2="Current: {{currentDesktop}}"
|
||||
AdvSceneSwitcher.condition.interval="Interval"
|
||||
AdvSceneSwitcher.condition.interval.entry="{{duration}} have passed"
|
||||
AdvSceneSwitcher.condition.counter="Count"
|
||||
AdvSceneSwitcher.condition.counter.type.below="Less than"
|
||||
AdvSceneSwitcher.condition.counter.type.above="More than"
|
||||
AdvSceneSwitcher.condition.counter.type.equal="Exactly"
|
||||
AdvSceneSwitcher.condition.counter.reset="Reset"
|
||||
AdvSceneSwitcher.condition.counter.entry.line1="{{macros}} was executed {{conditions}} {{count}} times"
|
||||
AdvSceneSwitcher.condition.counter.entry.line2="Current count: {{currentCount}} {{resetCount}}"
|
||||
AdvSceneSwitcher.condition.source="Source"
|
||||
AdvSceneSwitcher.condition.source.type.active="Is active"
|
||||
AdvSceneSwitcher.condition.source.type.showing="Is showing"
|
||||
AdvSceneSwitcher.condition.source.type.settings="Settings match"
|
||||
AdvSceneSwitcher.condition.source.regex="Use regular expressions"
|
||||
AdvSceneSwitcher.condition.source.getSettings="Get current settings"
|
||||
AdvSceneSwitcher.condition.source.entry.line1="{{sources}} {{conditions}}"
|
||||
AdvSceneSwitcher.condition.source.entry.line2="{{settings}}"
|
||||
AdvSceneSwitcher.condition.source.entry.line3="{{regex}} {{getSettings}}"
|
||||
|
||||
; Macro Actions
|
||||
AdvSceneSwitcher.action.switchScene="Switch scene"
|
||||
AdvSceneSwitcher.action.scene.entry="Switch to scene {{scenes}} using {{transitions}} with a duration of {{duration}} seconds"
|
||||
AdvSceneSwitcher.action.wait="Wait"
|
||||
AdvSceneSwitcher.action.wait.type.fixed="fixed"
|
||||
AdvSceneSwitcher.action.wait.type.random="random"
|
||||
AdvSceneSwitcher.action.wait.entry.fixed="Wait for {{waitType}} duration of {{duration}}"
|
||||
AdvSceneSwitcher.action.wait.entry.random="Wait for {{waitType}} duration from {{duration}} to {{duration2}}"
|
||||
AdvSceneSwitcher.action.audio="Audio"
|
||||
AdvSceneSwitcher.action.audio.type.mute="Mute"
|
||||
AdvSceneSwitcher.action.audio.type.unmute="Unmute"
|
||||
AdvSceneSwitcher.action.audio.type.sourceVolume="Set source volume"
|
||||
AdvSceneSwitcher.action.audio.type.masterVolume="Set master volume"
|
||||
AdvSceneSwitcher.action.audio.entry="{{actions}} {{audioSources}} {{volume}}"
|
||||
AdvSceneSwitcher.action.recording="Recording"
|
||||
AdvSceneSwitcher.action.recording.type.stop="Stop recording"
|
||||
AdvSceneSwitcher.action.recording.type.start="Start recording"
|
||||
AdvSceneSwitcher.action.recording.type.pause="Pause recording"
|
||||
AdvSceneSwitcher.action.recording.type.unpause="Unpause recording"
|
||||
AdvSceneSwitcher.action.recording.pause.hint="Note that depending on your recording settings you might not be able to pause recording"
|
||||
AdvSceneSwitcher.action.recording.entry="{{actions}}{{pauseHint}}"
|
||||
AdvSceneSwitcher.action.replay="Replay buffer"
|
||||
AdvSceneSwitcher.action.replay.type.stop="Stop replay buffer"
|
||||
AdvSceneSwitcher.action.replay.type.start="Start replay buffer"
|
||||
AdvSceneSwitcher.action.replay.type.save="Save replay buffer"
|
||||
AdvSceneSwitcher.action.replay.entry="{{actions}}"
|
||||
AdvSceneSwitcher.action.streaming="Streaming"
|
||||
AdvSceneSwitcher.action.streaming.type.stop="Stop streaming"
|
||||
AdvSceneSwitcher.action.streaming.type.start="Start streaming"
|
||||
AdvSceneSwitcher.action.streaming.entry="{{actions}}"
|
||||
AdvSceneSwitcher.action.run="Run"
|
||||
AdvSceneSwitcher.action.run.entry="Run {{filePath}} {{browseButton}}"
|
||||
AdvSceneSwitcher.action.sceneVisibility="Scene item visibility"
|
||||
AdvSceneSwitcher.action.sceneVisibility.type.show="Show"
|
||||
AdvSceneSwitcher.action.sceneVisibility.type.hide="Hide"
|
||||
AdvSceneSwitcher.action.sceneVisibility.entry="On {{scenes}} {{actions}} {{sources}}"
|
||||
AdvSceneSwitcher.action.filter="Filter"
|
||||
AdvSceneSwitcher.action.filter.type.enable="Enable"
|
||||
AdvSceneSwitcher.action.filter.type.disable="Disable"
|
||||
AdvSceneSwitcher.action.filter.entry="On {{sources}} {{actions}} {{filters}}"
|
||||
AdvSceneSwitcher.action.source="Source"
|
||||
AdvSceneSwitcher.action.source.type.enable="Enable"
|
||||
AdvSceneSwitcher.action.source.type.disable="Disable"
|
||||
AdvSceneSwitcher.action.source.type.settings="Set settings"
|
||||
AdvSceneSwitcher.action.source.entry="{{actions}} {{sources}}"
|
||||
AdvSceneSwitcher.action.source.warning="Warning: Enabling and disabling sources globally cannot be controlled by the OBS UI"
|
||||
AdvSceneSwitcher.action.source.getSettings="Get current settings"
|
||||
AdvSceneSwitcher.action.media="Media"
|
||||
AdvSceneSwitcher.action.media.type.play="Play"
|
||||
AdvSceneSwitcher.action.media.type.pause="Pause"
|
||||
AdvSceneSwitcher.action.media.type.stop="Stop"
|
||||
AdvSceneSwitcher.action.media.type.restart="Restart"
|
||||
AdvSceneSwitcher.action.media.type.next="Next"
|
||||
AdvSceneSwitcher.action.media.type.previous="Previous"
|
||||
AdvSceneSwitcher.action.media.entry="{{actions}} {{mediaSources}}"
|
||||
AdvSceneSwitcher.action.macro="Macro"
|
||||
AdvSceneSwitcher.action.macro.type.pause="Pause"
|
||||
AdvSceneSwitcher.action.macro.type.unpause="Unpause"
|
||||
AdvSceneSwitcher.action.macro.type.resetCounter="Reset counter"
|
||||
AdvSceneSwitcher.action.macro.entry="{{actions}} {{macros}}"
|
||||
AdvSceneSwitcher.action.pluginState="Plugin state"
|
||||
AdvSceneSwitcher.action.pluginState.type.stop="Stop the Advanced Scene Switcher plugin"
|
||||
AdvSceneSwitcher.action.pluginState.entry="{{actions}}"
|
||||
|
||||
; Transition Tab
|
||||
AdvSceneSwitcher.transitionTab.title="Transition"
|
||||
AdvSceneSwitcher.transitionTab.transitionOverride="Scene transitions defined in the scene switcher take priority over Transition Overrides defined per scene"
|
||||
AdvSceneSwitcher.transitionTab.setTransitionBy="When changing transitions:"
|
||||
AdvSceneSwitcher.transitionTab.transitionOverride="Set transition overrides"
|
||||
AdvSceneSwitcher.transitionTab.adjustActiveTransitionType="Change active transition type"
|
||||
AdvSceneSwitcher.transitionTab.transitionBehaviorSelectionError="At least one option must be enabled:\n\n - Use transition overrides\n\n - Change active transition type"
|
||||
AdvSceneSwitcher.transitionTab.transitionForAToB="Use transition for automated scene switch from scene A to scene B"
|
||||
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.entry="Switch from {{scenes}} to {{scenes2}} using {{transitions}}"
|
||||
AdvSceneSwitcher.transitionTab.entry="Switch from {{scenes}} to {{scenes2}} using {{transitions}} with a duration of {{duration}}"
|
||||
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
|
||||
AdvSceneSwitcher.pauseTab.title="Pause"
|
||||
@@ -185,18 +351,59 @@ AdvSceneSwitcher.sceneSequenceTab.loadSuccess="Advanced Scene Switcher settings
|
||||
AdvSceneSwitcher.sceneSequenceTab.fileType="Text files (*.txt)"
|
||||
AdvSceneSwitcher.sceneSequenceTab.interruptible="interruptible"
|
||||
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}} using {{transitions}} {{interruptible}}"
|
||||
AdvSceneSwitcher.sceneSequenceTab.extendEdit="Extend Sequence"
|
||||
AdvSceneSwitcher.sceneSequenceTab.extendEntry="After {{delay}} 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
|
||||
AdvSceneSwitcher.audioTab.title="Audio"
|
||||
AdvSceneSwitcher.audioTab.condition.above="above"
|
||||
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}} seconds switch to {{scenes}} using {{transitions}} {{ignoreInactiveSource}}"
|
||||
AdvSceneSwitcher.audioTab.multiMatchfallbackCondition="If multiple entries match ..."
|
||||
AdvSceneSwitcher.audioTab.multiMatchfallback="... for {{duration}} switch to {{scenes}} using {{transitions}}"
|
||||
AdvSceneSwitcher.audioTab.multiMatchfallback="... for {{duration}} seconds 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.condition.hasChanged="has 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.DisabledWarning="This functionality unfortunately had to be disabled on macOS due to library incompatibilities when running the obs-websocket plugin in parallel."
|
||||
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.sendSceneChange="Send messages for scene changes"
|
||||
AdvSceneSwitcher.networkTab.server.restrictSendToAutomatedSwitches="Only send messages for automated scene switches"
|
||||
AdvSceneSwitcher.networkTab.server.sendPreview="Send messages for preview scene change when running in Studio mode"
|
||||
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"
|
||||
@@ -233,6 +440,9 @@ AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.startStreaming="start stream
|
||||
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."
|
||||
|
||||
@@ -240,6 +450,8 @@ AdvSceneSwitcher.sceneTriggerTab.help="This tab allows you to trigger actions on
|
||||
AdvSceneSwitcher.hotkey.startSwitcherHotkey="Start 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.macro.pause="Pause macro %1"
|
||||
AdvSceneSwitcher.hotkey.macro.unpause="Unpause macro %1"
|
||||
|
||||
AdvSceneSwitcher.askBackup="Detected a new version of the Advanced Scene Switcher.\nShould a backup of the old settings be created?"
|
||||
|
||||
@@ -248,11 +460,17 @@ AdvSceneSwitcher.browse="Browse"
|
||||
|
||||
AdvSceneSwitcher.selectScene="--select scene--"
|
||||
AdvSceneSwitcher.selectPreviousScene="Previous Scene"
|
||||
AdvSceneSwitcher.currentTransition="Current Transition"
|
||||
AdvSceneSwitcher.selectTransition="--select transition--"
|
||||
AdvSceneSwitcher.selectWindow="--select window--"
|
||||
AdvSceneSwitcher.selectSource="--select source--"
|
||||
AdvSceneSwitcher.selectAudioSource="--select audio source--"
|
||||
AdvSceneSwitcher.selectVideoSource="--select video source--"
|
||||
AdvSceneSwitcher.selectMediaSource="--select media source--"
|
||||
AdvSceneSwitcher.selectProcess="--select process--"
|
||||
AdvSceneSwitcher.selectFilter="--select filter--"
|
||||
AdvSceneSwitcher.selectMacro="--select macro--"
|
||||
AdvSceneSwitcher.selectItem="--select item--"
|
||||
AdvSceneSwitcher.enterPath="--enter path--"
|
||||
AdvSceneSwitcher.enterText="--enter text--"
|
||||
AdvSceneSwitcher.invaildEntriesWillNotBeSaved="invalid entries will not be saved"
|
||||
@@ -265,3 +483,7 @@ AdvSceneSwitcher.unit.milliseconds="milliseconds"
|
||||
AdvSceneSwitcher.unit.secends="seconds"
|
||||
AdvSceneSwitcher.unit.minutes="minutes"
|
||||
AdvSceneSwitcher.unit.hours="hours"
|
||||
AdvSceneSwitcher.duration.condition.none="No time constraint"
|
||||
AdvSceneSwitcher.duration.condition.more="For at least"
|
||||
AdvSceneSwitcher.duration.condition.equal="For exactly"
|
||||
AdvSceneSwitcher.duration.condition.less="For at most"
|
||||
|
||||
423
data/locale/ru-RU.ini
Normal file
423
data/locale/ru-RU.ini
Normal file
@@ -0,0 +1,423 @@
|
||||
AdvSceneSwitcher.pluginName="Advanced Scene Switcher"
|
||||
AdvSceneSwitcher.windowTitle="Advanced Scene Switcher"
|
||||
|
||||
; General Tab
|
||||
AdvSceneSwitcher.generalTab.title="Общие"
|
||||
AdvSceneSwitcher.generalTab.status="Статус"
|
||||
AdvSceneSwitcher.generalTab.status.hotkeytips="Горячие клавиши можно определить в настройках OBS"
|
||||
AdvSceneSwitcher.generalTab.status.currentStatus="Advanced Scene Switcher сейчас:"
|
||||
AdvSceneSwitcher.generalTab.status.onStartup="При запуске OBS:"
|
||||
AdvSceneSwitcher.generalTab.status.onStartup.asLastRun="Запустить переключатель сцен, если он был запущен"
|
||||
AdvSceneSwitcher.generalTab.status.onStartup.alwaysStart="Всегда запускать переключатель сцен"
|
||||
AdvSceneSwitcher.generalTab.status.onStartup.doNotStart="Не запускать переключатель сцен"
|
||||
AdvSceneSwitcher.generalTab.status.start="Старт"
|
||||
AdvSceneSwitcher.generalTab.status.stop="Стоп"
|
||||
AdvSceneSwitcher.generalTab.status.autoStart="Автоматически запускать переключатель сцен, когда:"
|
||||
AdvSceneSwitcher.generalTab.status.autoStart.never="Никогда"
|
||||
AdvSceneSwitcher.generalTab.status.autoStart.recording="Запись"
|
||||
AdvSceneSwitcher.generalTab.status.autoStart.streaming="Вещание"
|
||||
AdvSceneSwitcher.generalTab.status.autoStart.recordingAndStreaming="Запись или потоковое вещание"
|
||||
AdvSceneSwitcher.generalTab.status.checkInterval="Проверять условия переключения каждый раз"
|
||||
AdvSceneSwitcher.generalTab.generalBehavior="Общее поведение"
|
||||
AdvSceneSwitcher.generalTab.generalBehavior.onNoMet="Если не выполняется условие переключения для"
|
||||
AdvSceneSwitcher.generalTab.generalBehavior.onNoMetDelayTooltip="Будет только настолько точным, насколько настроен интервал проверки."
|
||||
AdvSceneSwitcher.generalTab.generalBehavior.onNoMet.dontSwitch="Не переключаться"
|
||||
AdvSceneSwitcher.generalTab.generalBehavior.onNoMet.switchToRandom="Переключиться на любую сцену на вкладке Random"
|
||||
AdvSceneSwitcher.generalTab.generalBehavior.onNoMet.switchTo="Переключиться на:"
|
||||
AdvSceneSwitcher.generalTab.generalBehavior.cooldown="После совпадения не переключаться между сценами в течение"
|
||||
AdvSceneSwitcher.generalTab.generalBehavior.cooldownHint="В течение этого времени потенциальные совпадения будут игнорироваться!"
|
||||
AdvSceneSwitcher.generalTab.generalBehavior.verboseLogging="Включить ведение подробного журнала"
|
||||
AdvSceneSwitcher.generalTab.generalBehavior.saveWindowGeo="Сохранять положение и размер окна"
|
||||
AdvSceneSwitcher.generalTab.generalBehavior.disableUIHints="Отключить подсказки пользовательского интерфейса"
|
||||
AdvSceneSwitcher.generalTab.priority="Приоритет"
|
||||
AdvSceneSwitcher.generalTab.priority.description="Приоритет методов переключения (самый высокий приоритет - вверху)"
|
||||
AdvSceneSwitcher.generalTab.priority.threadPriority="Использовать приоритет потока"
|
||||
AdvSceneSwitcher.generalTab.priority.threadPriorityNotice="(Поднимать приоритет выше \"Нормальный\" не рекомендуется)"
|
||||
AdvSceneSwitcher.generalTab.saveOrLoadsettings="Сохранить / загрузить настройки"
|
||||
AdvSceneSwitcher.generalTab.saveOrLoadsettings.export="Экспорт"
|
||||
AdvSceneSwitcher.generalTab.saveOrLoadsettings.import="Импорт"
|
||||
AdvSceneSwitcher.generalTab.saveOrLoadsettings.exportWindowTitle="Экспортировать настройки расширенного переключателя сцен в файл ..."
|
||||
AdvSceneSwitcher.generalTab.saveOrLoadsettings.importWindowTitle="Импортировать настройки Advanced Scene Switcher из файла ..."
|
||||
AdvSceneSwitcher.generalTab.saveOrLoadsettings.textType="Текстовые файлы (*.txt)"
|
||||
AdvSceneSwitcher.generalTab.saveOrLoadsettings.loadFail="Advanced Scene Switcher не удалось импортировать настройки"
|
||||
AdvSceneSwitcher.generalTab.saveOrLoadsettings.loadSuccess="Настройки Advanced Scene Switcher импортированы успешно"
|
||||
AdvSceneSwitcher.generalTab.priority.fileContent="Содержание файла"
|
||||
AdvSceneSwitcher.generalTab.priority.sceneSequence="Последовательность сцены"
|
||||
AdvSceneSwitcher.generalTab.priority.idleDetection="Обнаружение простоя"
|
||||
AdvSceneSwitcher.generalTab.priority.executable="Исполняемый файл"
|
||||
AdvSceneSwitcher.generalTab.priority.screenRegion="Область экрана"
|
||||
AdvSceneSwitcher.generalTab.priority.windowTitle="Заголовок окна"
|
||||
AdvSceneSwitcher.generalTab.priority.media="Медиа"
|
||||
AdvSceneSwitcher.generalTab.priority.time="Время"
|
||||
AdvSceneSwitcher.generalTab.priority.audio="Аудио"
|
||||
AdvSceneSwitcher.generalTab.priority.video="Видео"
|
||||
AdvSceneSwitcher.generalTab.priority.macro="Макрос"
|
||||
|
||||
; Macro Tab
|
||||
AdvSceneSwitcher.macroTab.title="Макрос"
|
||||
AdvSceneSwitcher.macroTab.macros="Макросы"
|
||||
AdvSceneSwitcher.macroTab.help="Макросы позволяют выполнять ряд действий в зависимости от нескольких условий.\n\nНажмите на выделенный символ плюса, чтобы добавить новый макрос."
|
||||
AdvSceneSwitcher.macroTab.editConditionHelp="Этот раздел позволяет определить условия макроса.\n\nВыберите существующий или добавьте новый макрос слева."
|
||||
AdvSceneSwitcher.macroTab.editActionHelp="Этот раздел позволяет определить действия макроса.\n\n\nВыберите существующий или добавьте новый макрос слева."
|
||||
AdvSceneSwitcher.macroTab.edit="Редактировать макрос"
|
||||
AdvSceneSwitcher.macroTab.edit.logic="Тип логики:"
|
||||
AdvSceneSwitcher.macroTab.edit.condition="Тип условия:"
|
||||
AdvSceneSwitcher.macroTab.edit.action="Тип действия:"
|
||||
AdvSceneSwitcher.macroTab.add="Добавить новый макрос"
|
||||
AdvSceneSwitcher.macroTab.name="Имя:"
|
||||
AdvSceneSwitcher.macroTab.defaultname="Макрос %1"
|
||||
AdvSceneSwitcher.macroTab.exists="Имя макроса уже существует"
|
||||
AdvSceneSwitcher.macroTab.copy="Создать копию"
|
||||
; Macro Logic
|
||||
AdvSceneSwitcher.logic.none="Игнорировать вход"
|
||||
AdvSceneSwitcher.logic.and="И"
|
||||
AdvSceneSwitcher.logic.or="Или"
|
||||
AdvSceneSwitcher.logic.andNot="И не"
|
||||
AdvSceneSwitcher.logic.orNot="Или нет"
|
||||
AdvSceneSwitcher.logic.rootNone="Если"
|
||||
AdvSceneSwitcher.logic.not="Если нет"
|
||||
; Macro Conditions
|
||||
AdvSceneSwitcher.condition.audio="Аудио"
|
||||
AdvSceneSwitcher.ondition.audio.state.below="Ниже"
|
||||
AdvSceneSwitcher.ondition.audio.state.above="Выше"
|
||||
AdvSceneSwitcher.condition.audio.entry="Громкость {{audioSources}} равна {{condition}} {{volume}} в течении {{duration}} секунд"
|
||||
AdvSceneSwitcher.condition.region="Область экрана"
|
||||
AdvSceneSwitcher.condition.region.entry="Курсор находится в {{minX}} {{minY}} x {{maxX}} {{maxY}}"
|
||||
AdvSceneSwitcher.condition.scene="Сцена"
|
||||
AdvSceneSwitcher.condition.scene.type.current="Текущий"
|
||||
AdvSceneSwitcher.condition.scene.type.previous="Предыдущий"
|
||||
AdvSceneSwitcher.condition.scene.entry="{{sceneType}} сцена является {{scenes}} на {{duration}}"
|
||||
AdvSceneSwitcher.condition.window="Окно"
|
||||
AdvSceneSwitcher.condition.window.entry.line1="{{windows}} существует и ..."
|
||||
AdvSceneSwitcher.condition.window.entry.line2="... это {{fullscreen}} полноэкранный {{maximized}} максимизированный {{focused}} сфокусированный"
|
||||
AdvSceneSwitcher.condition.file="Файл"
|
||||
AdvSceneSwitcher.condition.file.entry.line1="Содержимое {{fileType}} {{filePath}} {{browseButton}} соответствует:"
|
||||
AdvSceneSwitcher.condition.file.entry.line2="{{matchText}}"
|
||||
AdvSceneSwitcher.condition.file.entry.line3="{{useRegex}} {{checkModificationDate}} {{checkFileContent}}"
|
||||
AdvSceneSwitcher.condition.media="Медиа"
|
||||
AdvSceneSwitcher.condition.media.entry="{{mediaSources}} состояние {{states}} и {{timeRestrictions}} {{time}}"
|
||||
AdvSceneSwitcher.condition.video="Видео"
|
||||
AdvSceneSwitcher.condition.video.condition.match="точно соответствует"
|
||||
AdvSceneSwitcher.condition.video.condition.differ="не совпадает"
|
||||
AdvSceneSwitcher.condition.video.condition.hasChanged="изменилось"
|
||||
AdvSceneSwitcher.condition.video.condition.hasNotChanged="не изменилось"
|
||||
AdvSceneSwitcher.condition.video.condition.noImage="не имеет вывода"
|
||||
AdvSceneSwitcher.condition.video.askFileAction="Вы хотите использовать существующий файл или создать скриншот текущего выбранного источника?"
|
||||
AdvSceneSwitcher.condition.video.askFileAction.file="Использовать существующий файл"
|
||||
AdvSceneSwitcher.condition.video.askFileAction.screenshot="Создать скриншот"
|
||||
AdvSceneSwitcher.condition.video.entry="{{videoSources}} {{condition}} {{filePath}} {{browseButton}} для {{duration}}"
|
||||
AdvSceneSwitcher.condition.stream="Потоковое вещание"
|
||||
AdvSceneSwitcher.condition.stream.state.start="Поток запущен"
|
||||
AdvSceneSwitcher.condition.stream.state.stop="Поток остановлен"
|
||||
AdvSceneSwitcher.condition.stream.entry="{{streamState}} для {{duration}}"
|
||||
AdvSceneSwitcher.condition.record="Запись"
|
||||
AdvSceneSwitcher.condition.record.state.start="Запись запущена"
|
||||
AdvSceneSwitcher.condition.record.state.pause="Запись приостановлена"
|
||||
AdvSceneSwitcher.condition.record.state.stop="Запись остановлена"
|
||||
AdvSceneSwitcher.condition.record.entry="{{recordState}} для {{duration}}"
|
||||
AdvSceneSwitcher.condition.process="Процесс"
|
||||
AdvSceneSwitcher.condition.process.entry="{{processes}} запущен {{focused}} и сфокусирован"
|
||||
AdvSceneSwitcher.condition.idle="Простой"
|
||||
AdvSceneSwitcher.condition.idle.entry="Не было ни клавиатуры, ни мыши в течении{{duration}}"
|
||||
AdvSceneSwitcher.condition.pluginState="Состояние плагина"
|
||||
AdvSceneSwitcher.condition.pluginState.state.sceneSwitched="Автоматическая смена сцены была запущена в этом интервале"
|
||||
AdvSceneSwitcher.condition.pluginState.entry="{{condition}}"
|
||||
|
||||
; Macro Actions
|
||||
AdvSceneSwitcher.action.switchScene="Переключить сцену"
|
||||
AdvSceneSwitcher.action.scene.entry="Перейти к сцене {{scenes}} используя {{transitions}} с продолжительностью {{duration}} секунд"
|
||||
AdvSceneSwitcher.action.wait="Подождать"
|
||||
AdvSceneSwitcher.action.wait.type.fixed="фиксированный"
|
||||
AdvSceneSwitcher.action.wait.type.random="случайный"
|
||||
AdvSceneSwitcher.action.wait.entry.fixed="Ожидать {{waitType}} в течении {{duration}}"
|
||||
AdvSceneSwitcher.action.wait.entry.random="Ожидание {{waitType}} длительностью от {{duration}} до {{duration2}}"
|
||||
AdvSceneSwitcher.action.audio="Аудио"
|
||||
AdvSceneSwitcher.action.audio.type.mute="Замьютить"
|
||||
AdvSceneSwitcher.action.audio.type.unmute="Размьютить"
|
||||
AdvSceneSwitcher.action.audio.type.sourceVolume="Установить громкость источника"
|
||||
AdvSceneSwitcher.action.audio.type.masterVolume="Установить главную громкость"
|
||||
AdvSceneSwitcher.action.audio.entry="{{actions}} {{audioSources}} {{volume}}"
|
||||
AdvSceneSwitcher.action.recording="Запись"
|
||||
AdvSceneSwitcher.action.recording.type.stop="Остановить запись"
|
||||
AdvSceneSwitcher.action.recording.type.start="Начать запись"
|
||||
AdvSceneSwitcher.action.recording.type.pause="Пауза записи"
|
||||
AdvSceneSwitcher.action.recording.type.unpause="Снять запись с паузы"
|
||||
AdvSceneSwitcher.action.recording.pause.hint="Обратите внимание, что в зависимости от настроек записи вы можете не иметь возможности приостановить запись"
|
||||
AdvSceneSwitcher.action.recording.entry="{{actions}}{{pauseHint}}"
|
||||
AdvSceneSwitcher.action.replay="Буфер воспроизведения"
|
||||
AdvSceneSwitcher.action.replay.type.stop="Остановить буфер воспроизведения"
|
||||
AdvSceneSwitcher.action.replay.type.start="Начать воспроизведение буфера"
|
||||
AdvSceneSwitcher.action.replay.type.save="Сохранить буфер воспроизведения"
|
||||
AdvSceneSwitcher.action.replay.entry="{{actions}}"
|
||||
AdvSceneSwitcher.action.streaming="Потоковое вещание"
|
||||
AdvSceneSwitcher.action.streaming.type.stop="Остановить потоковое вещание"
|
||||
AdvSceneSwitcher.action.streaming.type.start="Начать потоковое вещание"
|
||||
AdvSceneSwitcher.action.streaming.entry="{{actions}}"
|
||||
AdvSceneSwitcher.action.run="Запустить"
|
||||
AdvSceneSwitcher.action.run.entry="Запустить {{filePath}} {{browseButton}}"
|
||||
|
||||
|
||||
; Transition Tab
|
||||
AdvSceneSwitcher.transitionTab.title="Переход"
|
||||
AdvSceneSwitcher.transitionTab.setTransitionBy="При изменении переходов:"
|
||||
AdvSceneSwitcher.transitionTab.transitionOverride="Установить переопределение переходов"
|
||||
AdvSceneSwitcher.transitionTab.adjustActiveTransitionType="Изменить тип активного перехода"
|
||||
AdvSceneSwitcher.transitionTab.transitionBehaviorSelectionError="Должна быть включена хотя бы одна опция:\n\n - Использовать переопределения переходов\n\n - Изменить тип активного перехода"
|
||||
AdvSceneSwitcher.transitionTab.transitionForAToB="Использовать переход для автоматического переключения сцены со сцены A на сцену B"
|
||||
AdvSceneSwitcher.transitionTab.transitionsHelp="<html><head/><body><p>Эти настройки <span style=\"font-style:italic;\">только</span> влияют на переходы, вызванные переключателем сцен - Проверьте <a href=\"https://obsproject.com/forum/resources/transition-table.1174/\"><span style=\" text-decoration: underline; color:#268bd2;\">Transition Table</span></a> если вы хотите настроить его для ручного изменения сцены.<br/>Настройки, определенные здесь, имеют приоритет над настройками перехода, сконфигурированными в других местах переключателя сцен.<br/><br/>Нажмите на символ плюса ниже, чтобы добавить новую запись.</p></body></html>"
|
||||
AdvSceneSwitcher.transitionTab.defaultTransition="Изменить переход, если сцена активна"
|
||||
AdvSceneSwitcher.transitionTab.entry="Переход от {{scenes}} к {{scenes2}} с помощью {{transitions}} с длительностью {{duration}}"
|
||||
AdvSceneSwitcher.transitionTab.defaultTransitionEntry="Когда сцена {{scenes}} активна, изменить переход сцены по умолчанию на {{transitions}}"
|
||||
AdvSceneSwitcher.transitionTab.defaultTransitionsHelp="Нажмите на символ плюса, чтобы добавить запись."
|
||||
AdvSceneSwitcher.transitionTab.defaultTransition.delay="Переключить переход {{defTransitionDelay}} после смены сцены."
|
||||
AdvSceneSwitcher.transitionTab.defaultTransition.delay.help="Задержка используется для того, чтобы избежать отмены переключения сцены, что может произойти, если тип перехода будет изменен, когда переход еще продолжается."
|
||||
|
||||
; Pause Scenes Tab
|
||||
AdvSceneSwitcher.pauseTab.title="Пауза"
|
||||
AdvSceneSwitcher.pauseTab.pauseOnScene="Приостановить Scene Switcher на сцене"
|
||||
AdvSceneSwitcher.pauseTab.pauseInFocus1="Приостановить Scene Switcher когда "
|
||||
AdvSceneSwitcher.pauseTab.pauseInFocus2="находится в фокусе"
|
||||
AdvSceneSwitcher.pauseTab.pauseTypeScene="сцена активна"
|
||||
AdvSceneSwitcher.pauseTab.pauseTypeWindow="окно в фокусе"
|
||||
AdvSceneSwitcher.pauseTab.pauseTargetAll="все"
|
||||
AdvSceneSwitcher.pauseTab.pauseEntry="Пауза {{pauseTargets}} проверяет, когда {{pauseTypes}} {{scenes}} {{windows}}"
|
||||
AdvSceneSwitcher.pauseTab.help="На этой вкладке вы можете настроить приостановку отдельных методов переключения, если сцена активна или окно находится в фокусе.\n\nНажмите на выделенный символ плюса, чтобы продолжить."
|
||||
|
||||
; Window Title Tab
|
||||
AdvSceneSwitcher.windowTitleTab.title="Название"
|
||||
AdvSceneSwitcher.windowTitleTab.regexrDescription="<html><head/><body><p>Введите либо прямые заголовки окон, либо правильное регулярное выражение. Вы можете проверить синтаксис и соответствие регулярных выражений, используя <a href=\"https://regexr.com\"><span style=\" text-decoration: underline; color:#268bd2;\">RegExr</span></a></p></body></html>"
|
||||
AdvSceneSwitcher.windowTitleTab.stayInFocus1="Игнорировать имя этого окна"
|
||||
AdvSceneSwitcher.windowTitleTab.stayInFocus2=" "
|
||||
AdvSceneSwitcher.windowTitleTab.fullscreen="if fullscreen"
|
||||
AdvSceneSwitcher.windowTitleTab.maximized="if maximized"
|
||||
AdvSceneSwitcher.windowTitleTab.focused="if focused"
|
||||
AdvSceneSwitcher.windowTitleTab.entry="{{windows}} {{scenes}} {{transitions}} {{fullscreen}} {{maximized}} {{focused}}"
|
||||
AdvSceneSwitcher.windowTitleTab.windowsHelp="Переключение сцен на основе заголовков окон запущенных приложений.\nВы можете выбрать следующие дополнительные условия:\nОкно полноэкранное\nОкно максимизированное\nОкно сфокусированное\n\nНажмите на выделенный символ плюса, чтобы продолжить."
|
||||
AdvSceneSwitcher.windowTitleTab.ignoreWindowsHelp="Если заголовок окна игнорируется, переключатель сцен будет действовать так, как будто ранее выбранное окно все еще в фокусе.\nЭто позволит вам избежать переключения сцен, если вы часто переключаетесь на другое окно, которое не должно вызывать смену сцены.\n\nВыберите окно или введите заголовок окна выше и нажмите на символ плюса ниже, чтобы добавить его в список."
|
||||
|
||||
; Executable Tab
|
||||
AdvSceneSwitcher.executableTab.title="Исполняемый"
|
||||
AdvSceneSwitcher.executableTab.implemented="Implemented by dasOven"
|
||||
AdvSceneSwitcher.executableTab.requiresFocus="только если сфокусирован"
|
||||
AdvSceneSwitcher.executableTab.entry="Когда {{processes}} запущены, переключить на {{scenes}} используя {{transitions}} {{requiresFocus}}"
|
||||
AdvSceneSwitcher.executableTab.help="Эта вкладка позволит вам автоматически переключаться между сценами, если запущен процесс.\nЭто может быть полезно в ситуациях, когда имя окна может измениться или неизвестно.\n\nНажмите на выделенный символ плюса, чтобы продолжить."
|
||||
|
||||
; Screen Region Tab
|
||||
AdvSceneSwitcher.screenRegionTab.title="Регион"
|
||||
AdvSceneSwitcher.screenRegionTab.currentPosition="Курсор в данный момент находится в:"
|
||||
AdvSceneSwitcher.screenRegionTab.showGuideFrames="Показать направляющие кадры"
|
||||
AdvSceneSwitcher.screenRegionTab.hideGuideFrames="Скрыть направляющие рамки"
|
||||
AdvSceneSwitcher.screenRegionTab.excludeScenes.None="Нет выбора"
|
||||
AdvSceneSwitcher.screenRegionTab.entry="Если курсор находится в {{minX}} {{minY}} x {{maxX}} {{maxY}} переключить на {{scenes}} используя {{transitions}} если не указано {{excludeScenes}}"
|
||||
AdvSceneSwitcher.screenRegionTab.help="Эта вкладка позволит вам автоматически переключать сцены, основываясь на текущем положении курсора мыши.\n\nНажмите на выделенный символ плюса, чтобы продолжить."
|
||||
|
||||
; Media Tab
|
||||
AdvSceneSwitcher.mediaTab.title="Медиа"
|
||||
AdvSceneSwitcher.mediaTab.implemented="Implemented by Exeldro"
|
||||
AdvSceneSwitcher.mediaTab.states.none="Нет"
|
||||
AdvSceneSwitcher.mediaTab.states.playing="Играет"
|
||||
AdvSceneSwitcher.mediaTab.states.opening="Открытие"
|
||||
AdvSceneSwitcher.mediaTab.states.buffering="Буферизация"
|
||||
AdvSceneSwitcher.mediaTab.states.Paused="Приостановлено"
|
||||
AdvSceneSwitcher.mediaTab.states.stopped="Остановлено"
|
||||
AdvSceneSwitcher.mediaTab.states.ended="Закончилось"
|
||||
AdvSceneSwitcher.mediaTab.states.error="Ошибка"
|
||||
AdvSceneSwitcher.mediaTab.states.playedToEnd="Воспроизведено до конца"
|
||||
AdvSceneSwitcher.mediaTab.states.any="Любой"
|
||||
AdvSceneSwitcher.mediaTab.timeRestriction.none="Нет"
|
||||
AdvSceneSwitcher.mediaTab.timeRestriction.shorter="Время короче"
|
||||
AdvSceneSwitcher.mediaTab.timeRestriction.longer="Время больше"
|
||||
AdvSceneSwitcher.mediaTab.timeRestriction.remainShorter="Оставшееся время короче"
|
||||
AdvSceneSwitcher.mediaTab.timeRestriction.remainLonger="Время, оставшееся дольше"
|
||||
AdvSceneSwitcher.mediaTab.entry="Когда состояние {{mediaSources}} равно {{states}} и {{timeRestrictions}} {{time}} переключиться на {{scenes}} используя {{transitions}}"
|
||||
AdvSceneSwitcher.mediaTab.help="Эта вкладка позволит вам переключать сцены на основе состояний медиаисточников.\nНапример, вы можете автоматически переключиться на предыдущую сцену, когда выбранный медиаисточник закончил свое воспроизведение.\n\nНажмите на выделенный символ плюса, чтобы продолжить."
|
||||
|
||||
; File Tab
|
||||
AdvSceneSwitcher.fileTab.title="Файл"
|
||||
AdvSceneSwitcher.fileTab.readWriteSceneFile="Считать / записать сцену из / в файл"
|
||||
AdvSceneSwitcher.fileTab.currentSceneOutputFile="Записать имя текущей сцены в этот файл:"
|
||||
AdvSceneSwitcher.fileTab.switchSceneBaseOnFile="Включить переключение сцен на основе ввода файла"
|
||||
AdvSceneSwitcher.fileTab.switchSceneNameInputFile="Считать имя сцены, на которую нужно переключиться, из этого файла:"
|
||||
AdvSceneSwitcher.fileTab.switchSceneBaseOnFileContent="Переключать сцену на основе содержимого файла"
|
||||
AdvSceneSwitcher.fileTab.remoteFileWarning="Обратите внимание, что при выборе опции remote переключатель сцен будет пытаться получить доступ к удаленному местоположению каждые x мс, как указано на вкладке Общие"
|
||||
AdvSceneSwitcher.fileTab.remoteFileWarning1="Обратите внимание, что переключатель сцены будет пытаться получить доступ к удаленному местоположению каждые "
|
||||
AdvSceneSwitcher.fileTab.remoteFileWarning2="ms"
|
||||
AdvSceneSwitcher.fileTab.libcurlWarning="Не удалось загрузить libcurl! Доступ к удаленным файлам невозможен!"
|
||||
AdvSceneSwitcher.fileTab.selectWrite="Выберите файл для записи в ..."
|
||||
AdvSceneSwitcher.fileTab.selectRead="Выберите файл для чтения из ..."
|
||||
AdvSceneSwitcher.fileTab.textFileType="Текстовые файлы (*.txt)"
|
||||
AdvSceneSwitcher.fileTab.anyFileType="Любые файлы (*.*)"
|
||||
AdvSceneSwitcher.fileTab.remote="удаленный файл"
|
||||
AdvSceneSwitcher.fileTab.local="локальный файл"
|
||||
AdvSceneSwitcher.fileTab.useRegExp="использовать регулярные выражения (сопоставление шаблонов)"
|
||||
AdvSceneSwitcher.fileTab.checkfileContentTime="если дата модификации изменена"
|
||||
AdvSceneSwitcher.fileTab.checkfileContent="если содержимое изменено"
|
||||
AdvSceneSwitcher.fileTab.entry="Переключиться на {{scenes}} используя {{transitions}} если содержимое {{fileType}} {{filePath}} {{browseButton}} совпадает:"
|
||||
AdvSceneSwitcher.fileTab.entry2="{{matchText}}"
|
||||
AdvSceneSwitcher.fileTab.entry3="{{useRegex}} {{checkModificationDate}} {{checkFileContent}}"
|
||||
AdvSceneSwitcher.fileTab.help="Эта вкладка позволит вам автоматически переключать сцены на основе содержимого удаленных или локальных файлов.\n\nНажмите на выделенный символ плюса, чтобы продолжить."
|
||||
|
||||
; Random Tab
|
||||
AdvSceneSwitcher.randomTab.title="Рандом"
|
||||
AdvSceneSwitcher.randomTab.randomDisabledWarning="Функциональность отключена - для активации выберите \"Если условие переключения не выполнено, переключиться на любую сцену на вкладке Random\" на вкладке Общие"
|
||||
AdvSceneSwitcher.randomTab.entry="Если условие переключения не выполнено, переключиться на {{scenes}} используя {{transitions}} для {{delay}}"
|
||||
AdvSceneSwitcher.randomTab.help="Переключатель сцен будет случайным образом выбирать запись на этой вкладке для переключения на заданное время.\nЗаметьте, что одна и та же запись не будет выбрана дважды подряд.\n\nНажмите на выделенный символ плюса, чтобы продолжить."
|
||||
|
||||
; Time Tab
|
||||
AdvSceneSwitcher.timeTab.title="Время"
|
||||
AdvSceneSwitcher.timeTab.anyDay="В любой день"
|
||||
AdvSceneSwitcher.timeTab.mondays="По понедельникам"
|
||||
AdvSceneSwitcher.timeTab.tuesdays="По вторникам"
|
||||
AdvSceneSwitcher.timeTab.wednesdays="По средам"
|
||||
AdvSceneSwitcher.timeTab.thursdays="По четвергам"
|
||||
AdvSceneSwitcher.timeTab.fridays="По пятницам"
|
||||
AdvSceneSwitcher.timeTab.saturdays="По субботам"
|
||||
AdvSceneSwitcher.timeTab.sundays="По воскресеньям"
|
||||
AdvSceneSwitcher.timeTab.afterstart="После начала потокового вещания/записи"
|
||||
AdvSceneSwitcher.timeTab.afterstart.tip="Будет использоваться время относительно начала потокового вещания/записи"
|
||||
AdvSceneSwitcher.timeTab.entry="{{triggers}} в {{time}} переключаются на {{scenes}} используя {{transitions}}"
|
||||
AdvSceneSwitcher.timeTab.help="Эта вкладка позволит вам автоматически переключаться на другую сцену на основе текущего местного времени.\n\nЗаметьте, что переключатель сцен будет переключаться только в точное время, которое вы указали.\nУбедитесь, что вы настроили параметры приоритета на вкладке Общие по своему вкусу, чтобы выбранная временная точка не была пропущена из-за других методов переключения, имеющих более высокий приоритет.\n\nНажмите на выделенный символ плюса, чтобы продолжить."
|
||||
|
||||
; Idle Tab
|
||||
AdvSceneSwitcher.idleTab.title="Бездействие"
|
||||
AdvSceneSwitcher.idleTab.enable="Включить обнаружение простоя"
|
||||
AdvSceneSwitcher.idleTab.idleswitch="После {{duration}} отсутствия ввода с клавиатуры или мыши переключиться на сцену {{scenes}} используя {{transitions}}"
|
||||
AdvSceneSwitcher.idleTab.dontSwitchIfFocus1="Не переключаться, если"
|
||||
AdvSceneSwitcher.idleTab.dontSwitchIfFocus2="находится в фокусе"
|
||||
|
||||
; Scene Sequence Tab
|
||||
AdvSceneSwitcher.sceneSequenceTab.title="Последовательность"
|
||||
AdvSceneSwitcher.sceneSequenceTab.description="Последовательность автоматического переключения сцен может быть отменена либо паузой/остановкой переключателя сцен, либо ручным переключением на другую сцену"
|
||||
AdvSceneSwitcher.sceneSequenceTab.save="Сохранить последовательность сцен в файл"
|
||||
AdvSceneSwitcher.sceneSequenceTab.load="Загрузить последовательности сцен из файла"
|
||||
AdvSceneSwitcher.sceneSequenceTab.saveTitle="Сохранить последовательность сцен в файл ..."
|
||||
AdvSceneSwitcher.sceneSequenceTab.loadTitle="Выберите файл для чтения последовательности сцен из файла ..."
|
||||
AdvSceneSwitcher.sceneSequenceTab.loadFail="Advanced Scene Switcher не удалось импортировать настройки!"
|
||||
AdvSceneSwitcher.sceneSequenceTab.loadSuccess="Настройки Advanced Scene Switcher импортированы успешно!"
|
||||
AdvSceneSwitcher.sceneSequenceTab.fileType="Текстовые файлы (*.txt)"
|
||||
AdvSceneSwitcher.sceneSequenceTab.interruptible="прервать"
|
||||
AdvSceneSwitcher.sceneSequenceTab.interruptibleHint="Другие методы переключения разрешены для прерывания этой последовательности сцен"
|
||||
AdvSceneSwitcher.sceneSequenceTab.entry="Когда {{startScenes}} активен, переключить на {{scenes}} после {{delay}} используя {{transitions}} {{interruptible}}"
|
||||
AdvSceneSwitcher.sceneSequenceTab.extendEdit="Расширить последовательность"
|
||||
AdvSceneSwitcher.sceneSequenceTab.extendEntry="После {{delay}} переключиться на {{scenes}} используя {{transitions}}"
|
||||
AdvSceneSwitcher.sceneSequenceTab.help="Эта вкладка позволит вам автоматически переключаться на другую сцену, если сцена была активна в течение заданного периода времени.\nНапример, вы можете автоматически переключаться между двумя сценами.\n\nНажмите на выделенный символ плюса, чтобы продолжить."
|
||||
|
||||
; Audio Tab
|
||||
AdvSceneSwitcher.audioTab.title="Аудио"
|
||||
AdvSceneSwitcher.audioTab.condition.above="выше"
|
||||
AdvSceneSwitcher.audioTab.condition.below="ниже"
|
||||
AdvSceneSwitcher.audioTab.ignoreInactiveSource="если источник неактивен"
|
||||
AdvSceneSwitcher.audioTab.entry="Когда громкость {{audioSources}} равна {{condition}} {{volumeWidget}} в течение {{duration}} секунд переключить на {{scenes}} используя {{transitions}} {{ignoreInactiveSource}}"
|
||||
AdvSceneSwitcher.audioTab.multiMatchfallbackCondition="Если несколько записей совпадают ..."
|
||||
AdvSceneSwitcher.audioTab.multiMatchfallback="... в течение {{duration}} секунд переключить на {{scenes}} используя {{transitions}}"
|
||||
AdvSceneSwitcher.audioTab.help="Эта вкладка позволит вам переключать сцены в зависимости от громкости источников.\nНапример, вы можете автоматически переключиться на другую сцену, если громкость вашего микрофона достигнет определенного порога.\n\nНажмите на выделенный символ плюса, чтобы продолжить."
|
||||
|
||||
; Video Tab
|
||||
AdvSceneSwitcher.videoTab.title="Видео"
|
||||
AdvSceneSwitcher.videoTab.getScreenshot="Получить снимок экрана для выбранной записи"
|
||||
AdvSceneSwitcher.videoTab.getScreenshotHelp="Получить скриншот источника видео текущей выбранной записи и автоматически установить его в качестве целевого изображени"
|
||||
AdvSceneSwitcher.videoTab.condition.match="точно соответствует"
|
||||
AdvSceneSwitcher.videoTab.condition.match.tooltip="Точное совпадение требует, чтобы целевое и исходное изображение имели одинаковое разрешение.\nДаже каждый пиксель должен совпадать, поэтому не рекомендуется использовать форматы изображений, которые используют сжатие (например, .JPG)!"
|
||||
AdvSceneSwitcher.videoTab.condition.differ="не совпадает"
|
||||
AdvSceneSwitcher.videoTab.condition.hasNotChanged="не изменилось"
|
||||
AdvSceneSwitcher.videoTab.condition.hasChanged="изменилось"
|
||||
AdvSceneSwitcher.videoTab.ignoreInactiveSource="если источник неактивен"
|
||||
AdvSceneSwitcher.videoTab.entry="Когда {{videoSources}} {{condition}} {{filePath}} {{browseButton}} для {{duration}} переключиться на {{scenes}} используя {{transitions}} {{ignoreInactiveSource}}"
|
||||
AdvSceneSwitcher.videoTab.help="<html><head/><body><p>Эта вкладка позволит вам переключать сцены на основе текущего видеовыхода выбранных источников.<br/>Обязательно проверьте <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/> Нажмите на выделенный символ плюса, чтобы продолжить.</p></body></html>"
|
||||
|
||||
; Network Tab
|
||||
AdvSceneSwitcher.networkTab.title="Сеть"
|
||||
AdvSceneSwitcher.networkTab.warning="Запуск сервера вне локальной сети позволит третьим лицам читать активную сцену."
|
||||
AdvSceneSwitcher.networkTab.DisabledWarning="ту функциональность, к сожалению, пришлось отключить на macOS из-за несовместимости библиотек при параллельном запуске плагина obs-websocket."
|
||||
AdvSceneSwitcher.networkTab.server="Запустить сервер (отправляет сообщения о переключении сцены всем подключенным клиентам)"
|
||||
AdvSceneSwitcher.networkTab.server.port="Порт"
|
||||
AdvSceneSwitcher.networkTab.server.lockToIPv4="Заблокировать сервер на использование только IPv4"
|
||||
AdvSceneSwitcher.networkTab.server.restrictSendToAutomatedSwitches="Отправлять сообщения только для автоматических переключателей сцен"
|
||||
AdvSceneSwitcher.networkTab.startFailed.message="Сервер WebSockets не удалось запустить, возможно потому, что:\n - TCP порт %1 может в настоящее время использоваться в другом месте в этой системе, возможно, другим приложением. Попробуйте установить другой TCP порт в настройках сервера WebSocket, или остановите любое приложение, которое может использовать этот порт.\n - Сообщение об ошибке: %2"
|
||||
AdvSceneSwitcher.networkTab.server.status.currentStatus="Текущий статус"
|
||||
AdvSceneSwitcher.networkTab.server.status.notRunning="Не запущен"
|
||||
AdvSceneSwitcher.networkTab.server.status.starting="Запуск"
|
||||
AdvSceneSwitcher.networkTab.server.status.running="Работает"
|
||||
AdvSceneSwitcher.networkTab.server.restart="Перезапустить сервер"
|
||||
AdvSceneSwitcher.networkTab.client="Запустить клиент (Получает сообщения о переключении сцен)"
|
||||
AdvSceneSwitcher.networkTab.client.address="Имя хоста или IP-адрес"
|
||||
AdvSceneSwitcher.networkTab.client.port="Порт"
|
||||
AdvSceneSwitcher.networkTab.client.status.currentStatus="Текущий статус"
|
||||
AdvSceneSwitcher.networkTab.client.status.disconnected="Отключено"
|
||||
AdvSceneSwitcher.networkTab.client.status.connecting="Подключение"
|
||||
AdvSceneSwitcher.networkTab.client.status.connected="Подключено"
|
||||
AdvSceneSwitcher.networkTab.client.reconnect="Принудительное переподключение"
|
||||
|
||||
; Scene Group Tab
|
||||
AdvSceneSwitcher.sceneGroupTab.title="Группа сцен"
|
||||
AdvSceneSwitcher.sceneGroupTab.list="Группы сцен"
|
||||
AdvSceneSwitcher.sceneGroupTab.edit="Редактировать группы сцен"
|
||||
AdvSceneSwitcher.sceneGroupTab.edit.name="Имя:"
|
||||
AdvSceneSwitcher.sceneGroupTab.edit.type="Тип: {{type}}"
|
||||
AdvSceneSwitcher.sceneGroupTab.type.count="Количество"
|
||||
AdvSceneSwitcher.sceneGroupTab.type.time="Время"
|
||||
AdvSceneSwitcher.sceneGroupTab.type.random="Случайно"
|
||||
AdvSceneSwitcher.sceneGroupTab.edit.count="Переход к следующей сцене в списке после {{count}} совпадений"
|
||||
AdvSceneSwitcher.sceneGroupTab.edit.time="Переход к следующей сцене в списке по истечении {{time}}"
|
||||
AdvSceneSwitcher.sceneGroupTab.edit.random="Выбирать следующую сцену в списке случайным образом"
|
||||
AdvSceneSwitcher.sceneGroupTab.edit.repeat="Начинать с начала, если достигнут конец списка сцен"
|
||||
AdvSceneSwitcher.sceneGroupTab.edit.addScene="Добавить сцену"
|
||||
AdvSceneSwitcher.sceneGroupTab.add="Добавить группу сцен"
|
||||
AdvSceneSwitcher.sceneGroupTab.defaultname="Группа сцен %1"
|
||||
AdvSceneSwitcher.sceneGroupTab.exists="Группа сцен или название сцены уже существует"
|
||||
AdvSceneSwitcher.sceneGroupTab.help="Группы сцен могут быть выбраны в качестве цели так же, как и обычные сцены.\n\nКак следует из названия, группа сцен представляет собой набор из нескольких сцен.\nГруппа сцен будет продвигаться по списку назначенных ей сцен в зависимости от настроенных параметров, которые можно найти справа.\n\nВы можете настроить группу сцен на переход к следующей сцене в списке:\nПосле определенного количества раз, когда группа сцен выбрана в качестве цели.\nПо истечении определенного времени.\nИли случайным образом.\n\nНапример, группа сцен, содержащая сцены ...\nScene 1\nScene 2\nScene 3 \n... активирует \"Scene 1\" в первый раз, когда он выбран в качестве цели.\nВо второй раз он активируется \"Scene 2\".\nОставшееся время \"Scene 3\" будет активирован.\n\nНажмите на выделенный символ плюса ниже, чтобы добавить новую группу сцен."
|
||||
AdvSceneSwitcher.sceneGroupTab.scenes.help="Выберите группу сцен, которую вы хотите изменить слева.\n\nВыберите сцену для добавления в эту группу сцен, выбрав сцену выше и нажав на символ плюса ниже.\n\nСцена может быть добавлена несколько раз в одну и ту же группу сцен."
|
||||
|
||||
; Scene Trigger Tab
|
||||
AdvSceneSwitcher.sceneTriggerTab.title="Триггеры сцены"
|
||||
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerType.none="--выбрать триггер--"
|
||||
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerType.sceneActive="активен"
|
||||
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerType.sceneInactive="не активен"
|
||||
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerType.sceneLeave="переключился с"
|
||||
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.none="--выбрать действие--"
|
||||
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.startRecording="начать запись"
|
||||
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.pauseRecording="приостановить запись"
|
||||
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.unpauseRecording="отменить паузу записи"
|
||||
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.stopRecording="остановить запись"
|
||||
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.stopStreaming="остановить потоковое вещание"
|
||||
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.startStreaming="начать потоковое вещание"
|
||||
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.startReplayBuffer="запустить буфер воспроизведения"
|
||||
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.stopReplayBuffer="остановить буфер воспроизведения"
|
||||
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.muteSource="отключить источник"
|
||||
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.unmuteSource="включить источник"
|
||||
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.startSwitcher="запустить переключатель сцены"
|
||||
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.stopSwitcher="остановить переключатель сцены"
|
||||
AdvSceneSwitcher.sceneTriggerTab.entry="Когда {{scenes}} {{triggers}} {{actions}} {{audioSources}} после {{duration}}"
|
||||
AdvSceneSwitcher.sceneTriggerTab.help="Эта вкладка позволяет запускать действия при изменении сцены, например, остановку записи или потоковой передачи."
|
||||
|
||||
; Hotkey
|
||||
AdvSceneSwitcher.hotkey.startSwitcherHotkey="Запустить Advanced Scene Switcher"
|
||||
AdvSceneSwitcher.hotkey.stopSwitcherHotkey="становить Advanced Scene Switcher"
|
||||
AdvSceneSwitcher.hotkey.startStopToggleSwitcherHotkey="Переключить старт/стоп для Advanced Scene Switcher"
|
||||
|
||||
AdvSceneSwitcher.askBackup="Обнаружена новая версия Advanced Scene Switcher.\nНужно ли создать резервную копию старых настроек?"
|
||||
|
||||
AdvSceneSwitcher.close="Закрыть"
|
||||
AdvSceneSwitcher.browse="Обзор"
|
||||
|
||||
AdvSceneSwitcher.selectScene="--выбрать сцену--"
|
||||
AdvSceneSwitcher.selectPreviousScene="Предыдущая сцена"
|
||||
AdvSceneSwitcher.currentTransition="Текущий переход"
|
||||
AdvSceneSwitcher.selectTransition="--выбрать переход--"
|
||||
AdvSceneSwitcher.selectWindow="--выбрать окно--"
|
||||
AdvSceneSwitcher.selectAudioSource="--выбрать источник звука--"
|
||||
AdvSceneSwitcher.selectVideoSource="--sвыбрать источник видео--"
|
||||
AdvSceneSwitcher.selectMediaSource="--выбрать источник мультимедиа--"
|
||||
AdvSceneSwitcher.selectProcess="--выбрать процесс--"
|
||||
AdvSceneSwitcher.enterPath="--ввести путь--"
|
||||
AdvSceneSwitcher.enterText="--ввести текст--"
|
||||
AdvSceneSwitcher.invaildEntriesWillNotBeSaved="недействительные записи не будут сохранены"
|
||||
AdvSceneSwitcher.selectWindowTip="Используйте \"OBS\" для указания окна OBS\nИспользуйте \"Переключение задач\"для указания ALT + TAB"
|
||||
|
||||
AdvSceneSwitcher.status.active="Активный"
|
||||
AdvSceneSwitcher.status.inactive="Неактивен"
|
||||
|
||||
AdvSceneSwitcher.unit.milliseconds="миллисекунды"
|
||||
AdvSceneSwitcher.unit.secends="секунды"
|
||||
AdvSceneSwitcher.unit.minutes="минуты"
|
||||
AdvSceneSwitcher.unit.hours="часы"
|
||||
@@ -168,7 +168,7 @@ AdvSceneSwitcher.sceneSequenceTab.loadTitle="从文件加载序列 ..."
|
||||
AdvSceneSwitcher.sceneSequenceTab.loadFail="导入失败"
|
||||
AdvSceneSwitcher.sceneSequenceTab.loadSuccess="导入失败成功"
|
||||
AdvSceneSwitcher.sceneSequenceTab.fileType="文本文件 (*.txt)"
|
||||
AdvSceneSwitcher.sceneSequenceTab.entry="当场景 {{startScenes}} 被激活 {{delay}} {{delayUnits}}后使用转场特效 {{transitions}} 切换到场景 {{scenes}} {{interruptible}}"
|
||||
AdvSceneSwitcher.sceneSequenceTab.entry="当场景 {{startScenes}} 被激活 {{delay}} 后使用转场特效 {{transitions}} 切换到场景 {{scenes}} {{interruptible}}"
|
||||
|
||||
; Audio Tab
|
||||
AdvSceneSwitcher.audioTab.title="音频"
|
||||
|
||||
3
data/res/time.svg
Normal file
3
data/res/time.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" preserveAspectRatio="xMidYMid meet" viewBox="0 0 640 640" width="640" height="640"><defs><path d="M599.62 325.49C599.62 477.26 476.41 600.47 324.64 600.47C172.88 600.47 49.67 477.26 49.67 325.49C49.67 173.73 172.88 50.52 324.64 50.52C476.41 50.52 599.62 173.73 599.62 325.49Z" id="c3Wyq0kfYS"></path><mask id="maskanTyyngos" x="-20.33" y="-19.48" width="689.95" height="689.95" maskUnits="userSpaceOnUse"><rect x="-20.33" y="-19.48" width="689.95" height="689.95" fill="white"></rect><use xlink:href="#c3Wyq0kfYS" opacity="1" fill="black"></use></mask><path d="M334.97 96.07C339.51 96.07 343.2 99.75 343.2 104.3C343.2 151.24 343.2 283.84 343.2 330.78C343.2 335.32 339.51 339 334.97 339C329.2 339 320.09 339 314.31 339C309.77 339 306.09 335.32 306.09 330.78C306.09 283.84 306.09 151.24 306.09 104.3C306.09 99.75 309.77 96.07 314.31 96.07C320.09 96.07 329.2 96.07 334.97 96.07Z" id="fxMeu932c"></path><path d="M471.96 338.21C471.97 341.31 469.46 343.83 466.37 343.84C434.35 343.93 343.92 344.21 311.91 344.31C308.81 344.32 306.3 341.82 306.29 338.72C306.27 332.42 306.23 319.13 306.21 312.83C306.2 309.73 308.7 307.21 311.8 307.2C343.81 307.11 434.24 306.83 466.25 306.73C469.35 306.72 471.87 309.23 471.88 312.32C471.9 318.62 471.94 331.91 471.96 338.21Z" id="h65nGmP2BL"></path></defs><g><g><g><g mask="url(#maskanTyyngos)"><use xlink:href="#c3Wyq0kfYS" opacity="1" fill-opacity="0" stroke="#d2d2d2" stroke-width="70" stroke-opacity="1"></use></g></g><g><use xlink:href="#fxMeu932c" opacity="1" fill="#d2d2d2" fill-opacity="1"></use></g><g><use xlink:href="#h65nGmP2BL" opacity="1" fill="#d2d2d2" fill-opacity="1"></use></g></g></g></svg>
|
||||
|
After Width: | Height: | Size: 1.8 KiB |
1
deps/asio
vendored
Submodule
1
deps/asio
vendored
Submodule
Submodule deps/asio added at b73dc1d2c0
1
deps/websocketpp
vendored
Submodule
1
deps/websocketpp
vendored
Submodule
Submodule deps/websocketpp added at c6d7e295bf
File diff suppressed because it is too large
Load Diff
@@ -1,16 +1,13 @@
|
||||
#include <QMainWindow>
|
||||
#include <QAction>
|
||||
#include <QtGui/qstandarditemmodel.h>
|
||||
#include <QPropertyAnimation>
|
||||
#include <QGraphicsColorizeEffect>
|
||||
#include <QMessageBox>
|
||||
#include <QFileDialog>
|
||||
|
||||
#include <obs-module.h>
|
||||
#include <obs-frontend-api.h>
|
||||
#include <util/util.hpp>
|
||||
|
||||
#include "headers/advanced-scene-switcher.hpp"
|
||||
#include "headers/curl-helper.hpp"
|
||||
#include "headers/utility.hpp"
|
||||
#include "headers/version.h"
|
||||
|
||||
SwitcherData *switcher = nullptr;
|
||||
@@ -59,313 +56,49 @@ void AdvSceneSwitcher::loadUI()
|
||||
setupFileTab();
|
||||
setupTimeTab();
|
||||
setupAudioTab();
|
||||
setupVideoTab();
|
||||
setupNetworkTab();
|
||||
setupSceneGroupTab();
|
||||
setupTriggerTab();
|
||||
setupMacroTab();
|
||||
|
||||
setTabOrder();
|
||||
restoreWindowGeo();
|
||||
|
||||
loading = false;
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
* UI helpers
|
||||
* Saving and loading
|
||||
******************************************************************************/
|
||||
bool AdvSceneSwitcher::DisplayMessage(QString msg, bool question)
|
||||
void AskBackup(obs_data_t *obj)
|
||||
{
|
||||
if (question) {
|
||||
QMessageBox::StandardButton reply;
|
||||
reply = QMessageBox::question(
|
||||
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();
|
||||
}
|
||||
bool backupSettings = DisplayMessage(
|
||||
obs_module_text("AdvSceneSwitcher.askBackup"), true);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void addSelectionEntry(QComboBox *sel, const char *description,
|
||||
const char *tooltip = "")
|
||||
{
|
||||
sel->addItem(description);
|
||||
|
||||
if (strcmp(tooltip, "") != 0) {
|
||||
sel->setItemData(0, tooltip, Qt::ToolTipRole);
|
||||
}
|
||||
|
||||
QStandardItemModel *model =
|
||||
qobject_cast<QStandardItemModel *>(sel->model());
|
||||
QModelIndex firstIndex =
|
||||
model->index(0, sel->modelColumn(), sel->rootModelIndex());
|
||||
QStandardItem *firstItem = model->itemFromIndex(firstIndex);
|
||||
firstItem->setSelectable(false);
|
||||
firstItem->setEnabled(false);
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::populateSceneSelection(QComboBox *sel, bool addPrevious,
|
||||
bool addSceneGroup,
|
||||
bool addSelect,
|
||||
std::string selectText)
|
||||
{
|
||||
sel->clear();
|
||||
|
||||
if (addSelect) {
|
||||
if (selectText.empty()) {
|
||||
addSelectionEntry(
|
||||
sel,
|
||||
obs_module_text("AdvSceneSwitcher.selectScene"),
|
||||
obs_module_text(
|
||||
"AdvSceneSwitcher.invaildEntriesWillNotBeSaved"));
|
||||
} else {
|
||||
addSelectionEntry(sel, selectText.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
BPtr<char *> scenes = obs_frontend_get_scene_names();
|
||||
char **temp = scenes;
|
||||
while (*temp) {
|
||||
const char *name = *temp;
|
||||
sel->addItem(name);
|
||||
temp++;
|
||||
}
|
||||
|
||||
if (addPrevious) {
|
||||
sel->addItem(obs_module_text(
|
||||
"AdvSceneSwitcher.selectPreviousScene"));
|
||||
}
|
||||
|
||||
if (addSceneGroup) {
|
||||
for (auto &sg : switcher->sceneGroups) {
|
||||
sel->addItem(QString::fromStdString(sg.name));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::populateTransitionSelection(QComboBox *sel,
|
||||
bool addSelect)
|
||||
{
|
||||
if (addSelect) {
|
||||
addSelectionEntry(
|
||||
sel,
|
||||
obs_module_text("AdvSceneSwitcher.selectTransition"));
|
||||
}
|
||||
|
||||
obs_frontend_source_list *transitions = new obs_frontend_source_list();
|
||||
obs_frontend_get_transitions(transitions);
|
||||
|
||||
for (size_t i = 0; i < transitions->sources.num; i++) {
|
||||
const char *name =
|
||||
obs_source_get_name(transitions->sources.array[i]);
|
||||
sel->addItem(name);
|
||||
}
|
||||
|
||||
obs_frontend_source_list_free(transitions);
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::populateWindowSelection(QComboBox *sel, bool addSelect)
|
||||
{
|
||||
if (addSelect) {
|
||||
addSelectionEntry(
|
||||
sel, obs_module_text("AdvSceneSwitcher.selectWindow"));
|
||||
}
|
||||
|
||||
std::vector<std::string> windows;
|
||||
GetWindowList(windows);
|
||||
sort(windows.begin(), windows.end());
|
||||
|
||||
for (std::string &window : windows) {
|
||||
sel->addItem(window.c_str());
|
||||
}
|
||||
|
||||
#ifdef WIN32
|
||||
sel->setItemData(0, obs_module_text("AdvSceneSwitcher.selectWindowTip"),
|
||||
Qt::ToolTipRole);
|
||||
#endif
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::populateAudioSelection(QComboBox *sel, bool addSelect)
|
||||
{
|
||||
if (addSelect) {
|
||||
addSelectionEntry(
|
||||
sel,
|
||||
obs_module_text("AdvSceneSwitcher.selectAudioSource"),
|
||||
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);
|
||||
|
||||
if ((flags & OBS_SOURCE_AUDIO) != 0) {
|
||||
list->push_back(obs_source_get_name(source));
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
std::vector<std::string> audioSources;
|
||||
obs_enum_sources(sourceEnum, &audioSources);
|
||||
sort(audioSources.begin(), audioSources.end());
|
||||
for (std::string &source : audioSources) {
|
||||
sel->addItem(source.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::populateMediaSelection(QComboBox *sel, bool addSelect)
|
||||
{
|
||||
if (addSelect) {
|
||||
addSelectionEntry(
|
||||
sel,
|
||||
obs_module_text("AdvSceneSwitcher.selectMediaSource"),
|
||||
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);
|
||||
std::string sourceId = obs_source_get_id(source);
|
||||
if (sourceId.compare("ffmpeg_source") == 0 ||
|
||||
sourceId.compare("vlc_source") == 0) {
|
||||
list->push_back(obs_source_get_name(source));
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
std::vector<std::string> mediaSources;
|
||||
obs_enum_sources(sourceEnum, &mediaSources);
|
||||
sort(mediaSources.begin(), mediaSources.end());
|
||||
for (std::string &source : mediaSources) {
|
||||
sel->addItem(source.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::populateProcessSelection(QComboBox *sel, bool addSelect)
|
||||
{
|
||||
if (addSelect) {
|
||||
addSelectionEntry(
|
||||
sel, obs_module_text("AdvSceneSwitcher.selectProcess"));
|
||||
}
|
||||
|
||||
QStringList processes;
|
||||
GetProcessList(processes);
|
||||
processes.sort();
|
||||
for (QString &process : processes)
|
||||
sel->addItem(process);
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::listAddClicked(QListWidget *list,
|
||||
SwitchWidget *newWidget,
|
||||
QPushButton *addButton,
|
||||
QMetaObject::Connection *addHighlight)
|
||||
{
|
||||
if (!list || !newWidget) {
|
||||
blog(LOG_WARNING,
|
||||
"listAddClicked called without valid list or widget");
|
||||
if (!backupSettings) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (addButton && addHighlight) {
|
||||
addButton->disconnect(*addHighlight);
|
||||
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;
|
||||
}
|
||||
|
||||
QListWidgetItem *item;
|
||||
item = new QListWidgetItem(list);
|
||||
list->addItem(item);
|
||||
item->setSizeHint(newWidget->minimumSizeHint());
|
||||
list->setItemWidget(item, newWidget);
|
||||
|
||||
list->scrollToItem(item);
|
||||
}
|
||||
|
||||
bool AdvSceneSwitcher::listMoveUp(QListWidget *list)
|
||||
{
|
||||
int index = list->currentRow();
|
||||
if (index == -1 || index == 0) {
|
||||
return false;
|
||||
QFile file(directory);
|
||||
if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
|
||||
return;
|
||||
}
|
||||
|
||||
QWidget *row = list->itemWidget(list->currentItem());
|
||||
QListWidgetItem *itemN = list->currentItem()->clone();
|
||||
|
||||
list->insertItem(index - 1, itemN);
|
||||
list->setItemWidget(itemN, row);
|
||||
|
||||
list->takeItem(index + 1);
|
||||
list->setCurrentRow(index - 1);
|
||||
return true;
|
||||
obs_data_save_json(obj, file.fileName().toUtf8().constData());
|
||||
}
|
||||
|
||||
bool AdvSceneSwitcher::listMoveDown(QListWidget *list)
|
||||
{
|
||||
int index = list->currentRow();
|
||||
if (index == -1 || index == list->count() - 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
QWidget *row = list->itemWidget(list->currentItem());
|
||||
QListWidgetItem *itemN = list->currentItem()->clone();
|
||||
|
||||
list->insertItem(index + 2, itemN);
|
||||
list->setItemWidget(itemN, row);
|
||||
|
||||
list->takeItem(index);
|
||||
list->setCurrentRow(index + 1);
|
||||
return true;
|
||||
}
|
||||
|
||||
QMetaObject::Connection AdvSceneSwitcher::PulseWidget(QWidget *widget,
|
||||
QColor endColor,
|
||||
QColor startColor,
|
||||
QString specifier)
|
||||
{
|
||||
if (switcher->disableHints) {
|
||||
return QMetaObject::Connection();
|
||||
}
|
||||
|
||||
widget->setStyleSheet(specifier + "{ \
|
||||
border-style: outset; \
|
||||
border-width: 2px; \
|
||||
border-radius: 10px; \
|
||||
border-color: rgb(0,0,0,0) \
|
||||
}");
|
||||
|
||||
QGraphicsColorizeEffect *eEffect = new QGraphicsColorizeEffect(widget);
|
||||
widget->setGraphicsEffect(eEffect);
|
||||
QPropertyAnimation *paAnimation =
|
||||
new QPropertyAnimation(eEffect, "color");
|
||||
paAnimation->setStartValue(startColor);
|
||||
paAnimation->setEndValue(endColor);
|
||||
paAnimation->setDuration(1000);
|
||||
// Play backwards to return to original state on timer end
|
||||
paAnimation->setDirection(QAbstractAnimation::Backward);
|
||||
|
||||
auto con = QWidget::connect(
|
||||
paAnimation, &QPropertyAnimation::finished, [paAnimation]() {
|
||||
QTimer::singleShot(1000, [paAnimation] {
|
||||
paAnimation->start();
|
||||
});
|
||||
});
|
||||
|
||||
paAnimation->start();
|
||||
|
||||
return con;
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
* Saving and loading
|
||||
******************************************************************************/
|
||||
static void SaveSceneSwitcher(obs_data_t *save_data, bool saving, void *)
|
||||
{
|
||||
if (saving) {
|
||||
@@ -390,7 +123,7 @@ static void SaveSceneSwitcher(obs_data_t *save_data, bool saving, void *)
|
||||
obj = obs_data_create();
|
||||
}
|
||||
if (switcher->versionChanged(obj, g_GIT_SHA1)) {
|
||||
AdvSceneSwitcher::AskBackup(obj);
|
||||
AskBackup(obj);
|
||||
}
|
||||
|
||||
switcher->loadSettings(obj);
|
||||
@@ -427,9 +160,11 @@ void SwitcherData::Thread()
|
||||
bool match = false;
|
||||
OBSWeakSource scene;
|
||||
OBSWeakSource transition;
|
||||
|
||||
bool defTransitionMatch = false;
|
||||
OBSWeakSource defTransition;
|
||||
// The previous scene might have changed during the linger duration,
|
||||
// if a longer transition is used than the configured check interval
|
||||
bool setPrevSceneAfterLinger = false;
|
||||
bool macroMatch = false;
|
||||
macroSceneSwitched = false;
|
||||
|
||||
endTime = std::chrono::high_resolution_clock::now();
|
||||
|
||||
@@ -449,71 +184,27 @@ void SwitcherData::Thread()
|
||||
}
|
||||
}
|
||||
|
||||
vblog(LOG_INFO, "sleep for %ld", duration.count());
|
||||
vblog(LOG_INFO, "try to sleep for %ld", duration.count());
|
||||
setWaitScene();
|
||||
cv.wait_for(lock, duration);
|
||||
|
||||
startTime = std::chrono::high_resolution_clock::now();
|
||||
sleep = 0;
|
||||
linger = 0;
|
||||
|
||||
switcher->Prune();
|
||||
|
||||
if (switcher->stop) {
|
||||
Prune();
|
||||
if (stop) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (checkPause()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
checkDefaultSceneTransitions(defTransitionMatch, defTransition);
|
||||
|
||||
for (int switchFuncName : functionNamesByPriority) {
|
||||
switch (switchFuncName) {
|
||||
case read_file_func:
|
||||
checkSwitchInfoFromFile(match, scene,
|
||||
transition);
|
||||
checkFileContent(match, scene, transition);
|
||||
break;
|
||||
case idle_func:
|
||||
checkIdleSwitch(match, scene, transition);
|
||||
break;
|
||||
case exe_func:
|
||||
checkExeSwitch(match, scene, transition);
|
||||
break;
|
||||
case screen_region_func:
|
||||
checkScreenRegionSwitch(match, scene,
|
||||
transition);
|
||||
break;
|
||||
case window_title_func:
|
||||
checkWindowTitleSwitch(match, scene,
|
||||
transition);
|
||||
break;
|
||||
case round_trip_func:
|
||||
checkSceneSequence(match, scene, transition,
|
||||
linger);
|
||||
break;
|
||||
case media_func:
|
||||
checkMediaSwitch(match, scene, transition);
|
||||
break;
|
||||
case time_func:
|
||||
checkTimeSwitch(match, scene, transition);
|
||||
break;
|
||||
case audio_func:
|
||||
checkAudioSwitch(match, scene, transition);
|
||||
break;
|
||||
}
|
||||
|
||||
if (switcher->stop) {
|
||||
goto endLoop;
|
||||
}
|
||||
if (match) {
|
||||
break;
|
||||
}
|
||||
match = checkForMatch(scene, transition, linger,
|
||||
setPrevSceneAfterLinger, macroMatch);
|
||||
if (stop) {
|
||||
break;
|
||||
}
|
||||
|
||||
checkNoMatchSwitch(match, scene, transition, sleep);
|
||||
|
||||
checkSwitchCooldown(match);
|
||||
|
||||
if (linger) {
|
||||
@@ -521,9 +212,10 @@ void SwitcherData::Thread()
|
||||
vblog(LOG_INFO, "sleep for %ld before switching scene",
|
||||
duration.count());
|
||||
|
||||
setWaitScene();
|
||||
cv.wait_for(lock, duration);
|
||||
|
||||
if (switcher->stop) {
|
||||
if (stop) {
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -533,6 +225,8 @@ void SwitcherData::Thread()
|
||||
|
||||
match = false;
|
||||
linger = 0;
|
||||
} else if (setPrevSceneAfterLinger) {
|
||||
scene = previousScene;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -544,66 +238,158 @@ void SwitcherData::Thread()
|
||||
// leading to a deadlock, so we have to unlock()
|
||||
lock.unlock();
|
||||
|
||||
if (!match && defTransitionMatch) {
|
||||
setCurrentDefTransition(defTransition);
|
||||
}
|
||||
|
||||
if (match) {
|
||||
switchScene(scene, transition,
|
||||
tansitionOverrideOverride);
|
||||
if (macroMatch) {
|
||||
runMacros();
|
||||
} else {
|
||||
switchScene({scene, transition, 0});
|
||||
}
|
||||
}
|
||||
|
||||
writeSceneInfoToFile();
|
||||
}
|
||||
endLoop:
|
||||
|
||||
blog(LOG_INFO, "stopped");
|
||||
}
|
||||
|
||||
void switchScene(OBSWeakSource &scene, OBSWeakSource &transition,
|
||||
bool &transitionOverrideOverride)
|
||||
bool SwitcherData::checkForMatch(OBSWeakSource &scene,
|
||||
OBSWeakSource &transition, int &linger,
|
||||
bool &setPrevSceneAfterLinger,
|
||||
bool ¯oMatch)
|
||||
{
|
||||
if (!scene && switcher->verbose) {
|
||||
bool match = false;
|
||||
|
||||
if (uninterruptibleSceneSequenceActive) {
|
||||
match = checkSceneSequence(scene, transition, linger,
|
||||
setPrevSceneAfterLinger);
|
||||
if (match) {
|
||||
return match;
|
||||
}
|
||||
}
|
||||
|
||||
for (int switchFuncName : functionNamesByPriority) {
|
||||
switch (switchFuncName) {
|
||||
case read_file_func:
|
||||
match = checkSwitchInfoFromFile(scene, transition) ||
|
||||
checkFileContent(scene, transition);
|
||||
break;
|
||||
case idle_func:
|
||||
match = checkIdleSwitch(scene, transition);
|
||||
break;
|
||||
case exe_func:
|
||||
match = checkExeSwitch(scene, transition);
|
||||
break;
|
||||
case screen_region_func:
|
||||
match = checkScreenRegionSwitch(scene, transition);
|
||||
break;
|
||||
case window_title_func:
|
||||
match = checkWindowTitleSwitch(scene, transition);
|
||||
break;
|
||||
case round_trip_func:
|
||||
match = checkSceneSequence(scene, transition, linger,
|
||||
setPrevSceneAfterLinger);
|
||||
break;
|
||||
case media_func:
|
||||
match = checkMediaSwitch(scene, transition);
|
||||
break;
|
||||
case time_func:
|
||||
match = checkTimeSwitch(scene, transition);
|
||||
break;
|
||||
case audio_func:
|
||||
match = checkAudioSwitch(scene, transition);
|
||||
break;
|
||||
case video_func:
|
||||
match = checkVideoSwitch(scene, transition);
|
||||
break;
|
||||
case macro_func:
|
||||
if (checkMacros()) {
|
||||
match = true;
|
||||
macroMatch = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (stop) {
|
||||
return false;
|
||||
}
|
||||
if (match) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return match;
|
||||
}
|
||||
|
||||
void switchScene(const sceneSwitchInfo &sceneSwitch)
|
||||
{
|
||||
if (!sceneSwitch.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(sceneSwitch.scene);
|
||||
obs_source_t *currentSource = obs_frontend_get_current_scene();
|
||||
|
||||
if (source && source != currentSource) {
|
||||
transitionData td;
|
||||
setNextTransition(scene, currentSource, transition,
|
||||
transitionOverrideOverride, td);
|
||||
transitionData currentTransitionData;
|
||||
setNextTransition(sceneSwitch, currentSource,
|
||||
currentTransitionData);
|
||||
obs_frontend_set_current_scene(source);
|
||||
if (transitionOverrideOverride) {
|
||||
restoreTransitionOverride(source, td);
|
||||
if (switcher->transitionOverrideOverride) {
|
||||
restoreTransitionOverride(source,
|
||||
currentTransitionData);
|
||||
}
|
||||
|
||||
if (switcher->verbose) {
|
||||
blog(LOG_INFO, "switched scene");
|
||||
}
|
||||
|
||||
if (switcher->networkConfig.ShouldSendSceneChange()) {
|
||||
switcher->server.sendMessage(sceneSwitch);
|
||||
}
|
||||
}
|
||||
obs_source_release(currentSource);
|
||||
obs_source_release(source);
|
||||
}
|
||||
|
||||
void switchPreviewScene(const OBSWeakSource &ws)
|
||||
{
|
||||
auto source = obs_weak_source_get_source(ws);
|
||||
obs_frontend_set_current_preview_scene(source);
|
||||
obs_source_release(source);
|
||||
}
|
||||
|
||||
void SwitcherData::Start()
|
||||
{
|
||||
if (!(th && th->isRunning())) {
|
||||
stop = false;
|
||||
switcher->th = new SwitcherThread();
|
||||
switcher->th->start(
|
||||
(QThread::Priority)switcher->threadPriority);
|
||||
th = new SwitcherThread();
|
||||
th->start((QThread::Priority)threadPriority);
|
||||
|
||||
// Will be overwritten quickly but might be useful
|
||||
writeToStatusFile("Advanced Scene Switcher running");
|
||||
}
|
||||
|
||||
if (networkConfig.ServerEnabled) {
|
||||
server.start(networkConfig.ServerPort,
|
||||
networkConfig.LockToIPv4);
|
||||
}
|
||||
|
||||
if (networkConfig.ClientEnabled) {
|
||||
client.connect(networkConfig.GetClientUri());
|
||||
}
|
||||
}
|
||||
|
||||
void ResetMacroCounters()
|
||||
{
|
||||
for (auto &m : switcher->macros) {
|
||||
m.ResetCount();
|
||||
}
|
||||
}
|
||||
|
||||
void SwitcherData::Stop()
|
||||
{
|
||||
if (th && th->isRunning()) {
|
||||
switcher->stop = true;
|
||||
stop = true;
|
||||
transitionCv.notify_one();
|
||||
cv.notify_one();
|
||||
th->wait();
|
||||
@@ -611,7 +397,17 @@ void SwitcherData::Stop()
|
||||
th = nullptr;
|
||||
|
||||
writeToStatusFile("Advanced Scene Switcher stopped");
|
||||
ResetMacroCounters();
|
||||
}
|
||||
|
||||
server.stop();
|
||||
client.disconnect();
|
||||
}
|
||||
|
||||
void SwitcherData::setWaitScene()
|
||||
{
|
||||
waitScene = obs_frontend_get_current_scene();
|
||||
obs_source_release(waitScene);
|
||||
}
|
||||
|
||||
bool SwitcherData::sceneChangedDuringWait()
|
||||
@@ -641,11 +437,11 @@ extern "C" void FreeSceneSwitcher()
|
||||
switcher = nullptr;
|
||||
}
|
||||
|
||||
void handleSceneChange(SwitcherData *s)
|
||||
void handleSceneChange()
|
||||
{
|
||||
// Stop waiting if scene was manually changed
|
||||
if (s->sceneChangedDuringWait()) {
|
||||
s->cv.notify_one();
|
||||
// Stop waiting if scene was changed
|
||||
if (switcher->sceneChangedDuringWait()) {
|
||||
switcher->cv.notify_one();
|
||||
}
|
||||
|
||||
// Set previous scene
|
||||
@@ -653,41 +449,52 @@ void handleSceneChange(SwitcherData *s)
|
||||
obs_weak_source_t *ws = obs_source_get_weak_source(source);
|
||||
obs_source_release(source);
|
||||
obs_weak_source_release(ws);
|
||||
if (source && s->previousSceneHelper != ws) {
|
||||
s->previousScene = s->previousSceneHelper;
|
||||
s->previousSceneHelper = ws;
|
||||
if (source && switcher->previousSceneHelper != ws) {
|
||||
switcher->previousScene = switcher->previousSceneHelper;
|
||||
switcher->previousSceneHelper = ws;
|
||||
}
|
||||
|
||||
switcher->checkTriggers();
|
||||
switcher->checkDefaultSceneTransitions();
|
||||
|
||||
if (switcher->networkConfig.ShouldSendFrontendSceneChange()) {
|
||||
switcher->server.sendMessage({ws, nullptr, 0});
|
||||
}
|
||||
}
|
||||
|
||||
void handleTransitionStop(SwitcherData *s)
|
||||
void setLiveTime()
|
||||
{
|
||||
s->checkedDefTransition = false;
|
||||
switcher->liveTime = QDateTime::currentDateTime();
|
||||
}
|
||||
|
||||
void setLiveTime(SwitcherData *s)
|
||||
void resetLiveTime()
|
||||
{
|
||||
s->liveTime = QDateTime::currentDateTime();
|
||||
switcher->liveTime = QDateTime();
|
||||
}
|
||||
|
||||
void resetLiveTime(SwitcherData *s)
|
||||
void checkAutoStartRecording()
|
||||
{
|
||||
s->liveTime = QDateTime();
|
||||
if (switcher->autoStartEvent == AutoStartEvent::RECORDING ||
|
||||
switcher->autoStartEvent == AutoStartEvent::RECORINDG_OR_STREAMING)
|
||||
switcher->Start();
|
||||
}
|
||||
|
||||
void checkAutoStartRecording(SwitcherData *s)
|
||||
void checkAutoStartStreaming()
|
||||
{
|
||||
if (s->autoStartEvent == AutoStartEvent::RECORDING ||
|
||||
s->autoStartEvent == AutoStartEvent::RECORINDG_OR_STREAMING)
|
||||
s->Start();
|
||||
if (switcher->autoStartEvent == AutoStartEvent::STREAMING ||
|
||||
switcher->autoStartEvent == AutoStartEvent::RECORINDG_OR_STREAMING)
|
||||
switcher->Start();
|
||||
}
|
||||
|
||||
void checkAutoStartStreaming(SwitcherData *s)
|
||||
void handlePeviewSceneChange()
|
||||
{
|
||||
if (s->autoStartEvent == AutoStartEvent::STREAMING ||
|
||||
s->autoStartEvent == AutoStartEvent::RECORINDG_OR_STREAMING)
|
||||
s->Start();
|
||||
if (switcher->networkConfig.ShouldSendPrviewSceneChange()) {
|
||||
auto source = obs_frontend_get_current_preview_scene();
|
||||
auto weak = obs_source_get_weak_source(source);
|
||||
switcher->server.sendMessage({weak, nullptr, 0}, true);
|
||||
obs_weak_source_release(weak);
|
||||
obs_source_release(source);
|
||||
}
|
||||
}
|
||||
|
||||
// Note to future self:
|
||||
@@ -695,27 +502,31 @@ void checkAutoStartStreaming(SwitcherData *s)
|
||||
// frontend functions such as obs_frontend_set_current_scene()
|
||||
static void OBSEvent(enum obs_frontend_event event, void *switcher)
|
||||
{
|
||||
if (!switcher) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (event) {
|
||||
case OBS_FRONTEND_EVENT_EXIT:
|
||||
FreeSceneSwitcher();
|
||||
break;
|
||||
case OBS_FRONTEND_EVENT_SCENE_CHANGED:
|
||||
handleSceneChange((SwitcherData *)switcher);
|
||||
handleSceneChange();
|
||||
break;
|
||||
case OBS_FRONTEND_EVENT_TRANSITION_STOPPED:
|
||||
handleTransitionStop((SwitcherData *)switcher);
|
||||
case OBS_FRONTEND_EVENT_PREVIEW_SCENE_CHANGED:
|
||||
handlePeviewSceneChange();
|
||||
break;
|
||||
case OBS_FRONTEND_EVENT_RECORDING_STARTED:
|
||||
setLiveTime((SwitcherData *)switcher);
|
||||
checkAutoStartRecording((SwitcherData *)switcher);
|
||||
setLiveTime();
|
||||
checkAutoStartRecording();
|
||||
break;
|
||||
case OBS_FRONTEND_EVENT_STREAMING_STARTED:
|
||||
setLiveTime((SwitcherData *)switcher);
|
||||
checkAutoStartStreaming((SwitcherData *)switcher);
|
||||
setLiveTime();
|
||||
checkAutoStartStreaming();
|
||||
break;
|
||||
case OBS_FRONTEND_EVENT_RECORDING_STOPPED:
|
||||
case OBS_FRONTEND_EVENT_STREAMING_STOPPED:
|
||||
resetLiveTime((SwitcherData *)switcher);
|
||||
resetLiveTime();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
||||
270
src/duration-control.cpp
Normal file
270
src/duration-control.cpp
Normal file
@@ -0,0 +1,270 @@
|
||||
#include "headers/duration-control.hpp"
|
||||
#include "headers/utility.hpp"
|
||||
#include "obs-module.h"
|
||||
|
||||
#include <sstream>
|
||||
#include <iomanip>
|
||||
#include <QHBoxLayout>
|
||||
|
||||
void Duration::Save(obs_data_t *obj, const char *secondsName,
|
||||
const char *unitName)
|
||||
{
|
||||
obs_data_set_double(obj, secondsName, seconds);
|
||||
obs_data_set_int(obj, unitName, static_cast<int>(displayUnit));
|
||||
}
|
||||
|
||||
void Duration::Load(obs_data_t *obj, const char *secondsName,
|
||||
const char *unitName)
|
||||
{
|
||||
seconds = obs_data_get_double(obj, secondsName);
|
||||
displayUnit =
|
||||
static_cast<DurationUnit>(obs_data_get_int(obj, unitName));
|
||||
}
|
||||
|
||||
bool Duration::DurationReached()
|
||||
{
|
||||
if (_startTime.time_since_epoch().count() == 0) {
|
||||
_startTime = std::chrono::high_resolution_clock::now();
|
||||
}
|
||||
|
||||
auto runTime = std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||
std::chrono::high_resolution_clock::now() - _startTime);
|
||||
return runTime.count() >= seconds * 1000;
|
||||
}
|
||||
|
||||
void Duration::Reset()
|
||||
{
|
||||
_startTime = {};
|
||||
}
|
||||
|
||||
int durationUnitToMultiplier(DurationUnit u)
|
||||
{
|
||||
switch (u) {
|
||||
case DurationUnit::SECONDS:
|
||||
return 1;
|
||||
case DurationUnit::MINUTES:
|
||||
return 60;
|
||||
case DurationUnit::HOURS:
|
||||
return 3600;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::string durationUnitToString(DurationUnit u)
|
||||
{
|
||||
switch (u) {
|
||||
case DurationUnit::SECONDS:
|
||||
return obs_module_text("AdvSceneSwitcher.unit.secends");
|
||||
case DurationUnit::MINUTES:
|
||||
return obs_module_text("AdvSceneSwitcher.unit.minutes");
|
||||
case DurationUnit::HOURS:
|
||||
return obs_module_text("AdvSceneSwitcher.unit.hours");
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
std::string Duration::ToString()
|
||||
{
|
||||
std::ostringstream ss;
|
||||
ss << std::fixed << std::setprecision(2)
|
||||
<< seconds / durationUnitToMultiplier(displayUnit) << " "
|
||||
<< durationUnitToString(displayUnit);
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
static void populateUnits(QComboBox *list)
|
||||
{
|
||||
list->addItem(obs_module_text("AdvSceneSwitcher.unit.secends"));
|
||||
list->addItem(obs_module_text("AdvSceneSwitcher.unit.minutes"));
|
||||
list->addItem(obs_module_text("AdvSceneSwitcher.unit.hours"));
|
||||
}
|
||||
|
||||
DurationSelection::DurationSelection(QWidget *parent, bool showUnitSelection)
|
||||
: QWidget(parent), _unitMultiplier(1)
|
||||
{
|
||||
_duration = new QDoubleSpinBox(parent);
|
||||
_duration->setMaximum(86400); // 24 hours
|
||||
|
||||
_unitSelection = new QComboBox();
|
||||
populateUnits(_unitSelection);
|
||||
|
||||
QWidget::connect(_duration, SIGNAL(valueChanged(double)), this,
|
||||
SLOT(_DurationChanged(double)));
|
||||
QWidget::connect(_unitSelection, SIGNAL(currentIndexChanged(int)), this,
|
||||
SLOT(_UnitChanged(int)));
|
||||
|
||||
QHBoxLayout *layout = new QHBoxLayout;
|
||||
layout->setContentsMargins(0, 0, 0, 0);
|
||||
layout->setSpacing(11);
|
||||
layout->addWidget(_duration);
|
||||
if (showUnitSelection) {
|
||||
layout->addWidget(_unitSelection);
|
||||
}
|
||||
setLayout(layout);
|
||||
}
|
||||
|
||||
void DurationSelection::SetValue(double value)
|
||||
{
|
||||
_duration->setValue(value / _unitMultiplier);
|
||||
}
|
||||
|
||||
void DurationSelection::SetUnit(DurationUnit u)
|
||||
{
|
||||
_unitSelection->setCurrentIndex(static_cast<int>(u));
|
||||
}
|
||||
|
||||
void DurationSelection::SetDuration(Duration d)
|
||||
{
|
||||
SetUnit(d.displayUnit);
|
||||
SetValue(d.seconds);
|
||||
}
|
||||
|
||||
void DurationSelection::_DurationChanged(double value)
|
||||
{
|
||||
emit DurationChanged(value * _unitMultiplier);
|
||||
}
|
||||
|
||||
void DurationSelection::_UnitChanged(int idx)
|
||||
{
|
||||
DurationUnit unit = static_cast<DurationUnit>(idx);
|
||||
double prevMultiplier = _unitMultiplier;
|
||||
_unitMultiplier = durationUnitToMultiplier(unit);
|
||||
_duration->setValue(_duration->value() *
|
||||
(prevMultiplier / _unitMultiplier));
|
||||
|
||||
emit UnitChanged(unit);
|
||||
}
|
||||
|
||||
void DurationConstraint::Save(obs_data_t *obj, const char *condName,
|
||||
const char *secondsName, const char *unitName)
|
||||
{
|
||||
obs_data_set_int(obj, condName, static_cast<int>(_type));
|
||||
_dur.Save(obj, secondsName, unitName);
|
||||
}
|
||||
|
||||
void DurationConstraint::Load(obs_data_t *obj, const char *condName,
|
||||
const char *secondsName, const char *unitName)
|
||||
{
|
||||
// For backwards compatability check if duration value exist without
|
||||
// time constraint condition - if so assume DurationCondition::MORE
|
||||
if (!obs_data_has_user_value(obj, condName) &&
|
||||
obs_data_has_user_value(obj, secondsName)) {
|
||||
obs_data_set_int(obj, condName,
|
||||
static_cast<int>(DurationCondition::MORE));
|
||||
}
|
||||
|
||||
_type = static_cast<DurationCondition>(obs_data_get_int(obj, condName));
|
||||
_dur.Load(obj, secondsName, unitName);
|
||||
}
|
||||
|
||||
bool DurationConstraint::DurationReached()
|
||||
{
|
||||
switch (_type) {
|
||||
case DurationCondition::NONE:
|
||||
return true;
|
||||
break;
|
||||
case DurationCondition::MORE:
|
||||
return _dur.DurationReached();
|
||||
break;
|
||||
case DurationCondition::EQUAL:
|
||||
if (_dur.DurationReached() && !_timeReached) {
|
||||
_timeReached = true;
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case DurationCondition::LESS:
|
||||
return !_dur.DurationReached();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void DurationConstraint::Reset()
|
||||
{
|
||||
_timeReached = false;
|
||||
_dur.Reset();
|
||||
}
|
||||
|
||||
static void populateConditions(QComboBox *list)
|
||||
{
|
||||
list->addItem(
|
||||
obs_module_text("AdvSceneSwitcher.duration.condition.none"));
|
||||
list->addItem(
|
||||
obs_module_text("AdvSceneSwitcher.duration.condition.more"));
|
||||
list->addItem(
|
||||
obs_module_text("AdvSceneSwitcher.duration.condition.equal"));
|
||||
list->addItem(
|
||||
obs_module_text("AdvSceneSwitcher.duration.condition.less"));
|
||||
}
|
||||
|
||||
DurationConstraintEdit::DurationConstraintEdit(QWidget *parent)
|
||||
{
|
||||
_condition = new QComboBox(parent);
|
||||
_duration = new DurationSelection(parent);
|
||||
_toggle = new QPushButton(parent);
|
||||
_toggle->setMaximumSize(22, 22);
|
||||
_toggle->setIcon(
|
||||
QIcon(QString::fromStdString(getDataFilePath("res/time.svg"))));
|
||||
populateConditions(_condition);
|
||||
QWidget::connect(_condition, SIGNAL(currentIndexChanged(int)), this,
|
||||
SLOT(_ConditionChanged(int)));
|
||||
QObject::connect(_duration, &DurationSelection::DurationChanged, this,
|
||||
&DurationConstraintEdit::DurationChanged);
|
||||
QObject::connect(_duration, &DurationSelection::UnitChanged, this,
|
||||
&DurationConstraintEdit::UnitChanged);
|
||||
QWidget::connect(_toggle, SIGNAL(clicked()), this,
|
||||
SLOT(ToggleClicked()));
|
||||
|
||||
QHBoxLayout *layout = new QHBoxLayout;
|
||||
layout->setContentsMargins(0, 0, 0, 0);
|
||||
layout->setSpacing(11);
|
||||
layout->addWidget(_toggle);
|
||||
layout->addWidget(_condition);
|
||||
layout->addWidget(_duration);
|
||||
setLayout(layout);
|
||||
Collapse(true);
|
||||
}
|
||||
|
||||
void DurationConstraintEdit::SetValue(DurationConstraint &value)
|
||||
{
|
||||
_duration->SetDuration(value.GetDuration());
|
||||
_condition->setCurrentIndex(static_cast<int>(value.GetCondition()));
|
||||
_duration->setVisible(value.GetCondition() != DurationCondition::NONE);
|
||||
}
|
||||
|
||||
void DurationConstraintEdit::SetUnit(DurationUnit u)
|
||||
{
|
||||
_duration->SetUnit(u);
|
||||
}
|
||||
|
||||
void DurationConstraintEdit::SetDuration(const Duration &d)
|
||||
{
|
||||
_duration->SetDuration(d);
|
||||
}
|
||||
|
||||
void DurationConstraintEdit::_ConditionChanged(int value)
|
||||
{
|
||||
auto cond = static_cast<DurationCondition>(value);
|
||||
Collapse(cond == DurationCondition::NONE);
|
||||
emit ConditionChanged(cond);
|
||||
}
|
||||
|
||||
void DurationConstraintEdit::ToggleClicked()
|
||||
{
|
||||
Collapse(false);
|
||||
}
|
||||
|
||||
void DurationConstraintEdit::Collapse(bool collapse)
|
||||
{
|
||||
_toggle->setVisible(collapse);
|
||||
_duration->setVisible(!collapse);
|
||||
_condition->setVisible(!collapse);
|
||||
}
|
||||
554
src/general.cpp
554
src/general.cpp
@@ -1,10 +1,8 @@
|
||||
#include <QFileDialog>
|
||||
|
||||
#include "headers/advanced-scene-switcher.hpp"
|
||||
#include "headers/utility.hpp"
|
||||
#include "headers/version.h"
|
||||
|
||||
constexpr auto tab_count = 15;
|
||||
#include <QFileDialog>
|
||||
|
||||
QMetaObject::Connection inactivePluse;
|
||||
|
||||
@@ -61,14 +59,44 @@ void AdvSceneSwitcher::on_noMatchRandomSwitch_clicked()
|
||||
ui->randomDisabledWarning->setVisible(false);
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::on_noMatchDelay_valueChanged(double i)
|
||||
void AdvSceneSwitcher::NoMatchDelayDurationChanged(double sec)
|
||||
{
|
||||
if (loading) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
switcher->noMatchDelay = i;
|
||||
switcher->noMatchDelay.seconds = sec;
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::NoMatchDelayUnitChanged(DurationUnit unit)
|
||||
{
|
||||
if (loading) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
switcher->noMatchDelay.displayUnit = unit;
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::CooldownDurationChanged(double sec)
|
||||
{
|
||||
if (loading) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
switcher->cooldown.seconds = sec;
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::CooldownUnitChanged(DurationUnit unit)
|
||||
{
|
||||
if (loading) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
switcher->cooldown.displayUnit = unit;
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::on_startupBehavior_currentIndexChanged(int index)
|
||||
@@ -102,16 +130,6 @@ void AdvSceneSwitcher::on_noMatchSwitchScene_currentTextChanged(
|
||||
UpdateNonMatchingScene(text);
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::on_cooldownTime_valueChanged(double i)
|
||||
{
|
||||
if (loading) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
switcher->cooldown = i;
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::on_checkInterval_valueChanged(int value)
|
||||
{
|
||||
if (loading) {
|
||||
@@ -129,6 +147,7 @@ void AdvSceneSwitcher::SetStarted()
|
||||
ui->pluginRunningText->setText(
|
||||
obs_module_text("AdvSceneSwitcher.status.active"));
|
||||
ui->pluginRunningText->disconnect(inactivePluse);
|
||||
currentStatusActive = true;
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::SetStopped()
|
||||
@@ -137,8 +156,12 @@ void AdvSceneSwitcher::SetStopped()
|
||||
obs_module_text("AdvSceneSwitcher.generalTab.status.start"));
|
||||
ui->pluginRunningText->setText(
|
||||
obs_module_text("AdvSceneSwitcher.status.inactive"));
|
||||
inactivePluse = PulseWidget(ui->pluginRunningText, QColor(Qt::red),
|
||||
QColor(0, 0, 0, 0), "QLabel ");
|
||||
if (!switcher->disableHints) {
|
||||
inactivePluse = PulseWidget(ui->pluginRunningText,
|
||||
QColor(Qt::red), QColor(0, 0, 0, 0),
|
||||
"QLabel ");
|
||||
}
|
||||
currentStatusActive = false;
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::on_toggleStartButton_clicked()
|
||||
@@ -154,6 +177,9 @@ void AdvSceneSwitcher::on_toggleStartButton_clicked()
|
||||
|
||||
void AdvSceneSwitcher::closeEvent(QCloseEvent *)
|
||||
{
|
||||
switcher->windowPos = this->pos();
|
||||
switcher->windowSize = this->size();
|
||||
|
||||
obs_frontend_save();
|
||||
}
|
||||
|
||||
@@ -166,6 +192,15 @@ void AdvSceneSwitcher::on_verboseLogging_stateChanged(int state)
|
||||
switcher->verbose = state;
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::on_saveWindowGeo_stateChanged(int state)
|
||||
{
|
||||
if (loading) {
|
||||
return;
|
||||
}
|
||||
|
||||
switcher->saveWindowGeo = state;
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::on_uiHintsDisable_stateChanged(int state)
|
||||
{
|
||||
if (loading) {
|
||||
@@ -175,34 +210,6 @@ void AdvSceneSwitcher::on_uiHintsDisable_stateChanged(int 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()
|
||||
{
|
||||
QString directory = QFileDialog::getSaveFileName(
|
||||
@@ -288,45 +295,54 @@ int findTabIndex(QTabWidget *tabWidget, int pos)
|
||||
tabName = "generalTab";
|
||||
break;
|
||||
case 1:
|
||||
tabName = "transitionsTab";
|
||||
tabName = "macroTab";
|
||||
break;
|
||||
case 2:
|
||||
tabName = "pauseTab";
|
||||
tabName = "transitionsTab";
|
||||
break;
|
||||
case 3:
|
||||
tabName = "windowTitleTab";
|
||||
tabName = "pauseTab";
|
||||
break;
|
||||
case 4:
|
||||
tabName = "executableTab";
|
||||
tabName = "windowTitleTab";
|
||||
break;
|
||||
case 5:
|
||||
tabName = "screenRegionTab";
|
||||
tabName = "executableTab";
|
||||
break;
|
||||
case 6:
|
||||
tabName = "mediaTab";
|
||||
tabName = "screenRegionTab";
|
||||
break;
|
||||
case 7:
|
||||
tabName = "fileTab";
|
||||
tabName = "mediaTab";
|
||||
break;
|
||||
case 8:
|
||||
tabName = "randomTab";
|
||||
tabName = "fileTab";
|
||||
break;
|
||||
case 9:
|
||||
tabName = "timeTab";
|
||||
tabName = "randomTab";
|
||||
break;
|
||||
case 10:
|
||||
tabName = "idleTab";
|
||||
tabName = "timeTab";
|
||||
break;
|
||||
case 11:
|
||||
tabName = "sceneSequenceTab";
|
||||
tabName = "idleTab";
|
||||
break;
|
||||
case 12:
|
||||
tabName = "audioTab";
|
||||
tabName = "sceneSequenceTab";
|
||||
break;
|
||||
case 13:
|
||||
tabName = "sceneGroupTab";
|
||||
tabName = "audioTab";
|
||||
break;
|
||||
case 14:
|
||||
tabName = "videoTab";
|
||||
break;
|
||||
case 15:
|
||||
tabName = "networkTab";
|
||||
break;
|
||||
case 16:
|
||||
tabName = "sceneGroupTab";
|
||||
break;
|
||||
case 17:
|
||||
tabName = "sceneTriggerTab";
|
||||
break;
|
||||
}
|
||||
@@ -345,6 +361,10 @@ int findTabIndex(QTabWidget *tabWidget, int pos)
|
||||
|
||||
void AdvSceneSwitcher::setTabOrder()
|
||||
{
|
||||
if (!switcher->tabOrderValid()) {
|
||||
switcher->resetTabOrder();
|
||||
}
|
||||
|
||||
QTabBar *bar = ui->tabWidget->tabBar();
|
||||
for (int i = 0; i < bar->count(); ++i) {
|
||||
int curPos = findTabIndex(ui->tabWidget, switcher->tabOrder[i]);
|
||||
@@ -357,6 +377,14 @@ void AdvSceneSwitcher::setTabOrder()
|
||||
connect(bar, &QTabBar::tabMoved, this, &AdvSceneSwitcher::on_tabMoved);
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::restoreWindowGeo()
|
||||
{
|
||||
if (switcher->saveWindowGeo) {
|
||||
this->resize(switcher->windowSize);
|
||||
this->move(switcher->windowPos);
|
||||
}
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::on_tabMoved(int from, int to)
|
||||
{
|
||||
if (loading) {
|
||||
@@ -381,22 +409,25 @@ void SwitcherData::loadSettings(obs_data_t *obj)
|
||||
return;
|
||||
}
|
||||
|
||||
switcher->loadSceneGroups(obj);
|
||||
switcher->loadWindowTitleSwitches(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->loadSceneTriggers(obj);
|
||||
switcher->loadGeneralSettings(obj);
|
||||
switcher->loadHotkeys(obj);
|
||||
loadSceneGroups(obj);
|
||||
loadMacros(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)
|
||||
@@ -405,7 +436,11 @@ void SwitcherData::saveSettings(obs_data_t *obj)
|
||||
return;
|
||||
}
|
||||
|
||||
// Needs to be loaded before any entries which might rely on scene group
|
||||
// selections to be available.
|
||||
saveSceneGroups(obj);
|
||||
|
||||
saveMacros(obj);
|
||||
saveWindowTitleSwitches(obj);
|
||||
saveScreenRegionSwitches(obj);
|
||||
savePauseSwitches(obj);
|
||||
@@ -418,6 +453,8 @@ void SwitcherData::saveSettings(obs_data_t *obj)
|
||||
saveMediaSwitches(obj);
|
||||
saveTimeSwitches(obj);
|
||||
saveAudioSwitches(obj);
|
||||
saveVideoSwitches(obj);
|
||||
saveNetworkSwitches(obj);
|
||||
saveSceneTriggers(obj);
|
||||
saveGeneralSettings(obj);
|
||||
saveHotkeys(obj);
|
||||
@@ -426,104 +463,95 @@ void SwitcherData::saveSettings(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", interval);
|
||||
|
||||
std::string nonMatchingSceneName =
|
||||
GetWeakSourceName(switcher->nonMatchingScene);
|
||||
std::string nonMatchingSceneName = GetWeakSourceName(nonMatchingScene);
|
||||
obs_data_set_string(obj, "non_matching_scene",
|
||||
nonMatchingSceneName.c_str());
|
||||
obs_data_set_int(obj, "switch_if_not_matching",
|
||||
switcher->switchIfNotMatching);
|
||||
obs_data_set_double(obj, "noMatchDelay", switcher->noMatchDelay);
|
||||
obs_data_set_int(obj, "switch_if_not_matching", switchIfNotMatching);
|
||||
noMatchDelay.Save(obj, "noMatchDelay", "noMatchDelayUnit");
|
||||
|
||||
obs_data_set_double(obj, "cooldown", switcher->cooldown);
|
||||
cooldown.Save(obj, "cooldown", "cooldownUnit");
|
||||
|
||||
obs_data_set_bool(obj, "active", !switcher->stop);
|
||||
obs_data_set_int(obj, "startup_behavior", switcher->startupBehavior);
|
||||
obs_data_set_bool(obj, "active", !stop);
|
||||
obs_data_set_int(obj, "startup_behavior", startupBehavior);
|
||||
|
||||
obs_data_set_int(obj, "autoStartEvent",
|
||||
static_cast<int>(switcher->autoStartEvent));
|
||||
static_cast<int>(autoStartEvent));
|
||||
|
||||
obs_data_set_bool(obj, "verbose", switcher->verbose);
|
||||
obs_data_set_bool(obj, "disableHints", switcher->disableHints);
|
||||
obs_data_set_bool(obj, "verbose", verbose);
|
||||
obs_data_set_bool(obj, "disableHints", disableHints);
|
||||
|
||||
obs_data_set_int(obj, "priority0",
|
||||
switcher->functionNamesByPriority[0]);
|
||||
obs_data_set_int(obj, "priority1",
|
||||
switcher->functionNamesByPriority[1]);
|
||||
obs_data_set_int(obj, "priority2",
|
||||
switcher->functionNamesByPriority[2]);
|
||||
obs_data_set_int(obj, "priority3",
|
||||
switcher->functionNamesByPriority[3]);
|
||||
obs_data_set_int(obj, "priority4",
|
||||
switcher->functionNamesByPriority[4]);
|
||||
obs_data_set_int(obj, "priority5",
|
||||
switcher->functionNamesByPriority[5]);
|
||||
obs_data_set_int(obj, "priority6",
|
||||
switcher->functionNamesByPriority[6]);
|
||||
obs_data_set_int(obj, "priority7",
|
||||
switcher->functionNamesByPriority[7]);
|
||||
obs_data_set_int(obj, "priority8",
|
||||
switcher->functionNamesByPriority[8]);
|
||||
obs_data_set_int(obj, "priority0", functionNamesByPriority[0]);
|
||||
obs_data_set_int(obj, "priority1", functionNamesByPriority[1]);
|
||||
obs_data_set_int(obj, "priority2", functionNamesByPriority[2]);
|
||||
obs_data_set_int(obj, "priority3", functionNamesByPriority[3]);
|
||||
obs_data_set_int(obj, "priority4", functionNamesByPriority[4]);
|
||||
obs_data_set_int(obj, "priority5", functionNamesByPriority[5]);
|
||||
obs_data_set_int(obj, "priority6", functionNamesByPriority[6]);
|
||||
obs_data_set_int(obj, "priority7", functionNamesByPriority[7]);
|
||||
obs_data_set_int(obj, "priority8", functionNamesByPriority[8]);
|
||||
obs_data_set_int(obj, "priority9", functionNamesByPriority[9]);
|
||||
obs_data_set_int(obj, "priority10", functionNamesByPriority[10]);
|
||||
|
||||
obs_data_set_int(obj, "threadPriority", switcher->threadPriority);
|
||||
obs_data_set_int(obj, "threadPriority", threadPriority);
|
||||
|
||||
// After fresh install of OBS the vector can be empty
|
||||
// as save() might be called before first load()
|
||||
if (switcher->tabOrder.size() < tab_count) {
|
||||
switcher->tabOrder = std::vector<int>(tab_count);
|
||||
std::iota(switcher->tabOrder.begin(), switcher->tabOrder.end(),
|
||||
0);
|
||||
}
|
||||
obs_data_set_int(obj, "generalTabPos", tabOrder[0]);
|
||||
obs_data_set_int(obj, "macroTabPos", tabOrder[1]);
|
||||
obs_data_set_int(obj, "transitionTabPos", tabOrder[2]);
|
||||
obs_data_set_int(obj, "pauseTabPos", tabOrder[3]);
|
||||
obs_data_set_int(obj, "titleTabPos", tabOrder[4]);
|
||||
obs_data_set_int(obj, "exeTabPos", tabOrder[5]);
|
||||
obs_data_set_int(obj, "regionTabPos", tabOrder[6]);
|
||||
obs_data_set_int(obj, "mediaTabPos", tabOrder[7]);
|
||||
obs_data_set_int(obj, "fileTabPos", tabOrder[8]);
|
||||
obs_data_set_int(obj, "randomTabPos", tabOrder[9]);
|
||||
obs_data_set_int(obj, "timeTabPos", tabOrder[10]);
|
||||
obs_data_set_int(obj, "idleTabPos", tabOrder[11]);
|
||||
obs_data_set_int(obj, "sequenceTabPos", tabOrder[12]);
|
||||
obs_data_set_int(obj, "audioTabPos", tabOrder[13]);
|
||||
obs_data_set_int(obj, "videoTabPos", tabOrder[14]);
|
||||
obs_data_set_int(obj, "networkTabPos", tabOrder[15]);
|
||||
obs_data_set_int(obj, "sceneGroupTabPos", tabOrder[16]);
|
||||
obs_data_set_int(obj, "triggerTabPos", tabOrder[17]);
|
||||
|
||||
obs_data_set_int(obj, "generalTabPos", switcher->tabOrder[0]);
|
||||
obs_data_set_int(obj, "transitionTabPos", switcher->tabOrder[1]);
|
||||
obs_data_set_int(obj, "pauseTabPos", switcher->tabOrder[2]);
|
||||
obs_data_set_int(obj, "titleTabPos", switcher->tabOrder[3]);
|
||||
obs_data_set_int(obj, "exeTabPos", switcher->tabOrder[4]);
|
||||
obs_data_set_int(obj, "regionTabPos", switcher->tabOrder[5]);
|
||||
obs_data_set_int(obj, "mediaTabPos", switcher->tabOrder[6]);
|
||||
obs_data_set_int(obj, "fileTabPos", switcher->tabOrder[7]);
|
||||
obs_data_set_int(obj, "randomTabPos", switcher->tabOrder[8]);
|
||||
obs_data_set_int(obj, "timeTabPos", switcher->tabOrder[9]);
|
||||
obs_data_set_int(obj, "idleTabPos", switcher->tabOrder[10]);
|
||||
obs_data_set_int(obj, "sequenceTabPos", switcher->tabOrder[11]);
|
||||
obs_data_set_int(obj, "audioTabPos", switcher->tabOrder[12]);
|
||||
obs_data_set_int(obj, "sceneGroupTabPos", switcher->tabOrder[13]);
|
||||
obs_data_set_int(obj, "triggerTabPos", switcher->tabOrder[14]);
|
||||
obs_data_set_bool(obj, "saveWindowGeo", saveWindowGeo);
|
||||
obs_data_set_int(obj, "windowPosX", windowPos.x());
|
||||
obs_data_set_int(obj, "windowPosY", windowPos.y());
|
||||
obs_data_set_int(obj, "windowWidth", windowSize.width());
|
||||
obs_data_set_int(obj, "windowHeight", windowSize.height());
|
||||
}
|
||||
|
||||
void SwitcherData::loadGeneralSettings(obs_data_t *obj)
|
||||
{
|
||||
obs_data_set_default_int(obj, "interval", default_interval);
|
||||
switcher->interval = obs_data_get_int(obj, "interval");
|
||||
interval = obs_data_get_int(obj, "interval");
|
||||
|
||||
obs_data_set_default_int(obj, "switch_if_not_matching", NO_SWITCH);
|
||||
switcher->switchIfNotMatching =
|
||||
switchIfNotMatching =
|
||||
(NoMatch)obs_data_get_int(obj, "switch_if_not_matching");
|
||||
std::string nonMatchingScene =
|
||||
std::string nonMatchingSceneName =
|
||||
obs_data_get_string(obj, "non_matching_scene");
|
||||
switcher->nonMatchingScene =
|
||||
GetWeakSourceByName(nonMatchingScene.c_str());
|
||||
switcher->noMatchDelay = obs_data_get_double(obj, "noMatchDelay");
|
||||
nonMatchingScene = GetWeakSourceByName(nonMatchingSceneName.c_str());
|
||||
noMatchDelay.Load(obj, "noMatchDelay", "noMatchDelayUnit");
|
||||
|
||||
switcher->cooldown = obs_data_get_double(obj, "cooldown");
|
||||
cooldown.Load(obj, "cooldown", "cooldownUnit");
|
||||
|
||||
switcher->stop = !obs_data_get_bool(obj, "active");
|
||||
switcher->startupBehavior =
|
||||
stop = !obs_data_get_bool(obj, "active");
|
||||
startupBehavior =
|
||||
(StartupBehavior)obs_data_get_int(obj, "startup_behavior");
|
||||
if (switcher->startupBehavior == START) {
|
||||
switcher->stop = false;
|
||||
if (startupBehavior == START) {
|
||||
stop = false;
|
||||
}
|
||||
if (switcher->startupBehavior == STOP) {
|
||||
switcher->stop = true;
|
||||
if (startupBehavior == STOP) {
|
||||
stop = true;
|
||||
}
|
||||
|
||||
switcher->autoStartEvent = static_cast<AutoStartEvent>(
|
||||
autoStartEvent = static_cast<AutoStartEvent>(
|
||||
obs_data_get_int(obj, "autoStartEvent"));
|
||||
|
||||
switcher->verbose = obs_data_get_bool(obj, "verbose");
|
||||
switcher->disableHints = obs_data_get_bool(obj, "disableHints");
|
||||
verbose = obs_data_get_bool(obj, "verbose");
|
||||
disableHints = obs_data_get_bool(obj, "disableHints");
|
||||
|
||||
obs_data_set_default_int(obj, "priority0", default_priority_0);
|
||||
obs_data_set_default_int(obj, "priority1", default_priority_1);
|
||||
@@ -535,98 +563,114 @@ void SwitcherData::loadGeneralSettings(obs_data_t *obj)
|
||||
obs_data_set_default_int(obj, "priority7", default_priority_7);
|
||||
obs_data_set_default_int(obj, "priority8", default_priority_8);
|
||||
|
||||
switcher->functionNamesByPriority[0] =
|
||||
(obs_data_get_int(obj, "priority0"));
|
||||
switcher->functionNamesByPriority[1] =
|
||||
(obs_data_get_int(obj, "priority1"));
|
||||
switcher->functionNamesByPriority[2] =
|
||||
(obs_data_get_int(obj, "priority2"));
|
||||
switcher->functionNamesByPriority[3] =
|
||||
(obs_data_get_int(obj, "priority3"));
|
||||
switcher->functionNamesByPriority[4] =
|
||||
(obs_data_get_int(obj, "priority4"));
|
||||
switcher->functionNamesByPriority[5] =
|
||||
(obs_data_get_int(obj, "priority5"));
|
||||
switcher->functionNamesByPriority[6] =
|
||||
(obs_data_get_int(obj, "priority6"));
|
||||
switcher->functionNamesByPriority[7] =
|
||||
(obs_data_get_int(obj, "priority7"));
|
||||
switcher->functionNamesByPriority[8] =
|
||||
(obs_data_get_int(obj, "priority8"));
|
||||
if (!switcher->prioFuncsValid()) {
|
||||
switcher->functionNamesByPriority[0] = (default_priority_0);
|
||||
switcher->functionNamesByPriority[1] = (default_priority_1);
|
||||
switcher->functionNamesByPriority[2] = (default_priority_2);
|
||||
switcher->functionNamesByPriority[3] = (default_priority_3);
|
||||
switcher->functionNamesByPriority[4] = (default_priority_4);
|
||||
switcher->functionNamesByPriority[5] = (default_priority_5);
|
||||
switcher->functionNamesByPriority[6] = (default_priority_6);
|
||||
switcher->functionNamesByPriority[7] = (default_priority_7);
|
||||
switcher->functionNamesByPriority[8] = (default_priority_8);
|
||||
functionNamesByPriority[0] = (obs_data_get_int(obj, "priority0"));
|
||||
functionNamesByPriority[1] = (obs_data_get_int(obj, "priority1"));
|
||||
functionNamesByPriority[2] = (obs_data_get_int(obj, "priority2"));
|
||||
functionNamesByPriority[3] = (obs_data_get_int(obj, "priority3"));
|
||||
functionNamesByPriority[4] = (obs_data_get_int(obj, "priority4"));
|
||||
functionNamesByPriority[5] = (obs_data_get_int(obj, "priority5"));
|
||||
functionNamesByPriority[6] = (obs_data_get_int(obj, "priority6"));
|
||||
functionNamesByPriority[7] = (obs_data_get_int(obj, "priority7"));
|
||||
functionNamesByPriority[8] = (obs_data_get_int(obj, "priority8"));
|
||||
functionNamesByPriority[9] = (obs_data_get_int(obj, "priority9"));
|
||||
functionNamesByPriority[10] = (obs_data_get_int(obj, "priority10"));
|
||||
if (!prioFuncsValid()) {
|
||||
functionNamesByPriority[0] = (default_priority_0);
|
||||
functionNamesByPriority[1] = (default_priority_1);
|
||||
functionNamesByPriority[2] = (default_priority_2);
|
||||
functionNamesByPriority[3] = (default_priority_3);
|
||||
functionNamesByPriority[4] = (default_priority_4);
|
||||
functionNamesByPriority[5] = (default_priority_5);
|
||||
functionNamesByPriority[6] = (default_priority_6);
|
||||
functionNamesByPriority[7] = (default_priority_7);
|
||||
functionNamesByPriority[8] = (default_priority_8);
|
||||
functionNamesByPriority[9] = (default_priority_9);
|
||||
functionNamesByPriority[10] = (default_priority_10);
|
||||
}
|
||||
|
||||
obs_data_set_default_int(obj, "threadPriority",
|
||||
QThread::NormalPriority);
|
||||
switcher->threadPriority = obs_data_get_int(obj, "threadPriority");
|
||||
threadPriority = obs_data_get_int(obj, "threadPriority");
|
||||
|
||||
obs_data_set_default_int(obj, "generalTabPos", 0);
|
||||
obs_data_set_default_int(obj, "transitionTabPos", 1);
|
||||
obs_data_set_default_int(obj, "pauseTabPos", 2);
|
||||
obs_data_set_default_int(obj, "titleTabPos", 3);
|
||||
obs_data_set_default_int(obj, "exeTabPos", 4);
|
||||
obs_data_set_default_int(obj, "regionTabPos", 5);
|
||||
obs_data_set_default_int(obj, "mediaTabPos", 6);
|
||||
obs_data_set_default_int(obj, "fileTabPos", 7);
|
||||
obs_data_set_default_int(obj, "randomTabPos", 8);
|
||||
obs_data_set_default_int(obj, "timeTabPos", 9);
|
||||
obs_data_set_default_int(obj, "idleTabPos", 10);
|
||||
obs_data_set_default_int(obj, "sequenceTabPos", 11);
|
||||
obs_data_set_default_int(obj, "audioTabPos", 12);
|
||||
obs_data_set_default_int(obj, "sceneGroupTabPos", 13);
|
||||
obs_data_set_default_int(obj, "triggerTabPos", 14);
|
||||
obs_data_set_default_int(obj, "macroTabPos", 1);
|
||||
obs_data_set_default_int(obj, "transitionTabPos", 2);
|
||||
obs_data_set_default_int(obj, "pauseTabPos", 3);
|
||||
obs_data_set_default_int(obj, "titleTabPos", 4);
|
||||
obs_data_set_default_int(obj, "exeTabPos", 5);
|
||||
obs_data_set_default_int(obj, "regionTabPos", 6);
|
||||
obs_data_set_default_int(obj, "mediaTabPos", 7);
|
||||
obs_data_set_default_int(obj, "fileTabPos", 8);
|
||||
obs_data_set_default_int(obj, "randomTabPos", 9);
|
||||
obs_data_set_default_int(obj, "timeTabPos", 10);
|
||||
obs_data_set_default_int(obj, "idleTabPos", 11);
|
||||
obs_data_set_default_int(obj, "sequenceTabPos", 12);
|
||||
obs_data_set_default_int(obj, "audioTabPos", 13);
|
||||
obs_data_set_default_int(obj, "videoTabPos", 14);
|
||||
obs_data_set_default_int(obj, "networkTabPos", 15);
|
||||
obs_data_set_default_int(obj, "sceneGroupTabPos", 16);
|
||||
obs_data_set_default_int(obj, "triggerTabPos", 17);
|
||||
|
||||
switcher->tabOrder.emplace_back(
|
||||
(int)(obs_data_get_int(obj, "generalTabPos")));
|
||||
switcher->tabOrder.emplace_back(
|
||||
(int)(obs_data_get_int(obj, "transitionTabPos")));
|
||||
switcher->tabOrder.emplace_back(
|
||||
(int)(obs_data_get_int(obj, "pauseTabPos")));
|
||||
switcher->tabOrder.emplace_back(
|
||||
(int)(obs_data_get_int(obj, "titleTabPos")));
|
||||
switcher->tabOrder.emplace_back(
|
||||
(int)(obs_data_get_int(obj, "exeTabPos")));
|
||||
switcher->tabOrder.emplace_back(
|
||||
(int)(obs_data_get_int(obj, "regionTabPos")));
|
||||
switcher->tabOrder.emplace_back(
|
||||
(int)(obs_data_get_int(obj, "mediaTabPos")));
|
||||
switcher->tabOrder.emplace_back(
|
||||
(int)(obs_data_get_int(obj, "fileTabPos")));
|
||||
switcher->tabOrder.emplace_back(
|
||||
(int)(obs_data_get_int(obj, "randomTabPos")));
|
||||
switcher->tabOrder.emplace_back(
|
||||
(int)(obs_data_get_int(obj, "timeTabPos")));
|
||||
switcher->tabOrder.emplace_back(
|
||||
(int)(obs_data_get_int(obj, "idleTabPos")));
|
||||
switcher->tabOrder.emplace_back(
|
||||
(int)(obs_data_get_int(obj, "sequenceTabPos")));
|
||||
switcher->tabOrder.emplace_back(
|
||||
(int)(obs_data_get_int(obj, "audioTabPos")));
|
||||
switcher->tabOrder.emplace_back(
|
||||
(int)(obs_data_get_int(obj, "sceneGroupTabPos")));
|
||||
switcher->tabOrder.emplace_back(
|
||||
(int)(obs_data_get_int(obj, "triggerTabPos")));
|
||||
tabOrder.clear();
|
||||
tabOrder.emplace_back((int)(obs_data_get_int(obj, "generalTabPos")));
|
||||
tabOrder.emplace_back((int)(obs_data_get_int(obj, "macroTabPos")));
|
||||
tabOrder.emplace_back((int)(obs_data_get_int(obj, "transitionTabPos")));
|
||||
tabOrder.emplace_back((int)(obs_data_get_int(obj, "pauseTabPos")));
|
||||
tabOrder.emplace_back((int)(obs_data_get_int(obj, "titleTabPos")));
|
||||
tabOrder.emplace_back((int)(obs_data_get_int(obj, "exeTabPos")));
|
||||
tabOrder.emplace_back((int)(obs_data_get_int(obj, "regionTabPos")));
|
||||
tabOrder.emplace_back((int)(obs_data_get_int(obj, "mediaTabPos")));
|
||||
tabOrder.emplace_back((int)(obs_data_get_int(obj, "fileTabPos")));
|
||||
tabOrder.emplace_back((int)(obs_data_get_int(obj, "randomTabPos")));
|
||||
tabOrder.emplace_back((int)(obs_data_get_int(obj, "timeTabPos")));
|
||||
tabOrder.emplace_back((int)(obs_data_get_int(obj, "idleTabPos")));
|
||||
tabOrder.emplace_back((int)(obs_data_get_int(obj, "sequenceTabPos")));
|
||||
tabOrder.emplace_back((int)(obs_data_get_int(obj, "audioTabPos")));
|
||||
tabOrder.emplace_back((int)(obs_data_get_int(obj, "videoTabPos")));
|
||||
tabOrder.emplace_back((int)(obs_data_get_int(obj, "networkTabPos")));
|
||||
tabOrder.emplace_back((int)(obs_data_get_int(obj, "sceneGroupTabPos")));
|
||||
tabOrder.emplace_back((int)(obs_data_get_int(obj, "triggerTabPos")));
|
||||
|
||||
if (!tabOrderValid()) {
|
||||
resetTabOrder();
|
||||
}
|
||||
|
||||
saveWindowGeo = obs_data_get_bool(obj, "saveWindowGeo");
|
||||
windowPos = {(int)obs_data_get_int(obj, "windowPosX"),
|
||||
(int)obs_data_get_int(obj, "windowPosY")};
|
||||
windowSize = {(int)obs_data_get_int(obj, "windowWidth"),
|
||||
(int)obs_data_get_int(obj, "windowHeight")};
|
||||
}
|
||||
|
||||
bool SwitcherData::tabOrderValid()
|
||||
{
|
||||
auto tmp = std::vector<int>(tab_count);
|
||||
std::iota(tmp.begin(), tmp.end(), 0);
|
||||
|
||||
for (auto &p : tmp) {
|
||||
auto it = std::find(tabOrder.begin(), tabOrder.end(), p);
|
||||
if (it == tabOrder.end()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void SwitcherData::resetTabOrder()
|
||||
{
|
||||
tabOrder = std::vector<int>(tab_count);
|
||||
std::iota(tabOrder.begin(), tabOrder.end(), 0);
|
||||
}
|
||||
|
||||
void SwitcherData::checkNoMatchSwitch(bool &match, OBSWeakSource &scene,
|
||||
OBSWeakSource &transition, int &sleep)
|
||||
{
|
||||
if (match) {
|
||||
noMatchCount = 0;
|
||||
noMatchDelay.Reset();
|
||||
return;
|
||||
}
|
||||
|
||||
if ((noMatchCount * interval) / 1000.0 < noMatchDelay) {
|
||||
noMatchCount++;
|
||||
if (!noMatchDelay.DurationReached()) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -636,27 +680,22 @@ void SwitcherData::checkNoMatchSwitch(bool &match, OBSWeakSource &scene,
|
||||
transition = nullptr;
|
||||
}
|
||||
if (switchIfNotMatching == RANDOM_SWITCH) {
|
||||
checkRandom(match, scene, transition, sleep);
|
||||
match = checkRandom(scene, transition, sleep);
|
||||
}
|
||||
}
|
||||
|
||||
void SwitcherData::checkSwitchCooldown(bool &match)
|
||||
{
|
||||
if (!match || cooldown == 0.) {
|
||||
if (!match) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto now = std::chrono::high_resolution_clock::now();
|
||||
auto timePassed = std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||
now - lastMatchTime);
|
||||
|
||||
if (timePassed.count() > cooldown * 1000) {
|
||||
lastMatchTime = now;
|
||||
return;
|
||||
if (cooldown.DurationReached()) {
|
||||
cooldown.Reset();
|
||||
} else {
|
||||
match = false;
|
||||
vblog(LOG_INFO, "cooldown active - ignoring match");
|
||||
}
|
||||
|
||||
match = false;
|
||||
vblog(LOG_INFO, "cooldown active - ignoring match");
|
||||
}
|
||||
|
||||
void populateStartupBehavior(QComboBox *cb)
|
||||
@@ -681,6 +720,21 @@ void populateAutoStartEventSelection(QComboBox *cb)
|
||||
"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()
|
||||
{
|
||||
populateSceneSelection(ui->noMatchSwitchScene, false);
|
||||
@@ -697,16 +751,32 @@ void AdvSceneSwitcher::setupGeneralTab()
|
||||
}
|
||||
ui->noMatchSwitchScene->setCurrentText(
|
||||
GetWeakSourceName(switcher->nonMatchingScene).c_str());
|
||||
ui->noMatchDelay->setValue(switcher->noMatchDelay);
|
||||
ui->noMatchDelay->setToolTip(obs_module_text(
|
||||
|
||||
DurationSelection *noMatchDelay = new DurationSelection();
|
||||
noMatchDelay->SetDuration(switcher->noMatchDelay);
|
||||
noMatchDelay->setToolTip(obs_module_text(
|
||||
"AdvSceneSwitcher.generalTab.generalBehavior.onNoMetDelayTooltip"));
|
||||
ui->noMatchLayout->addWidget(noMatchDelay);
|
||||
QWidget::connect(noMatchDelay, SIGNAL(DurationChanged(double)), this,
|
||||
SLOT(NoMatchDelayDurationChanged(double)));
|
||||
QWidget::connect(noMatchDelay, SIGNAL(UnitChanged(DurationUnit)), this,
|
||||
SLOT(NoMatchDelayUnitChanged(DurationUnit)));
|
||||
|
||||
ui->checkInterval->setValue(switcher->interval);
|
||||
|
||||
ui->cooldownTime->setValue(switcher->cooldown);
|
||||
ui->cooldownTime->setToolTip(obs_module_text(
|
||||
DurationSelection *cooldownTime = new DurationSelection();
|
||||
cooldownTime->SetDuration(switcher->cooldown);
|
||||
cooldownTime->setToolTip(obs_module_text(
|
||||
"AdvSceneSwitcher.generalTab.generalBehavior.cooldownHint"));
|
||||
ui->cooldownLayout->addWidget(cooldownTime);
|
||||
ui->cooldownLayout->addStretch();
|
||||
QWidget::connect(cooldownTime, SIGNAL(DurationChanged(double)), this,
|
||||
SLOT(CooldownDurationChanged(double)));
|
||||
QWidget::connect(cooldownTime, SIGNAL(UnitChanged(DurationUnit)), this,
|
||||
SLOT(CooldownUnitChanged(DurationUnit)));
|
||||
|
||||
ui->verboseLogging->setChecked(switcher->verbose);
|
||||
ui->saveWindowGeo->setChecked(switcher->saveWindowGeo);
|
||||
ui->uiHintsDisable->setChecked(switcher->disableHints);
|
||||
|
||||
for (int p : switcher->functionNamesByPriority) {
|
||||
@@ -748,6 +818,14 @@ void AdvSceneSwitcher::setupGeneralTab()
|
||||
s = obs_module_text(
|
||||
"AdvSceneSwitcher.generalTab.priority.audio");
|
||||
break;
|
||||
case video_func:
|
||||
s = obs_module_text(
|
||||
"AdvSceneSwitcher.generalTab.priority.video");
|
||||
break;
|
||||
case macro_func:
|
||||
s = obs_module_text(
|
||||
"AdvSceneSwitcher.generalTab.priority.macro");
|
||||
break;
|
||||
}
|
||||
QString text(s.c_str());
|
||||
QListWidgetItem *item =
|
||||
@@ -780,4 +858,10 @@ void AdvSceneSwitcher::setupGeneralTab()
|
||||
} else {
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -5,11 +5,12 @@
|
||||
#include "ui_advanced-scene-switcher.h"
|
||||
#endif
|
||||
#include "switcher-data-structs.hpp"
|
||||
#include "platform-funcs.hpp"
|
||||
|
||||
#define blog(level, msg, ...) blog(level, "[adv-ss] " msg, ##__VA_ARGS__)
|
||||
#define vblog(level, msg, ...) \
|
||||
if (verbose) { \
|
||||
blog(level, "[adv-ss] " msg, ##__VA_ARGS__); \
|
||||
#define vblog(level, msg, ...) \
|
||||
if (switcher->verbose) { \
|
||||
blog(level, msg, ##__VA_ARGS__); \
|
||||
}
|
||||
|
||||
class QCloseEvent;
|
||||
@@ -23,6 +24,7 @@ class AdvSceneSwitcher : public QDialog {
|
||||
public:
|
||||
std::unique_ptr<Ui_AdvSceneSwitcher> ui;
|
||||
bool loading = true;
|
||||
bool currentStatusActive = false;
|
||||
|
||||
AdvSceneSwitcher(QWidget *parent);
|
||||
|
||||
@@ -39,8 +41,13 @@ public:
|
||||
int IgnoreIdleWindowsFindByData(const QString &window);
|
||||
|
||||
void UpdateNonMatchingScene(const QString &name);
|
||||
void OpenSequenceExtendEdit(SequenceWidget *sw);
|
||||
void SetEditSceneGroup(SceneGroup &sg);
|
||||
|
||||
bool addNewMacro(std::string &name);
|
||||
Macro *getSelectedMacro();
|
||||
void SetEditMacro(Macro &m);
|
||||
|
||||
void loadUI();
|
||||
void setupGeneralTab();
|
||||
void setupTitleTab();
|
||||
@@ -57,38 +64,16 @@ public:
|
||||
void setupAudioTab();
|
||||
void setupSceneGroupTab();
|
||||
void setupTriggerTab();
|
||||
void setupVideoTab();
|
||||
void setupNetworkTab();
|
||||
void setupMacroTab();
|
||||
void setTabOrder();
|
||||
|
||||
static bool DisplayMessage(QString msg, bool question = false);
|
||||
static void AskBackup(obs_data_t *obj);
|
||||
|
||||
static void populateSceneSelection(QComboBox *sel,
|
||||
bool addPrevious = false,
|
||||
bool addSceneGroup = false,
|
||||
bool addSelect = true,
|
||||
std::string selectText = "");
|
||||
|
||||
static void populateTransitionSelection(QComboBox *sel,
|
||||
bool addSelect = true);
|
||||
static void populateWindowSelection(QComboBox *sel,
|
||||
bool addSelect = true);
|
||||
static void populateAudioSelection(QComboBox *sel,
|
||||
bool addSelect = true);
|
||||
static void populateMediaSelection(QComboBox *sel,
|
||||
bool addSelect = true);
|
||||
static void populateProcessSelection(QComboBox *sel,
|
||||
bool addSelect = true);
|
||||
QMetaObject::Connection PulseWidget(QWidget *widget, QColor endColor,
|
||||
QColor = QColor(0, 0, 0, 0),
|
||||
QString specifier = "QLabel ");
|
||||
|
||||
void listAddClicked(QListWidget *list, SwitchWidget *newWidget,
|
||||
QPushButton *addButton = nullptr,
|
||||
QMetaObject::Connection *addHighlight = nullptr);
|
||||
bool listMoveUp(QListWidget *list);
|
||||
bool listMoveDown(QListWidget *list);
|
||||
void restoreWindowGeo();
|
||||
|
||||
signals:
|
||||
void MacroAdded(const QString &name);
|
||||
void MacroRemoved(const QString &name);
|
||||
void MacroRenamed(const QString &oldName, const QString newName);
|
||||
void SceneGroupAdded(const QString &name);
|
||||
void SceneGroupRemoved(const QString &name);
|
||||
void SceneGroupRenamed(const QString &oldName, const QString newName);
|
||||
@@ -101,8 +86,10 @@ public slots:
|
||||
void on_noMatchDontSwitch_clicked();
|
||||
void on_noMatchSwitch_clicked();
|
||||
void on_noMatchRandomSwitch_clicked();
|
||||
void on_noMatchDelay_valueChanged(double i);
|
||||
void on_cooldownTime_valueChanged(double i);
|
||||
void NoMatchDelayDurationChanged(double);
|
||||
void NoMatchDelayUnitChanged(DurationUnit);
|
||||
void CooldownDurationChanged(double);
|
||||
void CooldownUnitChanged(DurationUnit);
|
||||
void on_startupBehavior_currentIndexChanged(int index);
|
||||
void on_autoStartEvent_currentIndexChanged(int index);
|
||||
void on_noMatchSwitchScene_currentTextChanged(const QString &text);
|
||||
@@ -111,6 +98,20 @@ public slots:
|
||||
void on_tabMoved(int from, int to);
|
||||
void on_tabWidget_currentChanged(int index);
|
||||
|
||||
void on_macroAdd_clicked();
|
||||
void on_macroRemove_clicked();
|
||||
void on_macroUp_clicked();
|
||||
void on_macroDown_clicked();
|
||||
void on_macroName_editingFinished();
|
||||
void on_macros_currentRowChanged(int idx);
|
||||
void on_macros_itemChanged(QListWidgetItem *);
|
||||
void on_conditionAdd_clicked();
|
||||
void on_conditionRemove_clicked();
|
||||
void on_actionAdd_clicked();
|
||||
void on_actionRemove_clicked();
|
||||
void showMacroContextMenu(const QPoint &);
|
||||
void copyMacro();
|
||||
|
||||
void on_screenRegionSwitches_currentRowChanged(int idx);
|
||||
void on_showFrame_clicked();
|
||||
void on_screenRegionAdd_clicked();
|
||||
@@ -129,12 +130,15 @@ public slots:
|
||||
|
||||
void on_sceneSequenceAdd_clicked();
|
||||
void on_sceneSequenceRemove_clicked();
|
||||
void on_sceneSequenceSave_clicked();
|
||||
void on_sceneSequenceLoad_clicked();
|
||||
void on_sceneSequenceUp_clicked();
|
||||
void on_sceneSequenceDown_clicked();
|
||||
void on_sceneSequenceSave_clicked();
|
||||
void on_sceneSequenceLoad_clicked();
|
||||
void on_sequenceEdit_clicked();
|
||||
void on_sceneSequenceSwitches_itemDoubleClicked(QListWidgetItem *item);
|
||||
|
||||
void on_verboseLogging_stateChanged(int state);
|
||||
void on_saveWindowGeo_stateChanged(int state);
|
||||
void on_uiHintsDisable_stateChanged(int state);
|
||||
|
||||
void on_exportSettings_clicked();
|
||||
@@ -149,6 +153,8 @@ public slots:
|
||||
void on_defaultTransitionsUp_clicked();
|
||||
void on_defaultTransitionsDown_clicked();
|
||||
void on_transitionOverridecheckBox_stateChanged(int state);
|
||||
void on_adjustActiveTransitionType_stateChanged(int state);
|
||||
void defTransitionDelayValueChanged(int value);
|
||||
|
||||
void on_browseButton_clicked();
|
||||
void on_readFileCheckBox_stateChanged(int state);
|
||||
@@ -191,6 +197,26 @@ public slots:
|
||||
void on_audioDown_clicked();
|
||||
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_sendSceneChange_stateChanged(int state);
|
||||
void on_restrictSend_stateChanged(int state);
|
||||
void on_sendPreview_stateChanged(int state);
|
||||
void on_clientReconnect_clicked();
|
||||
void updateClientStatus();
|
||||
|
||||
void on_sceneGroupAdd_clicked();
|
||||
void on_sceneGroupRemove_clicked();
|
||||
void on_sceneGroupUp_clicked();
|
||||
@@ -213,49 +239,24 @@ public slots:
|
||||
void on_threadPriority_currentTextChanged(const QString &text);
|
||||
|
||||
void updateScreenRegionCursorPos();
|
||||
void updateStatus();
|
||||
|
||||
void on_close_clicked();
|
||||
|
||||
private:
|
||||
};
|
||||
|
||||
/******************************************************************************
|
||||
* Windowtitle helper
|
||||
******************************************************************************/
|
||||
void GetWindowList(std::vector<std::string> &windows);
|
||||
void GetWindowList(QStringList &windows);
|
||||
void GetCurrentWindowTitle(std::string &title);
|
||||
bool isFullscreen(std::string &title);
|
||||
bool isMaximized(std::string &title);
|
||||
|
||||
/******************************************************************************
|
||||
* Screenregion helper
|
||||
******************************************************************************/
|
||||
std::pair<int, int> getCursorPos();
|
||||
|
||||
/******************************************************************************
|
||||
* Idle detection helper
|
||||
******************************************************************************/
|
||||
int secondsSinceLastInput();
|
||||
|
||||
/******************************************************************************
|
||||
* Executable helper
|
||||
******************************************************************************/
|
||||
void GetProcessList(QStringList &processes);
|
||||
bool isInFocus(const QString &executable);
|
||||
|
||||
/******************************************************************************
|
||||
* Sceneswitch helper
|
||||
******************************************************************************/
|
||||
|
||||
void setNextTransition(OBSWeakSource &targetScene, obs_source_t *currentSource,
|
||||
OBSWeakSource &transition,
|
||||
bool &transitionOverrideOverride, transitionData &td);
|
||||
void overwriteTransitionOverride(obs_weak_source_t *sceneWs,
|
||||
obs_source_t *transition, transitionData &td);
|
||||
void restoreTransitionOverride(obs_source_t *scene, transitionData td);
|
||||
void switchScene(OBSWeakSource &scene, OBSWeakSource &transition,
|
||||
bool &transitionOverrideOverride);
|
||||
void setNextTransition(const sceneSwitchInfo &ssi, obs_source_t *currentSource,
|
||||
transitionData &td);
|
||||
void overwriteTransitionOverride(const sceneSwitchInfo &ssi,
|
||||
transitionData &td);
|
||||
void restoreTransitionOverride(obs_source_t *scene, const transitionData &td);
|
||||
void switchScene(const sceneSwitchInfo &ssi);
|
||||
void switchPreviewScene(const OBSWeakSource &ws);
|
||||
|
||||
/******************************************************************************
|
||||
* Main SwitcherData
|
||||
|
||||
110
src/headers/duration-control.hpp
Normal file
110
src/headers/duration-control.hpp
Normal file
@@ -0,0 +1,110 @@
|
||||
#pragma once
|
||||
#include <QWidget>
|
||||
#include <QDoubleSpinBox>
|
||||
#include <QComboBox>
|
||||
#include <QPushButton>
|
||||
#include <chrono>
|
||||
|
||||
#include "obs-data.h"
|
||||
|
||||
enum class DurationUnit {
|
||||
SECONDS,
|
||||
MINUTES,
|
||||
HOURS,
|
||||
};
|
||||
|
||||
class Duration {
|
||||
public:
|
||||
void Save(obs_data_t *obj, const char *secondsName = "seconds",
|
||||
const char *unitName = "displayUnit");
|
||||
void Load(obs_data_t *obj, const char *secondsName = "seconds",
|
||||
const char *unitName = "displayUnit");
|
||||
|
||||
bool DurationReached();
|
||||
void Reset();
|
||||
std::string ToString();
|
||||
|
||||
double seconds = 0.;
|
||||
// only used for UI
|
||||
DurationUnit displayUnit = DurationUnit::SECONDS;
|
||||
|
||||
private:
|
||||
std::chrono::high_resolution_clock::time_point _startTime;
|
||||
};
|
||||
|
||||
class DurationSelection : public QWidget {
|
||||
Q_OBJECT
|
||||
public:
|
||||
DurationSelection(QWidget *parent = nullptr,
|
||||
bool showUnitSelection = true);
|
||||
void SetValue(double value);
|
||||
void SetUnit(DurationUnit u);
|
||||
void SetDuration(Duration d);
|
||||
|
||||
private slots:
|
||||
void _DurationChanged(double value);
|
||||
void _UnitChanged(int idx);
|
||||
signals:
|
||||
void DurationChanged(double value); // always reutrn value in seconds
|
||||
void UnitChanged(DurationUnit u);
|
||||
|
||||
private:
|
||||
QDoubleSpinBox *_duration;
|
||||
QComboBox *_unitSelection;
|
||||
|
||||
double _unitMultiplier;
|
||||
};
|
||||
|
||||
enum class DurationCondition {
|
||||
NONE,
|
||||
MORE,
|
||||
EQUAL,
|
||||
LESS,
|
||||
};
|
||||
|
||||
class DurationConstraint {
|
||||
public:
|
||||
void Save(obs_data_t *obj, const char *condName = "time_constraint",
|
||||
const char *secondsName = "seconds",
|
||||
const char *unitName = "displayUnit");
|
||||
void Load(obs_data_t *obj, const char *condName = "time_constraint",
|
||||
const char *secondsName = "seconds",
|
||||
const char *unitName = "displayUnit");
|
||||
void SetCondition(DurationCondition cond) { _type = cond; }
|
||||
void SetDuration(const Duration &dur) { _dur = dur; }
|
||||
void SetValue(double value) { _dur.seconds = value; }
|
||||
void SetUnit(DurationUnit u) { _dur.displayUnit = u; }
|
||||
DurationCondition GetCondition() { return _type; }
|
||||
Duration GetDuration() { return _dur; }
|
||||
bool DurationReached();
|
||||
void Reset();
|
||||
|
||||
private:
|
||||
DurationCondition _type = DurationCondition::NONE;
|
||||
Duration _dur;
|
||||
bool _timeReached = false;
|
||||
};
|
||||
|
||||
class DurationConstraintEdit : public QWidget {
|
||||
Q_OBJECT
|
||||
public:
|
||||
DurationConstraintEdit(QWidget *parent = nullptr);
|
||||
void SetValue(DurationConstraint &value);
|
||||
void SetUnit(DurationUnit u);
|
||||
void SetDuration(const Duration &d);
|
||||
|
||||
private slots:
|
||||
void _ConditionChanged(int value);
|
||||
void ToggleClicked();
|
||||
signals:
|
||||
void DurationChanged(double value);
|
||||
void UnitChanged(DurationUnit u);
|
||||
void ConditionChanged(DurationCondition value);
|
||||
|
||||
private:
|
||||
void Collapse(bool collapse);
|
||||
|
||||
DurationSelection *_duration;
|
||||
QComboBox *_condition;
|
||||
QPushButton *_toggle;
|
||||
};
|
||||
64
src/headers/macro-action-audio.hpp
Normal file
64
src/headers/macro-action-audio.hpp
Normal file
@@ -0,0 +1,64 @@
|
||||
#pragma once
|
||||
#include <QSpinBox>
|
||||
#include "macro-action-edit.hpp"
|
||||
|
||||
enum class AudioAction {
|
||||
MUTE,
|
||||
UNMUTE,
|
||||
SOURCE_VOLUME,
|
||||
MASTER_VOLUME,
|
||||
};
|
||||
|
||||
class MacroActionAudio : public MacroAction {
|
||||
public:
|
||||
bool PerformAction();
|
||||
void LogAction();
|
||||
bool Save(obs_data_t *obj);
|
||||
bool Load(obs_data_t *obj);
|
||||
std::string GetId() { return id; };
|
||||
static std::shared_ptr<MacroAction> Create()
|
||||
{
|
||||
return std::make_shared<MacroActionAudio>();
|
||||
}
|
||||
|
||||
OBSWeakSource _audioSource;
|
||||
AudioAction _action = AudioAction::MUTE;
|
||||
int _volume = 0;
|
||||
|
||||
private:
|
||||
static bool _registered;
|
||||
static const std::string id;
|
||||
};
|
||||
|
||||
class MacroActionAudioEdit : public QWidget {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
MacroActionAudioEdit(
|
||||
QWidget *parent,
|
||||
std::shared_ptr<MacroActionAudio> entryData = nullptr);
|
||||
void SetWidgetVisibility();
|
||||
void UpdateEntryData();
|
||||
static QWidget *Create(QWidget *parent,
|
||||
std::shared_ptr<MacroAction> action)
|
||||
{
|
||||
return new MacroActionAudioEdit(
|
||||
parent,
|
||||
std::dynamic_pointer_cast<MacroActionAudio>(action));
|
||||
}
|
||||
|
||||
private slots:
|
||||
void SourceChanged(const QString &text);
|
||||
void ActionChanged(int value);
|
||||
void VolumeChanged(int value);
|
||||
|
||||
protected:
|
||||
QComboBox *_audioSources;
|
||||
QComboBox *_actions;
|
||||
QSpinBox *_volumePercent;
|
||||
std::shared_ptr<MacroActionAudio> _entryData;
|
||||
|
||||
private:
|
||||
QHBoxLayout *_mainLayout;
|
||||
bool _loading = true;
|
||||
};
|
||||
56
src/headers/macro-action-edit.hpp
Normal file
56
src/headers/macro-action-edit.hpp
Normal file
@@ -0,0 +1,56 @@
|
||||
#pragma once
|
||||
|
||||
#include <QWidget>
|
||||
#include <QComboBox>
|
||||
#include <QVBoxLayout>
|
||||
#include <QGroupBox>
|
||||
#include <deque>
|
||||
#include "macro.hpp"
|
||||
#include "section.hpp"
|
||||
|
||||
struct MacroActionInfo {
|
||||
using TCreateMethod = std::shared_ptr<MacroAction> (*)();
|
||||
using TCreateWidgetMethod = QWidget *(*)(QWidget *parent,
|
||||
std::shared_ptr<MacroAction>);
|
||||
TCreateMethod _createFunc;
|
||||
TCreateWidgetMethod _createWidgetFunc;
|
||||
std::string _name;
|
||||
};
|
||||
|
||||
class MacroActionFactory {
|
||||
public:
|
||||
MacroActionFactory() = delete;
|
||||
static bool Register(const std::string &id, MacroActionInfo);
|
||||
static std::shared_ptr<MacroAction> Create(const std::string &id);
|
||||
static QWidget *CreateWidget(const std::string &id, QWidget *parent,
|
||||
std::shared_ptr<MacroAction> action);
|
||||
static auto GetActionTypes() { return _methods; }
|
||||
static std::string GetActionName(const std::string &id);
|
||||
static std::string GetIdByName(const QString &name);
|
||||
|
||||
private:
|
||||
static std::map<std::string, MacroActionInfo> _methods;
|
||||
};
|
||||
|
||||
class MacroActionEdit : public QWidget {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
MacroActionEdit(QWidget *parent = nullptr,
|
||||
std::shared_ptr<MacroAction> * = nullptr,
|
||||
const std::string &id = "scene_switch",
|
||||
bool startCollapsed = false);
|
||||
void UpdateEntryData(const std::string &id, bool collapse);
|
||||
|
||||
private slots:
|
||||
void ActionSelectionChanged(const QString &text);
|
||||
|
||||
protected:
|
||||
QComboBox *_actionSelection;
|
||||
Section *_section;
|
||||
|
||||
std::shared_ptr<MacroAction> *_entryData;
|
||||
|
||||
private:
|
||||
bool _loading = true;
|
||||
};
|
||||
61
src/headers/macro-action-filter.hpp
Normal file
61
src/headers/macro-action-filter.hpp
Normal file
@@ -0,0 +1,61 @@
|
||||
#pragma once
|
||||
#include <QSpinBox>
|
||||
#include "macro-action-edit.hpp"
|
||||
|
||||
enum class FilterAction {
|
||||
ENABLE,
|
||||
DISABLE,
|
||||
};
|
||||
|
||||
class MacroActionFilter : public MacroAction {
|
||||
public:
|
||||
bool PerformAction();
|
||||
void LogAction();
|
||||
bool Save(obs_data_t *obj);
|
||||
bool Load(obs_data_t *obj);
|
||||
std::string GetId() { return id; };
|
||||
static std::shared_ptr<MacroAction> Create()
|
||||
{
|
||||
return std::make_shared<MacroActionFilter>();
|
||||
}
|
||||
|
||||
OBSWeakSource _source;
|
||||
OBSWeakSource _filter;
|
||||
FilterAction _action = FilterAction::ENABLE;
|
||||
|
||||
private:
|
||||
static bool _registered;
|
||||
static const std::string id;
|
||||
};
|
||||
|
||||
class MacroActionFilterEdit : public QWidget {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
MacroActionFilterEdit(
|
||||
QWidget *parent,
|
||||
std::shared_ptr<MacroActionFilter> entryData = nullptr);
|
||||
void UpdateEntryData();
|
||||
static QWidget *Create(QWidget *parent,
|
||||
std::shared_ptr<MacroAction> action)
|
||||
{
|
||||
return new MacroActionFilterEdit(
|
||||
parent,
|
||||
std::dynamic_pointer_cast<MacroActionFilter>(action));
|
||||
}
|
||||
|
||||
private slots:
|
||||
void SourceChanged(const QString &text);
|
||||
void FilterChanged(const QString &text);
|
||||
void ActionChanged(int value);
|
||||
|
||||
protected:
|
||||
QComboBox *_sources;
|
||||
QComboBox *_filters;
|
||||
QComboBox *_actions;
|
||||
std::shared_ptr<MacroActionFilter> _entryData;
|
||||
|
||||
private:
|
||||
QHBoxLayout *_mainLayout;
|
||||
bool _loading = true;
|
||||
};
|
||||
60
src/headers/macro-action-macro.hpp
Normal file
60
src/headers/macro-action-macro.hpp
Normal file
@@ -0,0 +1,60 @@
|
||||
#pragma once
|
||||
#include <QDoubleSpinBox>
|
||||
#include "macro-action-edit.hpp"
|
||||
#include "macro-selection.hpp"
|
||||
|
||||
enum class PerformMacroAction {
|
||||
PAUSE,
|
||||
UNPAUSE,
|
||||
RESET_COUNTER,
|
||||
};
|
||||
|
||||
class MacroActionMacro : public MacroRefAction {
|
||||
public:
|
||||
bool PerformAction();
|
||||
void LogAction();
|
||||
bool Save(obs_data_t *obj);
|
||||
bool Load(obs_data_t *obj);
|
||||
std::string GetId() { return id; };
|
||||
static std::shared_ptr<MacroAction> Create()
|
||||
{
|
||||
return std::make_shared<MacroActionMacro>();
|
||||
}
|
||||
|
||||
PerformMacroAction _action = PerformMacroAction::PAUSE;
|
||||
|
||||
private:
|
||||
static bool _registered;
|
||||
static const std::string id;
|
||||
};
|
||||
|
||||
class MacroActionMacroEdit : public QWidget {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
MacroActionMacroEdit(
|
||||
QWidget *parent,
|
||||
std::shared_ptr<MacroActionMacro> entryData = nullptr);
|
||||
void UpdateEntryData();
|
||||
static QWidget *Create(QWidget *parent,
|
||||
std::shared_ptr<MacroAction> action)
|
||||
{
|
||||
return new MacroActionMacroEdit(
|
||||
parent,
|
||||
std::dynamic_pointer_cast<MacroActionMacro>(action));
|
||||
}
|
||||
|
||||
private slots:
|
||||
void MacroChanged(const QString &text);
|
||||
void MacroRemove(const QString &name);
|
||||
void ActionChanged(int value);
|
||||
|
||||
protected:
|
||||
MacroSelection *_macros;
|
||||
QComboBox *_actions;
|
||||
std::shared_ptr<MacroActionMacro> _entryData;
|
||||
|
||||
private:
|
||||
QHBoxLayout *_mainLayout;
|
||||
bool _loading = true;
|
||||
};
|
||||
62
src/headers/macro-action-media.hpp
Normal file
62
src/headers/macro-action-media.hpp
Normal file
@@ -0,0 +1,62 @@
|
||||
#pragma once
|
||||
#include <QSpinBox>
|
||||
#include "macro-action-edit.hpp"
|
||||
|
||||
enum class MediaAction {
|
||||
PLAY,
|
||||
PAUSE,
|
||||
STOP,
|
||||
RESTART,
|
||||
NEXT,
|
||||
PREVIOUS,
|
||||
};
|
||||
|
||||
class MacroActionMedia : public MacroAction {
|
||||
public:
|
||||
bool PerformAction();
|
||||
void LogAction();
|
||||
bool Save(obs_data_t *obj);
|
||||
bool Load(obs_data_t *obj);
|
||||
std::string GetId() { return id; };
|
||||
static std::shared_ptr<MacroAction> Create()
|
||||
{
|
||||
return std::make_shared<MacroActionMedia>();
|
||||
}
|
||||
|
||||
OBSWeakSource _mediaSource;
|
||||
MediaAction _action = MediaAction::PLAY;
|
||||
|
||||
private:
|
||||
static bool _registered;
|
||||
static const std::string id;
|
||||
};
|
||||
|
||||
class MacroActionMediaEdit : public QWidget {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
MacroActionMediaEdit(
|
||||
QWidget *parent,
|
||||
std::shared_ptr<MacroActionMedia> entryData = nullptr);
|
||||
void UpdateEntryData();
|
||||
static QWidget *Create(QWidget *parent,
|
||||
std::shared_ptr<MacroAction> action)
|
||||
{
|
||||
return new MacroActionMediaEdit(
|
||||
parent,
|
||||
std::dynamic_pointer_cast<MacroActionMedia>(action));
|
||||
}
|
||||
|
||||
private slots:
|
||||
void SourceChanged(const QString &text);
|
||||
void ActionChanged(int value);
|
||||
|
||||
protected:
|
||||
QComboBox *_mediaSources;
|
||||
QComboBox *_actions;
|
||||
std::shared_ptr<MacroActionMedia> _entryData;
|
||||
|
||||
private:
|
||||
QHBoxLayout *_mainLayout;
|
||||
bool _loading = true;
|
||||
};
|
||||
55
src/headers/macro-action-plugin-state.hpp
Normal file
55
src/headers/macro-action-plugin-state.hpp
Normal file
@@ -0,0 +1,55 @@
|
||||
#pragma once
|
||||
#include <QDoubleSpinBox>
|
||||
#include "macro-action-edit.hpp"
|
||||
|
||||
enum class PluginStateAction {
|
||||
STOP,
|
||||
};
|
||||
|
||||
class MacroActionPluginState : public MacroAction {
|
||||
public:
|
||||
bool PerformAction();
|
||||
void LogAction();
|
||||
bool Save(obs_data_t *obj);
|
||||
bool Load(obs_data_t *obj);
|
||||
std::string GetId() { return id; };
|
||||
static std::shared_ptr<MacroAction> Create()
|
||||
{
|
||||
return std::make_shared<MacroActionPluginState>();
|
||||
}
|
||||
|
||||
PluginStateAction _action = PluginStateAction::STOP;
|
||||
|
||||
private:
|
||||
static bool _registered;
|
||||
static const std::string id;
|
||||
};
|
||||
|
||||
class MacroActionPluginStateEdit : public QWidget {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
MacroActionPluginStateEdit(
|
||||
QWidget *parent,
|
||||
std::shared_ptr<MacroActionPluginState> entryData = nullptr);
|
||||
void UpdateEntryData();
|
||||
static QWidget *Create(QWidget *parent,
|
||||
std::shared_ptr<MacroAction> action)
|
||||
{
|
||||
return new MacroActionPluginStateEdit(
|
||||
parent,
|
||||
std::dynamic_pointer_cast<MacroActionPluginState>(
|
||||
action));
|
||||
}
|
||||
|
||||
private slots:
|
||||
void ActionChanged(int value);
|
||||
|
||||
protected:
|
||||
QComboBox *_actions;
|
||||
std::shared_ptr<MacroActionPluginState> _entryData;
|
||||
|
||||
private:
|
||||
QHBoxLayout *_mainLayout;
|
||||
bool _loading = true;
|
||||
};
|
||||
59
src/headers/macro-action-recording.hpp
Normal file
59
src/headers/macro-action-recording.hpp
Normal file
@@ -0,0 +1,59 @@
|
||||
#pragma once
|
||||
#include <QDoubleSpinBox>
|
||||
#include <QLabel>
|
||||
#include "macro-action-edit.hpp"
|
||||
|
||||
enum class RecordAction {
|
||||
STOP,
|
||||
START,
|
||||
PAUSE,
|
||||
UNPAUSE,
|
||||
};
|
||||
|
||||
class MacroActionRecord : public MacroAction {
|
||||
public:
|
||||
bool PerformAction();
|
||||
void LogAction();
|
||||
bool Save(obs_data_t *obj);
|
||||
bool Load(obs_data_t *obj);
|
||||
std::string GetId() { return id; };
|
||||
static std::shared_ptr<MacroAction> Create()
|
||||
{
|
||||
return std::make_shared<MacroActionRecord>();
|
||||
}
|
||||
|
||||
RecordAction _action = RecordAction::STOP;
|
||||
|
||||
private:
|
||||
static bool _registered;
|
||||
static const std::string id;
|
||||
};
|
||||
|
||||
class MacroActionRecordEdit : public QWidget {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
MacroActionRecordEdit(
|
||||
QWidget *parent,
|
||||
std::shared_ptr<MacroActionRecord> entryData = nullptr);
|
||||
void UpdateEntryData();
|
||||
static QWidget *Create(QWidget *parent,
|
||||
std::shared_ptr<MacroAction> action)
|
||||
{
|
||||
return new MacroActionRecordEdit(
|
||||
parent,
|
||||
std::dynamic_pointer_cast<MacroActionRecord>(action));
|
||||
}
|
||||
|
||||
private slots:
|
||||
void ActionChanged(int value);
|
||||
|
||||
protected:
|
||||
QComboBox *_actions;
|
||||
QLabel *_pauseHint;
|
||||
std::shared_ptr<MacroActionRecord> _entryData;
|
||||
|
||||
private:
|
||||
QHBoxLayout *_mainLayout;
|
||||
bool _loading = true;
|
||||
};
|
||||
66
src/headers/macro-action-replay-buffer.hpp
Normal file
66
src/headers/macro-action-replay-buffer.hpp
Normal file
@@ -0,0 +1,66 @@
|
||||
#pragma once
|
||||
#include <QDoubleSpinBox>
|
||||
#include "macro-action-edit.hpp"
|
||||
#include "duration-control.hpp"
|
||||
|
||||
enum class ReplayBufferAction {
|
||||
STOP,
|
||||
START,
|
||||
SAVE,
|
||||
};
|
||||
|
||||
class MacroActionReplayBuffer : public MacroAction {
|
||||
public:
|
||||
bool PerformAction();
|
||||
void LogAction();
|
||||
bool Save(obs_data_t *obj);
|
||||
bool Load(obs_data_t *obj);
|
||||
std::string GetId() { return id; };
|
||||
static std::shared_ptr<MacroAction> Create()
|
||||
{
|
||||
return std::make_shared<MacroActionReplayBuffer>();
|
||||
}
|
||||
|
||||
ReplayBufferAction _action = ReplayBufferAction::STOP;
|
||||
|
||||
private:
|
||||
// Add artifical delay before trying to save the replay buffer again.
|
||||
//
|
||||
// Continiously calling obs_frontend_replay_buffer_save() does not
|
||||
// result in any output actually being written.
|
||||
// OBS_FRONTEND_EVENT_REPLAY_BUFFER_SAVED also seems to be sent before
|
||||
// any data is written.
|
||||
Duration _duration;
|
||||
|
||||
static bool _registered;
|
||||
static const std::string id;
|
||||
};
|
||||
|
||||
class MacroActionReplayBufferEdit : public QWidget {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
MacroActionReplayBufferEdit(
|
||||
QWidget *parent,
|
||||
std::shared_ptr<MacroActionReplayBuffer> entryData = nullptr);
|
||||
void UpdateEntryData();
|
||||
static QWidget *Create(QWidget *parent,
|
||||
std::shared_ptr<MacroAction> action)
|
||||
{
|
||||
return new MacroActionReplayBufferEdit(
|
||||
parent,
|
||||
std::dynamic_pointer_cast<MacroActionReplayBuffer>(
|
||||
action));
|
||||
}
|
||||
|
||||
private slots:
|
||||
void ActionChanged(int value);
|
||||
|
||||
protected:
|
||||
QComboBox *_actions;
|
||||
std::shared_ptr<MacroActionReplayBuffer> _entryData;
|
||||
|
||||
private:
|
||||
QHBoxLayout *_mainLayout;
|
||||
bool _loading = true;
|
||||
};
|
||||
52
src/headers/macro-action-run.hpp
Normal file
52
src/headers/macro-action-run.hpp
Normal file
@@ -0,0 +1,52 @@
|
||||
#pragma once
|
||||
#include "macro-action-edit.hpp"
|
||||
|
||||
#include <QLineEdit>
|
||||
#include <QPushButton>
|
||||
|
||||
class MacroActionRun : public MacroAction {
|
||||
public:
|
||||
bool PerformAction();
|
||||
void LogAction();
|
||||
bool Save(obs_data_t *obj);
|
||||
bool Load(obs_data_t *obj);
|
||||
std::string GetId() { return id; };
|
||||
static std::shared_ptr<MacroAction> Create()
|
||||
{
|
||||
return std::make_shared<MacroActionRun>();
|
||||
}
|
||||
|
||||
std::string _path = obs_module_text("AdvSceneSwitcher.enterPath");
|
||||
|
||||
private:
|
||||
static bool _registered;
|
||||
static const std::string id;
|
||||
};
|
||||
|
||||
class MacroActionRunEdit : public QWidget {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
MacroActionRunEdit(QWidget *parent,
|
||||
std::shared_ptr<MacroActionRun> entryData = nullptr);
|
||||
void UpdateEntryData();
|
||||
static QWidget *Create(QWidget *parent,
|
||||
std::shared_ptr<MacroAction> action)
|
||||
{
|
||||
return new MacroActionRunEdit(
|
||||
parent,
|
||||
std::dynamic_pointer_cast<MacroActionRun>(action));
|
||||
}
|
||||
|
||||
private slots:
|
||||
void FilePathChanged();
|
||||
void BrowseButtonClicked();
|
||||
|
||||
protected:
|
||||
std::shared_ptr<MacroActionRun> _entryData;
|
||||
|
||||
private:
|
||||
QLineEdit *_filePath;
|
||||
QPushButton *_browseButton;
|
||||
bool _loading = true;
|
||||
};
|
||||
49
src/headers/macro-action-scene-switch.hpp
Normal file
49
src/headers/macro-action-scene-switch.hpp
Normal file
@@ -0,0 +1,49 @@
|
||||
#pragma once
|
||||
#include "macro-action-edit.hpp"
|
||||
#include "switch-generic.hpp"
|
||||
#include "duration-control.hpp"
|
||||
|
||||
class MacroActionSwitchScene : public MacroAction, public SceneSwitcherEntry {
|
||||
public:
|
||||
bool PerformAction();
|
||||
void LogAction();
|
||||
bool Save(obs_data_t *obj);
|
||||
bool Load(obs_data_t *obj);
|
||||
std::string GetId() { return id; };
|
||||
|
||||
static std::shared_ptr<MacroAction> Create()
|
||||
{
|
||||
return std::make_shared<MacroActionSwitchScene>();
|
||||
}
|
||||
Duration _duration;
|
||||
|
||||
private:
|
||||
const char *getType() { return "MacroActionSwitchScene"; }
|
||||
|
||||
static bool _registered;
|
||||
static const std::string id;
|
||||
};
|
||||
|
||||
class MacroActionSwitchSceneEdit : public SwitchWidget {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
MacroActionSwitchSceneEdit(
|
||||
QWidget *parent,
|
||||
std::shared_ptr<MacroActionSwitchScene> entryData = nullptr);
|
||||
static QWidget *Create(QWidget *parent,
|
||||
std::shared_ptr<MacroAction> action)
|
||||
{
|
||||
return new MacroActionSwitchSceneEdit(
|
||||
parent,
|
||||
std::dynamic_pointer_cast<MacroActionSwitchScene>(
|
||||
action));
|
||||
}
|
||||
|
||||
private slots:
|
||||
void DurationChanged(double seconds);
|
||||
|
||||
protected:
|
||||
DurationSelection *_duration;
|
||||
std::shared_ptr<MacroActionSwitchScene> _entryData;
|
||||
};
|
||||
62
src/headers/macro-action-scene-visibility.hpp
Normal file
62
src/headers/macro-action-scene-visibility.hpp
Normal file
@@ -0,0 +1,62 @@
|
||||
#pragma once
|
||||
#include <QSpinBox>
|
||||
#include "macro-action-edit.hpp"
|
||||
|
||||
enum class SceneVisibilityAction {
|
||||
SHOW,
|
||||
HIDE,
|
||||
};
|
||||
|
||||
class MacroActionSceneVisibility : public MacroAction {
|
||||
public:
|
||||
bool PerformAction();
|
||||
void LogAction();
|
||||
bool Save(obs_data_t *obj);
|
||||
bool Load(obs_data_t *obj);
|
||||
std::string GetId() { return id; };
|
||||
static std::shared_ptr<MacroAction> Create()
|
||||
{
|
||||
return std::make_shared<MacroActionSceneVisibility>();
|
||||
}
|
||||
|
||||
OBSWeakSource _scene;
|
||||
OBSWeakSource _source;
|
||||
SceneVisibilityAction _action = SceneVisibilityAction::SHOW;
|
||||
|
||||
private:
|
||||
static bool _registered;
|
||||
static const std::string id;
|
||||
};
|
||||
|
||||
class MacroActionSceneVisibilityEdit : public QWidget {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
MacroActionSceneVisibilityEdit(
|
||||
QWidget *parent,
|
||||
std::shared_ptr<MacroActionSceneVisibility> entryData = nullptr);
|
||||
void UpdateEntryData();
|
||||
static QWidget *Create(QWidget *parent,
|
||||
std::shared_ptr<MacroAction> action)
|
||||
{
|
||||
return new MacroActionSceneVisibilityEdit(
|
||||
parent,
|
||||
std::dynamic_pointer_cast<MacroActionSceneVisibility>(
|
||||
action));
|
||||
}
|
||||
|
||||
private slots:
|
||||
void SceneChanged(const QString &text);
|
||||
void SourceChanged(const QString &text);
|
||||
void ActionChanged(int value);
|
||||
|
||||
protected:
|
||||
QComboBox *_scenes;
|
||||
QComboBox *_sources;
|
||||
QComboBox *_actions;
|
||||
std::shared_ptr<MacroActionSceneVisibility> _entryData;
|
||||
|
||||
private:
|
||||
QHBoxLayout *_mainLayout;
|
||||
bool _loading = true;
|
||||
};
|
||||
70
src/headers/macro-action-source.hpp
Normal file
70
src/headers/macro-action-source.hpp
Normal file
@@ -0,0 +1,70 @@
|
||||
#pragma once
|
||||
#include "macro-action-edit.hpp"
|
||||
|
||||
#include <QSpinBox>
|
||||
#include <QLabel>
|
||||
#include <QPushButton>
|
||||
#include <QPlainTextEdit>
|
||||
|
||||
enum class SourceAction {
|
||||
ENABLE,
|
||||
DISABLE,
|
||||
SETTINGS,
|
||||
};
|
||||
|
||||
class MacroActionSource : public MacroAction {
|
||||
public:
|
||||
bool PerformAction();
|
||||
void LogAction();
|
||||
bool Save(obs_data_t *obj);
|
||||
bool Load(obs_data_t *obj);
|
||||
std::string GetId() { return id; };
|
||||
static std::shared_ptr<MacroAction> Create()
|
||||
{
|
||||
return std::make_shared<MacroActionSource>();
|
||||
}
|
||||
|
||||
OBSWeakSource _source;
|
||||
std::string _settings = "";
|
||||
SourceAction _action = SourceAction::ENABLE;
|
||||
|
||||
private:
|
||||
static bool _registered;
|
||||
static const std::string id;
|
||||
};
|
||||
|
||||
class MacroActionSourceEdit : public QWidget {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
MacroActionSourceEdit(
|
||||
QWidget *parent,
|
||||
std::shared_ptr<MacroActionSource> entryData = nullptr);
|
||||
void UpdateEntryData();
|
||||
static QWidget *Create(QWidget *parent,
|
||||
std::shared_ptr<MacroAction> action)
|
||||
{
|
||||
return new MacroActionSourceEdit(
|
||||
parent,
|
||||
std::dynamic_pointer_cast<MacroActionSource>(action));
|
||||
}
|
||||
|
||||
private slots:
|
||||
void SourceChanged(const QString &text);
|
||||
void ActionChanged(int value);
|
||||
void GetSettingsClicked();
|
||||
void SettingsChanged();
|
||||
|
||||
protected:
|
||||
QComboBox *_sources;
|
||||
QComboBox *_actions;
|
||||
QPushButton *_getSettings;
|
||||
QPlainTextEdit *_settings;
|
||||
QLabel *_warning;
|
||||
std::shared_ptr<MacroActionSource> _entryData;
|
||||
|
||||
private:
|
||||
void SetWidgetVisibility(bool);
|
||||
QHBoxLayout *_mainLayout;
|
||||
bool _loading = true;
|
||||
};
|
||||
60
src/headers/macro-action-streaming.hpp
Normal file
60
src/headers/macro-action-streaming.hpp
Normal file
@@ -0,0 +1,60 @@
|
||||
#pragma once
|
||||
#include <QDoubleSpinBox>
|
||||
#include "macro-action-edit.hpp"
|
||||
#include "duration-control.hpp"
|
||||
|
||||
enum class StreamAction {
|
||||
STOP,
|
||||
START,
|
||||
};
|
||||
|
||||
class MacroActionStream : public MacroAction {
|
||||
public:
|
||||
bool PerformAction();
|
||||
void LogAction();
|
||||
bool Save(obs_data_t *obj);
|
||||
bool Load(obs_data_t *obj);
|
||||
std::string GetId() { return id; };
|
||||
static std::shared_ptr<MacroAction> Create()
|
||||
{
|
||||
return std::make_shared<MacroActionStream>();
|
||||
}
|
||||
|
||||
StreamAction _action = StreamAction::STOP;
|
||||
|
||||
private:
|
||||
// Acts as a safeguard for misconfigured streaming setups leading to an
|
||||
// endless error spam.
|
||||
Duration _retryCooldown;
|
||||
|
||||
static bool _registered;
|
||||
static const std::string id;
|
||||
};
|
||||
|
||||
class MacroActionStreamEdit : public QWidget {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
MacroActionStreamEdit(
|
||||
QWidget *parent,
|
||||
std::shared_ptr<MacroActionStream> entryData = nullptr);
|
||||
void UpdateEntryData();
|
||||
static QWidget *Create(QWidget *parent,
|
||||
std::shared_ptr<MacroAction> action)
|
||||
{
|
||||
return new MacroActionStreamEdit(
|
||||
parent,
|
||||
std::dynamic_pointer_cast<MacroActionStream>(action));
|
||||
}
|
||||
|
||||
private slots:
|
||||
void ActionChanged(int value);
|
||||
|
||||
protected:
|
||||
QComboBox *_actions;
|
||||
std::shared_ptr<MacroActionStream> _entryData;
|
||||
|
||||
private:
|
||||
QHBoxLayout *_mainLayout;
|
||||
bool _loading = true;
|
||||
};
|
||||
64
src/headers/macro-action-wait.hpp
Normal file
64
src/headers/macro-action-wait.hpp
Normal file
@@ -0,0 +1,64 @@
|
||||
#pragma once
|
||||
#include <QDoubleSpinBox>
|
||||
#include "macro-action-edit.hpp"
|
||||
#include "duration-control.hpp"
|
||||
|
||||
enum class WaitType {
|
||||
FIXED,
|
||||
RANDOM,
|
||||
};
|
||||
|
||||
class MacroActionWait : public MacroAction {
|
||||
public:
|
||||
bool PerformAction();
|
||||
bool Save(obs_data_t *obj);
|
||||
bool Load(obs_data_t *obj);
|
||||
std::string GetId() { return id; };
|
||||
static std::shared_ptr<MacroAction> Create()
|
||||
{
|
||||
return std::make_shared<MacroActionWait>();
|
||||
}
|
||||
Duration _duration;
|
||||
Duration _duration2;
|
||||
WaitType _waitType = WaitType::FIXED;
|
||||
|
||||
private:
|
||||
static bool _registered;
|
||||
static const std::string id;
|
||||
};
|
||||
|
||||
class MacroActionWaitEdit : public QWidget {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
MacroActionWaitEdit(
|
||||
QWidget *parent,
|
||||
std::shared_ptr<MacroActionWait> entryData = nullptr);
|
||||
void UpdateEntryData();
|
||||
void SetupFixedDurationEdit();
|
||||
void SetupRandomDurationEdit();
|
||||
static QWidget *Create(QWidget *parent,
|
||||
std::shared_ptr<MacroAction> action)
|
||||
{
|
||||
return new MacroActionWaitEdit(
|
||||
parent,
|
||||
std::dynamic_pointer_cast<MacroActionWait>(action));
|
||||
}
|
||||
|
||||
private slots:
|
||||
void DurationChanged(double value);
|
||||
void DurationUnitChanged(DurationUnit unit);
|
||||
void Duration2Changed(double value);
|
||||
void Duration2UnitChanged(DurationUnit unit);
|
||||
void TypeChanged(int value);
|
||||
|
||||
protected:
|
||||
DurationSelection *_duration;
|
||||
DurationSelection *_duration2;
|
||||
QComboBox *_waitType;
|
||||
std::shared_ptr<MacroActionWait> _entryData;
|
||||
|
||||
private:
|
||||
QHBoxLayout *_mainLayout;
|
||||
bool _loading = true;
|
||||
};
|
||||
73
src/headers/macro-condition-audio.hpp
Normal file
73
src/headers/macro-condition-audio.hpp
Normal file
@@ -0,0 +1,73 @@
|
||||
#pragma once
|
||||
#include "macro.hpp"
|
||||
#include "volume-control.hpp"
|
||||
#include <limits>
|
||||
#include <QWidget>
|
||||
#include <QComboBox>
|
||||
#include <chrono>
|
||||
|
||||
enum class AudioCondition {
|
||||
ABOVE,
|
||||
BELOW,
|
||||
};
|
||||
|
||||
class MacroConditionAudio : public MacroCondition {
|
||||
public:
|
||||
~MacroConditionAudio();
|
||||
bool CheckCondition();
|
||||
bool Save(obs_data_t *obj);
|
||||
bool Load(obs_data_t *obj);
|
||||
std::string GetId() { return id; };
|
||||
static std::shared_ptr<MacroCondition> Create()
|
||||
{
|
||||
return std::make_shared<MacroConditionAudio>();
|
||||
}
|
||||
static void SetVolumeLevel(void *data,
|
||||
const float magnitude[MAX_AUDIO_CHANNELS],
|
||||
const float peak[MAX_AUDIO_CHANNELS],
|
||||
const float inputPeak[MAX_AUDIO_CHANNELS]);
|
||||
void ResetVolmeter();
|
||||
|
||||
OBSWeakSource _audioSource;
|
||||
int _volume = 0;
|
||||
AudioCondition _condition = AudioCondition::ABOVE;
|
||||
obs_volmeter_t *_volmeter = nullptr;
|
||||
|
||||
private:
|
||||
float _peak = -std::numeric_limits<float>::infinity();
|
||||
static bool _registered;
|
||||
static const std::string id;
|
||||
};
|
||||
|
||||
class MacroConditionAudioEdit : public QWidget {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
MacroConditionAudioEdit(
|
||||
QWidget *parent,
|
||||
std::shared_ptr<MacroConditionAudio> cond = nullptr);
|
||||
void UpdateEntryData();
|
||||
static QWidget *Create(QWidget *parent,
|
||||
std::shared_ptr<MacroCondition> cond)
|
||||
{
|
||||
return new MacroConditionAudioEdit(
|
||||
parent,
|
||||
std::dynamic_pointer_cast<MacroConditionAudio>(cond));
|
||||
}
|
||||
void UpdateVolmeterSource();
|
||||
|
||||
private slots:
|
||||
void SourceChanged(const QString &text);
|
||||
void VolumeThresholdChanged(int vol);
|
||||
void ConditionChanged(int cond);
|
||||
|
||||
protected:
|
||||
QComboBox *_audioSources;
|
||||
QComboBox *_condition;
|
||||
QSpinBox *_volume;
|
||||
VolControl *_volMeter = nullptr;
|
||||
std::shared_ptr<MacroConditionAudio> _entryData;
|
||||
|
||||
private:
|
||||
bool _loading = true;
|
||||
};
|
||||
70
src/headers/macro-condition-counter.hpp
Normal file
70
src/headers/macro-condition-counter.hpp
Normal file
@@ -0,0 +1,70 @@
|
||||
#pragma once
|
||||
#include "macro.hpp"
|
||||
#include "macro-selection.hpp"
|
||||
#include <QWidget>
|
||||
#include <QSpinBox>
|
||||
#include <QLabel>
|
||||
#include <QTimer>
|
||||
|
||||
enum class CounterCondition {
|
||||
BELOW,
|
||||
ABOVE,
|
||||
EQUAL,
|
||||
};
|
||||
|
||||
class MacroConditionCounter : public MacroRefCondition {
|
||||
public:
|
||||
bool CheckCondition();
|
||||
bool Save(obs_data_t *obj);
|
||||
bool Load(obs_data_t *obj);
|
||||
std::string GetId() { return id; };
|
||||
static std::shared_ptr<MacroCondition> Create()
|
||||
{
|
||||
return std::make_shared<MacroConditionCounter>();
|
||||
}
|
||||
|
||||
CounterCondition _condition = CounterCondition::BELOW;
|
||||
int _count = 0;
|
||||
|
||||
private:
|
||||
static bool _registered;
|
||||
static const std::string id;
|
||||
};
|
||||
|
||||
class MacroConditionCounterEdit : public QWidget {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
MacroConditionCounterEdit(
|
||||
QWidget *parent,
|
||||
std::shared_ptr<MacroConditionCounter> cond = nullptr);
|
||||
void UpdateEntryData();
|
||||
static QWidget *Create(QWidget *parent,
|
||||
std::shared_ptr<MacroCondition> cond)
|
||||
{
|
||||
return new MacroConditionCounterEdit(
|
||||
parent,
|
||||
std::dynamic_pointer_cast<MacroConditionCounter>(cond));
|
||||
}
|
||||
|
||||
private slots:
|
||||
void MacroChanged(const QString &text);
|
||||
void MacroRemove(const QString &name);
|
||||
void CountChanged(int value);
|
||||
void ConditionChanged(int cond);
|
||||
void ResetClicked();
|
||||
void UpdateCount();
|
||||
|
||||
protected:
|
||||
MacroSelection *_macros;
|
||||
QComboBox *_conditions;
|
||||
QSpinBox *_count;
|
||||
QLabel *_currentCount;
|
||||
QPushButton *_resetCount;
|
||||
std::unique_ptr<QTimer> _timer;
|
||||
std::shared_ptr<MacroConditionCounter> _entryData;
|
||||
|
||||
private:
|
||||
void ResetTimer();
|
||||
bool _loading = true;
|
||||
};
|
||||
66
src/headers/macro-condition-edit.hpp
Normal file
66
src/headers/macro-condition-edit.hpp
Normal file
@@ -0,0 +1,66 @@
|
||||
#pragma once
|
||||
|
||||
#include "advanced-scene-switcher.hpp"
|
||||
#include "macro.hpp"
|
||||
#include "macro-condition-scene.hpp"
|
||||
#include "section.hpp"
|
||||
#include "utility.hpp"
|
||||
|
||||
#include <QGroupBox>
|
||||
|
||||
struct MacroConditionInfo {
|
||||
using TCreateMethod = std::shared_ptr<MacroCondition> (*)();
|
||||
using TCreateWidgetMethod =
|
||||
QWidget *(*)(QWidget *parent, std::shared_ptr<MacroCondition>);
|
||||
TCreateMethod _createFunc;
|
||||
TCreateWidgetMethod _createWidgetFunc;
|
||||
std::string _name;
|
||||
bool _useDurationConstraint = true;
|
||||
};
|
||||
|
||||
class MacroConditionFactory {
|
||||
public:
|
||||
MacroConditionFactory() = delete;
|
||||
static bool Register(const std::string &, MacroConditionInfo);
|
||||
static std::shared_ptr<MacroCondition> Create(const std::string &);
|
||||
static QWidget *CreateWidget(const std::string &id, QWidget *parent,
|
||||
std::shared_ptr<MacroCondition>);
|
||||
static auto GetConditionTypes() { return _methods; }
|
||||
static std::string GetConditionName(const std::string &);
|
||||
static std::string GetIdByName(const QString &name);
|
||||
static bool UsesDurationConstraint(const std::string &id);
|
||||
|
||||
private:
|
||||
static std::map<std::string, MacroConditionInfo> _methods;
|
||||
};
|
||||
|
||||
class MacroConditionEdit : public QWidget {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
MacroConditionEdit(QWidget *parent = nullptr,
|
||||
std::shared_ptr<MacroCondition> * = nullptr,
|
||||
const std::string &id = "scene", bool root = true,
|
||||
bool startCollapsed = false);
|
||||
bool IsRootNode();
|
||||
void UpdateEntryData(const std::string &id, bool collapse);
|
||||
|
||||
private slots:
|
||||
void LogicSelectionChanged(int idx);
|
||||
void ConditionSelectionChanged(const QString &text);
|
||||
void DurationChanged(double seconds);
|
||||
void DurationConditionChanged(DurationCondition cond);
|
||||
void DurationUnitChanged(DurationUnit unit);
|
||||
|
||||
protected:
|
||||
QComboBox *_logicSelection;
|
||||
QComboBox *_conditionSelection;
|
||||
Section *_section;
|
||||
DurationConstraintEdit *_dur;
|
||||
|
||||
std::shared_ptr<MacroCondition> *_entryData;
|
||||
|
||||
private:
|
||||
bool _isRoot = true;
|
||||
bool _loading = true;
|
||||
};
|
||||
82
src/headers/macro-condition-file.hpp
Normal file
82
src/headers/macro-condition-file.hpp
Normal file
@@ -0,0 +1,82 @@
|
||||
#pragma once
|
||||
#include "macro.hpp"
|
||||
#include <QWidget>
|
||||
#include <QComboBox>
|
||||
#include <QDateTime>
|
||||
#include <QLineEdit>
|
||||
#include <QPushButton>
|
||||
#include <QPlainTextEdit>
|
||||
#include <QCheckBox>
|
||||
|
||||
enum class FileType {
|
||||
LOCAL,
|
||||
REMOTE,
|
||||
};
|
||||
|
||||
class MacroConditionFile : public MacroCondition {
|
||||
public:
|
||||
bool CheckCondition();
|
||||
bool Save(obs_data_t *obj);
|
||||
bool Load(obs_data_t *obj);
|
||||
std::string GetId() { return id; };
|
||||
static std::shared_ptr<MacroCondition> Create()
|
||||
{
|
||||
return std::make_shared<MacroConditionFile>();
|
||||
}
|
||||
|
||||
std::string _file = obs_module_text("AdvSceneSwitcher.enterPath");
|
||||
std::string _text = obs_module_text("AdvSceneSwitcher.enterText");
|
||||
FileType _fileType = FileType::LOCAL;
|
||||
bool _useRegex = false;
|
||||
bool _useTime = false;
|
||||
bool _onlyMatchIfChanged = false;
|
||||
|
||||
private:
|
||||
bool matchFileContent(QString &filedata);
|
||||
bool checkRemoteFileContent();
|
||||
bool checkLocalFileContent();
|
||||
|
||||
QDateTime _lastMod;
|
||||
size_t _lastHash = 0;
|
||||
static bool _registered;
|
||||
static const std::string id;
|
||||
};
|
||||
|
||||
class MacroConditionFileEdit : public QWidget {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
MacroConditionFileEdit(
|
||||
QWidget *parent,
|
||||
std::shared_ptr<MacroConditionFile> cond = nullptr);
|
||||
void UpdateEntryData();
|
||||
static QWidget *Create(QWidget *parent,
|
||||
std::shared_ptr<MacroCondition> cond)
|
||||
{
|
||||
return new MacroConditionFileEdit(
|
||||
parent,
|
||||
std::dynamic_pointer_cast<MacroConditionFile>(cond));
|
||||
}
|
||||
|
||||
private slots:
|
||||
void FileTypeChanged(int index);
|
||||
void FilePathChanged();
|
||||
void BrowseButtonClicked();
|
||||
void MatchTextChanged();
|
||||
void UseRegexChanged(int state);
|
||||
void CheckModificationDateChanged(int state);
|
||||
void OnlyMatchIfChangedChanged(int state);
|
||||
|
||||
protected:
|
||||
QComboBox *_fileType;
|
||||
QLineEdit *_filePath;
|
||||
QPushButton *_browseButton;
|
||||
QPlainTextEdit *_matchText;
|
||||
QCheckBox *_useRegex;
|
||||
QCheckBox *_checkModificationDate;
|
||||
QCheckBox *_checkFileContent;
|
||||
std::shared_ptr<MacroConditionFile> _entryData;
|
||||
|
||||
private:
|
||||
bool _loading = true;
|
||||
};
|
||||
52
src/headers/macro-condition-idle.hpp
Normal file
52
src/headers/macro-condition-idle.hpp
Normal file
@@ -0,0 +1,52 @@
|
||||
#pragma once
|
||||
#include "macro.hpp"
|
||||
#include "duration-control.hpp"
|
||||
|
||||
#include <QWidget>
|
||||
#include <QComboBox>
|
||||
|
||||
class MacroConditionIdle : public MacroCondition {
|
||||
public:
|
||||
bool CheckCondition();
|
||||
bool Save(obs_data_t *obj);
|
||||
bool Load(obs_data_t *obj);
|
||||
std::string GetId() { return id; };
|
||||
static std::shared_ptr<MacroCondition> Create()
|
||||
{
|
||||
return std::make_shared<MacroConditionIdle>();
|
||||
}
|
||||
|
||||
Duration _duration;
|
||||
|
||||
private:
|
||||
static bool _registered;
|
||||
static const std::string id;
|
||||
};
|
||||
|
||||
class MacroConditionIdleEdit : public QWidget {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
MacroConditionIdleEdit(
|
||||
QWidget *parent,
|
||||
std::shared_ptr<MacroConditionIdle> cond = nullptr);
|
||||
void UpdateEntryData();
|
||||
static QWidget *Create(QWidget *parent,
|
||||
std::shared_ptr<MacroCondition> cond)
|
||||
{
|
||||
return new MacroConditionIdleEdit(
|
||||
parent,
|
||||
std::dynamic_pointer_cast<MacroConditionIdle>(cond));
|
||||
}
|
||||
|
||||
private slots:
|
||||
void DurationChanged(double seconds);
|
||||
void DurationUnitChanged(DurationUnit unit);
|
||||
|
||||
protected:
|
||||
DurationSelection *_duration;
|
||||
std::shared_ptr<MacroConditionIdle> _entryData;
|
||||
|
||||
private:
|
||||
bool _loading = true;
|
||||
};
|
||||
52
src/headers/macro-condition-interval.hpp
Normal file
52
src/headers/macro-condition-interval.hpp
Normal file
@@ -0,0 +1,52 @@
|
||||
#pragma once
|
||||
#include "macro.hpp"
|
||||
#include <QWidget>
|
||||
#include <QComboBox>
|
||||
#include "duration-control.hpp"
|
||||
|
||||
class MacroConditionInterval : public MacroCondition {
|
||||
public:
|
||||
bool CheckCondition();
|
||||
bool Save(obs_data_t *obj);
|
||||
bool Load(obs_data_t *obj);
|
||||
std::string GetId() { return id; };
|
||||
static std::shared_ptr<MacroCondition> Create()
|
||||
{
|
||||
return std::make_shared<MacroConditionInterval>();
|
||||
}
|
||||
|
||||
Duration _duration;
|
||||
|
||||
private:
|
||||
static bool _registered;
|
||||
static const std::string id;
|
||||
};
|
||||
|
||||
class MacroConditionIntervalEdit : public QWidget {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
MacroConditionIntervalEdit(
|
||||
QWidget *parent,
|
||||
std::shared_ptr<MacroConditionInterval> cond = nullptr);
|
||||
void UpdateEntryData();
|
||||
static QWidget *Create(QWidget *parent,
|
||||
std::shared_ptr<MacroCondition> cond)
|
||||
{
|
||||
return new MacroConditionIntervalEdit(
|
||||
parent,
|
||||
std::dynamic_pointer_cast<MacroConditionInterval>(
|
||||
cond));
|
||||
}
|
||||
|
||||
private slots:
|
||||
void DurationChanged(double seconds);
|
||||
void DurationUnitChanged(DurationUnit unit);
|
||||
|
||||
protected:
|
||||
DurationSelection *_duration;
|
||||
std::shared_ptr<MacroConditionInterval> _entryData;
|
||||
|
||||
private:
|
||||
bool _loading = true;
|
||||
};
|
||||
103
src/headers/macro-condition-media.hpp
Normal file
103
src/headers/macro-condition-media.hpp
Normal file
@@ -0,0 +1,103 @@
|
||||
#pragma once
|
||||
#include "macro.hpp"
|
||||
#include <limits>
|
||||
#include <QWidget>
|
||||
#include <QComboBox>
|
||||
|
||||
#include "duration-control.hpp"
|
||||
|
||||
enum class MediaTimeRestriction {
|
||||
TIME_RESTRICTION_NONE,
|
||||
TIME_RESTRICTION_SHORTER,
|
||||
TIME_RESTRICTION_LONGER,
|
||||
TIME_RESTRICTION_REMAINING_SHORTER,
|
||||
TIME_RESTRICTION_REMAINING_LONGER,
|
||||
};
|
||||
|
||||
constexpr auto custom_media_states_offset = 100;
|
||||
|
||||
enum class MediaState {
|
||||
// OBS's internal states
|
||||
OBS_MEDIA_STATE_NONE,
|
||||
OBS_MEDIA_STATE_PLAYING,
|
||||
OBS_MEDIA_STATE_OPENING,
|
||||
OBS_MEDIA_STATE_BUFFERING,
|
||||
OBS_MEDIA_STATE_PAUSED,
|
||||
OBS_MEDIA_STATE_STOPPED,
|
||||
OBS_MEDIA_STATE_ENDED,
|
||||
OBS_MEDIA_STATE_ERROR,
|
||||
// Just a marker
|
||||
LAST_OBS_MEDIA_STATE,
|
||||
// states added for use in the plugin
|
||||
PLAYED_TO_END = custom_media_states_offset,
|
||||
ANY,
|
||||
};
|
||||
|
||||
class MacroConditionMedia : public MacroCondition {
|
||||
public:
|
||||
~MacroConditionMedia();
|
||||
bool CheckCondition();
|
||||
bool Save(obs_data_t *obj);
|
||||
bool Load(obs_data_t *obj);
|
||||
std::string GetId() { return id; };
|
||||
static std::shared_ptr<MacroCondition> Create()
|
||||
{
|
||||
return std::make_shared<MacroConditionMedia>();
|
||||
}
|
||||
void ClearSignalHandler();
|
||||
void ResetSignalHandler();
|
||||
static void MediaStopped(void *data, calldata_t *);
|
||||
static void MediaEnded(void *data, calldata_t *);
|
||||
|
||||
OBSWeakSource _source = nullptr;
|
||||
MediaState _state = MediaState::OBS_MEDIA_STATE_NONE;
|
||||
MediaTimeRestriction _restriction =
|
||||
MediaTimeRestriction::TIME_RESTRICTION_NONE;
|
||||
Duration _time;
|
||||
|
||||
private:
|
||||
std::atomic_bool _stopped = {false};
|
||||
std::atomic_bool _ended = {false};
|
||||
// Trigger scene change only once even if media state might trigger repeatedly
|
||||
bool _alreadyMatched = false;
|
||||
// Workaround to enable use of "ended" to specify end of VLC playlist
|
||||
bool _previousStateEnded = false;
|
||||
bool _playedToEnd = false;
|
||||
|
||||
static bool _registered;
|
||||
static const std::string id;
|
||||
};
|
||||
|
||||
class MacroConditionMediaEdit : public QWidget {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
MacroConditionMediaEdit(
|
||||
QWidget *parent,
|
||||
std::shared_ptr<MacroConditionMedia> cond = nullptr);
|
||||
void UpdateEntryData();
|
||||
static QWidget *Create(QWidget *parent,
|
||||
std::shared_ptr<MacroCondition> cond)
|
||||
{
|
||||
return new MacroConditionMediaEdit(
|
||||
parent,
|
||||
std::dynamic_pointer_cast<MacroConditionMedia>(cond));
|
||||
}
|
||||
|
||||
private slots:
|
||||
void SourceChanged(const QString &text);
|
||||
void StateChanged(int index);
|
||||
void TimeRestrictionChanged(int index);
|
||||
void TimeChanged(double seconds);
|
||||
void TimeUnitChanged(DurationUnit unit);
|
||||
|
||||
protected:
|
||||
QComboBox *_mediaSources;
|
||||
QComboBox *_states;
|
||||
QComboBox *_timeRestrictions;
|
||||
DurationSelection *_time;
|
||||
std::shared_ptr<MacroConditionMedia> _entryData;
|
||||
|
||||
private:
|
||||
bool _loading = true;
|
||||
};
|
||||
54
src/headers/macro-condition-plugin-state.hpp
Normal file
54
src/headers/macro-condition-plugin-state.hpp
Normal file
@@ -0,0 +1,54 @@
|
||||
#pragma once
|
||||
#include "macro.hpp"
|
||||
#include <QWidget>
|
||||
#include <QComboBox>
|
||||
|
||||
enum class PluginStateCondition {
|
||||
SCENESWITCHED,
|
||||
};
|
||||
|
||||
class MacroConditionPluginState : public MacroCondition {
|
||||
public:
|
||||
bool CheckCondition();
|
||||
bool Save(obs_data_t *obj);
|
||||
bool Load(obs_data_t *obj);
|
||||
std::string GetId() { return id; };
|
||||
static std::shared_ptr<MacroCondition> Create()
|
||||
{
|
||||
return std::make_shared<MacroConditionPluginState>();
|
||||
}
|
||||
|
||||
PluginStateCondition _condition = PluginStateCondition::SCENESWITCHED;
|
||||
|
||||
private:
|
||||
static bool _registered;
|
||||
static const std::string id;
|
||||
};
|
||||
|
||||
class MacroConditionPluginStateEdit : public QWidget {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
MacroConditionPluginStateEdit(
|
||||
QWidget *parent,
|
||||
std::shared_ptr<MacroConditionPluginState> cond = nullptr);
|
||||
void UpdateEntryData();
|
||||
static QWidget *Create(QWidget *parent,
|
||||
std::shared_ptr<MacroCondition> cond)
|
||||
{
|
||||
return new MacroConditionPluginStateEdit(
|
||||
parent,
|
||||
std::dynamic_pointer_cast<MacroConditionPluginState>(
|
||||
cond));
|
||||
}
|
||||
|
||||
private slots:
|
||||
void ConditionChanged(int cond);
|
||||
|
||||
protected:
|
||||
QComboBox *_condition;
|
||||
std::shared_ptr<MacroConditionPluginState> _entryData;
|
||||
|
||||
private:
|
||||
bool _loading = true;
|
||||
};
|
||||
52
src/headers/macro-condition-process.hpp
Normal file
52
src/headers/macro-condition-process.hpp
Normal file
@@ -0,0 +1,52 @@
|
||||
#pragma once
|
||||
#include "macro.hpp"
|
||||
#include <QWidget>
|
||||
#include <QComboBox>
|
||||
|
||||
class MacroConditionProcess : public MacroCondition {
|
||||
public:
|
||||
bool CheckCondition();
|
||||
bool Save(obs_data_t *obj);
|
||||
bool Load(obs_data_t *obj);
|
||||
std::string GetId() { return id; };
|
||||
static std::shared_ptr<MacroCondition> Create()
|
||||
{
|
||||
return std::make_shared<MacroConditionProcess>();
|
||||
}
|
||||
|
||||
std::string _process;
|
||||
bool _focus = true;
|
||||
|
||||
private:
|
||||
static bool _registered;
|
||||
static const std::string id;
|
||||
};
|
||||
|
||||
class MacroConditionProcessEdit : public QWidget {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
MacroConditionProcessEdit(
|
||||
QWidget *parent,
|
||||
std::shared_ptr<MacroConditionProcess> cond = nullptr);
|
||||
void UpdateEntryData();
|
||||
static QWidget *Create(QWidget *parent,
|
||||
std::shared_ptr<MacroCondition> cond)
|
||||
{
|
||||
return new MacroConditionProcessEdit(
|
||||
parent,
|
||||
std::dynamic_pointer_cast<MacroConditionProcess>(cond));
|
||||
}
|
||||
|
||||
private slots:
|
||||
void ProcessChanged(const QString &text);
|
||||
void FocusChanged(int state);
|
||||
|
||||
protected:
|
||||
QComboBox *_processSelection;
|
||||
QCheckBox *_focused;
|
||||
std::shared_ptr<MacroConditionProcess> _entryData;
|
||||
|
||||
private:
|
||||
bool _loading = true;
|
||||
};
|
||||
55
src/headers/macro-condition-recording.hpp
Normal file
55
src/headers/macro-condition-recording.hpp
Normal file
@@ -0,0 +1,55 @@
|
||||
#pragma once
|
||||
#include "macro.hpp"
|
||||
#include <QWidget>
|
||||
#include <QComboBox>
|
||||
|
||||
enum class RecordState {
|
||||
STOP,
|
||||
PAUSE,
|
||||
START,
|
||||
};
|
||||
|
||||
class MacroConditionRecord : public MacroCondition {
|
||||
public:
|
||||
bool CheckCondition();
|
||||
bool Save(obs_data_t *obj);
|
||||
bool Load(obs_data_t *obj);
|
||||
std::string GetId() { return id; };
|
||||
static std::shared_ptr<MacroCondition> Create()
|
||||
{
|
||||
return std::make_shared<MacroConditionRecord>();
|
||||
}
|
||||
|
||||
RecordState _recordState;
|
||||
|
||||
private:
|
||||
static bool _registered;
|
||||
static const std::string id;
|
||||
};
|
||||
|
||||
class MacroConditionRecordEdit : public QWidget {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
MacroConditionRecordEdit(
|
||||
QWidget *parent,
|
||||
std::shared_ptr<MacroConditionRecord> cond = nullptr);
|
||||
void UpdateEntryData();
|
||||
static QWidget *Create(QWidget *parent,
|
||||
std::shared_ptr<MacroCondition> cond)
|
||||
{
|
||||
return new MacroConditionRecordEdit(
|
||||
parent,
|
||||
std::dynamic_pointer_cast<MacroConditionRecord>(cond));
|
||||
}
|
||||
|
||||
private slots:
|
||||
void StateChanged(int value);
|
||||
|
||||
protected:
|
||||
QComboBox *_recordState;
|
||||
std::shared_ptr<MacroConditionRecord> _entryData;
|
||||
|
||||
private:
|
||||
bool _loading = true;
|
||||
};
|
||||
55
src/headers/macro-condition-region.hpp
Normal file
55
src/headers/macro-condition-region.hpp
Normal file
@@ -0,0 +1,55 @@
|
||||
#pragma once
|
||||
#include "macro.hpp"
|
||||
#include <QWidget>
|
||||
#include <QComboBox>
|
||||
|
||||
class MacroConditionRegion : public MacroCondition {
|
||||
public:
|
||||
bool CheckCondition();
|
||||
bool Save(obs_data_t *obj);
|
||||
bool Load(obs_data_t *obj);
|
||||
std::string GetId() { return id; };
|
||||
static std::shared_ptr<MacroCondition> Create()
|
||||
{
|
||||
return std::make_shared<MacroConditionRegion>();
|
||||
}
|
||||
|
||||
int _minX = 0, _minY = 0, _maxX = 0, _maxY = 0;
|
||||
|
||||
private:
|
||||
static bool _registered;
|
||||
static const std::string id;
|
||||
};
|
||||
|
||||
class MacroConditionRegionEdit : public QWidget {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
MacroConditionRegionEdit(
|
||||
QWidget *parent,
|
||||
std::shared_ptr<MacroConditionRegion> cond = nullptr);
|
||||
void UpdateEntryData();
|
||||
static QWidget *Create(QWidget *parent,
|
||||
std::shared_ptr<MacroCondition> cond)
|
||||
{
|
||||
return new MacroConditionRegionEdit(
|
||||
parent,
|
||||
std::dynamic_pointer_cast<MacroConditionRegion>(cond));
|
||||
}
|
||||
|
||||
private slots:
|
||||
void MinXChanged(int pos);
|
||||
void MinYChanged(int pos);
|
||||
void MaxXChanged(int pos);
|
||||
void MaxYChanged(int pos);
|
||||
|
||||
protected:
|
||||
QSpinBox *_minX;
|
||||
QSpinBox *_minY;
|
||||
QSpinBox *_maxX;
|
||||
QSpinBox *_maxY;
|
||||
std::shared_ptr<MacroConditionRegion> _entryData;
|
||||
|
||||
private:
|
||||
bool _loading = true;
|
||||
};
|
||||
57
src/headers/macro-condition-scene.hpp
Normal file
57
src/headers/macro-condition-scene.hpp
Normal file
@@ -0,0 +1,57 @@
|
||||
#pragma once
|
||||
#include "macro.hpp"
|
||||
#include <QWidget>
|
||||
#include <QComboBox>
|
||||
|
||||
enum class SceneType {
|
||||
CURRENT,
|
||||
PREVIOUS,
|
||||
};
|
||||
|
||||
class MacroConditionScene : public MacroCondition {
|
||||
public:
|
||||
bool CheckCondition();
|
||||
bool Save(obs_data_t *obj);
|
||||
bool Load(obs_data_t *obj);
|
||||
std::string GetId() { return id; };
|
||||
static std::shared_ptr<MacroCondition> Create()
|
||||
{
|
||||
return std::make_shared<MacroConditionScene>();
|
||||
}
|
||||
|
||||
OBSWeakSource _scene;
|
||||
SceneType _type;
|
||||
|
||||
private:
|
||||
static bool _registered;
|
||||
static const std::string id;
|
||||
};
|
||||
|
||||
class MacroConditionSceneEdit : public QWidget {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
MacroConditionSceneEdit(
|
||||
QWidget *parent,
|
||||
std::shared_ptr<MacroConditionScene> cond = nullptr);
|
||||
void UpdateEntryData();
|
||||
static QWidget *Create(QWidget *parent,
|
||||
std::shared_ptr<MacroCondition> cond)
|
||||
{
|
||||
return new MacroConditionSceneEdit(
|
||||
parent,
|
||||
std::dynamic_pointer_cast<MacroConditionScene>(cond));
|
||||
}
|
||||
|
||||
private slots:
|
||||
void SceneChanged(const QString &text);
|
||||
void TypeChanged(int value);
|
||||
|
||||
protected:
|
||||
QComboBox *_sceneSelection;
|
||||
QComboBox *_sceneType;
|
||||
std::shared_ptr<MacroConditionScene> _entryData;
|
||||
|
||||
private:
|
||||
bool _loading = true;
|
||||
};
|
||||
68
src/headers/macro-condition-source.hpp
Normal file
68
src/headers/macro-condition-source.hpp
Normal file
@@ -0,0 +1,68 @@
|
||||
#pragma once
|
||||
#include "macro.hpp"
|
||||
#include <QWidget>
|
||||
#include <QComboBox>
|
||||
|
||||
enum class SourceCondition {
|
||||
ACTIVE,
|
||||
SHOWING,
|
||||
SETTINGS,
|
||||
};
|
||||
|
||||
class MacroConditionSource : public MacroCondition {
|
||||
public:
|
||||
bool CheckCondition();
|
||||
bool Save(obs_data_t *obj);
|
||||
bool Load(obs_data_t *obj);
|
||||
std::string GetId() { return id; };
|
||||
static std::shared_ptr<MacroCondition> Create()
|
||||
{
|
||||
return std::make_shared<MacroConditionSource>();
|
||||
}
|
||||
|
||||
OBSWeakSource _source = nullptr;
|
||||
SourceCondition _condition = SourceCondition::ACTIVE;
|
||||
std::string _settings = "";
|
||||
bool _regex = false;
|
||||
|
||||
private:
|
||||
static bool _registered;
|
||||
static const std::string id;
|
||||
};
|
||||
|
||||
class MacroConditionSourceEdit : public QWidget {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
MacroConditionSourceEdit(
|
||||
QWidget *parent,
|
||||
std::shared_ptr<MacroConditionSource> cond = nullptr);
|
||||
void UpdateEntryData();
|
||||
static QWidget *Create(QWidget *parent,
|
||||
std::shared_ptr<MacroCondition> cond)
|
||||
{
|
||||
return new MacroConditionSourceEdit(
|
||||
parent,
|
||||
std::dynamic_pointer_cast<MacroConditionSource>(cond));
|
||||
}
|
||||
|
||||
private slots:
|
||||
void SourceChanged(const QString &text);
|
||||
void ConditionChanged(int cond);
|
||||
void GetSettingsClicked();
|
||||
void SettingsChanged();
|
||||
void RegexChanged(int);
|
||||
|
||||
protected:
|
||||
QComboBox *_sources;
|
||||
QComboBox *_conditions;
|
||||
QPushButton *_getSettings;
|
||||
QPlainTextEdit *_settings;
|
||||
QCheckBox *_regex;
|
||||
|
||||
std::shared_ptr<MacroConditionSource> _entryData;
|
||||
|
||||
private:
|
||||
void SetSettingsSelectionVisible(bool visible);
|
||||
bool _loading = true;
|
||||
};
|
||||
54
src/headers/macro-condition-streaming.hpp
Normal file
54
src/headers/macro-condition-streaming.hpp
Normal file
@@ -0,0 +1,54 @@
|
||||
#pragma once
|
||||
#include "macro.hpp"
|
||||
#include <QWidget>
|
||||
#include <QComboBox>
|
||||
|
||||
enum class StreamState {
|
||||
STOP,
|
||||
START,
|
||||
};
|
||||
|
||||
class MacroConditionStream : public MacroCondition {
|
||||
public:
|
||||
bool CheckCondition();
|
||||
bool Save(obs_data_t *obj);
|
||||
bool Load(obs_data_t *obj);
|
||||
std::string GetId() { return id; };
|
||||
static std::shared_ptr<MacroCondition> Create()
|
||||
{
|
||||
return std::make_shared<MacroConditionStream>();
|
||||
}
|
||||
|
||||
StreamState _streamState;
|
||||
|
||||
private:
|
||||
static bool _registered;
|
||||
static const std::string id;
|
||||
};
|
||||
|
||||
class MacroConditionStreamEdit : public QWidget {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
MacroConditionStreamEdit(
|
||||
QWidget *parent,
|
||||
std::shared_ptr<MacroConditionStream> cond = nullptr);
|
||||
void UpdateEntryData();
|
||||
static QWidget *Create(QWidget *parent,
|
||||
std::shared_ptr<MacroCondition> cond)
|
||||
{
|
||||
return new MacroConditionStreamEdit(
|
||||
parent,
|
||||
std::dynamic_pointer_cast<MacroConditionStream>(cond));
|
||||
}
|
||||
|
||||
private slots:
|
||||
void StateChanged(int value);
|
||||
|
||||
protected:
|
||||
QComboBox *_streamState;
|
||||
std::shared_ptr<MacroConditionStream> _entryData;
|
||||
|
||||
private:
|
||||
bool _loading = true;
|
||||
};
|
||||
78
src/headers/macro-condition-video.hpp
Normal file
78
src/headers/macro-condition-video.hpp
Normal file
@@ -0,0 +1,78 @@
|
||||
#pragma once
|
||||
#include "macro.hpp"
|
||||
#include "screenshot-helper.hpp"
|
||||
|
||||
#include <QWidget>
|
||||
#include <QComboBox>
|
||||
#include <chrono>
|
||||
|
||||
enum class VideoCondition {
|
||||
MATCH,
|
||||
DIFFER,
|
||||
HAS_NOT_CHANGED,
|
||||
HAS_CHANGED,
|
||||
NO_IMAGE,
|
||||
};
|
||||
|
||||
class MacroConditionVideo : public MacroCondition {
|
||||
public:
|
||||
bool CheckCondition();
|
||||
bool Save(obs_data_t *obj);
|
||||
bool Load(obs_data_t *obj);
|
||||
std::string GetId() { return id; };
|
||||
QImage GetMatchImage() { return _matchImage; };
|
||||
static std::shared_ptr<MacroCondition> Create()
|
||||
{
|
||||
return std::make_shared<MacroConditionVideo>();
|
||||
}
|
||||
void GetScreenshot();
|
||||
bool LoadImageFromFile();
|
||||
|
||||
OBSWeakSource _videoSource;
|
||||
VideoCondition _condition = VideoCondition::MATCH;
|
||||
std::string _file = obs_module_text("AdvSceneSwitcher.enterPath");
|
||||
|
||||
private:
|
||||
bool Compare();
|
||||
|
||||
std::unique_ptr<AdvSSScreenshotObj> _screenshotData = nullptr;
|
||||
QImage _matchImage;
|
||||
static bool _registered;
|
||||
static const std::string id;
|
||||
};
|
||||
|
||||
class MacroConditionVideoEdit : public QWidget {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
MacroConditionVideoEdit(
|
||||
QWidget *parent,
|
||||
std::shared_ptr<MacroConditionVideo> cond = nullptr);
|
||||
void UpdateEntryData();
|
||||
static QWidget *Create(QWidget *parent,
|
||||
std::shared_ptr<MacroCondition> cond)
|
||||
{
|
||||
return new MacroConditionVideoEdit(
|
||||
parent,
|
||||
std::dynamic_pointer_cast<MacroConditionVideo>(cond));
|
||||
}
|
||||
|
||||
void UpdatePreviewTooltip();
|
||||
void SetFilePath(const QString &text);
|
||||
|
||||
private slots:
|
||||
void SourceChanged(const QString &text);
|
||||
void ConditionChanged(int cond);
|
||||
void FilePathChanged();
|
||||
void BrowseButtonClicked();
|
||||
|
||||
protected:
|
||||
QComboBox *_videoSelection;
|
||||
QComboBox *_condition;
|
||||
QLineEdit *_filePath;
|
||||
QPushButton *_browseButton;
|
||||
std::shared_ptr<MacroConditionVideo> _entryData;
|
||||
|
||||
private:
|
||||
bool _loading = true;
|
||||
};
|
||||
51
src/headers/macro-condition-virt-desktop.hpp
Normal file
51
src/headers/macro-condition-virt-desktop.hpp
Normal file
@@ -0,0 +1,51 @@
|
||||
#pragma once
|
||||
#include "macro.hpp"
|
||||
#include <QWidget>
|
||||
#include <QComboBox>
|
||||
|
||||
class MacroConditionVirtDesktop : public MacroCondition {
|
||||
public:
|
||||
bool CheckCondition();
|
||||
bool Save(obs_data_t *obj);
|
||||
bool Load(obs_data_t *obj);
|
||||
std::string GetId() { return id; };
|
||||
static std::shared_ptr<MacroCondition> Create()
|
||||
{
|
||||
return std::make_shared<MacroConditionVirtDesktop>();
|
||||
}
|
||||
long _desktop = 0;
|
||||
|
||||
private:
|
||||
static bool _registered;
|
||||
static const std::string id;
|
||||
};
|
||||
|
||||
class MacroConditionVirtDesktopEdit : public QWidget {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
MacroConditionVirtDesktopEdit(
|
||||
QWidget *parent,
|
||||
std::shared_ptr<MacroConditionVirtDesktop> cond = nullptr);
|
||||
void UpdateEntryData();
|
||||
static QWidget *Create(QWidget *parent,
|
||||
std::shared_ptr<MacroCondition> cond)
|
||||
{
|
||||
return new MacroConditionVirtDesktopEdit(
|
||||
parent,
|
||||
std::dynamic_pointer_cast<MacroConditionVirtDesktop>(
|
||||
cond));
|
||||
}
|
||||
|
||||
private slots:
|
||||
void DesktopChanged(int value);
|
||||
void UpdateCurrentDesktop();
|
||||
|
||||
protected:
|
||||
QComboBox *_virtDesktops;
|
||||
QLabel *_currentDesktop;
|
||||
std::shared_ptr<MacroConditionVirtDesktop> _entryData;
|
||||
|
||||
private:
|
||||
bool _loading = true;
|
||||
};
|
||||
64
src/headers/macro-condition-window.hpp
Normal file
64
src/headers/macro-condition-window.hpp
Normal file
@@ -0,0 +1,64 @@
|
||||
#pragma once
|
||||
#include "macro.hpp"
|
||||
#include <QWidget>
|
||||
#include <QComboBox>
|
||||
|
||||
class MacroConditionWindow : public MacroCondition {
|
||||
public:
|
||||
bool CheckCondition();
|
||||
bool Save(obs_data_t *obj);
|
||||
bool Load(obs_data_t *obj);
|
||||
std::string GetId() { return id; };
|
||||
static std::shared_ptr<MacroCondition> Create()
|
||||
{
|
||||
return std::make_shared<MacroConditionWindow>();
|
||||
}
|
||||
|
||||
private:
|
||||
bool CheckWindowTitleSwitchDirect(std::string ¤tWindowTitle);
|
||||
bool CheckWindowTitleSwitchRegex(std::string ¤tWindowTitle,
|
||||
std::vector<std::string> &windowList);
|
||||
|
||||
public:
|
||||
std::string _window;
|
||||
bool _fullscreen = false;
|
||||
bool _maximized = false;
|
||||
bool _focus = true;
|
||||
|
||||
private:
|
||||
static bool _registered;
|
||||
static const std::string id;
|
||||
};
|
||||
|
||||
class MacroConditionWindowEdit : public QWidget {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
MacroConditionWindowEdit(
|
||||
QWidget *parent,
|
||||
std::shared_ptr<MacroConditionWindow> cond = nullptr);
|
||||
void UpdateEntryData();
|
||||
static QWidget *Create(QWidget *parent,
|
||||
std::shared_ptr<MacroCondition> cond)
|
||||
{
|
||||
return new MacroConditionWindowEdit(
|
||||
parent,
|
||||
std::dynamic_pointer_cast<MacroConditionWindow>(cond));
|
||||
}
|
||||
|
||||
private slots:
|
||||
void WindowChanged(const QString &text);
|
||||
void FullscreenChanged(int state);
|
||||
void MaximizedChanged(int state);
|
||||
void FocusChanged(int state);
|
||||
|
||||
protected:
|
||||
QComboBox *_windowSelection;
|
||||
QCheckBox *_fullscreen;
|
||||
QCheckBox *_maximized;
|
||||
QCheckBox *_focused;
|
||||
std::shared_ptr<MacroConditionWindow> _entryData;
|
||||
|
||||
private:
|
||||
bool _loading = true;
|
||||
};
|
||||
17
src/headers/macro-selection.hpp
Normal file
17
src/headers/macro-selection.hpp
Normal file
@@ -0,0 +1,17 @@
|
||||
#pragma once
|
||||
#include <QComboBox>
|
||||
|
||||
class Macro;
|
||||
|
||||
class MacroSelection : public QComboBox {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
MacroSelection(QWidget *parent);
|
||||
void SetCurrentMacro(Macro *);
|
||||
|
||||
private slots:
|
||||
void MacroAdd(const QString &name);
|
||||
void MacroRemove(const QString &name);
|
||||
void MacroRename(const QString &oldName, const QString &newName);
|
||||
};
|
||||
146
src/headers/macro.hpp
Normal file
146
src/headers/macro.hpp
Normal file
@@ -0,0 +1,146 @@
|
||||
#pragma once
|
||||
#include "duration-control.hpp"
|
||||
|
||||
#include <string>
|
||||
#include <deque>
|
||||
#include <memory>
|
||||
#include <map>
|
||||
#include <obs.hpp>
|
||||
#include <obs-module.h>
|
||||
#include <QString>
|
||||
|
||||
constexpr auto macro_func = 10;
|
||||
constexpr auto default_priority_10 = macro_func;
|
||||
|
||||
constexpr auto logic_root_offset = 100;
|
||||
|
||||
enum class LogicType {
|
||||
ROOT_NONE = 0,
|
||||
ROOT_NOT,
|
||||
ROOT_LAST,
|
||||
// leave some space for potential expansion
|
||||
NONE = 100,
|
||||
AND,
|
||||
OR,
|
||||
AND_NOT,
|
||||
OR_NOT,
|
||||
LAST,
|
||||
};
|
||||
|
||||
static inline bool isRootLogicType(LogicType l)
|
||||
{
|
||||
return static_cast<int>(l) < logic_root_offset;
|
||||
}
|
||||
|
||||
struct LogicTypeInfo {
|
||||
std::string _name;
|
||||
};
|
||||
|
||||
class MacroCondition {
|
||||
public:
|
||||
virtual bool CheckCondition() = 0;
|
||||
virtual bool Save(obs_data_t *obj) = 0;
|
||||
virtual bool Load(obs_data_t *obj) = 0;
|
||||
virtual std::string GetId() = 0;
|
||||
|
||||
LogicType GetLogicType() { return _logic; }
|
||||
void SetLogicType(LogicType logic) { _logic = logic; }
|
||||
static const std::map<LogicType, LogicTypeInfo> logicTypes;
|
||||
|
||||
bool DurationReached() { return _duration.DurationReached(); }
|
||||
void ResetDuration() { _duration.Reset(); }
|
||||
DurationConstraint GetDurationConstraint() { return _duration; }
|
||||
void SetDurationConstraint(const DurationConstraint &dur);
|
||||
void SetDurationCondition(DurationCondition cond);
|
||||
void SetDurationUnit(DurationUnit u);
|
||||
void SetDuration(double seconds);
|
||||
|
||||
private:
|
||||
LogicType _logic;
|
||||
DurationConstraint _duration;
|
||||
};
|
||||
|
||||
class MacroAction {
|
||||
public:
|
||||
virtual bool PerformAction() = 0;
|
||||
virtual bool Save(obs_data_t *obj) = 0;
|
||||
virtual bool Load(obs_data_t *obj) = 0;
|
||||
virtual std::string GetId() = 0;
|
||||
virtual void LogAction();
|
||||
};
|
||||
|
||||
class Macro {
|
||||
public:
|
||||
Macro(const std::string &name = "");
|
||||
virtual ~Macro();
|
||||
|
||||
bool CeckMatch();
|
||||
bool PerformAction();
|
||||
bool Matched() { return _matched; }
|
||||
std::string Name() { return _name; }
|
||||
void SetName(const std::string &name);
|
||||
void SetPaused(bool pause = true) { _paused = pause; }
|
||||
bool Paused() { return _paused; }
|
||||
int GetCount() { return _count; };
|
||||
void ResetCount() { _count = 0; };
|
||||
std::deque<std::shared_ptr<MacroCondition>> &Conditions()
|
||||
{
|
||||
return _conditions;
|
||||
}
|
||||
std::deque<std::shared_ptr<MacroAction>> &Actions() { return _actions; }
|
||||
|
||||
bool Save(obs_data_t *obj);
|
||||
bool Load(obs_data_t *obj);
|
||||
// Some macros can refer to other macros, which are not yet loaded.
|
||||
// Use this function to set these references after loading is complete.
|
||||
void ResolveMacroRef();
|
||||
|
||||
// Helper function for plugin state condition regarding scene change
|
||||
bool SwitchesScene();
|
||||
|
||||
private:
|
||||
void SetupHotkeys();
|
||||
void ClearHotkeys();
|
||||
void SetHotkeysDesc();
|
||||
|
||||
std::string _name = "";
|
||||
std::deque<std::shared_ptr<MacroCondition>> _conditions;
|
||||
std::deque<std::shared_ptr<MacroAction>> _actions;
|
||||
bool _matched = false;
|
||||
bool _paused = false;
|
||||
int _count = 0;
|
||||
obs_hotkey_id _pauseHotkey = OBS_INVALID_HOTKEY_ID;
|
||||
obs_hotkey_id _unpauseHotkey = OBS_INVALID_HOTKEY_ID;
|
||||
};
|
||||
|
||||
Macro *GetMacroByName(const char *name);
|
||||
Macro *GetMacroByQString(const QString &name);
|
||||
|
||||
class MacroRef {
|
||||
public:
|
||||
MacroRef(){};
|
||||
MacroRef(std::string name);
|
||||
void UpdateRef();
|
||||
void UpdateRef(std::string name);
|
||||
void UpdateRef(QString name);
|
||||
void Save(obs_data_t *obj);
|
||||
void Load(obs_data_t *obj);
|
||||
Macro *get();
|
||||
Macro *operator->();
|
||||
|
||||
private:
|
||||
std::string _name = "";
|
||||
Macro *_ref = nullptr;
|
||||
};
|
||||
|
||||
class MacroRefCondition : public MacroCondition {
|
||||
public:
|
||||
void ResolveMacroRef();
|
||||
MacroRef _macro;
|
||||
};
|
||||
|
||||
class MacroRefAction : public MacroAction {
|
||||
public:
|
||||
void ResolveMacroRef();
|
||||
MacroRef _macro;
|
||||
};
|
||||
24
src/headers/name-dialog.hpp
Normal file
24
src/headers/name-dialog.hpp
Normal file
@@ -0,0 +1,24 @@
|
||||
#pragma once
|
||||
|
||||
#include <QDialog>
|
||||
#include <QLabel>
|
||||
#include <QLineEdit>
|
||||
|
||||
// Based on OBS's NameDialog
|
||||
class AdvSSNameDialog : public QDialog {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
AdvSSNameDialog(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;
|
||||
};
|
||||
13
src/headers/platform-funcs.hpp
Normal file
13
src/headers/platform-funcs.hpp
Normal file
@@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
void GetWindowList(std::vector<std::string> &windows);
|
||||
void GetWindowList(QStringList &windows);
|
||||
void GetCurrentWindowTitle(std::string &title);
|
||||
bool isFullscreen(const std::string &title);
|
||||
bool isMaximized(const std::string &title);
|
||||
std::pair<int, int> getCursorPos();
|
||||
int secondsSinceLastInput();
|
||||
void GetProcessList(QStringList &processes);
|
||||
bool isInFocus(const QString &executable);
|
||||
bool GetCurrentVirtualDesktop(long &desktop);
|
||||
bool GetVirtualDesktopCount(long &ndesktops);
|
||||
@@ -37,12 +37,12 @@ struct SceneGroup {
|
||||
|
||||
int currentCount = -1;
|
||||
std::chrono::high_resolution_clock::time_point lastAdvTime;
|
||||
OBSWeakSource lastRandomScene = nullptr;
|
||||
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_,
|
||||
inline SceneGroup(const std::string &name_) : name(name_){};
|
||||
inline SceneGroup(const std::string &name_, AdvanceCondition type_,
|
||||
const std::vector<OBSWeakSource> &scenes_, int count_,
|
||||
double time_, bool repeat_)
|
||||
: name(name_),
|
||||
type(type_),
|
||||
@@ -54,11 +54,6 @@ struct SceneGroup {
|
||||
}
|
||||
};
|
||||
|
||||
enum class SwitchTargetType {
|
||||
Scene,
|
||||
SceneGroup,
|
||||
};
|
||||
|
||||
class SceneGroupEditWidget : public QWidget {
|
||||
Q_OBJECT
|
||||
|
||||
@@ -87,22 +82,3 @@ private:
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
@@ -23,6 +23,10 @@ enum class sceneTriggerAction {
|
||||
STOP_REPLAY_BUFFER,
|
||||
|
||||
MUTE_SOURCE,
|
||||
UNMUTE_SOURCE,
|
||||
|
||||
START_SWITCHER,
|
||||
STOP_SWITCHER,
|
||||
};
|
||||
|
||||
struct SceneTrigger : SceneSwitcherEntry {
|
||||
@@ -36,7 +40,8 @@ struct SceneTrigger : SceneSwitcherEntry {
|
||||
void save(obs_data_t *obj);
|
||||
void load(obs_data_t *obj);
|
||||
|
||||
bool checkMatch(OBSWeakSource previousScene);
|
||||
bool checkMatch(OBSWeakSource currentScene,
|
||||
OBSWeakSource previousScene);
|
||||
void performAction();
|
||||
void logMatch();
|
||||
};
|
||||
|
||||
29
src/headers/screenshot-helper.hpp
Normal file
29
src/headers/screenshot-helper.hpp
Normal file
@@ -0,0 +1,29 @@
|
||||
#pragma once
|
||||
#include <obs.hpp>
|
||||
#include <string>
|
||||
#include <QImage>
|
||||
#include <chrono>
|
||||
|
||||
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;
|
||||
};
|
||||
44
src/headers/section.hpp
Normal file
44
src/headers/section.hpp
Normal file
@@ -0,0 +1,44 @@
|
||||
#pragma once
|
||||
|
||||
#include <QFrame>
|
||||
#include <QGridLayout>
|
||||
#include <QParallelAnimationGroup>
|
||||
#include <QScrollArea>
|
||||
#include <QToolButton>
|
||||
#include <QWidget>
|
||||
|
||||
class Section : public QWidget {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit Section(const int animationDuration = 300,
|
||||
QWidget *parent = 0);
|
||||
|
||||
void SetContent(QWidget *w, bool collapsed = true);
|
||||
void AddHeaderWidget(QWidget *);
|
||||
|
||||
protected:
|
||||
bool eventFilter(QObject *obj, QEvent *event) override;
|
||||
|
||||
private slots:
|
||||
void AnimationFinished();
|
||||
void Collapse(bool collapse);
|
||||
|
||||
private:
|
||||
void SetupAnimations();
|
||||
void CleanUpPreviousContent();
|
||||
|
||||
QGridLayout *_mainLayout;
|
||||
QHBoxLayout *_headerWidgetLayout;
|
||||
QToolButton *_toggleButton;
|
||||
QFrame *_headerLine;
|
||||
QParallelAnimationGroup *_toggleAnimation = nullptr;
|
||||
QParallelAnimationGroup *_contentAnimation = nullptr;
|
||||
QScrollArea *_contentArea = nullptr;
|
||||
QWidget *_content = nullptr;
|
||||
int _animationDuration;
|
||||
std::atomic_bool _transitioning = {false};
|
||||
std::atomic_bool _collapsed = {false};
|
||||
int _headerHeight = 0;
|
||||
int _contentHeight = 0;
|
||||
};
|
||||
@@ -1,7 +1,9 @@
|
||||
#pragma once
|
||||
#include <QSpinBox>
|
||||
#include <limits>
|
||||
|
||||
#include "switch-generic.hpp"
|
||||
#include "duration-control.hpp"
|
||||
#include "volume-control.hpp"
|
||||
|
||||
constexpr auto audio_func = 8;
|
||||
@@ -17,9 +19,9 @@ struct AudioSwitch : virtual SceneSwitcherEntry {
|
||||
OBSWeakSource audioSource = nullptr;
|
||||
int volumeThreshold = 0;
|
||||
audioCondition condition = ABOVE;
|
||||
double duration = 0;
|
||||
unsigned int matchCount = 0;
|
||||
float peak = -1;
|
||||
Duration duration;
|
||||
bool ignoreInactiveSource = true;
|
||||
float peak = -std::numeric_limits<float>::infinity();
|
||||
obs_volmeter_t *volmeter = nullptr;
|
||||
|
||||
const char *getType() { return "audio"; }
|
||||
@@ -48,8 +50,7 @@ struct AudioSwitchFallback : virtual SceneSwitcherEntry {
|
||||
void load(obs_data_t *obj);
|
||||
|
||||
bool enable = false;
|
||||
double duration = 0;
|
||||
unsigned int matchCount = 0;
|
||||
Duration duration;
|
||||
};
|
||||
|
||||
class AudioSwitchWidget : public SwitchWidget {
|
||||
@@ -69,12 +70,14 @@ private slots:
|
||||
void VolumeThresholdChanged(int vol);
|
||||
void ConditionChanged(int cond);
|
||||
void DurationChanged(double dur);
|
||||
void IgnoreInactiveChanged(int state);
|
||||
|
||||
private:
|
||||
QComboBox *audioSources;
|
||||
QComboBox *condition;
|
||||
QSpinBox *audioVolumeThreshold;
|
||||
QDoubleSpinBox *duration;
|
||||
DurationSelection *duration;
|
||||
QCheckBox *ignoreInactiveSource;
|
||||
VolControl *volMeter;
|
||||
|
||||
AudioSwitch *switchData;
|
||||
@@ -90,7 +93,7 @@ private slots:
|
||||
void DurationChanged(double dur);
|
||||
|
||||
private:
|
||||
QDoubleSpinBox *duration;
|
||||
DurationSelection *duration;
|
||||
|
||||
AudioSwitchFallback *switchData;
|
||||
};
|
||||
|
||||
@@ -2,12 +2,20 @@
|
||||
#include <obs.hpp>
|
||||
#include <QComboBox>
|
||||
|
||||
#include "scene-group.hpp"
|
||||
|
||||
enum class SwitchTargetType {
|
||||
Scene,
|
||||
SceneGroup,
|
||||
};
|
||||
|
||||
struct SceneSwitcherEntry {
|
||||
SwitchTargetType targetType = SwitchTargetType::Scene;
|
||||
SceneGroup *group = nullptr;
|
||||
OBSWeakSource scene = nullptr;
|
||||
OBSWeakSource transition = nullptr;
|
||||
bool usePreviousScene = false;
|
||||
bool useCurrentTransition = false;
|
||||
|
||||
virtual const char *getType() = 0;
|
||||
virtual bool initialized();
|
||||
@@ -64,14 +72,15 @@ class SwitchWidget : public QWidget {
|
||||
|
||||
public:
|
||||
SwitchWidget(QWidget *parent, SceneSwitcherEntry *s,
|
||||
bool usePreviousScene = true, bool addSceneGroup = false);
|
||||
bool usePreviousScene = true, bool addSceneGroup = false,
|
||||
bool addCurrentTransition = true);
|
||||
virtual SceneSwitcherEntry *getSwitchData();
|
||||
virtual void setSwitchData(SceneSwitcherEntry *s);
|
||||
void showSwitchData();
|
||||
|
||||
static void swapSwitchData(SwitchWidget *s1, SwitchWidget *s2);
|
||||
|
||||
private slots:
|
||||
protected slots:
|
||||
void SceneChanged(const QString &text);
|
||||
void TransitionChanged(const QString &text);
|
||||
void SceneGroupAdd(const QString &name);
|
||||
|
||||
119
src/headers/switch-network.hpp
Normal file
119
src/headers/switch-network.hpp
Normal file
@@ -0,0 +1,119 @@
|
||||
/*
|
||||
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;
|
||||
|
||||
struct sceneSwitchInfo;
|
||||
|
||||
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();
|
||||
|
||||
bool ShouldSendSceneChange();
|
||||
bool ShouldSendFrontendSceneChange();
|
||||
bool ShouldSendPrviewSceneChange();
|
||||
|
||||
// Server
|
||||
bool ServerEnabled;
|
||||
uint64_t ServerPort;
|
||||
bool LockToIPv4;
|
||||
|
||||
// Client
|
||||
bool ClientEnabled;
|
||||
std::string Address;
|
||||
uint64_t ClientPort;
|
||||
bool SendSceneChange;
|
||||
bool SendSceneChangeAll;
|
||||
bool SendPreview;
|
||||
};
|
||||
|
||||
class WSServer : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit WSServer();
|
||||
virtual ~WSServer();
|
||||
void start(quint16 port, bool lockToIPv4);
|
||||
void stop();
|
||||
void sendMessage(sceneSwitchInfo sceneSwitch, bool preview = false);
|
||||
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,
|
||||
Sequence,
|
||||
Audio,
|
||||
Video,
|
||||
};
|
||||
|
||||
struct PauseEntry : SceneSwitcherEntry {
|
||||
|
||||
@@ -1,53 +1,81 @@
|
||||
#pragma once
|
||||
#include <QVBoxLayout>
|
||||
|
||||
#include "switch-generic.hpp"
|
||||
#include "duration-control.hpp"
|
||||
|
||||
constexpr auto round_trip_func = 1;
|
||||
constexpr auto default_priority_1 = round_trip_func;
|
||||
|
||||
typedef enum {
|
||||
SECONDS,
|
||||
MINUTES,
|
||||
HOURS,
|
||||
} delay_units;
|
||||
|
||||
struct SceneSequenceSwitch : SceneSwitcherEntry {
|
||||
static bool pause;
|
||||
SwitchTargetType startTargetType = SwitchTargetType::Scene;
|
||||
OBSWeakSource startScene = nullptr;
|
||||
double delay = 0;
|
||||
int delayMultiplier = 1;
|
||||
Duration delay;
|
||||
bool interruptible = false;
|
||||
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"; }
|
||||
bool initialized();
|
||||
bool valid();
|
||||
void save(obs_data_t *obj);
|
||||
void load(obs_data_t *obj);
|
||||
void save(obs_data_t *obj, bool saveExt = true);
|
||||
void load(obs_data_t *obj, bool saveExt = true);
|
||||
|
||||
bool reduce();
|
||||
SceneSequenceSwitch *extend();
|
||||
|
||||
bool checkMatch(OBSWeakSource currentScene, int &linger,
|
||||
SceneSequenceSwitch *root = nullptr);
|
||||
bool checkDurationMatchInterruptible();
|
||||
void prepareUninterruptibleMatch(OBSWeakSource currentScene,
|
||||
int &linger);
|
||||
void advanceActiveSequence();
|
||||
void logAdvanceSequence();
|
||||
void logSequenceCanceled();
|
||||
};
|
||||
|
||||
class SequenceWidget : public SwitchWidget {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
SequenceWidget(QWidget *parent, SceneSequenceSwitch *s);
|
||||
SequenceWidget(QWidget *parent, SceneSequenceSwitch *s,
|
||||
bool extendSequence = false, bool editExtendMode = false,
|
||||
bool showExtendText = true);
|
||||
SceneSequenceSwitch *getSwitchData();
|
||||
void setSwitchData(SceneSequenceSwitch *s);
|
||||
|
||||
static void swapSwitchData(SequenceWidget *s1, SequenceWidget *s2);
|
||||
|
||||
void UpdateDelay();
|
||||
void UpdateWidgetStatus(bool showExtendText);
|
||||
void setExtendedSequenceStartScene();
|
||||
|
||||
private slots:
|
||||
void SceneChanged(const QString &text);
|
||||
void DelayChanged(double delay);
|
||||
void DelayUnitsChanged(int idx);
|
||||
void DelayUnitsChanged(DurationUnit);
|
||||
void StartSceneChanged(const QString &text);
|
||||
void InterruptibleChanged(int state);
|
||||
void ExtendClicked();
|
||||
void ReduceClicked();
|
||||
|
||||
private:
|
||||
QDoubleSpinBox *delay;
|
||||
QComboBox *delayUnits;
|
||||
protected:
|
||||
DurationSelection *delay;
|
||||
QComboBox *startScenes;
|
||||
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;
|
||||
};
|
||||
|
||||
@@ -1,25 +1,37 @@
|
||||
#pragma once
|
||||
#include <QDoubleSpinBox>
|
||||
|
||||
#include "switch-generic.hpp"
|
||||
|
||||
struct SceneTransition : SceneSwitcherEntry {
|
||||
OBSWeakSource scene2 = nullptr;
|
||||
double duration = 0.3;
|
||||
|
||||
const char *getType() { return "transition"; }
|
||||
bool initialized();
|
||||
bool valid();
|
||||
void save(obs_data_t *obj);
|
||||
void load(obs_data_t *obj);
|
||||
|
||||
inline SceneTransition(){};
|
||||
inline SceneTransition(OBSWeakSource scene_, OBSWeakSource scene2_,
|
||||
OBSWeakSource transition_)
|
||||
: SceneSwitcherEntry(scene_, transition_), scene2(scene2_)
|
||||
OBSWeakSource transition_, double duration_)
|
||||
: SceneSwitcherEntry(scene_, transition_),
|
||||
scene2(scene2_),
|
||||
duration(duration_)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
struct DefaultSceneTransition : SceneSwitcherEntry {
|
||||
static bool pause;
|
||||
static unsigned int delay;
|
||||
|
||||
const char *getType() { return "def_transition"; }
|
||||
void save(obs_data_t *obj);
|
||||
void load(obs_data_t *obj);
|
||||
bool checkMatch(OBSWeakSource currentScene);
|
||||
void setTransition();
|
||||
|
||||
inline DefaultSceneTransition(){};
|
||||
inline DefaultSceneTransition(OBSWeakSource scene_,
|
||||
@@ -42,9 +54,11 @@ public:
|
||||
|
||||
private slots:
|
||||
void Scene2Changed(const QString &text);
|
||||
void DurationChanged(double dur);
|
||||
|
||||
private:
|
||||
QComboBox *scenes2;
|
||||
QDoubleSpinBox *duration;
|
||||
|
||||
SceneTransition *switchData;
|
||||
};
|
||||
|
||||
76
src/headers/switch-video.hpp
Normal file
76
src/headers/switch-video.hpp
Normal file
@@ -0,0 +1,76 @@
|
||||
#pragma once
|
||||
#include <QSpinBox>
|
||||
|
||||
#include "switch-generic.hpp"
|
||||
#include "screenshot-helper.hpp"
|
||||
|
||||
constexpr auto video_func = 9;
|
||||
constexpr auto default_priority_9 = video_func;
|
||||
|
||||
enum class videoSwitchType {
|
||||
MATCH,
|
||||
DIFFER,
|
||||
HAS_NOT_CHANGED,
|
||||
HAS_CHANGED,
|
||||
};
|
||||
|
||||
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;
|
||||
};
|
||||
@@ -21,9 +21,16 @@
|
||||
#include "switch-transitions.hpp"
|
||||
#include "switch-window.hpp"
|
||||
#include "switch-sequence.hpp"
|
||||
#include "switch-video.hpp"
|
||||
#include "switch-network.hpp"
|
||||
|
||||
#include "macro.hpp"
|
||||
#include "duration-control.hpp"
|
||||
|
||||
constexpr auto default_interval = 300;
|
||||
constexpr auto previous_scene_name = "Previous Scene";
|
||||
constexpr auto current_transition_name = "Current Transition";
|
||||
constexpr auto tab_count = 18;
|
||||
|
||||
typedef enum { NO_SWITCH = 0, SWITCH = 1, RANDOM_SWITCH = 2 } NoMatch;
|
||||
typedef enum { PERSIST = 0, START = 1, STOP = 2 } StartupBehavior;
|
||||
@@ -35,6 +42,13 @@ enum class AutoStartEvent {
|
||||
RECORINDG_OR_STREAMING,
|
||||
};
|
||||
|
||||
typedef struct sceneSwitchInfo {
|
||||
OBSWeakSource scene;
|
||||
OBSWeakSource transition;
|
||||
int duration = 0;
|
||||
|
||||
} sceneSwitchInfo;
|
||||
|
||||
typedef struct transitionData {
|
||||
std::string name = "";
|
||||
int duration = 0;
|
||||
@@ -58,7 +72,8 @@ struct SwitcherData {
|
||||
bool verbose = false;
|
||||
bool disableHints = false;
|
||||
bool showFrame = false;
|
||||
bool tansitionOverrideOverride = false;
|
||||
bool transitionOverrideOverride = false;
|
||||
bool adjustActiveTransitionType = true;
|
||||
|
||||
int interval = default_interval;
|
||||
|
||||
@@ -66,16 +81,21 @@ struct SwitcherData {
|
||||
OBSWeakSource previousScene = nullptr;
|
||||
OBSWeakSource previousSceneHelper = nullptr;
|
||||
OBSWeakSource lastRandomScene;
|
||||
SceneGroup *lastRandomSceneGroup;
|
||||
OBSWeakSource nonMatchingScene;
|
||||
NoMatch switchIfNotMatching = NO_SWITCH;
|
||||
double noMatchDelay;
|
||||
double noMatchCount = 0;
|
||||
Duration noMatchDelay;
|
||||
StartupBehavior startupBehavior = PERSIST;
|
||||
AutoStartEvent autoStartEvent = AutoStartEvent::NEVER;
|
||||
|
||||
double cooldown = 0.;
|
||||
Duration cooldown;
|
||||
std::chrono::high_resolution_clock::time_point lastMatchTime;
|
||||
|
||||
std::deque<Macro> macros;
|
||||
// Allow only one macro scene change per scene switcher interval
|
||||
// with the top macro having the highest priority
|
||||
bool macroSceneSwitched = false;
|
||||
|
||||
std::deque<WindowSwitch> windowSwitches;
|
||||
std::vector<std::string> ignoreIdleWindows;
|
||||
std::string lastTitle;
|
||||
@@ -84,6 +104,7 @@ struct SwitcherData {
|
||||
|
||||
std::vector<std::string> ignoreWindowsSwitches;
|
||||
|
||||
bool uninterruptibleSceneSequenceActive = false;
|
||||
std::deque<SceneSequenceSwitch> sceneSequenceSwitches;
|
||||
|
||||
std::deque<RandomSwitch> randomSwitches;
|
||||
@@ -100,7 +121,6 @@ struct SwitcherData {
|
||||
|
||||
std::deque<SceneTransition> sceneTransitions;
|
||||
std::deque<DefaultSceneTransition> defaultSceneTransitions;
|
||||
bool checkedDefTransition = false;
|
||||
|
||||
std::deque<MediaSwitch> mediaSwitches;
|
||||
|
||||
@@ -112,12 +132,21 @@ struct SwitcherData {
|
||||
std::deque<AudioSwitch> audioSwitches;
|
||||
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>{
|
||||
default_priority_0, default_priority_1, default_priority_2,
|
||||
default_priority_3, default_priority_4, default_priority_5,
|
||||
default_priority_6, default_priority_7, default_priority_8};
|
||||
default_priority_6, default_priority_7, default_priority_8,
|
||||
default_priority_9, default_priority_10};
|
||||
|
||||
struct ThreadPrio {
|
||||
std::string name;
|
||||
@@ -146,57 +175,67 @@ struct SwitcherData {
|
||||
|
||||
uint32_t threadPriority = QThread::NormalPriority;
|
||||
|
||||
std::vector<int> tabOrder;
|
||||
std::vector<int> tabOrder = std::vector<int>(tab_count);
|
||||
|
||||
bool hotkeysRegistered = false;
|
||||
obs_hotkey_id startHotkey = 0;
|
||||
obs_hotkey_id stopHotkey = 0;
|
||||
obs_hotkey_id toggleHotkey = 0;
|
||||
|
||||
bool saveWindowGeo = false;
|
||||
QPoint windowPos = {};
|
||||
QSize windowSize = {};
|
||||
|
||||
bool versionChanged(obs_data_t *obj, std::string currentVersion);
|
||||
|
||||
void Thread();
|
||||
void Start();
|
||||
void Stop();
|
||||
|
||||
void setWaitScene();
|
||||
bool sceneChangedDuringWait();
|
||||
|
||||
bool prioFuncsValid();
|
||||
|
||||
bool tabOrderValid();
|
||||
void resetTabOrder();
|
||||
|
||||
void writeSceneInfoToFile();
|
||||
void writeToStatusFile(QString status);
|
||||
bool checkPause();
|
||||
void checkDefaultSceneTransitions(bool &match,
|
||||
OBSWeakSource &transition);
|
||||
void setCurrentDefTransition(OBSWeakSource &transition);
|
||||
void checkSceneSequence(bool &match, OBSWeakSource &scene,
|
||||
OBSWeakSource &transition, int &linger);
|
||||
void checkIdleSwitch(bool &match, OBSWeakSource &scene,
|
||||
OBSWeakSource &transition);
|
||||
void checkWindowTitleSwitch(bool &match, OBSWeakSource &scene,
|
||||
void writeToStatusFile(const QString &msg);
|
||||
|
||||
bool checkForMatch(OBSWeakSource &scene, OBSWeakSource &transition,
|
||||
int &linger, bool &setPreviousSceneAsMatch,
|
||||
bool ¯oMatch);
|
||||
bool checkMacros();
|
||||
bool runMacros();
|
||||
bool checkSceneSequence(OBSWeakSource &scene, OBSWeakSource &transition,
|
||||
int &linger, bool &setPrevSceneAfterLinger);
|
||||
bool checkIdleSwitch(OBSWeakSource &scene, OBSWeakSource &transition);
|
||||
bool checkWindowTitleSwitch(OBSWeakSource &scene,
|
||||
OBSWeakSource &transition);
|
||||
void checkExeSwitch(bool &match, OBSWeakSource &scene,
|
||||
OBSWeakSource &transition);
|
||||
void checkScreenRegionSwitch(bool &match, OBSWeakSource &scene,
|
||||
bool checkExeSwitch(OBSWeakSource &scene, OBSWeakSource &transition);
|
||||
bool checkScreenRegionSwitch(OBSWeakSource &scene,
|
||||
OBSWeakSource &transition);
|
||||
void checkSwitchInfoFromFile(bool &match, OBSWeakSource &scene,
|
||||
bool checkSwitchInfoFromFile(OBSWeakSource &scene,
|
||||
OBSWeakSource &transition);
|
||||
void checkFileContent(bool &match, OBSWeakSource &scene,
|
||||
OBSWeakSource &transition);
|
||||
void checkRandom(bool &match, OBSWeakSource &scene,
|
||||
OBSWeakSource &transition, int &delay);
|
||||
void checkMediaSwitch(bool &match, OBSWeakSource &scene,
|
||||
OBSWeakSource &transition);
|
||||
void checkTimeSwitch(bool &match, OBSWeakSource &scene,
|
||||
OBSWeakSource &transition);
|
||||
void checkAudioSwitch(bool &match, OBSWeakSource &scene,
|
||||
OBSWeakSource &transition);
|
||||
bool checkFileContent(OBSWeakSource &scene, OBSWeakSource &transition);
|
||||
bool checkRandom(OBSWeakSource &scene, OBSWeakSource &transition,
|
||||
int &delay);
|
||||
bool checkMediaSwitch(OBSWeakSource &scene, OBSWeakSource &transition);
|
||||
bool checkTimeSwitch(OBSWeakSource &scene, OBSWeakSource &transition);
|
||||
bool checkAudioSwitch(OBSWeakSource &scene, OBSWeakSource &transition);
|
||||
void checkAudioSwitchFallback(OBSWeakSource &scene,
|
||||
OBSWeakSource &transition);
|
||||
bool checkVideoSwitch(OBSWeakSource &scene, OBSWeakSource &transition);
|
||||
void checkNoMatchSwitch(bool &match, OBSWeakSource &scene,
|
||||
OBSWeakSource &transition, int &sleep);
|
||||
void checkSwitchCooldown(bool &match);
|
||||
void checkTriggers();
|
||||
bool checkPause();
|
||||
void checkDefaultSceneTransitions();
|
||||
|
||||
void saveSettings(obs_data_t *obj);
|
||||
void saveMacros(obs_data_t *obj);
|
||||
void saveWindowTitleSwitches(obs_data_t *obj);
|
||||
void saveScreenRegionSwitches(obs_data_t *obj);
|
||||
void savePauseSwitches(obs_data_t *obj);
|
||||
@@ -211,15 +250,17 @@ struct SwitcherData {
|
||||
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 saveHotkeys(obs_data_t *obj);
|
||||
void saveVersion(obs_data_t *obj, std::string currentVersion);
|
||||
void saveVersion(obs_data_t *obj, const std::string ¤tVersion);
|
||||
|
||||
void loadSettings(obs_data_t *obj);
|
||||
void loadMacros(obs_data_t *obj);
|
||||
void loadWindowTitleSwitches(obs_data_t *obj);
|
||||
void loadScreenRegionSwitches(obs_data_t *obj);
|
||||
void loadPauseSwitches(obs_data_t *obj);
|
||||
void loadOldPauseSwitches(obs_data_t *obj);
|
||||
void loadSceneSequenceSwitches(obs_data_t *obj);
|
||||
void loadSceneTransitions(obs_data_t *obj);
|
||||
void loadIdleSwitches(obs_data_t *obj);
|
||||
@@ -231,6 +272,8 @@ struct SwitcherData {
|
||||
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 loadHotkeys(obs_data_t *obj);
|
||||
|
||||
|
||||
@@ -1,144 +1,28 @@
|
||||
#pragma once
|
||||
|
||||
#include <QString>
|
||||
#include <QLayout>
|
||||
#include <QLabel>
|
||||
#include <unordered_map>
|
||||
|
||||
#include <QComboBox>
|
||||
#include <QMetaObject>
|
||||
#include <QListWidget>
|
||||
#include <QPushButton>
|
||||
#include <QColor>
|
||||
#include <obs.hpp>
|
||||
#include <obs-module.h>
|
||||
#include <obs-frontend-api.h>
|
||||
#include <deque>
|
||||
#include <unordered_map>
|
||||
#include "scene-group.hpp"
|
||||
|
||||
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)
|
||||
{
|
||||
obs_source_t *source = obs_weak_source_get_source(ws);
|
||||
if (source) {
|
||||
obs_source_release(source);
|
||||
}
|
||||
return !!source;
|
||||
}
|
||||
|
||||
static inline std::string GetWeakSourceName(obs_weak_source_t *weak_source)
|
||||
{
|
||||
std::string name;
|
||||
|
||||
obs_source_t *source = obs_weak_source_get_source(weak_source);
|
||||
if (source) {
|
||||
name = obs_source_get_name(source);
|
||||
obs_source_release(source);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
OBSWeakSource weak;
|
||||
obs_source_t *source = obs_get_source_by_name(name);
|
||||
if (source) {
|
||||
weak = obs_source_get_weak_source(source);
|
||||
obs_weak_source_release(weak);
|
||||
obs_source_release(source);
|
||||
}
|
||||
|
||||
return weak;
|
||||
}
|
||||
|
||||
static inline OBSWeakSource GetWeakSourceByQString(const QString &name)
|
||||
{
|
||||
return GetWeakSourceByName(name.toUtf8().constData());
|
||||
}
|
||||
|
||||
static inline OBSWeakSource GetWeakTransitionByName(const char *transitionName)
|
||||
{
|
||||
OBSWeakSource weak;
|
||||
obs_source_t *source = nullptr;
|
||||
|
||||
if (strcmp(transitionName, "Default") == 0) {
|
||||
source = obs_frontend_get_current_transition();
|
||||
weak = obs_source_get_weak_source(source);
|
||||
obs_source_release(source);
|
||||
obs_weak_source_release(weak);
|
||||
return weak;
|
||||
}
|
||||
|
||||
obs_frontend_source_list *transitions = new obs_frontend_source_list();
|
||||
obs_frontend_get_transitions(transitions);
|
||||
bool match = false;
|
||||
|
||||
for (size_t i = 0; i < transitions->sources.num; i++) {
|
||||
const char *name =
|
||||
obs_source_get_name(transitions->sources.array[i]);
|
||||
if (strcmp(transitionName, name) == 0) {
|
||||
match = true;
|
||||
source = transitions->sources.array[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (match) {
|
||||
weak = obs_source_get_weak_source(source);
|
||||
obs_weak_source_release(weak);
|
||||
}
|
||||
obs_frontend_source_list_free(transitions);
|
||||
|
||||
return weak;
|
||||
}
|
||||
|
||||
static inline OBSWeakSource GetWeakTransitionByQString(const QString &name)
|
||||
{
|
||||
return GetWeakTransitionByName(name.toUtf8().constData());
|
||||
}
|
||||
|
||||
static inline std::string
|
||||
getNextDelim(std::string text,
|
||||
std::unordered_map<std::string, QWidget *> placeholders)
|
||||
{
|
||||
size_t pos = std::string::npos;
|
||||
std::string res = "";
|
||||
|
||||
for (const auto &ph : placeholders) {
|
||||
size_t newPos = text.find(ph.first);
|
||||
if (newPos <= pos) {
|
||||
pos = newPos;
|
||||
res = ph.first;
|
||||
}
|
||||
}
|
||||
|
||||
if (pos == std::string::npos) {
|
||||
return "";
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
bool WeakSourceValid(obs_weak_source_t *ws);
|
||||
std::string GetWeakSourceName(obs_weak_source_t *weak_source);
|
||||
OBSWeakSource GetWeakSourceByName(const char *name);
|
||||
OBSWeakSource GetWeakSourceByQString(const QString &name);
|
||||
OBSWeakSource GetWeakTransitionByName(const char *transitionName);
|
||||
OBSWeakSource GetWeakTransitionByQString(const QString &name);
|
||||
OBSWeakSource GetWeakFilterByName(OBSWeakSource source, const char *name);
|
||||
OBSWeakSource GetWeakFilterByQString(OBSWeakSource source, const QString &name);
|
||||
bool compareIgnoringLineEnding(QString &s1, QString &s2);
|
||||
std::string getSourceSettings(OBSWeakSource ws);
|
||||
std::string getDataFilePath(const std::string &file);
|
||||
|
||||
/**
|
||||
* Populate layout with labels and widgets based on provided text
|
||||
@@ -148,37 +32,32 @@ getNextDelim(std::string text,
|
||||
* @param placeholders Map containing a mapping of placeholder strings to widgets.
|
||||
* @param addStretch Add addStretch() to layout.
|
||||
*/
|
||||
static inline void
|
||||
placeWidgets(std::string text, QBoxLayout *layout,
|
||||
std::unordered_map<std::string, QWidget *> placeholders,
|
||||
bool addStretch = true)
|
||||
{
|
||||
std::vector<std::pair<std::string, QWidget *>> labelsWidgetsPairs;
|
||||
|
||||
std::string delim = getNextDelim(text, placeholders);
|
||||
while (delim != "") {
|
||||
size_t pos = text.find(delim);
|
||||
if (pos != std::string::npos) {
|
||||
labelsWidgetsPairs.emplace_back(text.substr(0, pos),
|
||||
placeholders[delim]);
|
||||
text.erase(0, pos + delim.length());
|
||||
}
|
||||
delim = getNextDelim(text, placeholders);
|
||||
}
|
||||
|
||||
if (text != "") {
|
||||
labelsWidgetsPairs.emplace_back(text, nullptr);
|
||||
}
|
||||
|
||||
for (auto &lw : labelsWidgetsPairs) {
|
||||
if (lw.first != "") {
|
||||
layout->addWidget(new QLabel(lw.first.c_str()));
|
||||
}
|
||||
if (lw.second) {
|
||||
layout->addWidget(lw.second);
|
||||
}
|
||||
}
|
||||
if (addStretch) {
|
||||
layout->addStretch();
|
||||
}
|
||||
}
|
||||
void placeWidgets(std::string text, QBoxLayout *layout,
|
||||
std::unordered_map<std::string, QWidget *> placeholders,
|
||||
bool addStretch = true);
|
||||
void clearLayout(QLayout *layout);
|
||||
QMetaObject::Connection PulseWidget(QWidget *widget, QColor endColor,
|
||||
QColor = QColor(0, 0, 0, 0),
|
||||
QString specifier = "QLabel ");
|
||||
void listAddClicked(QListWidget *list, QWidget *newWidget,
|
||||
QPushButton *addButton = nullptr,
|
||||
QMetaObject::Connection *addHighlight = nullptr);
|
||||
bool listMoveUp(QListWidget *list);
|
||||
bool listMoveDown(QListWidget *list);
|
||||
bool DisplayMessage(const QString &msg, bool question = false);
|
||||
void addSelectionEntry(QComboBox *sel, const char *description,
|
||||
bool selectable = false, const char *tooltip = "");
|
||||
void populateTransitionSelection(QComboBox *sel, bool addCurrent = true,
|
||||
bool addSelect = true,
|
||||
bool selectable = false);
|
||||
void populateWindowSelection(QComboBox *sel, bool addSelect = true);
|
||||
void populateAudioSelection(QComboBox *sel, bool addSelect = true);
|
||||
void populateVideoSelection(QComboBox *sel, bool addSelect = true);
|
||||
void populateMediaSelection(QComboBox *sel, bool addSelect = true);
|
||||
void populateProcessSelection(QComboBox *sel, bool addSelect = true);
|
||||
void populateSourceSelection(QComboBox *list, bool addSelect = true);
|
||||
void populateSceneSelection(QComboBox *sel, bool addPrevious = false,
|
||||
bool addSceneGroup = false,
|
||||
std::deque<SceneGroup> *sceneGroups = nullptr,
|
||||
bool addSelect = true, std::string selectText = "",
|
||||
bool selectable = false);
|
||||
|
||||
@@ -68,41 +68,38 @@ void registerHotkeys()
|
||||
|
||||
void SwitcherData::saveHotkeys(obs_data_t *obj)
|
||||
{
|
||||
obs_data_array_t *startHotkeyArrray =
|
||||
obs_hotkey_save(switcher->startHotkey);
|
||||
obs_data_array_t *startHotkeyArrray = obs_hotkey_save(startHotkey);
|
||||
obs_data_set_array(obj, "startHotkey", startHotkeyArrray);
|
||||
obs_data_array_release(startHotkeyArrray);
|
||||
|
||||
obs_data_array_t *stopHotkeyArrray =
|
||||
obs_hotkey_save(switcher->stopHotkey);
|
||||
obs_data_array_t *stopHotkeyArrray = obs_hotkey_save(stopHotkey);
|
||||
|
||||
obs_data_set_array(obj, "stopHotkey", stopHotkeyArrray);
|
||||
obs_data_array_release(stopHotkeyArrray);
|
||||
|
||||
obs_data_array_t *toggleHotkeyArrray =
|
||||
obs_hotkey_save(switcher->toggleHotkey);
|
||||
obs_data_array_t *toggleHotkeyArrray = obs_hotkey_save(toggleHotkey);
|
||||
obs_data_set_array(obj, "toggleHotkey", toggleHotkeyArrray);
|
||||
obs_data_array_release(toggleHotkeyArrray);
|
||||
}
|
||||
|
||||
void SwitcherData::loadHotkeys(obs_data_t *obj)
|
||||
{
|
||||
if (!switcher->hotkeysRegistered) {
|
||||
if (!hotkeysRegistered) {
|
||||
registerHotkeys();
|
||||
}
|
||||
|
||||
obs_data_array_t *startHotkeyArrray =
|
||||
obs_data_get_array(obj, "startHotkey");
|
||||
obs_hotkey_load(switcher->startHotkey, startHotkeyArrray);
|
||||
obs_hotkey_load(startHotkey, startHotkeyArrray);
|
||||
obs_data_array_release(startHotkeyArrray);
|
||||
|
||||
obs_data_array_t *stopHotkeyArrray =
|
||||
obs_data_get_array(obj, "stopHotkey");
|
||||
obs_hotkey_load(switcher->stopHotkey, stopHotkeyArrray);
|
||||
obs_hotkey_load(stopHotkey, stopHotkeyArrray);
|
||||
obs_data_array_release(stopHotkeyArrray);
|
||||
|
||||
obs_data_array_t *toggleHotkeyArrray =
|
||||
obs_data_get_array(obj, "toggleHotkey");
|
||||
obs_hotkey_load(switcher->toggleHotkey, toggleHotkeyArrray);
|
||||
obs_hotkey_load(toggleHotkey, toggleHotkeyArrray);
|
||||
obs_data_array_release(toggleHotkeyArrray);
|
||||
}
|
||||
|
||||
@@ -14,7 +14,9 @@
|
||||
#undef Status
|
||||
#undef Unsorted
|
||||
#include <util/platform.h>
|
||||
#include "../headers/advanced-scene-switcher.hpp"
|
||||
#include <vector>
|
||||
#include <QStringList>
|
||||
#include <QRegularExpression>
|
||||
|
||||
static Display *xdisplay = 0;
|
||||
|
||||
@@ -203,6 +205,10 @@ void GetCurrentWindowTitle(std::string &title)
|
||||
&format, &num, &bytes,
|
||||
(uint8_t **)&data);
|
||||
|
||||
if (data == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
int status = 0;
|
||||
XTextProperty text;
|
||||
if (xstatus == Success) {
|
||||
@@ -247,7 +253,7 @@ std::pair<int, int> getCursorPos()
|
||||
return pos;
|
||||
}
|
||||
|
||||
bool isMaximized(std::string &title)
|
||||
bool isMaximized(const std::string &title)
|
||||
{
|
||||
if (!ewmhIsSupported())
|
||||
return false;
|
||||
@@ -296,7 +302,7 @@ bool isMaximized(std::string &title)
|
||||
return false;
|
||||
}
|
||||
|
||||
bool isFullscreen(std::string &title)
|
||||
bool isFullscreen(const std::string &title)
|
||||
{
|
||||
if (!ewmhIsSupported())
|
||||
return false;
|
||||
@@ -386,4 +392,68 @@ int secondsSinceLastInput()
|
||||
XCloseDisplay(display);
|
||||
|
||||
return idle_time;
|
||||
}
|
||||
}
|
||||
|
||||
bool GetCurrentVirtualDesktop(long &desktop)
|
||||
{
|
||||
Atom type;
|
||||
long unsigned nitems;
|
||||
long unsigned int bytes = 0;
|
||||
int format = 0;
|
||||
unsigned char *data;
|
||||
Window root;
|
||||
Atom request;
|
||||
Display *display = disp();
|
||||
|
||||
//TODO check for _NET_CURRENT_DESKTOP
|
||||
if (!ewmhIsSupported()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
request = XInternAtom(display, "_NET_CURRENT_DESKTOP", False);
|
||||
root = XDefaultRootWindow(display);
|
||||
|
||||
int status = XGetWindowProperty(display, root, request, 0L, 1L, false,
|
||||
XA_CARDINAL, &type, &format, &nitems,
|
||||
&bytes, &data);
|
||||
|
||||
if (status == Success && nitems > 0) {
|
||||
desktop = *((long *)data);
|
||||
} else {
|
||||
desktop = -1;
|
||||
}
|
||||
free(data);
|
||||
return desktop != -1;
|
||||
}
|
||||
|
||||
bool GetVirtualDesktopCount(long &ndesktops)
|
||||
{
|
||||
Atom type;
|
||||
long unsigned nitems;
|
||||
long unsigned int bytes = 0;
|
||||
int format = 0;
|
||||
unsigned char *data;
|
||||
Window root;
|
||||
Atom request;
|
||||
Display *display = disp();
|
||||
|
||||
//TODO check for _NET_NUMBER_OF_DESKTOPS
|
||||
if (!ewmhIsSupported()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
request = XInternAtom(display, "_NET_NUMBER_OF_DESKTOPS", False);
|
||||
root = XDefaultRootWindow(display);
|
||||
|
||||
int status = XGetWindowProperty(display, root, request, 0L, 1L, false,
|
||||
XA_CARDINAL, &type, &format, &nitems,
|
||||
&bytes, &data);
|
||||
|
||||
if (status == Success && nitems > 0) {
|
||||
ndesktops = *((long *)data);
|
||||
} else {
|
||||
ndesktops = 0;
|
||||
}
|
||||
free(data);
|
||||
return ndesktops != 0;
|
||||
}
|
||||
|
||||
191
src/macro-action-audio.cpp
Normal file
191
src/macro-action-audio.cpp
Normal file
@@ -0,0 +1,191 @@
|
||||
#include "headers/macro-action-audio.hpp"
|
||||
#include "headers/advanced-scene-switcher.hpp"
|
||||
#include "headers/utility.hpp"
|
||||
|
||||
const std::string MacroActionAudio::id = "audio";
|
||||
|
||||
bool MacroActionAudio::_registered = MacroActionFactory::Register(
|
||||
MacroActionAudio::id,
|
||||
{MacroActionAudio::Create, MacroActionAudioEdit::Create,
|
||||
"AdvSceneSwitcher.action.audio"});
|
||||
|
||||
const static std::map<AudioAction, std::string> actionTypes = {
|
||||
{AudioAction::MUTE, "AdvSceneSwitcher.action.audio.type.mute"},
|
||||
{AudioAction::UNMUTE, "AdvSceneSwitcher.action.audio.type.unmute"},
|
||||
{AudioAction::SOURCE_VOLUME,
|
||||
"AdvSceneSwitcher.action.audio.type.sourceVolume"},
|
||||
{AudioAction::MASTER_VOLUME,
|
||||
"AdvSceneSwitcher.action.audio.type.masterVolume"},
|
||||
};
|
||||
|
||||
bool MacroActionAudio::PerformAction()
|
||||
{
|
||||
auto s = obs_weak_source_get_source(_audioSource);
|
||||
switch (_action) {
|
||||
case AudioAction::MUTE:
|
||||
obs_source_set_muted(s, true);
|
||||
break;
|
||||
case AudioAction::UNMUTE:
|
||||
obs_source_set_muted(s, false);
|
||||
break;
|
||||
case AudioAction::SOURCE_VOLUME:
|
||||
obs_source_set_volume(s, (float)_volume / 100.0f);
|
||||
break;
|
||||
case AudioAction::MASTER_VOLUME:
|
||||
obs_set_master_volume((float)_volume / 100.0f);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
obs_source_release(s);
|
||||
return true;
|
||||
}
|
||||
|
||||
void MacroActionAudio::LogAction()
|
||||
{
|
||||
auto it = actionTypes.find(_action);
|
||||
if (it != actionTypes.end()) {
|
||||
vblog(LOG_INFO,
|
||||
"performed action \"%s\" for source \"%s\" with volume %d",
|
||||
it->second.c_str(),
|
||||
GetWeakSourceName(_audioSource).c_str(), _volume);
|
||||
} else {
|
||||
blog(LOG_WARNING, "ignored unknown audio action %d",
|
||||
static_cast<int>(_action));
|
||||
}
|
||||
}
|
||||
|
||||
bool MacroActionAudio::Save(obs_data_t *obj)
|
||||
{
|
||||
MacroAction::Save(obj);
|
||||
obs_data_set_string(obj, "audioSource",
|
||||
GetWeakSourceName(_audioSource).c_str());
|
||||
obs_data_set_int(obj, "action", static_cast<int>(_action));
|
||||
obs_data_set_int(obj, "volume", _volume);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MacroActionAudio::Load(obs_data_t *obj)
|
||||
{
|
||||
MacroAction::Load(obj);
|
||||
const char *audioSourceName = obs_data_get_string(obj, "audioSource");
|
||||
_audioSource = GetWeakSourceByName(audioSourceName);
|
||||
_action = static_cast<AudioAction>(obs_data_get_int(obj, "action"));
|
||||
_volume = obs_data_get_int(obj, "volume");
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline void populateActionSelection(QComboBox *list)
|
||||
{
|
||||
for (auto entry : actionTypes) {
|
||||
list->addItem(obs_module_text(entry.second.c_str()));
|
||||
}
|
||||
}
|
||||
|
||||
MacroActionAudioEdit::MacroActionAudioEdit(
|
||||
QWidget *parent, std::shared_ptr<MacroActionAudio> entryData)
|
||||
: QWidget(parent)
|
||||
{
|
||||
_audioSources = new QComboBox();
|
||||
_actions = new QComboBox();
|
||||
_volumePercent = new QSpinBox();
|
||||
_volumePercent->setMinimum(0);
|
||||
_volumePercent->setMaximum(2000);
|
||||
_volumePercent->setSuffix("%");
|
||||
|
||||
populateActionSelection(_actions);
|
||||
populateAudioSelection(_audioSources);
|
||||
|
||||
QWidget::connect(_actions, SIGNAL(currentIndexChanged(int)), this,
|
||||
SLOT(ActionChanged(int)));
|
||||
QWidget::connect(_audioSources,
|
||||
SIGNAL(currentTextChanged(const QString &)), this,
|
||||
SLOT(SourceChanged(const QString &)));
|
||||
QWidget::connect(_volumePercent, SIGNAL(valueChanged(int)), this,
|
||||
SLOT(VolumeChanged(int)));
|
||||
|
||||
QHBoxLayout *mainLayout = new QHBoxLayout;
|
||||
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
|
||||
{"{{audioSources}}", _audioSources},
|
||||
{"{{actions}}", _actions},
|
||||
{"{{volume}}", _volumePercent},
|
||||
};
|
||||
placeWidgets(obs_module_text("AdvSceneSwitcher.action.audio.entry"),
|
||||
mainLayout, widgetPlaceholders);
|
||||
setLayout(mainLayout);
|
||||
|
||||
_entryData = entryData;
|
||||
UpdateEntryData();
|
||||
_loading = false;
|
||||
}
|
||||
|
||||
bool hasVolumeControl(AudioAction action)
|
||||
{
|
||||
return action == AudioAction::SOURCE_VOLUME ||
|
||||
action == AudioAction::MASTER_VOLUME;
|
||||
}
|
||||
|
||||
bool hasSourceControl(AudioAction action)
|
||||
{
|
||||
return action != AudioAction::MASTER_VOLUME;
|
||||
}
|
||||
|
||||
void MacroActionAudioEdit::SetWidgetVisibility()
|
||||
{
|
||||
if (hasVolumeControl(_entryData->_action)) {
|
||||
_volumePercent->show();
|
||||
} else {
|
||||
_volumePercent->hide();
|
||||
}
|
||||
|
||||
if (hasSourceControl(_entryData->_action)) {
|
||||
_audioSources->show();
|
||||
} else {
|
||||
_audioSources->hide();
|
||||
}
|
||||
}
|
||||
|
||||
void MacroActionAudioEdit::UpdateEntryData()
|
||||
{
|
||||
if (!_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
_audioSources->setCurrentText(
|
||||
GetWeakSourceName(_entryData->_audioSource).c_str());
|
||||
_actions->setCurrentIndex(static_cast<int>(_entryData->_action));
|
||||
_volumePercent->setValue(_entryData->_volume);
|
||||
|
||||
SetWidgetVisibility();
|
||||
}
|
||||
|
||||
void MacroActionAudioEdit::SourceChanged(const QString &text)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_audioSource = GetWeakSourceByQString(text);
|
||||
}
|
||||
|
||||
void MacroActionAudioEdit::ActionChanged(int value)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_action = static_cast<AudioAction>(value);
|
||||
SetWidgetVisibility();
|
||||
}
|
||||
|
||||
void MacroActionAudioEdit::VolumeChanged(int value)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_volume = value;
|
||||
}
|
||||
155
src/macro-action-edit.cpp
Normal file
155
src/macro-action-edit.cpp
Normal file
@@ -0,0 +1,155 @@
|
||||
#include "headers/macro-action-edit.hpp"
|
||||
#include "headers/macro-action-scene-switch.hpp"
|
||||
#include "headers/utility.hpp"
|
||||
#include "headers/advanced-scene-switcher.hpp"
|
||||
|
||||
std::map<std::string, MacroActionInfo> MacroActionFactory::_methods;
|
||||
|
||||
bool MacroActionFactory::Register(const std::string &id, MacroActionInfo info)
|
||||
{
|
||||
if (auto it = _methods.find(id); it == _methods.end()) {
|
||||
_methods[id] = info;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
std::shared_ptr<MacroAction> MacroActionFactory::Create(const std::string &id)
|
||||
{
|
||||
if (auto it = _methods.find(id); it != _methods.end())
|
||||
return it->second._createFunc();
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
QWidget *MacroActionFactory::CreateWidget(const std::string &id,
|
||||
QWidget *parent,
|
||||
std::shared_ptr<MacroAction> action)
|
||||
{
|
||||
if (auto it = _methods.find(id); it != _methods.end())
|
||||
return it->second._createWidgetFunc(parent, action);
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::string MacroActionFactory::GetActionName(const std::string &id)
|
||||
{
|
||||
if (auto it = _methods.find(id); it != _methods.end()) {
|
||||
return it->second._name;
|
||||
}
|
||||
return "unknown action";
|
||||
}
|
||||
|
||||
std::string MacroActionFactory::GetIdByName(const QString &name)
|
||||
{
|
||||
for (auto it : _methods) {
|
||||
if (name == obs_module_text(it.second._name.c_str())) {
|
||||
return it.first;
|
||||
}
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
static inline void populateActionSelection(QComboBox *list)
|
||||
{
|
||||
for (auto entry : MacroActionFactory::GetActionTypes()) {
|
||||
list->addItem(obs_module_text(entry.second._name.c_str()));
|
||||
}
|
||||
}
|
||||
|
||||
MacroActionEdit::MacroActionEdit(QWidget *parent,
|
||||
std::shared_ptr<MacroAction> *entryData,
|
||||
const std::string &id, bool startCollapsed)
|
||||
: QWidget(parent)
|
||||
{
|
||||
_actionSelection = new QComboBox();
|
||||
_section = new Section(300);
|
||||
|
||||
QWidget::connect(_actionSelection,
|
||||
SIGNAL(currentTextChanged(const QString &)), this,
|
||||
SLOT(ActionSelectionChanged(const QString &)));
|
||||
|
||||
populateActionSelection(_actionSelection);
|
||||
|
||||
_section->AddHeaderWidget(_actionSelection);
|
||||
|
||||
QVBoxLayout *mainLayout = new QVBoxLayout;
|
||||
mainLayout->addWidget(_section);
|
||||
setLayout(mainLayout);
|
||||
|
||||
_entryData = entryData;
|
||||
UpdateEntryData(id, startCollapsed);
|
||||
|
||||
_loading = false;
|
||||
}
|
||||
|
||||
void MacroActionEdit::ActionSelectionChanged(const QString &text)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::string id = MacroActionFactory::GetIdByName(text);
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->reset();
|
||||
*_entryData = MacroActionFactory::Create(id);
|
||||
auto widget =
|
||||
MacroActionFactory::CreateWidget(id, window(), *_entryData);
|
||||
_section->SetContent(widget, false);
|
||||
}
|
||||
|
||||
void MacroActionEdit::UpdateEntryData(const std::string &id, bool collapse)
|
||||
{
|
||||
_actionSelection->setCurrentText(
|
||||
obs_module_text(MacroActionFactory::GetActionName(id).c_str()));
|
||||
auto widget =
|
||||
MacroActionFactory::CreateWidget(id, window(), *_entryData);
|
||||
_section->SetContent(widget, collapse);
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::on_actionAdd_clicked()
|
||||
{
|
||||
auto macro = getSelectedMacro();
|
||||
if (!macro) {
|
||||
return;
|
||||
}
|
||||
|
||||
MacroActionSwitchScene temp;
|
||||
std::string id = temp.GetId();
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
macro->Actions().emplace_back(MacroActionFactory::Create(id));
|
||||
auto newEntry =
|
||||
new MacroActionEdit(this, ¯o->Actions().back(), id, false);
|
||||
ui->macroEditActionLayout->addWidget(newEntry);
|
||||
ui->macroEditActionHelp->setVisible(false);
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::on_actionRemove_clicked()
|
||||
{
|
||||
auto macro = getSelectedMacro();
|
||||
if (!macro) {
|
||||
return;
|
||||
}
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
if (macro->Actions().empty()) {
|
||||
return;
|
||||
}
|
||||
macro->Actions().pop_back();
|
||||
|
||||
int count = ui->macroEditActionLayout->count();
|
||||
auto item = ui->macroEditActionLayout->takeAt(count - 1);
|
||||
|
||||
if (item) {
|
||||
auto widget = item->widget();
|
||||
if (widget) {
|
||||
widget->setVisible(false);
|
||||
}
|
||||
delete item;
|
||||
}
|
||||
|
||||
if (count == 1) {
|
||||
ui->macroEditActionHelp->setVisible(true);
|
||||
}
|
||||
}
|
||||
203
src/macro-action-filter.cpp
Normal file
203
src/macro-action-filter.cpp
Normal file
@@ -0,0 +1,203 @@
|
||||
#include "headers/macro-action-filter.hpp"
|
||||
#include "headers/advanced-scene-switcher.hpp"
|
||||
#include "headers/utility.hpp"
|
||||
|
||||
const std::string MacroActionFilter::id = "filter";
|
||||
|
||||
bool MacroActionFilter::_registered = MacroActionFactory::Register(
|
||||
MacroActionFilter::id,
|
||||
{MacroActionFilter::Create, MacroActionFilterEdit::Create,
|
||||
"AdvSceneSwitcher.action.filter"});
|
||||
|
||||
const static std::map<FilterAction, std::string> actionTypes = {
|
||||
{FilterAction::ENABLE, "AdvSceneSwitcher.action.filter.type.enable"},
|
||||
{FilterAction::DISABLE, "AdvSceneSwitcher.action.filter.type.disable"},
|
||||
};
|
||||
|
||||
bool MacroActionFilter::PerformAction()
|
||||
{
|
||||
auto s = obs_weak_source_get_source(_filter);
|
||||
switch (_action) {
|
||||
case FilterAction::ENABLE:
|
||||
obs_source_set_enabled(s, true);
|
||||
break;
|
||||
case FilterAction::DISABLE:
|
||||
obs_source_set_enabled(s, false);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
obs_source_release(s);
|
||||
return true;
|
||||
}
|
||||
|
||||
void MacroActionFilter::LogAction()
|
||||
{
|
||||
auto it = actionTypes.find(_action);
|
||||
if (it != actionTypes.end()) {
|
||||
vblog(LOG_INFO,
|
||||
"performed action \"%s\" for filter \"%s\" on source \"%s\"",
|
||||
it->second.c_str(), GetWeakSourceName(_filter).c_str(),
|
||||
GetWeakSourceName(_source).c_str());
|
||||
} else {
|
||||
blog(LOG_WARNING, "ignored unknown filter action %d",
|
||||
static_cast<int>(_action));
|
||||
}
|
||||
}
|
||||
|
||||
bool MacroActionFilter::Save(obs_data_t *obj)
|
||||
{
|
||||
MacroAction::Save(obj);
|
||||
obs_data_set_string(obj, "source", GetWeakSourceName(_source).c_str());
|
||||
obs_data_set_string(obj, "filter", GetWeakSourceName(_filter).c_str());
|
||||
obs_data_set_int(obj, "action", static_cast<int>(_action));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MacroActionFilter::Load(obs_data_t *obj)
|
||||
{
|
||||
MacroAction::Load(obj);
|
||||
const char *sourceName = obs_data_get_string(obj, "source");
|
||||
_source = GetWeakSourceByName(sourceName);
|
||||
const char *filterName = obs_data_get_string(obj, "filter");
|
||||
_filter = GetWeakFilterByQString(_source, filterName);
|
||||
_action = static_cast<FilterAction>(obs_data_get_int(obj, "action"));
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline void populateActionSelection(QComboBox *list)
|
||||
{
|
||||
for (auto entry : actionTypes) {
|
||||
list->addItem(obs_module_text(entry.second.c_str()));
|
||||
}
|
||||
}
|
||||
|
||||
static inline void populateFilters(QComboBox *list,
|
||||
OBSWeakSource weakSource = nullptr)
|
||||
{
|
||||
auto enumFilters = [](obs_source_t *, obs_source_t *filter, void *ptr) {
|
||||
QComboBox *list = reinterpret_cast<QComboBox *>(ptr);
|
||||
auto name = obs_source_get_name(filter);
|
||||
list->addItem(name);
|
||||
};
|
||||
|
||||
auto s = obs_weak_source_get_source(weakSource);
|
||||
obs_source_enum_filters(s, enumFilters, list);
|
||||
list->model()->sort(0);
|
||||
addSelectionEntry(list,
|
||||
obs_module_text("AdvSceneSwitcher.selectFilter"));
|
||||
obs_source_release(s);
|
||||
list->setCurrentIndex(0);
|
||||
}
|
||||
|
||||
static inline void hasFilterEnum(obs_source_t *, obs_source_t *filter,
|
||||
void *ptr)
|
||||
{
|
||||
if (!filter) {
|
||||
return;
|
||||
}
|
||||
bool *hasFilter = reinterpret_cast<bool *>(ptr);
|
||||
*hasFilter = true;
|
||||
}
|
||||
|
||||
static inline void populateSourcesWithFilter(QComboBox *list)
|
||||
{
|
||||
auto enumSourcesWithFilters = [](void *param, obs_source_t *source) {
|
||||
if (!source) {
|
||||
return true;
|
||||
}
|
||||
QComboBox *list = reinterpret_cast<QComboBox *>(param);
|
||||
bool hasFilter = false;
|
||||
obs_source_enum_filters(source, hasFilterEnum, &hasFilter);
|
||||
if (hasFilter) {
|
||||
list->addItem(obs_source_get_name(source));
|
||||
}
|
||||
return true;
|
||||
};
|
||||
obs_enum_sources(enumSourcesWithFilters, list);
|
||||
obs_enum_scenes(enumSourcesWithFilters, list);
|
||||
list->model()->sort(0);
|
||||
addSelectionEntry(list,
|
||||
obs_module_text("AdvSceneSwitcher.selectSource"));
|
||||
list->setCurrentIndex(0);
|
||||
}
|
||||
|
||||
MacroActionFilterEdit::MacroActionFilterEdit(
|
||||
QWidget *parent, std::shared_ptr<MacroActionFilter> entryData)
|
||||
: QWidget(parent)
|
||||
{
|
||||
_sources = new QComboBox();
|
||||
_filters = new QComboBox();
|
||||
_actions = new QComboBox();
|
||||
|
||||
populateActionSelection(_actions);
|
||||
populateSourcesWithFilter(_sources);
|
||||
|
||||
QWidget::connect(_actions, SIGNAL(currentIndexChanged(int)), this,
|
||||
SLOT(ActionChanged(int)));
|
||||
QWidget::connect(_sources, SIGNAL(currentTextChanged(const QString &)),
|
||||
this, SLOT(SourceChanged(const QString &)));
|
||||
QWidget::connect(_filters, SIGNAL(currentTextChanged(const QString &)),
|
||||
this, SLOT(FilterChanged(const QString &)));
|
||||
|
||||
QHBoxLayout *mainLayout = new QHBoxLayout;
|
||||
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
|
||||
{"{{sources}}", _sources},
|
||||
{"{{filters}}", _filters},
|
||||
{"{{actions}}", _actions},
|
||||
};
|
||||
placeWidgets(obs_module_text("AdvSceneSwitcher.action.filter.entry"),
|
||||
mainLayout, widgetPlaceholders);
|
||||
setLayout(mainLayout);
|
||||
|
||||
_entryData = entryData;
|
||||
UpdateEntryData();
|
||||
_loading = false;
|
||||
}
|
||||
|
||||
void MacroActionFilterEdit::UpdateEntryData()
|
||||
{
|
||||
if (!_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
_actions->setCurrentIndex(static_cast<int>(_entryData->_action));
|
||||
_sources->setCurrentText(
|
||||
GetWeakSourceName(_entryData->_source).c_str());
|
||||
populateFilters(_filters, _entryData->_source);
|
||||
_filters->setCurrentText(
|
||||
GetWeakSourceName(_entryData->_filter).c_str());
|
||||
}
|
||||
|
||||
void MacroActionFilterEdit::SourceChanged(const QString &text)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_source = GetWeakSourceByQString(text);
|
||||
}
|
||||
_filters->clear();
|
||||
populateFilters(_filters, _entryData->_source);
|
||||
}
|
||||
|
||||
void MacroActionFilterEdit::FilterChanged(const QString &text)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_filter = GetWeakFilterByQString(_entryData->_source, text);
|
||||
}
|
||||
|
||||
void MacroActionFilterEdit::ActionChanged(int value)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_action = static_cast<FilterAction>(value);
|
||||
}
|
||||
152
src/macro-action-macro.cpp
Normal file
152
src/macro-action-macro.cpp
Normal file
@@ -0,0 +1,152 @@
|
||||
#include "headers/macro-action-macro.hpp"
|
||||
#include "headers/advanced-scene-switcher.hpp"
|
||||
#include "headers/utility.hpp"
|
||||
|
||||
const std::string MacroActionMacro::id = "macro";
|
||||
|
||||
bool MacroActionMacro::_registered = MacroActionFactory::Register(
|
||||
MacroActionMacro::id,
|
||||
{MacroActionMacro::Create, MacroActionMacroEdit::Create,
|
||||
"AdvSceneSwitcher.action.macro"});
|
||||
|
||||
const static std::map<PerformMacroAction, std::string> actionTypes = {
|
||||
{PerformMacroAction::PAUSE, "AdvSceneSwitcher.action.macro.type.pause"},
|
||||
{PerformMacroAction::UNPAUSE,
|
||||
"AdvSceneSwitcher.action.macro.type.unpause"},
|
||||
{PerformMacroAction::RESET_COUNTER,
|
||||
"AdvSceneSwitcher.action.macro.type.resetCounter"},
|
||||
};
|
||||
|
||||
bool MacroActionMacro::PerformAction()
|
||||
{
|
||||
if (!_macro.get()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
switch (_action) {
|
||||
case PerformMacroAction::PAUSE:
|
||||
_macro->SetPaused();
|
||||
break;
|
||||
case PerformMacroAction::UNPAUSE:
|
||||
_macro->SetPaused(false);
|
||||
break;
|
||||
case PerformMacroAction::RESET_COUNTER:
|
||||
_macro->ResetCount();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void MacroActionMacro::LogAction()
|
||||
{
|
||||
if (!_macro.get()) {
|
||||
return;
|
||||
}
|
||||
switch (_action) {
|
||||
case PerformMacroAction::PAUSE:
|
||||
vblog(LOG_INFO, "paused \"%s\"", _macro->Name().c_str());
|
||||
break;
|
||||
case PerformMacroAction::UNPAUSE:
|
||||
vblog(LOG_INFO, "unpaused \"%s\"", _macro->Name().c_str());
|
||||
break;
|
||||
case PerformMacroAction::RESET_COUNTER:
|
||||
vblog(LOG_INFO, "reset counter for \"%s\"",
|
||||
_macro->Name().c_str());
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool MacroActionMacro::Save(obs_data_t *obj)
|
||||
{
|
||||
MacroAction::Save(obj);
|
||||
_macro.Save(obj);
|
||||
obs_data_set_int(obj, "action", static_cast<int>(_action));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MacroActionMacro::Load(obs_data_t *obj)
|
||||
{
|
||||
MacroAction::Load(obj);
|
||||
_macro.Load(obj);
|
||||
_action = static_cast<PerformMacroAction>(
|
||||
obs_data_get_int(obj, "action"));
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline void populateActionSelection(QComboBox *list)
|
||||
{
|
||||
for (auto entry : actionTypes) {
|
||||
list->addItem(obs_module_text(entry.second.c_str()));
|
||||
}
|
||||
}
|
||||
|
||||
MacroActionMacroEdit::MacroActionMacroEdit(
|
||||
QWidget *parent, std::shared_ptr<MacroActionMacro> entryData)
|
||||
: QWidget(parent)
|
||||
{
|
||||
_macros = new MacroSelection(parent);
|
||||
_actions = new QComboBox();
|
||||
|
||||
populateActionSelection(_actions);
|
||||
|
||||
QWidget::connect(_macros, SIGNAL(currentTextChanged(const QString &)),
|
||||
this, SLOT(MacroChanged(const QString &)));
|
||||
QWidget::connect(parent, SIGNAL(MacroRemoved(const QString &)), this,
|
||||
SLOT(MacroRemove(const QString &)));
|
||||
QWidget::connect(_actions, SIGNAL(currentIndexChanged(int)), this,
|
||||
SLOT(ActionChanged(int)));
|
||||
|
||||
QHBoxLayout *mainLayout = new QHBoxLayout;
|
||||
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
|
||||
{"{{actions}}", _actions},
|
||||
{"{{macros}}", _macros},
|
||||
};
|
||||
placeWidgets(obs_module_text("AdvSceneSwitcher.action.macro.entry"),
|
||||
mainLayout, widgetPlaceholders);
|
||||
setLayout(mainLayout);
|
||||
|
||||
_entryData = entryData;
|
||||
UpdateEntryData();
|
||||
_loading = false;
|
||||
}
|
||||
|
||||
void MacroActionMacroEdit::UpdateEntryData()
|
||||
{
|
||||
if (!_entryData) {
|
||||
return;
|
||||
}
|
||||
_actions->setCurrentIndex(static_cast<int>(_entryData->_action));
|
||||
_macros->SetCurrentMacro(_entryData->_macro.get());
|
||||
}
|
||||
|
||||
void MacroActionMacroEdit::MacroChanged(const QString &text)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_macro.UpdateRef(text);
|
||||
}
|
||||
|
||||
void MacroActionMacroEdit::ActionChanged(int value)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_action = static_cast<PerformMacroAction>(value);
|
||||
}
|
||||
|
||||
void MacroActionMacroEdit::MacroRemove(const QString &name)
|
||||
{
|
||||
UNUSED_PARAMETER(name);
|
||||
if (_entryData) {
|
||||
_entryData->_macro.UpdateRef();
|
||||
}
|
||||
}
|
||||
153
src/macro-action-media.cpp
Normal file
153
src/macro-action-media.cpp
Normal file
@@ -0,0 +1,153 @@
|
||||
#include "headers/macro-action-media.hpp"
|
||||
#include "headers/advanced-scene-switcher.hpp"
|
||||
#include "headers/utility.hpp"
|
||||
|
||||
const std::string MacroActionMedia::id = "media";
|
||||
|
||||
bool MacroActionMedia::_registered = MacroActionFactory::Register(
|
||||
MacroActionMedia::id,
|
||||
{MacroActionMedia::Create, MacroActionMediaEdit::Create,
|
||||
"AdvSceneSwitcher.action.media"});
|
||||
|
||||
const static std::map<MediaAction, std::string> actionTypes = {
|
||||
{MediaAction::PLAY, "AdvSceneSwitcher.action.media.type.play"},
|
||||
{MediaAction::PAUSE, "AdvSceneSwitcher.action.media.type.pause"},
|
||||
{MediaAction::STOP, "AdvSceneSwitcher.action.media.type.stop"},
|
||||
{MediaAction::RESTART, "AdvSceneSwitcher.action.media.type.restart"},
|
||||
{MediaAction::NEXT, "AdvSceneSwitcher.action.media.type.next"},
|
||||
{MediaAction::PREVIOUS, "AdvSceneSwitcher.action.media.type.previous"},
|
||||
};
|
||||
|
||||
bool MacroActionMedia::PerformAction()
|
||||
{
|
||||
auto source = obs_weak_source_get_source(_mediaSource);
|
||||
obs_media_state state = obs_source_media_get_state(source);
|
||||
switch (_action) {
|
||||
case MediaAction::PLAY:
|
||||
if (state == OBS_MEDIA_STATE_STOPPED ||
|
||||
state == OBS_MEDIA_STATE_ENDED) {
|
||||
obs_source_media_restart(source);
|
||||
} else {
|
||||
obs_source_media_play_pause(source, false);
|
||||
}
|
||||
break;
|
||||
case MediaAction::PAUSE:
|
||||
obs_source_media_play_pause(source, true);
|
||||
break;
|
||||
case MediaAction::STOP:
|
||||
obs_source_media_stop(source);
|
||||
break;
|
||||
case MediaAction::RESTART:
|
||||
obs_source_media_restart(source);
|
||||
break;
|
||||
case MediaAction::NEXT:
|
||||
obs_source_media_next(source);
|
||||
break;
|
||||
case MediaAction::PREVIOUS:
|
||||
obs_source_media_previous(source);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
obs_source_release(source);
|
||||
return true;
|
||||
}
|
||||
|
||||
void MacroActionMedia::LogAction()
|
||||
{
|
||||
auto it = actionTypes.find(_action);
|
||||
if (it != actionTypes.end()) {
|
||||
vblog(LOG_INFO, "performed action \"%s\" for source \"%s\"",
|
||||
it->second.c_str(),
|
||||
GetWeakSourceName(_mediaSource).c_str());
|
||||
} else {
|
||||
blog(LOG_WARNING, "ignored unknown media action %d",
|
||||
static_cast<int>(_action));
|
||||
}
|
||||
}
|
||||
|
||||
bool MacroActionMedia::Save(obs_data_t *obj)
|
||||
{
|
||||
MacroAction::Save(obj);
|
||||
obs_data_set_string(obj, "mediaSource",
|
||||
GetWeakSourceName(_mediaSource).c_str());
|
||||
obs_data_set_int(obj, "action", static_cast<int>(_action));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MacroActionMedia::Load(obs_data_t *obj)
|
||||
{
|
||||
MacroAction::Load(obj);
|
||||
const char *MediaSourceName = obs_data_get_string(obj, "mediaSource");
|
||||
_mediaSource = GetWeakSourceByName(MediaSourceName);
|
||||
_action = static_cast<MediaAction>(obs_data_get_int(obj, "action"));
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline void populateActionSelection(QComboBox *list)
|
||||
{
|
||||
for (auto entry : actionTypes) {
|
||||
list->addItem(obs_module_text(entry.second.c_str()));
|
||||
}
|
||||
}
|
||||
|
||||
MacroActionMediaEdit::MacroActionMediaEdit(
|
||||
QWidget *parent, std::shared_ptr<MacroActionMedia> entryData)
|
||||
: QWidget(parent)
|
||||
{
|
||||
_mediaSources = new QComboBox();
|
||||
_actions = new QComboBox();
|
||||
|
||||
populateActionSelection(_actions);
|
||||
populateMediaSelection(_mediaSources);
|
||||
|
||||
QWidget::connect(_actions, SIGNAL(currentIndexChanged(int)), this,
|
||||
SLOT(ActionChanged(int)));
|
||||
QWidget::connect(_mediaSources,
|
||||
SIGNAL(currentTextChanged(const QString &)), this,
|
||||
SLOT(SourceChanged(const QString &)));
|
||||
|
||||
QHBoxLayout *mainLayout = new QHBoxLayout;
|
||||
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
|
||||
{"{{mediaSources}}", _mediaSources},
|
||||
{"{{actions}}", _actions},
|
||||
};
|
||||
placeWidgets(obs_module_text("AdvSceneSwitcher.action.media.entry"),
|
||||
mainLayout, widgetPlaceholders);
|
||||
setLayout(mainLayout);
|
||||
|
||||
_entryData = entryData;
|
||||
UpdateEntryData();
|
||||
_loading = false;
|
||||
}
|
||||
|
||||
void MacroActionMediaEdit::UpdateEntryData()
|
||||
{
|
||||
if (!_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
_mediaSources->setCurrentText(
|
||||
GetWeakSourceName(_entryData->_mediaSource).c_str());
|
||||
_actions->setCurrentIndex(static_cast<int>(_entryData->_action));
|
||||
}
|
||||
|
||||
void MacroActionMediaEdit::SourceChanged(const QString &text)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_mediaSource = GetWeakSourceByQString(text);
|
||||
}
|
||||
|
||||
void MacroActionMediaEdit::ActionChanged(int value)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_action = static_cast<MediaAction>(value);
|
||||
}
|
||||
114
src/macro-action-plugin-state.cpp
Normal file
114
src/macro-action-plugin-state.cpp
Normal file
@@ -0,0 +1,114 @@
|
||||
#include "headers/macro-action-plugin-state.hpp"
|
||||
#include "headers/advanced-scene-switcher.hpp"
|
||||
#include "headers/utility.hpp"
|
||||
|
||||
#include <thread>
|
||||
|
||||
const std::string MacroActionPluginState::id = "plugin_state";
|
||||
|
||||
bool MacroActionPluginState::_registered = MacroActionFactory::Register(
|
||||
MacroActionPluginState::id,
|
||||
{MacroActionPluginState::Create, MacroActionPluginStateEdit::Create,
|
||||
"AdvSceneSwitcher.action.PluginState"});
|
||||
|
||||
const static std::map<PluginStateAction, std::string> actionTypes = {
|
||||
{PluginStateAction::STOP,
|
||||
"AdvSceneSwitcher.action.pluginState.type.stop"},
|
||||
};
|
||||
|
||||
void stopPlugin()
|
||||
{
|
||||
std::thread t([]() { switcher->Stop(); });
|
||||
t.detach();
|
||||
}
|
||||
|
||||
bool MacroActionPluginState::PerformAction()
|
||||
{
|
||||
switch (_action) {
|
||||
case PluginStateAction::STOP:
|
||||
stopPlugin();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void MacroActionPluginState::LogAction()
|
||||
{
|
||||
auto it = actionTypes.find(_action);
|
||||
switch (_action) {
|
||||
case PluginStateAction::STOP:
|
||||
blog(LOG_INFO, "stop() called by macro", it->second.c_str());
|
||||
break;
|
||||
default:
|
||||
blog(LOG_WARNING, "ignored unknown pluginState action %d",
|
||||
static_cast<int>(_action));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool MacroActionPluginState::Save(obs_data_t *obj)
|
||||
{
|
||||
MacroAction::Save(obj);
|
||||
obs_data_set_int(obj, "action", static_cast<int>(_action));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MacroActionPluginState::Load(obs_data_t *obj)
|
||||
{
|
||||
MacroAction::Load(obj);
|
||||
_action =
|
||||
static_cast<PluginStateAction>(obs_data_get_int(obj, "action"));
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline void populateActionSelection(QComboBox *list)
|
||||
{
|
||||
for (auto entry : actionTypes) {
|
||||
list->addItem(obs_module_text(entry.second.c_str()));
|
||||
}
|
||||
}
|
||||
|
||||
MacroActionPluginStateEdit::MacroActionPluginStateEdit(
|
||||
QWidget *parent, std::shared_ptr<MacroActionPluginState> entryData)
|
||||
: QWidget(parent)
|
||||
{
|
||||
_actions = new QComboBox();
|
||||
|
||||
populateActionSelection(_actions);
|
||||
|
||||
QWidget::connect(_actions, SIGNAL(currentIndexChanged(int)), this,
|
||||
SLOT(ActionChanged(int)));
|
||||
|
||||
QHBoxLayout *mainLayout = new QHBoxLayout;
|
||||
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
|
||||
{"{{actions}}", _actions},
|
||||
};
|
||||
placeWidgets(
|
||||
obs_module_text("AdvSceneSwitcher.action.pluginState.entry"),
|
||||
mainLayout, widgetPlaceholders);
|
||||
setLayout(mainLayout);
|
||||
|
||||
_entryData = entryData;
|
||||
UpdateEntryData();
|
||||
_loading = false;
|
||||
}
|
||||
|
||||
void MacroActionPluginStateEdit::UpdateEntryData()
|
||||
{
|
||||
if (!_entryData) {
|
||||
return;
|
||||
}
|
||||
_actions->setCurrentIndex(static_cast<int>(_entryData->_action));
|
||||
}
|
||||
|
||||
void MacroActionPluginStateEdit::ActionChanged(int value)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_action = static_cast<PluginStateAction>(value);
|
||||
}
|
||||
133
src/macro-action-recording.cpp
Normal file
133
src/macro-action-recording.cpp
Normal file
@@ -0,0 +1,133 @@
|
||||
#include "headers/macro-action-recording.hpp"
|
||||
#include "headers/advanced-scene-switcher.hpp"
|
||||
#include "headers/utility.hpp"
|
||||
|
||||
const std::string MacroActionRecord::id = "recording";
|
||||
|
||||
bool MacroActionRecord::_registered = MacroActionFactory::Register(
|
||||
MacroActionRecord::id,
|
||||
{MacroActionRecord::Create, MacroActionRecordEdit::Create,
|
||||
"AdvSceneSwitcher.action.recording"});
|
||||
|
||||
const static std::map<RecordAction, std::string> actionTypes = {
|
||||
{RecordAction::STOP, "AdvSceneSwitcher.action.recording.type.stop"},
|
||||
{RecordAction::START, "AdvSceneSwitcher.action.recording.type.start"},
|
||||
{RecordAction::PAUSE, "AdvSceneSwitcher.action.recording.type.pause"},
|
||||
{RecordAction::UNPAUSE,
|
||||
"AdvSceneSwitcher.action.recording.type.unpause"},
|
||||
};
|
||||
|
||||
bool MacroActionRecord::PerformAction()
|
||||
{
|
||||
switch (_action) {
|
||||
case RecordAction::STOP:
|
||||
if (obs_frontend_recording_active()) {
|
||||
obs_frontend_recording_stop();
|
||||
}
|
||||
break;
|
||||
case RecordAction::START:
|
||||
if (!obs_frontend_recording_active()) {
|
||||
obs_frontend_recording_start();
|
||||
}
|
||||
break;
|
||||
case RecordAction::PAUSE:
|
||||
if (obs_frontend_recording_active() &&
|
||||
!obs_frontend_recording_paused()) {
|
||||
obs_frontend_recording_pause(true);
|
||||
}
|
||||
break;
|
||||
case RecordAction::UNPAUSE:
|
||||
if (obs_frontend_recording_active() &&
|
||||
obs_frontend_recording_paused()) {
|
||||
obs_frontend_recording_pause(false);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void MacroActionRecord::LogAction()
|
||||
{
|
||||
auto it = actionTypes.find(_action);
|
||||
if (it != actionTypes.end()) {
|
||||
vblog(LOG_INFO, "performed action \"%s\"", it->second.c_str());
|
||||
} else {
|
||||
blog(LOG_WARNING, "ignored unknown recording action %d",
|
||||
static_cast<int>(_action));
|
||||
}
|
||||
}
|
||||
|
||||
bool MacroActionRecord::Save(obs_data_t *obj)
|
||||
{
|
||||
MacroAction::Save(obj);
|
||||
obs_data_set_int(obj, "action", static_cast<int>(_action));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MacroActionRecord::Load(obs_data_t *obj)
|
||||
{
|
||||
MacroAction::Load(obj);
|
||||
_action = static_cast<RecordAction>(obs_data_get_int(obj, "action"));
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline void populateActionSelection(QComboBox *list)
|
||||
{
|
||||
for (auto entry : actionTypes) {
|
||||
list->addItem(obs_module_text(entry.second.c_str()));
|
||||
}
|
||||
}
|
||||
|
||||
MacroActionRecordEdit::MacroActionRecordEdit(
|
||||
QWidget *parent, std::shared_ptr<MacroActionRecord> entryData)
|
||||
: QWidget(parent)
|
||||
{
|
||||
_actions = new QComboBox();
|
||||
_pauseHint = new QLabel(obs_module_text(
|
||||
"AdvSceneSwitcher.action.recording.pause.hint"));
|
||||
|
||||
populateActionSelection(_actions);
|
||||
|
||||
QWidget::connect(_actions, SIGNAL(currentIndexChanged(int)), this,
|
||||
SLOT(ActionChanged(int)));
|
||||
|
||||
QHBoxLayout *mainLayout = new QHBoxLayout;
|
||||
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
|
||||
{"{{actions}}", _actions},
|
||||
{"{{pauseHint}}", _pauseHint},
|
||||
};
|
||||
placeWidgets(obs_module_text("AdvSceneSwitcher.action.recording.entry"),
|
||||
mainLayout, widgetPlaceholders);
|
||||
setLayout(mainLayout);
|
||||
|
||||
_entryData = entryData;
|
||||
UpdateEntryData();
|
||||
_loading = false;
|
||||
}
|
||||
|
||||
bool isPauseAction(RecordAction a)
|
||||
{
|
||||
return a == RecordAction::PAUSE || a == RecordAction::UNPAUSE;
|
||||
}
|
||||
|
||||
void MacroActionRecordEdit::UpdateEntryData()
|
||||
{
|
||||
if (!_entryData) {
|
||||
return;
|
||||
}
|
||||
_actions->setCurrentIndex(static_cast<int>(_entryData->_action));
|
||||
_pauseHint->setVisible(isPauseAction(_entryData->_action));
|
||||
}
|
||||
|
||||
void MacroActionRecordEdit::ActionChanged(int value)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_action = static_cast<RecordAction>(value);
|
||||
_pauseHint->setVisible(isPauseAction(_entryData->_action));
|
||||
}
|
||||
121
src/macro-action-replay-buffer.cpp
Normal file
121
src/macro-action-replay-buffer.cpp
Normal file
@@ -0,0 +1,121 @@
|
||||
#include "headers/macro-action-replay-buffer.hpp"
|
||||
#include "headers/advanced-scene-switcher.hpp"
|
||||
#include "headers/utility.hpp"
|
||||
|
||||
const std::string MacroActionReplayBuffer::id = "replay_buffer";
|
||||
|
||||
bool MacroActionReplayBuffer::_registered = MacroActionFactory::Register(
|
||||
MacroActionReplayBuffer::id,
|
||||
{MacroActionReplayBuffer::Create, MacroActionReplayBufferEdit::Create,
|
||||
"AdvSceneSwitcher.action.replay"});
|
||||
|
||||
const static std::map<ReplayBufferAction, std::string> actionTypes = {
|
||||
{ReplayBufferAction::STOP, "AdvSceneSwitcher.action.replay.type.stop"},
|
||||
{ReplayBufferAction::START,
|
||||
"AdvSceneSwitcher.action.replay.type.start"},
|
||||
{ReplayBufferAction::SAVE, "AdvSceneSwitcher.action.replay.type.save"},
|
||||
};
|
||||
|
||||
bool MacroActionReplayBuffer::PerformAction()
|
||||
{
|
||||
switch (_action) {
|
||||
case ReplayBufferAction::STOP:
|
||||
if (obs_frontend_replay_buffer_active()) {
|
||||
obs_frontend_replay_buffer_stop();
|
||||
}
|
||||
break;
|
||||
case ReplayBufferAction::START:
|
||||
if (!obs_frontend_replay_buffer_active()) {
|
||||
obs_frontend_replay_buffer_start();
|
||||
}
|
||||
break;
|
||||
case ReplayBufferAction::SAVE:
|
||||
if (obs_frontend_replay_buffer_active() &&
|
||||
_duration.DurationReached()) {
|
||||
obs_frontend_replay_buffer_save();
|
||||
// Default buffer size is 20s so waiting for 10s before
|
||||
// trying to save again seems reasonable
|
||||
_duration.seconds = 10;
|
||||
_duration.Reset();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void MacroActionReplayBuffer::LogAction()
|
||||
{
|
||||
auto it = actionTypes.find(_action);
|
||||
if (it != actionTypes.end()) {
|
||||
vblog(LOG_INFO, "performed action \"%s\"", it->second.c_str());
|
||||
} else {
|
||||
blog(LOG_WARNING, "ignored unknown replay buffer action %d",
|
||||
static_cast<int>(_action));
|
||||
}
|
||||
}
|
||||
|
||||
bool MacroActionReplayBuffer::Save(obs_data_t *obj)
|
||||
{
|
||||
MacroAction::Save(obj);
|
||||
obs_data_set_int(obj, "action", static_cast<int>(_action));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MacroActionReplayBuffer::Load(obs_data_t *obj)
|
||||
{
|
||||
MacroAction::Load(obj);
|
||||
_action = static_cast<ReplayBufferAction>(
|
||||
obs_data_get_int(obj, "action"));
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline void populateActionSelection(QComboBox *list)
|
||||
{
|
||||
for (auto entry : actionTypes) {
|
||||
list->addItem(obs_module_text(entry.second.c_str()));
|
||||
}
|
||||
}
|
||||
|
||||
MacroActionReplayBufferEdit::MacroActionReplayBufferEdit(
|
||||
QWidget *parent, std::shared_ptr<MacroActionReplayBuffer> entryData)
|
||||
: QWidget(parent)
|
||||
{
|
||||
_actions = new QComboBox();
|
||||
|
||||
populateActionSelection(_actions);
|
||||
|
||||
QWidget::connect(_actions, SIGNAL(currentIndexChanged(int)), this,
|
||||
SLOT(ActionChanged(int)));
|
||||
|
||||
QHBoxLayout *mainLayout = new QHBoxLayout;
|
||||
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
|
||||
{"{{actions}}", _actions},
|
||||
};
|
||||
placeWidgets(obs_module_text("AdvSceneSwitcher.action.replay.entry"),
|
||||
mainLayout, widgetPlaceholders);
|
||||
setLayout(mainLayout);
|
||||
|
||||
_entryData = entryData;
|
||||
UpdateEntryData();
|
||||
_loading = false;
|
||||
}
|
||||
|
||||
void MacroActionReplayBufferEdit::UpdateEntryData()
|
||||
{
|
||||
if (!_entryData) {
|
||||
return;
|
||||
}
|
||||
_actions->setCurrentIndex(static_cast<int>(_entryData->_action));
|
||||
}
|
||||
|
||||
void MacroActionReplayBufferEdit::ActionChanged(int value)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_action = static_cast<ReplayBufferAction>(value);
|
||||
}
|
||||
98
src/macro-action-run.cpp
Normal file
98
src/macro-action-run.cpp
Normal file
@@ -0,0 +1,98 @@
|
||||
#include "headers/macro-action-run.hpp"
|
||||
#include "headers/advanced-scene-switcher.hpp"
|
||||
#include "headers/utility.hpp"
|
||||
|
||||
#include <QProcess>
|
||||
#include <QFileDialog>
|
||||
|
||||
const std::string MacroActionRun::id = "run";
|
||||
|
||||
bool MacroActionRun::_registered = MacroActionFactory::Register(
|
||||
MacroActionRun::id, {MacroActionRun::Create, MacroActionRunEdit::Create,
|
||||
"AdvSceneSwitcher.action.run"});
|
||||
|
||||
bool MacroActionRun::PerformAction()
|
||||
{
|
||||
QProcess::startDetached(QString::fromStdString(_path), QStringList());
|
||||
return true;
|
||||
}
|
||||
|
||||
void MacroActionRun::LogAction()
|
||||
{
|
||||
vblog(LOG_INFO, "run \"%s\"", _path.c_str());
|
||||
}
|
||||
|
||||
bool MacroActionRun::Save(obs_data_t *obj)
|
||||
{
|
||||
MacroAction::Save(obj);
|
||||
obs_data_set_string(obj, "path", _path.c_str());
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MacroActionRun::Load(obs_data_t *obj)
|
||||
{
|
||||
MacroAction::Load(obj);
|
||||
_path = obs_data_get_string(obj, "path");
|
||||
return true;
|
||||
}
|
||||
|
||||
MacroActionRunEdit::MacroActionRunEdit(
|
||||
QWidget *parent, std::shared_ptr<MacroActionRun> entryData)
|
||||
: QWidget(parent)
|
||||
{
|
||||
_filePath = new QLineEdit();
|
||||
_browseButton =
|
||||
new QPushButton(obs_module_text("AdvSceneSwitcher.browse"));
|
||||
_browseButton->setStyleSheet("border:1px solid gray;");
|
||||
|
||||
QWidget::connect(_filePath, SIGNAL(editingFinished()), this,
|
||||
SLOT(FilePathChanged()));
|
||||
QWidget::connect(_browseButton, SIGNAL(clicked()), this,
|
||||
SLOT(BrowseButtonClicked()));
|
||||
|
||||
QHBoxLayout *mainLayout = new QHBoxLayout;
|
||||
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
|
||||
{"{{filePath}}", _filePath},
|
||||
{"{{browseButton}}", _browseButton},
|
||||
};
|
||||
placeWidgets(obs_module_text("AdvSceneSwitcher.action.run.entry"),
|
||||
mainLayout, widgetPlaceholders);
|
||||
setLayout(mainLayout);
|
||||
|
||||
_entryData = entryData;
|
||||
UpdateEntryData();
|
||||
_loading = false;
|
||||
}
|
||||
|
||||
void MacroActionRunEdit::UpdateEntryData()
|
||||
{
|
||||
if (!_entryData) {
|
||||
return;
|
||||
}
|
||||
_filePath->setText(QString::fromStdString(_entryData->_path));
|
||||
}
|
||||
|
||||
void MacroActionRunEdit::FilePathChanged()
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_path = _filePath->text().toUtf8().constData();
|
||||
}
|
||||
|
||||
void MacroActionRunEdit::BrowseButtonClicked()
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
QString path = QFileDialog::getOpenFileName(this);
|
||||
if (path.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
_filePath->setText(path);
|
||||
FilePathChanged();
|
||||
}
|
||||
73
src/macro-action-scene-switch.cpp
Normal file
73
src/macro-action-scene-switch.cpp
Normal file
@@ -0,0 +1,73 @@
|
||||
#include "headers/macro-action-scene-switch.hpp"
|
||||
#include "headers/advanced-scene-switcher.hpp"
|
||||
#include "headers/utility.hpp"
|
||||
|
||||
const std::string MacroActionSwitchScene::id = "scene_switch";
|
||||
|
||||
bool MacroActionSwitchScene::_registered = MacroActionFactory::Register(
|
||||
MacroActionSwitchScene::id,
|
||||
{MacroActionSwitchScene::Create, MacroActionSwitchSceneEdit::Create,
|
||||
"AdvSceneSwitcher.action.switchScene"});
|
||||
|
||||
bool MacroActionSwitchScene::PerformAction()
|
||||
{
|
||||
OBSWeakSource scene = getScene();
|
||||
switchScene({scene, transition, (int)(_duration.seconds * 1000)});
|
||||
return true;
|
||||
}
|
||||
|
||||
void MacroActionSwitchScene::LogAction()
|
||||
{
|
||||
if (switcher->verbose) {
|
||||
logMatch();
|
||||
}
|
||||
}
|
||||
|
||||
bool MacroActionSwitchScene::Save(obs_data_t *obj)
|
||||
{
|
||||
MacroAction::Save(obj);
|
||||
SceneSwitcherEntry::save(obj);
|
||||
_duration.Save(obj);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MacroActionSwitchScene::Load(obs_data_t *obj)
|
||||
{
|
||||
MacroAction::Load(obj);
|
||||
SceneSwitcherEntry::load(obj);
|
||||
_duration.Load(obj);
|
||||
return true;
|
||||
}
|
||||
|
||||
MacroActionSwitchSceneEdit::MacroActionSwitchSceneEdit(
|
||||
QWidget *parent, std::shared_ptr<MacroActionSwitchScene> entryData)
|
||||
: SwitchWidget(parent, entryData.get(), true, true)
|
||||
{
|
||||
_duration = new DurationSelection(parent, false);
|
||||
QWidget::connect(_duration, SIGNAL(DurationChanged(double)), this,
|
||||
SLOT(DurationChanged(double)));
|
||||
|
||||
QHBoxLayout *mainLayout = new QHBoxLayout;
|
||||
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
|
||||
{"{{scenes}}", scenes},
|
||||
{"{{transitions}}", transitions},
|
||||
{"{{duration}}", _duration},
|
||||
};
|
||||
placeWidgets(obs_module_text("AdvSceneSwitcher.action.scene.entry"),
|
||||
mainLayout, widgetPlaceholders);
|
||||
setLayout(mainLayout);
|
||||
|
||||
_entryData = entryData;
|
||||
_duration->SetDuration(_entryData->_duration);
|
||||
SwitchWidget::loading = false;
|
||||
}
|
||||
|
||||
void MacroActionSwitchSceneEdit::DurationChanged(double seconds)
|
||||
{
|
||||
if (SwitchWidget::loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_duration.seconds = seconds;
|
||||
}
|
||||
212
src/macro-action-scene-visibility.cpp
Normal file
212
src/macro-action-scene-visibility.cpp
Normal file
@@ -0,0 +1,212 @@
|
||||
#include "headers/macro-action-scene-visibility.hpp"
|
||||
#include "headers/advanced-scene-switcher.hpp"
|
||||
#include "headers/utility.hpp"
|
||||
|
||||
const std::string MacroActionSceneVisibility::id = "scene_visibility";
|
||||
|
||||
bool MacroActionSceneVisibility::_registered = MacroActionFactory::Register(
|
||||
MacroActionSceneVisibility::id,
|
||||
{MacroActionSceneVisibility::Create,
|
||||
MacroActionSceneVisibilityEdit::Create,
|
||||
"AdvSceneSwitcher.action.sceneVisibility"});
|
||||
|
||||
const static std::map<SceneVisibilityAction, std::string> actionTypes = {
|
||||
{SceneVisibilityAction::SHOW,
|
||||
"AdvSceneSwitcher.action.sceneVisibility.type.show"},
|
||||
{SceneVisibilityAction::HIDE,
|
||||
"AdvSceneSwitcher.action.sceneVisibility.type.hide"},
|
||||
};
|
||||
|
||||
struct VisInfo {
|
||||
std::string name;
|
||||
bool visible;
|
||||
};
|
||||
|
||||
static bool visibilityEnum(obs_scene_t *, obs_sceneitem_t *item, void *ptr)
|
||||
{
|
||||
VisInfo *vInfo = reinterpret_cast<VisInfo *>(ptr);
|
||||
auto sourceName = obs_source_get_name(obs_sceneitem_get_source(item));
|
||||
if (vInfo->name == sourceName) {
|
||||
obs_sceneitem_set_visible(item, vInfo->visible);
|
||||
}
|
||||
|
||||
if (obs_sceneitem_is_group(item)) {
|
||||
obs_scene_t *scene = obs_sceneitem_group_get_scene(item);
|
||||
obs_scene_enum_items(scene, visibilityEnum, ptr);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MacroActionSceneVisibility::PerformAction()
|
||||
{
|
||||
auto s = obs_weak_source_get_source(_scene);
|
||||
auto scene = obs_scene_from_source(s);
|
||||
auto sourceName = GetWeakSourceName(_source);
|
||||
VisInfo vInfo = {sourceName, _action == SceneVisibilityAction::SHOW};
|
||||
|
||||
switch (_action) {
|
||||
case SceneVisibilityAction::SHOW:
|
||||
obs_scene_enum_items(scene, visibilityEnum, &vInfo);
|
||||
break;
|
||||
case SceneVisibilityAction::HIDE:
|
||||
obs_scene_enum_items(scene, visibilityEnum, &vInfo);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
obs_source_release(s);
|
||||
return true;
|
||||
}
|
||||
|
||||
void MacroActionSceneVisibility::LogAction()
|
||||
{
|
||||
auto it = actionTypes.find(_action);
|
||||
if (it != actionTypes.end()) {
|
||||
vblog(LOG_INFO,
|
||||
"performed action \"%s\" for source \"%s\" on scene \"%s\"",
|
||||
it->second.c_str(), GetWeakSourceName(_scene).c_str(),
|
||||
GetWeakSourceName(_scene).c_str());
|
||||
} else {
|
||||
blog(LOG_WARNING, "ignored unknown SceneVisibility action %d",
|
||||
static_cast<int>(_action));
|
||||
}
|
||||
}
|
||||
|
||||
bool MacroActionSceneVisibility::Save(obs_data_t *obj)
|
||||
{
|
||||
MacroAction::Save(obj);
|
||||
obs_data_set_string(obj, "scene", GetWeakSourceName(_scene).c_str());
|
||||
obs_data_set_string(obj, "source", GetWeakSourceName(_source).c_str());
|
||||
obs_data_set_int(obj, "action", static_cast<int>(_action));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MacroActionSceneVisibility::Load(obs_data_t *obj)
|
||||
{
|
||||
MacroAction::Load(obj);
|
||||
const char *sceneName = obs_data_get_string(obj, "scene");
|
||||
_scene = GetWeakSourceByName(sceneName);
|
||||
const char *sourceName = obs_data_get_string(obj, "source");
|
||||
_source = GetWeakSourceByName(sourceName);
|
||||
_action = static_cast<SceneVisibilityAction>(
|
||||
obs_data_get_int(obj, "action"));
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline void populateActionSelection(QComboBox *list)
|
||||
{
|
||||
for (auto entry : actionTypes) {
|
||||
list->addItem(obs_module_text(entry.second.c_str()));
|
||||
}
|
||||
}
|
||||
|
||||
static bool enumItem(obs_scene_t *, obs_sceneitem_t *item, void *ptr)
|
||||
{
|
||||
std::set<QString> *names = reinterpret_cast<std::set<QString> *>(ptr);
|
||||
|
||||
if (obs_sceneitem_is_group(item)) {
|
||||
obs_scene_t *scene = obs_sceneitem_group_get_scene(item);
|
||||
obs_scene_enum_items(scene, enumItem, ptr);
|
||||
}
|
||||
auto name = obs_source_get_name(obs_sceneitem_get_source(item));
|
||||
names->emplace(name);
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline void populateSceneItems(QComboBox *list,
|
||||
OBSWeakSource sceneWeakSource = nullptr)
|
||||
{
|
||||
std::set<QString> names;
|
||||
auto s = obs_weak_source_get_source(sceneWeakSource);
|
||||
auto scene = obs_scene_from_source(s);
|
||||
obs_scene_enum_items(scene, enumItem, &names);
|
||||
obs_source_release(s);
|
||||
|
||||
for (auto &name : names) {
|
||||
list->addItem(name);
|
||||
}
|
||||
list->model()->sort(0);
|
||||
addSelectionEntry(list, obs_module_text("AdvSceneSwitcher.selectItem"));
|
||||
list->setCurrentIndex(0);
|
||||
}
|
||||
|
||||
MacroActionSceneVisibilityEdit::MacroActionSceneVisibilityEdit(
|
||||
QWidget *parent, std::shared_ptr<MacroActionSceneVisibility> entryData)
|
||||
: QWidget(parent)
|
||||
{
|
||||
_scenes = new QComboBox();
|
||||
_sources = new QComboBox();
|
||||
_actions = new QComboBox();
|
||||
|
||||
populateActionSelection(_actions);
|
||||
populateSceneSelection(_scenes);
|
||||
|
||||
QWidget::connect(_actions, SIGNAL(currentIndexChanged(int)), this,
|
||||
SLOT(ActionChanged(int)));
|
||||
QWidget::connect(_scenes, SIGNAL(currentTextChanged(const QString &)),
|
||||
this, SLOT(SceneChanged(const QString &)));
|
||||
QWidget::connect(_sources, SIGNAL(currentTextChanged(const QString &)),
|
||||
this, SLOT(SourceChanged(const QString &)));
|
||||
|
||||
QHBoxLayout *mainLayout = new QHBoxLayout;
|
||||
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
|
||||
{"{{scenes}}", _scenes},
|
||||
{"{{sources}}", _sources},
|
||||
{"{{actions}}", _actions},
|
||||
};
|
||||
placeWidgets(obs_module_text(
|
||||
"AdvSceneSwitcher.action.SceneVisibility.entry"),
|
||||
mainLayout, widgetPlaceholders);
|
||||
setLayout(mainLayout);
|
||||
|
||||
_entryData = entryData;
|
||||
UpdateEntryData();
|
||||
_loading = false;
|
||||
}
|
||||
|
||||
void MacroActionSceneVisibilityEdit::UpdateEntryData()
|
||||
{
|
||||
if (!_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
_actions->setCurrentIndex(static_cast<int>(_entryData->_action));
|
||||
_scenes->setCurrentText(GetWeakSourceName(_entryData->_scene).c_str());
|
||||
populateSceneItems(_sources, _entryData->_scene);
|
||||
_sources->setCurrentText(
|
||||
GetWeakSourceName(_entryData->_source).c_str());
|
||||
}
|
||||
|
||||
void MacroActionSceneVisibilityEdit::SceneChanged(const QString &text)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_scene = GetWeakSourceByQString(text);
|
||||
}
|
||||
_sources->clear();
|
||||
populateSceneItems(_sources, _entryData->_scene);
|
||||
}
|
||||
|
||||
void MacroActionSceneVisibilityEdit::SourceChanged(const QString &text)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_source = GetWeakSourceByQString(text);
|
||||
}
|
||||
|
||||
void MacroActionSceneVisibilityEdit::ActionChanged(int value)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_action = static_cast<SceneVisibilityAction>(value);
|
||||
}
|
||||
201
src/macro-action-source.cpp
Normal file
201
src/macro-action-source.cpp
Normal file
@@ -0,0 +1,201 @@
|
||||
#include "headers/macro-action-source.hpp"
|
||||
#include "headers/advanced-scene-switcher.hpp"
|
||||
#include "headers/utility.hpp"
|
||||
|
||||
const std::string MacroActionSource::id = "source";
|
||||
|
||||
bool MacroActionSource::_registered = MacroActionFactory::Register(
|
||||
MacroActionSource::id,
|
||||
{MacroActionSource::Create, MacroActionSourceEdit::Create,
|
||||
"AdvSceneSwitcher.action.source"});
|
||||
|
||||
const static std::map<SourceAction, std::string> actionTypes = {
|
||||
{SourceAction::ENABLE, "AdvSceneSwitcher.action.source.type.enable"},
|
||||
{SourceAction::DISABLE, "AdvSceneSwitcher.action.source.type.disable"},
|
||||
{SourceAction::SETTINGS,
|
||||
"AdvSceneSwitcher.action.source.type.settings"},
|
||||
};
|
||||
|
||||
void setSourceSettings(obs_source_t *s, const std::string &settings)
|
||||
{
|
||||
if (settings.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
obs_data_t *data = obs_data_create_from_json(settings.c_str());
|
||||
if (!data) {
|
||||
blog(LOG_WARNING, "invalid source settings provided: \n%s",
|
||||
settings.c_str());
|
||||
return;
|
||||
}
|
||||
obs_source_update(s, data);
|
||||
obs_data_release(data);
|
||||
}
|
||||
|
||||
bool MacroActionSource::PerformAction()
|
||||
{
|
||||
auto s = obs_weak_source_get_source(_source);
|
||||
switch (_action) {
|
||||
case SourceAction::ENABLE:
|
||||
obs_source_set_enabled(s, true);
|
||||
break;
|
||||
case SourceAction::DISABLE:
|
||||
obs_source_set_enabled(s, false);
|
||||
break;
|
||||
case SourceAction::SETTINGS:
|
||||
setSourceSettings(s, _settings);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
obs_source_release(s);
|
||||
return true;
|
||||
}
|
||||
|
||||
void MacroActionSource::LogAction()
|
||||
{
|
||||
auto it = actionTypes.find(_action);
|
||||
if (it != actionTypes.end()) {
|
||||
vblog(LOG_INFO, "performed action \"%s\" for Source \"%s\"",
|
||||
it->second.c_str(), GetWeakSourceName(_source).c_str());
|
||||
} else {
|
||||
blog(LOG_WARNING, "ignored unknown source action %d",
|
||||
static_cast<int>(_action));
|
||||
}
|
||||
}
|
||||
|
||||
bool MacroActionSource::Save(obs_data_t *obj)
|
||||
{
|
||||
MacroAction::Save(obj);
|
||||
obs_data_set_string(obj, "source", GetWeakSourceName(_source).c_str());
|
||||
obs_data_set_int(obj, "action", static_cast<int>(_action));
|
||||
obs_data_set_string(obj, "settings", _settings.c_str());
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MacroActionSource::Load(obs_data_t *obj)
|
||||
{
|
||||
MacroAction::Load(obj);
|
||||
const char *sourceName = obs_data_get_string(obj, "source");
|
||||
_source = GetWeakSourceByName(sourceName);
|
||||
_action = static_cast<SourceAction>(obs_data_get_int(obj, "action"));
|
||||
_settings = obs_data_get_string(obj, "settings");
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline void populateActionSelection(QComboBox *list)
|
||||
{
|
||||
for (auto entry : actionTypes) {
|
||||
list->addItem(obs_module_text(entry.second.c_str()));
|
||||
}
|
||||
}
|
||||
|
||||
MacroActionSourceEdit::MacroActionSourceEdit(
|
||||
QWidget *parent, std::shared_ptr<MacroActionSource> entryData)
|
||||
: QWidget(parent)
|
||||
{
|
||||
_sources = new QComboBox();
|
||||
_actions = new QComboBox();
|
||||
_getSettings = new QPushButton(
|
||||
obs_module_text("AdvSceneSwitcher.action.source.getSettings"));
|
||||
_settings = new QPlainTextEdit();
|
||||
_warning = new QLabel(
|
||||
obs_module_text("AdvSceneSwitcher.action.source.warning"));
|
||||
|
||||
populateActionSelection(_actions);
|
||||
populateSourceSelection(_sources);
|
||||
|
||||
QWidget::connect(_actions, SIGNAL(currentIndexChanged(int)), this,
|
||||
SLOT(ActionChanged(int)));
|
||||
QWidget::connect(_sources, SIGNAL(currentTextChanged(const QString &)),
|
||||
this, SLOT(SourceChanged(const QString &)));
|
||||
QWidget::connect(_getSettings, SIGNAL(clicked()), this,
|
||||
SLOT(GetSettingsClicked()));
|
||||
QWidget::connect(_settings, SIGNAL(textChanged()), this,
|
||||
SLOT(SettingsChanged()));
|
||||
|
||||
QVBoxLayout *mainLayout = new QVBoxLayout;
|
||||
QHBoxLayout *entryLayout = new QHBoxLayout;
|
||||
QHBoxLayout *buttonLayout = new QHBoxLayout;
|
||||
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
|
||||
{"{{sources}}", _sources},
|
||||
{"{{actions}}", _actions},
|
||||
{"{{settings}}", _settings},
|
||||
{"{{getSettings}}", _getSettings},
|
||||
};
|
||||
placeWidgets(obs_module_text("AdvSceneSwitcher.action.source.entry"),
|
||||
entryLayout, widgetPlaceholders);
|
||||
mainLayout->addLayout(entryLayout);
|
||||
mainLayout->addWidget(_warning);
|
||||
mainLayout->addWidget(_settings);
|
||||
buttonLayout->addWidget(_getSettings);
|
||||
buttonLayout->addStretch();
|
||||
mainLayout->addLayout(buttonLayout);
|
||||
setLayout(mainLayout);
|
||||
|
||||
_entryData = entryData;
|
||||
UpdateEntryData();
|
||||
_loading = false;
|
||||
}
|
||||
|
||||
void MacroActionSourceEdit::UpdateEntryData()
|
||||
{
|
||||
if (!_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
_actions->setCurrentIndex(static_cast<int>(_entryData->_action));
|
||||
_sources->setCurrentText(
|
||||
GetWeakSourceName(_entryData->_source).c_str());
|
||||
_settings->setPlainText(QString::fromStdString(_entryData->_settings));
|
||||
SetWidgetVisibility(_entryData->_action == SourceAction::SETTINGS);
|
||||
}
|
||||
|
||||
void MacroActionSourceEdit::SourceChanged(const QString &text)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_source = GetWeakSourceByQString(text);
|
||||
}
|
||||
|
||||
void MacroActionSourceEdit::ActionChanged(int value)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_action = static_cast<SourceAction>(value);
|
||||
SetWidgetVisibility(_entryData->_action == SourceAction::SETTINGS);
|
||||
}
|
||||
|
||||
void MacroActionSourceEdit::GetSettingsClicked()
|
||||
{
|
||||
if (_loading || !_entryData || !_entryData->_source) {
|
||||
return;
|
||||
}
|
||||
|
||||
_settings->setPlainText(
|
||||
QString::fromStdString(getSourceSettings(_entryData->_source)));
|
||||
}
|
||||
|
||||
void MacroActionSourceEdit::SettingsChanged()
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_settings = _settings->toPlainText().toStdString();
|
||||
}
|
||||
|
||||
void MacroActionSourceEdit::SetWidgetVisibility(bool showSettings)
|
||||
{
|
||||
_settings->setVisible(showSettings);
|
||||
_getSettings->setVisible(showSettings);
|
||||
_warning->setVisible(!showSettings);
|
||||
adjustSize();
|
||||
}
|
||||
111
src/macro-action-streaming.cpp
Normal file
111
src/macro-action-streaming.cpp
Normal file
@@ -0,0 +1,111 @@
|
||||
#include "headers/macro-action-streaming.hpp"
|
||||
#include "headers/advanced-scene-switcher.hpp"
|
||||
#include "headers/utility.hpp"
|
||||
|
||||
const std::string MacroActionStream::id = "streaming";
|
||||
|
||||
bool MacroActionStream::_registered = MacroActionFactory::Register(
|
||||
MacroActionStream::id,
|
||||
{MacroActionStream::Create, MacroActionStreamEdit::Create,
|
||||
"AdvSceneSwitcher.action.streaming"});
|
||||
|
||||
const static std::map<StreamAction, std::string> actionTypes = {
|
||||
{StreamAction::STOP, "AdvSceneSwitcher.action.streaming.type.stop"},
|
||||
{StreamAction::START, "AdvSceneSwitcher.action.streaming.type.start"},
|
||||
};
|
||||
|
||||
bool MacroActionStream::PerformAction()
|
||||
{
|
||||
switch (_action) {
|
||||
case StreamAction::STOP:
|
||||
if (obs_frontend_streaming_active()) {
|
||||
obs_frontend_streaming_stop();
|
||||
}
|
||||
break;
|
||||
case StreamAction::START:
|
||||
if (!obs_frontend_streaming_active() &&
|
||||
_retryCooldown.DurationReached()) {
|
||||
obs_frontend_streaming_start();
|
||||
_retryCooldown.seconds++;
|
||||
_retryCooldown.Reset();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void MacroActionStream::LogAction()
|
||||
{
|
||||
auto it = actionTypes.find(_action);
|
||||
if (it != actionTypes.end()) {
|
||||
vblog(LOG_INFO, "performed action \"%s\"", it->second.c_str());
|
||||
} else {
|
||||
blog(LOG_WARNING, "ignored unknown streaming action %d",
|
||||
static_cast<int>(_action));
|
||||
}
|
||||
}
|
||||
|
||||
bool MacroActionStream::Save(obs_data_t *obj)
|
||||
{
|
||||
MacroAction::Save(obj);
|
||||
obs_data_set_int(obj, "action", static_cast<int>(_action));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MacroActionStream::Load(obs_data_t *obj)
|
||||
{
|
||||
MacroAction::Load(obj);
|
||||
_action = static_cast<StreamAction>(obs_data_get_int(obj, "action"));
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline void populateActionSelection(QComboBox *list)
|
||||
{
|
||||
for (auto entry : actionTypes) {
|
||||
list->addItem(obs_module_text(entry.second.c_str()));
|
||||
}
|
||||
}
|
||||
|
||||
MacroActionStreamEdit::MacroActionStreamEdit(
|
||||
QWidget *parent, std::shared_ptr<MacroActionStream> entryData)
|
||||
: QWidget(parent)
|
||||
{
|
||||
_actions = new QComboBox();
|
||||
|
||||
populateActionSelection(_actions);
|
||||
|
||||
QWidget::connect(_actions, SIGNAL(currentIndexChanged(int)), this,
|
||||
SLOT(ActionChanged(int)));
|
||||
|
||||
QHBoxLayout *mainLayout = new QHBoxLayout;
|
||||
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
|
||||
{"{{actions}}", _actions},
|
||||
};
|
||||
placeWidgets(obs_module_text("AdvSceneSwitcher.action.streaming.entry"),
|
||||
mainLayout, widgetPlaceholders);
|
||||
setLayout(mainLayout);
|
||||
|
||||
_entryData = entryData;
|
||||
UpdateEntryData();
|
||||
_loading = false;
|
||||
}
|
||||
|
||||
void MacroActionStreamEdit::UpdateEntryData()
|
||||
{
|
||||
if (!_entryData) {
|
||||
return;
|
||||
}
|
||||
_actions->setCurrentIndex(static_cast<int>(_entryData->_action));
|
||||
}
|
||||
|
||||
void MacroActionStreamEdit::ActionChanged(int value)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_action = static_cast<StreamAction>(value);
|
||||
}
|
||||
206
src/macro-action-wait.cpp
Normal file
206
src/macro-action-wait.cpp
Normal file
@@ -0,0 +1,206 @@
|
||||
#include "headers/macro-action-wait.hpp"
|
||||
#include "headers/advanced-scene-switcher.hpp"
|
||||
#include "headers/utility.hpp"
|
||||
|
||||
#include <random>
|
||||
|
||||
const std::string MacroActionWait::id = "wait";
|
||||
|
||||
bool MacroActionWait::_registered = MacroActionFactory::Register(
|
||||
MacroActionWait::id,
|
||||
{MacroActionWait::Create, MacroActionWaitEdit::Create,
|
||||
"AdvSceneSwitcher.action.wait"});
|
||||
|
||||
static std::map<WaitType, std::string> waitTypes = {
|
||||
{WaitType::FIXED, "AdvSceneSwitcher.action.wait.type.fixed"},
|
||||
{WaitType::RANDOM, "AdvSceneSwitcher.action.wait.type.random"},
|
||||
};
|
||||
|
||||
static std::random_device rd;
|
||||
static std::default_random_engine re(rd());
|
||||
|
||||
bool MacroActionWait::PerformAction()
|
||||
{
|
||||
double sleep_duration;
|
||||
if (_waitType == WaitType::FIXED) {
|
||||
sleep_duration = _duration.seconds;
|
||||
} else {
|
||||
double min = (_duration.seconds < _duration2.seconds)
|
||||
? _duration.seconds
|
||||
: _duration2.seconds;
|
||||
double max = (_duration.seconds < _duration2.seconds)
|
||||
? _duration2.seconds
|
||||
: _duration.seconds;
|
||||
std::uniform_real_distribution<double> unif(min, max);
|
||||
sleep_duration = unif(re);
|
||||
}
|
||||
vblog(LOG_INFO, "perform action wait with duration of %f",
|
||||
sleep_duration);
|
||||
|
||||
std::unique_lock<std::mutex> lock(switcher->m);
|
||||
switcher->cv.wait_for(lock, std::chrono::milliseconds((
|
||||
long long)(sleep_duration * 1000)));
|
||||
return !switcher->stop;
|
||||
}
|
||||
|
||||
bool MacroActionWait::Save(obs_data_t *obj)
|
||||
{
|
||||
MacroAction::Save(obj);
|
||||
_duration.Save(obj);
|
||||
_duration2.Save(obj, "seconds2", "displayUnit2");
|
||||
obs_data_set_int(obj, "waitType", static_cast<int>(_waitType));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MacroActionWait::Load(obs_data_t *obj)
|
||||
{
|
||||
MacroAction::Load(obj);
|
||||
_duration.Load(obj);
|
||||
_duration2.Load(obj, "seconds2", "displayUnit2");
|
||||
_waitType = static_cast<WaitType>(obs_data_get_int(obj, "waitType"));
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline void populateTypeSelection(QComboBox *list)
|
||||
{
|
||||
for (auto entry : waitTypes) {
|
||||
list->addItem(obs_module_text(entry.second.c_str()));
|
||||
}
|
||||
}
|
||||
|
||||
MacroActionWaitEdit::MacroActionWaitEdit(
|
||||
QWidget *parent, std::shared_ptr<MacroActionWait> entryData)
|
||||
: QWidget(parent)
|
||||
{
|
||||
_duration = new DurationSelection();
|
||||
_duration2 = new DurationSelection();
|
||||
_waitType = new QComboBox();
|
||||
|
||||
populateTypeSelection(_waitType);
|
||||
|
||||
QWidget::connect(_duration, SIGNAL(DurationChanged(double)), this,
|
||||
SLOT(DurationChanged(double)));
|
||||
QWidget::connect(_duration, SIGNAL(UnitChanged(DurationUnit)), this,
|
||||
SLOT(DurationUnitChanged(DurationUnit)));
|
||||
QWidget::connect(_duration2, SIGNAL(DurationChanged(double)), this,
|
||||
SLOT(Duration2Changed(double)));
|
||||
QWidget::connect(_duration2, SIGNAL(UnitChanged(DurationUnit)), this,
|
||||
SLOT(Duration2UnitChanged(DurationUnit)));
|
||||
QWidget::connect(_waitType, SIGNAL(currentIndexChanged(int)), this,
|
||||
SLOT(TypeChanged(int)));
|
||||
|
||||
_mainLayout = new QHBoxLayout;
|
||||
setLayout(_mainLayout);
|
||||
|
||||
_entryData = entryData;
|
||||
UpdateEntryData();
|
||||
_loading = false;
|
||||
}
|
||||
|
||||
void MacroActionWaitEdit::UpdateEntryData()
|
||||
{
|
||||
if (!_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (_entryData->_waitType == WaitType::FIXED) {
|
||||
SetupFixedDurationEdit();
|
||||
} else {
|
||||
SetupRandomDurationEdit();
|
||||
}
|
||||
|
||||
_duration->SetDuration(_entryData->_duration);
|
||||
_duration2->SetDuration(_entryData->_duration2);
|
||||
_waitType->setCurrentIndex(static_cast<int>(_entryData->_waitType));
|
||||
}
|
||||
|
||||
void MacroActionWaitEdit::SetupFixedDurationEdit()
|
||||
{
|
||||
_mainLayout->removeWidget(_duration);
|
||||
_mainLayout->removeWidget(_duration2);
|
||||
_mainLayout->removeWidget(_waitType);
|
||||
clearLayout(_mainLayout);
|
||||
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
|
||||
{"{{duration}}", _duration},
|
||||
{"{{waitType}}", _waitType},
|
||||
};
|
||||
placeWidgets(
|
||||
obs_module_text("AdvSceneSwitcher.action.wait.entry.fixed"),
|
||||
_mainLayout, widgetPlaceholders);
|
||||
_duration2->hide();
|
||||
}
|
||||
|
||||
void MacroActionWaitEdit::SetupRandomDurationEdit()
|
||||
{
|
||||
_mainLayout->removeWidget(_duration);
|
||||
_mainLayout->removeWidget(_duration2);
|
||||
_mainLayout->removeWidget(_waitType);
|
||||
clearLayout(_mainLayout);
|
||||
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
|
||||
{"{{duration}}", _duration},
|
||||
{"{{duration2}}", _duration2},
|
||||
{"{{waitType}}", _waitType},
|
||||
};
|
||||
placeWidgets(
|
||||
obs_module_text("AdvSceneSwitcher.action.wait.entry.random"),
|
||||
_mainLayout, widgetPlaceholders);
|
||||
_duration2->show();
|
||||
}
|
||||
|
||||
void MacroActionWaitEdit::TypeChanged(int value)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
WaitType type = static_cast<WaitType>(value);
|
||||
|
||||
if (type == WaitType::FIXED) {
|
||||
SetupFixedDurationEdit();
|
||||
} else {
|
||||
SetupRandomDurationEdit();
|
||||
}
|
||||
|
||||
_entryData->_waitType = type;
|
||||
}
|
||||
|
||||
void MacroActionWaitEdit::DurationChanged(double seconds)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_duration.seconds = seconds;
|
||||
}
|
||||
|
||||
void MacroActionWaitEdit::DurationUnitChanged(DurationUnit unit)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_duration.displayUnit = unit;
|
||||
}
|
||||
|
||||
void MacroActionWaitEdit::Duration2Changed(double seconds)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_duration2.seconds = seconds;
|
||||
}
|
||||
|
||||
void MacroActionWaitEdit::Duration2UnitChanged(DurationUnit unit)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_duration2.displayUnit = unit;
|
||||
}
|
||||
217
src/macro-condition-audio.cpp
Normal file
217
src/macro-condition-audio.cpp
Normal file
@@ -0,0 +1,217 @@
|
||||
#include "headers/macro-condition-edit.hpp"
|
||||
#include "headers/macro-condition-audio.hpp"
|
||||
#include "headers/utility.hpp"
|
||||
#include "headers/advanced-scene-switcher.hpp"
|
||||
|
||||
const std::string MacroConditionAudio::id = "audio";
|
||||
|
||||
bool MacroConditionAudio::_registered = MacroConditionFactory::Register(
|
||||
MacroConditionAudio::id,
|
||||
{MacroConditionAudio::Create, MacroConditionAudioEdit::Create,
|
||||
"AdvSceneSwitcher.condition.audio"});
|
||||
|
||||
static std::map<AudioCondition, std::string> audioConditionTypes = {
|
||||
{AudioCondition::ABOVE, "AdvSceneSwitcher.condition.audio.state.above"},
|
||||
{AudioCondition::BELOW, "AdvSceneSwitcher.condition.audio.state.below"},
|
||||
};
|
||||
|
||||
MacroConditionAudio::~MacroConditionAudio()
|
||||
{
|
||||
obs_volmeter_remove_callback(_volmeter, SetVolumeLevel, this);
|
||||
obs_volmeter_destroy(_volmeter);
|
||||
}
|
||||
|
||||
bool MacroConditionAudio::CheckCondition()
|
||||
{
|
||||
// peak will have a value from -60 db to 0 db
|
||||
bool volumeThresholdreached = false;
|
||||
|
||||
if (_condition == AudioCondition::ABOVE) {
|
||||
volumeThresholdreached = ((double)_peak + 60) * 1.7 > _volume;
|
||||
} else {
|
||||
volumeThresholdreached = ((double)_peak + 60) * 1.7 < _volume;
|
||||
}
|
||||
|
||||
// Reset for next check
|
||||
_peak = -std::numeric_limits<float>::infinity();
|
||||
|
||||
return volumeThresholdreached;
|
||||
}
|
||||
|
||||
bool MacroConditionAudio::Save(obs_data_t *obj)
|
||||
{
|
||||
MacroCondition::Save(obj);
|
||||
obs_data_set_string(obj, "audioSource",
|
||||
GetWeakSourceName(_audioSource).c_str());
|
||||
obs_data_set_int(obj, "volume", _volume);
|
||||
obs_data_set_int(obj, "condition", static_cast<int>(_condition));
|
||||
return true;
|
||||
}
|
||||
|
||||
obs_volmeter_t *AddVolmeterToSource(MacroConditionAudio *entry,
|
||||
obs_weak_source *source)
|
||||
{
|
||||
obs_volmeter_t *volmeter = obs_volmeter_create(OBS_FADER_LOG);
|
||||
obs_volmeter_add_callback(volmeter, MacroConditionAudio::SetVolumeLevel,
|
||||
entry);
|
||||
obs_source_t *as = obs_weak_source_get_source(source);
|
||||
if (!obs_volmeter_attach_source(volmeter, as)) {
|
||||
const char *name = obs_source_get_name(as);
|
||||
blog(LOG_WARNING, "failed to attach volmeter to source %s",
|
||||
name);
|
||||
}
|
||||
obs_source_release(as);
|
||||
|
||||
return volmeter;
|
||||
}
|
||||
|
||||
bool MacroConditionAudio::Load(obs_data_t *obj)
|
||||
{
|
||||
MacroCondition::Load(obj);
|
||||
const char *audioSourceName = obs_data_get_string(obj, "audioSource");
|
||||
_audioSource = GetWeakSourceByName(audioSourceName);
|
||||
_volume = obs_data_get_int(obj, "volume");
|
||||
_condition =
|
||||
static_cast<AudioCondition>(obs_data_get_int(obj, "condition"));
|
||||
_volmeter = AddVolmeterToSource(this, _audioSource);
|
||||
return true;
|
||||
}
|
||||
|
||||
void MacroConditionAudio::SetVolumeLevel(
|
||||
void *data, const float magnitude[MAX_AUDIO_CHANNELS],
|
||||
const float peak[MAX_AUDIO_CHANNELS],
|
||||
const float inputPeak[MAX_AUDIO_CHANNELS])
|
||||
{
|
||||
UNUSED_PARAMETER(magnitude);
|
||||
UNUSED_PARAMETER(inputPeak);
|
||||
MacroConditionAudio *c = static_cast<MacroConditionAudio *>(data);
|
||||
|
||||
for (int i = 0; i < MAX_AUDIO_CHANNELS; i++) {
|
||||
if (peak[i] > c->_peak) {
|
||||
c->_peak = peak[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MacroConditionAudio::ResetVolmeter()
|
||||
{
|
||||
obs_volmeter_remove_callback(_volmeter, SetVolumeLevel, this);
|
||||
obs_volmeter_destroy(_volmeter);
|
||||
|
||||
_volmeter = AddVolmeterToSource(this, _audioSource);
|
||||
}
|
||||
|
||||
static inline void populateConditionSelection(QComboBox *list)
|
||||
{
|
||||
for (auto entry : audioConditionTypes) {
|
||||
list->addItem(obs_module_text(entry.second.c_str()));
|
||||
}
|
||||
}
|
||||
|
||||
MacroConditionAudioEdit::MacroConditionAudioEdit(
|
||||
QWidget *parent, std::shared_ptr<MacroConditionAudio> entryData)
|
||||
: QWidget(parent)
|
||||
{
|
||||
_audioSources = new QComboBox();
|
||||
_condition = new QComboBox();
|
||||
_volume = new QSpinBox();
|
||||
|
||||
_volume->setSuffix("%");
|
||||
_volume->setMaximum(100);
|
||||
_volume->setMinimum(0);
|
||||
|
||||
QWidget::connect(_volume, SIGNAL(valueChanged(int)), this,
|
||||
SLOT(VolumeThresholdChanged(int)));
|
||||
QWidget::connect(_condition, SIGNAL(currentIndexChanged(int)), this,
|
||||
SLOT(ConditionChanged(int)));
|
||||
QWidget::connect(_audioSources,
|
||||
SIGNAL(currentTextChanged(const QString &)), this,
|
||||
SLOT(SourceChanged(const QString &)));
|
||||
|
||||
populateAudioSelection(_audioSources);
|
||||
populateConditionSelection(_condition);
|
||||
|
||||
QHBoxLayout *switchLayout = new QHBoxLayout;
|
||||
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
|
||||
{"{{audioSources}}", _audioSources},
|
||||
{"{{volume}}", _volume},
|
||||
{"{{condition}}", _condition},
|
||||
};
|
||||
placeWidgets(obs_module_text("AdvSceneSwitcher.condition.audio.entry"),
|
||||
switchLayout, widgetPlaceholders);
|
||||
|
||||
QVBoxLayout *mainLayout = new QVBoxLayout;
|
||||
|
||||
mainLayout->addLayout(switchLayout);
|
||||
|
||||
setLayout(mainLayout);
|
||||
|
||||
_entryData = entryData;
|
||||
UpdateEntryData();
|
||||
_loading = false;
|
||||
}
|
||||
|
||||
void MacroConditionAudioEdit::UpdateVolmeterSource()
|
||||
{
|
||||
delete _volMeter;
|
||||
obs_source_t *soruce =
|
||||
obs_weak_source_get_source(_entryData->_audioSource);
|
||||
_volMeter = new VolControl(soruce);
|
||||
obs_source_release(soruce);
|
||||
|
||||
QLayout *layout = this->layout();
|
||||
layout->addWidget(_volMeter);
|
||||
|
||||
QWidget::connect(_volMeter->GetSlider(), SIGNAL(valueChanged(int)),
|
||||
_volume, SLOT(setValue(int)));
|
||||
QWidget::connect(_volume, SIGNAL(valueChanged(int)),
|
||||
_volMeter->GetSlider(), SLOT(setValue(int)));
|
||||
|
||||
// Slider will default to 0 so set it manually once
|
||||
_volMeter->GetSlider()->setValue(_entryData->_volume);
|
||||
}
|
||||
|
||||
void MacroConditionAudioEdit::SourceChanged(const QString &text)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_audioSource = GetWeakSourceByQString(text);
|
||||
_entryData->ResetVolmeter();
|
||||
UpdateVolmeterSource();
|
||||
}
|
||||
|
||||
void MacroConditionAudioEdit::VolumeThresholdChanged(int vol)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_volume = vol;
|
||||
}
|
||||
|
||||
void MacroConditionAudioEdit::ConditionChanged(int cond)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_condition = static_cast<AudioCondition>(cond);
|
||||
}
|
||||
|
||||
void MacroConditionAudioEdit::UpdateEntryData()
|
||||
{
|
||||
if (!_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
_audioSources->setCurrentText(
|
||||
GetWeakSourceName(_entryData->_audioSource).c_str());
|
||||
_volume->setValue(_entryData->_volume);
|
||||
_condition->setCurrentIndex(static_cast<int>(_entryData->_condition));
|
||||
UpdateVolmeterSource();
|
||||
}
|
||||
194
src/macro-condition-counter.cpp
Normal file
194
src/macro-condition-counter.cpp
Normal file
@@ -0,0 +1,194 @@
|
||||
#include "headers/macro-condition-edit.hpp"
|
||||
#include "headers/macro-condition-counter.hpp"
|
||||
#include "headers/utility.hpp"
|
||||
#include "headers/advanced-scene-switcher.hpp"
|
||||
|
||||
const std::string MacroConditionCounter::id = "counter";
|
||||
|
||||
bool MacroConditionCounter::_registered = MacroConditionFactory::Register(
|
||||
MacroConditionCounter::id,
|
||||
{MacroConditionCounter::Create, MacroConditionCounterEdit::Create,
|
||||
"AdvSceneSwitcher.condition.counter"});
|
||||
|
||||
static std::map<CounterCondition, std::string> counterConditionTypes = {
|
||||
{CounterCondition::BELOW,
|
||||
"AdvSceneSwitcher.condition.counter.type.below"},
|
||||
{CounterCondition::ABOVE,
|
||||
"AdvSceneSwitcher.condition.counter.type.above"},
|
||||
{CounterCondition::EQUAL,
|
||||
"AdvSceneSwitcher.condition.counter.type.equal"},
|
||||
};
|
||||
|
||||
bool MacroConditionCounter::CheckCondition()
|
||||
{
|
||||
if (!_macro.get()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (_condition) {
|
||||
case CounterCondition::BELOW:
|
||||
return _macro->GetCount() < _count;
|
||||
case CounterCondition::ABOVE:
|
||||
return _macro->GetCount() > _count;
|
||||
case CounterCondition::EQUAL:
|
||||
return _macro->GetCount() == _count;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool MacroConditionCounter::Save(obs_data_t *obj)
|
||||
{
|
||||
MacroCondition::Save(obj);
|
||||
_macro.Save(obj);
|
||||
obs_data_set_int(obj, "condition", static_cast<int>(_condition));
|
||||
obs_data_set_int(obj, "count", _count);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MacroConditionCounter::Load(obs_data_t *obj)
|
||||
{
|
||||
MacroCondition::Load(obj);
|
||||
_macro.Load(obj);
|
||||
_condition = static_cast<CounterCondition>(
|
||||
obs_data_get_int(obj, "condition"));
|
||||
_count = obs_data_get_int(obj, "count");
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline void populateConditionSelection(QComboBox *list)
|
||||
{
|
||||
for (auto entry : counterConditionTypes) {
|
||||
list->addItem(obs_module_text(entry.second.c_str()));
|
||||
}
|
||||
}
|
||||
|
||||
MacroConditionCounterEdit::MacroConditionCounterEdit(
|
||||
QWidget *parent, std::shared_ptr<MacroConditionCounter> entryData)
|
||||
: QWidget(parent)
|
||||
{
|
||||
_macros = new MacroSelection(parent);
|
||||
_conditions = new QComboBox();
|
||||
_count = new QSpinBox();
|
||||
_currentCount = new QLabel();
|
||||
_resetCount = new QPushButton(
|
||||
obs_module_text("AdvSceneSwitcher.condition.counter.reset"));
|
||||
|
||||
_count->setMaximum(10000000);
|
||||
populateConditionSelection(_conditions);
|
||||
|
||||
QWidget::connect(_macros, SIGNAL(currentTextChanged(const QString &)),
|
||||
this, SLOT(MacroChanged(const QString &)));
|
||||
QWidget::connect(parent, SIGNAL(MacroRemoved(const QString &)), this,
|
||||
SLOT(MacroRemove(const QString &)));
|
||||
QWidget::connect(_conditions, SIGNAL(currentIndexChanged(int)), this,
|
||||
SLOT(ConditionChanged(int)));
|
||||
QWidget::connect(_count, SIGNAL(valueChanged(int)), this,
|
||||
SLOT(CountChanged(int)));
|
||||
QWidget::connect(_resetCount, SIGNAL(clicked()), this,
|
||||
SLOT(ResetClicked()));
|
||||
|
||||
QVBoxLayout *mainLayout = new QVBoxLayout;
|
||||
QHBoxLayout *line1Layout = new QHBoxLayout;
|
||||
QHBoxLayout *line2Layout = new QHBoxLayout;
|
||||
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
|
||||
{"{{macros}}", _macros},
|
||||
{"{{conditions}}", _conditions},
|
||||
{"{{count}}", _count},
|
||||
{"{{currentCount}}", _currentCount},
|
||||
{"{{resetCount}}", _resetCount},
|
||||
};
|
||||
placeWidgets(obs_module_text(
|
||||
"AdvSceneSwitcher.condition.counter.entry.line1"),
|
||||
line1Layout, widgetPlaceholders);
|
||||
placeWidgets(obs_module_text(
|
||||
"AdvSceneSwitcher.condition.counter.entry.line2"),
|
||||
line2Layout, widgetPlaceholders);
|
||||
mainLayout->addLayout(line1Layout);
|
||||
mainLayout->addLayout(line2Layout);
|
||||
setLayout(mainLayout);
|
||||
|
||||
_entryData = entryData;
|
||||
UpdateEntryData();
|
||||
_loading = false;
|
||||
}
|
||||
|
||||
void MacroConditionCounterEdit::UpdateEntryData()
|
||||
{
|
||||
if (!_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
_macros->SetCurrentMacro(_entryData->_macro.get());
|
||||
_conditions->setCurrentIndex(static_cast<int>(_entryData->_condition));
|
||||
_count->setValue(_entryData->_count);
|
||||
ResetTimer();
|
||||
}
|
||||
|
||||
void MacroConditionCounterEdit::MacroChanged(const QString &text)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_macro.UpdateRef(text);
|
||||
ResetTimer();
|
||||
}
|
||||
|
||||
void MacroConditionCounterEdit::CountChanged(int value)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_count = value;
|
||||
}
|
||||
|
||||
void MacroConditionCounterEdit::ConditionChanged(int cond)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_condition = static_cast<CounterCondition>(cond);
|
||||
}
|
||||
|
||||
void MacroConditionCounterEdit::MacroRemove(const QString &name)
|
||||
{
|
||||
UNUSED_PARAMETER(name);
|
||||
if (_entryData) {
|
||||
_entryData->_macro.UpdateRef();
|
||||
}
|
||||
}
|
||||
|
||||
void MacroConditionCounterEdit::ResetClicked()
|
||||
{
|
||||
if (_loading || !_entryData || !_entryData->_macro.get()) {
|
||||
return;
|
||||
}
|
||||
|
||||
_entryData->_macro->ResetCount();
|
||||
ResetTimer();
|
||||
}
|
||||
|
||||
void MacroConditionCounterEdit::UpdateCount()
|
||||
{
|
||||
if (_entryData && _entryData->_macro.get()) {
|
||||
_currentCount->setText(
|
||||
QString::number(_entryData->_macro->GetCount()));
|
||||
} else {
|
||||
_currentCount->setText("-");
|
||||
}
|
||||
}
|
||||
|
||||
void MacroConditionCounterEdit::ResetTimer()
|
||||
{
|
||||
_timer.reset(new QTimer(this));
|
||||
connect(_timer.get(), SIGNAL(timeout()), this, SLOT(UpdateCount()));
|
||||
_timer->start(1000);
|
||||
}
|
||||
268
src/macro-condition-edit.cpp
Normal file
268
src/macro-condition-edit.cpp
Normal file
@@ -0,0 +1,268 @@
|
||||
#include "headers/macro-condition-edit.hpp"
|
||||
#include "headers/macro-condition-scene.hpp"
|
||||
|
||||
std::map<std::string, MacroConditionInfo> MacroConditionFactory::_methods;
|
||||
|
||||
bool MacroConditionFactory::Register(const std::string &id,
|
||||
MacroConditionInfo info)
|
||||
{
|
||||
if (auto it = _methods.find(id); it == _methods.end()) {
|
||||
_methods[id] = info;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
std::shared_ptr<MacroCondition>
|
||||
MacroConditionFactory::Create(const std::string &id)
|
||||
{
|
||||
if (auto it = _methods.find(id); it != _methods.end()) {
|
||||
return it->second._createFunc();
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
QWidget *
|
||||
MacroConditionFactory::CreateWidget(const std::string &id, QWidget *parent,
|
||||
std::shared_ptr<MacroCondition> cond)
|
||||
{
|
||||
if (auto it = _methods.find(id); it != _methods.end()) {
|
||||
return it->second._createWidgetFunc(parent, cond);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::string MacroConditionFactory::GetConditionName(const std::string &id)
|
||||
{
|
||||
if (auto it = _methods.find(id); it != _methods.end()) {
|
||||
return it->second._name;
|
||||
}
|
||||
return "unknown condition";
|
||||
}
|
||||
|
||||
std::string MacroConditionFactory::GetIdByName(const QString &name)
|
||||
{
|
||||
for (auto it : _methods) {
|
||||
if (name == obs_module_text(it.second._name.c_str())) {
|
||||
return it.first;
|
||||
}
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
bool MacroConditionFactory::UsesDurationConstraint(const std::string &id)
|
||||
{
|
||||
if (auto it = _methods.find(id); it != _methods.end()) {
|
||||
return it->second._useDurationConstraint;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline void populateLogicSelection(QComboBox *list, bool root = false)
|
||||
{
|
||||
if (root) {
|
||||
for (auto entry : MacroCondition::logicTypes) {
|
||||
if (static_cast<int>(entry.first) < logic_root_offset) {
|
||||
list->addItem(obs_module_text(
|
||||
entry.second._name.c_str()));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (auto entry : MacroCondition::logicTypes) {
|
||||
if (static_cast<int>(entry.first) >=
|
||||
logic_root_offset) {
|
||||
list->addItem(obs_module_text(
|
||||
entry.second._name.c_str()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static inline void populateConditionSelection(QComboBox *list)
|
||||
{
|
||||
for (auto entry : MacroConditionFactory::GetConditionTypes()) {
|
||||
list->addItem(obs_module_text(entry.second._name.c_str()));
|
||||
}
|
||||
}
|
||||
|
||||
MacroConditionEdit::MacroConditionEdit(
|
||||
QWidget *parent, std::shared_ptr<MacroCondition> *entryData,
|
||||
const std::string &id, bool root, bool startCollapsed)
|
||||
: QWidget(parent)
|
||||
{
|
||||
_logicSelection = new QComboBox();
|
||||
_conditionSelection = new QComboBox();
|
||||
_section = new Section(300);
|
||||
_dur = new DurationConstraintEdit();
|
||||
|
||||
QWidget::connect(_logicSelection, SIGNAL(currentIndexChanged(int)),
|
||||
this, SLOT(LogicSelectionChanged(int)));
|
||||
QWidget::connect(_conditionSelection,
|
||||
SIGNAL(currentTextChanged(const QString &)), this,
|
||||
SLOT(ConditionSelectionChanged(const QString &)));
|
||||
QWidget::connect(_dur, SIGNAL(DurationChanged(double)), this,
|
||||
SLOT(DurationChanged(double)));
|
||||
QWidget::connect(_dur, SIGNAL(UnitChanged(DurationUnit)), this,
|
||||
SLOT(DurationUnitChanged(DurationUnit)));
|
||||
QWidget::connect(_dur, SIGNAL(ConditionChanged(DurationCondition)),
|
||||
this,
|
||||
SLOT(DurationConditionChanged(DurationCondition)));
|
||||
|
||||
populateLogicSelection(_logicSelection, root);
|
||||
populateConditionSelection(_conditionSelection);
|
||||
|
||||
_section->AddHeaderWidget(_logicSelection);
|
||||
_section->AddHeaderWidget(_conditionSelection);
|
||||
_section->AddHeaderWidget(_dur);
|
||||
|
||||
QVBoxLayout *mainLayout = new QVBoxLayout;
|
||||
mainLayout->addWidget(_section);
|
||||
setLayout(mainLayout);
|
||||
|
||||
_entryData = entryData;
|
||||
_isRoot = root;
|
||||
UpdateEntryData(id, startCollapsed);
|
||||
_loading = false;
|
||||
}
|
||||
|
||||
void MacroConditionEdit::LogicSelectionChanged(int idx)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
LogicType type;
|
||||
if (IsRootNode()) {
|
||||
type = static_cast<LogicType>(idx);
|
||||
} else {
|
||||
type = static_cast<LogicType>(idx + logic_root_offset);
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
(*_entryData)->SetLogicType(type);
|
||||
}
|
||||
|
||||
bool MacroConditionEdit::IsRootNode()
|
||||
{
|
||||
return _isRoot;
|
||||
}
|
||||
|
||||
void MacroConditionEdit::UpdateEntryData(const std::string &id, bool collapse)
|
||||
{
|
||||
_conditionSelection->setCurrentText(obs_module_text(
|
||||
MacroConditionFactory::GetConditionName(id).c_str()));
|
||||
auto widget =
|
||||
MacroConditionFactory::CreateWidget(id, window(), *_entryData);
|
||||
auto logic = (*_entryData)->GetLogicType();
|
||||
if (IsRootNode()) {
|
||||
_logicSelection->setCurrentIndex(static_cast<int>(logic));
|
||||
} else {
|
||||
_logicSelection->setCurrentIndex(static_cast<int>(logic) -
|
||||
logic_root_offset);
|
||||
}
|
||||
_section->SetContent(widget, collapse);
|
||||
|
||||
_dur->setVisible(MacroConditionFactory::UsesDurationConstraint(id));
|
||||
auto constraint = (*_entryData)->GetDurationConstraint();
|
||||
_dur->SetValue(constraint);
|
||||
}
|
||||
|
||||
void MacroConditionEdit::ConditionSelectionChanged(const QString &text)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::string id = MacroConditionFactory::GetIdByName(text);
|
||||
|
||||
auto temp = DurationConstraint();
|
||||
_dur->SetValue(temp);
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
auto logic = (*_entryData)->GetLogicType();
|
||||
_entryData->reset();
|
||||
*_entryData = MacroConditionFactory::Create(id);
|
||||
(*_entryData)->SetLogicType(logic);
|
||||
auto widget =
|
||||
MacroConditionFactory::CreateWidget(id, window(), *_entryData);
|
||||
_section->SetContent(widget, false);
|
||||
_dur->setVisible(MacroConditionFactory::UsesDurationConstraint(id));
|
||||
}
|
||||
|
||||
void MacroConditionEdit::DurationChanged(double seconds)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
(*_entryData)->SetDuration(seconds);
|
||||
}
|
||||
|
||||
void MacroConditionEdit::DurationConditionChanged(DurationCondition cond)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
(*_entryData)->SetDurationCondition(cond);
|
||||
}
|
||||
|
||||
void MacroConditionEdit::DurationUnitChanged(DurationUnit unit)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
(*_entryData)->SetDurationUnit(unit);
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::on_conditionAdd_clicked()
|
||||
{
|
||||
auto macro = getSelectedMacro();
|
||||
if (!macro) {
|
||||
return;
|
||||
}
|
||||
MacroConditionScene temp;
|
||||
std::string id = temp.GetId();
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
bool root = macro->Conditions().size() == 0;
|
||||
macro->Conditions().emplace_back(MacroConditionFactory::Create(id));
|
||||
auto logic = root ? LogicType::ROOT_NONE : LogicType::NONE;
|
||||
macro->Conditions().back()->SetLogicType(logic);
|
||||
auto newEntry = new MacroConditionEdit(
|
||||
this, ¯o->Conditions().back(), id, root, false);
|
||||
ui->macroEditConditionLayout->addWidget(newEntry);
|
||||
ui->macroEditConditionHelp->setVisible(false);
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::on_conditionRemove_clicked()
|
||||
{
|
||||
auto macro = getSelectedMacro();
|
||||
if (!macro) {
|
||||
return;
|
||||
}
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
if (macro->Conditions().empty()) {
|
||||
return;
|
||||
}
|
||||
macro->Conditions().pop_back();
|
||||
|
||||
int count = ui->macroEditConditionLayout->count();
|
||||
auto item = ui->macroEditConditionLayout->takeAt(count - 1);
|
||||
|
||||
if (item) {
|
||||
auto widget = item->widget();
|
||||
if (widget) {
|
||||
widget->setVisible(false);
|
||||
}
|
||||
delete item;
|
||||
}
|
||||
|
||||
if (count == 1) {
|
||||
ui->macroEditConditionHelp->setVisible(true);
|
||||
}
|
||||
}
|
||||
293
src/macro-condition-file.cpp
Normal file
293
src/macro-condition-file.cpp
Normal file
@@ -0,0 +1,293 @@
|
||||
#include "headers/macro-condition-edit.hpp"
|
||||
#include "headers/macro-condition-file.hpp"
|
||||
#include "headers/utility.hpp"
|
||||
#include "headers/advanced-scene-switcher.hpp"
|
||||
#include "headers/curl-helper.hpp"
|
||||
|
||||
#include <QTextStream>
|
||||
#include <QFileDialog>
|
||||
|
||||
const std::string MacroConditionFile::id = "file";
|
||||
|
||||
bool MacroConditionFile::_registered = MacroConditionFactory::Register(
|
||||
MacroConditionFile::id,
|
||||
{MacroConditionFile::Create, MacroConditionFileEdit::Create,
|
||||
"AdvSceneSwitcher.condition.file"});
|
||||
|
||||
static std::hash<std::string> strHash;
|
||||
|
||||
static size_t WriteCallback(void *contents, size_t size, size_t nmemb,
|
||||
void *userp)
|
||||
{
|
||||
((std::string *)userp)->append((char *)contents, size * nmemb);
|
||||
return size * nmemb;
|
||||
}
|
||||
|
||||
static std::string getRemoteData(std::string &url)
|
||||
{
|
||||
std::string readBuffer;
|
||||
|
||||
if (switcher->curl && f_curl_setopt && f_curl_perform) {
|
||||
f_curl_setopt(switcher->curl, CURLOPT_URL, url.c_str());
|
||||
f_curl_setopt(switcher->curl, CURLOPT_WRITEFUNCTION,
|
||||
WriteCallback);
|
||||
f_curl_setopt(switcher->curl, CURLOPT_WRITEDATA, &readBuffer);
|
||||
f_curl_perform(switcher->curl);
|
||||
}
|
||||
return readBuffer;
|
||||
}
|
||||
|
||||
bool MacroConditionFile::matchFileContent(QString &filedata)
|
||||
{
|
||||
if (_onlyMatchIfChanged) {
|
||||
size_t newHash = strHash(filedata.toUtf8().constData());
|
||||
if (newHash == _lastHash) {
|
||||
return false;
|
||||
}
|
||||
_lastHash = newHash;
|
||||
}
|
||||
|
||||
if (_useRegex) {
|
||||
QRegExp rx(QString::fromStdString(_text));
|
||||
return rx.exactMatch(filedata);
|
||||
}
|
||||
|
||||
QString text = QString::fromStdString(_text);
|
||||
return compareIgnoringLineEnding(text, filedata);
|
||||
}
|
||||
|
||||
bool MacroConditionFile::checkRemoteFileContent()
|
||||
{
|
||||
std::string data = getRemoteData(_file);
|
||||
QString qdata = QString::fromStdString(data);
|
||||
return matchFileContent(qdata);
|
||||
}
|
||||
|
||||
bool MacroConditionFile::checkLocalFileContent()
|
||||
{
|
||||
QString t = QString::fromStdString(_text);
|
||||
QFile file(QString::fromStdString(_file));
|
||||
if (_file.empty() || !file.open(QIODevice::ReadOnly)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (_useTime) {
|
||||
QDateTime newLastMod = QFileInfo(file).lastModified();
|
||||
if (_lastMod == newLastMod) {
|
||||
return false;
|
||||
}
|
||||
_lastMod = newLastMod;
|
||||
}
|
||||
|
||||
QString filedata = QTextStream(&file).readAll();
|
||||
bool match = matchFileContent(filedata);
|
||||
|
||||
file.close();
|
||||
return match;
|
||||
}
|
||||
|
||||
bool MacroConditionFile::CheckCondition()
|
||||
{
|
||||
if (_fileType == FileType::REMOTE) {
|
||||
return checkRemoteFileContent();
|
||||
} else {
|
||||
return checkLocalFileContent();
|
||||
}
|
||||
}
|
||||
|
||||
bool MacroConditionFile::Save(obs_data_t *obj)
|
||||
{
|
||||
MacroCondition::Save(obj);
|
||||
obs_data_set_string(obj, "file", _file.c_str());
|
||||
obs_data_set_string(obj, "text", _text.c_str());
|
||||
obs_data_set_int(obj, "fileType", static_cast<int>(_fileType));
|
||||
obs_data_set_bool(obj, "useRegex", _useRegex);
|
||||
obs_data_set_bool(obj, "useTime", _useTime);
|
||||
obs_data_set_bool(obj, "onlyMatchIfChanged", _onlyMatchIfChanged);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MacroConditionFile::Load(obs_data_t *obj)
|
||||
{
|
||||
MacroCondition::Load(obj);
|
||||
_file = obs_data_get_string(obj, "file");
|
||||
_text = obs_data_get_string(obj, "text");
|
||||
_fileType = static_cast<FileType>(obs_data_get_int(obj, "fileType"));
|
||||
_useRegex = obs_data_get_bool(obj, "useRegex");
|
||||
_useTime = obs_data_get_bool(obj, "useTime");
|
||||
_onlyMatchIfChanged = obs_data_get_bool(obj, "onlyMatchIfChanged");
|
||||
return true;
|
||||
}
|
||||
|
||||
MacroConditionFileEdit::MacroConditionFileEdit(
|
||||
QWidget *parent, std::shared_ptr<MacroConditionFile> entryData)
|
||||
: QWidget(parent)
|
||||
{
|
||||
_fileType = new QComboBox();
|
||||
_filePath = new QLineEdit();
|
||||
_browseButton =
|
||||
new QPushButton(obs_module_text("AdvSceneSwitcher.browse"));
|
||||
_browseButton->setStyleSheet("border:1px solid gray;");
|
||||
_matchText = new QPlainTextEdit();
|
||||
_useRegex = new QCheckBox(
|
||||
obs_module_text("AdvSceneSwitcher.fileTab.useRegExp"));
|
||||
_checkModificationDate = new QCheckBox(obs_module_text(
|
||||
"AdvSceneSwitcher.fileTab.checkfileContentTime"));
|
||||
_checkFileContent = new QCheckBox(
|
||||
obs_module_text("AdvSceneSwitcher.fileTab.checkfileContent"));
|
||||
|
||||
QWidget::connect(_fileType, SIGNAL(currentIndexChanged(int)), this,
|
||||
SLOT(FileTypeChanged(int)));
|
||||
QWidget::connect(_filePath, SIGNAL(editingFinished()), this,
|
||||
SLOT(FilePathChanged()));
|
||||
QWidget::connect(_browseButton, SIGNAL(clicked()), this,
|
||||
SLOT(BrowseButtonClicked()));
|
||||
QWidget::connect(_matchText, SIGNAL(textChanged()), this,
|
||||
SLOT(MatchTextChanged()));
|
||||
QWidget::connect(_useRegex, SIGNAL(stateChanged(int)), this,
|
||||
SLOT(UseRegexChanged(int)));
|
||||
QWidget::connect(_checkModificationDate, SIGNAL(stateChanged(int)),
|
||||
this, SLOT(CheckModificationDateChanged(int)));
|
||||
QWidget::connect(_checkFileContent, SIGNAL(stateChanged(int)), this,
|
||||
SLOT(OnlyMatchIfChangedChanged(int)));
|
||||
|
||||
_fileType->addItem(obs_module_text("AdvSceneSwitcher.fileTab.local"));
|
||||
_fileType->addItem(obs_module_text("AdvSceneSwitcher.fileTab.remote"));
|
||||
|
||||
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
|
||||
{"{{fileType}}", _fileType},
|
||||
{"{{filePath}}", _filePath},
|
||||
{"{{browseButton}}", _browseButton},
|
||||
{"{{matchText}}", _matchText},
|
||||
{"{{useRegex}}", _useRegex},
|
||||
{"{{checkModificationDate}}", _checkModificationDate},
|
||||
{"{{checkFileContent}}", _checkFileContent},
|
||||
};
|
||||
|
||||
QVBoxLayout *mainLayout = new QVBoxLayout;
|
||||
QHBoxLayout *line1Layout = new QHBoxLayout;
|
||||
QHBoxLayout *line2Layout = new QHBoxLayout;
|
||||
QHBoxLayout *line3Layout = new QHBoxLayout;
|
||||
placeWidgets(
|
||||
obs_module_text("AdvSceneSwitcher.condition.file.entry.line1"),
|
||||
line1Layout, widgetPlaceholders);
|
||||
placeWidgets(
|
||||
obs_module_text("AdvSceneSwitcher.condition.file.entry.line2"),
|
||||
line2Layout, widgetPlaceholders, false);
|
||||
placeWidgets(
|
||||
obs_module_text("AdvSceneSwitcher.condition.file.entry.line3"),
|
||||
line3Layout, widgetPlaceholders);
|
||||
mainLayout->addLayout(line1Layout);
|
||||
mainLayout->addLayout(line2Layout);
|
||||
mainLayout->addLayout(line3Layout);
|
||||
|
||||
setLayout(mainLayout);
|
||||
|
||||
_entryData = entryData;
|
||||
UpdateEntryData();
|
||||
_loading = false;
|
||||
}
|
||||
|
||||
void MacroConditionFileEdit::UpdateEntryData()
|
||||
{
|
||||
if (!_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
_fileType->setCurrentIndex(static_cast<int>(_entryData->_fileType));
|
||||
|
||||
_filePath->setText(QString::fromStdString(_entryData->_file));
|
||||
_matchText->setPlainText(QString::fromStdString(_entryData->_text));
|
||||
_useRegex->setChecked(_entryData->_useRegex);
|
||||
_checkModificationDate->setChecked(_entryData->_useTime);
|
||||
_checkFileContent->setChecked(_entryData->_onlyMatchIfChanged);
|
||||
}
|
||||
|
||||
void MacroConditionFileEdit::FileTypeChanged(int index)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
FileType type = static_cast<FileType>(index);
|
||||
|
||||
if (type == FileType::LOCAL) {
|
||||
_browseButton->setDisabled(false);
|
||||
_checkModificationDate->setDisabled(false);
|
||||
} else {
|
||||
_browseButton->setDisabled(true);
|
||||
_checkModificationDate->setDisabled(true);
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_fileType = type;
|
||||
}
|
||||
|
||||
void MacroConditionFileEdit::FilePathChanged()
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_file = _filePath->text().toUtf8().constData();
|
||||
}
|
||||
|
||||
void MacroConditionFileEdit::BrowseButtonClicked()
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
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 MacroConditionFileEdit::MatchTextChanged()
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_text = _matchText->toPlainText().toUtf8().constData();
|
||||
}
|
||||
|
||||
void MacroConditionFileEdit::UseRegexChanged(int state)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_useRegex = state;
|
||||
}
|
||||
|
||||
void MacroConditionFileEdit::CheckModificationDateChanged(int state)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_useTime = state;
|
||||
}
|
||||
|
||||
void MacroConditionFileEdit::OnlyMatchIfChangedChanged(int state)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_onlyMatchIfChanged = state;
|
||||
}
|
||||
83
src/macro-condition-idle.cpp
Normal file
83
src/macro-condition-idle.cpp
Normal file
@@ -0,0 +1,83 @@
|
||||
#include "headers/macro-condition-edit.hpp"
|
||||
#include "headers/macro-condition-idle.hpp"
|
||||
#include "headers/utility.hpp"
|
||||
#include "headers/advanced-scene-switcher.hpp"
|
||||
|
||||
const std::string MacroConditionIdle::id = "idle";
|
||||
|
||||
bool MacroConditionIdle::_registered = MacroConditionFactory::Register(
|
||||
MacroConditionIdle::id,
|
||||
{MacroConditionIdle::Create, MacroConditionIdleEdit::Create,
|
||||
"AdvSceneSwitcher.condition.idle", false});
|
||||
|
||||
bool MacroConditionIdle::CheckCondition()
|
||||
{
|
||||
return secondsSinceLastInput() >= _duration.seconds;
|
||||
}
|
||||
|
||||
bool MacroConditionIdle::Save(obs_data_t *obj)
|
||||
{
|
||||
MacroCondition::Save(obj);
|
||||
_duration.Save(obj);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MacroConditionIdle::Load(obs_data_t *obj)
|
||||
{
|
||||
MacroCondition::Load(obj);
|
||||
_duration.Load(obj);
|
||||
return true;
|
||||
}
|
||||
|
||||
MacroConditionIdleEdit::MacroConditionIdleEdit(
|
||||
QWidget *parent, std::shared_ptr<MacroConditionIdle> entryData)
|
||||
: QWidget(parent)
|
||||
{
|
||||
_duration = new DurationSelection();
|
||||
|
||||
QWidget::connect(_duration, SIGNAL(DurationChanged(double)), this,
|
||||
SLOT(DurationChanged(double)));
|
||||
QWidget::connect(_duration, SIGNAL(UnitChanged(DurationUnit)), this,
|
||||
SLOT(DurationUnitChanged(DurationUnit)));
|
||||
|
||||
QHBoxLayout *mainLayout = new QHBoxLayout;
|
||||
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
|
||||
{"{{duration}}", _duration},
|
||||
};
|
||||
placeWidgets(obs_module_text("AdvSceneSwitcher.condition.idle.entry"),
|
||||
mainLayout, widgetPlaceholders);
|
||||
setLayout(mainLayout);
|
||||
|
||||
_entryData = entryData;
|
||||
UpdateEntryData();
|
||||
_loading = false;
|
||||
}
|
||||
|
||||
void MacroConditionIdleEdit::DurationChanged(double seconds)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_duration.seconds = seconds;
|
||||
}
|
||||
|
||||
void MacroConditionIdleEdit::DurationUnitChanged(DurationUnit unit)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_duration.displayUnit = unit;
|
||||
}
|
||||
|
||||
void MacroConditionIdleEdit::UpdateEntryData()
|
||||
{
|
||||
if (!_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
_duration->SetDuration(_entryData->_duration);
|
||||
}
|
||||
88
src/macro-condition-interval.cpp
Normal file
88
src/macro-condition-interval.cpp
Normal file
@@ -0,0 +1,88 @@
|
||||
#include "headers/macro-condition-edit.hpp"
|
||||
#include "headers/macro-condition-interval.hpp"
|
||||
#include "headers/utility.hpp"
|
||||
#include "headers/advanced-scene-switcher.hpp"
|
||||
|
||||
const std::string MacroConditionInterval::id = "interval";
|
||||
|
||||
bool MacroConditionInterval::_registered = MacroConditionFactory::Register(
|
||||
MacroConditionInterval::id,
|
||||
{MacroConditionInterval::Create, MacroConditionIntervalEdit::Create,
|
||||
"AdvSceneSwitcher.condition.interval", false});
|
||||
|
||||
bool MacroConditionInterval::CheckCondition()
|
||||
{
|
||||
if (_duration.DurationReached()) {
|
||||
_duration.Reset();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool MacroConditionInterval::Save(obs_data_t *obj)
|
||||
{
|
||||
MacroCondition::Save(obj);
|
||||
_duration.Save(obj);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MacroConditionInterval::Load(obs_data_t *obj)
|
||||
{
|
||||
MacroCondition::Load(obj);
|
||||
_duration.Load(obj);
|
||||
return true;
|
||||
}
|
||||
|
||||
MacroConditionIntervalEdit::MacroConditionIntervalEdit(
|
||||
QWidget *parent, std::shared_ptr<MacroConditionInterval> entryData)
|
||||
: QWidget(parent)
|
||||
{
|
||||
_duration = new DurationSelection();
|
||||
|
||||
QWidget::connect(_duration, SIGNAL(DurationChanged(double)), this,
|
||||
SLOT(DurationChanged(double)));
|
||||
QWidget::connect(_duration, SIGNAL(UnitChanged(DurationUnit)), this,
|
||||
SLOT(DurationUnitChanged(DurationUnit)));
|
||||
|
||||
QHBoxLayout *mainLayout = new QHBoxLayout;
|
||||
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
|
||||
{"{{duration}}", _duration},
|
||||
};
|
||||
placeWidgets(
|
||||
obs_module_text("AdvSceneSwitcher.condition.interval.entry"),
|
||||
mainLayout, widgetPlaceholders);
|
||||
setLayout(mainLayout);
|
||||
|
||||
_entryData = entryData;
|
||||
UpdateEntryData();
|
||||
_loading = false;
|
||||
}
|
||||
|
||||
void MacroConditionIntervalEdit::DurationChanged(double seconds)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_duration.seconds = seconds;
|
||||
}
|
||||
|
||||
void MacroConditionIntervalEdit::DurationUnitChanged(DurationUnit unit)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_duration.displayUnit = unit;
|
||||
}
|
||||
|
||||
void MacroConditionIntervalEdit::UpdateEntryData()
|
||||
{
|
||||
if (!_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
_duration->SetDuration(_entryData->_duration);
|
||||
}
|
||||
376
src/macro-condition-media.cpp
Normal file
376
src/macro-condition-media.cpp
Normal file
@@ -0,0 +1,376 @@
|
||||
#include "headers/macro-condition-edit.hpp"
|
||||
#include "headers/macro-condition-media.hpp"
|
||||
#include "headers/utility.hpp"
|
||||
#include "headers/advanced-scene-switcher.hpp"
|
||||
|
||||
const std::string MacroConditionMedia::id = "media";
|
||||
|
||||
bool MacroConditionMedia::_registered = MacroConditionFactory::Register(
|
||||
MacroConditionMedia::id,
|
||||
{MacroConditionMedia::Create, MacroConditionMediaEdit::Create,
|
||||
"AdvSceneSwitcher.condition.media"});
|
||||
|
||||
static std::map<MediaTimeRestriction, std::string> mediaTimeRestrictions = {
|
||||
{MediaTimeRestriction::TIME_RESTRICTION_NONE,
|
||||
"AdvSceneSwitcher.mediaTab.timeRestriction.none"},
|
||||
{MediaTimeRestriction::TIME_RESTRICTION_SHORTER,
|
||||
"AdvSceneSwitcher.mediaTab.timeRestriction.shorter"},
|
||||
{MediaTimeRestriction::TIME_RESTRICTION_LONGER,
|
||||
"AdvSceneSwitcher.mediaTab.timeRestriction.longer"},
|
||||
{MediaTimeRestriction::TIME_RESTRICTION_REMAINING_SHORTER,
|
||||
"AdvSceneSwitcher.mediaTab.timeRestriction.remainShorter"},
|
||||
{MediaTimeRestriction::TIME_RESTRICTION_REMAINING_LONGER,
|
||||
"AdvSceneSwitcher.mediaTab.timeRestriction.remainLonger"},
|
||||
};
|
||||
|
||||
static std::map<MediaState, std::string> mediaStates = {
|
||||
{MediaState::OBS_MEDIA_STATE_NONE,
|
||||
"AdvSceneSwitcher.mediaTab.states.none"},
|
||||
{MediaState::OBS_MEDIA_STATE_PLAYING,
|
||||
"AdvSceneSwitcher.mediaTab.states.playing"},
|
||||
{MediaState::OBS_MEDIA_STATE_OPENING,
|
||||
"AdvSceneSwitcher.mediaTab.states.opening"},
|
||||
{MediaState::OBS_MEDIA_STATE_BUFFERING,
|
||||
"AdvSceneSwitcher.mediaTab.states.buffering"},
|
||||
{MediaState::OBS_MEDIA_STATE_PAUSED,
|
||||
"AdvSceneSwitcher.mediaTab.states.paused"},
|
||||
{MediaState::OBS_MEDIA_STATE_STOPPED,
|
||||
"AdvSceneSwitcher.mediaTab.states.stopped"},
|
||||
{MediaState::OBS_MEDIA_STATE_ENDED,
|
||||
"AdvSceneSwitcher.mediaTab.states.ended"},
|
||||
{MediaState::OBS_MEDIA_STATE_ERROR,
|
||||
"AdvSceneSwitcher.mediaTab.states.error"},
|
||||
{MediaState::PLAYED_TO_END,
|
||||
"AdvSceneSwitcher.mediaTab.states.playedToEnd"},
|
||||
{MediaState::ANY, "AdvSceneSwitcher.mediaTab.states.any"},
|
||||
};
|
||||
|
||||
MacroConditionMedia::~MacroConditionMedia()
|
||||
{
|
||||
obs_source_t *mediasource = obs_weak_source_get_source(_source);
|
||||
signal_handler_t *sh = obs_source_get_signal_handler(mediasource);
|
||||
signal_handler_disconnect(sh, "media_stopped", MediaStopped, this);
|
||||
signal_handler_disconnect(sh, "media_ended", MediaEnded, this);
|
||||
obs_source_release(mediasource);
|
||||
}
|
||||
|
||||
bool matchTime(const int64_t currentTime, const int64_t duration,
|
||||
const MediaTimeRestriction restriction, const int64_t time)
|
||||
{
|
||||
bool matchedTimeNone =
|
||||
(restriction == MediaTimeRestriction::TIME_RESTRICTION_NONE);
|
||||
bool matchedTimeLonger =
|
||||
(restriction ==
|
||||
MediaTimeRestriction::TIME_RESTRICTION_LONGER) &&
|
||||
(currentTime > time);
|
||||
bool matchedTimeShorter =
|
||||
(restriction ==
|
||||
MediaTimeRestriction::TIME_RESTRICTION_SHORTER) &&
|
||||
(currentTime < time);
|
||||
bool matchedTimeRemainLonger =
|
||||
(restriction ==
|
||||
MediaTimeRestriction::TIME_RESTRICTION_REMAINING_LONGER) &&
|
||||
(duration > currentTime && duration - currentTime > time);
|
||||
bool matchedTimeRemainShorter =
|
||||
(restriction ==
|
||||
MediaTimeRestriction::TIME_RESTRICTION_REMAINING_SHORTER) &&
|
||||
(duration > currentTime && duration - currentTime < time);
|
||||
|
||||
return matchedTimeNone || matchedTimeLonger || matchedTimeShorter ||
|
||||
matchedTimeRemainLonger || matchedTimeRemainShorter;
|
||||
}
|
||||
|
||||
bool MacroConditionMedia::CheckCondition()
|
||||
{
|
||||
if (!_source) {
|
||||
return false;
|
||||
}
|
||||
bool match = false;
|
||||
|
||||
obs_source_t *source = obs_weak_source_get_source(_source);
|
||||
auto duration = obs_source_media_get_duration(source);
|
||||
auto time = obs_source_media_get_time(source);
|
||||
obs_media_state currentState = obs_source_media_get_state(source);
|
||||
obs_source_release(source);
|
||||
|
||||
// To be able to compare to obs_media_state more easily
|
||||
int expectedState = static_cast<int>(_state);
|
||||
|
||||
bool matchedStopped = expectedState == OBS_MEDIA_STATE_STOPPED &&
|
||||
_stopped;
|
||||
|
||||
// The signal for the state ended is intentionally not used here
|
||||
// so matchedEnded can be used to specify the end of a VLC playlist
|
||||
// by two consequtive matches of OBS_MEDIA_STATE_ENDED
|
||||
//
|
||||
// This was done to reduce the likelyhood of interpreting a single
|
||||
// OBS_MEDIA_STATE_ENDED caught by obs_source_media_get_state()
|
||||
// as the end of the playlist of the VLC source, while actually being
|
||||
// in the middle of switching to the next item of the playlist
|
||||
//
|
||||
// If there is a separate obs_media_sate in the future for the
|
||||
// "end of playlist reached" signal the line below can be used
|
||||
// and an additional check for this new singal can be introduced
|
||||
//
|
||||
// bool matchedEnded = _state == OBS_MEDIA_STATE_ENDED && _ended;
|
||||
|
||||
bool ended = false;
|
||||
|
||||
if (currentState == OBS_MEDIA_STATE_ENDED) {
|
||||
ended = _previousStateEnded;
|
||||
_previousStateEnded = true;
|
||||
} else {
|
||||
_previousStateEnded = false;
|
||||
}
|
||||
|
||||
bool matchedEnded = ended && (expectedState == OBS_MEDIA_STATE_ENDED);
|
||||
|
||||
// match if playedToEnd was true in last interval
|
||||
// and playback is currently ended
|
||||
bool matchedPlayedToEnd = _state == MediaState::PLAYED_TO_END &&
|
||||
_playedToEnd && ended;
|
||||
|
||||
// interval * 2 to make sure not to miss any state changes
|
||||
// which happened during check of the conditions
|
||||
_playedToEnd = _playedToEnd ||
|
||||
(duration - time <= switcher->interval * 2);
|
||||
|
||||
// reset for next check
|
||||
if (ended) {
|
||||
_playedToEnd = false;
|
||||
}
|
||||
_stopped = false;
|
||||
_ended = false;
|
||||
|
||||
bool matchedState =
|
||||
(currentState == expectedState || _state == MediaState::ANY) ||
|
||||
matchedStopped || matchedEnded || matchedPlayedToEnd;
|
||||
|
||||
bool matchedTime =
|
||||
matchTime(time, duration, _restriction, _time.seconds * 1000);
|
||||
bool matched = matchedState && matchedTime;
|
||||
|
||||
if (matched && !_alreadyMatched) {
|
||||
match = true;
|
||||
}
|
||||
|
||||
_alreadyMatched = matched;
|
||||
|
||||
return match;
|
||||
}
|
||||
|
||||
bool MacroConditionMedia::Save(obs_data_t *obj)
|
||||
{
|
||||
MacroCondition::Save(obj);
|
||||
obs_data_set_string(obj, "source", GetWeakSourceName(_source).c_str());
|
||||
|
||||
obs_data_set_int(obj, "state", static_cast<int>(_state));
|
||||
obs_data_set_int(obj, "restriction", static_cast<int>(_restriction));
|
||||
_time.Save(obj);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MacroConditionMedia::Load(obs_data_t *obj)
|
||||
{
|
||||
MacroCondition::Load(obj);
|
||||
const char *sourceName = obs_data_get_string(obj, "source");
|
||||
_source = GetWeakSourceByName(sourceName);
|
||||
|
||||
_state = static_cast<MediaState>(obs_data_get_int(obj, "state"));
|
||||
_restriction = static_cast<MediaTimeRestriction>(
|
||||
obs_data_get_int(obj, "restriction"));
|
||||
_time.Load(obj);
|
||||
|
||||
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);
|
||||
return true;
|
||||
}
|
||||
|
||||
void MacroConditionMedia::ClearSignalHandler()
|
||||
{
|
||||
obs_source_t *mediasource = obs_weak_source_get_source(_source);
|
||||
signal_handler_t *sh = obs_source_get_signal_handler(mediasource);
|
||||
signal_handler_disconnect(sh, "media_stopped", MediaStopped, this);
|
||||
signal_handler_disconnect(sh, "media_ended", MediaEnded, this);
|
||||
obs_source_release(mediasource);
|
||||
}
|
||||
|
||||
void MacroConditionMedia::ResetSignalHandler()
|
||||
{
|
||||
obs_source_t *mediasource = obs_weak_source_get_source(_source);
|
||||
signal_handler_t *sh = obs_source_get_signal_handler(mediasource);
|
||||
signal_handler_disconnect(sh, "media_stopped", MediaStopped, this);
|
||||
signal_handler_disconnect(sh, "media_ended", MediaEnded, this);
|
||||
signal_handler_connect(sh, "media_stopped", MediaStopped, this);
|
||||
signal_handler_connect(sh, "media_ended", MediaEnded, this);
|
||||
obs_source_release(mediasource);
|
||||
}
|
||||
|
||||
void MacroConditionMedia::MediaStopped(void *data, calldata_t *)
|
||||
{
|
||||
MacroConditionMedia *media = static_cast<MacroConditionMedia *>(data);
|
||||
media->_stopped = true;
|
||||
}
|
||||
|
||||
void MacroConditionMedia::MediaEnded(void *data, calldata_t *)
|
||||
{
|
||||
MacroConditionMedia *media = static_cast<MacroConditionMedia *>(data);
|
||||
media->_ended = true;
|
||||
}
|
||||
|
||||
static void populateMediaTimeRestrictions(QComboBox *list)
|
||||
{
|
||||
for (auto entry : mediaTimeRestrictions) {
|
||||
list->addItem(obs_module_text(entry.second.c_str()));
|
||||
}
|
||||
}
|
||||
|
||||
static void populateMediaStates(QComboBox *list)
|
||||
{
|
||||
for (auto entry : mediaStates) {
|
||||
list->addItem(obs_module_text(entry.second.c_str()));
|
||||
}
|
||||
}
|
||||
|
||||
MacroConditionMediaEdit::MacroConditionMediaEdit(
|
||||
QWidget *parent, std::shared_ptr<MacroConditionMedia> entryData)
|
||||
: QWidget(parent)
|
||||
{
|
||||
_mediaSources = new QComboBox();
|
||||
_states = new QComboBox();
|
||||
_timeRestrictions = new QComboBox();
|
||||
_time = new DurationSelection();
|
||||
|
||||
QWidget::connect(_mediaSources,
|
||||
SIGNAL(currentTextChanged(const QString &)), this,
|
||||
SLOT(SourceChanged(const QString &)));
|
||||
QWidget::connect(_states, SIGNAL(currentIndexChanged(int)), this,
|
||||
SLOT(StateChanged(int)));
|
||||
QWidget::connect(_timeRestrictions, SIGNAL(currentIndexChanged(int)),
|
||||
this, SLOT(TimeRestrictionChanged(int)));
|
||||
QWidget::connect(_time, SIGNAL(DurationChanged(double)), this,
|
||||
SLOT(TimeChanged(double)));
|
||||
QWidget::connect(_time, SIGNAL(UnitChanged(DurationUnit)), this,
|
||||
SLOT(TimeUnitChanged(DurationUnit)));
|
||||
|
||||
populateMediaSelection(_mediaSources);
|
||||
populateMediaStates(_states);
|
||||
populateMediaTimeRestrictions(_timeRestrictions);
|
||||
|
||||
QHBoxLayout *mainLayout = new QHBoxLayout;
|
||||
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
|
||||
{"{{mediaSources}}", _mediaSources},
|
||||
{"{{states}}", _states},
|
||||
{"{{timeRestrictions}}", _timeRestrictions},
|
||||
{"{{time}}", _time},
|
||||
};
|
||||
placeWidgets(obs_module_text("AdvSceneSwitcher.condition.media.entry"),
|
||||
mainLayout, widgetPlaceholders);
|
||||
setLayout(mainLayout);
|
||||
|
||||
_entryData = entryData;
|
||||
UpdateEntryData();
|
||||
_loading = false;
|
||||
}
|
||||
|
||||
void MacroConditionMediaEdit::SourceChanged(const QString &text)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->ClearSignalHandler();
|
||||
_entryData->_source = GetWeakSourceByQString(text);
|
||||
_entryData->ResetSignalHandler();
|
||||
}
|
||||
|
||||
MediaState getMediaStateFromIdx(int idx)
|
||||
{
|
||||
if (idx < static_cast<int>(MediaState::LAST_OBS_MEDIA_STATE)) {
|
||||
return static_cast<MediaState>(idx);
|
||||
} else {
|
||||
return static_cast<MediaState>(
|
||||
idx -
|
||||
static_cast<int>(MediaState::LAST_OBS_MEDIA_STATE) +
|
||||
custom_media_states_offset);
|
||||
}
|
||||
}
|
||||
|
||||
void MacroConditionMediaEdit::StateChanged(int index)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_state = getMediaStateFromIdx(index);
|
||||
}
|
||||
|
||||
void MacroConditionMediaEdit::TimeRestrictionChanged(int index)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (static_cast<MediaTimeRestriction>(index) ==
|
||||
MediaTimeRestriction::TIME_RESTRICTION_NONE) {
|
||||
_time->setDisabled(true);
|
||||
} else {
|
||||
_time->setDisabled(false);
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_restriction = static_cast<MediaTimeRestriction>(index);
|
||||
}
|
||||
|
||||
void MacroConditionMediaEdit::TimeChanged(double seconds)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_time.seconds = seconds;
|
||||
}
|
||||
|
||||
void MacroConditionMediaEdit::TimeUnitChanged(DurationUnit unit)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_time.displayUnit = unit;
|
||||
}
|
||||
|
||||
int getIdxFromMediaState(MediaState state)
|
||||
{
|
||||
if (state < MediaState::LAST_OBS_MEDIA_STATE) {
|
||||
return static_cast<int>(state);
|
||||
} else {
|
||||
return static_cast<int>(state) - custom_media_states_offset +
|
||||
static_cast<int>(MediaState::LAST_OBS_MEDIA_STATE);
|
||||
}
|
||||
}
|
||||
|
||||
void MacroConditionMediaEdit::UpdateEntryData()
|
||||
{
|
||||
if (!_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
_mediaSources->setCurrentText(
|
||||
GetWeakSourceName(_entryData->_source).c_str());
|
||||
_states->setCurrentIndex(getIdxFromMediaState(_entryData->_state));
|
||||
_timeRestrictions->setCurrentIndex(
|
||||
static_cast<int>(_entryData->_restriction));
|
||||
_time->SetDuration(_entryData->_time);
|
||||
if (_entryData->_restriction ==
|
||||
MediaTimeRestriction::TIME_RESTRICTION_NONE) {
|
||||
_time->setDisabled(true);
|
||||
}
|
||||
}
|
||||
92
src/macro-condition-plugin-state.cpp
Normal file
92
src/macro-condition-plugin-state.cpp
Normal file
@@ -0,0 +1,92 @@
|
||||
#include "headers/macro-condition-edit.hpp"
|
||||
#include "headers/macro-condition-plugin-state.hpp"
|
||||
#include "headers/utility.hpp"
|
||||
#include "headers/advanced-scene-switcher.hpp"
|
||||
|
||||
const std::string MacroConditionPluginState::id = "plugin_state";
|
||||
|
||||
bool MacroConditionPluginState::_registered = MacroConditionFactory::Register(
|
||||
MacroConditionPluginState::id,
|
||||
{MacroConditionPluginState::Create,
|
||||
MacroConditionPluginStateEdit::Create,
|
||||
"AdvSceneSwitcher.condition.pluginState", false});
|
||||
|
||||
static std::map<PluginStateCondition, std::string> pluginStateConditionTypes = {
|
||||
{PluginStateCondition::SCENESWITCHED,
|
||||
"AdvSceneSwitcher.condition.pluginState.state.sceneSwitched"},
|
||||
};
|
||||
|
||||
bool MacroConditionPluginState::CheckCondition()
|
||||
{
|
||||
return switcher->macroSceneSwitched;
|
||||
}
|
||||
|
||||
bool MacroConditionPluginState::Save(obs_data_t *obj)
|
||||
{
|
||||
MacroCondition::Save(obj);
|
||||
obs_data_set_int(obj, "condition", static_cast<int>(_condition));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MacroConditionPluginState::Load(obs_data_t *obj)
|
||||
{
|
||||
MacroCondition::Load(obj);
|
||||
_condition = static_cast<PluginStateCondition>(
|
||||
obs_data_get_int(obj, "condition"));
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline void populateConditionSelection(QComboBox *list)
|
||||
{
|
||||
for (auto entry : pluginStateConditionTypes) {
|
||||
list->addItem(obs_module_text(entry.second.c_str()));
|
||||
}
|
||||
}
|
||||
|
||||
MacroConditionPluginStateEdit::MacroConditionPluginStateEdit(
|
||||
QWidget *parent, std::shared_ptr<MacroConditionPluginState> entryData)
|
||||
: QWidget(parent)
|
||||
{
|
||||
_condition = new QComboBox();
|
||||
|
||||
QWidget::connect(_condition, SIGNAL(currentIndexChanged(int)), this,
|
||||
SLOT(ConditionChanged(int)));
|
||||
populateConditionSelection(_condition);
|
||||
|
||||
QHBoxLayout *switchLayout = new QHBoxLayout;
|
||||
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
|
||||
{"{{condition}}", _condition},
|
||||
};
|
||||
placeWidgets(
|
||||
obs_module_text("AdvSceneSwitcher.condition.pluginState.entry"),
|
||||
switchLayout, widgetPlaceholders);
|
||||
|
||||
QVBoxLayout *mainLayout = new QVBoxLayout;
|
||||
|
||||
mainLayout->addLayout(switchLayout);
|
||||
|
||||
setLayout(mainLayout);
|
||||
|
||||
_entryData = entryData;
|
||||
UpdateEntryData();
|
||||
_loading = false;
|
||||
}
|
||||
|
||||
void MacroConditionPluginStateEdit::ConditionChanged(int cond)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_condition = static_cast<PluginStateCondition>(cond);
|
||||
}
|
||||
|
||||
void MacroConditionPluginStateEdit::UpdateEntryData()
|
||||
{
|
||||
if (!_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
_condition->setCurrentIndex(static_cast<int>(_entryData->_condition));
|
||||
}
|
||||
110
src/macro-condition-process.cpp
Normal file
110
src/macro-condition-process.cpp
Normal file
@@ -0,0 +1,110 @@
|
||||
#include "headers/macro-condition-edit.hpp"
|
||||
#include "headers/macro-condition-process.hpp"
|
||||
#include "headers/utility.hpp"
|
||||
#include "headers/advanced-scene-switcher.hpp"
|
||||
|
||||
#include <regex>
|
||||
|
||||
const std::string MacroConditionProcess::id = "process";
|
||||
|
||||
bool MacroConditionProcess::_registered = MacroConditionFactory::Register(
|
||||
MacroConditionProcess::id,
|
||||
{MacroConditionProcess::Create, MacroConditionProcessEdit::Create,
|
||||
"AdvSceneSwitcher.condition.process"});
|
||||
|
||||
bool MacroConditionProcess::CheckCondition()
|
||||
{
|
||||
std::string title;
|
||||
QStringList runningProcesses;
|
||||
|
||||
QString proc = QString::fromStdString(_process);
|
||||
|
||||
GetCurrentWindowTitle(title);
|
||||
GetProcessList(runningProcesses);
|
||||
|
||||
bool equals = runningProcesses.contains(proc);
|
||||
bool matches = runningProcesses.indexOf(QRegularExpression(proc)) != -1;
|
||||
bool focus = !_focus || isInFocus(proc);
|
||||
|
||||
return (equals || matches) && focus;
|
||||
}
|
||||
|
||||
bool MacroConditionProcess::Save(obs_data_t *obj)
|
||||
{
|
||||
MacroCondition::Save(obj);
|
||||
obs_data_set_string(obj, "process", _process.c_str());
|
||||
obs_data_set_bool(obj, "focus", _focus);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MacroConditionProcess::Load(obs_data_t *obj)
|
||||
{
|
||||
MacroCondition::Load(obj);
|
||||
_process = obs_data_get_string(obj, "process");
|
||||
_focus = obs_data_get_bool(obj, "focus");
|
||||
return true;
|
||||
}
|
||||
|
||||
MacroConditionProcessEdit::MacroConditionProcessEdit(
|
||||
QWidget *parent, std::shared_ptr<MacroConditionProcess> entryData)
|
||||
: QWidget(parent)
|
||||
{
|
||||
_processSelection = new QComboBox();
|
||||
_processSelection->setEditable(true);
|
||||
_processSelection->setMaxVisibleItems(20);
|
||||
|
||||
_focused = new QCheckBox();
|
||||
|
||||
QWidget::connect(_processSelection,
|
||||
SIGNAL(currentTextChanged(const QString &)), this,
|
||||
SLOT(ProcessChanged(const QString &)));
|
||||
QWidget::connect(_focused, SIGNAL(stateChanged(int)), this,
|
||||
SLOT(FocusChanged(int)));
|
||||
|
||||
populateProcessSelection(_processSelection);
|
||||
|
||||
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
|
||||
{"{{processes}}", _processSelection},
|
||||
{"{{focused}}", _focused},
|
||||
};
|
||||
|
||||
QHBoxLayout *mainLayout = new QHBoxLayout;
|
||||
placeWidgets(
|
||||
obs_module_text("AdvSceneSwitcher.condition.process.entry"),
|
||||
mainLayout, widgetPlaceholders);
|
||||
setLayout(mainLayout);
|
||||
|
||||
_entryData = entryData;
|
||||
UpdateEntryData();
|
||||
_loading = false;
|
||||
}
|
||||
|
||||
void MacroConditionProcessEdit::ProcessChanged(const QString &text)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_process = text.toStdString();
|
||||
}
|
||||
|
||||
void MacroConditionProcessEdit::FocusChanged(int state)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_focus = state;
|
||||
}
|
||||
|
||||
void MacroConditionProcessEdit::UpdateEntryData()
|
||||
{
|
||||
if (!_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
_processSelection->setCurrentText(_entryData->_process.c_str());
|
||||
_focused->setChecked(_entryData->_focus);
|
||||
}
|
||||
103
src/macro-condition-recording.cpp
Normal file
103
src/macro-condition-recording.cpp
Normal file
@@ -0,0 +1,103 @@
|
||||
#include "headers/macro-condition-edit.hpp"
|
||||
#include "headers/macro-condition-recording.hpp"
|
||||
#include "headers/utility.hpp"
|
||||
#include "headers/advanced-scene-switcher.hpp"
|
||||
|
||||
const std::string MacroConditionRecord::id = "recording";
|
||||
|
||||
bool MacroConditionRecord::_registered = MacroConditionFactory::Register(
|
||||
MacroConditionRecord::id,
|
||||
{MacroConditionRecord::Create, MacroConditionRecordEdit::Create,
|
||||
"AdvSceneSwitcher.condition.record"});
|
||||
|
||||
static std::map<RecordState, std::string> recordStates = {
|
||||
{RecordState::STOP, "AdvSceneSwitcher.condition.record.state.stop"},
|
||||
{RecordState::PAUSE, "AdvSceneSwitcher.condition.record.state.pause"},
|
||||
{RecordState::START, "AdvSceneSwitcher.condition.record.state.start"},
|
||||
};
|
||||
|
||||
bool MacroConditionRecord::CheckCondition()
|
||||
{
|
||||
bool stateMatch = false;
|
||||
switch (_recordState) {
|
||||
case RecordState::STOP:
|
||||
stateMatch = !obs_frontend_recording_active();
|
||||
break;
|
||||
case RecordState::PAUSE:
|
||||
stateMatch = obs_frontend_recording_paused();
|
||||
break;
|
||||
case RecordState::START:
|
||||
stateMatch = obs_frontend_recording_active();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return stateMatch;
|
||||
}
|
||||
|
||||
bool MacroConditionRecord::Save(obs_data_t *obj)
|
||||
{
|
||||
MacroCondition::Save(obj);
|
||||
obs_data_set_int(obj, "state", static_cast<int>(_recordState));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MacroConditionRecord::Load(obs_data_t *obj)
|
||||
{
|
||||
MacroCondition::Load(obj);
|
||||
_recordState = static_cast<RecordState>(obs_data_get_int(obj, "state"));
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline void populateStateSelection(QComboBox *list)
|
||||
{
|
||||
for (auto entry : recordStates) {
|
||||
list->addItem(obs_module_text(entry.second.c_str()));
|
||||
}
|
||||
}
|
||||
|
||||
MacroConditionRecordEdit::MacroConditionRecordEdit(
|
||||
QWidget *parent, std::shared_ptr<MacroConditionRecord> entryData)
|
||||
: QWidget(parent)
|
||||
{
|
||||
_recordState = new QComboBox();
|
||||
|
||||
QWidget::connect(_recordState, SIGNAL(currentIndexChanged(int)), this,
|
||||
SLOT(StateChanged(int)));
|
||||
|
||||
populateStateSelection(_recordState);
|
||||
|
||||
QHBoxLayout *mainLayout = new QHBoxLayout;
|
||||
|
||||
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
|
||||
{"{{recordState}}", _recordState},
|
||||
};
|
||||
|
||||
placeWidgets(obs_module_text("AdvSceneSwitcher.condition.record.entry"),
|
||||
mainLayout, widgetPlaceholders);
|
||||
setLayout(mainLayout);
|
||||
|
||||
_entryData = entryData;
|
||||
UpdateEntryData();
|
||||
_loading = false;
|
||||
}
|
||||
|
||||
void MacroConditionRecordEdit::StateChanged(int value)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_recordState = static_cast<RecordState>(value);
|
||||
}
|
||||
|
||||
void MacroConditionRecordEdit::UpdateEntryData()
|
||||
{
|
||||
if (!_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
_recordState->setCurrentIndex(
|
||||
static_cast<int>(_entryData->_recordState));
|
||||
}
|
||||
140
src/macro-condition-region.cpp
Normal file
140
src/macro-condition-region.cpp
Normal file
@@ -0,0 +1,140 @@
|
||||
#include "headers/macro-condition-edit.hpp"
|
||||
#include "headers/macro-condition-region.hpp"
|
||||
#include "headers/utility.hpp"
|
||||
#include "headers/advanced-scene-switcher.hpp"
|
||||
|
||||
const std::string MacroConditionRegion::id = "region";
|
||||
|
||||
bool MacroConditionRegion::_registered = MacroConditionFactory::Register(
|
||||
MacroConditionRegion::id,
|
||||
{MacroConditionRegion::Create, MacroConditionRegionEdit::Create,
|
||||
"AdvSceneSwitcher.condition.region"});
|
||||
|
||||
bool MacroConditionRegion::CheckCondition()
|
||||
{
|
||||
std::pair<int, int> cursorPos = getCursorPos();
|
||||
return cursorPos.first >= _minX && cursorPos.second >= _minY &&
|
||||
cursorPos.first <= _maxX && cursorPos.second <= _maxY;
|
||||
}
|
||||
|
||||
bool MacroConditionRegion::Save(obs_data_t *obj)
|
||||
{
|
||||
MacroCondition::Save(obj);
|
||||
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);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MacroConditionRegion::Load(obs_data_t *obj)
|
||||
{
|
||||
MacroCondition::Load(obj);
|
||||
_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");
|
||||
return true;
|
||||
}
|
||||
|
||||
MacroConditionRegionEdit::MacroConditionRegionEdit(
|
||||
QWidget *parent, std::shared_ptr<MacroConditionRegion> entryData)
|
||||
: QWidget(parent)
|
||||
{
|
||||
_minX = new QSpinBox();
|
||||
_minY = new QSpinBox();
|
||||
_maxX = new QSpinBox();
|
||||
_maxY = new QSpinBox();
|
||||
|
||||
_minX->setPrefix("Min X: ");
|
||||
_minY->setPrefix("Min Y: ");
|
||||
_maxX->setPrefix("Max X: ");
|
||||
_maxY->setPrefix("Max Y: ");
|
||||
|
||||
_minX->setMinimum(-1000000);
|
||||
_minY->setMinimum(-1000000);
|
||||
_maxX->setMinimum(-1000000);
|
||||
_maxY->setMinimum(-1000000);
|
||||
|
||||
_minX->setMaximum(1000000);
|
||||
_minY->setMaximum(1000000);
|
||||
_maxX->setMaximum(1000000);
|
||||
_maxY->setMaximum(1000000);
|
||||
|
||||
QWidget::connect(_minX, SIGNAL(valueChanged(int)), this,
|
||||
SLOT(MinXChanged(int)));
|
||||
QWidget::connect(_minY, SIGNAL(valueChanged(int)), this,
|
||||
SLOT(MinYChanged(int)));
|
||||
QWidget::connect(_maxX, SIGNAL(valueChanged(int)), this,
|
||||
SLOT(MaxXChanged(int)));
|
||||
QWidget::connect(_maxY, SIGNAL(valueChanged(int)), this,
|
||||
SLOT(MaxYChanged(int)));
|
||||
|
||||
QHBoxLayout *mainLayout = new QHBoxLayout;
|
||||
|
||||
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
|
||||
{"{{minX}}", _minX},
|
||||
{"{{minY}}", _minY},
|
||||
{"{{maxX}}", _maxX},
|
||||
{"{{maxY}}", _maxY}};
|
||||
|
||||
placeWidgets(obs_module_text("AdvSceneSwitcher.condition.region.entry"),
|
||||
mainLayout, widgetPlaceholders);
|
||||
setLayout(mainLayout);
|
||||
|
||||
_entryData = entryData;
|
||||
UpdateEntryData();
|
||||
_loading = false;
|
||||
}
|
||||
|
||||
void MacroConditionRegionEdit::MinXChanged(int pos)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_minX = pos;
|
||||
}
|
||||
|
||||
void MacroConditionRegionEdit::MinYChanged(int pos)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_minY = pos;
|
||||
}
|
||||
|
||||
void MacroConditionRegionEdit::MaxXChanged(int pos)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_maxX = pos;
|
||||
}
|
||||
|
||||
void MacroConditionRegionEdit::MaxYChanged(int pos)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_maxY = pos;
|
||||
}
|
||||
|
||||
void MacroConditionRegionEdit::UpdateEntryData()
|
||||
{
|
||||
if (!_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
_minX->setValue(_entryData->_minX);
|
||||
_minY->setValue(_entryData->_minY);
|
||||
_maxX->setValue(_entryData->_maxX);
|
||||
_maxY->setValue(_entryData->_maxY);
|
||||
}
|
||||
117
src/macro-condition-scene.cpp
Normal file
117
src/macro-condition-scene.cpp
Normal file
@@ -0,0 +1,117 @@
|
||||
#include "headers/macro-condition-edit.hpp"
|
||||
#include "headers/macro-condition-scene.hpp"
|
||||
#include "headers/utility.hpp"
|
||||
#include "headers/advanced-scene-switcher.hpp"
|
||||
|
||||
const std::string MacroConditionScene::id = "scene";
|
||||
|
||||
bool MacroConditionScene::_registered = MacroConditionFactory::Register(
|
||||
MacroConditionScene::id,
|
||||
{MacroConditionScene::Create, MacroConditionSceneEdit::Create,
|
||||
"AdvSceneSwitcher.condition.scene"});
|
||||
|
||||
static std::map<SceneType, std::string> sceneTypes = {
|
||||
{SceneType::CURRENT, "AdvSceneSwitcher.condition.scene.type.current"},
|
||||
{SceneType::PREVIOUS, "AdvSceneSwitcher.condition.scene.type.previous"},
|
||||
};
|
||||
|
||||
bool MacroConditionScene::CheckCondition()
|
||||
{
|
||||
bool sceneMatch = false;
|
||||
if (_type == SceneType::CURRENT) {
|
||||
obs_source_t *rawScene = obs_frontend_get_current_scene();
|
||||
OBSWeakSource currentScene =
|
||||
obs_source_get_weak_source(rawScene);
|
||||
sceneMatch = currentScene == _scene;
|
||||
obs_weak_source_release(currentScene);
|
||||
obs_source_release(rawScene);
|
||||
} else {
|
||||
sceneMatch = switcher->previousScene == _scene;
|
||||
}
|
||||
|
||||
return sceneMatch;
|
||||
}
|
||||
|
||||
bool MacroConditionScene::Save(obs_data_t *obj)
|
||||
{
|
||||
MacroCondition::Save(obj);
|
||||
obs_data_set_string(obj, "scene", GetWeakSourceName(_scene).c_str());
|
||||
obs_data_set_int(obj, "type", static_cast<int>(_type));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MacroConditionScene::Load(obs_data_t *obj)
|
||||
{
|
||||
MacroCondition::Load(obj);
|
||||
_scene = GetWeakSourceByName(obs_data_get_string(obj, "scene"));
|
||||
_type = static_cast<SceneType>(obs_data_get_int(obj, "type"));
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline void populateTypeSelection(QComboBox *list)
|
||||
{
|
||||
for (auto entry : sceneTypes) {
|
||||
list->addItem(obs_module_text(entry.second.c_str()));
|
||||
}
|
||||
}
|
||||
|
||||
MacroConditionSceneEdit::MacroConditionSceneEdit(
|
||||
QWidget *parent, std::shared_ptr<MacroConditionScene> entryData)
|
||||
: QWidget(parent)
|
||||
{
|
||||
_sceneSelection = new QComboBox();
|
||||
_sceneType = new QComboBox();
|
||||
|
||||
QWidget::connect(_sceneSelection,
|
||||
SIGNAL(currentTextChanged(const QString &)), this,
|
||||
SLOT(SceneChanged(const QString &)));
|
||||
QWidget::connect(_sceneType, SIGNAL(currentIndexChanged(int)), this,
|
||||
SLOT(TypeChanged(int)));
|
||||
|
||||
populateSceneSelection(_sceneSelection);
|
||||
populateTypeSelection(_sceneType);
|
||||
|
||||
QHBoxLayout *mainLayout = new QHBoxLayout;
|
||||
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
|
||||
{"{{scenes}}", _sceneSelection},
|
||||
{"{{sceneType}}", _sceneType},
|
||||
};
|
||||
placeWidgets(obs_module_text("AdvSceneSwitcher.condition.scene.entry"),
|
||||
mainLayout, widgetPlaceholders);
|
||||
setLayout(mainLayout);
|
||||
|
||||
_entryData = entryData;
|
||||
UpdateEntryData();
|
||||
_loading = false;
|
||||
}
|
||||
|
||||
void MacroConditionSceneEdit::SceneChanged(const QString &text)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_scene = GetWeakSourceByQString(text);
|
||||
}
|
||||
|
||||
void MacroConditionSceneEdit::TypeChanged(int value)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_type = static_cast<SceneType>(value);
|
||||
}
|
||||
|
||||
void MacroConditionSceneEdit::UpdateEntryData()
|
||||
{
|
||||
if (!_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
_sceneSelection->setCurrentText(
|
||||
GetWeakSourceName(_entryData->_scene).c_str());
|
||||
_sceneType->setCurrentIndex(static_cast<int>(_entryData->_type));
|
||||
}
|
||||
223
src/macro-condition-source.cpp
Normal file
223
src/macro-condition-source.cpp
Normal file
@@ -0,0 +1,223 @@
|
||||
#include "headers/macro-condition-edit.hpp"
|
||||
#include "headers/macro-condition-source.hpp"
|
||||
#include "headers/utility.hpp"
|
||||
#include "headers/advanced-scene-switcher.hpp"
|
||||
|
||||
#include <regex>
|
||||
|
||||
const std::string MacroConditionSource::id = "source";
|
||||
|
||||
bool MacroConditionSource::_registered = MacroConditionFactory::Register(
|
||||
MacroConditionSource::id,
|
||||
{MacroConditionSource::Create, MacroConditionSourceEdit::Create,
|
||||
"AdvSceneSwitcher.condition.source"});
|
||||
|
||||
static std::map<SourceCondition, std::string> sourceConditionTypes = {
|
||||
{SourceCondition::ACTIVE,
|
||||
"AdvSceneSwitcher.condition.source.type.active"},
|
||||
{SourceCondition::SHOWING,
|
||||
"AdvSceneSwitcher.condition.source.type.showing"},
|
||||
{SourceCondition::SETTINGS,
|
||||
"AdvSceneSwitcher.condition.source.type.settings"},
|
||||
};
|
||||
|
||||
bool checkSettings(const OBSWeakSource &source, const std::string &settings,
|
||||
bool useRegex)
|
||||
{
|
||||
bool ret = false;
|
||||
std::string currentSettings = getSourceSettings(source);
|
||||
if (useRegex) {
|
||||
try {
|
||||
std::regex expr(settings);
|
||||
ret = std::regex_match(currentSettings, expr);
|
||||
} catch (const std::regex_error &) {
|
||||
}
|
||||
} else {
|
||||
ret = currentSettings == settings;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool MacroConditionSource::CheckCondition()
|
||||
{
|
||||
if (!_source) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ret = false;
|
||||
auto s = obs_weak_source_get_source(_source);
|
||||
|
||||
switch (_condition) {
|
||||
case SourceCondition::ACTIVE:
|
||||
ret = obs_source_active(s);
|
||||
break;
|
||||
case SourceCondition::SHOWING:
|
||||
ret = obs_source_showing(s);
|
||||
break;
|
||||
case SourceCondition::SETTINGS:
|
||||
ret = checkSettings(_source, _settings, _regex);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
obs_source_release(s);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool MacroConditionSource::Save(obs_data_t *obj)
|
||||
{
|
||||
MacroCondition::Save(obj);
|
||||
obs_data_set_string(obj, "source", GetWeakSourceName(_source).c_str());
|
||||
obs_data_set_int(obj, "condition", static_cast<int>(_condition));
|
||||
obs_data_set_string(obj, "settings", _settings.c_str());
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MacroConditionSource::Load(obs_data_t *obj)
|
||||
{
|
||||
MacroCondition::Load(obj);
|
||||
const char *sourceName = obs_data_get_string(obj, "source");
|
||||
_source = GetWeakSourceByName(sourceName);
|
||||
_condition = static_cast<SourceCondition>(
|
||||
obs_data_get_int(obj, "condition"));
|
||||
_settings = obs_data_get_string(obj, "settings");
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline void populateConditionSelection(QComboBox *list)
|
||||
{
|
||||
for (auto entry : sourceConditionTypes) {
|
||||
list->addItem(obs_module_text(entry.second.c_str()));
|
||||
}
|
||||
}
|
||||
|
||||
MacroConditionSourceEdit::MacroConditionSourceEdit(
|
||||
QWidget *parent, std::shared_ptr<MacroConditionSource> entryData)
|
||||
: QWidget(parent)
|
||||
{
|
||||
_sources = new QComboBox();
|
||||
_conditions = new QComboBox();
|
||||
_getSettings = new QPushButton(obs_module_text(
|
||||
"AdvSceneSwitcher.condition.source.getSettings"));
|
||||
_settings = new QPlainTextEdit();
|
||||
_regex = new QCheckBox(
|
||||
obs_module_text("AdvSceneSwitcher.condition.source.regex"));
|
||||
|
||||
populateConditionSelection(_conditions);
|
||||
populateSourceSelection(_sources);
|
||||
|
||||
QWidget::connect(_sources, SIGNAL(currentTextChanged(const QString &)),
|
||||
this, SLOT(SourceChanged(const QString &)));
|
||||
QWidget::connect(_conditions, SIGNAL(currentIndexChanged(int)), this,
|
||||
SLOT(ConditionChanged(int)));
|
||||
QWidget::connect(_getSettings, SIGNAL(clicked()), this,
|
||||
SLOT(GetSettingsClicked()));
|
||||
QWidget::connect(_settings, SIGNAL(textChanged()), this,
|
||||
SLOT(SettingsChanged()));
|
||||
QWidget::connect(_regex, SIGNAL(stateChanged(int)), this,
|
||||
SLOT(RegexChanged(int)));
|
||||
|
||||
QVBoxLayout *mainLayout = new QVBoxLayout;
|
||||
QHBoxLayout *line1Layout = new QHBoxLayout;
|
||||
QHBoxLayout *line2Layout = new QHBoxLayout;
|
||||
QHBoxLayout *line3Layout = new QHBoxLayout;
|
||||
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
|
||||
{"{{sources}}", _sources}, {"{{conditions}}", _conditions},
|
||||
{"{{settings}}", _settings}, {"{{getSettings}}", _getSettings},
|
||||
{"{{regex}}", _regex},
|
||||
};
|
||||
placeWidgets(obs_module_text(
|
||||
"AdvSceneSwitcher.condition.source.entry.line1"),
|
||||
line1Layout, widgetPlaceholders);
|
||||
placeWidgets(obs_module_text(
|
||||
"AdvSceneSwitcher.condition.source.entry.line2"),
|
||||
line2Layout, widgetPlaceholders, false);
|
||||
placeWidgets(obs_module_text(
|
||||
"AdvSceneSwitcher.condition.source.entry.line3"),
|
||||
line3Layout, widgetPlaceholders);
|
||||
mainLayout->addLayout(line1Layout);
|
||||
mainLayout->addLayout(line2Layout);
|
||||
mainLayout->addLayout(line3Layout);
|
||||
setLayout(mainLayout);
|
||||
|
||||
_entryData = entryData;
|
||||
UpdateEntryData();
|
||||
_loading = false;
|
||||
}
|
||||
|
||||
void MacroConditionSourceEdit::SourceChanged(const QString &text)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_source = GetWeakSourceByQString(text);
|
||||
}
|
||||
|
||||
void MacroConditionSourceEdit::ConditionChanged(int index)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_condition = static_cast<SourceCondition>(index);
|
||||
SetSettingsSelectionVisible(_entryData->_condition ==
|
||||
SourceCondition::SETTINGS);
|
||||
}
|
||||
|
||||
void MacroConditionSourceEdit::GetSettingsClicked()
|
||||
{
|
||||
if (_loading || !_entryData || !_entryData->_source) {
|
||||
return;
|
||||
}
|
||||
|
||||
_settings->setPlainText(
|
||||
QString::fromStdString(getSourceSettings(_entryData->_source)));
|
||||
}
|
||||
|
||||
void MacroConditionSourceEdit::SettingsChanged()
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_settings = _settings->toPlainText().toStdString();
|
||||
}
|
||||
|
||||
void MacroConditionSourceEdit::RegexChanged(int state)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_regex = state;
|
||||
}
|
||||
|
||||
void MacroConditionSourceEdit::SetSettingsSelectionVisible(bool visible)
|
||||
{
|
||||
_settings->setVisible(visible);
|
||||
_getSettings->setVisible(visible);
|
||||
_regex->setVisible(visible);
|
||||
adjustSize();
|
||||
}
|
||||
|
||||
void MacroConditionSourceEdit::UpdateEntryData()
|
||||
{
|
||||
if (!_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
_sources->setCurrentText(
|
||||
GetWeakSourceName(_entryData->_source).c_str());
|
||||
_conditions->setCurrentIndex(static_cast<int>(_entryData->_condition));
|
||||
_settings->setPlainText(QString::fromStdString(_entryData->_settings));
|
||||
_regex->setChecked(_entryData->_regex);
|
||||
SetSettingsSelectionVisible(_entryData->_condition ==
|
||||
SourceCondition::SETTINGS);
|
||||
}
|
||||
99
src/macro-condition-streaming.cpp
Normal file
99
src/macro-condition-streaming.cpp
Normal file
@@ -0,0 +1,99 @@
|
||||
#include "headers/macro-condition-edit.hpp"
|
||||
#include "headers/macro-condition-streaming.hpp"
|
||||
#include "headers/utility.hpp"
|
||||
#include "headers/advanced-scene-switcher.hpp"
|
||||
|
||||
const std::string MacroConditionStream::id = "streaming";
|
||||
|
||||
bool MacroConditionStream::_registered = MacroConditionFactory::Register(
|
||||
MacroConditionStream::id,
|
||||
{MacroConditionStream::Create, MacroConditionStreamEdit::Create,
|
||||
"AdvSceneSwitcher.condition.stream"});
|
||||
|
||||
static std::map<StreamState, std::string> streamStates = {
|
||||
{StreamState::STOP, "AdvSceneSwitcher.condition.stream.state.stop"},
|
||||
{StreamState::START, "AdvSceneSwitcher.condition.stream.state.start"},
|
||||
};
|
||||
|
||||
bool MacroConditionStream::CheckCondition()
|
||||
{
|
||||
bool stateMatch = false;
|
||||
switch (_streamState) {
|
||||
case StreamState::STOP:
|
||||
stateMatch = !obs_frontend_streaming_active();
|
||||
break;
|
||||
case StreamState::START:
|
||||
stateMatch = obs_frontend_streaming_active();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return stateMatch;
|
||||
}
|
||||
|
||||
bool MacroConditionStream::Save(obs_data_t *obj)
|
||||
{
|
||||
MacroCondition::Save(obj);
|
||||
obs_data_set_int(obj, "state", static_cast<int>(_streamState));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MacroConditionStream::Load(obs_data_t *obj)
|
||||
{
|
||||
MacroCondition::Load(obj);
|
||||
_streamState = static_cast<StreamState>(obs_data_get_int(obj, "state"));
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline void populateStateSelection(QComboBox *list)
|
||||
{
|
||||
for (auto entry : streamStates) {
|
||||
list->addItem(obs_module_text(entry.second.c_str()));
|
||||
}
|
||||
}
|
||||
|
||||
MacroConditionStreamEdit::MacroConditionStreamEdit(
|
||||
QWidget *parent, std::shared_ptr<MacroConditionStream> entryData)
|
||||
: QWidget(parent)
|
||||
{
|
||||
_streamState = new QComboBox();
|
||||
|
||||
QWidget::connect(_streamState, SIGNAL(currentIndexChanged(int)), this,
|
||||
SLOT(StateChanged(int)));
|
||||
|
||||
populateStateSelection(_streamState);
|
||||
|
||||
QHBoxLayout *mainLayout = new QHBoxLayout;
|
||||
|
||||
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
|
||||
{"{{streamState}}", _streamState},
|
||||
};
|
||||
|
||||
placeWidgets(obs_module_text("AdvSceneSwitcher.condition.stream.entry"),
|
||||
mainLayout, widgetPlaceholders);
|
||||
setLayout(mainLayout);
|
||||
|
||||
_entryData = entryData;
|
||||
UpdateEntryData();
|
||||
_loading = false;
|
||||
}
|
||||
|
||||
void MacroConditionStreamEdit::StateChanged(int value)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_streamState = static_cast<StreamState>(value);
|
||||
}
|
||||
|
||||
void MacroConditionStreamEdit::UpdateEntryData()
|
||||
{
|
||||
if (!_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
_streamState->setCurrentIndex(
|
||||
static_cast<int>(_entryData->_streamState));
|
||||
}
|
||||
326
src/macro-condition-video.cpp
Normal file
326
src/macro-condition-video.cpp
Normal file
@@ -0,0 +1,326 @@
|
||||
#include "headers/macro-condition-edit.hpp"
|
||||
#include "headers/macro-condition-video.hpp"
|
||||
#include "headers/utility.hpp"
|
||||
#include "headers/advanced-scene-switcher.hpp"
|
||||
|
||||
#include <QFileDialog>
|
||||
#include <QBuffer>
|
||||
#include <QToolTip>
|
||||
#include <QMessageBox>
|
||||
|
||||
const std::string MacroConditionVideo::id = "video";
|
||||
|
||||
bool MacroConditionVideo::_registered = MacroConditionFactory::Register(
|
||||
MacroConditionVideo::id,
|
||||
{MacroConditionVideo::Create, MacroConditionVideoEdit::Create,
|
||||
"AdvSceneSwitcher.condition.video"});
|
||||
|
||||
static std::map<VideoCondition, std::string> conditionTypes = {
|
||||
{VideoCondition::MATCH,
|
||||
"AdvSceneSwitcher.condition.video.condition.match"},
|
||||
{VideoCondition::DIFFER,
|
||||
"AdvSceneSwitcher.condition.video.condition.differ"},
|
||||
{VideoCondition::HAS_NOT_CHANGED,
|
||||
"AdvSceneSwitcher.condition.video.condition.hasNotChanged"},
|
||||
{VideoCondition::HAS_CHANGED,
|
||||
"AdvSceneSwitcher.condition.video.condition.hasChanged"},
|
||||
{VideoCondition::NO_IMAGE,
|
||||
"AdvSceneSwitcher.condition.video.condition.noImage"},
|
||||
};
|
||||
|
||||
bool requiresFileInput(VideoCondition t)
|
||||
{
|
||||
return t == VideoCondition::MATCH || t == VideoCondition::DIFFER;
|
||||
}
|
||||
|
||||
bool MacroConditionVideo::CheckCondition()
|
||||
{
|
||||
bool match = false;
|
||||
|
||||
if (_screenshotData) {
|
||||
if (_screenshotData->done) {
|
||||
match = Compare();
|
||||
|
||||
if (!requiresFileInput(_condition)) {
|
||||
_matchImage = std::move(_screenshotData->image);
|
||||
}
|
||||
_screenshotData.reset(nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
GetScreenshot();
|
||||
return match;
|
||||
}
|
||||
|
||||
bool MacroConditionVideo::Save(obs_data_t *obj)
|
||||
{
|
||||
MacroCondition::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_string(obj, "filePath", _file.c_str());
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MacroConditionVideo::Load(obs_data_t *obj)
|
||||
{
|
||||
const char *videoSourceName = obs_data_get_string(obj, "videoSource");
|
||||
_videoSource = GetWeakSourceByName(videoSourceName);
|
||||
_condition =
|
||||
static_cast<VideoCondition>(obs_data_get_int(obj, "condition"));
|
||||
_file = obs_data_get_string(obj, "filePath");
|
||||
|
||||
if (requiresFileInput(_condition)) {
|
||||
(void)LoadImageFromFile();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void MacroConditionVideo::GetScreenshot()
|
||||
{
|
||||
auto source = obs_weak_source_get_source(_videoSource);
|
||||
_screenshotData = std::make_unique<AdvSSScreenshotObj>(source);
|
||||
obs_source_release(source);
|
||||
}
|
||||
|
||||
bool MacroConditionVideo::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 MacroConditionVideo::Compare()
|
||||
{
|
||||
switch (_condition) {
|
||||
case VideoCondition::MATCH:
|
||||
return _screenshotData->image == _matchImage;
|
||||
case VideoCondition::DIFFER:
|
||||
return _screenshotData->image != _matchImage;
|
||||
case VideoCondition::HAS_CHANGED:
|
||||
return _screenshotData->image != _matchImage;
|
||||
case VideoCondition::HAS_NOT_CHANGED:
|
||||
return _screenshotData->image == _matchImage;
|
||||
case VideoCondition::NO_IMAGE:
|
||||
return _screenshotData->image.isNull();
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline void populateConditionSelection(QComboBox *list)
|
||||
{
|
||||
for (auto entry : conditionTypes) {
|
||||
list->addItem(obs_module_text(entry.second.c_str()));
|
||||
}
|
||||
}
|
||||
|
||||
MacroConditionVideoEdit::MacroConditionVideoEdit(
|
||||
QWidget *parent, std::shared_ptr<MacroConditionVideo> entryData)
|
||||
: QWidget(parent)
|
||||
{
|
||||
_videoSelection = new QComboBox();
|
||||
_condition = new QComboBox();
|
||||
_filePath = new QLineEdit();
|
||||
_browseButton =
|
||||
new QPushButton(obs_module_text("AdvSceneSwitcher.browse"));
|
||||
|
||||
_filePath->setFixedWidth(100);
|
||||
|
||||
_browseButton->setStyleSheet("border:1px solid gray;");
|
||||
|
||||
QWidget::connect(_videoSelection,
|
||||
SIGNAL(currentTextChanged(const QString &)), this,
|
||||
SLOT(SourceChanged(const QString &)));
|
||||
QWidget::connect(_condition, SIGNAL(currentIndexChanged(int)), this,
|
||||
SLOT(ConditionChanged(int)));
|
||||
QWidget::connect(_filePath, SIGNAL(editingFinished()), this,
|
||||
SLOT(FilePathChanged()));
|
||||
QWidget::connect(_browseButton, SIGNAL(clicked()), this,
|
||||
SLOT(BrowseButtonClicked()));
|
||||
|
||||
populateVideoSelection(_videoSelection);
|
||||
populateConditionSelection(_condition);
|
||||
|
||||
QHBoxLayout *mainLayout = new QHBoxLayout;
|
||||
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
|
||||
{"{{videoSources}}", _videoSelection},
|
||||
{"{{condition}}", _condition},
|
||||
{"{{filePath}}", _filePath},
|
||||
{"{{browseButton}}", _browseButton},
|
||||
};
|
||||
placeWidgets(obs_module_text("AdvSceneSwitcher.condition.video.entry"),
|
||||
mainLayout, widgetPlaceholders);
|
||||
setLayout(mainLayout);
|
||||
|
||||
_entryData = entryData;
|
||||
UpdateEntryData();
|
||||
_loading = false;
|
||||
}
|
||||
|
||||
void MacroConditionVideoEdit::UpdatePreviewTooltip()
|
||||
{
|
||||
if (!_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!requiresFileInput(_entryData->_condition)) {
|
||||
this->setToolTip("");
|
||||
return;
|
||||
}
|
||||
|
||||
QImage preview = _entryData->GetMatchImage().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 MacroConditionVideoEdit::SetFilePath(const QString &text)
|
||||
{
|
||||
_filePath->setText(text);
|
||||
FilePathChanged();
|
||||
}
|
||||
|
||||
void MacroConditionVideoEdit::SourceChanged(const QString &text)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_videoSource = GetWeakSourceByQString(text);
|
||||
}
|
||||
|
||||
void MacroConditionVideoEdit::ConditionChanged(int cond)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_condition = static_cast<VideoCondition>(cond);
|
||||
|
||||
if (requiresFileInput(_entryData->_condition)) {
|
||||
_filePath->show();
|
||||
_browseButton->show();
|
||||
} else {
|
||||
_filePath->hide();
|
||||
_browseButton->hide();
|
||||
}
|
||||
|
||||
// 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 (_entryData->LoadImageFromFile()) {
|
||||
UpdatePreviewTooltip();
|
||||
}
|
||||
}
|
||||
|
||||
void MacroConditionVideoEdit::FilePathChanged()
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_file = _filePath->text().toUtf8().constData();
|
||||
if (_entryData->LoadImageFromFile()) {
|
||||
UpdatePreviewTooltip();
|
||||
}
|
||||
}
|
||||
|
||||
void MacroConditionVideoEdit::BrowseButtonClicked()
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
QString path;
|
||||
bool useExistingFile = false;
|
||||
// Ask whether to create screenshot or to select existing file
|
||||
if (_entryData->_videoSource) {
|
||||
QMessageBox msgBox(
|
||||
QMessageBox::Question,
|
||||
obs_module_text("AdvSceneSwitcher.windowTitle"),
|
||||
obs_module_text(
|
||||
"AdvSceneSwitcher.condition.video.askFileAction"),
|
||||
QMessageBox::Yes | QMessageBox::No);
|
||||
msgBox.setWindowFlags(Qt::Window | Qt::WindowTitleHint |
|
||||
Qt::CustomizeWindowHint);
|
||||
msgBox.setButtonText(
|
||||
QMessageBox::Yes,
|
||||
obs_module_text(
|
||||
"AdvSceneSwitcher.condition.video.askFileAction.file"));
|
||||
msgBox.setButtonText(
|
||||
QMessageBox::No,
|
||||
obs_module_text(
|
||||
"AdvSceneSwitcher.condition.video.askFileAction.screenshot"));
|
||||
useExistingFile = msgBox.exec() == QMessageBox::Yes;
|
||||
}
|
||||
|
||||
if (useExistingFile) {
|
||||
path = QFileDialog::getOpenFileName(this);
|
||||
if (path.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
} else {
|
||||
auto source =
|
||||
obs_weak_source_get_source(_entryData->_videoSource);
|
||||
auto screenshot = std::make_unique<AdvSSScreenshotObj>(source);
|
||||
obs_source_release(source);
|
||||
|
||||
path = QFileDialog::getSaveFileName(this);
|
||||
if (path.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
QFile file(path);
|
||||
if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
|
||||
return;
|
||||
}
|
||||
if (!screenshot->done) { // Screenshot usually completed by now
|
||||
std::this_thread::sleep_for(std::chrono::seconds(1));
|
||||
}
|
||||
if (!screenshot->done) {
|
||||
DisplayMessage("Failed to get screenshot of source!");
|
||||
return;
|
||||
}
|
||||
|
||||
screenshot->image.save(path);
|
||||
}
|
||||
SetFilePath(path);
|
||||
}
|
||||
|
||||
void MacroConditionVideoEdit::UpdateEntryData()
|
||||
{
|
||||
if (!_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
_videoSelection->setCurrentText(
|
||||
GetWeakSourceName(_entryData->_videoSource).c_str());
|
||||
_condition->setCurrentIndex(static_cast<int>(_entryData->_condition));
|
||||
_filePath->setText(QString::fromStdString(_entryData->_file));
|
||||
|
||||
if (!requiresFileInput(_entryData->_condition)) {
|
||||
_filePath->hide();
|
||||
_browseButton->hide();
|
||||
}
|
||||
}
|
||||
113
src/macro-condition-virt-desktop.cpp
Normal file
113
src/macro-condition-virt-desktop.cpp
Normal file
@@ -0,0 +1,113 @@
|
||||
#include "headers/macro-condition-edit.hpp"
|
||||
#include "headers/macro-condition-virt-desktop.hpp"
|
||||
#include "headers/utility.hpp"
|
||||
#include "headers/advanced-scene-switcher.hpp"
|
||||
|
||||
const std::string MacroConditionVirtDesktop::id = "virtual_desktop";
|
||||
|
||||
bool MacroConditionVirtDesktop::_registered = MacroConditionFactory::Register(
|
||||
MacroConditionVirtDesktop::id,
|
||||
{MacroConditionVirtDesktop::Create,
|
||||
MacroConditionVirtDesktopEdit::Create,
|
||||
"AdvSceneSwitcher.condition.virtDesktop"});
|
||||
|
||||
bool MacroConditionVirtDesktop::CheckCondition()
|
||||
{
|
||||
long curDesktop;
|
||||
GetCurrentVirtualDesktop(curDesktop);
|
||||
return _desktop == curDesktop;
|
||||
}
|
||||
|
||||
bool MacroConditionVirtDesktop::Save(obs_data_t *obj)
|
||||
{
|
||||
MacroCondition::Save(obj);
|
||||
obs_data_set_int(obj, "desktop", static_cast<int>(_desktop));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MacroConditionVirtDesktop::Load(obs_data_t *obj)
|
||||
{
|
||||
MacroCondition::Load(obj);
|
||||
_desktop = obs_data_get_int(obj, "desktop");
|
||||
return true;
|
||||
}
|
||||
|
||||
static void populateVirtualDesktopSelection(QComboBox *list)
|
||||
{
|
||||
long count;
|
||||
GetVirtualDesktopCount(count);
|
||||
|
||||
for (long i = 0; i < count; i++) {
|
||||
list->addItem(std::to_string(i).c_str());
|
||||
}
|
||||
}
|
||||
|
||||
MacroConditionVirtDesktopEdit::MacroConditionVirtDesktopEdit(
|
||||
QWidget *parent, std::shared_ptr<MacroConditionVirtDesktop> entryData)
|
||||
: QWidget(parent)
|
||||
{
|
||||
_currentDesktop = new QLabel();
|
||||
_currentDesktop->setTextInteractionFlags(Qt::TextSelectableByMouse);
|
||||
_virtDesktops = new QComboBox();
|
||||
QWidget::connect(_virtDesktops, SIGNAL(currentIndexChanged(int)), this,
|
||||
SLOT(DesktopChanged(int)));
|
||||
populateVirtualDesktopSelection(_virtDesktops);
|
||||
|
||||
QVBoxLayout *mainLayout = new QVBoxLayout;
|
||||
QHBoxLayout *line1Layout = new QHBoxLayout;
|
||||
QHBoxLayout *line2Layout = new QHBoxLayout;
|
||||
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
|
||||
{"{{virtDesktops}}", _virtDesktops},
|
||||
{"{{currentDesktop}}", _currentDesktop},
|
||||
};
|
||||
placeWidgets(
|
||||
obs_module_text(
|
||||
"AdvSceneSwitcher.condition.virtDesktop.entry.line1"),
|
||||
line1Layout, widgetPlaceholders);
|
||||
placeWidgets(
|
||||
obs_module_text(
|
||||
"AdvSceneSwitcher.condition.virtDesktop.entry.line2"),
|
||||
line2Layout, widgetPlaceholders);
|
||||
mainLayout->addLayout(line1Layout);
|
||||
mainLayout->addLayout(line2Layout);
|
||||
setLayout(mainLayout);
|
||||
|
||||
// Current virtual desktop position
|
||||
QTimer *timer = new QTimer(this);
|
||||
connect(timer, SIGNAL(timeout()), this, SLOT(UpdateCurrentDesktop()));
|
||||
timer->start(1000);
|
||||
|
||||
_entryData = entryData;
|
||||
UpdateEntryData();
|
||||
_loading = false;
|
||||
}
|
||||
|
||||
void MacroConditionVirtDesktopEdit::DesktopChanged(int value)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_desktop = value;
|
||||
}
|
||||
|
||||
void MacroConditionVirtDesktopEdit::UpdateEntryData()
|
||||
{
|
||||
if (!_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
_virtDesktops->setCurrentIndex(_entryData->_desktop);
|
||||
}
|
||||
|
||||
void MacroConditionVirtDesktopEdit::UpdateCurrentDesktop()
|
||||
{
|
||||
long curDesktop;
|
||||
if (GetCurrentVirtualDesktop(curDesktop)) {
|
||||
_currentDesktop->setText(QString::number(curDesktop));
|
||||
} else {
|
||||
_currentDesktop->setText(obs_module_text(
|
||||
"AdvSceneSwitcher.condition.virtDesktop.notAvailable"));
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user