mirror of
https://github.com/WarmUpTill/SceneSwitcher.git
synced 2026-08-29 04:36:21 -05:00
Compare commits
56 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
16ee789c82 | ||
|
|
d040a01016 | ||
|
|
eb952afe58 | ||
|
|
34cff925fc | ||
|
|
016d963598 | ||
|
|
d31ae77176 | ||
|
|
2c1b97e5cd | ||
|
|
c498d1509a | ||
|
|
2d61189655 | ||
|
|
82c20b871b | ||
|
|
bd52ef8e84 | ||
|
|
59e1ddd31e | ||
|
|
a320d78a08 | ||
|
|
ce5730428c | ||
|
|
f7b9795014 | ||
|
|
3417fc5407 | ||
|
|
2649efe8b3 | ||
|
|
3af5897427 | ||
|
|
92877a6b30 | ||
|
|
fbdaffcbf6 | ||
|
|
ba111c6caa | ||
|
|
5c8760ac0a | ||
|
|
e0cca2a0d4 | ||
|
|
d69aba8ab7 | ||
|
|
a2ba7adc56 | ||
|
|
0881146a25 | ||
|
|
5224d6589e | ||
|
|
15eeec8ebf | ||
|
|
ed7f375d64 | ||
|
|
82da6b3217 | ||
|
|
8d83df9034 | ||
|
|
0029977ae1 | ||
|
|
0a481b3e31 | ||
|
|
70b3c2e240 | ||
|
|
1785035b96 | ||
|
|
69ab101ba3 | ||
|
|
d1e752cda1 | ||
|
|
e9d366f2b9 | ||
|
|
b168518c60 | ||
|
|
845506f7e4 | ||
|
|
9dd47b811b | ||
|
|
eafad7fe7f | ||
|
|
f36062859d | ||
|
|
949e26781a | ||
|
|
2d70fbac36 | ||
|
|
bb954a2e9a | ||
|
|
631423a251 | ||
|
|
c527f15744 | ||
|
|
8fdacc3ef7 | ||
|
|
02918785ba | ||
|
|
2d9e6bcaa2 | ||
|
|
f2570e1deb | ||
|
|
f9afbcbc59 | ||
|
|
51b4ef326d | ||
|
|
ad08df3d2f | ||
|
|
4d32e921f9 |
79
.github/workflows/build-debian.yml
vendored
79
.github/workflows/build-debian.yml
vendored
@@ -4,46 +4,45 @@ on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
submodules: 'recursive'
|
||||
- name: check_libobs_revision
|
||||
run: |
|
||||
sudo apt update
|
||||
sudo apt install libobs-dev
|
||||
mkdir source
|
||||
cd source
|
||||
dpkg -l libobs-dev | tr -s " "| grep libobs | cut -d" " -f3 > libobs.rev
|
||||
- name: install_frontend_header
|
||||
run: |
|
||||
[ -e /usr/include/obs/obs-frontend-api.h ] && { echo "ERROR: obs-frontend-api.h already in system. Maybe the package libobs-dev is installing it."; exit 1; }
|
||||
cd source
|
||||
LIBOBSREV=$(cat libobs.rev)
|
||||
sudo apt update
|
||||
sudo apt install devscripts
|
||||
dget -u http://archive.ubuntu.com/ubuntu/pool/universe/o/obs-studio/obs-studio_$LIBOBSREV.dsc
|
||||
cd ..
|
||||
sudo find -name obs-frontend-api.h -exec cp {} /usr/include/obs/ \;
|
||||
egrep '#include <obs.h>' /usr/include/obs/obs-frontend-api.h || { echo "ERROR: check if the sed commands are needed from now."; exit 1; }
|
||||
sudo sed -i 's/#include <obs.h>/#include <obs\/obs.h>/' /usr/include/obs/obs-frontend-api.h
|
||||
sudo sed -i 's/#include <util\/darray.h>/#include <obs\/util\/darray.h>/' /usr/include/obs/obs-frontend-api.h
|
||||
- name: create_tarball
|
||||
run: |
|
||||
cd ..
|
||||
tar --exclude=.git -cvzf obs-scene-switcher_0.1+testonly.orig.tar.gz SceneSwitcher
|
||||
- name: create_debian_dir
|
||||
run: |
|
||||
cp -a CI/linux/debian .
|
||||
- name: install_dependencies
|
||||
run: |
|
||||
# devscripts and libobs-dev are needed but they were already installed
|
||||
# from check_libobs_revision and install_frontend_header sections.
|
||||
sudo apt update
|
||||
sudo apt install cmake debhelper libcurl4-openssl-dev libxss-dev libxtst-dev qtbase5-dev libopencv-dev libprocps-dev
|
||||
- name: build
|
||||
run: |
|
||||
debuild --no-lintian --no-sign
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
submodules: "recursive"
|
||||
- name: check_libobs_revision
|
||||
run: |
|
||||
sudo apt update
|
||||
sudo apt install libobs-dev
|
||||
mkdir source
|
||||
cd source
|
||||
dpkg -l libobs-dev | tr -s " "| grep libobs | cut -d" " -f3 > libobs.rev
|
||||
- name: install_frontend_header
|
||||
run: |
|
||||
[ -e /usr/include/obs/obs-frontend-api.h ] && { echo "ERROR: obs-frontend-api.h already in system. Maybe the package libobs-dev is installing it."; exit 1; }
|
||||
cd source
|
||||
LIBOBSREV=$(cat libobs.rev)
|
||||
sudo apt update
|
||||
sudo apt install devscripts
|
||||
dget -u http://archive.ubuntu.com/ubuntu/pool/universe/o/obs-studio/obs-studio_$LIBOBSREV.dsc
|
||||
cd ..
|
||||
sudo find -name obs-frontend-api.h -exec cp {} /usr/include/obs/ \;
|
||||
egrep '#include <obs.h>' /usr/include/obs/obs-frontend-api.h || { echo "ERROR: check if the sed commands are needed from now."; exit 1; }
|
||||
sudo sed -i 's/#include <obs.h>/#include <obs\/obs.h>/' /usr/include/obs/obs-frontend-api.h
|
||||
sudo sed -i 's/#include <util\/darray.h>/#include <obs\/util\/darray.h>/' /usr/include/obs/obs-frontend-api.h
|
||||
- name: create_tarball
|
||||
run: |
|
||||
cd ..
|
||||
tar --exclude=.git -cvzf obs-scene-switcher_0.1+testonly.orig.tar.gz SceneSwitcher
|
||||
- name: create_debian_dir
|
||||
run: |
|
||||
cp -a CI/linux/debian .
|
||||
- name: install_dependencies
|
||||
run: |
|
||||
# devscripts and libobs-dev are needed but they were already installed
|
||||
# from check_libobs_revision and install_frontend_header sections.
|
||||
sudo apt update
|
||||
sudo apt install cmake debhelper libcurl4-openssl-dev libxss-dev libxtst-dev qtbase5-dev libopencv-dev libprocps-dev
|
||||
- name: build
|
||||
run: |
|
||||
debuild --no-lintian --no-sign
|
||||
|
||||
90
.github/workflows/build.yml
vendored
90
.github/workflows/build.yml
vendored
@@ -12,25 +12,25 @@ jobs:
|
||||
name: "macOS 64-bit"
|
||||
runs-on: [macos-latest]
|
||||
env:
|
||||
QT_VERSION: '5.15.2'
|
||||
MACOS_DEPS_VERSION: '2020-12-11'
|
||||
QT_VERSION: "5.15.2"
|
||||
MACOS_DEPS_VERSION: "2020-12-11"
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2.3.4
|
||||
with:
|
||||
repository: obsproject/obs-studio
|
||||
submodules: 'recursive'
|
||||
ref: 'refs/tags/${{ env.OBS_TAG }}'
|
||||
submodules: "recursive"
|
||||
ref: "refs/tags/${{ env.OBS_TAG }}"
|
||||
- name: "Checkout plugin"
|
||||
uses: actions/checkout@v2.3.4
|
||||
with:
|
||||
path: UI/frontend-plugins/${{ env.PLUGIN_NAME }}
|
||||
submodules: 'recursive'
|
||||
submodules: "recursive"
|
||||
- name: Fetch Git Tags
|
||||
run: |
|
||||
cd UI/frontend-plugins/${{ env.PLUGIN_NAME }}
|
||||
git fetch --prune --tags --unshallow
|
||||
- name: 'Install prerequisites (Homebrew)'
|
||||
- name: "Install prerequisites (Homebrew)"
|
||||
shell: bash
|
||||
run: |
|
||||
if [ -d /usr/local/opt/openssl@1.0.2t ]; then
|
||||
@@ -42,20 +42,20 @@ jobs:
|
||||
brew untap local/python2
|
||||
fi
|
||||
brew bundle --file ./CI/scripts/macos/Brewfile
|
||||
- name: 'Install prerequisite: Pre-built dependencies'
|
||||
- name: "Install prerequisite: Pre-built dependencies"
|
||||
if: steps.deps-cache.outputs.cache-hit != 'true'
|
||||
shell: bash
|
||||
run: |
|
||||
curl -L -O https://github.com/obsproject/obs-deps/releases/download/${{ env.MACOS_DEPS_VERSION }}/macos-deps-${{ env.MACOS_DEPS_VERSION }}.tar.gz
|
||||
tar -xf ./macos-deps-${{ env.MACOS_DEPS_VERSION }}.tar.gz -C "/tmp"
|
||||
- name: 'Install prerequisite: Pre-built dependency Qt'
|
||||
- name: "Install prerequisite: Pre-built dependency Qt"
|
||||
if: steps.deps-qt-cache.outputs.cache-hit != 'true'
|
||||
shell: bash
|
||||
run: |
|
||||
curl -L -O https://github.com/obsproject/obs-deps/releases/download/${{ env.MACOS_DEPS_VERSION }}/macos-qt-${{ env.QT_VERSION }}-${{ env.MACOS_DEPS_VERSION }}.tar.gz
|
||||
tar -xf ./macos-qt-${{ env.QT_VERSION }}-${{ env.MACOS_DEPS_VERSION }}.tar.gz -C "/tmp"
|
||||
xattr -r -d com.apple.quarantine /tmp/obsdeps
|
||||
- name: 'Build prerequisite: OpenCV'
|
||||
- name: "Build prerequisite: OpenCV"
|
||||
shell: bash
|
||||
run: |
|
||||
cd UI/frontend-plugins/${{ env.PLUGIN_NAME }}/deps/opencv
|
||||
@@ -79,7 +79,7 @@ jobs:
|
||||
cd ./build
|
||||
make -j4
|
||||
cd -
|
||||
- name: 'Install prerequisite: Packages app'
|
||||
- name: "Install prerequisite: Packages app"
|
||||
if: success()
|
||||
shell: bash
|
||||
run: |
|
||||
@@ -90,12 +90,16 @@ jobs:
|
||||
if: success()
|
||||
shell: bash
|
||||
run: |
|
||||
fix_linker_paths() {
|
||||
install_name_tool -change @rpath/libobs-frontend-api.dylib @executable_path/../Frameworks/libobs-frontend-api.dylib $1
|
||||
install_name_tool -change @rpath/libobs.0.dylib @executable_path/../Frameworks/libobs.0.dylib $1
|
||||
install_name_tool -change /tmp/obsdeps/lib/QtWidgets.framework/Versions/5/QtWidgets @executable_path/../Frameworks/QtWidgets.framework/Versions/5/QtWidgets $1
|
||||
install_name_tool -change /tmp/obsdeps/lib/QtGui.framework/Versions/5/QtGui @executable_path/../Frameworks/QtGui.framework/Versions/5/QtGui $1
|
||||
install_name_tool -change /tmp/obsdeps/lib/QtCore.framework/Versions/5/QtCore @executable_path/../Frameworks/QtCore.framework/Versions/5/QtCore $1
|
||||
}
|
||||
cd UI/frontend-plugins/${{ env.PLUGIN_NAME }}
|
||||
install_name_tool -change @rpath/libobs-frontend-api.dylib @executable_path/../Frameworks/libobs-frontend-api.dylib ../../../build/UI/frontend-plugins/SceneSwitcher/advanced-scene-switcher.so
|
||||
install_name_tool -change @rpath/libobs.0.dylib @executable_path/../Frameworks/libobs.0.dylib ../../../build/UI/frontend-plugins/SceneSwitcher/advanced-scene-switcher.so
|
||||
install_name_tool -change /tmp/obsdeps/lib/QtWidgets.framework/Versions/5/QtWidgets @executable_path/../Frameworks/QtWidgets.framework/Versions/5/QtWidgets ../../../build/UI/frontend-plugins/SceneSwitcher/advanced-scene-switcher.so
|
||||
install_name_tool -change /tmp/obsdeps/lib/QtGui.framework/Versions/5/QtGui @executable_path/../Frameworks/QtGui.framework/Versions/5/QtGui ../../../build/UI/frontend-plugins/SceneSwitcher/advanced-scene-switcher.so
|
||||
install_name_tool -change /tmp/obsdeps/lib/QtCore.framework/Versions/5/QtCore @executable_path/../Frameworks/QtCore.framework/Versions/5/QtCore ../../../build/UI/frontend-plugins/SceneSwitcher/advanced-scene-switcher.so
|
||||
fix_linker_paths ../../../build/UI/frontend-plugins/SceneSwitcher/advanced-scene-switcher.so
|
||||
fix_linker_paths ../../../build/UI/frontend-plugins/SceneSwitcher/src/external-macro-modules/opencv/advanced-scene-switcher-opencv.so
|
||||
FILE_DATE=$(date +%Y-%m-%d)
|
||||
FILE_NAME=${{ env.PLUGIN_NAME }}-$FILE_DATE-${{ github.sha }}-macos.pkg
|
||||
echo "FILE_NAME=${FILE_NAME}" >> $GITHUB_ENV
|
||||
@@ -107,23 +111,23 @@ jobs:
|
||||
if: success()
|
||||
uses: actions/upload-artifact@v2.2.1
|
||||
with:
|
||||
name: '${{ env.FILE_NAME }}'
|
||||
name: "${{ env.FILE_NAME }}"
|
||||
path: ./nightly/*.pkg
|
||||
ubuntu64:
|
||||
name: 'Linux/Ubuntu 64-bit'
|
||||
name: "Linux/Ubuntu 64-bit"
|
||||
runs-on: [ubuntu-latest]
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2.3.4
|
||||
with:
|
||||
repository: obsproject/obs-studio
|
||||
submodules: 'recursive'
|
||||
ref: 'refs/tags/${{ env.OBS_TAG }}'
|
||||
submodules: "recursive"
|
||||
ref: "refs/tags/${{ env.OBS_TAG }}"
|
||||
- name: "Checkout plugin"
|
||||
uses: actions/checkout@v2.3.4
|
||||
with:
|
||||
path: UI/frontend-plugins/${{ env.PLUGIN_NAME }}
|
||||
submodules: 'recursive'
|
||||
submodules: "recursive"
|
||||
- name: Add plugin to obs cmake
|
||||
shell: bash
|
||||
run: echo "add_subdirectory(${{ env.PLUGIN_NAME }})" >> UI/frontend-plugins/CMakeLists.txt
|
||||
@@ -181,17 +185,17 @@ jobs:
|
||||
libxcb-xfixes0-dev \
|
||||
libopencv-dev \
|
||||
libprocps-dev
|
||||
- name: 'Configure'
|
||||
- name: "Configure"
|
||||
shell: bash
|
||||
run: |
|
||||
mkdir ./build
|
||||
cd ./build
|
||||
cmake -DENABLE_PIPEWIRE=OFF -DUNIX_STRUCTURE=0 -DCMAKE_INSTALL_PREFIX="${{ github.workspace }}/obs-studio-portable" -DWITH_RTMPS=OFF -DBUILD_BROWSER=OFF ..
|
||||
- name: 'Build'
|
||||
- name: "Build"
|
||||
shell: bash
|
||||
working-directory: ${{ github.workspace }}/build
|
||||
run: make -j4
|
||||
- name: 'Package'
|
||||
- name: "Package"
|
||||
shell: bash
|
||||
run: |
|
||||
FILE_DATE=$(date +%Y-%m-%d)
|
||||
@@ -201,12 +205,15 @@ jobs:
|
||||
strip -d ./build/UI/frontend-plugins/${{ env.PLUGIN_NAME }}/${{ env.LIB_NAME }}.so
|
||||
mv ./build/UI/frontend-plugins/${{ env.PLUGIN_NAME }}/${{ env.LIB_NAME }}.so ./${{ env.LIB_NAME }}/bin/64bit/${{ env.LIB_NAME }}.so
|
||||
mv ./UI/frontend-plugins/${{ env.PLUGIN_NAME }}/data ./${{ env.LIB_NAME }}/data
|
||||
# Macro modules
|
||||
strip -d ./build/UI/frontend-plugins/${{ env.PLUGIN_NAME }}/src/external-macro-modules/opencv/advanced-scene-switcher-opencv.so
|
||||
mv ./build/UI/frontend-plugins/${{ env.PLUGIN_NAME }}/src/external-macro-modules/opencv/advanced-scene-switcher-opencv.so ./${{ env.LIB_NAME }}/bin/64bit/
|
||||
tar -cvzf "${FILE_NAME}" ${{ env.LIB_NAME }}
|
||||
- name: 'Publish'
|
||||
- name: "Publish"
|
||||
uses: actions/upload-artifact@v2.2.1
|
||||
with:
|
||||
name: '${{ env.FILE_NAME }}'
|
||||
path: '*.tar.gz'
|
||||
name: "${{ env.FILE_NAME }}"
|
||||
path: "*.tar.gz"
|
||||
windows:
|
||||
name: Windows
|
||||
runs-on: [windows-latest]
|
||||
@@ -217,7 +224,7 @@ jobs:
|
||||
QT_VERSION: 5.10.1
|
||||
CMAKE_GENERATOR: "Visual Studio 16 2019"
|
||||
CMAKE_SYSTEM_VERSION: "10.0.18363.657"
|
||||
WINDOWS_DEPS_VERSION: '2019'
|
||||
WINDOWS_DEPS_VERSION: "2019"
|
||||
steps:
|
||||
- name: Add msbuild to PATH
|
||||
uses: microsoft/setup-msbuild@v1.0.2
|
||||
@@ -225,27 +232,27 @@ jobs:
|
||||
uses: actions/checkout@v2.3.4
|
||||
with:
|
||||
repository: obsproject/obs-studio
|
||||
submodules: 'recursive'
|
||||
ref: 'refs/tags/${{ env.OBS_TAG }}'
|
||||
submodules: "recursive"
|
||||
ref: "refs/tags/${{ env.OBS_TAG }}"
|
||||
- name: Checkout plugin
|
||||
uses: actions/checkout@v2.3.4
|
||||
with:
|
||||
path: UI/frontend-plugins/${{ env.PLUGIN_NAME}}
|
||||
submodules: 'recursive'
|
||||
submodules: "recursive"
|
||||
- name: Add plugin to obs cmake
|
||||
shell: cmd
|
||||
run: echo add_subdirectory(${{ env.PLUGIN_NAME }}) >> UI/frontend-plugins/CMakeLists.txt
|
||||
- name: Fetch Git Tags
|
||||
run: git fetch --prune --tags --unshallow
|
||||
- name: 'Install prerequisite: QT'
|
||||
- name: "Install prerequisite: QT"
|
||||
run: |
|
||||
curl -kLO https://cdn-fastly.obsproject.com/downloads/Qt_${{ env.QT_VERSION }}.7z -f --retry 5 -C -
|
||||
7z x Qt_${{ env.QT_VERSION }}.7z -o"${{ github.workspace }}/cmbuild/QT"
|
||||
- name: 'Install prerequisite: Pre-built dependencies'
|
||||
- name: "Install prerequisite: Pre-built dependencies"
|
||||
run: |
|
||||
curl -kLO https://cdn-fastly.obsproject.com/downloads/dependencies${{ env.WINDOWS_DEPS_VERSION }}.zip -f --retry 5 -C -
|
||||
7z x dependencies${{ env.WINDOWS_DEPS_VERSION }}.zip -o"${{ github.workspace }}/cmbuild/deps"
|
||||
- name: 'Build prerequisite: OpenCV'
|
||||
- name: "Build prerequisite: OpenCV"
|
||||
run: |
|
||||
cd UI/frontend-plugins/${{ env.PLUGIN_NAME }}/deps/opencv
|
||||
mkdir build
|
||||
@@ -283,19 +290,20 @@ jobs:
|
||||
$env:FILE_DATE=(Get-Date -UFormat "%F")
|
||||
$env:FILE_NAME="${{ env.PLUGIN_NAME }}-${env:FILE_DATE}-${{ github.sha }}-windows"
|
||||
echo "FILE_NAME=${env:FILE_NAME}" >> ${env:GITHUB_ENV}
|
||||
robocopy .\build${{ matrix.arch }}\rundir\RelWithDebInfo\obs-plugins\${{ matrix.arch }}bit\ .\package\obs-plugins\${{ matrix.arch }}bit ${{ env.LIB_NAME }}.* /E /XF .gitignore
|
||||
robocopy .\build${{ matrix.arch }}\rundir\RelWithDebInfo\obs-plugins\${{ matrix.arch }}bit\ .\package\obs-plugins\${{ matrix.arch }}bit ${{ env.LIB_NAME }}* /E /XF .gitignore
|
||||
robocopy .\build${{ matrix.arch }}\rundir\RelWithDebInfo\data\obs-plugins\${{ env.LIB_NAME }}\ .\package\data\obs-plugins\${{ env.LIB_NAME }}\ /E /XF .gitignore
|
||||
cp UI/frontend-plugins/${{ env.PLUGIN_NAME }}/deps/opencv/build/bin/Release/*dll package/obs-plugins/${{ matrix.arch }}bit
|
||||
cp UI/frontend-plugins/${{ env.PLUGIN_NAME }}/deps/openvr/bin/win${{ matrix.arch }}/*dll package/obs-plugins/${{ matrix.arch }}bit
|
||||
exit 0
|
||||
- name: Publish zip
|
||||
if: success()
|
||||
uses: actions/upload-artifact@v2.2.1
|
||||
with:
|
||||
name: '${{ env.FILE_NAME }}-${{ matrix.arch }}bit'
|
||||
name: "${{ env.FILE_NAME }}-${{ matrix.arch }}bit"
|
||||
path: package/*
|
||||
windows-installer:
|
||||
needs: [windows]
|
||||
name: 'Create Windows Installer'
|
||||
name: "Create Windows Installer"
|
||||
runs-on: [windows-latest]
|
||||
steps:
|
||||
- name: "Checkout plugin"
|
||||
@@ -332,12 +340,12 @@ jobs:
|
||||
if: success()
|
||||
uses: actions/upload-artifact@v2.2.0
|
||||
with:
|
||||
name: '${{ env.FILE_NAME }}-installer'
|
||||
name: "${{ env.FILE_NAME }}-installer"
|
||||
path: build/CI/windows/Output/*.exe
|
||||
release:
|
||||
needs: [macos64, ubuntu64, windows, windows-installer]
|
||||
name: 'Create Release'
|
||||
runs-on: [ubuntu-18.04]
|
||||
name: "Create Release"
|
||||
runs-on: [ubuntu-latest]
|
||||
steps:
|
||||
- name: "Checkout plugin"
|
||||
uses: actions/checkout@v2.3.4
|
||||
@@ -347,7 +355,7 @@ jobs:
|
||||
uses: actions/download-artifact@v2
|
||||
with:
|
||||
path: artifacts
|
||||
- name: 'Package'
|
||||
- name: "Package"
|
||||
shell: bash
|
||||
run: |
|
||||
mkdir -p ${{ env.PLUGIN_NAME }}/Linux ${{ env.PLUGIN_NAME }}/MacOs ${{ env.PLUGIN_NAME }}/Windows
|
||||
@@ -366,4 +374,4 @@ jobs:
|
||||
uses: actions/upload-artifact@v2.2.1
|
||||
with:
|
||||
name: "Release"
|
||||
path: '*.zip'
|
||||
path: "*.zip"
|
||||
|
||||
3
.gitmodules
vendored
3
.gitmodules
vendored
@@ -7,3 +7,6 @@
|
||||
[submodule "deps/opencv"]
|
||||
path = deps/opencv
|
||||
url = https://github.com/opencv/opencv.git
|
||||
[submodule "deps/openvr"]
|
||||
path = deps/openvr
|
||||
url = https://github.com/ValveSoftware/openvr.git
|
||||
|
||||
@@ -46,6 +46,22 @@
|
||||
<key>UID</key>
|
||||
<integer>0</integer>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>CHILDREN</key>
|
||||
<array/>
|
||||
<key>GID</key>
|
||||
<integer>80</integer>
|
||||
<key>PATH</key>
|
||||
<string>../../../../../build/UI/frontend-plugins/SceneSwitcher/src/external-macro-modules/opencv/advanced-scene-switcher-opencv.so</string>
|
||||
<key>PATH_TYPE</key>
|
||||
<integer>3</integer>
|
||||
<key>PERMISSIONS</key>
|
||||
<integer>493</integer>
|
||||
<key>TYPE</key>
|
||||
<integer>3</integer>
|
||||
<key>UID</key>
|
||||
<integer>0</integer>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>CHILDREN</key>
|
||||
<array/>
|
||||
|
||||
@@ -19,7 +19,14 @@ Optional:
|
||||
* libXtst
|
||||
* libcurl
|
||||
|
||||
Copy the advanced-scene-switcher.so file and into the OBS Studio plugin folder.
|
||||
If you are using snap you can install the following package for an OBS installation which comes bundled with the plugin:
|
||||
sudo snap install obs-studio
|
||||
|
||||
The plugin is also available via the Flatpak package manager for users who installed OBS via Flatpak:
|
||||
flatpak install com.obsproject.Studio.Plugin.SceneSwitcher
|
||||
|
||||
If you have installed OBS via other means, it is most likely necessary to install the plugin manually.
|
||||
To do so copy the advanced-scene-switcher.so file and into the OBS Studio plugin folder.
|
||||
The location of this folder can vary, so you might have to look around a bit.
|
||||
|
||||
Examples are ...
|
||||
@@ -30,13 +37,39 @@ Examples are ...
|
||||
~/.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:
|
||||
~/.config/obs-studio/plugins/advanced-scene-switcher/
|
||||
Unfortunately the expected location of the "locale" and "res" folder, which can be found in the data folder, can vary also.
|
||||
The data folder usually should be copied to "~/.config/obs-studio/plugins/advanced-scene-switcher/" and reflect the following structure:
|
||||
|
||||
/home/user/.config/obs-studio/plugins/
|
||||
└── advanced-scene-switcher
|
||||
├── bin
|
||||
│ └── 64bit
|
||||
│ └── advanced-scene-switcher.so
|
||||
└── data
|
||||
├── locale
|
||||
│ ├── de-DE.ini
|
||||
│ ├── en-US.ini
|
||||
│ ├── ru-RU.ini
|
||||
│ └── zh-CN.ini
|
||||
└── res
|
||||
├── cascadeClassifiers
|
||||
│ ├── haarcascade_eye_tree_eyeglasses.xml
|
||||
│ ├── haarcascade_eye.xml
|
||||
│ ├── haarcascade_frontalface_alt2.xml
|
||||
│ ├── haarcascade_frontalface_alt_tree.xml
|
||||
│ ├── haarcascade_frontalface_alt.xml
|
||||
│ ├── haarcascade_frontalface_default.xml
|
||||
│ ├── haarcascade_fullbody.xml
|
||||
│ ├── haarcascade_lefteye_2splits.xml
|
||||
│ ├── haarcascade_lowerbody.xml
|
||||
│ ├── haarcascade_profileface.xml
|
||||
│ ├── haarcascade_righteye_2splits.xml
|
||||
│ └── haarcascade_upperbody.xml
|
||||
└── time.svg
|
||||
|
||||
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.
|
||||
In doubt, please check where other "en-US.ini" files are located on your system.
|
||||
|
||||
@@ -14,7 +14,7 @@ AppVersion={#MyAppVersion}
|
||||
AppPublisherURL={#MyAppURL}
|
||||
AppSupportURL={#MyAppURL}
|
||||
AppUpdatesURL={#MyAppURL}
|
||||
DefaultDirName={code:GetDirName}
|
||||
DefaultDirName={code:GetDefaultDirectory}
|
||||
DefaultGroupName={#MyAppName}
|
||||
AllowNoIcons=yes
|
||||
; Uncomment the following line to run in non administrative install mode (install for current user only.)
|
||||
@@ -38,18 +38,18 @@ Source: "@ISS_PLUGIN_FILES_DIR@\*"; DestDir: "{app}"; Flags: ignoreversion recur
|
||||
Name: "{group}\{cm:UninstallProgram,{#MyAppName}}"; Filename: "{uninstallexe}"
|
||||
|
||||
[Code]
|
||||
// credit where it's due:
|
||||
// following function comes from https://github.com/Xaymar/obs-studio_amf-encoder-plugin/blob/master/%23Resources/Installer.in.iss#L45
|
||||
function GetDirName(Value: string): string;
|
||||
function GetDefaultDirectory(Value: String): String;
|
||||
var
|
||||
InstallPath: string;
|
||||
sInstallPath: String;
|
||||
begin
|
||||
// initialize default path, which will be returned when the following registry
|
||||
// key queries fail due to missing keys or for some different reason
|
||||
Result := ExpandConstant('{pf}\obs-studio');
|
||||
// query the first registry value; if this succeeds, return the obtained value
|
||||
if RegQueryStringValue(HKLM32, 'SOFTWARE\OBS Studio', '', InstallPath) then
|
||||
Result := InstallPath
|
||||
sInstallPath := Value;
|
||||
if (sInstallPath = '') then
|
||||
RegQueryStringValue(HKLM64, 'SOFTWARE\OBS Studio', '', sInstallPath);
|
||||
if (sInstallPath = '') then
|
||||
RegQueryStringValue(HKCU64, 'SOFTWARE\OBS Studio', '', sInstallPath);
|
||||
if (sInstallPath = '') then
|
||||
sInstallPath := ExpandConstant('{commonpf}\obs-studio');
|
||||
Result := sInstallPath
|
||||
end;
|
||||
|
||||
procedure CurStepChanged(CurStep: TSetupStep);
|
||||
|
||||
@@ -8,6 +8,7 @@ endif()
|
||||
# Compiler settings
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED YES)
|
||||
set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON)
|
||||
|
||||
if(CMAKE_COMPILER_IS_GNUCC
|
||||
OR CMAKE_COMPILER_IS_GNUCXX
|
||||
@@ -101,24 +102,6 @@ if(BUILD_OUT_OF_TREE)
|
||||
|
||||
find_package(CURL REQUIRED)
|
||||
include_directories("${CURL_INCLUDE_DIRS}")
|
||||
|
||||
# Backwards compatability with older OBS versions
|
||||
if(LibObs_VERSION_MAJOR GREATER_EQUAL 27)
|
||||
add_definitions(-DVCAM_SUPPORTED)
|
||||
else()
|
||||
message(
|
||||
WARNING
|
||||
"OBS version ${LibObs_VERSION_MAJOR} found - disabling virtual camera functionality"
|
||||
)
|
||||
endif()
|
||||
if(LibObs_VERSION_MAJOR GREATER_EQUAL 26)
|
||||
add_definitions(-DREPLAYBUFFER_SUPPORTED)
|
||||
else()
|
||||
message(
|
||||
WARNING
|
||||
"OBS version ${LibObs_VERSION_MAJOR} found - disabling replay buffer functionality"
|
||||
)
|
||||
endif()
|
||||
else()
|
||||
find_package(Libcurl REQUIRED)
|
||||
include_directories("${LIBCURL_INCLUDE_DIRS}")
|
||||
@@ -128,6 +111,7 @@ endif()
|
||||
|
||||
# Platform specific settings
|
||||
if(APPLE)
|
||||
set(CMAKE_SHARED_LIBRARY_SUFFIX ".so")
|
||||
find_library(COCOA Cocoa)
|
||||
if(BUILD_OUT_OF_TREE)
|
||||
find_package(Qt5MacExtras REQUIRED)
|
||||
@@ -215,17 +199,21 @@ set(advanced-scene-switcher_HEADERS
|
||||
src/headers/macro-action-media.hpp
|
||||
src/headers/macro-action-plugin-state.hpp
|
||||
src/headers/macro-action-preview-scene.hpp
|
||||
src/headers/macro-action-profile.hpp
|
||||
src/headers/macro-action-random.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-collection.hpp
|
||||
src/headers/macro-action-scene-order.hpp
|
||||
src/headers/macro-action-scene-swap.hpp
|
||||
src/headers/macro-action-scene-switch.hpp
|
||||
src/headers/macro-action-scene-transform.hpp
|
||||
src/headers/macro-action-scene-visibility.hpp
|
||||
src/headers/macro-action-screenshot.hpp
|
||||
src/headers/macro-action-source.hpp
|
||||
src/headers/macro-action-streaming.hpp
|
||||
src/headers/macro-action-systray.hpp
|
||||
src/headers/macro-action-timer.hpp
|
||||
src/headers/macro-action-transition.hpp
|
||||
src/headers/macro-action-virtual-cam.hpp
|
||||
@@ -250,9 +238,9 @@ set(advanced-scene-switcher_HEADERS
|
||||
src/headers/macro-condition-scene.hpp
|
||||
src/headers/macro-condition-source.hpp
|
||||
src/headers/macro-condition-streaming.hpp
|
||||
src/headers/macro-condition-studio-mode.hpp
|
||||
src/headers/macro-condition-timer.hpp
|
||||
src/headers/macro-condition-transition.hpp
|
||||
src/headers/macro-condition-video.hpp
|
||||
src/headers/macro-condition-virtual-cam.hpp
|
||||
src/headers/macro-condition-window.hpp
|
||||
src/headers/macro.hpp
|
||||
@@ -261,6 +249,7 @@ set(advanced-scene-switcher_HEADERS
|
||||
src/headers/macro-selection.hpp
|
||||
src/headers/curl-helper.hpp
|
||||
src/headers/hotkey.hpp
|
||||
src/headers/scene-item-selection.hpp
|
||||
src/headers/scene-selection.hpp
|
||||
src/headers/screenshot-helper.hpp
|
||||
src/headers/transition-selection.hpp
|
||||
@@ -268,6 +257,7 @@ set(advanced-scene-switcher_HEADERS
|
||||
src/headers/duration-control.hpp
|
||||
src/headers/file-selection.hpp
|
||||
src/headers/section.hpp
|
||||
src/headers/status-dock.hpp
|
||||
src/headers/platform-funcs.hpp
|
||||
src/headers/utility.hpp
|
||||
src/headers/volume-control.hpp
|
||||
@@ -308,17 +298,21 @@ set(advanced-scene-switcher_SOURCES
|
||||
src/macro-action-media.cpp
|
||||
src/macro-action-plugin-state.cpp
|
||||
src/macro-action-preview-scene.cpp
|
||||
src/macro-action-profile.cpp
|
||||
src/macro-action-random.cpp
|
||||
src/macro-action-recording.cpp
|
||||
src/macro-action-replay-buffer.cpp
|
||||
src/macro-action-run.cpp
|
||||
src/macro-action-scene-collection.cpp
|
||||
src/macro-action-scene-order.cpp
|
||||
src/macro-action-scene-swap.cpp
|
||||
src/macro-action-scene-switch.cpp
|
||||
src/macro-action-scene-transform.cpp
|
||||
src/macro-action-scene-visibility.cpp
|
||||
src/macro-action-screenshot.cpp
|
||||
src/macro-action-source.cpp
|
||||
src/macro-action-streaming.cpp
|
||||
src/macro-action-systray.cpp
|
||||
src/macro-action-timer.cpp
|
||||
src/macro-action-transition.cpp
|
||||
src/macro-action-virtual-cam.cpp
|
||||
@@ -343,9 +337,9 @@ set(advanced-scene-switcher_SOURCES
|
||||
src/macro-condition-scene.cpp
|
||||
src/macro-condition-source.cpp
|
||||
src/macro-condition-streaming.cpp
|
||||
src/macro-condition-studio-mode.cpp
|
||||
src/macro-condition-timer.cpp
|
||||
src/macro-condition-transition.cpp
|
||||
src/macro-condition-video.cpp
|
||||
src/macro-condition-virtual-cam.cpp
|
||||
src/macro-condition-window.cpp
|
||||
src/macro.cpp
|
||||
@@ -354,34 +348,44 @@ set(advanced-scene-switcher_SOURCES
|
||||
src/macro-selection.cpp
|
||||
src/macro-tab.cpp
|
||||
src/curl-helper.cpp
|
||||
src/scene-item-selection.cpp
|
||||
src/scene-selection.cpp
|
||||
src/screenshot-helper.cpp
|
||||
src/transition-selection.cpp
|
||||
src/name-dialog.cpp
|
||||
src/duration-control.cpp
|
||||
src/status-dock.cpp
|
||||
src/section.cpp
|
||||
src/utility.cpp
|
||||
src/volume-control.cpp
|
||||
src/version.cpp)
|
||||
|
||||
# opencv
|
||||
find_package(OpenCV)
|
||||
if(OpenCV_FOUND)
|
||||
include_directories("${OpenCV_INCLUDE_DIRS}")
|
||||
else()
|
||||
set(OpenCV_LIBRARIES "")
|
||||
message(
|
||||
WARNING
|
||||
"OpenCV not found! Functionality relying on OpenCV will be disabled!\nOpenCV sources are available under: ${CMAKE_CURRENT_SOURCE_DIR}/deps/opencv"
|
||||
)
|
||||
list(REMOVE_ITEM advanced-scene-switcher_SOURCES
|
||||
src/macro-condition-video.cpp)
|
||||
list(REMOVE_ITEM advanced-scene-switcher_HEADERS
|
||||
src/headers/macro-condition-video.hpp)
|
||||
# Backwards compatability checks with older OBS versions
|
||||
if(DEFINED LibObs_VERSION_MAJOR)
|
||||
if(LibObs_VERSION_MAJOR GREATER_EQUAL 27)
|
||||
add_definitions(-DVCAM_SUPPORTED)
|
||||
else()
|
||||
message(
|
||||
WARNING
|
||||
"OBS version ${LibObs_VERSION_MAJOR} found - disabling virtual camera functionality"
|
||||
)
|
||||
endif()
|
||||
if(LibObs_VERSION_MAJOR GREATER_EQUAL 26)
|
||||
add_definitions(-DREPLAYBUFFER_SUPPORTED)
|
||||
else()
|
||||
message(
|
||||
WARNING
|
||||
"OBS version ${LibObs_VERSION_MAJOR} found - disabling replay buffer and screenshot functionality"
|
||||
)
|
||||
list(REMOVE_ITEM advanced-scene-switcher_SOURCES
|
||||
src/macro-action-screenshot.cpp)
|
||||
list(REMOVE_ITEM advanced-scene-switcher_HEADERS
|
||||
src/headers/macro-action-screenshot.hpp)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
add_library(
|
||||
advanced-scene-switcher MODULE
|
||||
advanced-scene-switcher SHARED
|
||||
${advanced-scene-switcher_HEADERS}
|
||||
${advanced-scene-switcher_SOURCES}
|
||||
${advanced-scene-switcher_UI_HEADERS}
|
||||
@@ -391,13 +395,8 @@ add_library(
|
||||
# Out of tree build
|
||||
if(BUILD_OUT_OF_TREE)
|
||||
target_link_libraries(
|
||||
advanced-scene-switcher
|
||||
${advanced-scene-switcher_PLATFORM_LIBS}
|
||||
${OpenCV_LIBRARIES}
|
||||
${LIBOBS_LIB}
|
||||
${LIBOBS_FRONTEND_API_LIB}
|
||||
Qt5::Core
|
||||
Qt5::Widgets)
|
||||
advanced-scene-switcher ${advanced-scene-switcher_PLATFORM_LIBS}
|
||||
${LIBOBS_LIB} ${LIBOBS_FRONTEND_API_LIB} Qt5::Core Qt5::Widgets)
|
||||
|
||||
# Additional commands to install the module in the correct place. Find all the
|
||||
# translation files so we can copy them to the correct place later on.
|
||||
@@ -428,6 +427,8 @@ else()
|
||||
# In tree build
|
||||
target_link_libraries(
|
||||
advanced-scene-switcher ${advanced-scene-switcher_PLATFORM_LIBS}
|
||||
${OpenCV_LIBRARIES} obs-frontend-api Qt5::Widgets libobs)
|
||||
obs-frontend-api Qt5::Widgets libobs)
|
||||
install_obs_plugin_with_data(advanced-scene-switcher data)
|
||||
endif()
|
||||
|
||||
add_subdirectory(src/external-macro-modules)
|
||||
|
||||
@@ -15,6 +15,10 @@ For Linux the Snap package manager offers an OBS Studio installation which is bu
|
||||
```
|
||||
sudo snap install obs-studio
|
||||
```
|
||||
The plugin is also available via the Flatpak package manager for users who installed OBS via Flatpak:
|
||||
```
|
||||
flatpak install com.obsproject.Studio.Plugin.SceneSwitcher
|
||||
```
|
||||
|
||||
If that is not an option you will have to ...
|
||||
1. Copy the library to the plugins folder of you obs installation.
|
||||
|
||||
@@ -19,12 +19,12 @@ AdvSceneSwitcher.generalTab.status.autoStart.streaming="Streaming"
|
||||
AdvSceneSwitcher.generalTab.status.autoStart.recordingAndStreaming="Recording or Streaming"
|
||||
AdvSceneSwitcher.generalTab.status.checkInterval="Check switch conditions every"
|
||||
AdvSceneSwitcher.generalTab.generalBehavior="General behavior"
|
||||
AdvSceneSwitcher.generalTab.generalBehavior.onNoMet="If no switch condition is met for"
|
||||
AdvSceneSwitcher.generalTab.generalBehavior.onNoMet="If no actions are performed for"
|
||||
AdvSceneSwitcher.generalTab.generalBehavior.onNoMetDelayTooltip="Will only ever be as accurate as the configured check interval."
|
||||
AdvSceneSwitcher.generalTab.generalBehavior.onNoMet.dontSwitch="Don't switch"
|
||||
AdvSceneSwitcher.generalTab.generalBehavior.onNoMet.switchToRandom="Switch to any scene in Random tab"
|
||||
AdvSceneSwitcher.generalTab.generalBehavior.onNoMet.switchTo="Switch to:"
|
||||
AdvSceneSwitcher.generalTab.generalBehavior.cooldown="After a match do not switch scenes for"
|
||||
AdvSceneSwitcher.generalTab.generalBehavior.cooldown="After a match don't perform actions 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"
|
||||
@@ -57,6 +57,7 @@ AdvSceneSwitcher.generalTab.priority.macro="Macro"
|
||||
; Macro Tab
|
||||
AdvSceneSwitcher.macroTab.title="Macro"
|
||||
AdvSceneSwitcher.macroTab.macros="Macros"
|
||||
AdvSceneSwitcher.macroTab.priorityWarning="Note: It is recommended to configure macros to be the highest priority functionality.\nThis setting can be changed on the General tab."
|
||||
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."
|
||||
@@ -66,6 +67,10 @@ AdvSceneSwitcher.macroTab.edit.condition="Condition type:"
|
||||
AdvSceneSwitcher.macroTab.edit.action="Action type:"
|
||||
AdvSceneSwitcher.macroTab.add="Add new macro"
|
||||
AdvSceneSwitcher.macroTab.name="Name:"
|
||||
AdvSceneSwitcher.macroTab.run="Run macro"
|
||||
AdvSceneSwitcher.macroTab.runFail="Running \"%1\" failed!\nEither one of the actions failed or the macro is running already."
|
||||
AdvSceneSwitcher.macroTab.runInParallel="Run macro in parallel to other macros"
|
||||
AdvSceneSwitcher.macroTab.onChange="Perform actions only on condition change"
|
||||
AdvSceneSwitcher.macroTab.defaultname="Macro %1"
|
||||
AdvSceneSwitcher.macroTab.exists="Macro name exists already"
|
||||
AdvSceneSwitcher.macroTab.copy="Create copy"
|
||||
@@ -83,8 +88,10 @@ 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.state.below="below"
|
||||
AdvSceneSwitcher.condition.audio.state.above="above"
|
||||
AdvSceneSwitcher.condition.audio.state.mute="muted"
|
||||
AdvSceneSwitcher.condition.audio.state.unmute="unmuted"
|
||||
AdvSceneSwitcher.condition.audio.entry="Volume of {{audioSources}} is {{condition}} {{volume}}"
|
||||
AdvSceneSwitcher.condition.cursor="Cursor"
|
||||
AdvSceneSwitcher.condition.cursor.type.region="is in region"
|
||||
@@ -98,8 +105,9 @@ AdvSceneSwitcher.condition.scene.type.current="Current scene is"
|
||||
AdvSceneSwitcher.condition.scene.type.previous="Previous scene is"
|
||||
AdvSceneSwitcher.condition.scene.type.changed="Scene changed"
|
||||
AdvSceneSwitcher.condition.scene.type.notChanged="Scene has not changed"
|
||||
AdvSceneSwitcher.condition.scene.waitForTransition="Wait for transition to complete"
|
||||
AdvSceneSwitcher.condition.scene.entry="{{sceneType}} {{scenes}} {{waitForTransition}}"
|
||||
AdvSceneSwitcher.condition.scene.currentSceneTransitionBehaviour="During transition check for transition target scene"
|
||||
AdvSceneSwitcher.condition.scene.entry.line1="{{sceneType}} {{scenes}}"
|
||||
AdvSceneSwitcher.condition.scene.entry.line2="{{useTransitionTargetScene}}"
|
||||
AdvSceneSwitcher.condition.window="Window"
|
||||
AdvSceneSwitcher.condition.window.entry.line1="{{windows}} exist and ..."
|
||||
AdvSceneSwitcher.condition.window.entry.line2="... is {{fullscreen}} fullscreen {{maximized}} maximized {{focused}} focused {{windowFocusChanged}} foreground window changed"
|
||||
@@ -108,7 +116,10 @@ AdvSceneSwitcher.condition.file.entry.line1="Content of {{fileType}} {{filePath}
|
||||
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.media.anyOnScene="Any media source on"
|
||||
AdvSceneSwitcher.condition.media.allOnScene="All media sources on"
|
||||
AdvSceneSwitcher.condition.media.matchOnChange="Only match on change (Note: This option will be removed in a future version - please use time constraints instead)"
|
||||
AdvSceneSwitcher.condition.media.entry="{{mediaSources}}{{scenes}} 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"
|
||||
@@ -156,11 +167,15 @@ 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.state.running="Advanced scene switcher is running"
|
||||
AdvSceneSwitcher.condition.pluginState.entry="{{condition}}"
|
||||
AdvSceneSwitcher.condition.timer="Timer"
|
||||
AdvSceneSwitcher.condition.timer.type.fixed="Fixed"
|
||||
AdvSceneSwitcher.condition.timer.type.random="Random"
|
||||
AdvSceneSwitcher.condition.timer.pause="Pause"
|
||||
AdvSceneSwitcher.condition.timer.continue="Continue"
|
||||
AdvSceneSwitcher.condition.timer.entry.line1="{{duration}} have passed"
|
||||
AdvSceneSwitcher.condition.timer.entry.line1.fixed="{{type}} duration of {{duration}} has passed"
|
||||
AdvSceneSwitcher.condition.timer.entry.line1.random="{{type}} duration from {{duration}} to {{duration2}} has passed"
|
||||
AdvSceneSwitcher.condition.timer.entry.line2="Time remaining: {{remaining}} seconds"
|
||||
AdvSceneSwitcher.condition.timer.entry.line3="{{pauseContinue}} {{reset}} {{saveRemaining}} Save time remaining {{autoReset}} Automatically reset timer after duration was reached"
|
||||
AdvSceneSwitcher.condition.timer.reset="Reset"
|
||||
@@ -203,7 +218,7 @@ AdvSceneSwitcher.condition.sceneOrder.type.above="Is above"
|
||||
AdvSceneSwitcher.condition.sceneOrder.type.below="Is below"
|
||||
AdvSceneSwitcher.condition.sceneOrder.type.position="Is at position"
|
||||
AdvSceneSwitcher.condition.sceneOrder.positionInfo="The position value starts at the bottom with 0 and increases by one for each scene item including the ones in scene groups"
|
||||
AdvSceneSwitcher.condition.sceneOrder.entry="On {{scenes}} {{sources}} {{conditions}} {{sources2}} {{position}}"
|
||||
AdvSceneSwitcher.condition.sceneOrder.entry="On{{scenes}}{{sources}}{{conditions}}{{sources2}}{{position}}"
|
||||
AdvSceneSwitcher.condition.hotkey="Hotkey"
|
||||
AdvSceneSwitcher.condition.hotkey.name="Macro trigger hotkey"
|
||||
AdvSceneSwitcher.condition.hotkey.tip="Note: You can configure the keybindings for this hotkey in the OBS settings window"
|
||||
@@ -223,24 +238,37 @@ AdvSceneSwitcher.condition.date.entry.line2="{{repeat}} Repeat every {{duration}
|
||||
AdvSceneSwitcher.condition.sceneTransform="Scene item transform"
|
||||
AdvSceneSwitcher.condition.sceneTransform.getTransform="Get transform"
|
||||
AdvSceneSwitcher.condition.sceneTransform.regex="Use regular expressions"
|
||||
AdvSceneSwitcher.condition.sceneTransform.entry.line1="On {{scenes}} {{sources}} matches transform"
|
||||
AdvSceneSwitcher.condition.sceneTransform.entry.line1="On{{scenes}}{{sources}}matches transform"
|
||||
AdvSceneSwitcher.condition.sceneTransform.entry.line2="{{settings}}"
|
||||
AdvSceneSwitcher.condition.sceneTransform.entry.line3="{{regex}} {{getSettings}}"
|
||||
AdvSceneSwitcher.condition.transition="Transition"
|
||||
AdvSceneSwitcher.condition.transition.type.current="Current transition type is"
|
||||
AdvSceneSwitcher.condition.transition.type.duration="Current transition duration is"
|
||||
AdvSceneSwitcher.condition.transition.type.started="Transition active"
|
||||
AdvSceneSwitcher.condition.transition.type.started="Transition started"
|
||||
AdvSceneSwitcher.condition.transition.type.ended="Transition ended"
|
||||
AdvSceneSwitcher.condition.transition.type.transitionSource="Transitioning from"
|
||||
AdvSceneSwitcher.condition.transition.type.transitionTarget="Transitioning to"
|
||||
AdvSceneSwitcher.condition.transition.durationSuffix="seconds"
|
||||
AdvSceneSwitcher.condition.transition.entry="{{conditions}}{{transitions}}{{duration}}{{durationSuffix}}"
|
||||
AdvSceneSwitcher.condition.transition.entry="{{conditions}}{{transitions}}{{scenes}}{{duration}}{{durationSuffix}}"
|
||||
AdvSceneSwitcher.condition.sceneVisibility="Scene item visibility"
|
||||
AdvSceneSwitcher.condition.sceneVisibility.type.shown="Shown"
|
||||
AdvSceneSwitcher.condition.sceneVisibility.type.hidden="Hidden"
|
||||
AdvSceneSwitcher.condition.sceneVisibility.entry="On {{scenes}} {{sources}} is {{conditions}} "
|
||||
AdvSceneSwitcher.condition.sceneVisibility.entry="On{{scenes}}{{sources}}is{{conditions}}"
|
||||
AdvSceneSwitcher.condition.studioMode="Studio mode"
|
||||
AdvSceneSwitcher.condition.studioMode.state.active="Studio mode is active"
|
||||
AdvSceneSwitcher.condition.studioMode.state.notActive="Studio mode is not active"
|
||||
AdvSceneSwitcher.condition.studioMode.state.previewScene="Preview scene is"
|
||||
AdvSceneSwitcher.condition.studioMode.entry="{{conditions}}{{scenes}}"
|
||||
AdvSceneSwitcher.condition.openvr="OpenVR"
|
||||
AdvSceneSwitcher.condition.errorStatus="OpenVR error: "
|
||||
AdvSceneSwitcher.condition.openvr.entry.line1="HMD is in ..."
|
||||
AdvSceneSwitcher.condition.openvr.entry.line2="{{controls}}"
|
||||
AdvSceneSwitcher.condition.openvr.entry.line3="HMD is currently at {{xPos}} x {{yPos}} x {{zPos}}"
|
||||
|
||||
; 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.scene.blockUntilTransitionDone="Wait until transition to target scene is complete"
|
||||
AdvSceneSwitcher.action.wait="Wait"
|
||||
AdvSceneSwitcher.action.wait.type.fixed="fixed"
|
||||
AdvSceneSwitcher.action.wait.type.random="random"
|
||||
@@ -279,7 +307,7 @@ AdvSceneSwitcher.action.sceneVisibility.type.show="Show"
|
||||
AdvSceneSwitcher.action.sceneVisibility.type.hide="Hide"
|
||||
AdvSceneSwitcher.action.sceneVisibility.type.source="Source"
|
||||
AdvSceneSwitcher.action.sceneVisibility.type.sourceGroup="Any"
|
||||
AdvSceneSwitcher.action.sceneVisibility.entry="On {{scenes}} {{actions}} {{sourceTypes}} {{sources}}"
|
||||
AdvSceneSwitcher.action.sceneVisibility.entry="On{{scenes}}{{actions}}{{sourceTypes}}{{sources}}{{sourceGroups}}"
|
||||
AdvSceneSwitcher.action.filter="Filter"
|
||||
AdvSceneSwitcher.action.filter.type.enable="Enable"
|
||||
AdvSceneSwitcher.action.filter.type.disable="Disable"
|
||||
@@ -305,11 +333,14 @@ 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.type.run="Run"
|
||||
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.type.noMatch="Change the no-match behaviour:"
|
||||
AdvSceneSwitcher.action.pluginState.entry="{{actions}} {{values}} {{scenes}}"
|
||||
AdvSceneSwitcher.action.pluginState.type.import="Import settings from"
|
||||
AdvSceneSwitcher.action.pluginState.importWarning="Note: Action will be ignored while settings window is opened."
|
||||
AdvSceneSwitcher.action.pluginState.entry="{{actions}}{{values}}{{scenes}}{{settings}}{{settingsWarning}}"
|
||||
AdvSceneSwitcher.action.virtualCamera="Virtual camera"
|
||||
AdvSceneSwitcher.action.virtualCamera.type.stop="Stop virtual camera"
|
||||
AdvSceneSwitcher.action.virtualCamera.type.start="Start virtual camera"
|
||||
@@ -332,10 +363,10 @@ AdvSceneSwitcher.action.sceneOrder.type.moveDown="Move down"
|
||||
AdvSceneSwitcher.action.sceneOrder.type.moveTop="Move to top"
|
||||
AdvSceneSwitcher.action.sceneOrder.type.moveBottom="Move to bottom"
|
||||
AdvSceneSwitcher.action.sceneOrder.type.movePosition="Move to position"
|
||||
AdvSceneSwitcher.action.sceneOrder.entry="On {{scenes}} {{actions}} {{sources}} {{position}}"
|
||||
AdvSceneSwitcher.action.sceneOrder.entry="On{{scenes}}{{actions}}{{sources}}{{position}}"
|
||||
AdvSceneSwitcher.action.sceneTransform="Scene item transform"
|
||||
AdvSceneSwitcher.action.sceneTransform.getTransform="Get transform"
|
||||
AdvSceneSwitcher.action.sceneTransform.entry="On {{scenes}} transform {{sources}}"
|
||||
AdvSceneSwitcher.action.sceneTransform.entry="On{{scenes}}transform{{sources}}"
|
||||
AdvSceneSwitcher.action.file="File"
|
||||
AdvSceneSwitcher.action.file.type.write="Write"
|
||||
AdvSceneSwitcher.action.file.type.append="Append"
|
||||
@@ -354,9 +385,17 @@ AdvSceneSwitcher.action.timer.type.reset="Reset"
|
||||
AdvSceneSwitcher.action.timer.type.setTimeRemaining="Set time remaining of"
|
||||
AdvSceneSwitcher.action.timer.entry="{{timerAction}} timers on {{macros}} {{duration}}"
|
||||
AdvSceneSwitcher.action.random="Random"
|
||||
AdvSceneSwitcher.action.random.arguments="Macros:"
|
||||
AdvSceneSwitcher.action.random.addArgument="Add Macro"
|
||||
AdvSceneSwitcher.action.random.entry="Run {{macroSelection}}"
|
||||
AdvSceneSwitcher.action.random.entry="Randomly run any of the following macros (paused macros are ignored)"
|
||||
AdvSceneSwitcher.action.systray="System tray notification"
|
||||
AdvSceneSwitcher.action.systray.entry="Show notification: {{message}}"
|
||||
AdvSceneSwitcher.action.screenshot="Screenshot"
|
||||
AdvSceneSwitcher.action.screenshot.mainOutput="OBS's main output"
|
||||
AdvSceneSwitcher.action.screenshot.entry="Screenshot {{sources}}"
|
||||
AdvSceneSwitcher.action.profile="Profile"
|
||||
AdvSceneSwitcher.action.profile.entry="Switch active profile to {{profiles}}"
|
||||
AdvSceneSwitcher.action.sceneCollection="Scene collection"
|
||||
AdvSceneSwitcher.action.sceneCollection.entry="Switch active scene collection to {{sceneCollections}}"
|
||||
AdvSceneSwitcher.action.sceneCollection.warning="Note: Any actions following after this will not be executed as the changing scene collection will also reload the scene switcher settings.\nThe scene collection action will be ignored while the settings window is opened."
|
||||
|
||||
; Transition Tab
|
||||
AdvSceneSwitcher.transitionTab.title="Transition"
|
||||
@@ -602,8 +641,10 @@ 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.hotkey.macro.togglePause="Toggle pause of macro %1"
|
||||
|
||||
AdvSceneSwitcher.askBackup="Detected a new version of the Advanced Scene Switcher.\nShould a backup of the old settings be created?"
|
||||
AdvSceneSwitcher.askForMacro="Select macro {{macroSelection}}"
|
||||
|
||||
AdvSceneSwitcher.close="Close"
|
||||
AdvSceneSwitcher.browse="Browse"
|
||||
@@ -624,16 +665,22 @@ AdvSceneSwitcher.selectProcess="--select process--"
|
||||
AdvSceneSwitcher.selectFilter="--select filter--"
|
||||
AdvSceneSwitcher.selectMacro="--select macro--"
|
||||
AdvSceneSwitcher.selectItem="--select item--"
|
||||
AdvSceneSwitcher.selectProfile="--select profile--"
|
||||
AdvSceneSwitcher.selectSceneCollection="--select scene collection--"
|
||||
AdvSceneSwitcher.enterPath="--enter path--"
|
||||
AdvSceneSwitcher.enterText="--enter text--"
|
||||
AdvSceneSwitcher.invaildEntriesWillNotBeSaved="invalid entries will not be saved"
|
||||
AdvSceneSwitcher.selectWindowTip="Use \"OBS\" to specify OBS window\nUse \"Task Switching\"to specify ALT + TAB"
|
||||
AdvSceneSwitcher.sceneItemSelection.all="All"
|
||||
AdvSceneSwitcher.sceneItemSelection.any="Any"
|
||||
|
||||
AdvSceneSwitcher.status.active="Active"
|
||||
AdvSceneSwitcher.status.inactive="Inactive"
|
||||
AdvSceneSwitcher.running="Plugin running"
|
||||
AdvSceneSwitcher.stopped="Plugin stopped"
|
||||
|
||||
AdvSceneSwitcher.firstBootMessage="<html><head/><body><p>This seems to be the first time the Advanced Scene Switcher was started.<br>Please have a look at the <a href=\"https://github.com/WarmUpTill/SceneSwitcher/wiki\"><span style=\" text-decoration: underline; color:#268bd2;\">Wiki</span></a> for a list of guides and examples.<br>Do not hesitate to ask questions in the plugin's <a href=\"https://obsproject.com/forum/threads/advanced-scene-switcher.48264\"><span style=\" text-decoration: underline; color:#268bd2;\">thread</span></a> on the OBS forums!</p></body></html>"
|
||||
|
||||
AdvSceneSwitcher.unit.milliseconds="milliseconds"
|
||||
AdvSceneSwitcher.unit.secends="seconds"
|
||||
AdvSceneSwitcher.unit.minutes="minutes"
|
||||
|
||||
@@ -12,17 +12,23 @@ 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="全局行为"
|
||||
AdvSceneSwitcher.generalTab.generalBehavior.onNoMet="如果不符合任何切换条件"
|
||||
AdvSceneSwitcher.generalTab.generalBehavior.onNoMetDelayTooltip="时间精度最高只能达到配置的轮询间隔"
|
||||
AdvSceneSwitcher.generalTab.generalBehavior.onNoMet.dontSwitch="不切换"
|
||||
AdvSceneSwitcher.generalTab.generalBehavior.onNoMet.switchToRandom="切换到随机场景列表中任意随机场景"
|
||||
AdvSceneSwitcher.generalTab.generalBehavior.onNoMet.switchTo="切换到"
|
||||
AdvSceneSwitcher.generalTab.generalBehavior.cooldown="比赛结束多少秒后切换场景"
|
||||
AdvSceneSwitcher.generalTab.generalBehavior.cooldownHint="在此期间,可能的匹配项将被忽略!"
|
||||
AdvSceneSwitcher.generalTab.generalBehavior.verboseLogging="详细日志输出"
|
||||
AdvSceneSwitcher.generalTab.generalBehavior.saveWindowGeo="保存窗口位置和大小"
|
||||
AdvSceneSwitcher.generalTab.generalBehavior.showTrayNotifications="显示系统托盘通知"
|
||||
AdvSceneSwitcher.generalTab.generalBehavior.disableUIHints="禁用UI提示"
|
||||
AdvSceneSwitcher.generalTab.priority="优先级"
|
||||
AdvSceneSwitcher.generalTab.priority.description="切换场景优先级 (最上方的项优先级最高)"
|
||||
@@ -45,22 +51,338 @@ 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在左侧选择现有宏或添加新宏。\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="创建副本"
|
||||
AdvSceneSwitcher.macroTab.expandAll="全部展开"
|
||||
AdvSceneSwitcher.macroTab.collapseAll="全部收回"
|
||||
|
||||
; 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.condition.audio.state.below="小于"
|
||||
AdvSceneSwitcher.condition.audio.state.above="大于"
|
||||
AdvSceneSwitcher.condition.audio.entry="音量 来源 {{audioSources}} 是 {{condition}} {{volume}}"
|
||||
AdvSceneSwitcher.condition.cursor="光标"
|
||||
AdvSceneSwitcher.condition.cursor.type.region="当前位置"
|
||||
AdvSceneSwitcher.condition.cursor.type.moving="正在移动"
|
||||
AdvSceneSwitcher.condition.cursor.showFrame="显示帧率"
|
||||
AdvSceneSwitcher.condition.cursor.hideFrame="隐藏帧率"
|
||||
AdvSceneSwitcher.condition.cursor.entry.line1="光标 处于{{conditions}} {{minX}} {{minY}} {{maxX}} {{maxY}} - {{toggleFrameButton}}"
|
||||
AdvSceneSwitcher.condition.cursor.entry.line2="光标当前位于 {{xPos}} x {{yPos}}"
|
||||
AdvSceneSwitcher.condition.scene="场景"
|
||||
AdvSceneSwitcher.condition.scene.type.current="当前场景是"
|
||||
AdvSceneSwitcher.condition.scene.type.previous="上一个场景是"
|
||||
AdvSceneSwitcher.condition.scene.type.changed="场景改变了"
|
||||
AdvSceneSwitcher.condition.scene.type.notChanged="场景没有改变"
|
||||
AdvSceneSwitcher.condition.scene.waitForTransition="等待过渡完成"
|
||||
AdvSceneSwitcher.condition.scene.entry="{{sceneType}} {{scenes}} {{waitForTransition}}"
|
||||
AdvSceneSwitcher.condition.window="窗口"
|
||||
AdvSceneSwitcher.condition.window.entry.line1="{{windows}} 存在..."
|
||||
AdvSceneSwitcher.condition.window.entry.line2="... 并且是 {{fullscreen}} 全屏 {{maximized}} 最大化 {{focused}} 获得焦点 {{windowFocusChanged}} 焦点窗口已更改"
|
||||
AdvSceneSwitcher.condition.file="文件"
|
||||
AdvSceneSwitcher.condition.file.entry.line1="内容 {{fileType}} {{filePath}} (本地是文本,网络是获取的网页)完全匹配:"
|
||||
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.condition.pattern="匹配模式"
|
||||
AdvSceneSwitcher.condition.video.condition.object="包含对象"
|
||||
AdvSceneSwitcher.condition.video.askFileAction="是否要使用现有文件或创建当前选定源的屏幕截图?"
|
||||
AdvSceneSwitcher.condition.video.askFileAction.file="使用现有文件"
|
||||
AdvSceneSwitcher.condition.video.askFileAction.screenshot="创建屏幕截图"
|
||||
AdvSceneSwitcher.condition.video.usePatternForChangedCheck="使用模式匹配"
|
||||
AdvSceneSwitcher.condition.video.usePatternForChangedCheck.tooltip="这将允许您控制图像必须更改多少才能使条件为真."
|
||||
AdvSceneSwitcher.condition.video.patternThreshold="阈值: "
|
||||
AdvSceneSwitcher.condition.video.patternThresholdDescription="较高的阈值意味着模式需要更紧密地匹配视频源."
|
||||
AdvSceneSwitcher.condition.video.patternThresholdUseAlphaAsMask="使用alpha通道作为图案的遮罩."
|
||||
AdvSceneSwitcher.condition.video.objectScaleThreshold="模型比例: "
|
||||
AdvSceneSwitcher.condition.video.objectScaleThresholdDescription="较低的模比例将导致更多的匹配,但会导致更高的CPU负载."
|
||||
AdvSceneSwitcher.condition.video.minNeighborDescription="较高的“最小区域”值将导致较少但质量较高的匹配."
|
||||
AdvSceneSwitcher.condition.video.showMatch="显示模式"
|
||||
AdvSceneSwitcher.condition.video.screenshotFail="无法获取源的屏幕截图!"
|
||||
AdvSceneSwitcher.condition.video.patternMatchFail="没有找到模式!"
|
||||
AdvSceneSwitcher.condition.video.objectMatchFail="找不到对象!"
|
||||
AdvSceneSwitcher.condition.video.modelLoadFail="无法加载模型数据!"
|
||||
AdvSceneSwitcher.condition.video.entry="{{videoSources}} {{condition}} {{imagePath}}"
|
||||
AdvSceneSwitcher.condition.video.entry.modelPath="模型数据 (haar级联分类器): {{modelDataPath}}"
|
||||
AdvSceneSwitcher.condition.video.entry.minNeighbor="最小区域: {{minNeighbors}}"
|
||||
AdvSceneSwitcher.condition.video.entry.minSize="最小尺寸: {{minSizeX}} x {{minSizeY}}"
|
||||
AdvSceneSwitcher.condition.video.entry.maxSize="最大尺寸: {{maxSizeX}} x {{maxSizeY}}"
|
||||
AdvSceneSwitcher.condition.video.entry.throttle="{{throttleEnable}}通过只执行每一次检查来减少CPU负载 {{throttleCount}} 毫秒"
|
||||
AdvSceneSwitcher.condition.stream="推流"
|
||||
AdvSceneSwitcher.condition.stream.state.start="推流启动"
|
||||
AdvSceneSwitcher.condition.stream.state.stop="推流停止"
|
||||
AdvSceneSwitcher.condition.stream.state.starting="推流启动中"
|
||||
AdvSceneSwitcher.condition.stream.state.stopping="推流停止中"
|
||||
AdvSceneSwitcher.condition.stream.entry="{{streamState}}"
|
||||
AdvSceneSwitcher.condition.record="录制"
|
||||
AdvSceneSwitcher.condition.record.state.start="录制运行中"
|
||||
AdvSceneSwitcher.condition.record.state.pause="录制暂停"
|
||||
AdvSceneSwitcher.condition.record.state.stop="录制停止"
|
||||
AdvSceneSwitcher.condition.record.entry="{{recordState}}"
|
||||
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}}"
|
||||
AdvSceneSwitcher.condition.timer="计时器"
|
||||
AdvSceneSwitcher.condition.timer.pause="暂停"
|
||||
AdvSceneSwitcher.condition.timer.continue="继续"
|
||||
AdvSceneSwitcher.condition.timer.entry.line1="{{duration}} 时间倒计时"
|
||||
AdvSceneSwitcher.condition.timer.entry.line2="剩余时间: {{remaining}} 秒"
|
||||
AdvSceneSwitcher.condition.timer.entry.line3="{{pauseContinue}} {{reset}} {{saveRemaining}} 保存剩余时间 {{autoReset}} 达到时间后自动重置计时器"
|
||||
AdvSceneSwitcher.condition.timer.reset="重置"
|
||||
AdvSceneSwitcher.condition.macro="宏"
|
||||
AdvSceneSwitcher.condition.macro.type.count="计数"
|
||||
AdvSceneSwitcher.condition.macro.type.state="状态"
|
||||
AdvSceneSwitcher.condition.macro.type.selection="条件类型: {{types}}"
|
||||
AdvSceneSwitcher.condition.macro.count.type.below="少于"
|
||||
AdvSceneSwitcher.condition.macro.count.type.above="大于"
|
||||
AdvSceneSwitcher.condition.macro.count.type.equal="等于"
|
||||
AdvSceneSwitcher.condition.macro.count.reset="重置"
|
||||
AdvSceneSwitcher.condition.macro.pausedWarning="所选宏当前已暂停!"
|
||||
AdvSceneSwitcher.condition.macro.state.entry="条件 {{macros}} 是真的"
|
||||
AdvSceneSwitcher.condition.macro.count.entry.line1="{{macros}} 运行 {{conditions}} {{count}} 次(相对于下面计数匹配)"
|
||||
AdvSceneSwitcher.condition.macro.count.entry.line2="当前计数: {{currentCount}} {{resetCount}}"
|
||||
AdvSceneSwitcher.condition.source="源"
|
||||
AdvSceneSwitcher.condition.source.type.active="激活状态"
|
||||
AdvSceneSwitcher.condition.source.type.showing="显示状态"
|
||||
AdvSceneSwitcher.condition.source.type.settings="设置完全匹配"
|
||||
AdvSceneSwitcher.condition.source.regex="使用正则表达式"
|
||||
AdvSceneSwitcher.condition.source.getSettings="获取当前设置"
|
||||
AdvSceneSwitcher.condition.source.entry.line1="{{sources}} {{conditions}}"
|
||||
AdvSceneSwitcher.condition.source.entry.line2="{{settings}}"
|
||||
AdvSceneSwitcher.condition.source.entry.line3="{{regex}} {{getSettings}}"
|
||||
AdvSceneSwitcher.condition.virtualCamera="虚拟摄像机"
|
||||
AdvSceneSwitcher.condition.virtualCamera.state.start="虚拟摄像机启动"
|
||||
AdvSceneSwitcher.condition.virtualCamera.state.stop="虚拟摄像机停止"
|
||||
AdvSceneSwitcher.condition.virtualCamera.entry="{{states}}"
|
||||
AdvSceneSwitcher.condition.filter="滤镜"
|
||||
AdvSceneSwitcher.condition.filter.type.active="已启用"
|
||||
AdvSceneSwitcher.condition.filter.type.showing="已关闭"
|
||||
AdvSceneSwitcher.condition.filter.type.settings="设置完全匹配"
|
||||
AdvSceneSwitcher.condition.filter.regex="使用正则表达式"
|
||||
AdvSceneSwitcher.condition.filter.getSettings="获取当前设置"
|
||||
AdvSceneSwitcher.condition.filter.entry.line1="在 {{sources}} {{filters}} {{conditions}}"
|
||||
AdvSceneSwitcher.condition.filter.entry.line2="{{settings}}"
|
||||
AdvSceneSwitcher.condition.filter.entry.line3="{{regex}} {{getSettings}}"
|
||||
AdvSceneSwitcher.condition.sceneOrder="场景项目顺序"
|
||||
AdvSceneSwitcher.condition.sceneOrder.type.above="高于"
|
||||
AdvSceneSwitcher.condition.sceneOrder.type.below="低于"
|
||||
AdvSceneSwitcher.condition.sceneOrder.type.position="位置值排比"
|
||||
AdvSceneSwitcher.condition.sceneOrder.positionInfo="位置值从底部以0开始,对于每个场景项目(包括场景组中的项目)增加1,自行测试,按照上下往上排比数值。"
|
||||
AdvSceneSwitcher.condition.sceneOrder.entry="在 {{scenes}} {{sources}} {{conditions}} {{sources2}} {{position}}"
|
||||
AdvSceneSwitcher.condition.hotkey="热键"
|
||||
AdvSceneSwitcher.condition.hotkey.name="宏触发热键"
|
||||
AdvSceneSwitcher.condition.hotkey.tip="注意:您可以在OBS设置窗口中为此热键配置按键绑定"
|
||||
AdvSceneSwitcher.condition.hotkey.entry="名称: {{name}}"
|
||||
AdvSceneSwitcher.condition.replay="回放缓存"
|
||||
AdvSceneSwitcher.condition.replay.state.stopped="回放缓存已停止"
|
||||
AdvSceneSwitcher.condition.replay.state.started="回放缓存已启动"
|
||||
AdvSceneSwitcher.condition.replay.state.saved="已保存回放缓存"
|
||||
AdvSceneSwitcher.condition.replay.entry="{{state}}"
|
||||
AdvSceneSwitcher.condition.date="日期"
|
||||
AdvSceneSwitcher.condition.date.state.at="现在"
|
||||
AdvSceneSwitcher.condition.date.state.after="之后"
|
||||
AdvSceneSwitcher.condition.date.state.before="之前"
|
||||
AdvSceneSwitcher.condition.date.state.between="之间"
|
||||
AdvSceneSwitcher.condition.date.entry.line1="{{condition}} {{dateTime}} {{dateTime2}} {{ignoreDate}} 忽略日期 {{ignoreTime}} 忽略时间"
|
||||
AdvSceneSwitcher.condition.date.entry.line2="{{repeat}} 重复的每一次 {{duration}} 匹配的时间"
|
||||
AdvSceneSwitcher.condition.sceneTransform="场景项目被改变"
|
||||
AdvSceneSwitcher.condition.sceneTransform.getTransform="内容或者设置被改变了"
|
||||
AdvSceneSwitcher.condition.sceneTransform.regex="使用正则表达式"
|
||||
AdvSceneSwitcher.condition.sceneTransform.entry.line1="在 {{scenes}} {{sources}} 匹配变换"
|
||||
AdvSceneSwitcher.condition.sceneTransform.entry.line2="{{settings}}"
|
||||
AdvSceneSwitcher.condition.sceneTransform.entry.line3="{{regex}} {{getSettings}}"
|
||||
AdvSceneSwitcher.condition.transition="转场"
|
||||
AdvSceneSwitcher.condition.transition.type.current="当前转场特效类型为"
|
||||
AdvSceneSwitcher.condition.transition.type.duration="当前转场特效持续时间为"
|
||||
AdvSceneSwitcher.condition.transition.type.started="转场特效开始"
|
||||
AdvSceneSwitcher.condition.transition.type.ended="转场特效结束"
|
||||
AdvSceneSwitcher.condition.transition.durationSuffix="秒"
|
||||
AdvSceneSwitcher.condition.transition.entry="{{conditions}}{{transitions}}{{duration}}{{durationSuffix}}"
|
||||
AdvSceneSwitcher.condition.sceneVisibility="场景项目可见"
|
||||
AdvSceneSwitcher.condition.sceneVisibility.type.shown="显示的"
|
||||
AdvSceneSwitcher.condition.sceneVisibility.type.hidden="隐藏的"
|
||||
AdvSceneSwitcher.condition.sceneVisibility.entry="在 {{scenes}} {{sources}} 是 {{conditions}} "
|
||||
|
||||
; 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.fade="{{fade}}淡出 {{duration}} 秒"
|
||||
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.arguments="参数:"
|
||||
AdvSceneSwitcher.action.run.addArgument="添加参数"
|
||||
AdvSceneSwitcher.action.run.addArgumentDescription="添加新参数:"
|
||||
AdvSceneSwitcher.action.run.entry="运行 {{filePath}}"
|
||||
AdvSceneSwitcher.action.sceneVisibility="场景项目可见性"
|
||||
AdvSceneSwitcher.action.sceneVisibility.type.show="显示"
|
||||
AdvSceneSwitcher.action.sceneVisibility.type.hide="隐藏"
|
||||
AdvSceneSwitcher.action.sceneVisibility.type.source="来源"
|
||||
AdvSceneSwitcher.action.sceneVisibility.type.sourceGroup="全部"
|
||||
AdvSceneSwitcher.action.sceneVisibility.entry="在 {{scenes}} {{actions}} {{sourceTypes}} {{sources}}"
|
||||
AdvSceneSwitcher.action.filter="滤镜"
|
||||
AdvSceneSwitcher.action.filter.type.enable="开启"
|
||||
AdvSceneSwitcher.action.filter.type.disable="关闭"
|
||||
AdvSceneSwitcher.action.filter.type.settings="参数设置"
|
||||
AdvSceneSwitcher.action.filter.entry="在 {{sources}} {{actions}} {{filters}}"
|
||||
AdvSceneSwitcher.action.filter.getSettings="获取当前设置"
|
||||
AdvSceneSwitcher.action.source="来源"
|
||||
AdvSceneSwitcher.action.source.type.enable="启用"
|
||||
AdvSceneSwitcher.action.source.type.disable="停用"
|
||||
AdvSceneSwitcher.action.source.type.settings="配置设置"
|
||||
AdvSceneSwitcher.action.source.entry="{{actions}} {{sources}}"
|
||||
AdvSceneSwitcher.action.source.warning="警告:OBS UI无法控制全局启用和禁用源,注意,通过这个设置禁用,我测试,你必须通过这个在启用才行,不然点击小眼睛你无法显示!"
|
||||
AdvSceneSwitcher.action.source.getSettings="获取当前设置"
|
||||
AdvSceneSwitcher.action.media="媒体"
|
||||
AdvSceneSwitcher.action.media.type.play="开始"
|
||||
AdvSceneSwitcher.action.media.type.pause="暂停"
|
||||
AdvSceneSwitcher.action.media.type.stop="停止"
|
||||
AdvSceneSwitcher.action.media.type.restart="重新启动"
|
||||
AdvSceneSwitcher.action.media.type.next="下一个"
|
||||
AdvSceneSwitcher.action.media.type.previous="上一个"
|
||||
AdvSceneSwitcher.action.media.entry="{{actions}} {{mediaSources}}"
|
||||
AdvSceneSwitcher.action.macro="宏"
|
||||
AdvSceneSwitcher.action.macro.type.pause="暂停"
|
||||
AdvSceneSwitcher.action.macro.type.unpause="取消暂停"
|
||||
AdvSceneSwitcher.action.macro.type.resetCounter="复位计数器"
|
||||
AdvSceneSwitcher.action.macro.entry="{{actions}} {{macros}}"
|
||||
AdvSceneSwitcher.action.pluginState="插件状态"
|
||||
AdvSceneSwitcher.action.pluginState.type.stop="停止高级场景切换插件"
|
||||
AdvSceneSwitcher.action.pluginState.type.noMatch="没有匹配项:"
|
||||
AdvSceneSwitcher.action.pluginState.entry="{{actions}} {{values}} {{scenes}}"
|
||||
AdvSceneSwitcher.action.virtualCamera="虚拟摄像机"
|
||||
AdvSceneSwitcher.action.virtualCamera.type.stop="停止虚拟摄像机"
|
||||
AdvSceneSwitcher.action.virtualCamera.type.start="启动虚拟摄像机"
|
||||
AdvSceneSwitcher.action.virtualCamera.entry="{{actions}}"
|
||||
AdvSceneSwitcher.action.hotkey="热键"
|
||||
AdvSceneSwitcher.action.hotkey.leftShift="左 Shift"
|
||||
AdvSceneSwitcher.action.hotkey.rightShift="右 Shift"
|
||||
AdvSceneSwitcher.action.hotkey.leftCtrl="左 Ctrl"
|
||||
AdvSceneSwitcher.action.hotkey.rightCtrl="右 Ctrl"
|
||||
AdvSceneSwitcher.action.hotkey.leftAlt="左 Alt"
|
||||
AdvSceneSwitcher.action.hotkey.rightAlt="右 Alt"
|
||||
AdvSceneSwitcher.action.hotkey.leftMeta="左 Meta"
|
||||
AdvSceneSwitcher.action.hotkey.rightMeta="右 Meta"
|
||||
AdvSceneSwitcher.action.hotkey.onlyOBS="仅向OBS发送按键"
|
||||
AdvSceneSwitcher.action.hotkey.disabled="无法模拟按键-功能已禁用!"
|
||||
AdvSceneSwitcher.action.hotkey.entry="按下 {{keys}} for {{duration}} 毫秒"
|
||||
AdvSceneSwitcher.action.sceneOrder="场景项目顺序"
|
||||
AdvSceneSwitcher.action.sceneOrder.type.moveUp="上移"
|
||||
AdvSceneSwitcher.action.sceneOrder.type.moveDown="下移"
|
||||
AdvSceneSwitcher.action.sceneOrder.type.moveTop="顶部"
|
||||
AdvSceneSwitcher.action.sceneOrder.type.moveBottom="底部"
|
||||
AdvSceneSwitcher.action.sceneOrder.type.movePosition="移动到数值位置"
|
||||
AdvSceneSwitcher.action.sceneOrder.entry="在 {{scenes}} {{actions}} {{sources}} {{position}}"
|
||||
AdvSceneSwitcher.action.sceneTransform="场景项变换"
|
||||
AdvSceneSwitcher.action.sceneTransform.getTransform="得到变换"
|
||||
AdvSceneSwitcher.action.sceneTransform.entry="On {{scenes}} transform {{sources}}"
|
||||
AdvSceneSwitcher.action.file="文件"
|
||||
AdvSceneSwitcher.action.file.type.write="写入"
|
||||
AdvSceneSwitcher.action.file.type.append="追加写入"
|
||||
AdvSceneSwitcher.action.file.entry="{{actions}} to {{filePath}}:"
|
||||
AdvSceneSwitcher.action.previewScene="切换预览场景"
|
||||
AdvSceneSwitcher.action.previewScene.entry="将预览场景切换到 {{scenes}}"
|
||||
AdvSceneSwitcher.action.SceneSwap="交换场景 (Studio mode)"
|
||||
AdvSceneSwitcher.action.SceneSwap.entry="在studio模式下交换预览和编程场景"
|
||||
AdvSceneSwitcher.action.transition="过场特效"
|
||||
AdvSceneSwitcher.action.transition.entry.line1="{{setType}}将过场特效类型设置为 {{transitions}}"
|
||||
AdvSceneSwitcher.action.transition.entry.line2="{{setDuration}}将过场特效持续时间设置为 {{duration}}seconds"
|
||||
AdvSceneSwitcher.action.timer="计时器"
|
||||
AdvSceneSwitcher.action.timer.type.pause="暂停"
|
||||
AdvSceneSwitcher.action.timer.type.continue="继续"
|
||||
AdvSceneSwitcher.action.timer.type.reset="重置"
|
||||
AdvSceneSwitcher.action.timer.type.setTimeRemaining="设置剩余时间"
|
||||
AdvSceneSwitcher.action.timer.entry="{{timerAction}} 定时在 {{macros}} {{duration}}"
|
||||
AdvSceneSwitcher.action.random="随机"
|
||||
AdvSceneSwitcher.action.random.arguments="宏:"
|
||||
AdvSceneSwitcher.action.random.addArgument="增加 宏"
|
||||
AdvSceneSwitcher.action.random.entry="运行 {{macroSelection}}"
|
||||
|
||||
; Transition Tab
|
||||
AdvSceneSwitcher.transitionTab.title="转场特效"
|
||||
AdvSceneSwitcher.transitionTab.setTransitionBy="更改转场特效时:"
|
||||
AdvSceneSwitcher.transitionTab.transitionOverride="在场景切换器中设定的转场特效优先级高于场景设定的转场特效"
|
||||
AdvSceneSwitcher.transitionTab.adjustActiveTransitionType="更改激活转场特效类型"
|
||||
AdvSceneSwitcher.transitionTab.transitionForAToB="当自动从场景A切换到场景B时使用的转场特效"
|
||||
AdvSceneSwitcher.transitionTab.transitionsHelp="<html><head/><body><p>这里的设定<span style=\"font-style:bold;\">只影响由场景切换器引起的转场有效</span>,不影响你手动的引起的转场。<br/>在这里设定的转场特效优先级高于场景切换器其他地方设置的<br/><br/>Click the plus symbol below to continue.</p></body></html>"
|
||||
AdvSceneSwitcher.transitionTab.transitionsHelp="<html><head/><body><p>这里的设定<span style=\"font-style:bold;\">只影响由场景切换器引起的转场有效</span>,不影响你手动的引起的转场。<br/>在这里设定的转场特效优先级高于场景切换器其他地方设置的<br/><br/>单击加号添加项目.</p></body></html>"
|
||||
AdvSceneSwitcher.transitionTab.defaultTransition="当切换到这个场景时修改默认转场特效"
|
||||
AdvSceneSwitcher.transitionTab.switchDefaultLabel="活跃时切换默认转场特效为"
|
||||
AdvSceneSwitcher.transitionTab.entry="从场景 {{scenes}} 切换到场景 {{scenes2}} 时使用转场特效 {{transitions}}"
|
||||
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="当切换到这个场景时暂停场景切换器"
|
||||
AdvSceneSwitcher.pauseTab.pauseInFocus1="当窗口"
|
||||
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="窗口标题"
|
||||
@@ -71,31 +393,37 @@ AdvSceneSwitcher.windowTitleTab.fullscreen="全屏时"
|
||||
AdvSceneSwitcher.windowTitleTab.maximized="最大化时"
|
||||
AdvSceneSwitcher.windowTitleTab.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="感谢dasOven的实现"
|
||||
AdvSceneSwitcher.executableTab.requiresFocus="仅获得焦点时"
|
||||
AdvSceneSwitcher.executableTab.entry="当 {{processes}} 正在运行时使用 {{transitions}} 切换到 {{scenes}} {{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}} 使用转场特效 {{transitions}} 切换到场景 {{scenes}} unless in {{excludeScenes}}"
|
||||
AdvSceneSwitcher.screenRegionTab.help="此选项卡允许您根据鼠标光标的当前位置自动切换场景。\n\n单击高亮显示的加号继续."
|
||||
|
||||
; Media Tab
|
||||
AdvSceneSwitcher.mediaTab.title="媒体"
|
||||
AdvSceneSwitcher.mediaTab.implemented="感谢Exeldro的实现"
|
||||
AdvSceneSwitcher.mediaTab.states.none="空"
|
||||
AdvSceneSwitcher.mediaTab.states.none="无"
|
||||
AdvSceneSwitcher.mediaTab.states.playing="播放中"
|
||||
AdvSceneSwitcher.mediaTab.states.opening="正在打开"
|
||||
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="播放时间小于"
|
||||
@@ -103,6 +431,7 @@ AdvSceneSwitcher.mediaTab.timeRestriction.longer="播放时间大于"
|
||||
AdvSceneSwitcher.mediaTab.timeRestriction.remainShorter="剩余时间少于"
|
||||
AdvSceneSwitcher.mediaTab.timeRestriction.remainLonger="剩余时间多于"
|
||||
AdvSceneSwitcher.mediaTab.entry="当 {{mediaSources}} 状态为 {{states}} 并且 {{timeRestrictions}} {{time}} 时使用转场特效 {{transitions}} 切换到场景 {{scenes}}"
|
||||
AdvSceneSwitcher.mediaTab.help="此选项卡允许您根据媒体源的状态切换场景。\n例如,一旦所选媒体源结束播放,您可以自动切换回上一个场景。\n\n单击高亮显示的加号继续."
|
||||
|
||||
; File Tab
|
||||
AdvSceneSwitcher.fileTab.title="文件"
|
||||
@@ -127,11 +456,13 @@ AdvSceneSwitcher.fileTab.checkfileContent="仅当文件发生修改时"
|
||||
AdvSceneSwitcher.fileTab.entry="如果 {{fileType}} 文件 {{filePath}} {{browseButton}} 匹配下列规则,使用转场特效 {{transitions}} 切换到场景 {{scenes}}"
|
||||
AdvSceneSwitcher.fileTab.entry2="{{matchText}}"
|
||||
AdvSceneSwitcher.fileTab.entry3="{{useRegex}} {{checkModificationDate}} {{checkFileContent}}"
|
||||
AdvSceneSwitcher.fileTab.help="此选项卡允许您根据远程或本地文件的内容自动切换场景。\n\n单击突出显示的加号继续."
|
||||
|
||||
; Random Tab
|
||||
AdvSceneSwitcher.randomTab.title="随机场景列表"
|
||||
AdvSceneSwitcher.randomTab.randomDisabledWarning="功能已被禁用 - 可通过在通用页设置\"如果不符合任何切换条件切换到随机场景列表中任意随机场景\"启用"
|
||||
AdvSceneSwitcher.randomTab.entry="如果没有切换器条件满足,使用转场特效 {{transitions}} 切换到场景 {{scenes}} 并停留 {{delay}}"
|
||||
AdvSceneSwitcher.randomTab.help="场景切换程序将在此选项卡上随机选择一个条目,以便在配置的时间内切换到该条目。\n请注意,同一条目不会连续选择两次。\n\n单击高亮显示的加号继续."
|
||||
|
||||
; Time Tab
|
||||
AdvSceneSwitcher.timeTab.title="时间"
|
||||
@@ -146,15 +477,12 @@ AdvSceneSwitcher.timeTab.sundays="每周日"
|
||||
AdvSceneSwitcher.timeTab.afterstart="推流或录制开始后"
|
||||
AdvSceneSwitcher.timeTab.afterstart.tip="相对于直播或录制开始之后的时间点"
|
||||
AdvSceneSwitcher.timeTab.entry="{{triggers}} 的 {{time}} 使用转场特效 {{transitions}} 切换到场景 {{scenes}}"
|
||||
AdvSceneSwitcher.timeTab.help="此选项卡将允许您根据当前本地时间自动切换到其他场景。\n\n请注意,场景切换程序仅在您指定的确切时间切换场景。\n请确保您已根据自己的喜好在“常规”选项卡上配置了优先级设置,以便不会因其他切换而错过选定的时间点具有较高优先级的方法。\n\n单击突出显示的加号继续."
|
||||
|
||||
; Idle Tab
|
||||
AdvSceneSwitcher.idleTab.title="闲置检测"
|
||||
AdvSceneSwitcher.idleTab.enable="启用闲置检测"
|
||||
AdvSceneSwitcher.idleTab.condition1="超过"
|
||||
AdvSceneSwitcher.idleTab.condition2="没有鼠标或键盘操作"
|
||||
AdvSceneSwitcher.idleTab.switchToScene="切换到场景"
|
||||
AdvSceneSwitcher.idleTab.usingTransition1="使用"
|
||||
AdvSceneSwitcher.idleTab.usingTransition2="转场特效"
|
||||
AdvSceneSwitcher.idleTab.idleswitch="超过 {{duration}} 如果没有键盘或鼠标输入,则切换到场景 {{scenes}} 使用 {{transitions}}"
|
||||
AdvSceneSwitcher.idleTab.dontSwitchIfFocus1="如果当前窗口焦点在"
|
||||
AdvSceneSwitcher.idleTab.dontSwitchIfFocus2="时不切换"
|
||||
|
||||
@@ -169,35 +497,148 @@ AdvSceneSwitcher.sceneSequenceTab.loadFail="导入失败"
|
||||
AdvSceneSwitcher.sceneSequenceTab.loadSuccess="导入失败成功"
|
||||
AdvSceneSwitcher.sceneSequenceTab.fileType="文本文件 (*.txt)"
|
||||
AdvSceneSwitcher.sceneSequenceTab.entry="当场景 {{startScenes}} 被激活 {{delay}} 后使用转场特效 {{transitions}} 切换到场景 {{scenes}} {{interruptible}}"
|
||||
AdvSceneSwitcher.sceneSequenceTab.interruptible="中断"
|
||||
AdvSceneSwitcher.sceneSequenceTab.interruptibleHint="允许使用其他切换方法中断此场景序列"
|
||||
AdvSceneSwitcher.sceneSequenceTab.extendEdit="扩展序列"
|
||||
AdvSceneSwitcher.sceneSequenceTab.extendEntry="之后 {{delay}} 切换到 {{scenes}} 使用 {{transitions}}"
|
||||
AdvSceneSwitcher.sceneSequenceTab.help="如果某个场景在配置的时间段内处于活动状态,此选项卡将允许您自动切换到其他场景。\n例如,您可以自动在两个场景之间来回循环。\n\n单击高亮显示的加号继续."
|
||||
|
||||
; Audio Tab
|
||||
AdvSceneSwitcher.audioTab.title="音频"
|
||||
AdvSceneSwitcher.audioTab.entry="当{{audioSources}} is {{condition}} {{volumeWidget}} for {{duration}} 使用转场特效 {{transitions}} 切换到场景 {{scenes}}"
|
||||
AdvSceneSwitcher.audioTab.condition.above="高于"
|
||||
AdvSceneSwitcher.audioTab.condition.below="低于"
|
||||
AdvSceneSwitcher.audioTab.ignoreInactiveSource="除非源处于非活动状态"
|
||||
AdvSceneSwitcher.audioTab.entry="当{{audioSources}} 在 {{condition}} {{volumeWidget}} for {{duration}} 使用转场特效 {{transitions}} 切换到场景 {{scenes}}"
|
||||
AdvSceneSwitcher.audioTab.multiMatchfallbackCondition="如果多个条目匹配 ..."
|
||||
AdvSceneSwitcher.audioTab.multiMatchfallback="... for {{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.entry="当 {{videoSources}} {{condition}} {{filePath}} {{browseButton}} for {{duration}} 切换 {{scenes}} using {{transitions}} {{ignoreInactiveSource}}"
|
||||
AdvSceneSwitcher.videoTab.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.description="此选项卡将允许您远程控制另一个OBS实例的活动场景。\n请注意,所有OBS实例上的场景名称必须完全匹配."
|
||||
AdvSceneSwitcher.networkTab.warning="在本地网络之外运行服务器将允许第三方读取活动场景."
|
||||
AdvSceneSwitcher.networkTab.DisabledWarning="不幸的是,在并行运行obs websocket插件时,由于库不兼容,不得不在macOS上禁用此功能."
|
||||
AdvSceneSwitcher.networkTab.server="启动服务器(向所有连接的客户端发送场景切换消息)"
|
||||
AdvSceneSwitcher.networkTab.server.port="端口"
|
||||
AdvSceneSwitcher.networkTab.server.lockToIPv4="锁定为仅使用IPv4将服务器"
|
||||
AdvSceneSwitcher.networkTab.server.sendSceneChange="发送更改场景的消息"
|
||||
AdvSceneSwitcher.networkTab.server.restrictSendToAutomatedSwitches="仅为自动场景切换发送消息"
|
||||
AdvSceneSwitcher.networkTab.server.sendPreview="在Studio模式下运行时发送预览场景更改消息"
|
||||
AdvSceneSwitcher.networkTab.startFailed.message="WebSockets服务器无法启动,可能是因为:\n-TCP端口%1当前可能正在该系统的其他位置使用,可能是由其他应用程序使用。请尝试在WebSocket服务器设置中设置其他TCP端口,或停止可能正在使用此端口的任何应用程序。\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例如,包含场景的场景组…\n场景1\n场景2\n场景3\n。。。将在第一次选择“场景1”作为目标时激活它。\n第二次将激活“场景2”\n剩余时间将激活“场景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.sceneTriggerAction.startVirtualCamera="启动虚拟摄像机"
|
||||
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.stopVirtualCamera="停止虚拟摄像机"
|
||||
AdvSceneSwitcher.sceneTriggerTab.entry="当 {{scenes}} {{triggers}} {{actions}} {{audioSources}} 在 {{duration}} 后"
|
||||
AdvSceneSwitcher.sceneTriggerTab.help="此选项卡允许您在场景更改时触发操作,如停止录制或流式处理."
|
||||
|
||||
; Hotkey
|
||||
AdvSceneSwitcher.hotkey.startSwitcherHotkey="启动高级场景切换器"
|
||||
AdvSceneSwitcher.hotkey.stopSwitcherHotkey="停止高级场景切换器"
|
||||
AdvSceneSwitcher.hotkey.startStopToggleSwitcherHotkey="切换(启动/停止)高级场景切换器"
|
||||
AdvSceneSwitcher.hotkey.macro.pause="暂停宏 %1"
|
||||
AdvSceneSwitcher.hotkey.macro.unpause="取消暂停宏 %1"
|
||||
|
||||
AdvSceneSwitcher.askBackup="检测到新版本的高级场景切换程序。\n是否要创建旧设置的备份?"
|
||||
|
||||
AdvSceneSwitcher.close="关闭"
|
||||
AdvSceneSwitcher.browse="浏览文件"
|
||||
|
||||
AdvSceneSwitcher.selectScene="--选择场景--"
|
||||
AdvSceneSwitcher.selectPreviousScene="上个场景"
|
||||
AdvSceneSwitcher.selectCurrentScene="当前场景"
|
||||
AdvSceneSwitcher.selectAnyScene="任何场景"
|
||||
AdvSceneSwitcher.currentTransition="当前转场特效"
|
||||
AdvSceneSwitcher.anyTransition="任何转场特效"
|
||||
AdvSceneSwitcher.selectTransition="--选择转场特效--"
|
||||
AdvSceneSwitcher.selectWindow="--选择窗口--"
|
||||
AdvSceneSwitcher.selectSource="--选择源--"
|
||||
AdvSceneSwitcher.selectAudioSource="--选择音频源--"
|
||||
AdvSceneSwitcher.selectVideoSource="--选择视频源--"
|
||||
AdvSceneSwitcher.selectMediaSource="--选择媒体源--"
|
||||
AdvSceneSwitcher.selectProcess="--选择进程--"
|
||||
AdvSceneSwitcher.selectFilter="--select filter--"
|
||||
AdvSceneSwitcher.selectMacro="--选择宏--"
|
||||
AdvSceneSwitcher.selectItem="--选择项目--"
|
||||
AdvSceneSwitcher.enterPath="--输入路径--"
|
||||
AdvSceneSwitcher.enterText="--输入文本--"
|
||||
AdvSceneSwitcher.invaildEntriesWillNotBeSaved="无效项将不会被保存"
|
||||
AdvSceneSwitcher.selectWindowTip="使用 \"OBS\" 来指定OBS窗口\n使用 \"Task Switching\" 来指定 Alt + Tab"
|
||||
AdvSceneSwitcher.selectWindowTip="使用 \"OBS\" 来指定OBS窗口\n使用 \"任务切换\" 来指定 Alt + Tab"
|
||||
|
||||
AdvSceneSwitcher.status.active="运行中"
|
||||
AdvSceneSwitcher.status.inactive="已停止"
|
||||
AdvSceneSwitcher.running="插件运行"
|
||||
AdvSceneSwitcher.stopped="插件停止"
|
||||
|
||||
AdvSceneSwitcher.unit.milliseconds="毫秒"
|
||||
AdvSceneSwitcher.unit.secends="秒"
|
||||
AdvSceneSwitcher.unit.minutes="分钟"
|
||||
AdvSceneSwitcher.unit.hours="小时"
|
||||
|
||||
AdvSceneSwitcher.duration.condition.none="没有时间限制"
|
||||
AdvSceneSwitcher.duration.condition.more="时间少于"
|
||||
AdvSceneSwitcher.duration.condition.equal="时间等于"
|
||||
AdvSceneSwitcher.duration.condition.less="时间大于"
|
||||
|
||||
1
deps/openvr
vendored
Submodule
1
deps/openvr
vendored
Submodule
Submodule deps/openvr added at 4c85abcb7f
@@ -545,6 +545,13 @@
|
||||
<string>AdvSceneSwitcher.macroTab.macros</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_36">
|
||||
<item>
|
||||
<widget class="QLabel" name="macroPriorityWarning">
|
||||
<property name="text">
|
||||
<string>AdvSceneSwitcher.macroTab.priorityWarning</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QGridLayout" name="gridLayout_26">
|
||||
<item row="0" column="0">
|
||||
@@ -722,7 +729,7 @@
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_34">
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_18" stretch="0,0,0">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_18" stretch="0,0,0,0,0,0">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_20">
|
||||
<property name="text">
|
||||
@@ -733,6 +740,27 @@
|
||||
<item>
|
||||
<widget class="QLineEdit" name="macroName"/>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="runMacro">
|
||||
<property name="text">
|
||||
<string>AdvSceneSwitcher.macroTab.run</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="runMacroInParallel">
|
||||
<property name="text">
|
||||
<string>AdvSceneSwitcher.macroTab.runInParallel</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="runMacroOnChange">
|
||||
<property name="text">
|
||||
<string>AdvSceneSwitcher.macroTab.onChange</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_13">
|
||||
<property name="orientation">
|
||||
@@ -762,8 +790,8 @@
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>767</width>
|
||||
<height>220</height>
|
||||
<width>792</width>
|
||||
<height>218</height>
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_35" stretch="10,1">
|
||||
@@ -861,8 +889,8 @@
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>767</width>
|
||||
<height>219</height>
|
||||
<width>792</width>
|
||||
<height>218</height>
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_33" stretch="10,1">
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include <QMainWindow>
|
||||
#include <QAction>
|
||||
#include <QFileDialog>
|
||||
#include <QDirIterator>
|
||||
#include <regex>
|
||||
#include <filesystem>
|
||||
|
||||
@@ -8,11 +9,16 @@
|
||||
#include <obs-frontend-api.h>
|
||||
|
||||
#include "headers/advanced-scene-switcher.hpp"
|
||||
#include "headers/status-dock.hpp"
|
||||
#include "headers/curl-helper.hpp"
|
||||
#include "headers/utility.hpp"
|
||||
#include "headers/version.h"
|
||||
|
||||
SwitcherData *switcher = nullptr;
|
||||
SwitcherData *GetSwitcher()
|
||||
{
|
||||
return switcher;
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
* Create the Advanced Scene Switcher settings window
|
||||
@@ -81,6 +87,7 @@ void AdvSceneSwitcher::loadUI()
|
||||
|
||||
setTabOrder();
|
||||
restoreWindowGeo();
|
||||
checkFirstTimeSetup();
|
||||
|
||||
loading = false;
|
||||
}
|
||||
@@ -88,34 +95,6 @@ void AdvSceneSwitcher::loadUI()
|
||||
/******************************************************************************
|
||||
* Saving and loading
|
||||
******************************************************************************/
|
||||
void 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());
|
||||
}
|
||||
|
||||
static void SaveSceneSwitcher(obs_data_t *save_data, bool saving, void *)
|
||||
{
|
||||
if (saving) {
|
||||
@@ -126,6 +105,10 @@ static void SaveSceneSwitcher(obs_data_t *save_data, bool saving, void *)
|
||||
obs_data_set_obj(save_data, "advanced-scene-switcher", obj);
|
||||
obs_data_release(obj);
|
||||
} else {
|
||||
// Stop the scene switcher at least once to
|
||||
// avoid scene duplication issues with scene collection changes
|
||||
switcher->Stop();
|
||||
|
||||
switcher->m.lock();
|
||||
obs_data_t *obj =
|
||||
obs_data_get_obj(save_data, "advanced-scene-switcher");
|
||||
@@ -133,17 +116,13 @@ static void SaveSceneSwitcher(obs_data_t *save_data, bool saving, void *)
|
||||
obj = obs_data_create();
|
||||
}
|
||||
if (switcher->versionChanged(obj, g_GIT_SHA1)) {
|
||||
AskBackup(obj);
|
||||
AskForBackup(obj);
|
||||
}
|
||||
switcher->loadSettings(obj);
|
||||
obs_data_release(obj);
|
||||
switcher->m.unlock();
|
||||
|
||||
// Stop the scene switcher at least once to
|
||||
// avoid scene duplication issues with scene collection changes
|
||||
bool start = !switcher->stop;
|
||||
switcher->Stop();
|
||||
if (start) {
|
||||
if (!switcher->stop) {
|
||||
switcher->Start();
|
||||
}
|
||||
}
|
||||
@@ -438,9 +417,10 @@ void SwitcherData::Stop()
|
||||
{
|
||||
if (th && th->isRunning()) {
|
||||
stop = true;
|
||||
cv.notify_one();
|
||||
cv.notify_all();
|
||||
abortMacroWait = true;
|
||||
macroWaitCv.notify_one();
|
||||
macroWaitCv.notify_all();
|
||||
macroTransitionCv.notify_all();
|
||||
th->wait();
|
||||
delete th;
|
||||
th = nullptr;
|
||||
@@ -579,6 +559,7 @@ void setTranstionEnd()
|
||||
{
|
||||
switcher->lastTransitionEndTime =
|
||||
std::chrono::high_resolution_clock::now();
|
||||
switcher->macroTransitionCv.notify_all();
|
||||
}
|
||||
|
||||
void setStreamStarting()
|
||||
@@ -643,6 +624,26 @@ static void OBSEvent(enum obs_frontend_event event, void *switcher)
|
||||
}
|
||||
}
|
||||
|
||||
void LoadPlugins()
|
||||
{
|
||||
QFileInfo fi(obs_get_module_binary_path(obs_current_module()));
|
||||
QDirIterator it(fi.absolutePath(), QStringList() << "*.so",
|
||||
QDir::Files);
|
||||
while (it.hasNext()) {
|
||||
auto file = it.next();
|
||||
if (it.fileName() == "advanced-scene-switcher.so") {
|
||||
continue;
|
||||
}
|
||||
blog(LOG_INFO, "attempting to load \"%s\"",
|
||||
file.toStdString().c_str());
|
||||
auto lib = new QLibrary(file, nullptr);
|
||||
if (lib->load()) {
|
||||
blog(LOG_INFO, "successfully loaded \"%s\"",
|
||||
file.toStdString().c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AdvSceneSwitcher *ssWindow;
|
||||
|
||||
extern "C" void InitSceneSwitcher()
|
||||
@@ -650,9 +651,6 @@ extern "C" void InitSceneSwitcher()
|
||||
blog(LOG_INFO, "version: %s", g_GIT_TAG);
|
||||
blog(LOG_INFO, "version: %s", g_GIT_SHA1);
|
||||
|
||||
QAction *action = (QAction *)obs_frontend_add_tools_menu_qaction(
|
||||
obs_module_text("AdvSceneSwitcher.pluginName"));
|
||||
|
||||
switcher = new SwitcherData;
|
||||
|
||||
if (loadCurl() && f_curl_init) {
|
||||
@@ -660,6 +658,14 @@ extern "C" void InitSceneSwitcher()
|
||||
}
|
||||
|
||||
PlatformInit();
|
||||
#if !defined(_WIN32)
|
||||
// Windows does not require the plugins to be loaded manually
|
||||
LoadPlugins();
|
||||
#endif
|
||||
|
||||
dock = new StatusDock(
|
||||
static_cast<QMainWindow *>(obs_frontend_get_main_window()));
|
||||
obs_frontend_add_dock(dock);
|
||||
|
||||
auto cb = []() {
|
||||
if (switcher->settingsWindowOpened) {
|
||||
@@ -667,8 +673,9 @@ extern "C" void InitSceneSwitcher()
|
||||
ssWindow->raise();
|
||||
ssWindow->activateWindow();
|
||||
} else {
|
||||
ssWindow = new AdvSceneSwitcher(
|
||||
(QMainWindow *)obs_frontend_get_main_window());
|
||||
ssWindow =
|
||||
new AdvSceneSwitcher(static_cast<QMainWindow *>(
|
||||
obs_frontend_get_main_window()));
|
||||
ssWindow->setAttribute(Qt::WA_DeleteOnClose);
|
||||
ssWindow->show();
|
||||
}
|
||||
@@ -677,5 +684,7 @@ extern "C" void InitSceneSwitcher()
|
||||
obs_frontend_add_save_callback(SaveSceneSwitcher, nullptr);
|
||||
obs_frontend_add_event_callback(OBSEvent, switcher);
|
||||
|
||||
QAction *action = (QAction *)obs_frontend_add_tools_menu_qaction(
|
||||
obs_module_text("AdvSceneSwitcher.pluginName"));
|
||||
action->connect(action, &QAction::triggered, cb);
|
||||
}
|
||||
|
||||
5
src/external-macro-modules/CMakeLists.txt
Normal file
5
src/external-macro-modules/CMakeLists.txt
Normal file
@@ -0,0 +1,5 @@
|
||||
# Add macro conditions or actions which have dependencies to external libraries
|
||||
# or other components which might potentially not be fulfilled by the user and
|
||||
# thus cause issues.
|
||||
add_subdirectory(opencv)
|
||||
add_subdirectory(openvr)
|
||||
50
src/external-macro-modules/opencv/CMakeLists.txt
Normal file
50
src/external-macro-modules/opencv/CMakeLists.txt
Normal file
@@ -0,0 +1,50 @@
|
||||
cmake_minimum_required(VERSION 3.14)
|
||||
project(advanced-scene-switcher-opencv)
|
||||
|
||||
add_definitions(-DADVSS_MODULE)
|
||||
|
||||
find_package(OpenCV)
|
||||
if(OpenCV_FOUND)
|
||||
include_directories("${OpenCV_INCLUDE_DIRS}")
|
||||
else()
|
||||
set(OpenCV_LIBRARIES "")
|
||||
message(
|
||||
WARNING
|
||||
"OpenCV not found! Functionality relying on OpenCV will be disabled!\nOpenCV sources are available under: ${CMAKE_CURRENT_SOURCE_DIR}/deps/opencv"
|
||||
)
|
||||
return()
|
||||
endif()
|
||||
|
||||
include_directories("${CMAKE_CURRENT_SOURCE_DIR}/../../headers")
|
||||
set(module_SOURCES macro-condition-video.cpp macro-condition-video.hpp)
|
||||
add_library(advanced-scene-switcher-opencv MODULE ${module_SOURCES})
|
||||
|
||||
if(BUILD_OUT_OF_TREE)
|
||||
target_link_libraries(
|
||||
advanced-scene-switcher-opencv
|
||||
advanced-scene-switcher
|
||||
${LIBOBS_LIB}
|
||||
${LIBOBS_FRONTEND_API_LIB}
|
||||
${OpenCV_LIBRARIES}
|
||||
Qt5::Core
|
||||
Qt5::Widgets)
|
||||
|
||||
if(UNIX AND NOT APPLE)
|
||||
if(NOT LIB_OUT_DIR)
|
||||
set(LIB_OUT_DIR "/lib/obs-plugins")
|
||||
endif()
|
||||
set_target_properties(advanced-scene-switcher-opencv PROPERTIES PREFIX "")
|
||||
install(TARGETS advanced-scene-switcher-opencv
|
||||
LIBRARY DESTINATION ${CMAKE_INSTALL_PREFIX}/${LIB_OUT_DIR})
|
||||
endif()
|
||||
else()
|
||||
target_link_libraries(
|
||||
advanced-scene-switcher-opencv
|
||||
advanced-scene-switcher
|
||||
obs-frontend-api
|
||||
${OpenCV_LIBRARIES}
|
||||
Qt5::Core
|
||||
Qt5::Widgets
|
||||
libobs)
|
||||
install_obs_plugin(advanced-scene-switcher-opencv)
|
||||
endif()
|
||||
@@ -1,7 +1,9 @@
|
||||
#include "headers/macro-condition-edit.hpp"
|
||||
#include "headers/macro-condition-video.hpp"
|
||||
#include "headers/utility.hpp"
|
||||
#include "headers/advanced-scene-switcher.hpp"
|
||||
#include "macro-condition-video.hpp"
|
||||
|
||||
#include <advanced-scene-switcher.hpp>
|
||||
#include <macro-condition-edit.hpp>
|
||||
#include <switcher-data-structs.hpp>
|
||||
#include <utility.hpp>
|
||||
|
||||
#include <QFileDialog>
|
||||
#include <QBuffer>
|
||||
@@ -450,9 +452,9 @@ MacroConditionVideoEdit::MacroConditionVideoEdit(
|
||||
|
||||
_throttleEnable = new QCheckBox();
|
||||
_throttleCount = new QSpinBox();
|
||||
_throttleCount->setMinimum(1 * switcher->interval);
|
||||
_throttleCount->setMaximum(10 * switcher->interval);
|
||||
_throttleCount->setSingleStep(switcher->interval);
|
||||
_throttleCount->setMinimum(1 * GetSwitcher()->interval);
|
||||
_throttleCount->setMaximum(10 * GetSwitcher()->interval);
|
||||
_throttleCount->setSingleStep(GetSwitcher()->interval);
|
||||
_showMatch = new QPushButton(
|
||||
obs_module_text("AdvSceneSwitcher.condition.video.showMatch"));
|
||||
|
||||
@@ -595,7 +597,7 @@ void MacroConditionVideoEdit::SourceChanged(const QString &text)
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
std::lock_guard<std::mutex> lock(GetSwitcher()->m);
|
||||
_entryData->_videoSource = GetWeakSourceByQString(text);
|
||||
_entryData->ResetLastMatch();
|
||||
emit HeaderInfoChanged(
|
||||
@@ -608,7 +610,7 @@ void MacroConditionVideoEdit::ConditionChanged(int cond)
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
std::lock_guard<std::mutex> lock(GetSwitcher()->m);
|
||||
_entryData->_condition = static_cast<VideoCondition>(cond);
|
||||
_entryData->ResetLastMatch();
|
||||
SetWidgetVisibility();
|
||||
@@ -634,7 +636,7 @@ void MacroConditionVideoEdit::ImagePathChanged(const QString &text)
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
std::lock_guard<std::mutex> lock(GetSwitcher()->m);
|
||||
_entryData->_file = text.toUtf8().constData();
|
||||
_entryData->ResetLastMatch();
|
||||
if (_entryData->LoadImageFromFile()) {
|
||||
@@ -712,7 +714,7 @@ void MacroConditionVideoEdit::UsePatternForChangedCheckChanged(int value)
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
std::lock_guard<std::mutex> lock(GetSwitcher()->m);
|
||||
_entryData->_usePatternForChangedCheck = value;
|
||||
_patternThreshold->setVisible(value);
|
||||
adjustSize();
|
||||
@@ -724,7 +726,7 @@ void MacroConditionVideoEdit::PatternThresholdChanged(double value)
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
std::lock_guard<std::mutex> lock(GetSwitcher()->m);
|
||||
_entryData->_patternThreshold = value;
|
||||
}
|
||||
|
||||
@@ -734,7 +736,7 @@ void MacroConditionVideoEdit::UseAlphaAsMaskChanged(int value)
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
std::lock_guard<std::mutex> lock(GetSwitcher()->m);
|
||||
_entryData->_useAlphaAsMask = value;
|
||||
_entryData->LoadImageFromFile();
|
||||
}
|
||||
@@ -745,7 +747,7 @@ void MacroConditionVideoEdit::ObjectScaleThresholdChanged(double value)
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
std::lock_guard<std::mutex> lock(GetSwitcher()->m);
|
||||
_entryData->_scaleFactor = value;
|
||||
}
|
||||
|
||||
@@ -755,7 +757,7 @@ void MacroConditionVideoEdit::MinNeighborsChanged(int value)
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
std::lock_guard<std::mutex> lock(GetSwitcher()->m);
|
||||
_entryData->_minNeighbors = value;
|
||||
}
|
||||
|
||||
@@ -765,7 +767,7 @@ void MacroConditionVideoEdit::MinSizeXChanged(int value)
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
std::lock_guard<std::mutex> lock(GetSwitcher()->m);
|
||||
_entryData->_minSizeX = value;
|
||||
}
|
||||
|
||||
@@ -775,7 +777,7 @@ void MacroConditionVideoEdit::MinSizeYChanged(int value)
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
std::lock_guard<std::mutex> lock(GetSwitcher()->m);
|
||||
_entryData->_minSizeY = value;
|
||||
}
|
||||
|
||||
@@ -785,7 +787,7 @@ void MacroConditionVideoEdit::MaxSizeXChanged(int value)
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
std::lock_guard<std::mutex> lock(GetSwitcher()->m);
|
||||
_entryData->_maxSizeX = value;
|
||||
}
|
||||
|
||||
@@ -795,7 +797,7 @@ void MacroConditionVideoEdit::MaxSizeYChanged(int value)
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
std::lock_guard<std::mutex> lock(GetSwitcher()->m);
|
||||
_entryData->_maxSizeY = value;
|
||||
}
|
||||
|
||||
@@ -805,7 +807,7 @@ void MacroConditionVideoEdit::ThrottleEnableChanged(int value)
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
std::lock_guard<std::mutex> lock(GetSwitcher()->m);
|
||||
_entryData->_throttleEnabled = value;
|
||||
_throttleCount->setEnabled(value);
|
||||
}
|
||||
@@ -816,8 +818,8 @@ void MacroConditionVideoEdit::ThrottleCountChanged(int value)
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_throttleCount = value / switcher->interval;
|
||||
std::lock_guard<std::mutex> lock(GetSwitcher()->m);
|
||||
_entryData->_throttleCount = value / GetSwitcher()->interval;
|
||||
}
|
||||
|
||||
QImage markPatterns(cv::Mat &matchResult, QImage &image, QImage &pattern)
|
||||
@@ -907,7 +909,7 @@ void MacroConditionVideoEdit::ModelPathChanged(const QString &text)
|
||||
|
||||
bool dataLoaded = false;
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
std::lock_guard<std::mutex> lock(GetSwitcher()->m);
|
||||
std::string path = text.toStdString();
|
||||
dataLoaded = _entryData->LoadModelData(path);
|
||||
}
|
||||
@@ -1003,6 +1005,6 @@ void MacroConditionVideoEdit::UpdateEntryData()
|
||||
_maxSizeY->setValue(_entryData->_maxSizeY);
|
||||
_throttleEnable->setChecked(_entryData->_throttleEnabled);
|
||||
_throttleCount->setValue(_entryData->_throttleCount *
|
||||
switcher->interval);
|
||||
GetSwitcher()->interval);
|
||||
SetWidgetVisibility();
|
||||
}
|
||||
@@ -1,13 +1,14 @@
|
||||
#pragma once
|
||||
#include "macro.hpp"
|
||||
#include "screenshot-helper.hpp"
|
||||
#include "file-selection.hpp"
|
||||
#include <macro.hpp>
|
||||
#include <screenshot-helper.hpp>
|
||||
#include <file-selection.hpp>
|
||||
|
||||
#include <QWidget>
|
||||
#include <QComboBox>
|
||||
#include <QCheckBox>
|
||||
#include <QHBoxLayout>
|
||||
#include <chrono>
|
||||
#undef NO // MacOS macro that can conflict with OpenCV
|
||||
#include <opencv2/opencv.hpp>
|
||||
|
||||
enum class VideoCondition {
|
||||
89
src/external-macro-modules/openvr/CMakeLists.txt
Normal file
89
src/external-macro-modules/openvr/CMakeLists.txt
Normal file
@@ -0,0 +1,89 @@
|
||||
cmake_minimum_required(VERSION 3.14)
|
||||
project(advanced-scene-switcher-openvr)
|
||||
|
||||
if(NOT WIN32)
|
||||
message(
|
||||
WARNING "OpenVR condition is only supported on Windows builds for now.")
|
||||
return()
|
||||
endif(NOT WIN32)
|
||||
|
||||
add_definitions(-DADVSS_MODULE)
|
||||
|
||||
# openvr
|
||||
if(NOT OpenVR_DIR)
|
||||
set(OpenVR_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../../../deps/openvr)
|
||||
endif()
|
||||
|
||||
if(EXISTS ${OpenVR_DIR})
|
||||
set(SIZEOF_VOIDP ${CMAKE_SIZEOF_VOID_P})
|
||||
if((NOT APPLE) AND (CMAKE_SIZEOF_VOID_P EQUAL 8))
|
||||
set(PROCESSOR_ARCH "64")
|
||||
else()
|
||||
set(PROCESSOR_ARCH "32")
|
||||
endif()
|
||||
if(WIN32)
|
||||
set(PLATFORM_NAME "win")
|
||||
elseif(UNIX AND NOT APPLE)
|
||||
if(CMAKE_SYSTEM_NAME MATCHES ".*Linux")
|
||||
set(PLATFORM_NAME "linux")
|
||||
endif()
|
||||
elseif(APPLE)
|
||||
if(CMAKE_SYSTEM_NAME MATCHES ".*Darwin.*" OR CMAKE_SYSTEM_NAME MATCHES
|
||||
".*MacOS.*")
|
||||
set(PLATFORM_NAME "osx")
|
||||
endif()
|
||||
endif()
|
||||
set(OpenVR_INCLUDE_DIRS ${OpenVR_DIR}/headers)
|
||||
set(OpenVR_BINARIES
|
||||
${OpenVR_DIR}/bin/${PLATFORM_NAME}${PROCESSOR_ARCH}/${CMAKE_SHARED_LIBRARY_PREFIX}openvr_api${CMAKE_SHARED_LIBRARY_SUFFIX}
|
||||
)
|
||||
set(OpenVR_LIBRARIES
|
||||
${OpenVR_DIR}/lib/${PLATFORM_NAME}${PROCESSOR_ARCH}/${CMAKE_SHARED_LIBRARY_PREFIX}openvr_api${CMAKE_IMPORT_LIBRARY_SUFFIX}
|
||||
)
|
||||
set(OpenVR_FOUND TRUE)
|
||||
endif()
|
||||
|
||||
if(OpenVR_FOUND)
|
||||
include_directories("${OpenVR_INCLUDE_DIRS}")
|
||||
else()
|
||||
set(OpenVR_LIBRARIES "")
|
||||
message(
|
||||
WARNING
|
||||
"OpenVR not found! Functionality relying on OpenVR will be disabled!\nOpenVR sources are available under: ${CMAKE_CURRENT_SOURCE_DIR}/deps/openvr"
|
||||
return
|
||||
())
|
||||
endif()
|
||||
|
||||
include_directories("${CMAKE_CURRENT_SOURCE_DIR}/../../headers")
|
||||
set(module_SOURCES macro-condition-openvr.cpp macro-condition-openvr.hpp)
|
||||
add_library(advanced-scene-switcher-openvr MODULE ${module_SOURCES})
|
||||
|
||||
if(BUILD_OUT_OF_TREE)
|
||||
target_link_libraries(
|
||||
advanced-scene-switcher-openvr
|
||||
advanced-scene-switcher
|
||||
${LIBOBS_LIB}
|
||||
${LIBOBS_FRONTEND_API_LIB}
|
||||
${OpenVR_LIBRARIES}
|
||||
Qt5::Core
|
||||
Qt5::Widgets)
|
||||
|
||||
if(UNIX AND NOT APPLE)
|
||||
if(NOT LIB_OUT_DIR)
|
||||
set(LIB_OUT_DIR "/lib/obs-plugins")
|
||||
endif()
|
||||
set_target_properties(advanced-scene-switcher-openvr PROPERTIES PREFIX "")
|
||||
install(TARGETS advanced-scene-switcher-openvr
|
||||
LIBRARY DESTINATION ${CMAKE_INSTALL_PREFIX}/${LIB_OUT_DIR})
|
||||
endif()
|
||||
else()
|
||||
target_link_libraries(
|
||||
advanced-scene-switcher-openvr
|
||||
advanced-scene-switcher
|
||||
obs-frontend-api
|
||||
${OpenVR_LIBRARIES}
|
||||
Qt5::Core
|
||||
Qt5::Widgets
|
||||
libobs)
|
||||
install_obs_plugin(advanced-scene-switcher-openvr)
|
||||
endif()
|
||||
297
src/external-macro-modules/openvr/macro-condition-openvr.cpp
Normal file
297
src/external-macro-modules/openvr/macro-condition-openvr.cpp
Normal file
@@ -0,0 +1,297 @@
|
||||
#include "macro-condition-openvr.hpp"
|
||||
|
||||
#include <macro-condition-edit.hpp>
|
||||
#include <utility.hpp>
|
||||
#include <advanced-scene-switcher.hpp>
|
||||
#include <openvr.h>
|
||||
|
||||
const std::string MacroConditionOpenVR::id = "openvr";
|
||||
|
||||
bool MacroConditionOpenVR::_registered = MacroConditionFactory::Register(
|
||||
MacroConditionOpenVR::id,
|
||||
{MacroConditionOpenVR::Create, MacroConditionOpenVREdit::Create,
|
||||
"AdvSceneSwitcher.condition.openvr"});
|
||||
|
||||
static vr::IVRSystem *openvrSystem;
|
||||
std::mutex openvrMutex;
|
||||
std::condition_variable openvrCV;
|
||||
|
||||
void processOpenVREvents()
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(openvrMutex);
|
||||
vr::VREvent_t event;
|
||||
while (true) {
|
||||
if (openvrSystem->PollNextEvent(&event, sizeof(event)) &&
|
||||
event.eventType == vr::VREvent_Quit) {
|
||||
openvrSystem->AcknowledgeQuit_Exiting();
|
||||
vr::VR_Shutdown();
|
||||
openvrSystem = nullptr;
|
||||
break;
|
||||
}
|
||||
openvrCV.wait_for(lock, std::chrono::milliseconds(100));
|
||||
}
|
||||
blog(LOG_INFO, "stop handling openVR events");
|
||||
}
|
||||
|
||||
void initOpenVR(vr::EVRInitError &err)
|
||||
{
|
||||
openvrSystem = vr::VR_Init(&err, vr::VRApplication_Background);
|
||||
if (openvrSystem) {
|
||||
// Don't kill OBS if SteamVR is exiting
|
||||
std::thread t(processOpenVREvents);
|
||||
t.detach();
|
||||
}
|
||||
}
|
||||
|
||||
struct TrackingData {
|
||||
float x, y, z;
|
||||
bool valid = false;
|
||||
};
|
||||
|
||||
TrackingData getOpenVRPos(vr::EVRInitError &err)
|
||||
{
|
||||
TrackingData data;
|
||||
std::unique_lock<std::mutex> lock(openvrMutex);
|
||||
if (!openvrSystem) {
|
||||
initOpenVR(err);
|
||||
}
|
||||
|
||||
if (openvrSystem && vr::VRCompositor() &&
|
||||
openvrSystem->IsTrackedDeviceConnected(0)) {
|
||||
vr::TrackedDevicePose_t poses[vr::k_unMaxTrackedDeviceCount];
|
||||
openvrSystem->GetDeviceToAbsoluteTrackingPose(
|
||||
vr::TrackingUniverseStanding, 0.0, poses,
|
||||
vr::k_unMaxTrackedDeviceCount);
|
||||
auto hmdPose = poses[vr::k_unTrackedDeviceIndex_Hmd];
|
||||
auto mat = hmdPose.mDeviceToAbsoluteTracking.m;
|
||||
data.x = mat[0][3];
|
||||
data.y = mat[1][3];
|
||||
data.z = mat[2][3];
|
||||
data.valid = true;
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
bool MacroConditionOpenVR::CheckCondition()
|
||||
{
|
||||
vr::EVRInitError err;
|
||||
TrackingData data = getOpenVRPos(err);
|
||||
if (!data.valid) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return data.x >= _minX && data.y >= _minY && data.z >= _minZ &&
|
||||
data.x <= _maxX && data.y <= _maxY && data.z <= _maxZ;
|
||||
}
|
||||
|
||||
bool MacroConditionOpenVR::Save(obs_data_t *obj)
|
||||
{
|
||||
MacroCondition::Save(obj);
|
||||
obs_data_set_double(obj, "minX", _minX);
|
||||
obs_data_set_double(obj, "minY", _minY);
|
||||
obs_data_set_double(obj, "minZ", _minZ);
|
||||
obs_data_set_double(obj, "maxX", _maxX);
|
||||
obs_data_set_double(obj, "maxY", _maxY);
|
||||
obs_data_set_double(obj, "maxZ", _maxZ);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MacroConditionOpenVR::Load(obs_data_t *obj)
|
||||
{
|
||||
MacroCondition::Load(obj);
|
||||
_minX = obs_data_get_double(obj, "minX");
|
||||
_minY = obs_data_get_double(obj, "minY");
|
||||
_minZ = obs_data_get_double(obj, "minZ");
|
||||
_maxX = obs_data_get_double(obj, "maxX");
|
||||
_maxY = obs_data_get_double(obj, "maxY");
|
||||
_maxZ = obs_data_get_double(obj, "maxZ");
|
||||
return true;
|
||||
}
|
||||
|
||||
MacroConditionOpenVREdit::MacroConditionOpenVREdit(
|
||||
QWidget *parent, std::shared_ptr<MacroConditionOpenVR> entryData)
|
||||
: QWidget(parent)
|
||||
{
|
||||
_minX = new QDoubleSpinBox();
|
||||
_minY = new QDoubleSpinBox();
|
||||
_minZ = new QDoubleSpinBox();
|
||||
_maxX = new QDoubleSpinBox();
|
||||
_maxY = new QDoubleSpinBox();
|
||||
_maxZ = new QDoubleSpinBox();
|
||||
_xPos = new QLabel("-");
|
||||
_yPos = new QLabel("-");
|
||||
_zPos = new QLabel("-");
|
||||
_errLabel = new QLabel();
|
||||
_errLabel->setVisible(false);
|
||||
|
||||
_minX->setPrefix("Min X: ");
|
||||
_minY->setPrefix("Min Y: ");
|
||||
_minZ->setPrefix("Min Z: ");
|
||||
_maxX->setPrefix("Max X: ");
|
||||
_maxY->setPrefix("Max Y: ");
|
||||
_maxZ->setPrefix("Max Z: ");
|
||||
|
||||
_minX->setMinimum(-99);
|
||||
_minY->setMinimum(-99);
|
||||
_minZ->setMinimum(-99);
|
||||
_maxX->setMinimum(-99);
|
||||
_maxY->setMinimum(-99);
|
||||
_maxZ->setMinimum(-99);
|
||||
|
||||
_minX->setMaximum(99);
|
||||
_minY->setMaximum(99);
|
||||
_minZ->setMaximum(99);
|
||||
_maxX->setMaximum(99);
|
||||
_maxY->setMaximum(99);
|
||||
_maxZ->setMaximum(99);
|
||||
|
||||
QWidget::connect(_minX, SIGNAL(valueChanged(double)), this,
|
||||
SLOT(MinXChanged(double)));
|
||||
QWidget::connect(_minY, SIGNAL(valueChanged(double)), this,
|
||||
SLOT(MinYChanged(double)));
|
||||
QWidget::connect(_minZ, SIGNAL(valueChanged(double)), this,
|
||||
SLOT(MinZChanged(double)));
|
||||
QWidget::connect(_maxX, SIGNAL(valueChanged(double)), this,
|
||||
SLOT(MaxXChanged(double)));
|
||||
QWidget::connect(_maxY, SIGNAL(valueChanged(double)), this,
|
||||
SLOT(MaxYChanged(double)));
|
||||
QWidget::connect(_maxZ, SIGNAL(valueChanged(double)), this,
|
||||
SLOT(MaxZChanged(double)));
|
||||
|
||||
QGridLayout *controlsLayout = new QGridLayout;
|
||||
controlsLayout->addWidget(_minX, 0, 0);
|
||||
controlsLayout->addWidget(_minY, 0, 1);
|
||||
controlsLayout->addWidget(_minZ, 0, 2);
|
||||
controlsLayout->addWidget(_maxX, 1, 0);
|
||||
controlsLayout->addWidget(_maxY, 1, 1);
|
||||
controlsLayout->addWidget(_maxZ, 1, 2);
|
||||
QWidget *controls = new QWidget;
|
||||
controls->setLayout(controlsLayout);
|
||||
controls->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
|
||||
|
||||
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
|
||||
{"{{controls}}", controls},
|
||||
{"{{xPos}}", _xPos},
|
||||
{"{{yPos}}", _yPos},
|
||||
{"{{zPos}}", _zPos},
|
||||
};
|
||||
QHBoxLayout *line1 = new QHBoxLayout;
|
||||
placeWidgets(obs_module_text(
|
||||
"AdvSceneSwitcher.condition.openvr.entry.line1"),
|
||||
line1, widgetPlaceholders);
|
||||
QHBoxLayout *line2 = new QHBoxLayout;
|
||||
placeWidgets(obs_module_text(
|
||||
"AdvSceneSwitcher.condition.openvr.entry.line2"),
|
||||
line2, widgetPlaceholders);
|
||||
QHBoxLayout *line3 = new QHBoxLayout;
|
||||
placeWidgets(obs_module_text(
|
||||
"AdvSceneSwitcher.condition.openvr.entry.line3"),
|
||||
line3, widgetPlaceholders);
|
||||
QVBoxLayout *mainLayout = new QVBoxLayout;
|
||||
mainLayout->addLayout(line1);
|
||||
mainLayout->addLayout(line2);
|
||||
mainLayout->addLayout(line3);
|
||||
mainLayout->addWidget(_errLabel);
|
||||
setLayout(mainLayout);
|
||||
|
||||
connect(&_timer, &QTimer::timeout, this,
|
||||
&MacroConditionOpenVREdit::UpdateOpenVRPos);
|
||||
_timer.start(1000);
|
||||
|
||||
_entryData = entryData;
|
||||
UpdateEntryData();
|
||||
_loading = false;
|
||||
}
|
||||
|
||||
void MacroConditionOpenVREdit::MinXChanged(double pos)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(GetSwitcher()->m);
|
||||
_entryData->_minX = pos;
|
||||
}
|
||||
|
||||
void MacroConditionOpenVREdit::MinYChanged(double pos)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(GetSwitcher()->m);
|
||||
_entryData->_minY = pos;
|
||||
}
|
||||
|
||||
void MacroConditionOpenVREdit::MinZChanged(double pos)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(GetSwitcher()->m);
|
||||
_entryData->_minZ = pos;
|
||||
}
|
||||
|
||||
void MacroConditionOpenVREdit::MaxXChanged(double pos)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(GetSwitcher()->m);
|
||||
_entryData->_maxX = pos;
|
||||
}
|
||||
|
||||
void MacroConditionOpenVREdit::MaxYChanged(double pos)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(GetSwitcher()->m);
|
||||
_entryData->_maxY = pos;
|
||||
}
|
||||
|
||||
void MacroConditionOpenVREdit::MaxZChanged(double pos)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(GetSwitcher()->m);
|
||||
_entryData->_maxZ = pos;
|
||||
}
|
||||
|
||||
void MacroConditionOpenVREdit::UpdateOpenVRPos()
|
||||
{
|
||||
vr::EVRInitError err;
|
||||
TrackingData data = getOpenVRPos(err);
|
||||
if (data.valid) {
|
||||
_xPos->setText(QString::number(data.x));
|
||||
_yPos->setText(QString::number(data.y));
|
||||
_zPos->setText(QString::number(data.z));
|
||||
} else {
|
||||
_xPos->setText("-");
|
||||
_yPos->setText("-");
|
||||
_zPos->setText("-");
|
||||
_errLabel->setText(
|
||||
QString(obs_module_text(
|
||||
"AdvSceneSwitcher.condition.errorStatus")) +
|
||||
QString(vr::VR_GetVRInitErrorAsEnglishDescription(err)));
|
||||
}
|
||||
_errLabel->setVisible(!data.valid);
|
||||
adjustSize();
|
||||
}
|
||||
|
||||
void MacroConditionOpenVREdit::UpdateEntryData()
|
||||
{
|
||||
if (!_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
_minX->setValue(_entryData->_minX);
|
||||
_minY->setValue(_entryData->_minY);
|
||||
_maxX->setValue(_entryData->_maxX);
|
||||
_maxY->setValue(_entryData->_maxY);
|
||||
}
|
||||
68
src/external-macro-modules/openvr/macro-condition-openvr.hpp
Normal file
68
src/external-macro-modules/openvr/macro-condition-openvr.hpp
Normal file
@@ -0,0 +1,68 @@
|
||||
#pragma once
|
||||
#include <macro.hpp>
|
||||
|
||||
#include <QDoubleSpinBox>
|
||||
#include <QComboBox>
|
||||
#include <QPushButton>
|
||||
#include <QTimer>
|
||||
|
||||
class MacroConditionOpenVR : 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<MacroConditionOpenVR>();
|
||||
}
|
||||
|
||||
double _minX = 0, _minY = 0, _minZ = 0, _maxX = 0, _maxY = 0, _maxZ = 0;
|
||||
|
||||
private:
|
||||
static bool _registered;
|
||||
static const std::string id;
|
||||
};
|
||||
|
||||
class MacroConditionOpenVREdit : public QWidget {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
MacroConditionOpenVREdit(
|
||||
QWidget *parent,
|
||||
std::shared_ptr<MacroConditionOpenVR> cond = nullptr);
|
||||
void UpdateEntryData();
|
||||
static QWidget *Create(QWidget *parent,
|
||||
std::shared_ptr<MacroCondition> cond)
|
||||
{
|
||||
return new MacroConditionOpenVREdit(
|
||||
parent,
|
||||
std::dynamic_pointer_cast<MacroConditionOpenVR>(cond));
|
||||
}
|
||||
|
||||
private slots:
|
||||
void MinXChanged(double pos);
|
||||
void MinYChanged(double pos);
|
||||
void MinZChanged(double pos);
|
||||
void MaxXChanged(double pos);
|
||||
void MaxYChanged(double pos);
|
||||
void MaxZChanged(double pos);
|
||||
void UpdateOpenVRPos();
|
||||
|
||||
protected:
|
||||
QDoubleSpinBox *_minX;
|
||||
QDoubleSpinBox *_minY;
|
||||
QDoubleSpinBox *_minZ;
|
||||
QDoubleSpinBox *_maxX;
|
||||
QDoubleSpinBox *_maxY;
|
||||
QDoubleSpinBox *_maxZ;
|
||||
QLabel *_xPos;
|
||||
QLabel *_yPos;
|
||||
QLabel *_zPos;
|
||||
QLabel *_errLabel;
|
||||
std::shared_ptr<MacroConditionOpenVR> _entryData;
|
||||
|
||||
private:
|
||||
QTimer _timer;
|
||||
bool _loading = true;
|
||||
};
|
||||
@@ -1,12 +1,10 @@
|
||||
#include "headers/advanced-scene-switcher.hpp"
|
||||
#include "headers/status-dock.hpp"
|
||||
#include "headers/utility.hpp"
|
||||
#include "headers/version.h"
|
||||
|
||||
#include <QFileDialog>
|
||||
#include <QGuiApplication>
|
||||
#if __linux__
|
||||
#include <QDesktopWidget>
|
||||
#endif
|
||||
#include <QStandardPaths>
|
||||
|
||||
QMetaObject::Connection inactivePluse;
|
||||
|
||||
@@ -231,13 +229,53 @@ void AdvSceneSwitcher::on_uiHintsDisable_stateChanged(int state)
|
||||
switcher->disableHints = state;
|
||||
}
|
||||
|
||||
QString getDefaultSaveLocation()
|
||||
{
|
||||
QString desktopPath = QStandardPaths::writableLocation(
|
||||
QStandardPaths::DesktopLocation);
|
||||
auto scName = obs_frontend_get_current_scene_collection();
|
||||
QString sceneCollectionName(scName);
|
||||
bfree(scName);
|
||||
auto timestamp = QDateTime::currentDateTime();
|
||||
return desktopPath + "/adv-ss-" + sceneCollectionName + "-" +
|
||||
timestamp.toString("yyyy.MM.dd.hh.mm.ss");
|
||||
}
|
||||
|
||||
void AskForBackup(obs_data_t *obj)
|
||||
{
|
||||
bool backupSettings = DisplayMessage(
|
||||
obs_module_text("AdvSceneSwitcher.askBackup"), true);
|
||||
|
||||
if (!backupSettings) {
|
||||
return;
|
||||
}
|
||||
|
||||
QString path = QFileDialog::getSaveFileName(
|
||||
nullptr,
|
||||
obs_module_text(
|
||||
"AdvSceneSwitcher.generalTab.saveOrLoadsettings.importWindowTitle"),
|
||||
getDefaultSaveLocation(),
|
||||
obs_module_text(
|
||||
"AdvSceneSwitcher.generalTab.saveOrLoadsettings.textType"));
|
||||
if (path.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
QFile file(path);
|
||||
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(
|
||||
this,
|
||||
tr(obs_module_text(
|
||||
"AdvSceneSwitcher.generalTab.saveOrLoadsettings.exportWindowTitle")),
|
||||
QDir::currentPath(),
|
||||
getDefaultSaveLocation(),
|
||||
tr(obs_module_text(
|
||||
"AdvSceneSwitcher.generalTab.saveOrLoadsettings.textType")));
|
||||
if (directory.isEmpty()) {
|
||||
@@ -398,20 +436,6 @@ void AdvSceneSwitcher::setTabOrder()
|
||||
connect(bar, &QTabBar::tabMoved, this, &AdvSceneSwitcher::on_tabMoved);
|
||||
}
|
||||
|
||||
bool windowPosValid(QPoint pos)
|
||||
{
|
||||
#if __linux__
|
||||
// QDesktopWidget::availableGeometry() is deprecated.
|
||||
// Switch to QGuiApplication::screenAt() when it is available on most Linux platforms.
|
||||
QDesktopWidget *desktop = QApplication::desktop();
|
||||
auto screen = desktop->availableGeometry(pos);
|
||||
return (pos.x() > screen.x()) && (pos.y() > screen.y()) &&
|
||||
(pos.x() < screen.width() && (pos.y() < screen.height()));
|
||||
#else
|
||||
return !!QGuiApplication::screenAt(pos);
|
||||
#endif
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::restoreWindowGeo()
|
||||
{
|
||||
if (switcher->saveWindowGeo && windowPosValid(switcher->windowPos)) {
|
||||
@@ -420,6 +444,14 @@ void AdvSceneSwitcher::restoreWindowGeo()
|
||||
}
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::checkFirstTimeSetup()
|
||||
{
|
||||
if (switcher->firstBoot && !switcher->disableHints) {
|
||||
DisplayMessage(
|
||||
obs_module_text("AdvSceneSwitcher.firstBootMessage"));
|
||||
}
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::on_tabMoved(int from, int to)
|
||||
{
|
||||
if (loading) {
|
||||
@@ -429,10 +461,8 @@ void AdvSceneSwitcher::on_tabMoved(int from, int to)
|
||||
std::swap(switcher->tabOrder[from], switcher->tabOrder[to]);
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::on_tabWidget_currentChanged(int index)
|
||||
void AdvSceneSwitcher::on_tabWidget_currentChanged(int)
|
||||
{
|
||||
UNUSED_PARAMETER(index);
|
||||
|
||||
switcher->showFrame = false;
|
||||
clearFrames(ui->screenRegionSwitches);
|
||||
SetShowFrames();
|
||||
@@ -463,6 +493,7 @@ void SwitcherData::loadSettings(obs_data_t *obj)
|
||||
loadSceneTriggers(obj);
|
||||
loadGeneralSettings(obj);
|
||||
loadHotkeys(obj);
|
||||
loadDock(obj);
|
||||
}
|
||||
|
||||
void SwitcherData::saveSettings(obs_data_t *obj)
|
||||
@@ -493,6 +524,7 @@ void SwitcherData::saveSettings(obs_data_t *obj)
|
||||
saveSceneTriggers(obj);
|
||||
saveGeneralSettings(obj);
|
||||
saveHotkeys(obj);
|
||||
saveDock(obj);
|
||||
saveVersion(obj, g_GIT_SHA1);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
#pragma once
|
||||
#ifdef BUILD_OUT_OF_TREE
|
||||
#include "../../forms/ui_advanced-scene-switcher.h"
|
||||
#elif defined ADVSS_MODULE
|
||||
#include "../../../ui_advanced-scene-switcher.h"
|
||||
#else
|
||||
#include "ui_advanced-scene-switcher.h"
|
||||
#endif
|
||||
@@ -81,6 +83,7 @@ public:
|
||||
void setupMacroTab();
|
||||
void setTabOrder();
|
||||
void restoreWindowGeo();
|
||||
void checkFirstTimeSetup();
|
||||
|
||||
signals:
|
||||
void MacroAdded(const QString &name);
|
||||
@@ -115,6 +118,9 @@ public slots:
|
||||
void on_macroUp_clicked();
|
||||
void on_macroDown_clicked();
|
||||
void on_macroName_editingFinished();
|
||||
void on_runMacro_clicked();
|
||||
void on_runMacroInParallel_stateChanged(int value);
|
||||
void on_runMacroOnChange_stateChanged(int value);
|
||||
void on_macros_currentRowChanged(int idx);
|
||||
void on_macros_itemChanged(QListWidgetItem *);
|
||||
void on_conditionAdd_clicked();
|
||||
@@ -285,8 +291,14 @@ void restoreTransitionOverride(obs_source_t *scene, const transitionData &td);
|
||||
void switchScene(const sceneSwitchInfo &ssi);
|
||||
void switchPreviewScene(const OBSWeakSource &ws);
|
||||
|
||||
/******************************************************************************
|
||||
* Settings helper
|
||||
******************************************************************************/
|
||||
void AskForBackup(obs_data_t *obj);
|
||||
|
||||
/******************************************************************************
|
||||
* Main SwitcherData
|
||||
******************************************************************************/
|
||||
struct SwitcherData;
|
||||
extern SwitcherData *switcher;
|
||||
SwitcherData *GetSwitcher();
|
||||
|
||||
@@ -5,8 +5,8 @@ struct MacroActionInfo {
|
||||
using TCreateMethod = std::shared_ptr<MacroAction> (*)();
|
||||
using TCreateWidgetMethod = QWidget *(*)(QWidget *parent,
|
||||
std::shared_ptr<MacroAction>);
|
||||
TCreateMethod _createFunc;
|
||||
TCreateWidgetMethod _createWidgetFunc;
|
||||
TCreateMethod _createFunc = nullptr;
|
||||
TCreateWidgetMethod _createWidgetFunc = nullptr;
|
||||
std::string _name;
|
||||
};
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ enum class PerformMacroAction {
|
||||
PAUSE,
|
||||
UNPAUSE,
|
||||
RESET_COUNTER,
|
||||
RUN,
|
||||
};
|
||||
|
||||
class MacroActionMacro : public MacroRefAction {
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
#pragma once
|
||||
#include "macro-action-edit.hpp"
|
||||
#include "file-selection.hpp"
|
||||
|
||||
#include <QDoubleSpinBox>
|
||||
#include <QHBoxLayout>
|
||||
#include <QLabel>
|
||||
|
||||
enum class PluginStateAction {
|
||||
STOP,
|
||||
NO_MATCH_BEHAVIOUR,
|
||||
IMPORT_SETTINGS,
|
||||
};
|
||||
|
||||
class MacroActionPluginState : public MacroAction {
|
||||
@@ -23,6 +25,7 @@ public:
|
||||
|
||||
PluginStateAction _action = PluginStateAction::STOP;
|
||||
int _value = 0;
|
||||
std::string _settingsPath;
|
||||
OBSWeakSource _scene;
|
||||
|
||||
private:
|
||||
@@ -51,15 +54,17 @@ private slots:
|
||||
void ActionChanged(int value);
|
||||
void ValueChanged(int value);
|
||||
void SceneChanged(const QString &text);
|
||||
void PathChanged(const QString &text);
|
||||
|
||||
protected:
|
||||
QComboBox *_actions;
|
||||
QComboBox *_values;
|
||||
QComboBox *_scenes;
|
||||
FileSelection *_settings;
|
||||
QLabel *_settingsWarning;
|
||||
std::shared_ptr<MacroActionPluginState> _entryData;
|
||||
|
||||
private:
|
||||
void SetWidgetVisibility(PluginStateAction, int);
|
||||
QHBoxLayout *_mainLayout;
|
||||
void SetWidgetVisibility();
|
||||
bool _loading = true;
|
||||
};
|
||||
|
||||
51
src/headers/macro-action-profile.hpp
Normal file
51
src/headers/macro-action-profile.hpp
Normal file
@@ -0,0 +1,51 @@
|
||||
#pragma once
|
||||
#include "macro-action-edit.hpp"
|
||||
|
||||
class MacroActionProfile : public MacroAction {
|
||||
public:
|
||||
bool PerformAction();
|
||||
void LogAction();
|
||||
bool Save(obs_data_t *obj);
|
||||
bool Load(obs_data_t *obj);
|
||||
std::string GetShortDesc();
|
||||
std::string GetId() { return id; };
|
||||
static std::shared_ptr<MacroAction> Create()
|
||||
{
|
||||
return std::make_shared<MacroActionProfile>();
|
||||
}
|
||||
|
||||
std::string _profile;
|
||||
|
||||
private:
|
||||
static bool _registered;
|
||||
static const std::string id;
|
||||
};
|
||||
|
||||
class MacroActionProfileEdit : public QWidget {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
MacroActionProfileEdit(
|
||||
QWidget *parent,
|
||||
std::shared_ptr<MacroActionProfile> entryData = nullptr);
|
||||
void UpdateEntryData();
|
||||
static QWidget *Create(QWidget *parent,
|
||||
std::shared_ptr<MacroAction> action)
|
||||
{
|
||||
return new MacroActionProfileEdit(
|
||||
parent,
|
||||
std::dynamic_pointer_cast<MacroActionProfile>(action));
|
||||
}
|
||||
|
||||
private slots:
|
||||
void ProfileChanged(const QString &text);
|
||||
signals:
|
||||
void HeaderInfoChanged(const QString &);
|
||||
|
||||
protected:
|
||||
QComboBox *_profiles;
|
||||
std::shared_ptr<MacroActionProfile> _entryData;
|
||||
|
||||
private:
|
||||
bool _loading = true;
|
||||
};
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
#include <QPushButton>
|
||||
#include <QListWidget>
|
||||
#include <QDialog>
|
||||
#include <unordered_map>
|
||||
|
||||
class MacroActionRandom : public MultiMacroRefAction {
|
||||
@@ -27,6 +28,17 @@ private:
|
||||
static const std::string id;
|
||||
};
|
||||
|
||||
class MacroDialog : public QDialog {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
MacroDialog(QWidget *parent);
|
||||
static bool AskForMacro(QWidget *parent, std::string ¯oName);
|
||||
|
||||
private:
|
||||
MacroSelection *_macroSelection;
|
||||
};
|
||||
|
||||
class MacroActionRandomEdit : public QWidget {
|
||||
Q_OBJECT
|
||||
|
||||
@@ -48,14 +60,14 @@ private slots:
|
||||
void MacroRename(const QString &oldName, const QString &newName);
|
||||
void AddMacro();
|
||||
void RemoveMacro();
|
||||
int FindEntry(const std::string ¯o);
|
||||
void MacroSelectionChanged(int idx);
|
||||
|
||||
protected:
|
||||
std::shared_ptr<MacroActionRandom> _entryData;
|
||||
|
||||
private:
|
||||
MacroSelection *_macroSelection;
|
||||
int FindEntry(const std::string ¯o);
|
||||
void SetMacroListSize();
|
||||
|
||||
QListWidget *_macroList;
|
||||
QPushButton *_add;
|
||||
QPushButton *_remove;
|
||||
|
||||
@@ -56,6 +56,8 @@ protected:
|
||||
std::shared_ptr<MacroActionRun> _entryData;
|
||||
|
||||
private:
|
||||
void SetArgListSize();
|
||||
|
||||
FileSelection *_filePath;
|
||||
QListWidget *_argList;
|
||||
QPushButton *_addArg;
|
||||
|
||||
52
src/headers/macro-action-scene-collection.hpp
Normal file
52
src/headers/macro-action-scene-collection.hpp
Normal file
@@ -0,0 +1,52 @@
|
||||
#pragma once
|
||||
#include "macro-action-edit.hpp"
|
||||
|
||||
class MacroActionSceneCollection : public MacroAction {
|
||||
public:
|
||||
bool PerformAction();
|
||||
void LogAction();
|
||||
bool Save(obs_data_t *obj);
|
||||
bool Load(obs_data_t *obj);
|
||||
std::string GetShortDesc();
|
||||
std::string GetId() { return id; };
|
||||
static std::shared_ptr<MacroAction> Create()
|
||||
{
|
||||
return std::make_shared<MacroActionSceneCollection>();
|
||||
}
|
||||
|
||||
std::string _sceneCollection;
|
||||
|
||||
private:
|
||||
static bool _registered;
|
||||
static const std::string id;
|
||||
};
|
||||
|
||||
class MacroActionSceneCollectionEdit : public QWidget {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
MacroActionSceneCollectionEdit(
|
||||
QWidget *parent,
|
||||
std::shared_ptr<MacroActionSceneCollection> entryData = nullptr);
|
||||
void UpdateEntryData();
|
||||
static QWidget *Create(QWidget *parent,
|
||||
std::shared_ptr<MacroAction> action)
|
||||
{
|
||||
return new MacroActionSceneCollectionEdit(
|
||||
parent,
|
||||
std::dynamic_pointer_cast<MacroActionSceneCollection>(
|
||||
action));
|
||||
}
|
||||
|
||||
private slots:
|
||||
void SceneCollectionChanged(const QString &text);
|
||||
signals:
|
||||
void HeaderInfoChanged(const QString &);
|
||||
|
||||
protected:
|
||||
QComboBox *_sceneCollections;
|
||||
std::shared_ptr<MacroActionSceneCollection> _entryData;
|
||||
|
||||
private:
|
||||
bool _loading = true;
|
||||
};
|
||||
@@ -1,7 +1,9 @@
|
||||
#pragma once
|
||||
#include <QSpinBox>
|
||||
#include "macro-action-edit.hpp"
|
||||
#include "scene-selection.hpp"
|
||||
#include "scene-item-selection.hpp"
|
||||
|
||||
#include <QSpinBox>
|
||||
|
||||
enum class SceneOrderAction {
|
||||
MOVE_UP,
|
||||
@@ -25,7 +27,7 @@ public:
|
||||
}
|
||||
|
||||
SceneSelection _scene;
|
||||
OBSWeakSource _source;
|
||||
SceneItemSelection _source;
|
||||
SceneOrderAction _action = SceneOrderAction::MOVE_UP;
|
||||
int _position = 0;
|
||||
|
||||
@@ -53,7 +55,7 @@ public:
|
||||
|
||||
private slots:
|
||||
void SceneChanged(const SceneSelection &);
|
||||
void SourceChanged(const QString &text);
|
||||
void SourceChanged(const SceneItemSelection &);
|
||||
void ActionChanged(int value);
|
||||
void PositionChanged(int value);
|
||||
signals:
|
||||
@@ -61,7 +63,7 @@ signals:
|
||||
|
||||
protected:
|
||||
SceneSelectionWidget *_scenes;
|
||||
QComboBox *_sources;
|
||||
SceneItemSelectionWidget *_sources;
|
||||
QComboBox *_actions;
|
||||
QSpinBox *_position;
|
||||
std::shared_ptr<MacroActionSceneOrder> _entryData;
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
#pragma once
|
||||
#include "macro-action-edit.hpp"
|
||||
#include "switch-generic.hpp"
|
||||
#include "scene-selection.hpp"
|
||||
#include "transition-selection.hpp"
|
||||
#include "duration-control.hpp"
|
||||
|
||||
// TODO: Switch to using SceneSelection class and widget instead
|
||||
#include <QCheckBox>
|
||||
|
||||
class MacroActionSwitchScene : public MacroAction, public SceneSwitcherEntry {
|
||||
class MacroActionSwitchScene : public MacroAction {
|
||||
public:
|
||||
bool PerformAction();
|
||||
void LogAction();
|
||||
@@ -18,7 +19,10 @@ public:
|
||||
{
|
||||
return std::make_shared<MacroActionSwitchScene>();
|
||||
}
|
||||
SceneSelection _scene;
|
||||
TransitionSelection _transition;
|
||||
Duration _duration;
|
||||
bool _blockUntilTransitionDone = true;
|
||||
|
||||
private:
|
||||
const char *getType() { return "MacroActionSwitchScene"; }
|
||||
@@ -27,7 +31,7 @@ private:
|
||||
static const std::string id;
|
||||
};
|
||||
|
||||
class MacroActionSwitchSceneEdit : public SwitchWidget {
|
||||
class MacroActionSwitchSceneEdit : public QWidget {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
@@ -44,12 +48,21 @@ public:
|
||||
}
|
||||
|
||||
private slots:
|
||||
void SceneChanged(const SceneSelection &);
|
||||
void TransitionChanged(const TransitionSelection &);
|
||||
void DurationChanged(double seconds);
|
||||
void ChangeHeaderInfo(const QString &);
|
||||
void BlockUntilTransitionDoneChanged(int state);
|
||||
signals:
|
||||
void HeaderInfoChanged(const QString &);
|
||||
|
||||
protected:
|
||||
SceneSelectionWidget *_scenes;
|
||||
TransitionSelectionWidget *_transitions;
|
||||
DurationSelection *_duration;
|
||||
QCheckBox *_blockUntilTransitionDone;
|
||||
|
||||
std::shared_ptr<MacroActionSwitchScene> _entryData;
|
||||
|
||||
private:
|
||||
bool _loading = true;
|
||||
};
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
#pragma once
|
||||
#include <QSpinBox>
|
||||
#include <QPlainTextEdit>
|
||||
#include "macro-action-edit.hpp"
|
||||
#include "scene-selection.hpp"
|
||||
#include "scene-item-selection.hpp"
|
||||
|
||||
#include <QSpinBox>
|
||||
#include <QPlainTextEdit>
|
||||
|
||||
class MacroActionSceneTransform : public MacroAction {
|
||||
public:
|
||||
@@ -20,9 +22,9 @@ public:
|
||||
void SetSettings(std::string &);
|
||||
|
||||
SceneSelection _scene;
|
||||
OBSWeakSource _source;
|
||||
struct obs_transform_info _info;
|
||||
struct obs_sceneitem_crop _crop;
|
||||
SceneItemSelection _source;
|
||||
struct obs_transform_info _info = {};
|
||||
struct obs_sceneitem_crop _crop = {};
|
||||
|
||||
private:
|
||||
static bool _registered;
|
||||
@@ -48,7 +50,7 @@ public:
|
||||
|
||||
private slots:
|
||||
void SceneChanged(const SceneSelection &);
|
||||
void SourceChanged(const QString &text);
|
||||
void SourceChanged(const SceneItemSelection &);
|
||||
void GetSettingsClicked();
|
||||
void SettingsChanged();
|
||||
signals:
|
||||
@@ -56,7 +58,7 @@ signals:
|
||||
|
||||
protected:
|
||||
SceneSelectionWidget *_scenes;
|
||||
QComboBox *_sources;
|
||||
SceneItemSelectionWidget *_sources;
|
||||
QPushButton *_getSettings;
|
||||
QPlainTextEdit *_settings;
|
||||
std::shared_ptr<MacroActionSceneTransform> _entryData;
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
#pragma once
|
||||
#include <QSpinBox>
|
||||
#include "macro-action-edit.hpp"
|
||||
#include "scene-selection.hpp"
|
||||
#include "scene-item-selection.hpp"
|
||||
|
||||
#include <QSpinBox>
|
||||
|
||||
enum class SceneVisibilityAction {
|
||||
SHOW,
|
||||
@@ -28,7 +30,7 @@ public:
|
||||
|
||||
SceneSelection _scene;
|
||||
SceneItemSourceType _sourceType = SceneItemSourceType::SOURCE;
|
||||
OBSWeakSource _source;
|
||||
SceneItemSelection _source;
|
||||
std::string _sourceGroup = "";
|
||||
SceneVisibilityAction _action = SceneVisibilityAction::SHOW;
|
||||
|
||||
@@ -57,7 +59,8 @@ public:
|
||||
private slots:
|
||||
void SceneChanged(const SceneSelection &);
|
||||
void SourceTypeChanged(int value);
|
||||
void SourceChanged(const QString &text);
|
||||
void SourceChanged(const SceneItemSelection &);
|
||||
void SourceGroupChanged(const QString &text);
|
||||
void ActionChanged(int value);
|
||||
signals:
|
||||
void HeaderInfoChanged(const QString &);
|
||||
@@ -65,11 +68,14 @@ signals:
|
||||
protected:
|
||||
SceneSelectionWidget *_scenes;
|
||||
QComboBox *_sourceTypes;
|
||||
QComboBox *_sources;
|
||||
SceneItemSelectionWidget *_sources;
|
||||
QComboBox *_sourceGroups;
|
||||
QComboBox *_actions;
|
||||
std::shared_ptr<MacroActionSceneVisibility> _entryData;
|
||||
|
||||
private:
|
||||
void SetWidgetVisibility();
|
||||
|
||||
QHBoxLayout *_mainLayout;
|
||||
bool _loading = true;
|
||||
};
|
||||
|
||||
50
src/headers/macro-action-screenshot.hpp
Normal file
50
src/headers/macro-action-screenshot.hpp
Normal file
@@ -0,0 +1,50 @@
|
||||
#pragma once
|
||||
#include "macro-action-edit.hpp"
|
||||
|
||||
class MacroActionScreenshot : public MacroAction {
|
||||
public:
|
||||
bool PerformAction();
|
||||
void LogAction();
|
||||
bool Save(obs_data_t *obj);
|
||||
bool Load(obs_data_t *obj);
|
||||
std::string GetShortDesc();
|
||||
std::string GetId() { return id; };
|
||||
static std::shared_ptr<MacroAction> Create()
|
||||
{
|
||||
return std::make_shared<MacroActionScreenshot>();
|
||||
}
|
||||
OBSWeakSource _source;
|
||||
|
||||
private:
|
||||
static bool _registered;
|
||||
static const std::string id;
|
||||
};
|
||||
|
||||
class MacroActionScreenshotEdit : public QWidget {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
MacroActionScreenshotEdit(
|
||||
QWidget *parent,
|
||||
std::shared_ptr<MacroActionScreenshot> entryData = nullptr);
|
||||
void UpdateEntryData();
|
||||
static QWidget *Create(QWidget *parent,
|
||||
std::shared_ptr<MacroAction> action)
|
||||
{
|
||||
return new MacroActionScreenshotEdit(
|
||||
parent,
|
||||
std::dynamic_pointer_cast<MacroActionScreenshot>(
|
||||
action));
|
||||
}
|
||||
private slots:
|
||||
void SourceChanged(const QString &text);
|
||||
signals:
|
||||
void HeaderInfoChanged(const QString &);
|
||||
|
||||
protected:
|
||||
QComboBox *_sources;
|
||||
std::shared_ptr<MacroActionScreenshot> _entryData;
|
||||
|
||||
private:
|
||||
bool _loading = true;
|
||||
};
|
||||
49
src/headers/macro-action-systray.hpp
Normal file
49
src/headers/macro-action-systray.hpp
Normal file
@@ -0,0 +1,49 @@
|
||||
#pragma once
|
||||
#include "macro-action-edit.hpp"
|
||||
|
||||
#include <QLineEdit>
|
||||
|
||||
class MacroActionSystray : 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<MacroActionSystray>();
|
||||
}
|
||||
|
||||
std::string _msg = "";
|
||||
|
||||
private:
|
||||
static bool _registered;
|
||||
static const std::string id;
|
||||
};
|
||||
|
||||
class MacroActionSystrayEdit : public QWidget {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
MacroActionSystrayEdit(
|
||||
QWidget *parent,
|
||||
std::shared_ptr<MacroActionSystray> entryData = nullptr);
|
||||
static QWidget *Create(QWidget *parent,
|
||||
std::shared_ptr<MacroAction> action)
|
||||
{
|
||||
return new MacroActionSystrayEdit(
|
||||
parent,
|
||||
std::dynamic_pointer_cast<MacroActionSystray>(action));
|
||||
}
|
||||
|
||||
private slots:
|
||||
void MessageChanged();
|
||||
|
||||
protected:
|
||||
std::shared_ptr<MacroActionSystray> _entryData;
|
||||
|
||||
private:
|
||||
QLineEdit *_msg;
|
||||
bool _loading = true;
|
||||
};
|
||||
@@ -9,6 +9,8 @@
|
||||
enum class AudioCondition {
|
||||
ABOVE,
|
||||
BELOW,
|
||||
MUTE,
|
||||
UNMUTE,
|
||||
};
|
||||
|
||||
class MacroConditionAudio : public MacroCondition {
|
||||
@@ -72,5 +74,7 @@ protected:
|
||||
std::shared_ptr<MacroConditionAudio> _entryData;
|
||||
|
||||
private:
|
||||
void SetWidgetVisibility();
|
||||
|
||||
bool _loading = true;
|
||||
};
|
||||
|
||||
@@ -5,8 +5,8 @@ struct MacroConditionInfo {
|
||||
using TCreateMethod = std::shared_ptr<MacroCondition> (*)();
|
||||
using TCreateWidgetMethod =
|
||||
QWidget *(*)(QWidget *parent, std::shared_ptr<MacroCondition>);
|
||||
TCreateMethod _createFunc;
|
||||
TCreateWidgetMethod _createWidgetFunc;
|
||||
TCreateMethod _createFunc = nullptr;
|
||||
TCreateWidgetMethod _createWidgetFunc = nullptr;
|
||||
std::string _name;
|
||||
bool _useDurationConstraint = true;
|
||||
};
|
||||
|
||||
@@ -3,8 +3,10 @@
|
||||
#include <limits>
|
||||
#include <QWidget>
|
||||
#include <QComboBox>
|
||||
#include <QCheckBox>
|
||||
|
||||
#include "duration-control.hpp"
|
||||
#include "scene-selection.hpp"
|
||||
|
||||
enum class MediaTimeRestriction {
|
||||
TIME_RESTRICTION_NONE,
|
||||
@@ -33,6 +35,12 @@ enum class MediaState {
|
||||
ANY,
|
||||
};
|
||||
|
||||
enum class MediaSourceType {
|
||||
SOURCE,
|
||||
ANY,
|
||||
ALL,
|
||||
};
|
||||
|
||||
class MacroConditionMedia : public MacroCondition {
|
||||
public:
|
||||
~MacroConditionMedia();
|
||||
@@ -50,15 +58,23 @@ public:
|
||||
static void MediaStopped(void *data, calldata_t *);
|
||||
static void MediaEnded(void *data, calldata_t *);
|
||||
|
||||
MediaSourceType _sourceType = MediaSourceType::SOURCE;
|
||||
SceneSelection _scene;
|
||||
OBSWeakSource _source = nullptr;
|
||||
std::vector<MacroConditionMedia> _sources;
|
||||
MediaState _state = MediaState::OBS_MEDIA_STATE_NONE;
|
||||
MediaTimeRestriction _restriction =
|
||||
MediaTimeRestriction::TIME_RESTRICTION_NONE;
|
||||
Duration _time;
|
||||
bool _onlyMatchonChagne = false;
|
||||
|
||||
private:
|
||||
std::atomic_bool _stopped = {false};
|
||||
std::atomic_bool _ended = {false};
|
||||
bool CheckMediaMatch();
|
||||
|
||||
bool _stopped = false;
|
||||
bool _ended = false;
|
||||
// TODO: Remove _alreadyMatched as it does not make much sense when
|
||||
// time restrictions for macro conditions are available.
|
||||
// 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
|
||||
@@ -87,20 +103,25 @@ public:
|
||||
|
||||
private slots:
|
||||
void SourceChanged(const QString &text);
|
||||
void SceneChanged(const SceneSelection &);
|
||||
void StateChanged(int index);
|
||||
void TimeRestrictionChanged(int index);
|
||||
void TimeChanged(double seconds);
|
||||
void TimeUnitChanged(DurationUnit unit);
|
||||
void OnChangeChanged(int);
|
||||
signals:
|
||||
void HeaderInfoChanged(const QString &);
|
||||
|
||||
protected:
|
||||
SceneSelectionWidget *_scenes;
|
||||
QComboBox *_mediaSources;
|
||||
QComboBox *_states;
|
||||
QComboBox *_timeRestrictions;
|
||||
DurationSelection *_time;
|
||||
QCheckBox *_onChange;
|
||||
std::shared_ptr<MacroConditionMedia> _entryData;
|
||||
|
||||
private:
|
||||
void SetWidgetVisibility();
|
||||
bool _loading = true;
|
||||
};
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
|
||||
enum class PluginStateCondition {
|
||||
SCENESWITCHED,
|
||||
RUNNING,
|
||||
};
|
||||
|
||||
class MacroConditionPluginState : public MacroCondition {
|
||||
|
||||
@@ -20,7 +20,7 @@ public:
|
||||
return std::make_shared<MacroConditionRecord>();
|
||||
}
|
||||
|
||||
RecordState _recordState;
|
||||
RecordState _recordState = RecordState::STOP;
|
||||
|
||||
private:
|
||||
static bool _registered;
|
||||
|
||||
@@ -22,7 +22,7 @@ public:
|
||||
return std::make_shared<MacroConditionReplayBuffer>();
|
||||
}
|
||||
|
||||
ReplayBufferState _state;
|
||||
ReplayBufferState _state = ReplayBufferState::STOP;
|
||||
|
||||
private:
|
||||
static bool _registered;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
#include "macro.hpp"
|
||||
#include "scene-selection.hpp"
|
||||
#include "scene-item-selection.hpp"
|
||||
|
||||
#include <QWidget>
|
||||
#include <QComboBox>
|
||||
@@ -24,8 +25,8 @@ public:
|
||||
}
|
||||
|
||||
SceneSelection _scene;
|
||||
OBSWeakSource _source;
|
||||
OBSWeakSource _source2;
|
||||
SceneItemSelection _source;
|
||||
SceneItemSelection _source2;
|
||||
int _position = 0;
|
||||
SceneOrderCondition _condition = SceneOrderCondition::ABOVE;
|
||||
|
||||
@@ -53,8 +54,8 @@ public:
|
||||
|
||||
private slots:
|
||||
void SceneChanged(const SceneSelection &);
|
||||
void SourceChanged(const QString &text);
|
||||
void Source2Changed(const QString &text);
|
||||
void SourceChanged(const SceneItemSelection &);
|
||||
void Source2Changed(const SceneItemSelection &);
|
||||
void ConditionChanged(int cond);
|
||||
void PositionChanged(int cond);
|
||||
signals:
|
||||
@@ -63,8 +64,8 @@ signals:
|
||||
protected:
|
||||
SceneSelectionWidget *_scenes;
|
||||
QComboBox *_conditions;
|
||||
QComboBox *_sources;
|
||||
QComboBox *_sources2;
|
||||
SceneItemSelectionWidget *_sources;
|
||||
SceneItemSelectionWidget *_sources2;
|
||||
QSpinBox *_position;
|
||||
QLabel *_posInfo;
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
#include "macro.hpp"
|
||||
#include "scene-selection.hpp"
|
||||
#include "scene-item-selection.hpp"
|
||||
|
||||
#include <QSpinBox>
|
||||
#include <QPlainTextEdit>
|
||||
@@ -19,7 +20,7 @@ public:
|
||||
}
|
||||
|
||||
SceneSelection _scene;
|
||||
OBSWeakSource _source;
|
||||
SceneItemSelection _source;
|
||||
bool _regex = false;
|
||||
std::string _settings = "";
|
||||
|
||||
@@ -48,7 +49,7 @@ public:
|
||||
|
||||
private slots:
|
||||
void SceneChanged(const SceneSelection &);
|
||||
void SourceChanged(const QString &text);
|
||||
void SourceChanged(const SceneItemSelection &);
|
||||
void GetSettingsClicked();
|
||||
void SettingsChanged();
|
||||
void RegexChanged(int);
|
||||
@@ -57,7 +58,7 @@ signals:
|
||||
|
||||
protected:
|
||||
SceneSelectionWidget *_scenes;
|
||||
QComboBox *_sources;
|
||||
SceneItemSelectionWidget *_sources;
|
||||
QPushButton *_getSettings;
|
||||
QPlainTextEdit *_settings;
|
||||
QCheckBox *_regex;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
#include "macro.hpp"
|
||||
#include "scene-selection.hpp"
|
||||
#include "scene-item-selection.hpp"
|
||||
|
||||
#include <QComboBox>
|
||||
|
||||
@@ -22,7 +23,7 @@ public:
|
||||
}
|
||||
|
||||
SceneSelection _scene;
|
||||
OBSWeakSource _source;
|
||||
SceneItemSelection _source;
|
||||
SceneVisibilityCondition _condition = SceneVisibilityCondition::SHOWN;
|
||||
|
||||
private:
|
||||
@@ -49,14 +50,14 @@ public:
|
||||
|
||||
private slots:
|
||||
void SceneChanged(const SceneSelection &);
|
||||
void SourceChanged(const QString &text);
|
||||
void SourceChanged(const SceneItemSelection &);
|
||||
void ConditionChanged(int cond);
|
||||
signals:
|
||||
void HeaderInfoChanged(const QString &);
|
||||
|
||||
protected:
|
||||
SceneSelectionWidget *_scenes;
|
||||
QComboBox *_sources;
|
||||
SceneItemSelectionWidget *_sources;
|
||||
QComboBox *_conditions;
|
||||
|
||||
std::shared_ptr<MacroConditionSceneVisibility> _entryData;
|
||||
|
||||
@@ -25,9 +25,12 @@ public:
|
||||
return std::make_shared<MacroConditionScene>();
|
||||
}
|
||||
|
||||
bool _waitForTransition = false;
|
||||
SceneSelection _scene;
|
||||
SceneType _type;
|
||||
SceneType _type = SceneType::CURRENT;
|
||||
// During a transition "current" scene could either stand for the scene
|
||||
// being transitioned to or the scene still being transitioned away
|
||||
// from.
|
||||
bool _useTransitionTargetScene = false;
|
||||
|
||||
private:
|
||||
std::chrono::high_resolution_clock::time_point _lastSceneChangeTime{};
|
||||
@@ -54,14 +57,14 @@ public:
|
||||
private slots:
|
||||
void SceneChanged(const SceneSelection &);
|
||||
void TypeChanged(int value);
|
||||
void WaitForTransitionChanged(int state);
|
||||
void UseTransitionTargetSceneChanged(int state);
|
||||
signals:
|
||||
void HeaderInfoChanged(const QString &);
|
||||
|
||||
protected:
|
||||
SceneSelectionWidget *_scenes;
|
||||
QComboBox *_sceneType;
|
||||
QCheckBox *_waitForTransition;
|
||||
QCheckBox *_useTransitionTargetScene;
|
||||
std::shared_ptr<MacroConditionScene> _entryData;
|
||||
|
||||
private:
|
||||
|
||||
@@ -21,7 +21,7 @@ public:
|
||||
return std::make_shared<MacroConditionStream>();
|
||||
}
|
||||
|
||||
StreamState _streamState;
|
||||
StreamState _streamState = StreamState::STOP;
|
||||
|
||||
private:
|
||||
std::chrono::high_resolution_clock::time_point _lastStreamStartingTime{};
|
||||
|
||||
65
src/headers/macro-condition-studio-mode.hpp
Normal file
65
src/headers/macro-condition-studio-mode.hpp
Normal file
@@ -0,0 +1,65 @@
|
||||
#pragma once
|
||||
#include "macro.hpp"
|
||||
#include "scene-selection.hpp"
|
||||
|
||||
#include <QWidget>
|
||||
#include <QComboBox>
|
||||
|
||||
enum class StudioModeCondition {
|
||||
STUDIO_MODE_ACTIVE,
|
||||
STUDIO_MODE_NOT_ACTIVE,
|
||||
PREVIEW_SCENE,
|
||||
};
|
||||
|
||||
class MacroConditionStudioMode : public MacroCondition {
|
||||
public:
|
||||
bool CheckCondition();
|
||||
bool Save(obs_data_t *obj);
|
||||
bool Load(obs_data_t *obj);
|
||||
std::string GetShortDesc();
|
||||
std::string GetId() { return id; };
|
||||
static std::shared_ptr<MacroCondition> Create()
|
||||
{
|
||||
return std::make_shared<MacroConditionStudioMode>();
|
||||
}
|
||||
|
||||
StudioModeCondition _condition =
|
||||
StudioModeCondition::STUDIO_MODE_ACTIVE;
|
||||
SceneSelection _scene;
|
||||
|
||||
private:
|
||||
static bool _registered;
|
||||
static const std::string id;
|
||||
};
|
||||
|
||||
class MacroConditionStudioModeEdit : public QWidget {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
MacroConditionStudioModeEdit(
|
||||
QWidget *parent,
|
||||
std::shared_ptr<MacroConditionStudioMode> cond = nullptr);
|
||||
void UpdateEntryData();
|
||||
static QWidget *Create(QWidget *parent,
|
||||
std::shared_ptr<MacroCondition> cond)
|
||||
{
|
||||
return new MacroConditionStudioModeEdit(
|
||||
parent,
|
||||
std::dynamic_pointer_cast<MacroConditionStudioMode>(
|
||||
cond));
|
||||
}
|
||||
private slots:
|
||||
void ConditionChanged(int cond);
|
||||
void SceneChanged(const SceneSelection &);
|
||||
signals:
|
||||
void HeaderInfoChanged(const QString &);
|
||||
|
||||
protected:
|
||||
QComboBox *_condition;
|
||||
SceneSelectionWidget *_scenes;
|
||||
std::shared_ptr<MacroConditionStudioMode> _entryData;
|
||||
|
||||
private:
|
||||
void SetWidgetVisibility();
|
||||
bool _loading = true;
|
||||
};
|
||||
@@ -5,6 +5,15 @@
|
||||
#include <QCheckBox>
|
||||
#include <QPushButton>
|
||||
#include <QTimer>
|
||||
#include <QComboBox>
|
||||
#include <QHBoxLayout>
|
||||
|
||||
#include <random>
|
||||
|
||||
enum class TimerType {
|
||||
FIXED,
|
||||
RANDOM,
|
||||
};
|
||||
|
||||
class MacroConditionTimer : public MacroCondition {
|
||||
public:
|
||||
@@ -20,13 +29,17 @@ public:
|
||||
void Continue();
|
||||
void Reset();
|
||||
|
||||
TimerType _type = TimerType::FIXED;
|
||||
Duration _duration;
|
||||
Duration _duration2;
|
||||
bool _paused = false;
|
||||
bool _saveRemaining = false;
|
||||
double _remaining = 0.;
|
||||
bool _oneshot = false;
|
||||
|
||||
private:
|
||||
void SetRandomTimeRemaining();
|
||||
std::default_random_engine _re;
|
||||
static bool _registered;
|
||||
static const std::string id;
|
||||
};
|
||||
@@ -48,8 +61,11 @@ public:
|
||||
}
|
||||
|
||||
private slots:
|
||||
void TimerTypeChanged(int type);
|
||||
void DurationChanged(double seconds);
|
||||
void DurationUnitChanged(DurationUnit unit);
|
||||
void Duration2Changed(double seconds);
|
||||
void Duration2UnitChanged(DurationUnit unit);
|
||||
void SaveRemainingChanged(int state);
|
||||
void AutoResetChanged(int state);
|
||||
void PauseContinueClicked();
|
||||
@@ -59,7 +75,9 @@ private slots:
|
||||
protected:
|
||||
void SetPauseContinueButtonLabel();
|
||||
|
||||
QComboBox *_timerTypes;
|
||||
DurationSelection *_duration;
|
||||
DurationSelection *_duration2;
|
||||
QCheckBox *_autoReset;
|
||||
QCheckBox *_saveRemaining;
|
||||
QPushButton *_reset;
|
||||
@@ -68,6 +86,9 @@ protected:
|
||||
std::shared_ptr<MacroConditionTimer> _entryData;
|
||||
|
||||
private:
|
||||
void SetWidgetVisibility();
|
||||
|
||||
QHBoxLayout *_timerLayout;
|
||||
QTimer timer;
|
||||
bool _loading = true;
|
||||
};
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#include "macro.hpp"
|
||||
#include "duration-control.hpp"
|
||||
#include "transition-selection.hpp"
|
||||
#include "scene-selection.hpp"
|
||||
|
||||
#include <QWidget>
|
||||
#include <QComboBox>
|
||||
@@ -11,6 +12,8 @@ enum class TransitionCondition {
|
||||
DURATION,
|
||||
STARTED,
|
||||
ENDED,
|
||||
TRANSITION_SOURCE,
|
||||
TRANSITION_TARGET,
|
||||
};
|
||||
|
||||
class MacroConditionTransition : public MacroCondition {
|
||||
@@ -29,6 +32,7 @@ public:
|
||||
|
||||
TransitionCondition _condition = TransitionCondition::CURRENT;
|
||||
TransitionSelection _transition;
|
||||
SceneSelection _scene;
|
||||
Duration _duration;
|
||||
|
||||
private:
|
||||
@@ -62,6 +66,7 @@ public:
|
||||
private slots:
|
||||
void ConditionChanged(int cond);
|
||||
void TransitionChanged(const TransitionSelection &);
|
||||
void SceneChanged(const SceneSelection &);
|
||||
void DurationChanged(double seconds);
|
||||
signals:
|
||||
void HeaderInfoChanged(const QString &);
|
||||
@@ -69,6 +74,7 @@ signals:
|
||||
protected:
|
||||
QComboBox *_conditions;
|
||||
TransitionSelectionWidget *_transitions;
|
||||
SceneSelectionWidget *_scenes;
|
||||
DurationSelection *_duration;
|
||||
QLabel *_durationSuffix;
|
||||
std::shared_ptr<MacroConditionTransition> _entryData;
|
||||
|
||||
@@ -21,7 +21,7 @@ public:
|
||||
return std::make_shared<MacroConditionVCam>();
|
||||
}
|
||||
|
||||
VCamState _state;
|
||||
VCamState _state = VCamState::STOP;
|
||||
|
||||
private:
|
||||
static bool _registered;
|
||||
|
||||
@@ -14,7 +14,7 @@ public:
|
||||
virtual std::string GetId() = 0;
|
||||
|
||||
protected:
|
||||
int _idx;
|
||||
int _idx = 0;
|
||||
bool _collapsed = false;
|
||||
};
|
||||
|
||||
|
||||
@@ -2,16 +2,16 @@
|
||||
#include "macro-segment.hpp"
|
||||
#include "duration-control.hpp"
|
||||
|
||||
#include <QString>
|
||||
#include <string>
|
||||
#include <deque>
|
||||
#include <memory>
|
||||
#include <map>
|
||||
#include <thread>
|
||||
#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;
|
||||
|
||||
@@ -55,7 +55,7 @@ public:
|
||||
void SetDuration(double seconds);
|
||||
|
||||
private:
|
||||
LogicType _logic;
|
||||
LogicType _logic = LogicType::ROOT_NONE;
|
||||
DurationConstraint _duration;
|
||||
};
|
||||
|
||||
@@ -73,12 +73,17 @@ public:
|
||||
virtual ~Macro();
|
||||
|
||||
bool CeckMatch();
|
||||
bool PerformAction();
|
||||
bool PerformAction(bool forceParallel = false,
|
||||
bool ignorePause = false);
|
||||
bool Matched() { return _matched; }
|
||||
std::string Name() { return _name; }
|
||||
void SetName(const std::string &name);
|
||||
void SetRunInParallel(bool parallel) { _runInParallel = parallel; }
|
||||
bool RunInParallel() { return _runInParallel; }
|
||||
void SetPaused(bool pause = true);
|
||||
bool Paused() { return _paused; }
|
||||
void SetMatchOnChange(bool onChange) { _matchOnChange = onChange; }
|
||||
bool MatchOnChange() { return _matchOnChange; }
|
||||
int GetCount() { return _count; };
|
||||
void ResetCount() { _count = 0; };
|
||||
std::deque<std::shared_ptr<MacroCondition>> &Conditions()
|
||||
@@ -103,15 +108,25 @@ private:
|
||||
void ClearHotkeys();
|
||||
void SetHotkeysDesc();
|
||||
void ResetTimers();
|
||||
void RunActions(bool &ret, bool ignorePause);
|
||||
void RunActions(bool ignorePause);
|
||||
|
||||
std::string _name = "";
|
||||
std::deque<std::shared_ptr<MacroCondition>> _conditions;
|
||||
std::deque<std::shared_ptr<MacroAction>> _actions;
|
||||
bool _runInParallel = false;
|
||||
bool _matched = false;
|
||||
bool _lastMatched = false;
|
||||
bool _matchOnChange = false;
|
||||
bool _paused = false;
|
||||
int _count = 0;
|
||||
obs_hotkey_id _pauseHotkey = OBS_INVALID_HOTKEY_ID;
|
||||
obs_hotkey_id _unpauseHotkey = OBS_INVALID_HOTKEY_ID;
|
||||
obs_hotkey_id _togglePauseHotkey = OBS_INVALID_HOTKEY_ID;
|
||||
|
||||
bool _stop = false;
|
||||
bool _done = true;
|
||||
std::thread _thread;
|
||||
};
|
||||
|
||||
Macro *GetMacroByName(const char *name);
|
||||
|
||||
59
src/headers/scene-item-selection.hpp
Normal file
59
src/headers/scene-item-selection.hpp
Normal file
@@ -0,0 +1,59 @@
|
||||
#pragma once
|
||||
#include <QComboBox>
|
||||
#include <obs-data.h>
|
||||
|
||||
#include "scene-selection.hpp"
|
||||
#include "utility.hpp"
|
||||
|
||||
class SceneItemSelection {
|
||||
public:
|
||||
void Save(obs_data_t *obj, const char *name = "sceneItem",
|
||||
const char *targetName = "sceneItemTarget",
|
||||
const char *idxName = "sceneItemIdx");
|
||||
void Load(obs_data_t *obj, const char *name = "sceneItem",
|
||||
const char *targetName = "sceneItemTarget",
|
||||
const char *idxName = "sceneItemIdx");
|
||||
|
||||
enum class Target { ALL, ANY, INDIVIDUAL };
|
||||
Target GetType() { return _target; }
|
||||
std::vector<obs_scene_item *> GetSceneItems(SceneSelection &s);
|
||||
std::string ToString();
|
||||
|
||||
private:
|
||||
OBSWeakSource _sceneItem;
|
||||
Target _target = Target::ALL;
|
||||
int _idx = 0;
|
||||
friend class SceneItemSelectionWidget;
|
||||
};
|
||||
|
||||
class SceneItemSelectionWidget : public QWidget {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
enum class AllSelectionType { ALL, ANY };
|
||||
SceneItemSelectionWidget(
|
||||
QWidget *parent, bool allSelection = true,
|
||||
AllSelectionType allType = AllSelectionType::ALL);
|
||||
void SetSceneItem(const SceneItemSelection &);
|
||||
void SetScene(const SceneSelection &);
|
||||
void SetShowAll(bool);
|
||||
void SetShowAllSelectionType(AllSelectionType t);
|
||||
signals:
|
||||
void SceneItemChanged(const SceneItemSelection &);
|
||||
|
||||
private slots:
|
||||
void SceneChanged(const SceneSelection &);
|
||||
void SelectionChanged(const QString &name);
|
||||
void IdxChanged(int);
|
||||
|
||||
private:
|
||||
void SetupIdxSelection(int);
|
||||
|
||||
QComboBox *_sceneItems;
|
||||
QComboBox *_idx;
|
||||
|
||||
SceneSelection _scene;
|
||||
OBSWeakSource _sceneItem;
|
||||
bool _showAll = false;
|
||||
AllSelectionType _allType = AllSelectionType::ALL;
|
||||
};
|
||||
30
src/headers/status-dock.hpp
Normal file
30
src/headers/status-dock.hpp
Normal file
@@ -0,0 +1,30 @@
|
||||
#pragma once
|
||||
#include <QDockWidget>
|
||||
#include <QPushButton>
|
||||
#include <QLabel>
|
||||
#include <QTimer>
|
||||
#include <obs-data.h>
|
||||
|
||||
class StatusDock : public QDockWidget {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
StatusDock(QWidget *parent = 0);
|
||||
|
||||
private slots:
|
||||
void ButtonClicked();
|
||||
void UpdateStatus();
|
||||
|
||||
private:
|
||||
void SetStopped();
|
||||
void SetStarted();
|
||||
|
||||
QPushButton *_button;
|
||||
QLabel *_status;
|
||||
QTimer _timer;
|
||||
};
|
||||
|
||||
extern StatusDock *dock;
|
||||
|
||||
void saveDock(obs_data_t *obj);
|
||||
void loadDock(obs_data_t *obj);
|
||||
@@ -14,7 +14,6 @@
|
||||
#include "volume-control.hpp"
|
||||
|
||||
constexpr auto audio_func = 8;
|
||||
constexpr auto default_priority_8 = audio_func;
|
||||
|
||||
typedef enum {
|
||||
ABOVE,
|
||||
@@ -44,7 +43,7 @@ struct AudioSwitch : virtual SceneSwitcherEntry {
|
||||
|
||||
AudioSwitch(){};
|
||||
AudioSwitch(const AudioSwitch &other);
|
||||
AudioSwitch(AudioSwitch &&other);
|
||||
AudioSwitch(AudioSwitch &&other) noexcept;
|
||||
~AudioSwitch();
|
||||
AudioSwitch &operator=(const AudioSwitch &other);
|
||||
AudioSwitch &operator=(AudioSwitch &&other) noexcept;
|
||||
|
||||
@@ -10,7 +10,6 @@
|
||||
#include "switch-generic.hpp"
|
||||
|
||||
constexpr auto exe_func = 3;
|
||||
constexpr auto default_priority_3 = exe_func;
|
||||
|
||||
struct ExecutableSwitch : SceneSwitcherEntry {
|
||||
static bool pause;
|
||||
|
||||
@@ -11,7 +11,6 @@
|
||||
#include <obs-module.h>
|
||||
|
||||
constexpr auto read_file_func = 0;
|
||||
constexpr auto default_priority_0 = read_file_func;
|
||||
|
||||
typedef enum {
|
||||
LOCAL,
|
||||
|
||||
@@ -10,7 +10,6 @@
|
||||
|
||||
constexpr auto default_idle_time = 60;
|
||||
constexpr auto idle_func = 2;
|
||||
constexpr auto default_priority_2 = idle_func;
|
||||
|
||||
struct IdleData : SceneSwitcherEntry {
|
||||
static bool pause;
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
#include "switch-generic.hpp"
|
||||
|
||||
constexpr auto media_func = 6;
|
||||
constexpr auto default_priority_6 = media_func;
|
||||
|
||||
typedef enum {
|
||||
TIME_RESTRICTION_NONE,
|
||||
@@ -52,7 +51,7 @@ struct MediaSwitch : SceneSwitcherEntry {
|
||||
inline MediaSwitch(){};
|
||||
|
||||
MediaSwitch(const MediaSwitch &other);
|
||||
MediaSwitch(MediaSwitch &&other);
|
||||
MediaSwitch(MediaSwitch &&other) noexcept;
|
||||
~MediaSwitch();
|
||||
MediaSwitch &operator=(const MediaSwitch &other);
|
||||
MediaSwitch &operator=(MediaSwitch &&other) noexcept;
|
||||
|
||||
@@ -70,8 +70,8 @@ private:
|
||||
QString getRemoteEndpoint(connection_hdl hdl);
|
||||
|
||||
server _server;
|
||||
quint16 _serverPort;
|
||||
bool _lockToIPv4;
|
||||
quint16 _serverPort = 55555;
|
||||
bool _lockToIPv4 = false;
|
||||
std::set<connection_hdl, std::owner_less<connection_hdl>> _connections;
|
||||
QMutex _clMutex;
|
||||
QThreadPool _threadPool;
|
||||
|
||||
@@ -10,7 +10,6 @@
|
||||
#include "switch-generic.hpp"
|
||||
|
||||
constexpr auto screen_region_func = 4;
|
||||
constexpr auto default_priority_4 = screen_region_func;
|
||||
|
||||
struct ScreenRegionSwitch : SceneSwitcherEntry {
|
||||
static bool pause;
|
||||
|
||||
@@ -12,7 +12,6 @@
|
||||
#include "duration-control.hpp"
|
||||
|
||||
constexpr auto round_trip_func = 1;
|
||||
constexpr auto default_priority_1 = round_trip_func;
|
||||
|
||||
struct SceneSequenceSwitch : SceneSwitcherEntry {
|
||||
static bool pause;
|
||||
|
||||
@@ -11,7 +11,6 @@
|
||||
#include "switch-generic.hpp"
|
||||
|
||||
constexpr auto time_func = 7;
|
||||
constexpr auto default_priority_7 = time_func;
|
||||
|
||||
typedef enum {
|
||||
ANY_DAY = 0,
|
||||
|
||||
@@ -12,7 +12,6 @@
|
||||
#include "screenshot-helper.hpp"
|
||||
|
||||
constexpr auto video_func = 9;
|
||||
constexpr auto default_priority_9 = video_func;
|
||||
|
||||
enum class videoSwitchType {
|
||||
MATCH,
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
#include "switch-generic.hpp"
|
||||
|
||||
constexpr auto window_title_func = 5;
|
||||
constexpr auto default_priority_5 = window_title_func;
|
||||
|
||||
struct WindowSwitch : SceneSwitcherEntry {
|
||||
static bool pause;
|
||||
|
||||
@@ -33,6 +33,18 @@ constexpr auto previous_scene_name = "Previous Scene";
|
||||
constexpr auto current_transition_name = "Current Transition";
|
||||
constexpr auto tab_count = 18;
|
||||
|
||||
constexpr auto default_priority_0 = macro_func;
|
||||
constexpr auto default_priority_1 = read_file_func;
|
||||
constexpr auto default_priority_2 = idle_func;
|
||||
constexpr auto default_priority_3 = audio_func;
|
||||
constexpr auto default_priority_4 = media_func;
|
||||
constexpr auto default_priority_5 = video_func;
|
||||
constexpr auto default_priority_6 = time_func;
|
||||
constexpr auto default_priority_7 = screen_region_func;
|
||||
constexpr auto default_priority_8 = round_trip_func;
|
||||
constexpr auto default_priority_9 = window_title_func;
|
||||
constexpr auto default_priority_10 = exe_func;
|
||||
|
||||
typedef enum { NO_SWITCH = 0, SWITCH = 1, RANDOM_SWITCH = 2 } NoMatch;
|
||||
typedef enum { PERSIST = 0, START = 1, STOP = 2 } StartupBehavior;
|
||||
|
||||
@@ -65,6 +77,7 @@ struct SwitcherData {
|
||||
SwitcherThread *th = nullptr;
|
||||
|
||||
bool settingsWindowOpened = false;
|
||||
bool firstBoot = true;
|
||||
|
||||
std::condition_variable cv;
|
||||
std::mutex m;
|
||||
@@ -88,7 +101,7 @@ struct SwitcherData {
|
||||
std::chrono::high_resolution_clock::time_point lastStreamStartingTime{};
|
||||
std::chrono::high_resolution_clock::time_point lastStreamStoppingTime{};
|
||||
OBSWeakSource lastRandomScene;
|
||||
SceneGroup *lastRandomSceneGroup;
|
||||
SceneGroup *lastRandomSceneGroup = nullptr;
|
||||
OBSWeakSource nonMatchingScene;
|
||||
NoMatch switchIfNotMatching = NO_SWITCH;
|
||||
Duration noMatchDelay;
|
||||
@@ -105,6 +118,7 @@ struct SwitcherData {
|
||||
std::deque<std::shared_ptr<Macro>> macros;
|
||||
std::condition_variable macroWaitCv;
|
||||
std::atomic_bool abortMacroWait = {false};
|
||||
std::condition_variable macroTransitionCv;
|
||||
bool macroSceneSwitched = false;
|
||||
bool replayBufferSaved = false;
|
||||
|
||||
|
||||
@@ -27,13 +27,13 @@ std::string getSourceSettings(OBSWeakSource ws);
|
||||
void setSourceSettings(obs_source_t *s, const std::string &settings);
|
||||
bool compareSourceSettings(const OBSWeakSource &source,
|
||||
const std::string &settings, bool regex);
|
||||
std::vector<obs_scene_item *> getSceneItemsWithName(OBSScene scene,
|
||||
std::vector<obs_scene_item *> getSceneItemsWithName(obs_scene_t *scene,
|
||||
std::string &name);
|
||||
std::string getDataFilePath(const std::string &file);
|
||||
bool matchJson(const std::string &json1, const std::string &json2,
|
||||
bool useRegex);
|
||||
QString formatJsonString(std::string);
|
||||
QString formatJsonString(const char *);
|
||||
QString formatJsonString(QString);
|
||||
QString escapeForRegex(QString &s);
|
||||
void loadTransformState(obs_data_t *obj, struct obs_transform_info &info,
|
||||
struct obs_sceneitem_crop &crop);
|
||||
@@ -55,6 +55,7 @@ void listAddClicked(QListWidget *list, QWidget *newWidget,
|
||||
QMetaObject::Connection *addHighlight = nullptr);
|
||||
bool listMoveUp(QListWidget *list);
|
||||
bool listMoveDown(QListWidget *list);
|
||||
void setHeightToContentHeight(QListWidget *list);
|
||||
bool DisplayMessage(const QString &msg, bool question = false);
|
||||
void DisplayTrayMessage(const QString &title, const QString &msg);
|
||||
void addSelectionEntry(QComboBox *sel, const char *description,
|
||||
@@ -63,7 +64,8 @@ void populateTransitionSelection(QComboBox *sel, bool addCurrent = true,
|
||||
bool addAny = false);
|
||||
void populateWindowSelection(QComboBox *sel, bool addSelect = true);
|
||||
void populateAudioSelection(QComboBox *sel, bool addSelect = true);
|
||||
void populateVideoSelection(QComboBox *sel, bool addSelect = true);
|
||||
void populateVideoSelection(QComboBox *sel, bool addScenes = false,
|
||||
bool addSelect = true);
|
||||
void populateMediaSelection(QComboBox *sel, bool addSelect = true);
|
||||
void populateProcessSelection(QComboBox *sel, bool addSelect = true);
|
||||
void populateSourceSelection(QComboBox *list, bool addSelect = true);
|
||||
@@ -79,4 +81,5 @@ void populateFilterSelection(QComboBox *list,
|
||||
void populateSceneItemSelection(QComboBox *list,
|
||||
OBSWeakSource sceneWeakSource = nullptr);
|
||||
void populateSceneItemSelection(QComboBox *list, SceneSelection &s);
|
||||
void populateSourceTypeSelection(QComboBox *list);
|
||||
void populateSourceGroupSelection(QComboBox *list);
|
||||
bool windowPosValid(QPoint pos);
|
||||
|
||||
@@ -4,13 +4,8 @@
|
||||
#include <fstream>
|
||||
#include <regex>
|
||||
|
||||
void startHotkeyFunc(void *data, obs_hotkey_id id, obs_hotkey_t *hotkey,
|
||||
bool pressed)
|
||||
void startHotkeyFunc(void *, obs_hotkey_id, obs_hotkey_t *, bool pressed)
|
||||
{
|
||||
UNUSED_PARAMETER(id);
|
||||
UNUSED_PARAMETER(data);
|
||||
UNUSED_PARAMETER(hotkey);
|
||||
|
||||
if (pressed) {
|
||||
if (!(switcher->th && switcher->th->isRunning())) {
|
||||
switcher->Start();
|
||||
@@ -18,13 +13,8 @@ void startHotkeyFunc(void *data, obs_hotkey_id id, obs_hotkey_t *hotkey,
|
||||
}
|
||||
}
|
||||
|
||||
void stopHotkeyFunc(void *data, obs_hotkey_id id, obs_hotkey_t *hotkey,
|
||||
bool pressed)
|
||||
void stopHotkeyFunc(void *, obs_hotkey_id, obs_hotkey_t *, bool pressed)
|
||||
{
|
||||
UNUSED_PARAMETER(id);
|
||||
UNUSED_PARAMETER(data);
|
||||
UNUSED_PARAMETER(hotkey);
|
||||
|
||||
if (pressed) {
|
||||
if (switcher->th && switcher->th->isRunning()) {
|
||||
switcher->Stop();
|
||||
@@ -32,13 +22,9 @@ void stopHotkeyFunc(void *data, obs_hotkey_id id, obs_hotkey_t *hotkey,
|
||||
}
|
||||
}
|
||||
|
||||
void startStopToggleHotkeyFunc(void *data, obs_hotkey_id id,
|
||||
obs_hotkey_t *hotkey, bool pressed)
|
||||
void startStopToggleHotkeyFunc(void *, obs_hotkey_id, obs_hotkey_t *,
|
||||
bool pressed)
|
||||
{
|
||||
UNUSED_PARAMETER(id);
|
||||
UNUSED_PARAMETER(data);
|
||||
UNUSED_PARAMETER(hotkey);
|
||||
|
||||
if (pressed) {
|
||||
if (switcher->th && switcher->th->isRunning()) {
|
||||
switcher->Stop();
|
||||
|
||||
@@ -117,7 +117,7 @@ bool MacroActionAudio::PerformAction()
|
||||
obs_source_set_muted(s, false);
|
||||
break;
|
||||
case AudioAction::SOURCE_VOLUME:
|
||||
if (_fade) {
|
||||
if (_fade && _duration.seconds != 0) {
|
||||
startSourceFade(_duration, (float)_volume / 100.0f,
|
||||
_audioSource);
|
||||
} else {
|
||||
@@ -125,7 +125,7 @@ bool MacroActionAudio::PerformAction()
|
||||
}
|
||||
break;
|
||||
case AudioAction::MASTER_VOLUME:
|
||||
if (_fade) {
|
||||
if (_fade && _duration.seconds != 0) {
|
||||
startMasterFade(_duration, (float)_volume / 100.0f);
|
||||
} else {
|
||||
obs_set_master_volume((float)_volume / 100.0f);
|
||||
@@ -259,6 +259,7 @@ void MacroActionAudioEdit::SetWidgetVisibility()
|
||||
setLayoutVisible(_fadeLayout, hasVolumeControl(_entryData->_action));
|
||||
adjustSize();
|
||||
}
|
||||
|
||||
void MacroActionAudioEdit::UpdateEntryData()
|
||||
{
|
||||
if (!_entryData) {
|
||||
|
||||
@@ -184,7 +184,7 @@ void AdvSceneSwitcher::RemoveMacroAction(int idx)
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
macro->Actions().erase(macro->Actions().begin() + idx);
|
||||
switcher->abortMacroWait = true;
|
||||
switcher->macroWaitCv.notify_one();
|
||||
switcher->macroWaitCv.notify_all();
|
||||
macro->UpdateActionIndices();
|
||||
|
||||
clearLayout(ui->macroEditActionLayout, idx);
|
||||
|
||||
@@ -15,6 +15,7 @@ const static std::map<PerformMacroAction, std::string> actionTypes = {
|
||||
"AdvSceneSwitcher.action.macro.type.unpause"},
|
||||
{PerformMacroAction::RESET_COUNTER,
|
||||
"AdvSceneSwitcher.action.macro.type.resetCounter"},
|
||||
{PerformMacroAction::RUN, "AdvSceneSwitcher.action.macro.type.run"},
|
||||
};
|
||||
|
||||
bool MacroActionMacro::PerformAction()
|
||||
@@ -33,6 +34,9 @@ bool MacroActionMacro::PerformAction()
|
||||
case PerformMacroAction::RESET_COUNTER:
|
||||
_macro->ResetCount();
|
||||
break;
|
||||
case PerformMacroAction::RUN:
|
||||
_macro->PerformAction();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -55,6 +59,10 @@ void MacroActionMacro::LogAction()
|
||||
vblog(LOG_INFO, "reset counter for \"%s\"",
|
||||
_macro->Name().c_str());
|
||||
break;
|
||||
case PerformMacroAction::RUN:
|
||||
vblog(LOG_INFO, "run nested macro \"%s\"",
|
||||
_macro->Name().c_str());
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -153,9 +161,8 @@ void MacroActionMacroEdit::ActionChanged(int value)
|
||||
_entryData->_action = static_cast<PerformMacroAction>(value);
|
||||
}
|
||||
|
||||
void MacroActionMacroEdit::MacroRemove(const QString &name)
|
||||
void MacroActionMacroEdit::MacroRemove(const QString &)
|
||||
{
|
||||
UNUSED_PARAMETER(name);
|
||||
if (_entryData) {
|
||||
_entryData->_macro.UpdateRef();
|
||||
}
|
||||
|
||||
@@ -16,6 +16,8 @@ const static std::map<PluginStateAction, std::string> actionTypes = {
|
||||
"AdvSceneSwitcher.action.pluginState.type.stop"},
|
||||
{PluginStateAction::NO_MATCH_BEHAVIOUR,
|
||||
"AdvSceneSwitcher.action.pluginState.type.noMatch"},
|
||||
{PluginStateAction::IMPORT_SETTINGS,
|
||||
"AdvSceneSwitcher.action.pluginState.type.import"},
|
||||
};
|
||||
|
||||
const static std::map<NoMatch, std::string> noMatchValues = {
|
||||
@@ -33,6 +35,19 @@ void stopPlugin()
|
||||
t.detach();
|
||||
}
|
||||
|
||||
void importSettings(const std::string &path)
|
||||
{
|
||||
if (switcher->settingsWindowOpened) {
|
||||
return;
|
||||
}
|
||||
obs_data_t *obj = obs_data_create_from_json_file(path.c_str());
|
||||
if (!obj) {
|
||||
return;
|
||||
}
|
||||
switcher->loadSettings(obj);
|
||||
obs_data_release(obj);
|
||||
}
|
||||
|
||||
void setNoMatchBehaviour(int value, OBSWeakSource &scene)
|
||||
{
|
||||
switcher->switchIfNotMatching = static_cast<NoMatch>(value);
|
||||
@@ -50,6 +65,11 @@ bool MacroActionPluginState::PerformAction()
|
||||
case PluginStateAction::NO_MATCH_BEHAVIOUR:
|
||||
setNoMatchBehaviour(_value, _scene);
|
||||
break;
|
||||
case PluginStateAction::IMPORT_SETTINGS:
|
||||
importSettings(_settingsPath);
|
||||
// There is no point in continuing
|
||||
// The settings will be invalid
|
||||
return false;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -62,6 +82,13 @@ void MacroActionPluginState::LogAction()
|
||||
case PluginStateAction::STOP:
|
||||
blog(LOG_INFO, "stop() called by macro");
|
||||
break;
|
||||
case PluginStateAction::NO_MATCH_BEHAVIOUR:
|
||||
vblog(LOG_INFO, "setting no match to %d", _value);
|
||||
break;
|
||||
case PluginStateAction::IMPORT_SETTINGS:
|
||||
vblog(LOG_INFO, "importing settings from %s",
|
||||
_settingsPath.c_str());
|
||||
break;
|
||||
default:
|
||||
blog(LOG_WARNING, "ignored unknown pluginState action %d",
|
||||
static_cast<int>(_action));
|
||||
@@ -75,6 +102,7 @@ bool MacroActionPluginState::Save(obs_data_t *obj)
|
||||
obs_data_set_int(obj, "action", static_cast<int>(_action));
|
||||
obs_data_set_int(obj, "value", _value);
|
||||
obs_data_set_string(obj, "scene", GetWeakSourceName(_scene).c_str());
|
||||
obs_data_set_string(obj, "settingsPath", _settingsPath.c_str());
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -86,6 +114,7 @@ bool MacroActionPluginState::Load(obs_data_t *obj)
|
||||
_value = obs_data_get_int(obj, "value");
|
||||
const char *sceneName = obs_data_get_string(obj, "scene");
|
||||
_scene = GetWeakSourceByName(sceneName);
|
||||
_settingsPath = obs_data_get_string(obj, "settingsPath");
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -113,6 +142,9 @@ MacroActionPluginStateEdit::MacroActionPluginStateEdit(
|
||||
_actions = new QComboBox();
|
||||
_values = new QComboBox();
|
||||
_scenes = new QComboBox();
|
||||
_settings = new FileSelection();
|
||||
_settingsWarning = new QLabel(obs_module_text(
|
||||
"AdvSceneSwitcher.action.pluginState.importWarning"));
|
||||
|
||||
populateActionSelection(_actions);
|
||||
populateSceneSelection(_scenes);
|
||||
@@ -123,12 +155,16 @@ MacroActionPluginStateEdit::MacroActionPluginStateEdit(
|
||||
SLOT(ValueChanged(int)));
|
||||
QWidget::connect(_scenes, SIGNAL(currentTextChanged(const QString &)),
|
||||
this, SLOT(SceneChanged(const QString &)));
|
||||
QWidget::connect(_settings, SIGNAL(PathChanged(const QString &)), this,
|
||||
SLOT(PathChanged(const QString &)));
|
||||
|
||||
QHBoxLayout *mainLayout = new QHBoxLayout;
|
||||
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
|
||||
{"{{actions}}", _actions},
|
||||
{"{{values}}", _values},
|
||||
{"{{scenes}}", _scenes},
|
||||
{"{{settings}}", _settings},
|
||||
{"{{settingsWarning}}", _settingsWarning},
|
||||
};
|
||||
placeWidgets(
|
||||
obs_module_text("AdvSceneSwitcher.action.pluginState.entry"),
|
||||
@@ -149,7 +185,8 @@ void MacroActionPluginStateEdit::UpdateEntryData()
|
||||
populateValueSelection(_values, _entryData->_action);
|
||||
_values->setCurrentIndex(_entryData->_value);
|
||||
_scenes->setCurrentText(GetWeakSourceName(_entryData->_scene).c_str());
|
||||
SetWidgetVisibility(_entryData->_action, _entryData->_value);
|
||||
_settings->SetPath(QString::fromStdString(_entryData->_settingsPath));
|
||||
SetWidgetVisibility();
|
||||
}
|
||||
|
||||
void MacroActionPluginStateEdit::ActionChanged(int value)
|
||||
@@ -161,7 +198,7 @@ void MacroActionPluginStateEdit::ActionChanged(int value)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_action = static_cast<PluginStateAction>(value);
|
||||
SetWidgetVisibility(_entryData->_action, _entryData->_value);
|
||||
SetWidgetVisibility();
|
||||
}
|
||||
|
||||
_values->clear();
|
||||
@@ -176,7 +213,7 @@ void MacroActionPluginStateEdit::ValueChanged(int value)
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_value = value;
|
||||
SetWidgetVisibility(_entryData->_action, _entryData->_value);
|
||||
SetWidgetVisibility();
|
||||
}
|
||||
|
||||
void MacroActionPluginStateEdit::SceneChanged(const QString &text)
|
||||
@@ -189,15 +226,40 @@ void MacroActionPluginStateEdit::SceneChanged(const QString &text)
|
||||
_entryData->_scene = GetWeakSourceByQString(text);
|
||||
}
|
||||
|
||||
void MacroActionPluginStateEdit::SetWidgetVisibility(PluginStateAction action,
|
||||
int value)
|
||||
void MacroActionPluginStateEdit::PathChanged(const QString &text)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_settingsPath = text.toStdString();
|
||||
}
|
||||
|
||||
void MacroActionPluginStateEdit::SetWidgetVisibility()
|
||||
{
|
||||
if (!_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
_values->hide();
|
||||
_scenes->hide();
|
||||
if (action == PluginStateAction::NO_MATCH_BEHAVIOUR) {
|
||||
_settings->hide();
|
||||
_settingsWarning->hide();
|
||||
switch (_entryData->_action) {
|
||||
case PluginStateAction::STOP:
|
||||
break;
|
||||
case PluginStateAction::NO_MATCH_BEHAVIOUR:
|
||||
_values->show();
|
||||
if ((NoMatch)value == SWITCH) {
|
||||
if ((NoMatch)_entryData->_value == SWITCH) {
|
||||
_scenes->show();
|
||||
}
|
||||
break;
|
||||
case PluginStateAction::IMPORT_SETTINGS:
|
||||
_settings->show();
|
||||
_settingsWarning->show();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
99
src/macro-action-profile.cpp
Normal file
99
src/macro-action-profile.cpp
Normal file
@@ -0,0 +1,99 @@
|
||||
#include "headers/macro-action-profile.hpp"
|
||||
#include "headers/advanced-scene-switcher.hpp"
|
||||
#include "headers/utility.hpp"
|
||||
|
||||
const std::string MacroActionProfile::id = "profile";
|
||||
|
||||
bool MacroActionProfile::_registered = MacroActionFactory::Register(
|
||||
MacroActionProfile::id,
|
||||
{MacroActionProfile::Create, MacroActionProfileEdit::Create,
|
||||
"AdvSceneSwitcher.action.profile"});
|
||||
|
||||
bool MacroActionProfile::PerformAction()
|
||||
{
|
||||
obs_frontend_set_current_profile(_profile.c_str());
|
||||
return true;
|
||||
}
|
||||
|
||||
void MacroActionProfile::LogAction()
|
||||
{
|
||||
vblog(LOG_INFO, "set profile type to \"%s\"", _profile.c_str());
|
||||
}
|
||||
|
||||
bool MacroActionProfile::Save(obs_data_t *obj)
|
||||
{
|
||||
MacroAction::Save(obj);
|
||||
obs_data_set_string(obj, "profile", _profile.c_str());
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MacroActionProfile::Load(obs_data_t *obj)
|
||||
{
|
||||
MacroAction::Load(obj);
|
||||
_profile = obs_data_get_string(obj, "profile");
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string MacroActionProfile::GetShortDesc()
|
||||
{
|
||||
return _profile;
|
||||
}
|
||||
|
||||
void populateProfileSelection(QComboBox *box)
|
||||
{
|
||||
auto profiles = obs_frontend_get_profiles();
|
||||
char **temp = profiles;
|
||||
while (*temp) {
|
||||
const char *name = *temp;
|
||||
box->addItem(name);
|
||||
temp++;
|
||||
}
|
||||
bfree(profiles);
|
||||
box->model()->sort(0);
|
||||
addSelectionEntry(
|
||||
box, obs_module_text("AdvSceneSwitcher.selectProfile"), false);
|
||||
box->setCurrentIndex(0);
|
||||
}
|
||||
|
||||
MacroActionProfileEdit::MacroActionProfileEdit(
|
||||
QWidget *parent, std::shared_ptr<MacroActionProfile> entryData)
|
||||
: QWidget(parent)
|
||||
{
|
||||
_profiles = new QComboBox();
|
||||
populateProfileSelection(_profiles);
|
||||
QWidget::connect(_profiles, SIGNAL(currentTextChanged(const QString &)),
|
||||
this, SLOT(ProfileChanged(const QString &)));
|
||||
|
||||
QHBoxLayout *mainLayout = new QHBoxLayout;
|
||||
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
|
||||
{"{{profiles}}", _profiles},
|
||||
};
|
||||
placeWidgets(obs_module_text("AdvSceneSwitcher.action.profile.entry"),
|
||||
mainLayout, widgetPlaceholders);
|
||||
setLayout(mainLayout);
|
||||
|
||||
_entryData = entryData;
|
||||
UpdateEntryData();
|
||||
_loading = false;
|
||||
}
|
||||
|
||||
void MacroActionProfileEdit::UpdateEntryData()
|
||||
{
|
||||
if (!_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
_profiles->setCurrentText(QString::fromStdString(_entryData->_profile));
|
||||
}
|
||||
|
||||
void MacroActionProfileEdit::ProfileChanged(const QString &text)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_profile = text.toStdString();
|
||||
emit HeaderInfoChanged(
|
||||
QString::fromStdString(_entryData->GetShortDesc()));
|
||||
}
|
||||
@@ -3,6 +3,7 @@
|
||||
#include "headers/name-dialog.hpp"
|
||||
#include "headers/utility.hpp"
|
||||
|
||||
#include <QDialogButtonBox>
|
||||
#include <cstdlib>
|
||||
|
||||
const std::string MacroActionRandom::id = "random";
|
||||
@@ -93,7 +94,6 @@ MacroActionRandomEdit::MacroActionRandomEdit(
|
||||
QWidget *parent, std::shared_ptr<MacroActionRandom> entryData)
|
||||
: QWidget(parent)
|
||||
{
|
||||
_macroSelection = new MacroSelection(window());
|
||||
_macroList = new QListWidget();
|
||||
_macroList->setSortingEnabled(true);
|
||||
_add = new QPushButton();
|
||||
@@ -117,9 +117,7 @@ MacroActionRandomEdit::MacroActionRandomEdit(
|
||||
SLOT(MacroRename(const QString &, const QString &)));
|
||||
|
||||
auto *entryLayout = new QHBoxLayout;
|
||||
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
|
||||
{"{{macroSelection}}", _macroSelection},
|
||||
};
|
||||
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {};
|
||||
placeWidgets(obs_module_text("AdvSceneSwitcher.action.random.entry"),
|
||||
entryLayout, widgetPlaceholders);
|
||||
|
||||
@@ -130,8 +128,6 @@ MacroActionRandomEdit::MacroActionRandomEdit(
|
||||
|
||||
auto *mainLayout = new QVBoxLayout;
|
||||
mainLayout->addLayout(entryLayout);
|
||||
mainLayout->addWidget(new QLabel(
|
||||
obs_module_text("AdvSceneSwitcher.action.random.arguments")));
|
||||
mainLayout->addWidget(_macroList);
|
||||
mainLayout->addLayout(argButtonLayout);
|
||||
setLayout(mainLayout);
|
||||
@@ -155,6 +151,7 @@ void MacroActionRandomEdit::UpdateEntryData()
|
||||
QListWidgetItem *item = new QListWidgetItem(name, _macroList);
|
||||
item->setData(Qt::UserRole, name);
|
||||
}
|
||||
SetMacroListSize();
|
||||
}
|
||||
|
||||
void MacroActionRandomEdit::MacroRemove(const QString &name)
|
||||
@@ -192,8 +189,14 @@ void MacroActionRandomEdit::AddMacro()
|
||||
return;
|
||||
}
|
||||
|
||||
auto macroName = _macroSelection->currentText();
|
||||
MacroRef macro(macroName.toStdString());
|
||||
std::string macroName;
|
||||
bool accepted = MacroDialog::AskForMacro(this, macroName);
|
||||
|
||||
if (!accepted || macroName.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
MacroRef macro(macroName);
|
||||
|
||||
if (!macro.get()) {
|
||||
return;
|
||||
@@ -203,12 +206,14 @@ void MacroActionRandomEdit::AddMacro()
|
||||
return;
|
||||
}
|
||||
|
||||
QVariant v = QVariant::fromValue(macroName);
|
||||
QListWidgetItem *item = new QListWidgetItem(macroName, _macroList);
|
||||
item->setData(Qt::UserRole, macroName);
|
||||
QVariant v = QVariant::fromValue(QString::fromStdString(macroName));
|
||||
QListWidgetItem *item = new QListWidgetItem(
|
||||
QString::fromStdString(macroName), _macroList);
|
||||
item->setData(Qt::UserRole, QString::fromStdString(macroName));
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_macros.push_back(macro);
|
||||
SetMacroListSize();
|
||||
}
|
||||
|
||||
void MacroActionRandomEdit::RemoveMacro()
|
||||
@@ -232,6 +237,7 @@ void MacroActionRandomEdit::RemoveMacro()
|
||||
}
|
||||
}
|
||||
delete item;
|
||||
SetMacroListSize();
|
||||
}
|
||||
|
||||
int MacroActionRandomEdit::FindEntry(const std::string ¯o)
|
||||
@@ -251,13 +257,52 @@ int MacroActionRandomEdit::FindEntry(const std::string ¯o)
|
||||
return idx;
|
||||
}
|
||||
|
||||
void MacroActionRandomEdit::MacroSelectionChanged(int idx)
|
||||
void MacroActionRandomEdit::SetMacroListSize()
|
||||
{
|
||||
if (_loading || !_entryData || idx == -1) {
|
||||
return;
|
||||
setHeightToContentHeight(_macroList);
|
||||
adjustSize();
|
||||
}
|
||||
|
||||
MacroDialog::MacroDialog(QWidget *)
|
||||
{
|
||||
setModal(true);
|
||||
setWindowModality(Qt::WindowModality::ApplicationModal);
|
||||
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
|
||||
setMinimumWidth(350);
|
||||
setMinimumHeight(70);
|
||||
|
||||
QDialogButtonBox *buttonbox = new QDialogButtonBox(
|
||||
QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
|
||||
|
||||
buttonbox->setCenterButtons(true);
|
||||
connect(buttonbox, &QDialogButtonBox::accepted, this, &QDialog::accept);
|
||||
connect(buttonbox, &QDialogButtonBox::rejected, this, &QDialog::reject);
|
||||
|
||||
_macroSelection = new MacroSelection(window());
|
||||
auto *selectionLayout = new QHBoxLayout;
|
||||
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
|
||||
{"{{macroSelection}}", _macroSelection},
|
||||
};
|
||||
placeWidgets(obs_module_text("AdvSceneSwitcher.askForMacro"),
|
||||
selectionLayout, widgetPlaceholders);
|
||||
auto *layout = new QVBoxLayout();
|
||||
layout->addLayout(selectionLayout);
|
||||
layout->addWidget(buttonbox);
|
||||
setLayout(layout);
|
||||
}
|
||||
|
||||
bool MacroDialog::AskForMacro(QWidget *parent, std::string ¯oName)
|
||||
{
|
||||
MacroDialog dialog(parent);
|
||||
dialog.setWindowTitle(obs_module_text("AdvSceneSwitcher.windowTitle"));
|
||||
|
||||
if (dialog.exec() != DialogCode::Accepted) {
|
||||
return false;
|
||||
}
|
||||
macroName = dialog._macroSelection->currentText().toUtf8().constData();
|
||||
if (macroName == obs_module_text("AdvSceneSwitcher.selectMacro")) {
|
||||
return false;
|
||||
}
|
||||
|
||||
QListWidgetItem *item = _macroList->item(idx);
|
||||
QString name = item->text();
|
||||
_macroSelection->SetCurrentMacro(GetMacroByName(name.toUtf8().data()));
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
|
||||
#include <QProcess>
|
||||
#include <QFileDialog>
|
||||
#include <QDesktopServices>
|
||||
|
||||
const std::string MacroActionRun::id = "run";
|
||||
|
||||
@@ -14,7 +15,13 @@ bool MacroActionRun::_registered = MacroActionFactory::Register(
|
||||
|
||||
bool MacroActionRun::PerformAction()
|
||||
{
|
||||
QProcess::startDetached(QString::fromStdString(_path), _args);
|
||||
if (!QProcess::startDetached(QString::fromStdString(_path), _args) &&
|
||||
_args.empty()) {
|
||||
vblog(LOG_INFO, "run \"%s\" using QDesktopServices",
|
||||
_path.c_str());
|
||||
QDesktopServices::openUrl(
|
||||
QUrl::fromLocalFile(QString::fromStdString(_path)));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -137,6 +144,7 @@ void MacroActionRunEdit::UpdateEntryData()
|
||||
QListWidgetItem *item = new QListWidgetItem(arg, _argList);
|
||||
item->setData(Qt::UserRole, arg);
|
||||
}
|
||||
SetArgListSize();
|
||||
}
|
||||
|
||||
void MacroActionRunEdit::PathChanged(const QString &text)
|
||||
@@ -175,6 +183,7 @@ void MacroActionRunEdit::AddArg()
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_args << arg;
|
||||
SetArgListSize();
|
||||
}
|
||||
|
||||
void MacroActionRunEdit::RemoveArg()
|
||||
@@ -195,6 +204,7 @@ void MacroActionRunEdit::RemoveArg()
|
||||
return;
|
||||
}
|
||||
delete item;
|
||||
SetArgListSize();
|
||||
}
|
||||
|
||||
void MacroActionRunEdit::ArgUp()
|
||||
@@ -224,3 +234,9 @@ void MacroActionRunEdit::ArgDown()
|
||||
_entryData->_args.move(idx, idx + 1);
|
||||
}
|
||||
}
|
||||
|
||||
void MacroActionRunEdit::SetArgListSize()
|
||||
{
|
||||
setHeightToContentHeight(_argList);
|
||||
adjustSize();
|
||||
}
|
||||
|
||||
117
src/macro-action-scene-collection.cpp
Normal file
117
src/macro-action-scene-collection.cpp
Normal file
@@ -0,0 +1,117 @@
|
||||
#include "headers/macro-action-scene-collection.hpp"
|
||||
#include "headers/advanced-scene-switcher.hpp"
|
||||
#include "headers/utility.hpp"
|
||||
|
||||
const std::string MacroActionSceneCollection::id = "scene_collection";
|
||||
|
||||
bool MacroActionSceneCollection::_registered = MacroActionFactory::Register(
|
||||
MacroActionSceneCollection::id,
|
||||
{MacroActionSceneCollection::Create,
|
||||
MacroActionSceneCollectionEdit::Create,
|
||||
"AdvSceneSwitcher.action.sceneCollection"});
|
||||
|
||||
bool MacroActionSceneCollection::PerformAction()
|
||||
{
|
||||
// Changing the scene collection will also reload the settings of the
|
||||
// scene switcher, so to avoid issues of not being able to change
|
||||
// settings ignore this action if the settings dialog is opened.
|
||||
if (switcher->settingsWindowOpened) {
|
||||
return false;
|
||||
}
|
||||
obs_frontend_set_current_scene_collection(_sceneCollection.c_str());
|
||||
// It does not make sense to continue as the current settings will be
|
||||
// invalid after switching scene collection.
|
||||
return false;
|
||||
}
|
||||
|
||||
void MacroActionSceneCollection::LogAction()
|
||||
{
|
||||
vblog(LOG_INFO, "set scene collection type to \"%s\"",
|
||||
_sceneCollection.c_str());
|
||||
}
|
||||
|
||||
bool MacroActionSceneCollection::Save(obs_data_t *obj)
|
||||
{
|
||||
MacroAction::Save(obj);
|
||||
obs_data_set_string(obj, "sceneCollection", _sceneCollection.c_str());
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MacroActionSceneCollection::Load(obs_data_t *obj)
|
||||
{
|
||||
MacroAction::Load(obj);
|
||||
_sceneCollection = obs_data_get_string(obj, "sceneCollection");
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string MacroActionSceneCollection::GetShortDesc()
|
||||
{
|
||||
return _sceneCollection;
|
||||
}
|
||||
|
||||
void populateSceneCollectionSelection(QComboBox *box)
|
||||
{
|
||||
auto sceneCollections = obs_frontend_get_scene_collections();
|
||||
char **temp = sceneCollections;
|
||||
while (*temp) {
|
||||
const char *name = *temp;
|
||||
box->addItem(name);
|
||||
temp++;
|
||||
}
|
||||
bfree(sceneCollections);
|
||||
box->model()->sort(0);
|
||||
addSelectionEntry(
|
||||
box, obs_module_text("AdvSceneSwitcher.selectSceneCollection"),
|
||||
false);
|
||||
box->setCurrentIndex(0);
|
||||
}
|
||||
|
||||
MacroActionSceneCollectionEdit::MacroActionSceneCollectionEdit(
|
||||
QWidget *parent, std::shared_ptr<MacroActionSceneCollection> entryData)
|
||||
: QWidget(parent)
|
||||
{
|
||||
_sceneCollections = new QComboBox();
|
||||
populateSceneCollectionSelection(_sceneCollections);
|
||||
QWidget::connect(_sceneCollections,
|
||||
SIGNAL(currentTextChanged(const QString &)), this,
|
||||
SLOT(SceneCollectionChanged(const QString &)));
|
||||
|
||||
QHBoxLayout *entryLayout = new QHBoxLayout;
|
||||
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
|
||||
{"{{sceneCollections}}", _sceneCollections},
|
||||
};
|
||||
placeWidgets(obs_module_text(
|
||||
"AdvSceneSwitcher.action.sceneCollection.entry"),
|
||||
entryLayout, widgetPlaceholders);
|
||||
QVBoxLayout *mainLayout = new QVBoxLayout;
|
||||
mainLayout->addLayout(entryLayout);
|
||||
mainLayout->addWidget(new QLabel(obs_module_text(
|
||||
"AdvSceneSwitcher.action.sceneCollection.warning")));
|
||||
setLayout(mainLayout);
|
||||
|
||||
_entryData = entryData;
|
||||
UpdateEntryData();
|
||||
_loading = false;
|
||||
}
|
||||
|
||||
void MacroActionSceneCollectionEdit::UpdateEntryData()
|
||||
{
|
||||
if (!_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
_sceneCollections->setCurrentText(
|
||||
QString::fromStdString(_entryData->_sceneCollection));
|
||||
}
|
||||
|
||||
void MacroActionSceneCollectionEdit::SceneCollectionChanged(const QString &text)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_sceneCollection = text.toStdString();
|
||||
emit HeaderInfoChanged(
|
||||
QString::fromStdString(_entryData->GetShortDesc()));
|
||||
}
|
||||
@@ -22,37 +22,77 @@ const static std::map<SceneOrderAction, std::string> actionTypes = {
|
||||
"AdvSceneSwitcher.action.sceneOrder.type.movePosition"},
|
||||
};
|
||||
|
||||
bool MacroActionSceneOrder::PerformAction()
|
||||
void moveSceneItemsUp(std::vector<obs_scene_item *> &items)
|
||||
{
|
||||
auto s = obs_weak_source_get_source(_scene.GetScene());
|
||||
auto scene = obs_scene_from_source(s);
|
||||
auto name = GetWeakSourceName(_source);
|
||||
auto items = getSceneItemsWithName(scene, name);
|
||||
// In the case of the same source being in two sequential positions
|
||||
// moving the sources up will cause the sources to swap positions due to
|
||||
// the order scene items are being iterated over, which is most likely
|
||||
// not the desired effect.
|
||||
// Instead we reverse the order of the scene items so all scene items
|
||||
// will be moved up.
|
||||
std::reverse(items.begin(), items.end());
|
||||
|
||||
for (auto &i : items) {
|
||||
switch (_action) {
|
||||
case SceneOrderAction::MOVE_UP:
|
||||
obs_sceneitem_set_order(i, OBS_ORDER_MOVE_UP);
|
||||
break;
|
||||
case SceneOrderAction::MOVE_DOWN:
|
||||
obs_sceneitem_set_order(i, OBS_ORDER_MOVE_DOWN);
|
||||
break;
|
||||
case SceneOrderAction::MOVE_TOP:
|
||||
obs_sceneitem_set_order(i, OBS_ORDER_MOVE_TOP);
|
||||
break;
|
||||
case SceneOrderAction::MOVE_BOTTOM:
|
||||
obs_sceneitem_set_order(i, OBS_ORDER_MOVE_BOTTOM);
|
||||
break;
|
||||
case SceneOrderAction::POSITION:
|
||||
obs_sceneitem_set_order_position(i, _position);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
obs_sceneitem_set_order(i, OBS_ORDER_MOVE_UP);
|
||||
obs_sceneitem_release(i);
|
||||
}
|
||||
}
|
||||
|
||||
obs_source_release(s);
|
||||
void moveSceneItemsDown(std::vector<obs_scene_item *> &items)
|
||||
{
|
||||
for (auto &i : items) {
|
||||
obs_sceneitem_set_order(i, OBS_ORDER_MOVE_DOWN);
|
||||
obs_sceneitem_release(i);
|
||||
}
|
||||
}
|
||||
|
||||
void moveSceneItemsTop(std::vector<obs_scene_item *> &items)
|
||||
{
|
||||
for (auto &i : items) {
|
||||
obs_sceneitem_set_order(i, OBS_ORDER_MOVE_TOP);
|
||||
obs_sceneitem_release(i);
|
||||
}
|
||||
}
|
||||
|
||||
void moveSceneItemsBottom(std::vector<obs_scene_item *> &items)
|
||||
{
|
||||
for (auto &i : items) {
|
||||
obs_sceneitem_set_order(i, OBS_ORDER_MOVE_BOTTOM);
|
||||
obs_sceneitem_release(i);
|
||||
}
|
||||
}
|
||||
|
||||
void moveSceneItemsPos(std::vector<obs_scene_item *> &items, int pos)
|
||||
{
|
||||
for (auto &i : items) {
|
||||
obs_sceneitem_set_order_position(i, pos);
|
||||
obs_sceneitem_release(i);
|
||||
}
|
||||
}
|
||||
|
||||
bool MacroActionSceneOrder::PerformAction()
|
||||
{
|
||||
auto items = _source.GetSceneItems(_scene);
|
||||
|
||||
switch (_action) {
|
||||
case SceneOrderAction::MOVE_UP:
|
||||
moveSceneItemsUp(items);
|
||||
break;
|
||||
case SceneOrderAction::MOVE_DOWN:
|
||||
moveSceneItemsDown(items);
|
||||
break;
|
||||
case SceneOrderAction::MOVE_TOP:
|
||||
moveSceneItemsTop(items);
|
||||
break;
|
||||
case SceneOrderAction::MOVE_BOTTOM:
|
||||
moveSceneItemsBottom(items);
|
||||
break;
|
||||
case SceneOrderAction::POSITION:
|
||||
moveSceneItemsPos(items, _position);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -62,7 +102,7 @@ void MacroActionSceneOrder::LogAction()
|
||||
if (it != actionTypes.end()) {
|
||||
vblog(LOG_INFO,
|
||||
"performed order action \"%s\" for source \"%s\" on scene \"%s\"",
|
||||
it->second.c_str(), GetWeakSourceName(_source).c_str(),
|
||||
it->second.c_str(), _source.ToString().c_str(),
|
||||
_scene.ToString().c_str());
|
||||
} else {
|
||||
blog(LOG_WARNING, "ignored unknown scene order action %d",
|
||||
@@ -74,7 +114,7 @@ bool MacroActionSceneOrder::Save(obs_data_t *obj)
|
||||
{
|
||||
MacroAction::Save(obj);
|
||||
_scene.Save(obj);
|
||||
obs_data_set_string(obj, "source", GetWeakSourceName(_source).c_str());
|
||||
_source.Save(obj);
|
||||
obs_data_set_int(obj, "action", static_cast<int>(_action));
|
||||
obs_data_set_int(obj, "position", _position);
|
||||
return true;
|
||||
@@ -82,10 +122,16 @@ bool MacroActionSceneOrder::Save(obs_data_t *obj)
|
||||
|
||||
bool MacroActionSceneOrder::Load(obs_data_t *obj)
|
||||
{
|
||||
// Convert old data format
|
||||
// TODO: Remove in future version
|
||||
if (obs_data_has_user_value(obj, "source")) {
|
||||
auto sourceName = obs_data_get_string(obj, "source");
|
||||
obs_data_set_string(obj, "sceneItem", sourceName);
|
||||
}
|
||||
|
||||
MacroAction::Load(obj);
|
||||
_scene.Load(obj);
|
||||
const char *sourceName = obs_data_get_string(obj, "source");
|
||||
_source = GetWeakSourceByName(sourceName);
|
||||
_source.Load(obj);
|
||||
_action =
|
||||
static_cast<SceneOrderAction>(obs_data_get_int(obj, "action"));
|
||||
_position = obs_data_get_int(obj, "position");
|
||||
@@ -94,10 +140,10 @@ bool MacroActionSceneOrder::Load(obs_data_t *obj)
|
||||
|
||||
std::string MacroActionSceneOrder::GetShortDesc()
|
||||
{
|
||||
if (_source) {
|
||||
return _scene.ToString() + " - " + GetWeakSourceName(_source);
|
||||
if (_source.ToString().empty()) {
|
||||
return "";
|
||||
}
|
||||
return "";
|
||||
return _scene.ToString() + " - " + _source.ToString();
|
||||
}
|
||||
|
||||
static inline void populateActionSelection(QComboBox *list)
|
||||
@@ -112,7 +158,7 @@ MacroActionSceneOrderEdit::MacroActionSceneOrderEdit(
|
||||
: QWidget(parent)
|
||||
{
|
||||
_scenes = new SceneSelectionWidget(window(), false, false, true);
|
||||
_sources = new QComboBox();
|
||||
_sources = new SceneItemSelectionWidget(parent);
|
||||
_actions = new QComboBox();
|
||||
_position = new QSpinBox();
|
||||
|
||||
@@ -122,8 +168,11 @@ MacroActionSceneOrderEdit::MacroActionSceneOrderEdit(
|
||||
SLOT(ActionChanged(int)));
|
||||
QWidget::connect(_scenes, SIGNAL(SceneChanged(const SceneSelection &)),
|
||||
this, SLOT(SceneChanged(const SceneSelection &)));
|
||||
QWidget::connect(_sources, SIGNAL(currentTextChanged(const QString &)),
|
||||
this, SLOT(SourceChanged(const QString &)));
|
||||
QWidget::connect(_scenes, SIGNAL(SceneChanged(const SceneSelection &)),
|
||||
_sources, SLOT(SceneChanged(const SceneSelection &)));
|
||||
QWidget::connect(_sources,
|
||||
SIGNAL(SceneItemChanged(const SceneItemSelection &)),
|
||||
this, SLOT(SourceChanged(const SceneItemSelection &)));
|
||||
QWidget::connect(_position, SIGNAL(valueChanged(int)), this,
|
||||
SLOT(PositionChanged(int)));
|
||||
|
||||
@@ -152,9 +201,7 @@ void MacroActionSceneOrderEdit::UpdateEntryData()
|
||||
|
||||
_actions->setCurrentIndex(static_cast<int>(_entryData->_action));
|
||||
_scenes->SetScene(_entryData->_scene);
|
||||
populateSceneItemSelection(_sources, _entryData->_scene);
|
||||
_sources->setCurrentText(
|
||||
GetWeakSourceName(_entryData->_source).c_str());
|
||||
_sources->SetSceneItem(_entryData->_source);
|
||||
_position->setValue(_entryData->_position);
|
||||
_position->setVisible(_entryData->_action ==
|
||||
SceneOrderAction::POSITION);
|
||||
@@ -165,22 +212,19 @@ void MacroActionSceneOrderEdit::SceneChanged(const SceneSelection &s)
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_scene = s;
|
||||
}
|
||||
_sources->clear();
|
||||
populateSceneItemSelection(_sources, _entryData->_scene);
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_scene = s;
|
||||
}
|
||||
|
||||
void MacroActionSceneOrderEdit::SourceChanged(const QString &text)
|
||||
void MacroActionSceneOrderEdit::SourceChanged(const SceneItemSelection &item)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_source = GetWeakSourceByQString(text);
|
||||
_entryData->_source = item;
|
||||
emit HeaderInfoChanged(
|
||||
QString::fromStdString(_entryData->GetShortDesc()));
|
||||
}
|
||||
|
||||
@@ -9,75 +9,165 @@ bool MacroActionSwitchScene::_registered = MacroActionFactory::Register(
|
||||
{MacroActionSwitchScene::Create, MacroActionSwitchSceneEdit::Create,
|
||||
"AdvSceneSwitcher.action.switchScene"});
|
||||
|
||||
void waitForTransitionChange(OBSWeakSource &target)
|
||||
{
|
||||
const int maxNrChecks = 100;
|
||||
int curCheckNr = 0;
|
||||
bool transitionEnded = false;
|
||||
|
||||
auto sceneCheckStartedOn = obs_frontend_get_current_scene();
|
||||
obs_source_release(sceneCheckStartedOn);
|
||||
switcher->abortMacroWait = false;
|
||||
std::unique_lock<std::mutex> lock(switcher->m);
|
||||
while (!transitionEnded && curCheckNr < maxNrChecks &&
|
||||
!switcher->abortMacroWait) {
|
||||
switcher->abortMacroWait = false;
|
||||
switcher->macroTransitionCv.wait_for(
|
||||
lock,
|
||||
std::chrono::milliseconds(
|
||||
(long long)switcher->interval),
|
||||
[] { return switcher->abortMacroWait.load(); });
|
||||
auto curScene = obs_frontend_get_current_scene();
|
||||
obs_source_release(curScene);
|
||||
transitionEnded = curScene != sceneCheckStartedOn ||
|
||||
switcher->currentScene == target;
|
||||
++curCheckNr;
|
||||
}
|
||||
}
|
||||
|
||||
bool MacroActionSwitchScene::PerformAction()
|
||||
{
|
||||
OBSWeakSource scene = getScene();
|
||||
switchScene({scene, transition, (int)(_duration.seconds * 1000)});
|
||||
auto scene = _scene.GetScene();
|
||||
switchScene({scene, _transition.GetTransition(),
|
||||
(int)(_duration.seconds * 1000)});
|
||||
if (_blockUntilTransitionDone) {
|
||||
waitForTransitionChange(scene);
|
||||
return !switcher->abortMacroWait;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void MacroActionSwitchScene::LogAction()
|
||||
{
|
||||
if (switcher->verbose) {
|
||||
logMatch();
|
||||
auto t = _scene.GetType();
|
||||
auto sceneName = GetWeakSourceName(_scene.GetScene(false));
|
||||
switch (t) {
|
||||
case SceneSelectionType::SCENE:
|
||||
vblog(LOG_INFO, "switch to scene '%s'",
|
||||
_scene.ToString().c_str());
|
||||
break;
|
||||
case SceneSelectionType::GROUP:
|
||||
vblog(LOG_INFO, "switch to scene '%s' (scene group '%s')",
|
||||
sceneName.c_str(), _scene.ToString().c_str());
|
||||
break;
|
||||
case SceneSelectionType::PREVIOUS:
|
||||
vblog(LOG_INFO, "switch to previous scene '%s'",
|
||||
sceneName.c_str());
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool MacroActionSwitchScene::Save(obs_data_t *obj)
|
||||
{
|
||||
MacroAction::Save(obj);
|
||||
SceneSwitcherEntry::save(obj);
|
||||
_scene.Save(obj);
|
||||
_transition.Save(obj);
|
||||
_duration.Save(obj);
|
||||
obs_data_set_bool(obj, "blockUntilTransitionDone",
|
||||
_blockUntilTransitionDone);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MacroActionSwitchScene::Load(obs_data_t *obj)
|
||||
{
|
||||
// Convert old data format
|
||||
// TODO: Remove in future version
|
||||
if (obs_data_has_user_value(obj, "targetType")) {
|
||||
auto sceneName = obs_data_get_string(obj, "target");
|
||||
obs_data_set_string(obj, "scene", sceneName);
|
||||
auto transitionName = obs_data_get_string(obj, "transition");
|
||||
bool usePreviousScene =
|
||||
strcmp(sceneName, previous_scene_name) == 0;
|
||||
bool useCurrentTransition =
|
||||
strcmp(transitionName, current_transition_name) == 0;
|
||||
obs_data_set_int(
|
||||
obj, "sceneType",
|
||||
(usePreviousScene)
|
||||
? static_cast<int>(SceneSelectionType::PREVIOUS)
|
||||
: obs_data_get_int(obj, "targetType"));
|
||||
obs_data_set_int(
|
||||
obj, "transitionType",
|
||||
(useCurrentTransition)
|
||||
? static_cast<int>(
|
||||
TransitionSelectionType::CURRENT)
|
||||
: static_cast<int>(
|
||||
TransitionSelectionType::TRANSITION));
|
||||
}
|
||||
|
||||
MacroAction::Load(obj);
|
||||
SceneSwitcherEntry::load(obj);
|
||||
_scene.Load(obj);
|
||||
_transition.Load(obj);
|
||||
_duration.Load(obj);
|
||||
_blockUntilTransitionDone =
|
||||
obs_data_get_bool(obj, "blockUntilTransitionDone");
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string MacroActionSwitchScene::GetShortDesc()
|
||||
{
|
||||
if (targetType == SwitchTargetType::Scene && scene) {
|
||||
return GetWeakSourceName(scene);
|
||||
}
|
||||
if (targetType == SwitchTargetType::SceneGroup && group) {
|
||||
return group->name;
|
||||
}
|
||||
return "";
|
||||
return _scene.ToString();
|
||||
}
|
||||
|
||||
MacroActionSwitchSceneEdit::MacroActionSwitchSceneEdit(
|
||||
QWidget *parent, std::shared_ptr<MacroActionSwitchScene> entryData)
|
||||
: SwitchWidget(parent, entryData.get(), true, true)
|
||||
: QWidget(parent)
|
||||
{
|
||||
_scenes = new SceneSelectionWidget(window(), true, true);
|
||||
_transitions = new TransitionSelectionWidget(this);
|
||||
_duration = new DurationSelection(parent, false);
|
||||
_blockUntilTransitionDone = new QCheckBox(obs_module_text(
|
||||
"AdvSceneSwitcher.action.scene.blockUntilTransitionDone"));
|
||||
|
||||
QWidget::connect(_scenes, SIGNAL(SceneChanged(const SceneSelection &)),
|
||||
this, SLOT(SceneChanged(const SceneSelection &)));
|
||||
QWidget::connect(_transitions,
|
||||
SIGNAL(TransitionChanged(const TransitionSelection &)),
|
||||
this,
|
||||
SLOT(TransitionChanged(const TransitionSelection &)));
|
||||
QWidget::connect(_duration, SIGNAL(DurationChanged(double)), this,
|
||||
SLOT(DurationChanged(double)));
|
||||
QWidget::connect(scenes, SIGNAL(currentTextChanged(const QString &)),
|
||||
this, SLOT(ChangeHeaderInfo(const QString &)));
|
||||
QWidget::connect(_blockUntilTransitionDone, SIGNAL(stateChanged(int)),
|
||||
this, SLOT(BlockUntilTransitionDoneChanged(int)));
|
||||
|
||||
QHBoxLayout *mainLayout = new QHBoxLayout;
|
||||
QHBoxLayout *entryLayout = new QHBoxLayout;
|
||||
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
|
||||
{"{{scenes}}", scenes},
|
||||
{"{{transitions}}", transitions},
|
||||
{"{{scenes}}", _scenes},
|
||||
{"{{transitions}}", _transitions},
|
||||
{"{{duration}}", _duration},
|
||||
{"{{blockUntilTransitionDone}}", _blockUntilTransitionDone},
|
||||
};
|
||||
placeWidgets(obs_module_text("AdvSceneSwitcher.action.scene.entry"),
|
||||
mainLayout, widgetPlaceholders);
|
||||
entryLayout, widgetPlaceholders);
|
||||
|
||||
QVBoxLayout *mainLayout = new QVBoxLayout;
|
||||
mainLayout->addLayout(entryLayout);
|
||||
mainLayout->addWidget(_blockUntilTransitionDone);
|
||||
setLayout(mainLayout);
|
||||
|
||||
_entryData = entryData;
|
||||
_scenes->SetScene(_entryData->_scene);
|
||||
_transitions->SetTransition(_entryData->_transition);
|
||||
_duration->SetDuration(_entryData->_duration);
|
||||
SwitchWidget::loading = false;
|
||||
_blockUntilTransitionDone->setChecked(
|
||||
_entryData->_blockUntilTransitionDone);
|
||||
_loading = false;
|
||||
}
|
||||
|
||||
void MacroActionSwitchSceneEdit::DurationChanged(double seconds)
|
||||
{
|
||||
if (SwitchWidget::loading || !_entryData) {
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -85,8 +175,34 @@ void MacroActionSwitchSceneEdit::DurationChanged(double seconds)
|
||||
_entryData->_duration.seconds = seconds;
|
||||
}
|
||||
|
||||
void MacroActionSwitchSceneEdit::ChangeHeaderInfo(const QString &)
|
||||
void MacroActionSwitchSceneEdit::BlockUntilTransitionDoneChanged(int state)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_blockUntilTransitionDone = state;
|
||||
}
|
||||
|
||||
void MacroActionSwitchSceneEdit::SceneChanged(const SceneSelection &s)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_scene = s;
|
||||
emit HeaderInfoChanged(
|
||||
QString::fromStdString(_entryData->GetShortDesc()));
|
||||
}
|
||||
|
||||
void MacroActionSwitchSceneEdit::TransitionChanged(const TransitionSelection &t)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_transition = t;
|
||||
}
|
||||
|
||||
@@ -12,13 +12,8 @@ bool MacroActionSceneTransform::_registered = MacroActionFactory::Register(
|
||||
|
||||
bool MacroActionSceneTransform::PerformAction()
|
||||
{
|
||||
auto s = obs_weak_source_get_source(_scene.GetScene(false));
|
||||
auto scene = obs_scene_from_source(s);
|
||||
auto name = GetWeakSourceName(_source);
|
||||
auto items = getSceneItemsWithName(scene, name);
|
||||
|
||||
auto items = _source.GetSceneItems(_scene);
|
||||
for (auto &item : items) {
|
||||
// Apply settings
|
||||
obs_sceneitem_defer_update_begin(item);
|
||||
obs_sceneitem_set_info(item, &_info);
|
||||
obs_sceneitem_set_crop(item, &_crop);
|
||||
@@ -26,8 +21,6 @@ bool MacroActionSceneTransform::PerformAction()
|
||||
|
||||
obs_sceneitem_release(item);
|
||||
}
|
||||
|
||||
obs_source_release(s);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -35,34 +28,40 @@ void MacroActionSceneTransform::LogAction()
|
||||
{
|
||||
vblog(LOG_INFO,
|
||||
"performed transform action for source \"%s\" on scene \"%s\"",
|
||||
GetWeakSourceName(_source).c_str(), _scene.ToString().c_str());
|
||||
_source.ToString().c_str(), _scene.ToString().c_str());
|
||||
}
|
||||
|
||||
bool MacroActionSceneTransform::Save(obs_data_t *obj)
|
||||
{
|
||||
MacroAction::Save(obj);
|
||||
_scene.Save(obj);
|
||||
obs_data_set_string(obj, "source", GetWeakSourceName(_source).c_str());
|
||||
_source.Save(obj);
|
||||
saveTransformState(obj, _info, _crop);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MacroActionSceneTransform::Load(obs_data_t *obj)
|
||||
{
|
||||
// Convert old data format
|
||||
// TODO: Remove in future version
|
||||
if (obs_data_has_user_value(obj, "source")) {
|
||||
auto sourceName = obs_data_get_string(obj, "source");
|
||||
obs_data_set_string(obj, "sceneItem", sourceName);
|
||||
}
|
||||
|
||||
MacroAction::Load(obj);
|
||||
_scene.Load(obj);
|
||||
const char *sourceName = obs_data_get_string(obj, "source");
|
||||
_source = GetWeakSourceByName(sourceName);
|
||||
_source.Load(obj);
|
||||
loadTransformState(obj, _info, _crop);
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string MacroActionSceneTransform::GetShortDesc()
|
||||
{
|
||||
if (_source) {
|
||||
return _scene.ToString() + " - " + GetWeakSourceName(_source);
|
||||
if (_source.ToString().empty()) {
|
||||
return "";
|
||||
}
|
||||
return "";
|
||||
return _scene.ToString() + " - " + _source.ToString();
|
||||
}
|
||||
|
||||
std::string MacroActionSceneTransform::GetSettings()
|
||||
@@ -81,9 +80,13 @@ void MacroActionSceneTransform::SetSettings(std::string &settings)
|
||||
return;
|
||||
}
|
||||
loadTransformState(data, _info, _crop);
|
||||
auto items = _source.GetSceneItems(_scene);
|
||||
if (items.empty()) {
|
||||
return;
|
||||
}
|
||||
if (obs_data_has_user_value(data, "size")) {
|
||||
auto obj = obs_data_get_obj(data, "size");
|
||||
auto source = obs_weak_source_get_source(_source);
|
||||
auto source = obs_sceneitem_get_source(items[0]);
|
||||
if (double h = obs_data_get_double(obj, "height")) {
|
||||
_info.scale.y =
|
||||
h / double(obs_source_get_height(source));
|
||||
@@ -93,9 +96,10 @@ void MacroActionSceneTransform::SetSettings(std::string &settings)
|
||||
w / double(obs_source_get_width(source));
|
||||
}
|
||||
obs_data_release(obj);
|
||||
obs_source_release(source);
|
||||
}
|
||||
|
||||
for (auto item : items) {
|
||||
obs_sceneitem_release(item);
|
||||
}
|
||||
obs_data_release(data);
|
||||
}
|
||||
|
||||
@@ -104,15 +108,18 @@ MacroActionSceneTransformEdit::MacroActionSceneTransformEdit(
|
||||
: QWidget(parent)
|
||||
{
|
||||
_scenes = new SceneSelectionWidget(window(), false, false, true);
|
||||
_sources = new QComboBox();
|
||||
_sources = new SceneItemSelectionWidget(parent);
|
||||
_getSettings = new QPushButton(obs_module_text(
|
||||
"AdvSceneSwitcher.action.sceneTransform.getTransform"));
|
||||
_settings = new QPlainTextEdit();
|
||||
|
||||
QWidget::connect(_scenes, SIGNAL(SceneChanged(const SceneSelection &)),
|
||||
this, SLOT(SceneChanged(const SceneSelection &)));
|
||||
QWidget::connect(_sources, SIGNAL(currentTextChanged(const QString &)),
|
||||
this, SLOT(SourceChanged(const QString &)));
|
||||
QWidget::connect(_scenes, SIGNAL(SceneChanged(const SceneSelection &)),
|
||||
_sources, SLOT(SceneChanged(const SceneSelection &)));
|
||||
QWidget::connect(_sources,
|
||||
SIGNAL(SceneItemChanged(const SceneItemSelection &)),
|
||||
this, SLOT(SourceChanged(const SceneItemSelection &)));
|
||||
QWidget::connect(_getSettings, SIGNAL(clicked()), this,
|
||||
SLOT(GetSettingsClicked()));
|
||||
QWidget::connect(_settings, SIGNAL(textChanged()), this,
|
||||
@@ -152,13 +159,8 @@ void MacroActionSceneTransformEdit::UpdateEntryData()
|
||||
}
|
||||
|
||||
_scenes->SetScene(_entryData->_scene);
|
||||
populateSceneItemSelection(_sources, _entryData->_scene);
|
||||
_sources->setCurrentText(
|
||||
GetWeakSourceName(_entryData->_source).c_str());
|
||||
if (_entryData->_source) {
|
||||
_settings->setPlainText(
|
||||
formatJsonString(_entryData->GetSettings()));
|
||||
}
|
||||
_sources->SetSceneItem(_entryData->_source);
|
||||
_settings->setPlainText(formatJsonString(_entryData->GetSettings()));
|
||||
}
|
||||
|
||||
void MacroActionSceneTransformEdit::SceneChanged(const SceneSelection &s)
|
||||
@@ -166,44 +168,39 @@ void MacroActionSceneTransformEdit::SceneChanged(const SceneSelection &s)
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_scene = s;
|
||||
}
|
||||
_sources->clear();
|
||||
populateSceneItemSelection(_sources, _entryData->_scene);
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_scene = s;
|
||||
}
|
||||
|
||||
void MacroActionSceneTransformEdit::SourceChanged(const QString &text)
|
||||
void MacroActionSceneTransformEdit::SourceChanged(const SceneItemSelection &item)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_source = GetWeakSourceByQString(text);
|
||||
_entryData->_source = item;
|
||||
emit HeaderInfoChanged(
|
||||
QString::fromStdString(_entryData->GetShortDesc()));
|
||||
}
|
||||
|
||||
void MacroActionSceneTransformEdit::GetSettingsClicked()
|
||||
{
|
||||
if (_loading || !_entryData || !_entryData->_scene.GetScene(false) ||
|
||||
!_entryData->_source) {
|
||||
if (_loading || !_entryData || !_entryData->_scene.GetScene(false)) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto s = obs_weak_source_get_source(_entryData->_scene.GetScene(false));
|
||||
auto scene = obs_scene_from_source(s);
|
||||
auto name = GetWeakSourceName(_entryData->_source);
|
||||
auto item = obs_scene_find_source_recursive(scene, name.c_str());
|
||||
obs_source_release(s);
|
||||
|
||||
if (!item) {
|
||||
auto items = _entryData->_source.GetSceneItems(_entryData->_scene);
|
||||
if (items.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
_settings->setPlainText(formatJsonString(getSceneItemTransform(item)));
|
||||
auto settings = getSceneItemTransform(items[0]);
|
||||
_settings->setPlainText(formatJsonString(settings));
|
||||
for (auto item : items) {
|
||||
obs_sceneitem_release(item);
|
||||
}
|
||||
}
|
||||
|
||||
void MacroActionSceneTransformEdit::SettingsChanged()
|
||||
|
||||
@@ -29,23 +29,6 @@ struct VisibilityData {
|
||||
bool visible;
|
||||
};
|
||||
|
||||
static bool visibilitySourceEnum(obs_scene_t *, obs_sceneitem_t *item,
|
||||
void *ptr)
|
||||
{
|
||||
VisibilityData *vInfo = reinterpret_cast<VisibilityData *>(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, visibilitySourceEnum, ptr);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool visibilitySourceTypeEnum(obs_scene_t *, obs_sceneitem_t *item,
|
||||
void *ptr)
|
||||
{
|
||||
@@ -67,25 +50,29 @@ static bool visibilitySourceTypeEnum(obs_scene_t *, obs_sceneitem_t *item,
|
||||
|
||||
bool MacroActionSceneVisibility::PerformAction()
|
||||
{
|
||||
auto s = obs_weak_source_get_source(_scene.GetScene());
|
||||
auto scene = obs_scene_from_source(s);
|
||||
auto sourceName = GetWeakSourceName(_source);
|
||||
VisibilityData vInfo = {"", _action == SceneVisibilityAction::SHOW};
|
||||
|
||||
switch (_sourceType) {
|
||||
case SceneItemSourceType::SOURCE:
|
||||
vInfo.name = sourceName;
|
||||
obs_scene_enum_items(scene, visibilitySourceEnum, &vInfo);
|
||||
case SceneItemSourceType::SOURCE: {
|
||||
auto items = _source.GetSceneItems(_scene);
|
||||
for (auto item : items) {
|
||||
obs_sceneitem_set_visible(
|
||||
item, _action == SceneVisibilityAction::SHOW);
|
||||
obs_sceneitem_release(item);
|
||||
}
|
||||
break;
|
||||
case SceneItemSourceType::SOURCE_GROUP:
|
||||
vInfo.name = _sourceGroup;
|
||||
}
|
||||
case SceneItemSourceType::SOURCE_GROUP: {
|
||||
auto s = obs_weak_source_get_source(_scene.GetScene());
|
||||
auto scene = obs_scene_from_source(s);
|
||||
VisibilityData vInfo = {_sourceGroup,
|
||||
_action == SceneVisibilityAction::SHOW};
|
||||
obs_scene_enum_items(scene, visibilitySourceTypeEnum, &vInfo);
|
||||
obs_source_release(s);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
obs_source_release(s);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -96,8 +83,7 @@ void MacroActionSceneVisibility::LogAction()
|
||||
if (_sourceType == SceneItemSourceType::SOURCE) {
|
||||
vblog(LOG_INFO,
|
||||
"performed visibility action \"%s\" for source \"%s\" on scene \"%s\"",
|
||||
it->second.c_str(),
|
||||
GetWeakSourceName(_source).c_str(),
|
||||
it->second.c_str(), _source.ToString().c_str(),
|
||||
_scene.ToString().c_str());
|
||||
} else {
|
||||
vblog(LOG_INFO,
|
||||
@@ -116,10 +102,9 @@ bool MacroActionSceneVisibility::Save(obs_data_t *obj)
|
||||
MacroAction::Save(obj);
|
||||
_scene.Save(obj);
|
||||
if (_sourceType == SceneItemSourceType::SOURCE) {
|
||||
obs_data_set_string(obj, "source",
|
||||
GetWeakSourceName(_source).c_str());
|
||||
_source.Save(obj);
|
||||
} else {
|
||||
obs_data_set_string(obj, "source", _sourceGroup.c_str());
|
||||
obs_data_set_string(obj, "sourceGroup", _sourceGroup.c_str());
|
||||
}
|
||||
obs_data_set_int(obj, "action", static_cast<int>(_action));
|
||||
obs_data_set_int(obj, "sourceType", static_cast<int>(_sourceType));
|
||||
@@ -128,13 +113,20 @@ bool MacroActionSceneVisibility::Save(obs_data_t *obj)
|
||||
|
||||
bool MacroActionSceneVisibility::Load(obs_data_t *obj)
|
||||
{
|
||||
// Convert old data format
|
||||
// TODO: Remove in future version
|
||||
if (obs_data_has_user_value(obj, "source")) {
|
||||
auto sourceName = obs_data_get_string(obj, "source");
|
||||
obs_data_set_string(obj, "sceneItem", sourceName);
|
||||
obs_data_set_string(obj, "sourceGroup", sourceName);
|
||||
}
|
||||
|
||||
MacroAction::Load(obj);
|
||||
_scene.Load(obj);
|
||||
_source.Load(obj);
|
||||
_sourceType = static_cast<SceneItemSourceType>(
|
||||
obs_data_get_int(obj, "sourceType"));
|
||||
const char *sourceName = obs_data_get_string(obj, "source");
|
||||
_source = GetWeakSourceByName(sourceName);
|
||||
_sourceGroup = sourceName;
|
||||
_sourceGroup = obs_data_get_string(obj, "sourceGroup");
|
||||
_action = static_cast<SceneVisibilityAction>(
|
||||
obs_data_get_int(obj, "action"));
|
||||
return true;
|
||||
@@ -142,8 +134,9 @@ bool MacroActionSceneVisibility::Load(obs_data_t *obj)
|
||||
|
||||
std::string MacroActionSceneVisibility::GetShortDesc()
|
||||
{
|
||||
if (_sourceType == SceneItemSourceType::SOURCE && _source) {
|
||||
return _scene.ToString() + " - " + GetWeakSourceName(_source);
|
||||
if (_sourceType == SceneItemSourceType::SOURCE &&
|
||||
!_source.ToString().empty()) {
|
||||
return _scene.ToString() + " - " + _source.ToString();
|
||||
}
|
||||
if (_sourceType == SceneItemSourceType::SOURCE_GROUP &&
|
||||
!_sourceGroup.empty()) {
|
||||
@@ -175,11 +168,12 @@ MacroActionSceneVisibilityEdit::MacroActionSceneVisibilityEdit(
|
||||
{
|
||||
_scenes = new SceneSelectionWidget(window(), false, true, true);
|
||||
_sourceTypes = new QComboBox();
|
||||
_sources = new QComboBox();
|
||||
_sources->setSizeAdjustPolicy(QComboBox::AdjustToContents);
|
||||
_sources = new SceneItemSelectionWidget(parent);
|
||||
_sourceGroups = new QComboBox();
|
||||
_actions = new QComboBox();
|
||||
|
||||
populateSourceItemTypeSelection(_sourceTypes);
|
||||
populateSourceGroupSelection(_sourceGroups);
|
||||
populateActionSelection(_actions);
|
||||
|
||||
QWidget::connect(_actions, SIGNAL(currentIndexChanged(int)), this,
|
||||
@@ -188,14 +182,19 @@ MacroActionSceneVisibilityEdit::MacroActionSceneVisibilityEdit(
|
||||
this, SLOT(SceneChanged(const SceneSelection &)));
|
||||
QWidget::connect(_sourceTypes, SIGNAL(currentIndexChanged(int)), this,
|
||||
SLOT(SourceTypeChanged(int)));
|
||||
QWidget::connect(_sources, SIGNAL(currentTextChanged(const QString &)),
|
||||
this, SLOT(SourceChanged(const QString &)));
|
||||
QWidget::connect(_scenes, SIGNAL(SceneChanged(const SceneSelection &)),
|
||||
_sources, SLOT(SceneChanged(const SceneSelection &)));
|
||||
QWidget::connect(_sources,
|
||||
SIGNAL(SceneItemChanged(const SceneItemSelection &)),
|
||||
this, SLOT(SourceChanged(const SceneItemSelection &)));
|
||||
QWidget::connect(_sourceGroups,
|
||||
SIGNAL(currentTextChanged(const QString &)), this,
|
||||
SLOT(SourceGroupChanged(const QString &)));
|
||||
|
||||
QHBoxLayout *mainLayout = new QHBoxLayout;
|
||||
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
|
||||
{"{{scenes}}", _scenes},
|
||||
{"{{sourceTypes}}", _sourceTypes},
|
||||
{"{{sources}}", _sources},
|
||||
{"{{scenes}}", _scenes}, {"{{sourceTypes}}", _sourceTypes},
|
||||
{"{{sources}}", _sources}, {"{{sourceGroups}}", _sourceGroups},
|
||||
{"{{actions}}", _actions},
|
||||
};
|
||||
placeWidgets(obs_module_text(
|
||||
@@ -218,14 +217,10 @@ void MacroActionSceneVisibilityEdit::UpdateEntryData()
|
||||
_sourceTypes->setCurrentIndex(
|
||||
static_cast<int>(_entryData->_sourceType));
|
||||
_scenes->SetScene(_entryData->_scene);
|
||||
if (_entryData->_sourceType == SceneItemSourceType::SOURCE) {
|
||||
populateSceneItemSelection(_sources, _entryData->_scene);
|
||||
_sources->setCurrentText(
|
||||
GetWeakSourceName(_entryData->_source).c_str());
|
||||
} else {
|
||||
populateSourceTypeSelection(_sources);
|
||||
_sources->setCurrentText(_entryData->_sourceGroup.c_str());
|
||||
}
|
||||
_sources->SetSceneItem((_entryData->_source));
|
||||
_sourceGroups->setCurrentText(
|
||||
QString::fromStdString(_entryData->_sourceGroup));
|
||||
SetWidgetVisibility();
|
||||
}
|
||||
|
||||
void MacroActionSceneVisibilityEdit::SceneChanged(const SceneSelection &s)
|
||||
@@ -233,13 +228,9 @@ void MacroActionSceneVisibilityEdit::SceneChanged(const SceneSelection &s)
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_scene = s;
|
||||
}
|
||||
_sources->clear();
|
||||
populateSceneItemSelection(_sources, _entryData->_scene);
|
||||
_sources->adjustSize();
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_scene = s;
|
||||
}
|
||||
|
||||
void MacroActionSceneVisibilityEdit::SourceTypeChanged(int value)
|
||||
@@ -247,35 +238,36 @@ void MacroActionSceneVisibilityEdit::SourceTypeChanged(int value)
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_sourceType =
|
||||
static_cast<SceneItemSourceType>(value);
|
||||
}
|
||||
|
||||
_sources->clear();
|
||||
if (_entryData->_sourceType == SceneItemSourceType::SOURCE) {
|
||||
populateSceneItemSelection(_sources, _entryData->_scene);
|
||||
} else {
|
||||
populateSourceTypeSelection(_sources);
|
||||
}
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_sourceType = static_cast<SceneItemSourceType>(value);
|
||||
SetWidgetVisibility();
|
||||
}
|
||||
|
||||
void MacroActionSceneVisibilityEdit::SourceChanged(const QString &text)
|
||||
void MacroActionSceneVisibilityEdit::SourceChanged(
|
||||
const SceneItemSelection &item)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
if (_entryData->_sourceType == SceneItemSourceType::SOURCE) {
|
||||
_entryData->_source = GetWeakSourceByQString(text);
|
||||
_entryData->_source = item;
|
||||
emit HeaderInfoChanged(
|
||||
QString::fromStdString(_entryData->GetShortDesc()));
|
||||
}
|
||||
|
||||
void MacroActionSceneVisibilityEdit::SourceGroupChanged(const QString &text)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
if (text == obs_module_text("AdvSceneSwitcher.selectItem")) {
|
||||
_entryData->_sourceGroup = "";
|
||||
} else {
|
||||
if (text == obs_module_text("AdvSceneSwitcher.selectItem")) {
|
||||
_entryData->_sourceGroup = "";
|
||||
} else {
|
||||
_entryData->_sourceGroup = text.toStdString();
|
||||
}
|
||||
_entryData->_sourceGroup = text.toStdString();
|
||||
}
|
||||
emit HeaderInfoChanged(
|
||||
QString::fromStdString(_entryData->GetShortDesc()));
|
||||
@@ -290,3 +282,15 @@ void MacroActionSceneVisibilityEdit::ActionChanged(int value)
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_action = static_cast<SceneVisibilityAction>(value);
|
||||
}
|
||||
|
||||
void MacroActionSceneVisibilityEdit::SetWidgetVisibility()
|
||||
{
|
||||
if (!_entryData) {
|
||||
return;
|
||||
}
|
||||
_sources->setVisible(_entryData->_sourceType ==
|
||||
SceneItemSourceType::SOURCE);
|
||||
_sourceGroups->setVisible(_entryData->_sourceType ==
|
||||
SceneItemSourceType::SOURCE_GROUP);
|
||||
adjustSize();
|
||||
}
|
||||
|
||||
110
src/macro-action-screenshot.cpp
Normal file
110
src/macro-action-screenshot.cpp
Normal file
@@ -0,0 +1,110 @@
|
||||
#include "headers/macro-action-screenshot.hpp"
|
||||
#include "headers/advanced-scene-switcher.hpp"
|
||||
#include "headers/utility.hpp"
|
||||
|
||||
const std::string MacroActionScreenshot::id = "screenshot";
|
||||
|
||||
bool MacroActionScreenshot::_registered = MacroActionFactory::Register(
|
||||
MacroActionScreenshot::id,
|
||||
{MacroActionScreenshot::Create, MacroActionScreenshotEdit::Create,
|
||||
"AdvSceneSwitcher.action.screenshot"});
|
||||
|
||||
bool MacroActionScreenshot::PerformAction()
|
||||
{
|
||||
if (_source) {
|
||||
auto s = obs_weak_source_get_source(_source);
|
||||
obs_frontend_take_source_screenshot(s);
|
||||
obs_source_release(s);
|
||||
} else {
|
||||
obs_frontend_take_screenshot();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void MacroActionScreenshot::LogAction()
|
||||
{
|
||||
vblog(LOG_INFO, "trigger screenshot for source \"%s\"",
|
||||
GetWeakSourceName(_source).c_str());
|
||||
}
|
||||
|
||||
bool MacroActionScreenshot::Save(obs_data_t *obj)
|
||||
{
|
||||
MacroAction::Save(obj);
|
||||
obs_data_set_string(obj, "source", GetWeakSourceName(_source).c_str());
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MacroActionScreenshot::Load(obs_data_t *obj)
|
||||
{
|
||||
MacroAction::Load(obj);
|
||||
const char *sourceName = obs_data_get_string(obj, "source");
|
||||
_source = GetWeakSourceByName(sourceName);
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string MacroActionScreenshot::GetShortDesc()
|
||||
{
|
||||
if (_source) {
|
||||
return GetWeakSourceName(_source);
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
void addOBSMainOutputEntry(QComboBox *cb)
|
||||
{
|
||||
cb->insertItem(
|
||||
0, obs_module_text(
|
||||
"AdvSceneSwitcher.action.screenshot.mainOutput"));
|
||||
}
|
||||
|
||||
MacroActionScreenshotEdit::MacroActionScreenshotEdit(
|
||||
QWidget *parent, std::shared_ptr<MacroActionScreenshot> entryData)
|
||||
: QWidget(parent)
|
||||
{
|
||||
_sources = new QComboBox();
|
||||
populateVideoSelection(_sources, true, false);
|
||||
addOBSMainOutputEntry(_sources);
|
||||
|
||||
QWidget::connect(_sources, SIGNAL(currentTextChanged(const QString &)),
|
||||
this, SLOT(SourceChanged(const QString &)));
|
||||
|
||||
QHBoxLayout *mainLayout = new QHBoxLayout;
|
||||
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
|
||||
{"{{sources}}", _sources},
|
||||
};
|
||||
placeWidgets(
|
||||
obs_module_text("AdvSceneSwitcher.action.screenshot.entry"),
|
||||
mainLayout, widgetPlaceholders);
|
||||
|
||||
setLayout(mainLayout);
|
||||
|
||||
_entryData = entryData;
|
||||
UpdateEntryData();
|
||||
_loading = false;
|
||||
}
|
||||
|
||||
void MacroActionScreenshotEdit::UpdateEntryData()
|
||||
{
|
||||
if (!_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (_entryData->_source) {
|
||||
_sources->setCurrentText(
|
||||
GetWeakSourceName(_entryData->_source).c_str());
|
||||
} else {
|
||||
_sources->setCurrentIndex(0);
|
||||
}
|
||||
}
|
||||
|
||||
void MacroActionScreenshotEdit::SourceChanged(const QString &text)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_source = GetWeakSourceByQString(text);
|
||||
emit HeaderInfoChanged(
|
||||
QString::fromStdString(_entryData->GetShortDesc()));
|
||||
}
|
||||
70
src/macro-action-systray.cpp
Normal file
70
src/macro-action-systray.cpp
Normal file
@@ -0,0 +1,70 @@
|
||||
#include "headers/macro-action-systray.hpp"
|
||||
#include "headers/advanced-scene-switcher.hpp"
|
||||
#include "headers/utility.hpp"
|
||||
|
||||
const std::string MacroActionSystray::id = "systray_notification";
|
||||
|
||||
bool MacroActionSystray::_registered = MacroActionFactory::Register(
|
||||
MacroActionSystray::id,
|
||||
{MacroActionSystray::Create, MacroActionSystrayEdit::Create,
|
||||
"AdvSceneSwitcher.action.systray"});
|
||||
|
||||
bool MacroActionSystray::PerformAction()
|
||||
{
|
||||
if (_msg.empty()) {
|
||||
return true;
|
||||
}
|
||||
DisplayTrayMessage(obs_module_text("AdvSceneSwitcher.pluginName"),
|
||||
QString::fromStdString(_msg));
|
||||
return true;
|
||||
}
|
||||
|
||||
void MacroActionSystray::LogAction()
|
||||
{
|
||||
vblog(LOG_INFO, "display systray message \"%s\"", _msg.c_str());
|
||||
}
|
||||
|
||||
bool MacroActionSystray::Save(obs_data_t *obj)
|
||||
{
|
||||
MacroAction::Save(obj);
|
||||
obs_data_set_string(obj, "message", _msg.c_str());
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MacroActionSystray::Load(obs_data_t *obj)
|
||||
{
|
||||
MacroAction::Load(obj);
|
||||
_msg = obs_data_get_string(obj, "message");
|
||||
return true;
|
||||
}
|
||||
|
||||
MacroActionSystrayEdit::MacroActionSystrayEdit(
|
||||
QWidget *parent, std::shared_ptr<MacroActionSystray> entryData)
|
||||
: QWidget(parent)
|
||||
{
|
||||
_msg = new QLineEdit();
|
||||
QWidget::connect(_msg, SIGNAL(editingFinished()), this,
|
||||
SLOT(MessageChanged()));
|
||||
|
||||
QHBoxLayout *mainLayout = new QHBoxLayout;
|
||||
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
|
||||
{"{{message}}", _msg},
|
||||
};
|
||||
placeWidgets(obs_module_text("AdvSceneSwitcher.action.systray.entry"),
|
||||
mainLayout, widgetPlaceholders);
|
||||
setLayout(mainLayout);
|
||||
|
||||
_entryData = entryData;
|
||||
_msg->setText(QString::fromStdString(_entryData->_msg));
|
||||
_loading = false;
|
||||
}
|
||||
|
||||
void MacroActionSystrayEdit::MessageChanged()
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_msg = _msg->text().toStdString();
|
||||
}
|
||||
@@ -13,6 +13,9 @@ bool MacroConditionAudio::_registered = MacroConditionFactory::Register(
|
||||
static std::map<AudioCondition, std::string> audioConditionTypes = {
|
||||
{AudioCondition::ABOVE, "AdvSceneSwitcher.condition.audio.state.above"},
|
||||
{AudioCondition::BELOW, "AdvSceneSwitcher.condition.audio.state.below"},
|
||||
{AudioCondition::MUTE, "AdvSceneSwitcher.condition.audio.state.mute"},
|
||||
{AudioCondition::UNMUTE,
|
||||
"AdvSceneSwitcher.condition.audio.state.unmute"},
|
||||
};
|
||||
|
||||
MacroConditionAudio::~MacroConditionAudio()
|
||||
@@ -23,19 +26,32 @@ MacroConditionAudio::~MacroConditionAudio()
|
||||
|
||||
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;
|
||||
bool ret = false;
|
||||
auto s = obs_weak_source_get_source(_audioSource);
|
||||
switch (_condition) {
|
||||
case AudioCondition::ABOVE:
|
||||
// peak will have a value from -60 db to 0 db
|
||||
ret = ((double)_peak + 60) * 1.7 > _volume;
|
||||
break;
|
||||
case AudioCondition::BELOW:
|
||||
// peak will have a value from -60 db to 0 db
|
||||
ret = ((double)_peak + 60) * 1.7 < _volume;
|
||||
break;
|
||||
case AudioCondition::MUTE:
|
||||
ret = obs_source_muted(s);
|
||||
break;
|
||||
case AudioCondition::UNMUTE:
|
||||
ret = !obs_source_muted(s);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
// Reset for next check
|
||||
_peak = -std::numeric_limits<float>::infinity();
|
||||
|
||||
return volumeThresholdreached;
|
||||
obs_source_release(s);
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool MacroConditionAudio::Save(obs_data_t *obj)
|
||||
@@ -85,13 +101,10 @@ std::string MacroConditionAudio::GetShortDesc()
|
||||
return "";
|
||||
}
|
||||
|
||||
void MacroConditionAudio::SetVolumeLevel(
|
||||
void *data, const float magnitude[MAX_AUDIO_CHANNELS],
|
||||
const float peak[MAX_AUDIO_CHANNELS],
|
||||
const float inputPeak[MAX_AUDIO_CHANNELS])
|
||||
void MacroConditionAudio::SetVolumeLevel(void *data, const float *,
|
||||
const float peak[MAX_AUDIO_CHANNELS],
|
||||
const float *)
|
||||
{
|
||||
UNUSED_PARAMETER(magnitude);
|
||||
UNUSED_PARAMETER(inputPeak);
|
||||
MacroConditionAudio *c = static_cast<MacroConditionAudio *>(data);
|
||||
|
||||
for (int i = 0; i < MAX_AUDIO_CHANNELS; i++) {
|
||||
@@ -189,6 +202,7 @@ void MacroConditionAudioEdit::SourceChanged(const QString &text)
|
||||
_entryData->_audioSource = GetWeakSourceByQString(text);
|
||||
_entryData->ResetVolmeter();
|
||||
UpdateVolmeterSource();
|
||||
SetWidgetVisibility();
|
||||
emit HeaderInfoChanged(
|
||||
QString::fromStdString(_entryData->GetShortDesc()));
|
||||
}
|
||||
@@ -211,6 +225,7 @@ void MacroConditionAudioEdit::ConditionChanged(int cond)
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_condition = static_cast<AudioCondition>(cond);
|
||||
SetWidgetVisibility();
|
||||
}
|
||||
|
||||
void MacroConditionAudioEdit::UpdateEntryData()
|
||||
@@ -224,4 +239,18 @@ void MacroConditionAudioEdit::UpdateEntryData()
|
||||
_volume->setValue(_entryData->_volume);
|
||||
_condition->setCurrentIndex(static_cast<int>(_entryData->_condition));
|
||||
UpdateVolmeterSource();
|
||||
SetWidgetVisibility();
|
||||
}
|
||||
|
||||
void MacroConditionAudioEdit::SetWidgetVisibility()
|
||||
{
|
||||
if (!_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
_volume->setVisible(_entryData->_condition == AudioCondition::ABOVE ||
|
||||
_entryData->_condition == AudioCondition::BELOW);
|
||||
_volMeter->setVisible(_entryData->_condition == AudioCondition::ABOVE ||
|
||||
_entryData->_condition == AudioCondition::BELOW);
|
||||
adjustSize();
|
||||
}
|
||||
|
||||
@@ -10,12 +10,8 @@ bool MacroConditionHotkey::_registered = MacroConditionFactory::Register(
|
||||
{MacroConditionHotkey::Create, MacroConditionHotkeyEdit::Create,
|
||||
"AdvSceneSwitcher.condition.hotkey", false});
|
||||
|
||||
static void hotkeyCB(void *data, obs_hotkey_id id, obs_hotkey_t *hotkey,
|
||||
bool pressed)
|
||||
static void hotkeyCB(void *data, obs_hotkey_id, obs_hotkey_t *, bool pressed)
|
||||
{
|
||||
UNUSED_PARAMETER(id);
|
||||
UNUSED_PARAMETER(data);
|
||||
UNUSED_PARAMETER(hotkey);
|
||||
if (pressed) {
|
||||
auto c = static_cast<MacroConditionHotkey *>(data);
|
||||
c->SetPressed();
|
||||
|
||||
@@ -282,9 +282,8 @@ void MacroConditionMacroEdit::ConditionChanged(int cond)
|
||||
_entryData->_counterCondition = static_cast<CounterCondition>(cond);
|
||||
}
|
||||
|
||||
void MacroConditionMacroEdit::MacroRemove(const QString &name)
|
||||
void MacroConditionMacroEdit::MacroRemove(const QString &)
|
||||
{
|
||||
UNUSED_PARAMETER(name);
|
||||
if (_entryData) {
|
||||
_entryData->_macro.UpdateRef();
|
||||
}
|
||||
|
||||
@@ -80,18 +80,19 @@ bool matchTime(const int64_t currentTime, const int64_t duration,
|
||||
matchedTimeRemainLonger || matchedTimeRemainShorter;
|
||||
}
|
||||
|
||||
bool MacroConditionMedia::CheckCondition()
|
||||
bool MacroConditionMedia::CheckMediaMatch()
|
||||
{
|
||||
|
||||
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);
|
||||
obs_source_t *s = obs_weak_source_get_source(_source);
|
||||
auto duration = obs_source_media_get_duration(s);
|
||||
auto time = obs_source_media_get_time(s);
|
||||
obs_media_state currentState = obs_source_media_get_state(s);
|
||||
obs_source_release(s);
|
||||
|
||||
// To be able to compare to obs_media_state more easily
|
||||
int expectedState = static_cast<int>(_state);
|
||||
@@ -150,49 +151,149 @@ bool MacroConditionMedia::CheckCondition()
|
||||
matchTime(time, duration, _restriction, _time.seconds * 1000);
|
||||
bool matched = matchedState && matchedTime;
|
||||
|
||||
if (matched && !_alreadyMatched) {
|
||||
if (matched && !(_onlyMatchonChagne && _alreadyMatched)) {
|
||||
match = true;
|
||||
}
|
||||
|
||||
_alreadyMatched = matched;
|
||||
|
||||
return match;
|
||||
}
|
||||
|
||||
bool MacroConditionMedia::CheckCondition()
|
||||
{
|
||||
bool match = false;
|
||||
switch (_sourceType) {
|
||||
case MediaSourceType::ANY:
|
||||
for (auto &source : _sources) {
|
||||
match = match || source.CheckCondition();
|
||||
}
|
||||
break;
|
||||
case MediaSourceType::ALL: {
|
||||
bool res = true;
|
||||
for (auto &source : _sources) {
|
||||
res = res && source.CheckCondition();
|
||||
}
|
||||
match = res;
|
||||
break;
|
||||
}
|
||||
case MediaSourceType::SOURCE:
|
||||
match = CheckMediaMatch();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return match;
|
||||
}
|
||||
|
||||
bool MacroConditionMedia::Save(obs_data_t *obj)
|
||||
{
|
||||
MacroCondition::Save(obj);
|
||||
obs_data_set_string(obj, "source", GetWeakSourceName(_source).c_str());
|
||||
|
||||
_scene.Save(obj);
|
||||
obs_data_set_int(obj, "sourceType", static_cast<int>(_sourceType));
|
||||
obs_data_set_int(obj, "state", static_cast<int>(_state));
|
||||
obs_data_set_int(obj, "restriction", static_cast<int>(_restriction));
|
||||
_time.Save(obj);
|
||||
obs_data_set_bool(obj, "matchOnChagne", _onlyMatchonChagne);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool enumSceneItem(obs_scene_t *, obs_sceneitem_t *item, void *ptr)
|
||||
{
|
||||
auto *sources = reinterpret_cast<std::vector<OBSWeakSource> *>(ptr);
|
||||
|
||||
if (obs_sceneitem_is_group(item)) {
|
||||
obs_scene_t *scene = obs_sceneitem_group_get_scene(item);
|
||||
obs_scene_enum_items(scene, enumSceneItem, ptr);
|
||||
}
|
||||
|
||||
auto source = obs_sceneitem_get_source(item);
|
||||
std::string sourceId = obs_source_get_id(source);
|
||||
|
||||
if (sourceId.compare("ffmpeg_source") == 0 ||
|
||||
sourceId.compare("vlc_source") == 0) {
|
||||
auto ws = obs_source_get_weak_source(source);
|
||||
obs_weak_source_release(ws);
|
||||
sources->emplace_back(ws);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void forMediaSourceOnSceneAddMediaCondition(
|
||||
OBSWeakSource sceneWeakSource, MacroConditionMedia *origCond,
|
||||
std::vector<MacroConditionMedia> &conditions)
|
||||
{
|
||||
conditions.clear();
|
||||
std::vector<OBSWeakSource> mediaSources;
|
||||
auto s = obs_weak_source_get_source(sceneWeakSource);
|
||||
auto scene = obs_scene_from_source(s);
|
||||
obs_scene_enum_items(scene, enumSceneItem, &mediaSources);
|
||||
obs_source_release(s);
|
||||
|
||||
for (auto &source : mediaSources) {
|
||||
MacroConditionMedia cond(*origCond);
|
||||
cond._sourceType = MediaSourceType::SOURCE;
|
||||
cond._source = source;
|
||||
conditions.push_back(cond);
|
||||
}
|
||||
}
|
||||
|
||||
bool MacroConditionMedia::Load(obs_data_t *obj)
|
||||
{
|
||||
MacroCondition::Load(obj);
|
||||
const char *sourceName = obs_data_get_string(obj, "source");
|
||||
_source = GetWeakSourceByName(sourceName);
|
||||
|
||||
_scene.Load(obj);
|
||||
_sourceType = static_cast<MediaSourceType>(
|
||||
obs_data_get_int(obj, "sourceType"));
|
||||
_state = static_cast<MediaState>(obs_data_get_int(obj, "state"));
|
||||
_restriction = static_cast<MediaTimeRestriction>(
|
||||
obs_data_get_int(obj, "restriction"));
|
||||
_time.Load(obj);
|
||||
if (!obs_data_has_user_value(obj, "matchOnChagne")) {
|
||||
_onlyMatchonChagne = true;
|
||||
} else {
|
||||
_onlyMatchonChagne = obs_data_get_bool(obj, "matchOnChagne");
|
||||
}
|
||||
|
||||
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);
|
||||
if (_sourceType == MediaSourceType::SOURCE) {
|
||||
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);
|
||||
}
|
||||
|
||||
forMediaSourceOnSceneAddMediaCondition(_scene.GetScene(), this,
|
||||
_sources);
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string MacroConditionMedia::GetShortDesc()
|
||||
{
|
||||
if (_source) {
|
||||
return GetWeakSourceName(_source);
|
||||
switch (_sourceType) {
|
||||
case MediaSourceType::SOURCE:
|
||||
if (_source) {
|
||||
return GetWeakSourceName(_source);
|
||||
}
|
||||
break;
|
||||
case MediaSourceType::ANY:
|
||||
if (_scene.GetScene()) {
|
||||
return obs_module_text(
|
||||
"AdvSceneSwitcher.condition.media.anyOnScene") +
|
||||
std::string(" ") + _scene.ToString();
|
||||
}
|
||||
break;
|
||||
case MediaSourceType::ALL:
|
||||
if (_scene.GetScene()) {
|
||||
return obs_module_text(
|
||||
"AdvSceneSwitcher.condition.media.allOnScene") +
|
||||
std::string(" ") + _scene.ToString();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return "";
|
||||
}
|
||||
@@ -243,18 +344,33 @@ static void populateMediaStates(QComboBox *list)
|
||||
}
|
||||
}
|
||||
|
||||
static void addAnyAndAllStates(QComboBox *list)
|
||||
{
|
||||
list->insertItem(
|
||||
1,
|
||||
obs_module_text("AdvSceneSwitcher.condition.media.anyOnScene"));
|
||||
list->insertItem(
|
||||
1,
|
||||
obs_module_text("AdvSceneSwitcher.condition.media.allOnScene"));
|
||||
}
|
||||
|
||||
MacroConditionMediaEdit::MacroConditionMediaEdit(
|
||||
QWidget *parent, std::shared_ptr<MacroConditionMedia> entryData)
|
||||
: QWidget(parent)
|
||||
{
|
||||
_mediaSources = new QComboBox();
|
||||
_scenes = new SceneSelectionWidget(window());
|
||||
_states = new QComboBox();
|
||||
_timeRestrictions = new QComboBox();
|
||||
_time = new DurationSelection();
|
||||
_onChange = new QCheckBox(obs_module_text(
|
||||
"AdvSceneSwitcher.condition.media.matchOnChange"));
|
||||
|
||||
QWidget::connect(_mediaSources,
|
||||
SIGNAL(currentTextChanged(const QString &)), this,
|
||||
SLOT(SourceChanged(const QString &)));
|
||||
QWidget::connect(_scenes, SIGNAL(SceneChanged(const SceneSelection &)),
|
||||
this, SLOT(SceneChanged(const SceneSelection &)));
|
||||
QWidget::connect(_states, SIGNAL(currentIndexChanged(int)), this,
|
||||
SLOT(StateChanged(int)));
|
||||
QWidget::connect(_timeRestrictions, SIGNAL(currentIndexChanged(int)),
|
||||
@@ -263,20 +379,27 @@ MacroConditionMediaEdit::MacroConditionMediaEdit(
|
||||
SLOT(TimeChanged(double)));
|
||||
QWidget::connect(_time, SIGNAL(UnitChanged(DurationUnit)), this,
|
||||
SLOT(TimeUnitChanged(DurationUnit)));
|
||||
QWidget::connect(_onChange, SIGNAL(stateChanged(int)), this,
|
||||
SLOT(OnChangeChanged(int)));
|
||||
|
||||
populateMediaSelection(_mediaSources);
|
||||
addAnyAndAllStates(_mediaSources);
|
||||
populateMediaStates(_states);
|
||||
populateMediaTimeRestrictions(_timeRestrictions);
|
||||
|
||||
QHBoxLayout *mainLayout = new QHBoxLayout;
|
||||
QHBoxLayout *entryLayout = new QHBoxLayout;
|
||||
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
|
||||
{"{{mediaSources}}", _mediaSources},
|
||||
{"{{scenes}}", _scenes},
|
||||
{"{{states}}", _states},
|
||||
{"{{timeRestrictions}}", _timeRestrictions},
|
||||
{"{{time}}", _time},
|
||||
};
|
||||
placeWidgets(obs_module_text("AdvSceneSwitcher.condition.media.entry"),
|
||||
mainLayout, widgetPlaceholders);
|
||||
entryLayout, widgetPlaceholders);
|
||||
QVBoxLayout *mainLayout = new QVBoxLayout;
|
||||
mainLayout->addLayout(entryLayout);
|
||||
mainLayout->addWidget(_onChange);
|
||||
setLayout(mainLayout);
|
||||
|
||||
_entryData = entryData;
|
||||
@@ -291,11 +414,41 @@ void MacroConditionMediaEdit::SourceChanged(const QString &text)
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
|
||||
if (text ==
|
||||
obs_module_text("AdvSceneSwitcher.condition.media.anyOnScene")) {
|
||||
_entryData->_sourceType = MediaSourceType::ANY;
|
||||
} else if (text ==
|
||||
obs_module_text(
|
||||
"AdvSceneSwitcher.condition.media.allOnScene")) {
|
||||
_entryData->_sourceType = MediaSourceType::ALL;
|
||||
} else {
|
||||
_entryData->_sources.clear();
|
||||
_entryData->_sourceType = MediaSourceType::SOURCE;
|
||||
}
|
||||
|
||||
_entryData->ClearSignalHandler();
|
||||
_entryData->_source = GetWeakSourceByQString(text);
|
||||
_entryData->ResetSignalHandler();
|
||||
emit HeaderInfoChanged(
|
||||
QString::fromStdString(_entryData->GetShortDesc()));
|
||||
|
||||
SetWidgetVisibility();
|
||||
}
|
||||
|
||||
void MacroConditionMediaEdit::SceneChanged(const SceneSelection &s)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_scene = s;
|
||||
forMediaSourceOnSceneAddMediaCondition(_entryData->_scene.GetScene(),
|
||||
_entryData.get(),
|
||||
_entryData->_sources);
|
||||
emit HeaderInfoChanged(
|
||||
QString::fromStdString(_entryData->GetShortDesc()));
|
||||
}
|
||||
|
||||
MediaState getMediaStateFromIdx(int idx)
|
||||
@@ -318,6 +471,11 @@ void MacroConditionMediaEdit::StateChanged(int index)
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_state = getMediaStateFromIdx(index);
|
||||
if (_entryData->_sourceType != MediaSourceType::SOURCE) {
|
||||
forMediaSourceOnSceneAddMediaCondition(
|
||||
_entryData->_scene.GetScene(), _entryData.get(),
|
||||
_entryData->_sources);
|
||||
}
|
||||
}
|
||||
|
||||
void MacroConditionMediaEdit::TimeRestrictionChanged(int index)
|
||||
@@ -335,6 +493,11 @@ void MacroConditionMediaEdit::TimeRestrictionChanged(int index)
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_restriction = static_cast<MediaTimeRestriction>(index);
|
||||
if (_entryData->_sourceType != MediaSourceType::SOURCE) {
|
||||
forMediaSourceOnSceneAddMediaCondition(
|
||||
_entryData->_scene.GetScene(), _entryData.get(),
|
||||
_entryData->_sources);
|
||||
}
|
||||
}
|
||||
|
||||
void MacroConditionMediaEdit::TimeChanged(double seconds)
|
||||
@@ -345,6 +508,11 @@ void MacroConditionMediaEdit::TimeChanged(double seconds)
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_time.seconds = seconds;
|
||||
if (_entryData->_sourceType != MediaSourceType::SOURCE) {
|
||||
forMediaSourceOnSceneAddMediaCondition(
|
||||
_entryData->_scene.GetScene(), _entryData.get(),
|
||||
_entryData->_sources);
|
||||
}
|
||||
}
|
||||
|
||||
void MacroConditionMediaEdit::TimeUnitChanged(DurationUnit unit)
|
||||
@@ -355,6 +523,31 @@ void MacroConditionMediaEdit::TimeUnitChanged(DurationUnit unit)
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_time.displayUnit = unit;
|
||||
if (_entryData->_sourceType != MediaSourceType::SOURCE) {
|
||||
forMediaSourceOnSceneAddMediaCondition(
|
||||
_entryData->_scene.GetScene(), _entryData.get(),
|
||||
_entryData->_sources);
|
||||
}
|
||||
}
|
||||
|
||||
void MacroConditionMediaEdit::OnChangeChanged(int value)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_onlyMatchonChagne = value;
|
||||
if (_entryData->_sourceType != MediaSourceType::SOURCE) {
|
||||
forMediaSourceOnSceneAddMediaCondition(
|
||||
_entryData->_scene.GetScene(), _entryData.get(),
|
||||
_entryData->_sources);
|
||||
}
|
||||
}
|
||||
|
||||
void MacroConditionMediaEdit::SetWidgetVisibility()
|
||||
{
|
||||
_scenes->setVisible(_entryData->_sourceType != MediaSourceType::SOURCE);
|
||||
}
|
||||
|
||||
int getIdxFromMediaState(MediaState state)
|
||||
@@ -373,8 +566,24 @@ void MacroConditionMediaEdit::UpdateEntryData()
|
||||
return;
|
||||
}
|
||||
|
||||
_mediaSources->setCurrentText(
|
||||
GetWeakSourceName(_entryData->_source).c_str());
|
||||
switch (_entryData->_sourceType) {
|
||||
case MediaSourceType::ANY:
|
||||
_mediaSources->setCurrentText(obs_module_text(
|
||||
"AdvSceneSwitcher.condition.media.anyOnScene"));
|
||||
break;
|
||||
case MediaSourceType::ALL:
|
||||
_mediaSources->setCurrentText(obs_module_text(
|
||||
"AdvSceneSwitcher.condition.media.allOnScene"));
|
||||
break;
|
||||
case MediaSourceType::SOURCE:
|
||||
_mediaSources->setCurrentText(
|
||||
GetWeakSourceName(_entryData->_source).c_str());
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
_scenes->SetScene(_entryData->_scene);
|
||||
_states->setCurrentIndex(getIdxFromMediaState(_entryData->_state));
|
||||
_timeRestrictions->setCurrentIndex(
|
||||
static_cast<int>(_entryData->_restriction));
|
||||
@@ -383,4 +592,6 @@ void MacroConditionMediaEdit::UpdateEntryData()
|
||||
MediaTimeRestriction::TIME_RESTRICTION_NONE) {
|
||||
_time->setDisabled(true);
|
||||
}
|
||||
_onChange->setChecked(_entryData->_onlyMatchonChagne);
|
||||
SetWidgetVisibility();
|
||||
}
|
||||
|
||||
@@ -9,16 +9,26 @@ bool MacroConditionPluginState::_registered = MacroConditionFactory::Register(
|
||||
MacroConditionPluginState::id,
|
||||
{MacroConditionPluginState::Create,
|
||||
MacroConditionPluginStateEdit::Create,
|
||||
"AdvSceneSwitcher.condition.pluginState", false});
|
||||
"AdvSceneSwitcher.condition.pluginState"});
|
||||
|
||||
static std::map<PluginStateCondition, std::string> pluginStateConditionTypes = {
|
||||
{PluginStateCondition::SCENESWITCHED,
|
||||
"AdvSceneSwitcher.condition.pluginState.state.sceneSwitched"},
|
||||
{PluginStateCondition::RUNNING,
|
||||
"AdvSceneSwitcher.condition.pluginState.state.running"},
|
||||
};
|
||||
|
||||
bool MacroConditionPluginState::CheckCondition()
|
||||
{
|
||||
return switcher->macroSceneSwitched;
|
||||
switch (_condition) {
|
||||
case PluginStateCondition::SCENESWITCHED:
|
||||
return switcher->macroSceneSwitched;
|
||||
case PluginStateCondition::RUNNING:
|
||||
return true;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool MacroConditionPluginState::Save(obs_data_t *obj)
|
||||
|
||||
@@ -19,78 +19,110 @@ static std::map<SceneOrderCondition, std::string> sceneOrderConditionTypes = {
|
||||
"AdvSceneSwitcher.condition.sceneOrder.type.position"},
|
||||
};
|
||||
|
||||
struct PosInfo {
|
||||
std::string name;
|
||||
std::vector<int> pos = {};
|
||||
struct PosInfo2 {
|
||||
obs_scene_item *item;
|
||||
int pos = -1;
|
||||
int curPos = 0;
|
||||
};
|
||||
|
||||
static bool getSceneItemPositions(obs_scene_t *, obs_sceneitem_t *item,
|
||||
void *ptr)
|
||||
static bool getSceneItemPositionHelper(obs_scene_t *, obs_sceneitem_t *item,
|
||||
void *ptr)
|
||||
{
|
||||
PosInfo *posInfo = reinterpret_cast<PosInfo *>(ptr);
|
||||
auto sourceName = obs_source_get_name(obs_sceneitem_get_source(item));
|
||||
if (posInfo->name == sourceName) {
|
||||
posInfo->pos.push_back(posInfo->curPos);
|
||||
PosInfo2 *posInfo = reinterpret_cast<PosInfo2 *>(ptr);
|
||||
if (posInfo->item == item) {
|
||||
posInfo->pos = posInfo->curPos;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (obs_sceneitem_is_group(item)) {
|
||||
obs_scene_t *scene = obs_sceneitem_group_get_scene(item);
|
||||
obs_scene_enum_items(scene, getSceneItemPositions, ptr);
|
||||
obs_scene_enum_items(scene, getSceneItemPositionHelper, ptr);
|
||||
}
|
||||
posInfo->curPos += 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool isAbove(std::vector<int> &v1, std::vector<int> &v2)
|
||||
PosInfo2 getSceneItemPos(obs_scene_item *item, obs_scene *scene)
|
||||
{
|
||||
for (int i : v1) {
|
||||
for (int j : v2) {
|
||||
if (i > j) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
PosInfo2 pos{item};
|
||||
obs_scene_enum_items(scene, getSceneItemPositionHelper, &pos);
|
||||
return pos;
|
||||
}
|
||||
|
||||
static bool isBelow(std::vector<int> &v1, std::vector<int> &v2)
|
||||
std::vector<int> getSceneItemPositions(std::vector<obs_scene_item *> &items,
|
||||
obs_scene *scene)
|
||||
{
|
||||
for (int i : v1) {
|
||||
for (int j : v2) {
|
||||
if (i < j) {
|
||||
return true;
|
||||
std::vector<int> positions;
|
||||
for (auto item : items) {
|
||||
auto pos = getSceneItemPos(item, scene);
|
||||
if (pos.pos != -1) {
|
||||
positions.emplace_back(pos.pos);
|
||||
}
|
||||
}
|
||||
return positions;
|
||||
}
|
||||
|
||||
static bool isAbove(std::vector<int> &pos1, std::vector<int> &pos2)
|
||||
{
|
||||
if (pos1.empty() || pos2.empty()) {
|
||||
return false;
|
||||
}
|
||||
for (int i : pos1) {
|
||||
for (int j : pos2) {
|
||||
if (i <= j) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool isBelow(std::vector<int> &pos1, std::vector<int> &pos2)
|
||||
{
|
||||
if (pos1.empty() || pos2.empty()) {
|
||||
return false;
|
||||
}
|
||||
for (int i : pos1) {
|
||||
for (int j : pos2) {
|
||||
if (i >= j) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MacroConditionSceneOrder::CheckCondition()
|
||||
{
|
||||
if (!_source) {
|
||||
auto items1 = _source.GetSceneItems(_scene);
|
||||
if (items1.empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ret = false;
|
||||
auto items2 = _source2.GetSceneItems(_scene);
|
||||
auto s = obs_weak_source_get_source(_scene.GetScene(false));
|
||||
auto scene = obs_scene_from_source(s);
|
||||
auto name1 = GetWeakSourceName(_source);
|
||||
auto name2 = GetWeakSourceName(_source2);
|
||||
PosInfo pos1 = {name1};
|
||||
PosInfo pos2 = {name2};
|
||||
obs_scene_enum_items(scene, getSceneItemPositions, &pos1);
|
||||
obs_scene_enum_items(scene, getSceneItemPositions, &pos2);
|
||||
auto positions1 = getSceneItemPositions(items1, scene);
|
||||
auto positions2 = getSceneItemPositions(items2, scene);
|
||||
|
||||
for (auto i : items1) {
|
||||
obs_sceneitem_release(i);
|
||||
}
|
||||
for (auto i : items2) {
|
||||
obs_sceneitem_release(i);
|
||||
}
|
||||
|
||||
bool ret = false;
|
||||
|
||||
switch (_condition) {
|
||||
case SceneOrderCondition::ABOVE:
|
||||
ret = isAbove(pos1.pos, pos2.pos);
|
||||
ret = isAbove(positions1, positions2);
|
||||
break;
|
||||
case SceneOrderCondition::BELOW:
|
||||
ret = isBelow(pos1.pos, pos2.pos);
|
||||
ret = isBelow(positions1, positions2);
|
||||
break;
|
||||
case SceneOrderCondition::POSITION:
|
||||
for (int p : pos1.pos) {
|
||||
for (int p : positions1) {
|
||||
if (p == _position)
|
||||
ret = true;
|
||||
}
|
||||
@@ -100,7 +132,6 @@ bool MacroConditionSceneOrder::CheckCondition()
|
||||
}
|
||||
|
||||
obs_source_release(s);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -108,9 +139,8 @@ bool MacroConditionSceneOrder::Save(obs_data_t *obj)
|
||||
{
|
||||
MacroCondition::Save(obj);
|
||||
_scene.Save(obj);
|
||||
obs_data_set_string(obj, "source", GetWeakSourceName(_source).c_str());
|
||||
obs_data_set_string(obj, "source2",
|
||||
GetWeakSourceName(_source2).c_str());
|
||||
_source.Save(obj);
|
||||
_source2.Save(obj, "sceneItem2", "sceneItemTarget2", "sceneItemIdx2");
|
||||
obs_data_set_int(obj, "condition", static_cast<int>(_condition));
|
||||
obs_data_set_int(obj, "position", _position);
|
||||
return true;
|
||||
@@ -118,12 +148,19 @@ bool MacroConditionSceneOrder::Save(obs_data_t *obj)
|
||||
|
||||
bool MacroConditionSceneOrder::Load(obs_data_t *obj)
|
||||
{
|
||||
// Convert old data format
|
||||
// TODO: Remove in future version
|
||||
if (obs_data_has_user_value(obj, "source")) {
|
||||
auto sourceName = obs_data_get_string(obj, "source");
|
||||
obs_data_set_string(obj, "sceneItem", sourceName);
|
||||
auto sourceName2 = obs_data_get_string(obj, "source2");
|
||||
obs_data_set_string(obj, "sceneItem2", sourceName2);
|
||||
}
|
||||
|
||||
MacroCondition::Load(obj);
|
||||
_scene.Load(obj);
|
||||
const char *sourceName = obs_data_get_string(obj, "source");
|
||||
_source = GetWeakSourceByName(sourceName);
|
||||
const char *source2Name = obs_data_get_string(obj, "source2");
|
||||
_source2 = GetWeakSourceByName(source2Name);
|
||||
_source.Load(obj);
|
||||
_source2.Load(obj, "sceneItem2", "sceneItemTarget2", "sceneItemIdx2");
|
||||
_condition = static_cast<SceneOrderCondition>(
|
||||
obs_data_get_int(obj, "condition"));
|
||||
_position = obs_data_get_int(obj, "position");
|
||||
@@ -132,15 +169,15 @@ bool MacroConditionSceneOrder::Load(obs_data_t *obj)
|
||||
|
||||
std::string MacroConditionSceneOrder::GetShortDesc()
|
||||
{
|
||||
if (_source) {
|
||||
std::string header =
|
||||
_scene.ToString() + " - " + GetWeakSourceName(_source);
|
||||
if (_source2 && _condition != SceneOrderCondition::POSITION) {
|
||||
header += " - " + GetWeakSourceName(_source2);
|
||||
}
|
||||
return header;
|
||||
if (_source.ToString().empty()) {
|
||||
return "";
|
||||
}
|
||||
return "";
|
||||
std::string header = _scene.ToString() + " - " + _source.ToString();
|
||||
if (!_source2.ToString().empty() &&
|
||||
_condition != SceneOrderCondition::POSITION) {
|
||||
header += " - " + _source2.ToString();
|
||||
}
|
||||
return header;
|
||||
}
|
||||
|
||||
static inline void populateConditionSelection(QComboBox *list)
|
||||
@@ -155,21 +192,37 @@ MacroConditionSceneOrderEdit::MacroConditionSceneOrderEdit(
|
||||
: QWidget(parent)
|
||||
{
|
||||
_scenes = new SceneSelectionWidget(window(), false, false, true);
|
||||
_sources = new QComboBox();
|
||||
_sources2 = new QComboBox();
|
||||
_sources = new SceneItemSelectionWidget(parent);
|
||||
_sources2 = new SceneItemSelectionWidget(parent);
|
||||
_conditions = new QComboBox();
|
||||
_position = new QSpinBox();
|
||||
_posInfo = new QLabel(obs_module_text(
|
||||
"AdvSceneSwitcher.condition.sceneOrder.positionInfo"));
|
||||
|
||||
populateConditionSelection(_conditions);
|
||||
if (entryData.get()) {
|
||||
if (entryData->_condition == SceneOrderCondition::POSITION) {
|
||||
_sources->SetShowAllSelectionType(
|
||||
SceneItemSelectionWidget::AllSelectionType::ANY);
|
||||
} else {
|
||||
_sources->SetShowAllSelectionType(
|
||||
SceneItemSelectionWidget::AllSelectionType::ALL);
|
||||
}
|
||||
}
|
||||
|
||||
QWidget::connect(_scenes, SIGNAL(SceneChanged(const SceneSelection &)),
|
||||
this, SLOT(SceneChanged(const SceneSelection &)));
|
||||
QWidget::connect(_sources, SIGNAL(currentTextChanged(const QString &)),
|
||||
this, SLOT(SourceChanged(const QString &)));
|
||||
QWidget::connect(_sources2, SIGNAL(currentTextChanged(const QString &)),
|
||||
this, SLOT(Source2Changed(const QString &)));
|
||||
QWidget::connect(_scenes, SIGNAL(SceneChanged(const SceneSelection &)),
|
||||
_sources, SLOT(SceneChanged(const SceneSelection &)));
|
||||
QWidget::connect(_scenes, SIGNAL(SceneChanged(const SceneSelection &)),
|
||||
_sources2, SLOT(SceneChanged(const SceneSelection &)));
|
||||
QWidget::connect(_sources,
|
||||
SIGNAL(SceneItemChanged(const SceneItemSelection &)),
|
||||
this, SLOT(SourceChanged(const SceneItemSelection &)));
|
||||
QWidget::connect(_sources2,
|
||||
SIGNAL(SceneItemChanged(const SceneItemSelection &)),
|
||||
this,
|
||||
SLOT(Source2Changed(const SceneItemSelection &)));
|
||||
QWidget::connect(_conditions, SIGNAL(currentIndexChanged(int)), this,
|
||||
SLOT(ConditionChanged(int)));
|
||||
QWidget::connect(_position, SIGNAL(valueChanged(int)), this,
|
||||
@@ -199,36 +252,31 @@ void MacroConditionSceneOrderEdit::SceneChanged(const SceneSelection &s)
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_scene = s;
|
||||
}
|
||||
_sources->clear();
|
||||
_sources2->clear();
|
||||
populateSceneItemSelection(_sources, _entryData->_scene);
|
||||
populateSceneItemSelection(_sources2, _entryData->_scene);
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_scene = s;
|
||||
}
|
||||
|
||||
void MacroConditionSceneOrderEdit::SourceChanged(const QString &text)
|
||||
void MacroConditionSceneOrderEdit::SourceChanged(const SceneItemSelection &item)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_source = GetWeakSourceByQString(text);
|
||||
_entryData->_source = item;
|
||||
emit HeaderInfoChanged(
|
||||
QString::fromStdString(_entryData->GetShortDesc()));
|
||||
}
|
||||
|
||||
void MacroConditionSceneOrderEdit::Source2Changed(const QString &text)
|
||||
void MacroConditionSceneOrderEdit::Source2Changed(const SceneItemSelection &item)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_source2 = GetWeakSourceByQString(text);
|
||||
_entryData->_source2 = item;
|
||||
emit HeaderInfoChanged(
|
||||
QString::fromStdString(_entryData->GetShortDesc()));
|
||||
}
|
||||
@@ -238,11 +286,21 @@ void MacroConditionSceneOrderEdit::ConditionChanged(int index)
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_condition = static_cast<SceneOrderCondition>(index);
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_condition =
|
||||
static_cast<SceneOrderCondition>(index);
|
||||
}
|
||||
SetWidgetVisibility(_entryData->_condition ==
|
||||
SceneOrderCondition::POSITION);
|
||||
if (_entryData->_condition == SceneOrderCondition::POSITION) {
|
||||
_sources->SetShowAllSelectionType(
|
||||
SceneItemSelectionWidget::AllSelectionType::ANY);
|
||||
} else {
|
||||
_sources->SetShowAllSelectionType(
|
||||
SceneItemSelectionWidget::AllSelectionType::ALL);
|
||||
}
|
||||
|
||||
emit HeaderInfoChanged(
|
||||
QString::fromStdString(_entryData->GetShortDesc()));
|
||||
}
|
||||
@@ -272,12 +330,9 @@ void MacroConditionSceneOrderEdit::UpdateEntryData()
|
||||
}
|
||||
|
||||
_scenes->SetScene(_entryData->_scene);
|
||||
populateSceneItemSelection(_sources, _entryData->_scene);
|
||||
populateSceneItemSelection(_sources2, _entryData->_scene);
|
||||
_sources->setCurrentText(
|
||||
GetWeakSourceName(_entryData->_source).c_str());
|
||||
_sources2->setCurrentText(
|
||||
GetWeakSourceName(_entryData->_source2).c_str());
|
||||
_sources->SetSceneItem(_entryData->_source);
|
||||
_sources2->SetSceneItem(_entryData->_source2);
|
||||
_position->setValue(_entryData->_position);
|
||||
_conditions->setCurrentIndex(static_cast<int>(_entryData->_condition));
|
||||
SetWidgetVisibility(_entryData->_condition ==
|
||||
SceneOrderCondition::POSITION);
|
||||
|
||||
@@ -15,10 +15,7 @@ bool MacroConditionSceneTransform::_registered =
|
||||
bool MacroConditionSceneTransform::CheckCondition()
|
||||
{
|
||||
bool ret = false;
|
||||
auto s = obs_weak_source_get_source(_scene.GetScene(false));
|
||||
auto scene = obs_scene_from_source(s);
|
||||
auto name = GetWeakSourceName(_source);
|
||||
auto items = getSceneItemsWithName(scene, name);
|
||||
auto items = _source.GetSceneItems(_scene);
|
||||
|
||||
for (auto &item : items) {
|
||||
auto json = getSceneItemTransform(item);
|
||||
@@ -27,8 +24,6 @@ bool MacroConditionSceneTransform::CheckCondition()
|
||||
}
|
||||
obs_sceneitem_release(item);
|
||||
}
|
||||
|
||||
obs_source_release(s);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -36,7 +31,7 @@ bool MacroConditionSceneTransform::Save(obs_data_t *obj)
|
||||
{
|
||||
MacroCondition::Save(obj);
|
||||
_scene.Save(obj);
|
||||
obs_data_set_string(obj, "source", GetWeakSourceName(_source).c_str());
|
||||
_source.Save(obj);
|
||||
obs_data_set_string(obj, "settings", _settings.c_str());
|
||||
obs_data_set_bool(obj, "regex", _regex);
|
||||
return true;
|
||||
@@ -44,10 +39,16 @@ bool MacroConditionSceneTransform::Save(obs_data_t *obj)
|
||||
|
||||
bool MacroConditionSceneTransform::Load(obs_data_t *obj)
|
||||
{
|
||||
// Convert old data format
|
||||
// TODO: Remove in future version
|
||||
if (obs_data_has_user_value(obj, "source")) {
|
||||
auto sourceName = obs_data_get_string(obj, "source");
|
||||
obs_data_set_string(obj, "sceneItem", sourceName);
|
||||
}
|
||||
|
||||
MacroCondition::Load(obj);
|
||||
_scene.Load(obj);
|
||||
const char *sourceName = obs_data_get_string(obj, "source");
|
||||
_source = GetWeakSourceByName(sourceName);
|
||||
_source.Load(obj);
|
||||
_settings = obs_data_get_string(obj, "settings");
|
||||
_regex = obs_data_get_bool(obj, "regex");
|
||||
return true;
|
||||
@@ -55,10 +56,11 @@ bool MacroConditionSceneTransform::Load(obs_data_t *obj)
|
||||
|
||||
std::string MacroConditionSceneTransform::GetShortDesc()
|
||||
{
|
||||
if (_source) {
|
||||
return _scene.ToString() + " - " + GetWeakSourceName(_source);
|
||||
if (_source.ToString().empty()) {
|
||||
return "";
|
||||
}
|
||||
return "";
|
||||
|
||||
return _scene.ToString() + " - " + _source.ToString();
|
||||
}
|
||||
|
||||
MacroConditionSceneTransformEdit::MacroConditionSceneTransformEdit(
|
||||
@@ -67,7 +69,8 @@ MacroConditionSceneTransformEdit::MacroConditionSceneTransformEdit(
|
||||
: QWidget(parent)
|
||||
{
|
||||
_scenes = new SceneSelectionWidget(window(), false, false, true);
|
||||
_sources = new QComboBox();
|
||||
_sources = new SceneItemSelectionWidget(
|
||||
parent, true, SceneItemSelectionWidget::AllSelectionType::ANY);
|
||||
_getSettings = new QPushButton(obs_module_text(
|
||||
"AdvSceneSwitcher.condition.sceneTransform.getTransform"));
|
||||
_settings = new QPlainTextEdit();
|
||||
@@ -76,8 +79,11 @@ MacroConditionSceneTransformEdit::MacroConditionSceneTransformEdit(
|
||||
|
||||
QWidget::connect(_scenes, SIGNAL(SceneChanged(const SceneSelection &)),
|
||||
this, SLOT(SceneChanged(const SceneSelection &)));
|
||||
QWidget::connect(_sources, SIGNAL(currentTextChanged(const QString &)),
|
||||
this, SLOT(SourceChanged(const QString &)));
|
||||
QWidget::connect(_scenes, SIGNAL(SceneChanged(const SceneSelection &)),
|
||||
_sources, SLOT(SceneChanged(const SceneSelection &)));
|
||||
QWidget::connect(_sources,
|
||||
SIGNAL(SceneItemChanged(const SceneItemSelection &)),
|
||||
this, SLOT(SourceChanged(const SceneItemSelection &)));
|
||||
QWidget::connect(_getSettings, SIGNAL(clicked()), this,
|
||||
SLOT(GetSettingsClicked()));
|
||||
QWidget::connect(_settings, SIGNAL(textChanged()), this,
|
||||
@@ -125,14 +131,9 @@ void MacroConditionSceneTransformEdit::UpdateEntryData()
|
||||
}
|
||||
|
||||
_scenes->SetScene(_entryData->_scene);
|
||||
populateSceneItemSelection(_sources, _entryData->_scene);
|
||||
_sources->setCurrentText(
|
||||
GetWeakSourceName(_entryData->_source).c_str());
|
||||
_sources->SetSceneItem(_entryData->_source);
|
||||
_regex->setChecked(_entryData->_regex);
|
||||
if (_entryData->_source) {
|
||||
_settings->setPlainText(
|
||||
QString::fromStdString(_entryData->_settings));
|
||||
}
|
||||
_settings->setPlainText(QString::fromStdString(_entryData->_settings));
|
||||
}
|
||||
|
||||
void MacroConditionSceneTransformEdit::SceneChanged(const SceneSelection &s)
|
||||
@@ -140,48 +141,45 @@ void MacroConditionSceneTransformEdit::SceneChanged(const SceneSelection &s)
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_scene = s;
|
||||
}
|
||||
_sources->clear();
|
||||
populateSceneItemSelection(_sources, _entryData->_scene);
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_scene = s;
|
||||
}
|
||||
|
||||
void MacroConditionSceneTransformEdit::SourceChanged(const QString &text)
|
||||
void MacroConditionSceneTransformEdit::SourceChanged(
|
||||
const SceneItemSelection &item)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_source = GetWeakSourceByQString(text);
|
||||
_entryData->_source = item;
|
||||
emit HeaderInfoChanged(
|
||||
QString::fromStdString(_entryData->GetShortDesc()));
|
||||
}
|
||||
|
||||
void MacroConditionSceneTransformEdit::GetSettingsClicked()
|
||||
{
|
||||
if (_loading || !_entryData || !_entryData->_scene.GetScene(false) ||
|
||||
!_entryData->_source) {
|
||||
|
||||
if (_loading || !_entryData || !_entryData->_scene.GetScene(false)) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto s = obs_weak_source_get_source(_entryData->_scene.GetScene(false));
|
||||
auto scene = obs_scene_from_source(s);
|
||||
auto name = GetWeakSourceName(_entryData->_source);
|
||||
auto item = obs_scene_find_source_recursive(scene, name.c_str());
|
||||
obs_source_release(s);
|
||||
|
||||
if (!item) {
|
||||
auto items = _entryData->_source.GetSceneItems(_entryData->_scene);
|
||||
if (items.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
QString json = formatJsonString(getSceneItemTransform(item));
|
||||
auto settings = formatJsonString(getSceneItemTransform(items[0]));
|
||||
if (_entryData->_regex) {
|
||||
json = escapeForRegex(json);
|
||||
settings = escapeForRegex(settings);
|
||||
}
|
||||
_settings->setPlainText(settings);
|
||||
|
||||
for (auto item : items) {
|
||||
obs_sceneitem_release(item);
|
||||
}
|
||||
_settings->setPlainText(json);
|
||||
}
|
||||
|
||||
void MacroConditionSceneTransformEdit::SettingsChanged()
|
||||
|
||||
@@ -22,52 +22,54 @@ static std::map<SceneVisibilityCondition, std::string>
|
||||
"AdvSceneSwitcher.condition.sceneVisibility.type.hidden"},
|
||||
};
|
||||
|
||||
struct VisibilityData {
|
||||
std::string name;
|
||||
bool visible;
|
||||
bool result;
|
||||
};
|
||||
|
||||
static bool visibilityEnum(obs_scene_t *, obs_sceneitem_t *item, void *ptr)
|
||||
bool areAllSceneItemsShown(std::vector<obs_scene_item *> &items)
|
||||
{
|
||||
VisibilityData *vInfo = reinterpret_cast<VisibilityData *>(ptr);
|
||||
auto sourceName = obs_source_get_name(obs_sceneitem_get_source(item));
|
||||
if (vInfo->name == sourceName &&
|
||||
obs_sceneitem_visible(item) == vInfo->visible) {
|
||||
vInfo->result = true;
|
||||
return false;
|
||||
bool ret = true;
|
||||
for (auto item : items) {
|
||||
if (!obs_sceneitem_visible(item)) {
|
||||
ret = false;
|
||||
}
|
||||
obs_sceneitem_release(item);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (obs_sceneitem_is_group(item)) {
|
||||
obs_scene_t *scene = obs_sceneitem_group_get_scene(item);
|
||||
obs_scene_enum_items(scene, visibilityEnum, ptr);
|
||||
bool areAllSceneItemsHidden(std::vector<obs_scene_item *> &items)
|
||||
{
|
||||
bool ret = true;
|
||||
for (auto item : items) {
|
||||
if (obs_sceneitem_visible(item)) {
|
||||
ret = false;
|
||||
}
|
||||
obs_sceneitem_release(item);
|
||||
}
|
||||
|
||||
return true;
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool MacroConditionSceneVisibility::CheckCondition()
|
||||
{
|
||||
if (!_source) {
|
||||
auto items = _source.GetSceneItems(_scene);
|
||||
if (items.empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto s = obs_weak_source_get_source(_scene.GetScene());
|
||||
auto scene = obs_scene_from_source(s);
|
||||
auto sourceName = GetWeakSourceName(_source);
|
||||
VisibilityData data = {sourceName,
|
||||
_condition == SceneVisibilityCondition::SHOWN,
|
||||
false};
|
||||
obs_scene_enum_items(scene, visibilityEnum, &data);
|
||||
obs_source_release(s);
|
||||
return data.result;
|
||||
switch (_condition) {
|
||||
case SceneVisibilityCondition::SHOWN:
|
||||
return areAllSceneItemsShown(items);
|
||||
case SceneVisibilityCondition::HIDDEN:
|
||||
return areAllSceneItemsHidden(items);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool MacroConditionSceneVisibility::Save(obs_data_t *obj)
|
||||
{
|
||||
MacroCondition::Save(obj);
|
||||
_scene.Save(obj);
|
||||
obs_data_set_string(obj, "source", GetWeakSourceName(_source).c_str());
|
||||
_source.Save(obj);
|
||||
|
||||
obs_data_set_int(obj, "condition", static_cast<int>(_condition));
|
||||
|
||||
@@ -76,10 +78,16 @@ bool MacroConditionSceneVisibility::Save(obs_data_t *obj)
|
||||
|
||||
bool MacroConditionSceneVisibility::Load(obs_data_t *obj)
|
||||
{
|
||||
// Convert old data format
|
||||
// TODO: Remove in future version
|
||||
if (obs_data_has_user_value(obj, "source")) {
|
||||
auto sourceName = obs_data_get_string(obj, "source");
|
||||
obs_data_set_string(obj, "sceneItem", sourceName);
|
||||
}
|
||||
|
||||
MacroCondition::Load(obj);
|
||||
_scene.Load(obj);
|
||||
const char *sourceName = obs_data_get_string(obj, "source");
|
||||
_source = GetWeakSourceByName(sourceName);
|
||||
_source.Load(obj);
|
||||
_condition = static_cast<SceneVisibilityCondition>(
|
||||
obs_data_get_int(obj, "condition"));
|
||||
return true;
|
||||
@@ -87,10 +95,10 @@ bool MacroConditionSceneVisibility::Load(obs_data_t *obj)
|
||||
|
||||
std::string MacroConditionSceneVisibility::GetShortDesc()
|
||||
{
|
||||
if (_source) {
|
||||
return _scene.ToString() + " - " + GetWeakSourceName(_source);
|
||||
if (_source.ToString().empty()) {
|
||||
return "";
|
||||
}
|
||||
return "";
|
||||
return _scene.ToString() + " - " + _source.ToString();
|
||||
}
|
||||
|
||||
static inline void populateConditionSelection(QComboBox *list)
|
||||
@@ -106,16 +114,18 @@ MacroConditionSceneVisibilityEdit::MacroConditionSceneVisibilityEdit(
|
||||
: QWidget(parent)
|
||||
{
|
||||
_scenes = new SceneSelectionWidget(window(), false, true, true);
|
||||
_sources = new QComboBox();
|
||||
_sources->setSizeAdjustPolicy(QComboBox::AdjustToContents);
|
||||
_sources = new SceneItemSelectionWidget(parent);
|
||||
_conditions = new QComboBox();
|
||||
|
||||
populateConditionSelection(_conditions);
|
||||
|
||||
QWidget::connect(_scenes, SIGNAL(SceneChanged(const SceneSelection &)),
|
||||
this, SLOT(SceneChanged(const SceneSelection &)));
|
||||
QWidget::connect(_sources, SIGNAL(currentTextChanged(const QString &)),
|
||||
this, SLOT(SourceChanged(const QString &)));
|
||||
QWidget::connect(_scenes, SIGNAL(SceneChanged(const SceneSelection &)),
|
||||
_sources, SLOT(SceneChanged(const SceneSelection &)));
|
||||
QWidget::connect(_sources,
|
||||
SIGNAL(SceneItemChanged(const SceneItemSelection &)),
|
||||
this, SLOT(SourceChanged(const SceneItemSelection &)));
|
||||
QWidget::connect(_conditions, SIGNAL(currentIndexChanged(int)), this,
|
||||
SLOT(ConditionChanged(int)));
|
||||
|
||||
@@ -136,14 +146,17 @@ MacroConditionSceneVisibilityEdit::MacroConditionSceneVisibilityEdit(
|
||||
_loading = false;
|
||||
}
|
||||
|
||||
void MacroConditionSceneVisibilityEdit::SourceChanged(const QString &text)
|
||||
void MacroConditionSceneVisibilityEdit::SourceChanged(
|
||||
const SceneItemSelection &item)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_source = GetWeakSourceByQString(text);
|
||||
_entryData->_source = item;
|
||||
emit HeaderInfoChanged(
|
||||
QString::fromStdString(_entryData->GetShortDesc()));
|
||||
}
|
||||
|
||||
void MacroConditionSceneVisibilityEdit::SceneChanged(const SceneSelection &s)
|
||||
@@ -151,13 +164,10 @@ void MacroConditionSceneVisibilityEdit::SceneChanged(const SceneSelection &s)
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_scene = s;
|
||||
}
|
||||
_sources->clear();
|
||||
populateSceneItemSelection(_sources, _entryData->_scene);
|
||||
_sources->adjustSize();
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_scene = s;
|
||||
emit HeaderInfoChanged(
|
||||
QString::fromStdString(_entryData->GetShortDesc()));
|
||||
}
|
||||
|
||||
void MacroConditionSceneVisibilityEdit::ConditionChanged(int index)
|
||||
@@ -178,7 +188,5 @@ void MacroConditionSceneVisibilityEdit::UpdateEntryData()
|
||||
|
||||
_conditions->setCurrentIndex(static_cast<int>(_entryData->_condition));
|
||||
_scenes->SetScene(_entryData->_scene);
|
||||
populateSceneItemSelection(_sources, _entryData->_scene);
|
||||
_sources->setCurrentText(
|
||||
GetWeakSourceName(_entryData->_source).c_str());
|
||||
_sources->SetSceneItem(_entryData->_source);
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@ bool MacroConditionScene::CheckCondition()
|
||||
|
||||
switch (_type) {
|
||||
case SceneType::CURRENT:
|
||||
if (_waitForTransition) {
|
||||
if (!_useTransitionTargetScene) {
|
||||
return switcher->currentScene == _scene.GetScene(false);
|
||||
} else {
|
||||
|
||||
@@ -58,7 +58,8 @@ bool MacroConditionScene::Save(obs_data_t *obj)
|
||||
MacroCondition::Save(obj);
|
||||
_scene.Save(obj);
|
||||
obs_data_set_int(obj, "type", static_cast<int>(_type));
|
||||
obs_data_set_bool(obj, "waitForTransition", _waitForTransition);
|
||||
obs_data_set_bool(obj, "useTransitionTargetScene",
|
||||
_useTransitionTargetScene);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -67,11 +68,12 @@ bool MacroConditionScene::Load(obs_data_t *obj)
|
||||
MacroCondition::Load(obj);
|
||||
_scene.Load(obj);
|
||||
_type = static_cast<SceneType>(obs_data_get_int(obj, "type"));
|
||||
if (!obs_data_has_user_value(obj, "waitForTransition")) {
|
||||
_waitForTransition = true;
|
||||
if (obs_data_has_user_value(obj, "waitForTransition")) {
|
||||
_useTransitionTargetScene =
|
||||
!obs_data_get_bool(obj, "waitForTransition");
|
||||
} else {
|
||||
_waitForTransition =
|
||||
obs_data_get_bool(obj, "waitForTransition");
|
||||
_useTransitionTargetScene =
|
||||
obs_data_get_bool(obj, "useTransitionTargetScene");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -94,26 +96,34 @@ MacroConditionSceneEdit::MacroConditionSceneEdit(
|
||||
{
|
||||
_scenes = new SceneSelectionWidget(window(), false, false, false);
|
||||
_sceneType = new QComboBox();
|
||||
_waitForTransition = new QCheckBox(obs_module_text(
|
||||
"AdvSceneSwitcher.condition.scene.waitForTransition"));
|
||||
_useTransitionTargetScene = new QCheckBox(obs_module_text(
|
||||
"AdvSceneSwitcher.condition.scene.currentSceneTransitionBehaviour"));
|
||||
|
||||
QWidget::connect(_scenes, SIGNAL(SceneChanged(const SceneSelection &)),
|
||||
this, SLOT(SceneChanged(const SceneSelection &)));
|
||||
QWidget::connect(_sceneType, SIGNAL(currentIndexChanged(int)), this,
|
||||
SLOT(TypeChanged(int)));
|
||||
QWidget::connect(_waitForTransition, SIGNAL(stateChanged(int)), this,
|
||||
SLOT(WaitForTransitionChanged(int)));
|
||||
QWidget::connect(_useTransitionTargetScene, SIGNAL(stateChanged(int)),
|
||||
this, SLOT(UseTransitionTargetSceneChanged(int)));
|
||||
|
||||
populateTypeSelection(_sceneType);
|
||||
|
||||
QHBoxLayout *mainLayout = new QHBoxLayout;
|
||||
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
|
||||
{"{{scenes}}", _scenes},
|
||||
{"{{sceneType}}", _sceneType},
|
||||
{"{{waitForTransition}}", _waitForTransition},
|
||||
{"{{useTransitionTargetScene}}", _useTransitionTargetScene},
|
||||
};
|
||||
placeWidgets(obs_module_text("AdvSceneSwitcher.condition.scene.entry"),
|
||||
mainLayout, widgetPlaceholders);
|
||||
QHBoxLayout *line1Layout = new QHBoxLayout;
|
||||
placeWidgets(
|
||||
obs_module_text("AdvSceneSwitcher.condition.scene.entry.line1"),
|
||||
line1Layout, widgetPlaceholders);
|
||||
QHBoxLayout *line2Layout = new QHBoxLayout;
|
||||
placeWidgets(
|
||||
obs_module_text("AdvSceneSwitcher.condition.scene.entry.line2"),
|
||||
line2Layout, widgetPlaceholders);
|
||||
QVBoxLayout *mainLayout = new QVBoxLayout;
|
||||
mainLayout->addLayout(line1Layout);
|
||||
mainLayout->addLayout(line2Layout);
|
||||
setLayout(mainLayout);
|
||||
|
||||
_entryData = entryData;
|
||||
@@ -144,21 +154,23 @@ void MacroConditionSceneEdit::TypeChanged(int value)
|
||||
SetWidgetVisibility();
|
||||
}
|
||||
|
||||
void MacroConditionSceneEdit::WaitForTransitionChanged(int state)
|
||||
void MacroConditionSceneEdit::UseTransitionTargetSceneChanged(int state)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_waitForTransition = state;
|
||||
_entryData->_useTransitionTargetScene = state;
|
||||
}
|
||||
|
||||
void MacroConditionSceneEdit::SetWidgetVisibility()
|
||||
{
|
||||
_scenes->setVisible(_entryData->_type == SceneType::CURRENT ||
|
||||
_entryData->_type == SceneType::PREVIOUS);
|
||||
_waitForTransition->setVisible(_entryData->_type == SceneType::CURRENT);
|
||||
_useTransitionTargetScene->setVisible(_entryData->_type ==
|
||||
SceneType::CURRENT);
|
||||
adjustSize();
|
||||
}
|
||||
|
||||
void MacroConditionSceneEdit::UpdateEntryData()
|
||||
@@ -169,6 +181,7 @@ void MacroConditionSceneEdit::UpdateEntryData()
|
||||
|
||||
_scenes->SetScene(_entryData->_scene);
|
||||
_sceneType->setCurrentIndex(static_cast<int>(_entryData->_type));
|
||||
_waitForTransition->setChecked(_entryData->_waitForTransition);
|
||||
_useTransitionTargetScene->setChecked(
|
||||
_entryData->_useTransitionTargetScene);
|
||||
SetWidgetVisibility();
|
||||
}
|
||||
|
||||
151
src/macro-condition-studio-mode.cpp
Normal file
151
src/macro-condition-studio-mode.cpp
Normal file
@@ -0,0 +1,151 @@
|
||||
#include "headers/macro-condition-edit.hpp"
|
||||
#include "headers/macro-condition-studio-mode.hpp"
|
||||
#include "headers/utility.hpp"
|
||||
#include "headers/advanced-scene-switcher.hpp"
|
||||
|
||||
const std::string MacroConditionStudioMode::id = "studio_mode";
|
||||
|
||||
bool MacroConditionStudioMode::_registered = MacroConditionFactory::Register(
|
||||
MacroConditionStudioMode::id,
|
||||
{MacroConditionStudioMode::Create, MacroConditionStudioModeEdit::Create,
|
||||
"AdvSceneSwitcher.condition.studioMode"});
|
||||
|
||||
static std::map<StudioModeCondition, std::string> studioModeConditionTypes = {
|
||||
{StudioModeCondition::STUDIO_MODE_ACTIVE,
|
||||
"AdvSceneSwitcher.condition.studioMode.state.active"},
|
||||
{StudioModeCondition::STUDIO_MODE_NOT_ACTIVE,
|
||||
"AdvSceneSwitcher.condition.studioMode.state.notActive"},
|
||||
{StudioModeCondition::PREVIEW_SCENE,
|
||||
"AdvSceneSwitcher.condition.studioMode.state.previewScene"},
|
||||
};
|
||||
|
||||
bool MacroConditionStudioMode::CheckCondition()
|
||||
{
|
||||
bool ret = false;
|
||||
switch (_condition) {
|
||||
case StudioModeCondition::STUDIO_MODE_ACTIVE:
|
||||
ret = obs_frontend_preview_program_mode_active();
|
||||
break;
|
||||
case StudioModeCondition::STUDIO_MODE_NOT_ACTIVE:
|
||||
ret = !obs_frontend_preview_program_mode_active();
|
||||
break;
|
||||
case StudioModeCondition::PREVIEW_SCENE: {
|
||||
auto s = obs_frontend_get_current_preview_scene();
|
||||
auto scene = obs_source_get_weak_source(s);
|
||||
ret = _scene.GetScene() == scene;
|
||||
obs_weak_source_release(scene);
|
||||
obs_source_release(s);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool MacroConditionStudioMode::Save(obs_data_t *obj)
|
||||
{
|
||||
MacroCondition::Save(obj);
|
||||
obs_data_set_int(obj, "condition", static_cast<int>(_condition));
|
||||
_scene.Save(obj);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MacroConditionStudioMode::Load(obs_data_t *obj)
|
||||
{
|
||||
MacroCondition::Load(obj);
|
||||
_condition = static_cast<StudioModeCondition>(
|
||||
obs_data_get_int(obj, "condition"));
|
||||
_scene.Load(obj);
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string MacroConditionStudioMode::GetShortDesc()
|
||||
{
|
||||
if (_condition == StudioModeCondition::PREVIEW_SCENE) {
|
||||
return _scene.ToString();
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
static inline void populateConditionSelection(QComboBox *list)
|
||||
{
|
||||
for (auto entry : studioModeConditionTypes) {
|
||||
list->addItem(obs_module_text(entry.second.c_str()));
|
||||
}
|
||||
}
|
||||
|
||||
MacroConditionStudioModeEdit::MacroConditionStudioModeEdit(
|
||||
QWidget *parent, std::shared_ptr<MacroConditionStudioMode> entryData)
|
||||
: QWidget(parent)
|
||||
{
|
||||
_condition = new QComboBox();
|
||||
_scenes = new SceneSelectionWidget(window());
|
||||
|
||||
populateConditionSelection(_condition);
|
||||
|
||||
QWidget::connect(_condition, SIGNAL(currentIndexChanged(int)), this,
|
||||
SLOT(ConditionChanged(int)));
|
||||
QWidget::connect(_scenes, SIGNAL(SceneChanged(const SceneSelection &)),
|
||||
this, SLOT(SceneChanged(const SceneSelection &)));
|
||||
|
||||
QHBoxLayout *layout = new QHBoxLayout;
|
||||
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
|
||||
{"{{conditions}}", _condition},
|
||||
{"{{scenes}}", _scenes},
|
||||
};
|
||||
placeWidgets(
|
||||
obs_module_text("AdvSceneSwitcher.condition.studioMode.entry"),
|
||||
layout, widgetPlaceholders);
|
||||
setLayout(layout);
|
||||
|
||||
_entryData = entryData;
|
||||
UpdateEntryData();
|
||||
_loading = false;
|
||||
}
|
||||
|
||||
void MacroConditionStudioModeEdit::ConditionChanged(int cond)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_condition = static_cast<StudioModeCondition>(cond);
|
||||
SetWidgetVisibility();
|
||||
emit HeaderInfoChanged(
|
||||
QString::fromStdString(_entryData->GetShortDesc()));
|
||||
}
|
||||
|
||||
void MacroConditionStudioModeEdit::SceneChanged(const SceneSelection &s)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_scene = s;
|
||||
emit HeaderInfoChanged(
|
||||
QString::fromStdString(_entryData->GetShortDesc()));
|
||||
}
|
||||
|
||||
void MacroConditionStudioModeEdit::UpdateEntryData()
|
||||
{
|
||||
if (!_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
_condition->setCurrentIndex(static_cast<int>(_entryData->_condition));
|
||||
_scenes->SetScene(_entryData->_scene);
|
||||
SetWidgetVisibility();
|
||||
}
|
||||
|
||||
void MacroConditionStudioModeEdit::SetWidgetVisibility()
|
||||
{
|
||||
if (!_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
_scenes->setVisible(_entryData->_condition ==
|
||||
StudioModeCondition::PREVIEW_SCENE);
|
||||
}
|
||||
@@ -10,6 +10,11 @@ bool MacroConditionTimer::_registered = MacroConditionFactory::Register(
|
||||
{MacroConditionTimer::Create, MacroConditionTimerEdit::Create,
|
||||
"AdvSceneSwitcher.condition.timer", false});
|
||||
|
||||
static std::map<TimerType, std::string> timerTypes = {
|
||||
{TimerType::FIXED, "AdvSceneSwitcher.condition.timer.type.fixed"},
|
||||
{TimerType::RANDOM, "AdvSceneSwitcher.condition.timer.type.random"},
|
||||
};
|
||||
|
||||
bool MacroConditionTimer::CheckCondition()
|
||||
{
|
||||
if (_paused) {
|
||||
@@ -18,16 +23,37 @@ bool MacroConditionTimer::CheckCondition()
|
||||
if (_duration.DurationReached()) {
|
||||
if (!_oneshot) {
|
||||
_duration.Reset();
|
||||
if (_type == TimerType::RANDOM) {
|
||||
SetRandomTimeRemaining();
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void MacroConditionTimer::SetRandomTimeRemaining()
|
||||
{
|
||||
double min, max;
|
||||
if (_duration.seconds <= _duration2.seconds) {
|
||||
min = _duration.seconds;
|
||||
max = _duration2.seconds;
|
||||
} else {
|
||||
min = _duration2.seconds;
|
||||
max = _duration.seconds;
|
||||
}
|
||||
std::uniform_real_distribution<double> unif(min, max);
|
||||
|
||||
double remainingTime = unif(_re);
|
||||
_duration.SetTimeRemaining(remainingTime);
|
||||
}
|
||||
|
||||
bool MacroConditionTimer::Save(obs_data_t *obj)
|
||||
{
|
||||
MacroCondition::Save(obj);
|
||||
obs_data_set_int(obj, "type", static_cast<int>(_type));
|
||||
_duration.Save(obj);
|
||||
_duration2.Save(obj, "seconds2", "displayUnit2");
|
||||
if (!_paused) {
|
||||
_remaining = _duration.TimeRemaining();
|
||||
}
|
||||
@@ -45,7 +71,9 @@ bool MacroConditionTimer::Save(obs_data_t *obj)
|
||||
bool MacroConditionTimer::Load(obs_data_t *obj)
|
||||
{
|
||||
MacroCondition::Load(obj);
|
||||
_type = static_cast<TimerType>(obs_data_get_int(obj, "type"));
|
||||
_duration.Load(obj);
|
||||
_duration2.Load(obj, "seconds2", "displayUnit2");
|
||||
_remaining = obs_data_get_double(obj, "remaining");
|
||||
_paused = obs_data_get_bool(obj, "paused");
|
||||
_saveRemaining = obs_data_get_bool(obj, "saveRemaining");
|
||||
@@ -78,13 +106,25 @@ void MacroConditionTimer::Reset()
|
||||
{
|
||||
_remaining = _duration.seconds;
|
||||
_duration.Reset();
|
||||
if (_type == TimerType::RANDOM) {
|
||||
SetRandomTimeRemaining();
|
||||
}
|
||||
}
|
||||
|
||||
static inline void populateTimerTypeSelection(QComboBox *list)
|
||||
{
|
||||
for (auto entry : timerTypes) {
|
||||
list->addItem(obs_module_text(entry.second.c_str()));
|
||||
}
|
||||
}
|
||||
|
||||
MacroConditionTimerEdit::MacroConditionTimerEdit(
|
||||
QWidget *parent, std::shared_ptr<MacroConditionTimer> entryData)
|
||||
: QWidget(parent)
|
||||
{
|
||||
_timerTypes = new QComboBox();
|
||||
_duration = new DurationSelection();
|
||||
_duration2 = new DurationSelection();
|
||||
_autoReset = new QCheckBox();
|
||||
_saveRemaining = new QCheckBox();
|
||||
_pauseConinue = new QPushButton(
|
||||
@@ -93,10 +133,18 @@ MacroConditionTimerEdit::MacroConditionTimerEdit(
|
||||
obs_module_text("AdvSceneSwitcher.condition.timer.reset"));
|
||||
_remaining = new QLabel();
|
||||
|
||||
populateTimerTypeSelection(_timerTypes);
|
||||
|
||||
QWidget::connect(_timerTypes, SIGNAL(currentIndexChanged(int)), this,
|
||||
SLOT(TimerTypeChanged(int)));
|
||||
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(_pauseConinue, SIGNAL(clicked()), this,
|
||||
SLOT(PauseContinueClicked()));
|
||||
QWidget::connect(_reset, SIGNAL(clicked()), this, SLOT(ResetClicked()));
|
||||
@@ -105,9 +153,11 @@ MacroConditionTimerEdit::MacroConditionTimerEdit(
|
||||
QWidget::connect(_saveRemaining, SIGNAL(stateChanged(int)), this,
|
||||
SLOT(SaveRemainingChanged(int)));
|
||||
|
||||
auto line1Layout = new QHBoxLayout;
|
||||
_timerLayout = new QHBoxLayout;
|
||||
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
|
||||
{"{{type}}", _timerTypes},
|
||||
{"{{duration}}", _duration},
|
||||
{"{{duration2}}", _duration2},
|
||||
{"{{autoReset}}", _autoReset},
|
||||
{"{{remaining}}", _remaining},
|
||||
{"{{pauseContinue}}", _pauseConinue},
|
||||
@@ -115,8 +165,9 @@ MacroConditionTimerEdit::MacroConditionTimerEdit(
|
||||
{"{{saveRemaining}}", _saveRemaining},
|
||||
};
|
||||
placeWidgets(
|
||||
obs_module_text("AdvSceneSwitcher.condition.timer.entry.line1"),
|
||||
line1Layout, widgetPlaceholders);
|
||||
obs_module_text(
|
||||
"AdvSceneSwitcher.condition.timer.entry.line1.fixed"),
|
||||
_timerLayout, widgetPlaceholders);
|
||||
auto line2Layout = new QHBoxLayout;
|
||||
placeWidgets(
|
||||
obs_module_text("AdvSceneSwitcher.condition.timer.entry.line2"),
|
||||
@@ -127,7 +178,7 @@ MacroConditionTimerEdit::MacroConditionTimerEdit(
|
||||
line3Layout, widgetPlaceholders);
|
||||
|
||||
auto *mainLayout = new QVBoxLayout;
|
||||
mainLayout->addLayout(line1Layout);
|
||||
mainLayout->addLayout(_timerLayout);
|
||||
mainLayout->addLayout(line2Layout);
|
||||
mainLayout->addLayout(line3Layout);
|
||||
setLayout(mainLayout);
|
||||
@@ -140,6 +191,17 @@ MacroConditionTimerEdit::MacroConditionTimerEdit(
|
||||
timer.start(1000);
|
||||
}
|
||||
|
||||
void MacroConditionTimerEdit::TimerTypeChanged(int type)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_type = static_cast<TimerType>(type);
|
||||
SetWidgetVisibility();
|
||||
}
|
||||
|
||||
void MacroConditionTimerEdit::DurationChanged(double seconds)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
@@ -160,6 +222,26 @@ void MacroConditionTimerEdit::DurationUnitChanged(DurationUnit unit)
|
||||
_entryData->_duration.displayUnit = unit;
|
||||
}
|
||||
|
||||
void MacroConditionTimerEdit::Duration2Changed(double seconds)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_duration2.seconds = seconds;
|
||||
}
|
||||
|
||||
void MacroConditionTimerEdit::Duration2UnitChanged(DurationUnit unit)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_duration2.displayUnit = unit;
|
||||
}
|
||||
|
||||
void MacroConditionTimerEdit::SaveRemainingChanged(int state)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
@@ -243,8 +325,44 @@ void MacroConditionTimerEdit::UpdateEntryData()
|
||||
return;
|
||||
}
|
||||
|
||||
_timerTypes->setCurrentIndex(static_cast<int>(_entryData->_type));
|
||||
_duration->SetDuration(_entryData->_duration);
|
||||
_duration2->SetDuration(_entryData->_duration2);
|
||||
_autoReset->setChecked(!_entryData->_oneshot);
|
||||
_saveRemaining->setChecked(_entryData->_saveRemaining);
|
||||
SetPauseContinueButtonLabel();
|
||||
SetWidgetVisibility();
|
||||
}
|
||||
|
||||
void MacroConditionTimerEdit::SetWidgetVisibility()
|
||||
{
|
||||
if (!_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
_timerLayout->removeWidget(_timerTypes);
|
||||
_timerLayout->removeWidget(_duration);
|
||||
_timerLayout->removeWidget(_duration2);
|
||||
|
||||
clearLayout(_timerLayout);
|
||||
|
||||
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
|
||||
{"{{type}}", _timerTypes},
|
||||
{"{{duration}}", _duration},
|
||||
{"{{duration2}}", _duration2},
|
||||
};
|
||||
|
||||
if (_entryData->_type == TimerType::RANDOM) {
|
||||
placeWidgets(
|
||||
obs_module_text(
|
||||
"AdvSceneSwitcher.condition.timer.entry.line1.random"),
|
||||
_timerLayout, widgetPlaceholders);
|
||||
_duration2->show();
|
||||
} else {
|
||||
placeWidgets(
|
||||
obs_module_text(
|
||||
"AdvSceneSwitcher.condition.timer.entry.line1.fixed"),
|
||||
_timerLayout, widgetPlaceholders);
|
||||
_duration2->hide();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ const std::string MacroConditionTransition::id = "transition";
|
||||
bool MacroConditionTransition::_registered = MacroConditionFactory::Register(
|
||||
MacroConditionTransition::id,
|
||||
{MacroConditionTransition::Create, MacroConditionTransitionEdit::Create,
|
||||
"AdvSceneSwitcher.condition.transition", false});
|
||||
"AdvSceneSwitcher.condition.transition"});
|
||||
|
||||
static std::map<TransitionCondition, std::string> filterConditionTypes = {
|
||||
{TransitionCondition::CURRENT,
|
||||
@@ -19,6 +19,10 @@ static std::map<TransitionCondition, std::string> filterConditionTypes = {
|
||||
"AdvSceneSwitcher.condition.transition.type.started"},
|
||||
{TransitionCondition::ENDED,
|
||||
"AdvSceneSwitcher.condition.transition.type.ended"},
|
||||
{TransitionCondition::TRANSITION_SOURCE,
|
||||
"AdvSceneSwitcher.condition.transition.type.transitionSource"},
|
||||
{TransitionCondition::TRANSITION_TARGET,
|
||||
"AdvSceneSwitcher.condition.transition.type.transitionTarget"},
|
||||
};
|
||||
|
||||
bool isCurrentTransition(OBSWeakSource &t)
|
||||
@@ -46,15 +50,26 @@ bool anySceneTransitionStarted()
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool isTargetScene(OBSWeakSource &target)
|
||||
{
|
||||
auto source = obs_frontend_get_current_scene();
|
||||
auto targetScene = obs_source_get_weak_source(source);
|
||||
bool ret = target == targetScene;
|
||||
obs_weak_source_release(targetScene);
|
||||
obs_source_release(source);
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool MacroConditionTransition::CheckCondition()
|
||||
{
|
||||
bool anyTransitionEnded = _lastTransitionEndTime !=
|
||||
switcher->lastTransitionEndTime;
|
||||
bool anyTransitionStarted = anySceneTransitionStarted();
|
||||
bool transitionStarted = false;
|
||||
bool transitionEnded = false;
|
||||
|
||||
if (_transition.GetType() == TransitionSelectionType::ANY) {
|
||||
transitionStarted = anySceneTransitionStarted();
|
||||
transitionStarted = anyTransitionStarted;
|
||||
transitionEnded = anyTransitionEnded;
|
||||
} else {
|
||||
transitionStarted = _started;
|
||||
@@ -78,6 +93,15 @@ bool MacroConditionTransition::CheckCondition()
|
||||
case TransitionCondition::ENDED:
|
||||
ret = transitionEnded;
|
||||
break;
|
||||
case TransitionCondition::TRANSITION_SOURCE:
|
||||
ret = anyTransitionStarted &&
|
||||
_scene.GetScene() == switcher->currentScene;
|
||||
break;
|
||||
case TransitionCondition::TRANSITION_TARGET: {
|
||||
auto scene = _scene.GetScene();
|
||||
ret = anyTransitionStarted && isTargetScene(scene);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -132,6 +156,7 @@ bool MacroConditionTransition::Save(obs_data_t *obj)
|
||||
MacroCondition::Save(obj);
|
||||
obs_data_set_int(obj, "condition", static_cast<int>(_condition));
|
||||
_transition.Save(obj);
|
||||
_scene.Save(obj);
|
||||
_duration.Save(obj);
|
||||
return true;
|
||||
}
|
||||
@@ -142,6 +167,7 @@ bool MacroConditionTransition::Load(obs_data_t *obj)
|
||||
_condition = static_cast<TransitionCondition>(
|
||||
obs_data_get_int(obj, "condition"));
|
||||
_transition.Load(obj);
|
||||
_scene.Load(obj);
|
||||
_duration.Load(obj);
|
||||
ConnectToTransitionSignals();
|
||||
return true;
|
||||
@@ -149,7 +175,13 @@ bool MacroConditionTransition::Load(obs_data_t *obj)
|
||||
|
||||
std::string MacroConditionTransition::GetShortDesc()
|
||||
{
|
||||
return _transition.ToString();
|
||||
if (_condition == TransitionCondition::CURRENT ||
|
||||
_condition == TransitionCondition::DURATION ||
|
||||
_condition == TransitionCondition::STARTED ||
|
||||
_condition == TransitionCondition::ENDED) {
|
||||
return _transition.ToString();
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
static inline void populateConditionSelection(QComboBox *list)
|
||||
@@ -165,6 +197,7 @@ MacroConditionTransitionEdit::MacroConditionTransitionEdit(
|
||||
{
|
||||
_conditions = new QComboBox();
|
||||
_transitions = new TransitionSelectionWidget(this, true, true);
|
||||
_scenes = new SceneSelectionWidget(this, false, true, true);
|
||||
_duration = new DurationSelection(this, false);
|
||||
_durationSuffix = new QLabel(obs_module_text(
|
||||
"AdvSceneSwitcher.condition.transition.durationSuffix"));
|
||||
@@ -177,6 +210,8 @@ MacroConditionTransitionEdit::MacroConditionTransitionEdit(
|
||||
SIGNAL(TransitionChanged(const TransitionSelection &)),
|
||||
this,
|
||||
SLOT(TransitionChanged(const TransitionSelection &)));
|
||||
QWidget::connect(_scenes, SIGNAL(SceneChanged(const SceneSelection &)),
|
||||
this, SLOT(SceneChanged(const SceneSelection &)));
|
||||
QWidget::connect(_duration, SIGNAL(DurationChanged(double)), this,
|
||||
SLOT(DurationChanged(double)));
|
||||
|
||||
@@ -184,6 +219,7 @@ MacroConditionTransitionEdit::MacroConditionTransitionEdit(
|
||||
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
|
||||
{"{{conditions}}", _conditions},
|
||||
{"{{transitions}}", _transitions},
|
||||
{"{{scenes}}", _scenes},
|
||||
{"{{duration}}", _duration},
|
||||
{"{{durationSuffix}}", _durationSuffix},
|
||||
};
|
||||
@@ -228,6 +264,16 @@ void MacroConditionTransitionEdit::TransitionChanged(
|
||||
QString::fromStdString(_entryData->GetShortDesc()));
|
||||
}
|
||||
|
||||
void MacroConditionTransitionEdit::SceneChanged(const SceneSelection &s)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_scene = s;
|
||||
}
|
||||
|
||||
void MacroConditionTransitionEdit::DurationChanged(double seconds)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
@@ -248,6 +294,10 @@ void MacroConditionTransitionEdit::SetWidgetVisibility()
|
||||
_entryData->_condition == TransitionCondition::CURRENT ||
|
||||
_entryData->_condition == TransitionCondition::STARTED ||
|
||||
_entryData->_condition == TransitionCondition::ENDED);
|
||||
_scenes->setVisible(_entryData->_condition ==
|
||||
TransitionCondition::TRANSITION_SOURCE ||
|
||||
_entryData->_condition ==
|
||||
TransitionCondition::TRANSITION_TARGET);
|
||||
_duration->setVisible(_entryData->_condition ==
|
||||
TransitionCondition::DURATION);
|
||||
_durationSuffix->setVisible(_entryData->_condition ==
|
||||
@@ -268,5 +318,6 @@ void MacroConditionTransitionEdit::UpdateEntryData()
|
||||
SetWidgetVisibility();
|
||||
_conditions->setCurrentIndex(static_cast<int>(_entryData->_condition));
|
||||
_transitions->SetTransition(_entryData->_transition);
|
||||
_scenes->SetScene(_entryData->_scene);
|
||||
_duration->SetDuration(_entryData->_duration);
|
||||
}
|
||||
|
||||
@@ -82,7 +82,7 @@ void AdvSceneSwitcher::on_macroRemove_clicked()
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
switcher->abortMacroWait = true;
|
||||
switcher->macroWaitCv.notify_one();
|
||||
switcher->macroWaitCv.notify_all();
|
||||
int idx = ui->macros->currentRow();
|
||||
QString::fromStdString(switcher->macros[idx]->Name());
|
||||
switcher->macros.erase(switcher->macros.begin() + idx);
|
||||
@@ -173,6 +173,41 @@ void AdvSceneSwitcher::on_macroName_editingFinished()
|
||||
emit MacroRenamed(oldName, newName);
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::on_runMacro_clicked()
|
||||
{
|
||||
Macro *macro = getSelectedMacro();
|
||||
if (!macro) {
|
||||
return;
|
||||
}
|
||||
|
||||
bool ret = macro->PerformAction(true, true);
|
||||
if (!ret) {
|
||||
QString err =
|
||||
obs_module_text("AdvSceneSwitcher.macroTab.runFail");
|
||||
DisplayMessage(err.arg(QString::fromStdString(macro->Name())));
|
||||
}
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::on_runMacroInParallel_stateChanged(int value)
|
||||
{
|
||||
Macro *macro = getSelectedMacro();
|
||||
if (!macro) {
|
||||
return;
|
||||
}
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
macro->SetRunInParallel(value);
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::on_runMacroOnChange_stateChanged(int value)
|
||||
{
|
||||
Macro *macro = getSelectedMacro();
|
||||
if (!macro) {
|
||||
return;
|
||||
}
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
macro->SetMatchOnChange(value);
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::PopulateMacroActions(Macro &m, uint32_t afterIdx)
|
||||
{
|
||||
auto &actions = m.Actions();
|
||||
@@ -204,7 +239,14 @@ void AdvSceneSwitcher::PopulateMacroConditions(Macro &m, uint32_t afterIdx)
|
||||
|
||||
void AdvSceneSwitcher::SetEditMacro(Macro &m)
|
||||
{
|
||||
ui->macroName->setText(m.Name().c_str());
|
||||
{
|
||||
const QSignalBlocker b1(ui->macroName);
|
||||
const QSignalBlocker b2(ui->runMacroInParallel);
|
||||
const QSignalBlocker b3(ui->runMacroOnChange);
|
||||
ui->macroName->setText(m.Name().c_str());
|
||||
ui->runMacroInParallel->setChecked(m.RunInParallel());
|
||||
ui->runMacroOnChange->setChecked(m.MatchOnChange());
|
||||
}
|
||||
clearLayout(ui->macroEditConditionLayout);
|
||||
clearLayout(ui->macroEditActionLayout);
|
||||
|
||||
@@ -347,6 +389,9 @@ void AdvSceneSwitcher::setupMacroTab()
|
||||
&AdvSceneSwitcher::ShowMacroConditionsContextMenu);
|
||||
|
||||
ui->macroEdit->setDisabled(true);
|
||||
|
||||
ui->macroPriorityWarning->setVisible(
|
||||
switcher->functionNamesByPriority[0] != macro_func);
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::ShowMacroContextMenu(const QPoint &pos)
|
||||
|
||||
157
src/macro.cpp
157
src/macro.cpp
@@ -29,6 +29,10 @@ Macro::Macro(const std::string &name)
|
||||
|
||||
Macro::~Macro()
|
||||
{
|
||||
_stop = true;
|
||||
if (_thread.joinable()) {
|
||||
_thread.join();
|
||||
}
|
||||
ClearHotkeys();
|
||||
}
|
||||
|
||||
@@ -91,9 +95,16 @@ bool Macro::CeckMatch()
|
||||
vblog(LOG_INFO, "condition %s returned %d", c->GetId().c_str(),
|
||||
cond);
|
||||
}
|
||||
|
||||
vblog(LOG_INFO, "Macro %s returned %d", _name.c_str(), _matched);
|
||||
|
||||
bool newLastMatched = _matched;
|
||||
if (_matched && _matchOnChange && _lastMatched == _matched) {
|
||||
vblog(LOG_INFO, "ignore match for Macro %s (on change)",
|
||||
_name.c_str());
|
||||
_matched = false;
|
||||
}
|
||||
_lastMatched = newLastMatched;
|
||||
|
||||
// TODO: Move back to PerformAction() once new scene collection frontend
|
||||
// events are available - see:
|
||||
// https://github.com/obsproject/obs-studio/commit/feda1aaa283e8a99f6ba1159cfe6b9c1f2934a61
|
||||
@@ -104,15 +115,23 @@ bool Macro::CeckMatch()
|
||||
return _matched;
|
||||
}
|
||||
|
||||
bool Macro::PerformAction()
|
||||
bool Macro::PerformAction(bool forceParallel, bool ignorePause)
|
||||
{
|
||||
if (!_done) {
|
||||
vblog(LOG_INFO, "macro %s already running", _name.c_str());
|
||||
return !forceParallel;
|
||||
}
|
||||
|
||||
bool ret = true;
|
||||
for (auto &a : _actions) {
|
||||
a->LogAction();
|
||||
ret = ret && a->PerformAction();
|
||||
if (!ret) {
|
||||
return false;
|
||||
_done = false;
|
||||
if (_runInParallel || forceParallel) {
|
||||
if (_thread.joinable()) {
|
||||
_thread.join();
|
||||
}
|
||||
_thread = std::thread(
|
||||
[this, ignorePause] { RunActions(ignorePause); });
|
||||
} else {
|
||||
RunActions(ret, ignorePause);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
@@ -130,6 +149,27 @@ void Macro::ResetTimers()
|
||||
}
|
||||
}
|
||||
|
||||
void Macro::RunActions(bool &retVal, bool ignorePause)
|
||||
{
|
||||
bool ret = true;
|
||||
for (auto &a : _actions) {
|
||||
a->LogAction();
|
||||
ret = ret && a->PerformAction();
|
||||
if (!ret || _stop || (_paused && !ignorePause)) {
|
||||
retVal = ret;
|
||||
_done = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
_done = true;
|
||||
}
|
||||
|
||||
void Macro::RunActions(bool ignorePause)
|
||||
{
|
||||
bool unused;
|
||||
RunActions(unused, ignorePause);
|
||||
}
|
||||
|
||||
void Macro::SetPaused(bool pause)
|
||||
{
|
||||
if (_paused && !pause) {
|
||||
@@ -160,6 +200,8 @@ bool Macro::Save(obs_data_t *obj)
|
||||
{
|
||||
obs_data_set_string(obj, "name", _name.c_str());
|
||||
obs_data_set_bool(obj, "pause", _paused);
|
||||
obs_data_set_bool(obj, "parallel", _runInParallel);
|
||||
obs_data_set_bool(obj, "onChange", _matchOnChange);
|
||||
|
||||
obs_data_array_t *pauseHotkey = obs_hotkey_save(_pauseHotkey);
|
||||
obs_data_set_array(obj, "pauseHotkey", pauseHotkey);
|
||||
@@ -167,6 +209,10 @@ bool Macro::Save(obs_data_t *obj)
|
||||
obs_data_array_t *unpauseHotkey = obs_hotkey_save(_unpauseHotkey);
|
||||
obs_data_set_array(obj, "unpauseHotkey", unpauseHotkey);
|
||||
obs_data_array_release(unpauseHotkey);
|
||||
obs_data_array_t *togglePauseHotkey =
|
||||
obs_hotkey_save(_togglePauseHotkey);
|
||||
obs_data_set_array(obj, "togglePauseHotkey", togglePauseHotkey);
|
||||
obs_data_array_release(togglePauseHotkey);
|
||||
|
||||
obs_data_array_t *conditions = obs_data_array_create();
|
||||
for (auto &c : _conditions) {
|
||||
@@ -233,15 +279,20 @@ bool Macro::Load(obs_data_t *obj)
|
||||
{
|
||||
_name = obs_data_get_string(obj, "name");
|
||||
_paused = obs_data_get_bool(obj, "pause");
|
||||
_runInParallel = obs_data_get_bool(obj, "parallel");
|
||||
_matchOnChange = obs_data_get_bool(obj, "onChange");
|
||||
|
||||
obs_data_array_t *pauseHotkey = obs_data_get_array(obj, "pauseHotkey");
|
||||
obs_hotkey_load(_pauseHotkey, pauseHotkey);
|
||||
obs_data_array_release(pauseHotkey);
|
||||
|
||||
obs_data_array_t *unpauseHotkey =
|
||||
obs_data_get_array(obj, "unpauseHotkey");
|
||||
obs_hotkey_load(_unpauseHotkey, unpauseHotkey);
|
||||
obs_data_array_release(unpauseHotkey);
|
||||
obs_data_array_t *togglePauseHotkey =
|
||||
obs_data_get_array(obj, "togglePauseHotkey");
|
||||
obs_hotkey_load(_togglePauseHotkey, togglePauseHotkey);
|
||||
obs_data_array_release(togglePauseHotkey);
|
||||
|
||||
SetHotkeysDesc();
|
||||
|
||||
@@ -331,75 +382,90 @@ bool Macro::SwitchesScene()
|
||||
return false;
|
||||
}
|
||||
|
||||
static void pauseCB(void *data, obs_hotkey_id id, obs_hotkey_t *hotkey,
|
||||
bool pressed)
|
||||
static void pauseCB(void *data, obs_hotkey_id, obs_hotkey_t *, bool pressed)
|
||||
{
|
||||
UNUSED_PARAMETER(id);
|
||||
UNUSED_PARAMETER(data);
|
||||
UNUSED_PARAMETER(hotkey);
|
||||
if (pressed) {
|
||||
auto m = static_cast<Macro *>(data);
|
||||
m->SetPaused(true);
|
||||
}
|
||||
}
|
||||
|
||||
static void unpauseCB(void *data, obs_hotkey_id id, obs_hotkey_t *hotkey,
|
||||
bool pressed)
|
||||
static void unpauseCB(void *data, obs_hotkey_id, obs_hotkey_t *, bool pressed)
|
||||
{
|
||||
UNUSED_PARAMETER(id);
|
||||
UNUSED_PARAMETER(data);
|
||||
UNUSED_PARAMETER(hotkey);
|
||||
if (pressed) {
|
||||
auto m = static_cast<Macro *>(data);
|
||||
m->SetPaused(false);
|
||||
}
|
||||
}
|
||||
|
||||
static void togglePauseCB(void *data, obs_hotkey_id, obs_hotkey_t *,
|
||||
bool pressed)
|
||||
{
|
||||
if (pressed) {
|
||||
auto m = static_cast<Macro *>(data);
|
||||
m->SetPaused(!m->Paused());
|
||||
}
|
||||
}
|
||||
|
||||
static int macroHotkeyID = 0;
|
||||
|
||||
obs_hotkey_id registerHotkeyHelper(const std::string prefix,
|
||||
const char *formatModuleText, Macro *macro,
|
||||
obs_hotkey_func func)
|
||||
{
|
||||
macroHotkeyID++;
|
||||
|
||||
std::string hotkeyName = prefix + std::to_string(macroHotkeyID);
|
||||
QString format{obs_module_text(formatModuleText)};
|
||||
QString hotkeyDesc = format.arg(QString::fromStdString(macro->Name()));
|
||||
return obs_hotkey_register_frontend(hotkeyName.c_str(),
|
||||
hotkeyDesc.toStdString().c_str(),
|
||||
func, macro);
|
||||
}
|
||||
|
||||
void Macro::SetupHotkeys()
|
||||
{
|
||||
if (_pauseHotkey != OBS_INVALID_HOTKEY_ID ||
|
||||
_unpauseHotkey != OBS_INVALID_HOTKEY_ID) {
|
||||
_unpauseHotkey != OBS_INVALID_HOTKEY_ID ||
|
||||
_togglePauseHotkey != OBS_INVALID_HOTKEY_ID) {
|
||||
ClearHotkeys();
|
||||
}
|
||||
|
||||
macroHotkeyID++;
|
||||
|
||||
std::string hotkeyName =
|
||||
"macro_pause_hotkey_" + std::to_string(macroHotkeyID);
|
||||
QString format{obs_module_text("AdvSceneSwitcher.hotkey.macro.pause")};
|
||||
QString hotkeyDesc = format.arg(QString::fromStdString(_name));
|
||||
_pauseHotkey = obs_hotkey_register_frontend(
|
||||
hotkeyName.c_str(), hotkeyDesc.toStdString().c_str(), pauseCB,
|
||||
this);
|
||||
|
||||
macroHotkeyID++;
|
||||
|
||||
hotkeyName = "macro_pause_hotkey_" + _name;
|
||||
format = {obs_module_text("AdvSceneSwitcher.hotkey.macro.unpause")};
|
||||
hotkeyDesc = format.arg(QString::fromStdString(_name));
|
||||
_unpauseHotkey = obs_hotkey_register_frontend(
|
||||
hotkeyName.c_str(), hotkeyDesc.toStdString().c_str(), unpauseCB,
|
||||
this);
|
||||
_pauseHotkey = registerHotkeyHelper(
|
||||
"macro_pause_hotkey_", "AdvSceneSwitcher.hotkey.macro.pause",
|
||||
this, pauseCB);
|
||||
_unpauseHotkey = registerHotkeyHelper(
|
||||
"macro_unpause_hotkey_",
|
||||
"AdvSceneSwitcher.hotkey.macro.unpause", this, unpauseCB);
|
||||
_togglePauseHotkey = registerHotkeyHelper(
|
||||
"macro_toggle_pause_hotkey_",
|
||||
"AdvSceneSwitcher.hotkey.macro.togglePause", this,
|
||||
togglePauseCB);
|
||||
}
|
||||
|
||||
void Macro::ClearHotkeys()
|
||||
{
|
||||
obs_hotkey_unregister(_pauseHotkey);
|
||||
obs_hotkey_unregister(_unpauseHotkey);
|
||||
obs_hotkey_unregister(_togglePauseHotkey);
|
||||
}
|
||||
|
||||
void setHotkeyDescriptionHelper(const char *formatModuleText,
|
||||
const std::string name, const obs_hotkey_id id)
|
||||
{
|
||||
QString format{obs_module_text(formatModuleText)};
|
||||
QString hotkeyDesc = format.arg(QString::fromStdString(name));
|
||||
obs_hotkey_set_description(id, hotkeyDesc.toStdString().c_str());
|
||||
}
|
||||
|
||||
void Macro::SetHotkeysDesc()
|
||||
{
|
||||
QString format{obs_module_text("AdvSceneSwitcher.hotkey.macro.pause")};
|
||||
QString hotkeyDesc = format.arg(QString::fromStdString(_name));
|
||||
obs_hotkey_set_description(_pauseHotkey,
|
||||
hotkeyDesc.toStdString().c_str());
|
||||
format = {obs_module_text("AdvSceneSwitcher.hotkey.macro.unpause")};
|
||||
hotkeyDesc = format.arg(QString::fromStdString(_name));
|
||||
obs_hotkey_set_description(_unpauseHotkey,
|
||||
hotkeyDesc.toStdString().c_str());
|
||||
setHotkeyDescriptionHelper("AdvSceneSwitcher.hotkey.macro.pause", _name,
|
||||
_pauseHotkey);
|
||||
setHotkeyDescriptionHelper("AdvSceneSwitcher.hotkey.macro.unpause",
|
||||
_name, _unpauseHotkey);
|
||||
setHotkeyDescriptionHelper("AdvSceneSwitcher.hotkey.macro.togglePause",
|
||||
_name, _togglePauseHotkey);
|
||||
}
|
||||
|
||||
bool MacroCondition::Save(obs_data_t *obj)
|
||||
@@ -519,7 +585,6 @@ bool SwitcherData::runMacros()
|
||||
if (!m->PerformAction()) {
|
||||
blog(LOG_WARNING, "abort macro: %s",
|
||||
m->Name().c_str());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
240
src/scene-item-selection.cpp
Normal file
240
src/scene-item-selection.cpp
Normal file
@@ -0,0 +1,240 @@
|
||||
#include "headers/scene-item-selection.hpp"
|
||||
|
||||
#include <obs-module.h>
|
||||
|
||||
void SceneItemSelection::Save(obs_data_t *obj, const char *name,
|
||||
const char *targetName, const char *idxName)
|
||||
{
|
||||
obs_data_set_int(obj, targetName, static_cast<int>(_target));
|
||||
if (_target == SceneItemSelection::Target::INDIVIDUAL) {
|
||||
obs_data_set_int(obj, idxName, _idx);
|
||||
} else {
|
||||
obs_data_set_int(obj, idxName, 0);
|
||||
}
|
||||
obs_data_set_string(obj, name, GetWeakSourceName(_sceneItem).c_str());
|
||||
}
|
||||
|
||||
void SceneItemSelection::Load(obs_data_t *obj, const char *name,
|
||||
const char *targetName, const char *idxName)
|
||||
{
|
||||
_target = static_cast<SceneItemSelection::Target>(
|
||||
obs_data_get_int(obj, targetName));
|
||||
_idx = obs_data_get_int(obj, idxName);
|
||||
auto sceneItemName = obs_data_get_string(obj, name);
|
||||
_sceneItem = GetWeakSourceByName(sceneItemName);
|
||||
}
|
||||
|
||||
struct ItemCountData {
|
||||
std::string name;
|
||||
int count = 0;
|
||||
};
|
||||
|
||||
static bool countSceneItem(obs_scene_t *, obs_sceneitem_t *item, void *ptr)
|
||||
{
|
||||
auto data = reinterpret_cast<ItemCountData *>(ptr);
|
||||
|
||||
if (obs_sceneitem_is_group(item)) {
|
||||
obs_scene_t *scene = obs_sceneitem_group_get_scene(item);
|
||||
obs_scene_enum_items(scene, countSceneItem, ptr);
|
||||
}
|
||||
auto name = obs_source_get_name(obs_sceneitem_get_source(item));
|
||||
if (name == data->name) {
|
||||
data->count++;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
int getCountOfSceneItemOccurance(SceneSelection &s, std::string &name,
|
||||
bool enumAllScenes = true)
|
||||
{
|
||||
ItemCountData data{name};
|
||||
if (enumAllScenes && (s.GetType() == SceneSelectionType::CURRENT ||
|
||||
s.GetType() == SceneSelectionType::PREVIOUS)) {
|
||||
auto enumScenes = [](void *param, obs_source_t *source) {
|
||||
if (!source) {
|
||||
return true;
|
||||
}
|
||||
auto data = reinterpret_cast<ItemCountData *>(param);
|
||||
auto scene = obs_scene_from_source(source);
|
||||
obs_scene_enum_items(scene, countSceneItem, data);
|
||||
return true;
|
||||
};
|
||||
obs_enum_scenes(enumScenes, &data);
|
||||
} else {
|
||||
auto source = obs_weak_source_get_source(s.GetScene(false));
|
||||
auto scene = obs_scene_from_source(source);
|
||||
obs_scene_enum_items(scene, countSceneItem, &data);
|
||||
obs_source_release(source);
|
||||
}
|
||||
return data.count;
|
||||
}
|
||||
|
||||
std::vector<obs_scene_item *>
|
||||
SceneItemSelection::GetSceneItems(SceneSelection &sceneSelection)
|
||||
{
|
||||
auto s = obs_weak_source_get_source(sceneSelection.GetScene(false));
|
||||
auto scene = obs_scene_from_source(s);
|
||||
auto name = GetWeakSourceName(_sceneItem);
|
||||
int count = getCountOfSceneItemOccurance(sceneSelection, name, false);
|
||||
auto items = getSceneItemsWithName(scene, name);
|
||||
obs_source_release(s);
|
||||
|
||||
std::vector<obs_scene_item *> ret;
|
||||
|
||||
if (_target == SceneItemSelection::Target::ALL ||
|
||||
_target == SceneItemSelection::Target::ANY) {
|
||||
ret = items;
|
||||
} else {
|
||||
// Index order starts at the bottom and increases to the top
|
||||
// As this might be confusing reverse that order internally
|
||||
int idx = count - 1 - _idx;
|
||||
|
||||
if (idx >= 0 && idx < items.size()) {
|
||||
obs_sceneitem_addref(items[idx]);
|
||||
ret.emplace_back(items[idx]);
|
||||
}
|
||||
|
||||
for (auto item : items) {
|
||||
obs_sceneitem_release(item);
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::string SceneItemSelection::ToString()
|
||||
{
|
||||
return GetWeakSourceName(_sceneItem);
|
||||
}
|
||||
|
||||
SceneItemSelectionWidget::SceneItemSelectionWidget(QWidget *parent,
|
||||
bool showAll,
|
||||
AllSelectionType type)
|
||||
: QWidget(parent), _showAll(showAll), _allType(type)
|
||||
{
|
||||
_sceneItems = new QComboBox();
|
||||
_idx = new QComboBox();
|
||||
|
||||
_sceneItems->setSizeAdjustPolicy(QComboBox::AdjustToContents);
|
||||
_idx->setSizeAdjustPolicy(QComboBox::AdjustToContents);
|
||||
|
||||
populateSceneItemSelection(_sceneItems);
|
||||
|
||||
QWidget::connect(_sceneItems,
|
||||
SIGNAL(currentTextChanged(const QString &)), this,
|
||||
SLOT(SelectionChanged(const QString &)));
|
||||
QWidget::connect(_idx, SIGNAL(currentIndexChanged(int)), this,
|
||||
SLOT(IdxChanged(int)));
|
||||
auto layout = new QHBoxLayout;
|
||||
layout->addWidget(_idx);
|
||||
layout->addWidget(_sceneItems);
|
||||
setLayout(layout);
|
||||
_idx->hide();
|
||||
}
|
||||
|
||||
void SceneItemSelectionWidget::SetSceneItem(const SceneItemSelection &item)
|
||||
{
|
||||
_sceneItems->setCurrentText(
|
||||
QString::fromStdString(GetWeakSourceName(item._sceneItem)));
|
||||
if (item._target == SceneItemSelection::Target::ALL) {
|
||||
_allType = AllSelectionType::ALL;
|
||||
_idx->setCurrentIndex(0);
|
||||
} else if (item._target == SceneItemSelection::Target::ANY) {
|
||||
_allType = AllSelectionType::ANY;
|
||||
_idx->setCurrentIndex(0);
|
||||
} else {
|
||||
int idx = item._idx;
|
||||
if (_showAll) {
|
||||
idx += 1;
|
||||
}
|
||||
_idx->setCurrentIndex(idx);
|
||||
}
|
||||
}
|
||||
|
||||
void SceneItemSelectionWidget::SetScene(const SceneSelection &s)
|
||||
{
|
||||
_scene = s;
|
||||
_sceneItems->clear();
|
||||
_idx->hide();
|
||||
populateSceneItemSelection(_sceneItems, _scene);
|
||||
}
|
||||
|
||||
void SceneItemSelectionWidget::SetShowAll(bool value)
|
||||
{
|
||||
_showAll = value;
|
||||
}
|
||||
|
||||
void SceneItemSelectionWidget::SetShowAllSelectionType(AllSelectionType t)
|
||||
{
|
||||
_allType = t;
|
||||
_sceneItems->setCurrentIndex(0);
|
||||
}
|
||||
|
||||
void SceneItemSelectionWidget::SceneChanged(const SceneSelection &s)
|
||||
{
|
||||
SetScene(s);
|
||||
adjustSize();
|
||||
}
|
||||
|
||||
void SceneItemSelectionWidget::SelectionChanged(const QString &name)
|
||||
{
|
||||
SceneItemSelection s;
|
||||
_sceneItem = GetWeakSourceByQString(name);
|
||||
s._sceneItem = _sceneItem;
|
||||
if (_allType == AllSelectionType::ALL) {
|
||||
s._target = SceneItemSelection::Target::ALL;
|
||||
} else {
|
||||
s._target = SceneItemSelection::Target::ANY;
|
||||
}
|
||||
auto stdName = name.toStdString();
|
||||
int sceneItemCount = getCountOfSceneItemOccurance(_scene, stdName);
|
||||
if (sceneItemCount > 1) {
|
||||
_idx->show();
|
||||
SetupIdxSelection(sceneItemCount);
|
||||
} else {
|
||||
_idx->hide();
|
||||
}
|
||||
emit SceneItemChanged(s);
|
||||
}
|
||||
|
||||
void SceneItemSelectionWidget::IdxChanged(int idx)
|
||||
{
|
||||
if (idx < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
SceneItemSelection s;
|
||||
s._sceneItem = _sceneItem;
|
||||
if (_showAll && idx == 0) {
|
||||
if (_allType == AllSelectionType::ALL) {
|
||||
s._target = SceneItemSelection::Target::ALL;
|
||||
} else {
|
||||
s._target = SceneItemSelection::Target::ANY;
|
||||
}
|
||||
s._idx = 0;
|
||||
} else {
|
||||
s._target = SceneItemSelection::Target::INDIVIDUAL;
|
||||
if (_showAll) {
|
||||
idx -= 1;
|
||||
}
|
||||
s._idx = idx;
|
||||
}
|
||||
emit SceneItemChanged(s);
|
||||
}
|
||||
|
||||
void SceneItemSelectionWidget::SetupIdxSelection(int sceneItemCount)
|
||||
{
|
||||
_idx->clear();
|
||||
if (_showAll) {
|
||||
if (_allType == AllSelectionType::ALL) {
|
||||
_idx->addItem(obs_module_text(
|
||||
"AdvSceneSwitcher.sceneItemSelection.all"));
|
||||
} else {
|
||||
_idx->addItem(obs_module_text(
|
||||
"AdvSceneSwitcher.sceneItemSelection.any"));
|
||||
}
|
||||
}
|
||||
for (int i = 1; i <= sceneItemCount; ++i) {
|
||||
_idx->addItem(QString::number(i) + ".");
|
||||
}
|
||||
adjustSize();
|
||||
}
|
||||
91
src/status-dock.cpp
Normal file
91
src/status-dock.cpp
Normal file
@@ -0,0 +1,91 @@
|
||||
#include "headers/status-dock.hpp"
|
||||
#include "headers/advanced-scene-switcher.hpp"
|
||||
#include "headers/utility.hpp"
|
||||
|
||||
#include <obs-module.h>
|
||||
#include <QMainWindow>
|
||||
#include <QLayout>
|
||||
|
||||
StatusDock *dock = nullptr;
|
||||
|
||||
StatusDock::StatusDock(QWidget *parent)
|
||||
: QDockWidget(obs_module_text("AdvSceneSwitcher.windowTitle"), parent)
|
||||
{
|
||||
_button = new QPushButton(
|
||||
obs_module_text("AdvSceneSwitcher.generalTab.status.start"));
|
||||
_status =
|
||||
new QLabel(obs_module_text("AdvSceneSwitcher.status.inactive"));
|
||||
QWidget::connect(_button, SIGNAL(clicked()), this,
|
||||
SLOT(ButtonClicked()));
|
||||
QHBoxLayout *statusLayout = new QHBoxLayout;
|
||||
statusLayout->addWidget(new QLabel(obs_module_text(
|
||||
"AdvSceneSwitcher.generalTab.status.currentStatus")));
|
||||
statusLayout->addWidget(_status);
|
||||
QVBoxLayout *layout = new QVBoxLayout;
|
||||
layout->addLayout(statusLayout);
|
||||
layout->addWidget(_button);
|
||||
layout->addStretch();
|
||||
QWidget *tmp = new QWidget;
|
||||
tmp->setLayout(layout);
|
||||
setWidget(tmp);
|
||||
connect(&_timer, SIGNAL(timeout()), this, SLOT(UpdateStatus()));
|
||||
_timer.start(1000);
|
||||
}
|
||||
|
||||
void StatusDock::ButtonClicked()
|
||||
{
|
||||
if (switcher->th && switcher->th->isRunning()) {
|
||||
switcher->Stop();
|
||||
SetStopped();
|
||||
} else {
|
||||
switcher->Start();
|
||||
SetStarted();
|
||||
}
|
||||
}
|
||||
|
||||
void StatusDock::UpdateStatus()
|
||||
{
|
||||
if (switcher->th && switcher->th->isRunning()) {
|
||||
SetStopped();
|
||||
} else {
|
||||
SetStarted();
|
||||
}
|
||||
}
|
||||
|
||||
void StatusDock::SetStopped()
|
||||
{
|
||||
_button->setText(
|
||||
obs_module_text("AdvSceneSwitcher.generalTab.status.stop"));
|
||||
_status->setText(obs_module_text("AdvSceneSwitcher.status.active"));
|
||||
}
|
||||
|
||||
void StatusDock::SetStarted()
|
||||
{
|
||||
_button->setText(
|
||||
obs_module_text("AdvSceneSwitcher.generalTab.status.start"));
|
||||
_status->setText(obs_module_text("AdvSceneSwitcher.status.inactive"));
|
||||
}
|
||||
|
||||
void saveDock(obs_data_t *obj)
|
||||
{
|
||||
obs_data_set_bool(obj, "statusDockVisible", dock->isVisible());
|
||||
obs_data_set_bool(obj, "statusDockFloating", dock->isFloating());
|
||||
obs_data_set_int(obj, "statusDockPosX", dock->pos().x());
|
||||
obs_data_set_int(obj, "statusDockPosY", dock->pos().y());
|
||||
obs_data_set_int(obj, "statusDockPosWidth", dock->width());
|
||||
obs_data_set_int(obj, "statusDockPosHeight", dock->height());
|
||||
}
|
||||
|
||||
void loadDock(obs_data_t *obj)
|
||||
{
|
||||
dock->setVisible(obs_data_get_bool(obj, "statusDockVisible"));
|
||||
dock->setFloating(obs_data_get_bool(obj, "statusDockFloating"));
|
||||
QPoint pos = {
|
||||
static_cast<int>(obs_data_get_int(obj, "statusDockPosX")),
|
||||
static_cast<int>(obs_data_get_int(obj, "statusDockPosY"))};
|
||||
if (windowPosValid(pos)) {
|
||||
dock->resize(obs_data_get_int(obj, "statusDockPosWidth"),
|
||||
obs_data_get_int(obj, "statusDockPosHeight"));
|
||||
dock->move(pos);
|
||||
}
|
||||
}
|
||||
@@ -238,13 +238,10 @@ void AdvSceneSwitcher::setupAudioTab()
|
||||
ui->audioFallback->setChecked(switcher->audioFallback.enable);
|
||||
}
|
||||
|
||||
void AudioSwitch::setVolumeLevel(void *data,
|
||||
const float magnitude[MAX_AUDIO_CHANNELS],
|
||||
void AudioSwitch::setVolumeLevel(void *data, const float *,
|
||||
const float peak[MAX_AUDIO_CHANNELS],
|
||||
const float inputPeak[MAX_AUDIO_CHANNELS])
|
||||
const float *)
|
||||
{
|
||||
UNUSED_PARAMETER(magnitude);
|
||||
UNUSED_PARAMETER(inputPeak);
|
||||
AudioSwitch *s = static_cast<AudioSwitch *>(data);
|
||||
|
||||
for (int i = 0; i < MAX_AUDIO_CHANNELS; i++) {
|
||||
@@ -347,7 +344,7 @@ AudioSwitch::AudioSwitch(const AudioSwitch &other)
|
||||
volmeter = AddVolmeterToSource(this, other.audioSource);
|
||||
}
|
||||
|
||||
AudioSwitch::AudioSwitch(AudioSwitch &&other)
|
||||
AudioSwitch::AudioSwitch(AudioSwitch &&other) noexcept
|
||||
: SceneSwitcherEntry(other.targetType, other.group, other.scene,
|
||||
other.transition, other.usePreviousScene),
|
||||
audioSource(other.audioSource),
|
||||
|
||||
@@ -342,7 +342,7 @@ MediaSwitch::MediaSwitch(const MediaSwitch &other)
|
||||
obs_source_release(mediasource);
|
||||
}
|
||||
|
||||
MediaSwitch::MediaSwitch(MediaSwitch &&other)
|
||||
MediaSwitch::MediaSwitch(MediaSwitch &&other) noexcept
|
||||
: SceneSwitcherEntry(other.targetType, other.group, other.scene,
|
||||
other.transition, other.usePreviousScene),
|
||||
source(other.source),
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user