mirror of
https://github.com/WarmUpTill/SceneSwitcher.git
synced 2026-08-29 12:45:28 -05:00
Compare commits
91 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
95537f39e4 | ||
|
|
0490c97ad9 | ||
|
|
4b17314c66 | ||
|
|
7459793fe3 | ||
|
|
8a39a859be | ||
|
|
f8a592f17a | ||
|
|
f824ac8f4e | ||
|
|
8e9323173a | ||
|
|
9bc59c1082 | ||
|
|
8b9f03a349 | ||
|
|
093b0f9ec0 | ||
|
|
254eabfa8d | ||
|
|
b2da8faf37 | ||
|
|
3eba20e2d8 | ||
|
|
08841aa88e | ||
|
|
2e96c226b5 | ||
|
|
c34308f743 | ||
|
|
4af07d9d55 | ||
|
|
d743260ca8 | ||
|
|
8ff50056f0 | ||
|
|
b786831f3b | ||
|
|
32e2a5f44c | ||
|
|
e3f277c32a | ||
|
|
5a7d76061c | ||
|
|
39a8c34fa6 | ||
|
|
ead51e2f13 | ||
|
|
3b465e1fdc | ||
|
|
26fd26051b | ||
|
|
08933a3683 | ||
|
|
9be00ba3ca | ||
|
|
cce15529fe | ||
|
|
588f90f0b1 | ||
|
|
d3220c40dc | ||
|
|
79c659b4fa | ||
|
|
292adb990d | ||
|
|
b7a0f6c5d0 | ||
|
|
b3f1aff03c | ||
|
|
aea9d1b319 | ||
|
|
1fd724e4f4 | ||
|
|
3d1c613f1c | ||
|
|
9ca2f4470b | ||
|
|
34527b08a9 | ||
|
|
3ae1050488 | ||
|
|
d7ec7c6e40 | ||
|
|
e98ee64974 | ||
|
|
7c6428b27e | ||
|
|
aca9b84fc6 | ||
|
|
d397f5e406 | ||
|
|
97cbcd7e61 | ||
|
|
64fd8dd50d | ||
|
|
a0dd1cf3ef | ||
|
|
25cf51abea | ||
|
|
3c8f5088d3 | ||
|
|
66f3c74cf3 | ||
|
|
6c23110fb9 | ||
|
|
c9001df97c | ||
|
|
76fa1f8942 | ||
|
|
55ca146a82 | ||
|
|
a3625242de | ||
|
|
db4214c918 | ||
|
|
f024199e2d | ||
|
|
299e7bc868 | ||
|
|
0262e066bf | ||
|
|
bee75336d0 | ||
|
|
96b472776a | ||
|
|
fb457f723a | ||
|
|
eee3e05c10 | ||
|
|
ccc147a1b3 | ||
|
|
45a149f6df | ||
|
|
076be21d40 | ||
|
|
ab5bdb32f5 | ||
|
|
8cfed048cc | ||
|
|
c842595214 | ||
|
|
8fa3e8b038 | ||
|
|
174c17edea | ||
|
|
9d8fadda80 | ||
|
|
58028d3c3d | ||
|
|
3dcabe543f | ||
|
|
1a7ebc5569 | ||
|
|
522ab2963e | ||
|
|
d9bd98ce9c | ||
|
|
2af0735a2f | ||
|
|
556943c293 | ||
|
|
e3004184c4 | ||
|
|
7b2c2e91db | ||
|
|
06ea7bf1d9 | ||
|
|
1474509cb9 | ||
|
|
87aa8d3b40 | ||
|
|
d3e8b76fa6 | ||
|
|
99c9b1a4a2 | ||
|
|
8c0aad4c96 |
49
.github/workflows/build-debian.yml
vendored
Normal file
49
.github/workflows/build-debian.yml
vendored
Normal file
@@ -0,0 +1,49 @@
|
||||
name: debian-build
|
||||
|
||||
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
|
||||
120
.github/workflows/build.yml
vendored
120
.github/workflows/build.yml
vendored
@@ -1,14 +1,11 @@
|
||||
name: build obs plugin
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master ]
|
||||
pull_request:
|
||||
branches: [ master ]
|
||||
on: [push, pull_request, workflow_dispatch]
|
||||
|
||||
env:
|
||||
PLUGIN_NAME: SceneSwitcher
|
||||
LIB_NAME: advanced-scene-switcher
|
||||
OBS_TAG: 27.1.1
|
||||
|
||||
jobs:
|
||||
macos64:
|
||||
@@ -23,6 +20,7 @@ jobs:
|
||||
with:
|
||||
repository: obsproject/obs-studio
|
||||
submodules: 'recursive'
|
||||
ref: 'refs/tags/${{ env.OBS_TAG }}'
|
||||
- name: "Checkout plugin"
|
||||
uses: actions/checkout@v2.3.4
|
||||
with:
|
||||
@@ -57,6 +55,15 @@ jobs:
|
||||
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'
|
||||
shell: bash
|
||||
run: |
|
||||
cd UI/frontend-plugins/${{ env.PLUGIN_NAME }}/deps/opencv
|
||||
mkdir build
|
||||
cd build
|
||||
cmake -DBUILD_LIST="core,imgproc,objdetect" ..
|
||||
make -j4
|
||||
make install
|
||||
- name: Configure
|
||||
shell: bash
|
||||
run: |
|
||||
@@ -104,13 +111,14 @@ jobs:
|
||||
path: ./nightly/*.pkg
|
||||
ubuntu64:
|
||||
name: 'Linux/Ubuntu 64-bit'
|
||||
runs-on: [ubuntu-18.04]
|
||||
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 }}'
|
||||
- name: "Checkout plugin"
|
||||
uses: actions/checkout@v2.3.4
|
||||
with:
|
||||
@@ -168,7 +176,11 @@ jobs:
|
||||
qtbase5-private-dev \
|
||||
libqt5svg5-dev \
|
||||
swig \
|
||||
libxss-dev
|
||||
libxss-dev \
|
||||
libx11-xcb-dev \
|
||||
libxcb-xfixes0-dev \
|
||||
libopencv-dev \
|
||||
libprocps-dev
|
||||
- name: 'Configure'
|
||||
shell: bash
|
||||
run: |
|
||||
@@ -186,6 +198,7 @@ jobs:
|
||||
FILE_NAME=${{ env.PLUGIN_NAME }}-$FILE_DATE-${{ github.sha }}-linux64.tar.gz
|
||||
echo "FILE_NAME=${FILE_NAME}" >> $GITHUB_ENV
|
||||
mkdir -p ./${{ env.LIB_NAME }}/bin/64bit/
|
||||
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
|
||||
tar -cvzf "${FILE_NAME}" ${{ env.LIB_NAME }}
|
||||
@@ -197,6 +210,9 @@ jobs:
|
||||
windows:
|
||||
name: Windows
|
||||
runs-on: [windows-latest]
|
||||
strategy:
|
||||
matrix:
|
||||
arch: [32, 64]
|
||||
env:
|
||||
QT_VERSION: 5.10.1
|
||||
CMAKE_GENERATOR: "Visual Studio 16 2019"
|
||||
@@ -210,6 +226,7 @@ jobs:
|
||||
with:
|
||||
repository: obsproject/obs-studio
|
||||
submodules: 'recursive'
|
||||
ref: 'refs/tags/${{ env.OBS_TAG }}'
|
||||
- name: Checkout plugin
|
||||
uses: actions/checkout@v2.3.4
|
||||
with:
|
||||
@@ -228,53 +245,97 @@ jobs:
|
||||
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'
|
||||
run: |
|
||||
cd UI/frontend-plugins/${{ env.PLUGIN_NAME }}/deps/opencv
|
||||
mkdir build
|
||||
cd build
|
||||
if ( ${{ matrix.arch }} -eq 32 )
|
||||
{
|
||||
cmake -G"${{ env.CMAKE_GENERATOR }}" -A"Win32" -DBUILD_LIST="core,imgproc,objdetect" ..
|
||||
}
|
||||
else
|
||||
{
|
||||
cmake -G"${{ env.CMAKE_GENERATOR }}" -A"x64" -DBUILD_LIST="core,imgproc,objdetect" ..
|
||||
}
|
||||
msbuild /m /p:Configuration=Release OpenCV.sln
|
||||
msbuild INSTALL.vcxproj
|
||||
- name: Configure
|
||||
run: |
|
||||
mkdir ./package
|
||||
mkdir ./build32
|
||||
cd ./build32
|
||||
cmake -G"${{ env.CMAKE_GENERATOR }}" -A"Win32" -DCMAKE_SYSTEM_VERSION="${{ env.CMAKE_SYSTEM_VERSION }}" -DBUILD_BROWSER=false -DBUILD_CAPTIONS=false -DCOMPILE_D3D12_HOOK=false -DDepsPath="${{ github.workspace }}/cmbuild/deps/win32" -DQTDIR="${{ github.workspace }}/cmbuild/QT/${{ env.QT_VERSION }}/msvc2017" -DCOPIED_DEPENDENCIES=FALSE -DCOPY_DEPENDENCIES=TRUE ..
|
||||
cd ..
|
||||
mkdir ./build64
|
||||
cd ./build64
|
||||
cmake -G"${{ env.CMAKE_GENERATOR }}" -A"x64" -DCMAKE_SYSTEM_VERSION="${{ env.CMAKE_SYSTEM_VERSION }}" -DBUILD_BROWSER=false -DBUILD_CAPTIONS=false -DCOMPILE_D3D12_HOOK=false -DDepsPath="${{ github.workspace }}/cmbuild/deps/win64" -DQTDIR="${{ github.workspace }}/cmbuild/QT/${{ env.QT_VERSION }}/msvc2017_64" -DCOPIED_DEPENDENCIES=FALSE -DCOPY_DEPENDENCIES=TRUE ..
|
||||
mkdir ./build${{ matrix.arch }}
|
||||
cd ./build${{ matrix.arch }}
|
||||
Get-Location
|
||||
if ( ${{ matrix.arch }} -eq 32 )
|
||||
{
|
||||
cmake -G"${{ env.CMAKE_GENERATOR }}" -A"Win32" -DOpenCV_DIR="${{ github.workspace }}/UI/frontend-plugins/SceneSwitcher/deps/opencv/build/" -DCMAKE_SYSTEM_VERSION="${{ env.CMAKE_SYSTEM_VERSION }}" -DBUILD_BROWSER=false -DBUILD_CAPTIONS=false -DCOMPILE_D3D12_HOOK=false -DDepsPath="${{ github.workspace }}/cmbuild/deps/win32" -DQTDIR="${{ github.workspace }}/cmbuild/QT/${{ env.QT_VERSION }}/msvc2017" -DCOPIED_DEPENDENCIES=FALSE -DCOPY_DEPENDENCIES=TRUE ..
|
||||
}
|
||||
else
|
||||
{
|
||||
cmake -G"${{ env.CMAKE_GENERATOR }}" -A"x64" -DOpenCV_DIR="${{ github.workspace }}/UI/frontend-plugins/SceneSwitcher/deps/opencv/build/" -DCMAKE_SYSTEM_VERSION="${{ env.CMAKE_SYSTEM_VERSION }}" -DBUILD_BROWSER=false -DBUILD_CAPTIONS=false -DCOMPILE_D3D12_HOOK=false -DDepsPath="${{ github.workspace }}/cmbuild/deps/win64" -DQTDIR="${{ github.workspace }}/cmbuild/QT/${{ env.QT_VERSION }}/msvc2017_64" -DCOPIED_DEPENDENCIES=FALSE -DCOPY_DEPENDENCIES=TRUE ..
|
||||
}
|
||||
- name: Build
|
||||
run: |
|
||||
msbuild /m /p:Configuration=RelWithDebInfo .\build32\obs-studio.sln
|
||||
msbuild /m /p:Configuration=RelWithDebInfo .\build64\obs-studio.sln
|
||||
msbuild /m /p:Configuration=RelWithDebInfo .\build${{ matrix.arch }}\obs-studio.sln
|
||||
- name: Package
|
||||
if: success()
|
||||
run: |
|
||||
$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 .\build32\rundir\RelWithDebInfo\obs-plugins\32bit\ .\package\obs-plugins\32bit ${{ env.LIB_NAME }}.* /E /XF .gitignore
|
||||
robocopy .\build64\rundir\RelWithDebInfo\obs-plugins\64bit\ .\package\obs-plugins\64bit ${{ env.LIB_NAME }}.* /E /XF .gitignore
|
||||
robocopy .\build64\rundir\RelWithDebInfo\data\obs-plugins\${{ env.LIB_NAME }}\ .\package\data\obs-plugins\${{ 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
|
||||
exit 0
|
||||
- name: Publish zip
|
||||
if: success()
|
||||
uses: actions/upload-artifact@v2.2.1
|
||||
with:
|
||||
name: '${{ env.FILE_NAME }}'
|
||||
name: '${{ env.FILE_NAME }}-${{ matrix.arch }}bit'
|
||||
path: package/*
|
||||
- name: "Package Installer (Prereqs)"
|
||||
windows-installer:
|
||||
needs: [windows]
|
||||
name: 'Create Windows Installer'
|
||||
runs-on: [windows-latest]
|
||||
steps:
|
||||
- name: "Checkout plugin"
|
||||
uses: actions/checkout@v2.3.4
|
||||
- name: Add msbuild to PATH
|
||||
uses: microsoft/setup-msbuild@v1.0.2
|
||||
- name: "Prepare innosetup"
|
||||
run: |
|
||||
curl "-kL" "https://files.jrsoftware.org/is/6/innosetup-6.0.3.exe" "-f" "--retry" "5" "-o" "inno.exe"
|
||||
.\inno.exe /VERYSILENT /SP- /SUPPRESSMSGBOXES /NORESTART
|
||||
mkdir build
|
||||
cd build
|
||||
cmake ..
|
||||
continue-on-error: true
|
||||
- name: Download artifacts
|
||||
uses: actions/download-artifact@v2
|
||||
with:
|
||||
path: artifacts
|
||||
- name: "Prepare package dir"
|
||||
run: |
|
||||
curl "-kL" "https://github.com/Xaymar/msvc-redist-helper/releases/download/0.1/msvc-redist-helper-64.exe" "-f" "--retry" "5" "-o" "msvc-redist-helper-64.exe"
|
||||
curl "-kL" "https://github.com/Xaymar/msvc-redist-helper/releases/download/0.1/msvc-redist-helper-32.exe" "-f" "--retry" "5" "-o" "msvc-redist-helper-32.exe"
|
||||
curl "-kL" "https://files.jrsoftware.org/is/6/innosetup-6.0.3.exe" "-f" "--retry" "5" "-o" "inno.exe"
|
||||
.\inno.exe /VERYSILENT /SP- /SUPPRESSMSGBOXES /NORESTART
|
||||
- name: "Package Installer (Compile)"
|
||||
$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}
|
||||
mkdir package
|
||||
cd package
|
||||
cp -r ../artifacts/${env:FILE_NAME}-32bit/* .
|
||||
cp -r ../artifacts/${env:FILE_NAME}-64bit/obs-plugins/* obs-plugins
|
||||
- name: "Compile"
|
||||
run: |
|
||||
& 'C:\Program Files (x86)\Inno Setup 6\ISCC.exe' /Qp ".\build64\UI\frontend-plugins\${{ env.PLUGIN_NAME }}\CI\windows\setup.iss"
|
||||
- name: Publish installer
|
||||
& 'C:\Program Files (x86)\Inno Setup 6\ISCC.exe' /Qp ".\build\CI\windows\setup.iss"
|
||||
- name: "Publish"
|
||||
if: success()
|
||||
uses: actions/upload-artifact@v2.2.0
|
||||
with:
|
||||
name: '${{ env.FILE_NAME }}-installer'
|
||||
path: build64/UI/frontend-plugins/SceneSwitcher/CI/windows/Output/*.exe
|
||||
path: build/CI/windows/Output/*.exe
|
||||
release:
|
||||
needs: [macos64, ubuntu64, windows]
|
||||
needs: [macos64, ubuntu64, windows, windows-installer]
|
||||
name: 'Create Release'
|
||||
runs-on: [ubuntu-18.04]
|
||||
steps:
|
||||
@@ -292,7 +353,8 @@ jobs:
|
||||
mkdir -p ${{ env.PLUGIN_NAME }}/Linux ${{ env.PLUGIN_NAME }}/MacOs ${{ env.PLUGIN_NAME }}/Windows
|
||||
tar xf artifacts/${{ env.PLUGIN_NAME }}*-linux64.tar.gz/${{ env.PLUGIN_NAME }}*-linux64.tar.gz -C ${{ env.PLUGIN_NAME }}/Linux/
|
||||
mv artifacts/${{ env.PLUGIN_NAME }}*-macos.pkg/${{ env.PLUGIN_NAME }}*-macos.pkg ${{ env.PLUGIN_NAME }}/MacOs/${{ env.PLUGIN_NAME }}.pkg
|
||||
mv ./artifacts/${{ env.PLUGIN_NAME }}*-windows/* ${{ env.PLUGIN_NAME }}/Windows/
|
||||
mv ./artifacts/${{ env.PLUGIN_NAME }}*-windows-32bit/* ${{ env.PLUGIN_NAME }}/Windows/
|
||||
mv ./artifacts/${{ env.PLUGIN_NAME }}*-windows-64bit/obs-plugins/* ${{ env.PLUGIN_NAME }}/Windows/obs-plugins
|
||||
mv ./artifacts/${{ env.PLUGIN_NAME }}*-windows-installer/*.exe ${{ env.PLUGIN_NAME }}/Windows/
|
||||
rm ${{ env.PLUGIN_NAME }}/Windows/obs-plugins/32bit/advanced-scene-switcher.pdb
|
||||
rm ${{ env.PLUGIN_NAME }}/Windows/obs-plugins/64bit/advanced-scene-switcher.pdb
|
||||
|
||||
3
.gitmodules
vendored
3
.gitmodules
vendored
@@ -4,3 +4,6 @@
|
||||
[submodule "deps/websocketpp"]
|
||||
path = deps/websocketpp
|
||||
url = https://github.com/zaphoyd/websocketpp.git
|
||||
[submodule "deps/opencv"]
|
||||
path = deps/opencv
|
||||
url = https://github.com/opencv/opencv.git
|
||||
|
||||
23
BUILDING.md
23
BUILDING.md
@@ -16,7 +16,20 @@ git clone --recursive https://github.com/WarmUpTill/SceneSwitcher.git
|
||||
```
|
||||
|
||||
Then modify the obs-studio/UI/frontend-plugins/CMakeLists.txt file and add an entry for the scene switcher:
|
||||
```
|
||||
add_subdirectory(SceneSwitcher)
|
||||
```
|
||||
|
||||
Now follow the [build instructions for obs-studio](https://obsproject.com/wiki/install-instructions) for your particular platform.
|
||||
|
||||
Note that on Linux systems it might be necessary to additionally install the following packages to fulfill the dependencies to `XTest`, `XScreensaver` and `OpenCV` - exact command may differ:
|
||||
```
|
||||
sudo apt-get install \
|
||||
libxtst-dev \
|
||||
libxss-dev \
|
||||
libopencv-dev
|
||||
```
|
||||
|
||||
|
||||
## Compiling out of tree
|
||||
### Prerequisites
|
||||
@@ -41,7 +54,7 @@ code of OBS Studio, located at [source_directory]/libobs/.
|
||||
- **LIBOBS_FRONTEND_INCLUDE_DIR** (path) : location of the obs-frontend-api
|
||||
subfolder in the source code of OBS Studio, located at [source_directory]/UI/obs-frontend-api.
|
||||
|
||||
- **CURL_LIB** (filepath) : location of the libcurl.lib file
|
||||
- **CURL_LIBRARY** (filepath) : location of the libcurl.lib file
|
||||
(part of the dependencies2019/win64/bin folder used to build OBS)
|
||||
- **CURL_INCLUDE_DIR** (path) : location of the curl
|
||||
subfolder in OBS dependencies folder: ".../dependencies2019/win64/include/"
|
||||
@@ -55,6 +68,14 @@ Assuming that you set up Qt via QT installer:
|
||||
Just keep hitting configure until all the vars are filled out. Then hit generate.
|
||||
|
||||
### Linux
|
||||
Install dependencies `XTest`, `XScreensaver` and `OpenCV` - exact command may differ:
|
||||
```
|
||||
sudo apt-get install \
|
||||
libxtst-dev \
|
||||
libxss-dev \
|
||||
libopencv-dev
|
||||
```
|
||||
|
||||
Most versions of Linux you can use cmake-gui or the command line.
|
||||
|
||||
**For the command line:**
|
||||
|
||||
5
CI/linux/debian/changelog
Normal file
5
CI/linux/debian/changelog
Normal file
@@ -0,0 +1,5 @@
|
||||
obs-scene-switcher (0.1+testonly) focal; urgency=medium
|
||||
|
||||
* Initial release (Closes: #123456)
|
||||
|
||||
-- Joao Eriberto Mota Filho <eriberto@debian.org> Fri, 03 Sep 2021 11:39:52 -0300
|
||||
22
CI/linux/debian/control
Normal file
22
CI/linux/debian/control
Normal file
@@ -0,0 +1,22 @@
|
||||
Source: obs-scene-switcher
|
||||
Section: video
|
||||
Priority: optional
|
||||
Maintainer: Joao Eriberto Mota Filho <eriberto@debian.org>
|
||||
Build-Depends: cmake,
|
||||
debhelper-compat (= 12),
|
||||
libcurl4-openssl-dev,
|
||||
libobs-dev,
|
||||
libxtst-dev,
|
||||
qtbase5-dev,
|
||||
libxss-dev,
|
||||
libopencv-dev
|
||||
Standards-Version: 4.6.0
|
||||
Homepage: https://obsproject.com/forum/resources/advanced-scene-switcher.395/
|
||||
Rules-Requires-Root: no
|
||||
|
||||
Package: obs-scene-switcher
|
||||
Architecture: any
|
||||
Depends: ${misc:Depends}, ${shlibs:Depends}
|
||||
Description: automated scene switcher for OBS Studio (plugin)
|
||||
Advanced Scene Switcher is based on the built in scene switcher. It extends
|
||||
its functionality by adding several features.
|
||||
60
CI/linux/debian/copyright
Normal file
60
CI/linux/debian/copyright
Normal file
@@ -0,0 +1,60 @@
|
||||
Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
|
||||
Upstream-Name: SceneSwitcher
|
||||
Upstream-Contact: https://github.com/WarmUpTill/SceneSwitcher/issues
|
||||
Source: https://github.com/WarmUpTill/SceneSwitcher
|
||||
|
||||
Files: *
|
||||
Copyright: 2016-2021 WarmUpTill <WarmUpTill@web.de>
|
||||
License: GPL-2
|
||||
|
||||
Files: cmake/GetGitRevisionDescription.cmake
|
||||
cmake/GetGitRevisionDescription.cmake.in
|
||||
Copyright: 2009-2013 Iowa State University
|
||||
2013-2020 Contributors
|
||||
2013-2020 Ryan Pavlik
|
||||
License: BSL-1.0
|
||||
|
||||
Files: debian/*
|
||||
Copyright: 2021 Joao Eriberto Mota Filho <eriberto@debian.org>
|
||||
License: BSL-1.0
|
||||
|
||||
License: GPL-2
|
||||
This package is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; version 2.
|
||||
.
|
||||
This package is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
.
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>
|
||||
.
|
||||
On Debian systems, the complete text of the GNU General
|
||||
Public License version 2 can be found in "/usr/share/common-licenses/GPL-2".
|
||||
|
||||
License: BSL-1.0
|
||||
Boost Software License - Version 1.0 - August 17th, 2003
|
||||
.
|
||||
Permission is hereby granted, free of charge, to any person or organization
|
||||
obtaining a copy of the software and accompanying documentation covered by
|
||||
this license (the "Software") to use, reproduce, display, distribute,
|
||||
execute, and transmit the Software, and to prepare derivative works of the
|
||||
Software, and to permit third-parties to whom the Software is furnished to
|
||||
do so, all subject to the following:
|
||||
.
|
||||
The copyright notices in the Software and this entire statement, including
|
||||
the above license grant, this restriction and the following disclaimer,
|
||||
must be included in all copies of the Software, in whole or in part, and
|
||||
all derivative works of the Software, unless such copies or derivative
|
||||
works are solely in the form of machine-executable object code generated by
|
||||
a source language processor.
|
||||
.
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
16
CI/linux/debian/rules
Normal file
16
CI/linux/debian/rules
Normal file
@@ -0,0 +1,16 @@
|
||||
#!/usr/bin/make -f
|
||||
#export DH_VERBOSE = 1
|
||||
|
||||
export DEB_BUILD_MAINT_OPTIONS = hardening=+all
|
||||
|
||||
export QT_SELECT = qt5
|
||||
|
||||
%:
|
||||
dh $@
|
||||
|
||||
|
||||
dh_auto_configure -- -DBUILD_OUT_OF_TREE=1 \
|
||||
-DLIBOBS_INCLUDE_DIR=/usr/include/obs \
|
||||
-DLIBOBS_LIB=/usr/lib/${DEB_TARGET_MULTIARCH}/libobs.so \
|
||||
-DLIBOBS_FRONTEND_INCLUDE_DIR=/usr/include/obs \
|
||||
-DLIBOBS_FRONTEND_API_LIB=/usr/lib/${DEB_TARGET_MULTIARCH}/libobs-frontend-api.so
|
||||
1
CI/linux/debian/source/format
Normal file
1
CI/linux/debian/source/format
Normal file
@@ -0,0 +1 @@
|
||||
3.0 (native)
|
||||
@@ -46,6 +46,294 @@
|
||||
<key>UID</key>
|
||||
<integer>0</integer>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>CHILDREN</key>
|
||||
<array/>
|
||||
<key>GID</key>
|
||||
<integer>80</integer>
|
||||
<key>PATH</key>
|
||||
<string>../../deps/opencv/build/lib/libopencv_objdetect.4.5.3.dylib</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/>
|
||||
<key>GID</key>
|
||||
<integer>80</integer>
|
||||
<key>PATH</key>
|
||||
<string>../../deps/opencv/build/lib/libopencv_features2d.4.5.dylib</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/>
|
||||
<key>GID</key>
|
||||
<integer>80</integer>
|
||||
<key>PATH</key>
|
||||
<string>../../deps/opencv/build/lib/libopencv_core.4.5.dylib</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/>
|
||||
<key>GID</key>
|
||||
<integer>80</integer>
|
||||
<key>PATH</key>
|
||||
<string>../../deps/opencv/build/lib/libopencv_calib3d.4.5.dylib</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/>
|
||||
<key>GID</key>
|
||||
<integer>80</integer>
|
||||
<key>PATH</key>
|
||||
<string>../../deps/opencv/build/lib/libopencv_calib3d.4.5.3.dylib</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/>
|
||||
<key>GID</key>
|
||||
<integer>80</integer>
|
||||
<key>PATH</key>
|
||||
<string>../../deps/opencv/build/lib/libopencv_imgproc.4.5.dylib</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/>
|
||||
<key>GID</key>
|
||||
<integer>80</integer>
|
||||
<key>PATH</key>
|
||||
<string>../../deps/opencv/build/lib/libopencv_features2d.4.5.3.dylib</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/>
|
||||
<key>GID</key>
|
||||
<integer>80</integer>
|
||||
<key>PATH</key>
|
||||
<string>../../deps/opencv/build/lib/libopencv_core.4.5.3.dylib</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/>
|
||||
<key>GID</key>
|
||||
<integer>80</integer>
|
||||
<key>PATH</key>
|
||||
<string>../../deps/opencv/build/lib/libopencv_flann.4.5.dylib</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/>
|
||||
<key>GID</key>
|
||||
<integer>80</integer>
|
||||
<key>PATH</key>
|
||||
<string>../../deps/opencv/build/lib/libopencv_calib3d.dylib</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/>
|
||||
<key>GID</key>
|
||||
<integer>80</integer>
|
||||
<key>PATH</key>
|
||||
<string>../../deps/opencv/build/lib/libopencv_objdetect.dylib</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/>
|
||||
<key>GID</key>
|
||||
<integer>80</integer>
|
||||
<key>PATH</key>
|
||||
<string>../../deps/opencv/build/lib/libopencv_imgproc.4.5.3.dylib</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/>
|
||||
<key>GID</key>
|
||||
<integer>80</integer>
|
||||
<key>PATH</key>
|
||||
<string>../../deps/opencv/build/lib/libopencv_flann.dylib</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/>
|
||||
<key>GID</key>
|
||||
<integer>80</integer>
|
||||
<key>PATH</key>
|
||||
<string>../../deps/opencv/build/lib/libopencv_imgproc.dylib</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/>
|
||||
<key>GID</key>
|
||||
<integer>80</integer>
|
||||
<key>PATH</key>
|
||||
<string>../../deps/opencv/build/lib/libopencv_flann.4.5.3.dylib</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/>
|
||||
<key>GID</key>
|
||||
<integer>80</integer>
|
||||
<key>PATH</key>
|
||||
<string>../../deps/opencv/build/lib/libopencv_features2d.dylib</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/>
|
||||
<key>GID</key>
|
||||
<integer>80</integer>
|
||||
<key>PATH</key>
|
||||
<string>../../deps/opencv/build/lib/libopencv_core.dylib</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/>
|
||||
<key>GID</key>
|
||||
<integer>80</integer>
|
||||
<key>PATH</key>
|
||||
<string>../../deps/opencv/build/lib/libopencv_objdetect.4.5.dylib</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>
|
||||
</array>
|
||||
<key>GID</key>
|
||||
<integer>80</integer>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
Note that you will have to use OBS version 26.1 or newer!
|
||||
Note that you will have to use OBS version 27 or newer!
|
||||
|
||||
--- Windows ---
|
||||
Recommended: Run the provided installer. (You might have to click 'More info' and select 'Run anyway' if it is blocked by Windows)
|
||||
@@ -8,17 +8,17 @@ It is usually located at 'C:\Program Files (x86)\obs-studio\'.
|
||||
Remember to install the Visual C++ Redistributable for Visual Studio 2019, if you have not done so already. (See plugin overview page)
|
||||
|
||||
--- macOS ---
|
||||
Recommended: Run the provided installer. (You might have to right-click or control-click and select 'open' if it is blocked)
|
||||
|
||||
Alternatively extract the *so file and data folder and either ...
|
||||
|
||||
... right-click the OBS app inside your Applications folder and choose 'Show Package Contents'.
|
||||
Copy the advanced-scene-switcher.so file to 'Contents/Plugins' and the 'data' folder to 'Contents/Resources'.
|
||||
|
||||
... or copy the advanced-scene-switcher.so file to Library/Application Support/obs-studio/plugins/advanced-scene-switcher/bin/ .
|
||||
And the 'data' folder to 'Library/Application Support/obs-studio/plugins/advanced-scene-switcher/'.
|
||||
Run the provided installer. (You might have to right-click or control-click and select 'open' if it is blocked)
|
||||
|
||||
--- Linux ---
|
||||
Note that the plugin has dependencies to:
|
||||
* libXss
|
||||
* libopencv-imgproc
|
||||
* libopencv-objdetect
|
||||
Optional:
|
||||
* libXtst
|
||||
* libcurl
|
||||
|
||||
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.
|
||||
|
||||
|
||||
153
CMakeLists.txt
153
CMakeLists.txt
@@ -42,27 +42,17 @@ endif()
|
||||
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake/")
|
||||
include(GetGitRevisionDescription)
|
||||
get_git_head_revision(GIT_REFSPEC GIT_SHA1)
|
||||
git_describe(GIT_TAG)
|
||||
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/cmake/version.cpp.in"
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/src/version.cpp" @ONLY)
|
||||
if(WIN32)
|
||||
get_filename_component(ISS_PLUGIN_FILES_DIR
|
||||
"${CMAKE_BINARY_DIR}\\..\\package" ABSOLUTE)
|
||||
file(TO_NATIVE_PATH "${ISS_PLUGIN_FILES_DIR}" ISS_PLUGIN_FILES_DIR)
|
||||
get_filename_component(ISS_MSVC_REDIST_HELPER_DIR
|
||||
"${CMAKE_PACKAGE_PREFIX}\\.." ABSOLUTE)
|
||||
file(TO_NATIVE_PATH "${ISS_MSVC_REDIST_HELPER_DIR}"
|
||||
ISS_MSVC_REDIST_HELPER_DIR)
|
||||
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/CI/windows/setup.iss.in"
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/CI/windows/setup.iss" @ONLY)
|
||||
endif()
|
||||
|
||||
# Windows installer
|
||||
if(WIN32)
|
||||
get_filename_component(ISS_PLUGIN_FILES_DIR
|
||||
"${CMAKE_BINARY_DIR}\\..\\package" ABSOLUTE)
|
||||
file(TO_NATIVE_PATH "${ISS_PLUGIN_FILES_DIR}" ISS_PLUGIN_FILES_DIR)
|
||||
get_filename_component(ISS_MSVC_REDIST_HELPER_DIR
|
||||
"${CMAKE_PACKAGE_PREFIX}\\.." ABSOLUTE)
|
||||
get_filename_component(ISS_MSVC_REDIST_HELPER_DIR "${CMAKE_BINARY_DIR}\\.."
|
||||
ABSOLUTE)
|
||||
file(TO_NATIVE_PATH "${ISS_MSVC_REDIST_HELPER_DIR}"
|
||||
ISS_MSVC_REDIST_HELPER_DIR)
|
||||
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/CI/windows/setup.iss.in"
|
||||
@@ -73,19 +63,67 @@ endif()
|
||||
if(BUILD_OUT_OF_TREE)
|
||||
set(CMAKE_PREFIX_PATH "${QTDIR}")
|
||||
set(CMAKE_INCLUDE_CURRENT_DIR ON)
|
||||
list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/external")
|
||||
include(external/FindLibObs.cmake)
|
||||
find_package(LibObs REQUIRED)
|
||||
find_package(Qt5Core REQUIRED)
|
||||
find_package(Qt5Widgets REQUIRED)
|
||||
find_path(LIBOBS_FRONTEND_INCLUDE_DIR HINTS ${LIBOBS_INCLUDE_DIRS})
|
||||
find_file(
|
||||
LIBOBS_FRONTEND_API_LIB
|
||||
NAMES libobs-frontend-api.*
|
||||
HINTS ${LIBOBS_LIB})
|
||||
find_package(LibObs)
|
||||
find_package(LibObs-frontend-api)
|
||||
if(LibObs_FOUND)
|
||||
set(LIBOBS_LIB ${LIBOBS_LIBRARIES})
|
||||
set(LIBOBS_INCLUDE_DIR ${LIBOBS_INCLUDE_DIRS})
|
||||
endif()
|
||||
if(LibObs-frontend-api_FOUND)
|
||||
set(LIBOBS_FRONTEND_API_LIB ${LIBOBS-FRONTEND-API_LIBRARIES})
|
||||
set(LIBOBS_FRONTEND_INCLUDE_DIR ${LIBOBS-FRONTEND-API_INCLUDE_DIR})
|
||||
endif()
|
||||
|
||||
include_directories("${LIBOBS_INCLUDE_DIRS}" "${LIBOBS_FRONTEND_INCLUDE_DIR}"
|
||||
if(NOT LIBOBS_LIB)
|
||||
message(FATAL_ERROR "obs library not found - please set LIBOBS_LIB")
|
||||
endif()
|
||||
if(NOT LIBOBS_FRONTEND_API_LIB)
|
||||
message(
|
||||
FATAL_ERROR
|
||||
"libobs frontend-api library not found - please set LIBOBS_FRONTEND_API_LIB"
|
||||
)
|
||||
endif()
|
||||
if(NOT LIBOBS_INCLUDE_DIR)
|
||||
message(
|
||||
FATAL_ERROR "obs.hpp header not found - please set LIBOBS_INCLUDE_DIR")
|
||||
endif()
|
||||
if(NOT LIBOBS_FRONTEND_INCLUDE_DIR)
|
||||
message(
|
||||
FATAL_ERROR
|
||||
" obs-frontend-api.h not found - please set LIBOBS_FRONTEND_INCLUDE_DIR"
|
||||
)
|
||||
endif()
|
||||
|
||||
include_directories("${LIBOBS_INCLUDE_DIR}" "${LIBOBS_FRONTEND_INCLUDE_DIR}"
|
||||
${Qt5Core_INCLUDES} ${Qt5Widgets_INCLUDES})
|
||||
|
||||
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}")
|
||||
add_definitions(-DVCAM_SUPPORTED)
|
||||
add_definitions(-DREPLAYBUFFER_SUPPORTED)
|
||||
endif()
|
||||
|
||||
# Platform specific settings
|
||||
@@ -97,14 +135,22 @@ if(APPLE)
|
||||
include_directories(${COCOA})
|
||||
endif()
|
||||
|
||||
find_package(Libcurl REQUIRED)
|
||||
include_directories("${LIBCURL_INCLUDE_DIRS}")
|
||||
|
||||
if(UNIX AND NOT APPLE)
|
||||
find_package(X11 REQUIRED COMPONENTS Xtst Xss)
|
||||
link_libraries(${X11_LIBRARIES})
|
||||
find_path(PROCPS_INCLUDE_DIR NAMES proc/procps.h)
|
||||
if(NOT PROCPS_INCLUDE_DIR)
|
||||
message(
|
||||
FATAL_ERROR "procps include dir not found - please set PROCPS_INCLUDE_DIR"
|
||||
)
|
||||
endif()
|
||||
find_library(PROCPS_LIBRARY NAMES procps)
|
||||
if(NOT PROCPS_LIBRARY)
|
||||
message(FATAL_ERROR "procps lib not found - please set PROCPS_LIBRARY")
|
||||
endif()
|
||||
message (STATUS "WHAT THE FUCK IS GOING ON ${PROCPS_LIBRARY}")
|
||||
link_libraries(${X11_LIBRARIES} ${procps_LIBRARIES})
|
||||
include_directories("${X11_INCLUDE_DIR}" "${X11_Xtst_INCLUDE_PATH}"
|
||||
"${X11_Xss_INCLUDE_PATH}")
|
||||
"${X11_Xss_INCLUDE_PATH}" "${PROCPS_INCLUDE_DIR}")
|
||||
endif()
|
||||
|
||||
if(WIN32)
|
||||
@@ -119,7 +165,7 @@ elseif(APPLE)
|
||||
else()
|
||||
set(advanced-scene-switcher_PLATFORM_SOURCES
|
||||
src/linux/advanced-scene-switcher-nix.cpp)
|
||||
set(advanced-scene-switcher_PLATFORM_LIBS Xss)
|
||||
set(advanced-scene-switcher_PLATFORM_LIBS Xss ${PROCPS_LIBRARY})
|
||||
endif()
|
||||
|
||||
# asio and websocketpp
|
||||
@@ -130,6 +176,10 @@ if(WIN32)
|
||||
add_definitions(-D_WEBSOCKETPP_CPP11_STL_)
|
||||
endif()
|
||||
|
||||
# opencv
|
||||
find_package(OpenCV REQUIRED)
|
||||
include_directories("${OpenCV_INCLUDE_DIRS}")
|
||||
|
||||
# Setup QT tools
|
||||
set(CMAKE_AUTOMOC ON)
|
||||
set(CMAKE_AUTOUIC ON)
|
||||
@@ -169,15 +219,19 @@ set(advanced-scene-switcher_HEADERS
|
||||
src/headers/macro-action-macro.hpp
|
||||
src/headers/macro-action-media.hpp
|
||||
src/headers/macro-action-plugin-state.hpp
|
||||
src/headers/macro-action-preview-scene.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-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-source.hpp
|
||||
src/headers/macro-action-streaming.hpp
|
||||
src/headers/macro-action-timer.hpp
|
||||
src/headers/macro-action-transition.hpp
|
||||
src/headers/macro-action-virtual-cam.hpp
|
||||
src/headers/macro-action-wait.hpp
|
||||
src/headers/macro-condition-edit.hpp
|
||||
@@ -195,10 +249,12 @@ set(advanced-scene-switcher_HEADERS
|
||||
src/headers/macro-condition-recording.hpp
|
||||
src/headers/macro-condition-replay-buffer.hpp
|
||||
src/headers/macro-condition-scene-order.hpp
|
||||
src/headers/macro-condition-scene-transform.hpp
|
||||
src/headers/macro-condition-scene.hpp
|
||||
src/headers/macro-condition-source.hpp
|
||||
src/headers/macro-condition-streaming.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
|
||||
@@ -210,8 +266,10 @@ set(advanced-scene-switcher_HEADERS
|
||||
src/headers/hotkey.hpp
|
||||
src/headers/scene-selection.hpp
|
||||
src/headers/screenshot-helper.hpp
|
||||
src/headers/transition-selection.hpp
|
||||
src/headers/name-dialog.hpp
|
||||
src/headers/duration-control.hpp
|
||||
src/headers/file-selection.hpp
|
||||
src/headers/section.hpp
|
||||
src/headers/platform-funcs.hpp
|
||||
src/headers/utility.hpp
|
||||
@@ -235,6 +293,7 @@ set(advanced-scene-switcher_SOURCES
|
||||
src/switch-window.cpp
|
||||
src/switch-media.cpp
|
||||
src/switch-network.cpp
|
||||
src/file-selection.cpp
|
||||
src/hotkey.cpp
|
||||
src/general.cpp
|
||||
src/switch-pause.cpp
|
||||
@@ -251,15 +310,19 @@ set(advanced-scene-switcher_SOURCES
|
||||
src/macro-action-macro.cpp
|
||||
src/macro-action-media.cpp
|
||||
src/macro-action-plugin-state.cpp
|
||||
src/macro-action-preview-scene.cpp
|
||||
src/macro-action-recording.cpp
|
||||
src/macro-action-replay-buffer.cpp
|
||||
src/macro-action-run.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-source.cpp
|
||||
src/macro-action-streaming.cpp
|
||||
src/macro-action-timer.cpp
|
||||
src/macro-action-transition.cpp
|
||||
src/macro-action-virtual-cam.cpp
|
||||
src/macro-action-wait.cpp
|
||||
src/macro-condition-edit.cpp
|
||||
@@ -277,10 +340,12 @@ set(advanced-scene-switcher_SOURCES
|
||||
src/macro-condition-recording.cpp
|
||||
src/macro-condition-replay-buffer.cpp
|
||||
src/macro-condition-scene-order.cpp
|
||||
src/macro-condition-scene-transform.cpp
|
||||
src/macro-condition-scene.cpp
|
||||
src/macro-condition-source.cpp
|
||||
src/macro-condition-streaming.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
|
||||
@@ -292,6 +357,7 @@ set(advanced-scene-switcher_SOURCES
|
||||
src/curl-helper.cpp
|
||||
src/scene-selection.cpp
|
||||
src/screenshot-helper.cpp
|
||||
src/transition-selection.cpp
|
||||
src/name-dialog.cpp
|
||||
src/duration-control.cpp
|
||||
src/section.cpp
|
||||
@@ -310,8 +376,13 @@ add_library(
|
||||
# Out of tree build
|
||||
if(BUILD_OUT_OF_TREE)
|
||||
target_link_libraries(
|
||||
advanced-scene-switcher ${advanced-scene-switcher_PLATFORM_LIBS}
|
||||
${LIBOBS_LIB} ${LIBOBS_FRONTEND_API_LIB} Qt5::Core Qt5::Widgets)
|
||||
advanced-scene-switcher
|
||||
${advanced-scene-switcher_PLATFORM_LIBS}
|
||||
${OpenCV_LIBRARIES}
|
||||
${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.
|
||||
@@ -324,24 +395,24 @@ if(BUILD_OUT_OF_TREE)
|
||||
|
||||
# Linux
|
||||
if(UNIX AND NOT APPLE)
|
||||
if(ARCH EQUAL 64)
|
||||
set(ARCH_NAME "x86_64")
|
||||
else()
|
||||
set(ARCH_NAME "i686")
|
||||
if(NOT LIB_OUT_DIR)
|
||||
set(LIB_OUT_DIR "/lib/obs-plugins")
|
||||
endif()
|
||||
if(NOT DATA_OUT_DIR)
|
||||
set(DATA_OUT_DIR "/share/obs/obs-plugins/advanced-scene-switcher")
|
||||
endif()
|
||||
set_target_properties(advanced-scene-switcher PROPERTIES PREFIX "")
|
||||
install(TARGETS advanced-scene-switcher
|
||||
LIBRARY DESTINATION ${CMAKE_INSTALL_PREFIX}/lib/obs-plugins)
|
||||
install(
|
||||
FILES ${ASS_TRANSLATION_FILES}
|
||||
DESTINATION
|
||||
"${CMAKE_INSTALL_PREFIX}/share/obs/obs-plugins/advanced-scene-switcher/locale"
|
||||
)
|
||||
LIBRARY DESTINATION ${CMAKE_INSTALL_PREFIX}/${LIB_OUT_DIR})
|
||||
install(DIRECTORY data/locale
|
||||
DESTINATION ${CMAKE_INSTALL_PREFIX}/${DATA_OUT_DIR})
|
||||
install(DIRECTORY data/res
|
||||
DESTINATION ${CMAKE_INSTALL_PREFIX}/${DATA_OUT_DIR})
|
||||
endif()
|
||||
else()
|
||||
# In tree build
|
||||
target_link_libraries(
|
||||
advanced-scene-switcher ${advanced-scene-switcher_PLATFORM_LIBS}
|
||||
obs-frontend-api Qt5::Widgets libobs)
|
||||
${OpenCV_LIBRARIES} obs-frontend-api Qt5::Widgets libobs)
|
||||
install_obs_plugin_with_data(advanced-scene-switcher data)
|
||||
endif()
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# SceneSwitcher
|
||||
An automated scene switcher for OBS Studio
|
||||
An automated scene switcher for OBS Studio.
|
||||
|
||||
More information can be found on https://obsproject.com/forum/resources/automatic-scene-switching.395/.
|
||||
|
||||
@@ -13,7 +13,7 @@ For the Windows and MacOS platforms, it is recommended to run the provided insta
|
||||
|
||||
For Linux the Snap package manager offers an OBS Studio installation which is bundled with the plugin:
|
||||
```
|
||||
sudo snap install obs-studi
|
||||
sudo snap install obs-studio
|
||||
```
|
||||
|
||||
If that is not an option you will have to ...
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
#include "src/headers/version.h"
|
||||
#define GIT_SHA1 "@GIT_SHA1@"
|
||||
const char g_GIT_SHA1[] = GIT_SHA1;
|
||||
#define GIT_TAG "@GIT_TAG@"
|
||||
const char g_GIT_SHA1[] = GIT_SHA1;
|
||||
const char g_GIT_TAG[] = GIT_TAG;
|
||||
@@ -28,6 +28,7 @@ AdvSceneSwitcher.generalTab.generalBehavior.cooldown="After a match do not switc
|
||||
AdvSceneSwitcher.generalTab.generalBehavior.cooldownHint="During this time potential matches will be ignored!"
|
||||
AdvSceneSwitcher.generalTab.generalBehavior.verboseLogging="Enable verbose logging"
|
||||
AdvSceneSwitcher.generalTab.generalBehavior.saveWindowGeo="Save window position and size"
|
||||
AdvSceneSwitcher.generalTab.generalBehavior.showTrayNotifications="Show system tray notifications"
|
||||
AdvSceneSwitcher.generalTab.generalBehavior.disableUIHints="Disable UI hints"
|
||||
AdvSceneSwitcher.generalTab.priority="Priority"
|
||||
AdvSceneSwitcher.generalTab.priority.description="Switching methods priority (Highest priority is at the top)"
|
||||
@@ -93,14 +94,17 @@ AdvSceneSwitcher.condition.cursor.hideFrame="Hide frame"
|
||||
AdvSceneSwitcher.condition.cursor.entry.line1="Cursor is {{conditions}} {{minX}} {{minY}} {{maxX}} {{maxY}} - {{toggleFrameButton}}"
|
||||
AdvSceneSwitcher.condition.cursor.entry.line2="Cursor is currently at {{xPos}} x {{yPos}}"
|
||||
AdvSceneSwitcher.condition.scene="Scene"
|
||||
AdvSceneSwitcher.condition.scene.type.current="Current"
|
||||
AdvSceneSwitcher.condition.scene.type.previous="Previous"
|
||||
AdvSceneSwitcher.condition.scene.entry="{{sceneType}} scene is {{scenes}}"
|
||||
AdvSceneSwitcher.condition.scene.type.current="Current scene is"
|
||||
AdvSceneSwitcher.condition.scene.type.previous="Previous scene is"
|
||||
AdvSceneSwitcher.condition.scene.type.changed="Scene chagned"
|
||||
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.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"
|
||||
AdvSceneSwitcher.condition.file="File"
|
||||
AdvSceneSwitcher.condition.file.entry.line1="Content of {{fileType}} {{filePath}} {{browseButton}} matches:"
|
||||
AdvSceneSwitcher.condition.file.entry.line1="Content of {{fileType}} {{filePath}} matches:"
|
||||
AdvSceneSwitcher.condition.file.entry.line2="{{matchText}}"
|
||||
AdvSceneSwitcher.condition.file.entry.line3="{{useRegex}} {{checkModificationDate}} {{checkFileContent}}"
|
||||
AdvSceneSwitcher.condition.media="Media"
|
||||
@@ -111,13 +115,35 @@ AdvSceneSwitcher.condition.video.condition.differ="does not match"
|
||||
AdvSceneSwitcher.condition.video.condition.hasChanged="has changed"
|
||||
AdvSceneSwitcher.condition.video.condition.hasNotChanged="has not changed"
|
||||
AdvSceneSwitcher.condition.video.condition.noImage="has no output"
|
||||
AdvSceneSwitcher.condition.video.condition.pattern="matches pattern"
|
||||
AdvSceneSwitcher.condition.video.condition.object="contains object"
|
||||
AdvSceneSwitcher.condition.video.askFileAction="Do you want to use an existing file or create a screenshot of the currently selected source?"
|
||||
AdvSceneSwitcher.condition.video.askFileAction.file="Use existing file"
|
||||
AdvSceneSwitcher.condition.video.askFileAction.screenshot="Create screenshot"
|
||||
AdvSceneSwitcher.condition.video.entry="{{videoSources}} {{condition}} {{filePath}} {{browseButton}}"
|
||||
AdvSceneSwitcher.condition.video.usePatternForChangedCheck="Use pattern matching"
|
||||
AdvSceneSwitcher.condition.video.usePatternForChangedCheck.tooltip="This will allow you to control how much the image has to change for the condition to be true."
|
||||
AdvSceneSwitcher.condition.video.patternThreshold="Threshold: "
|
||||
AdvSceneSwitcher.condition.video.patternThresholdDescription="A higher threshold value means that the pattern needs to match the video source more closely."
|
||||
AdvSceneSwitcher.condition.video.patternThresholdUseAlphaAsMask="Use alpha channel as mask for pattern."
|
||||
AdvSceneSwitcher.condition.video.objectScaleThreshold="Scale factor: "
|
||||
AdvSceneSwitcher.condition.video.objectScaleThresholdDescription="A lower scale factor will lead to more matches but higher CPU load."
|
||||
AdvSceneSwitcher.condition.video.minNeighborDescription="A higher minimum neighbors value will result in fewer but higher quality matches."
|
||||
AdvSceneSwitcher.condition.video.showMatch="Show match"
|
||||
AdvSceneSwitcher.condition.video.screenshotFail="Failed to get screenshot of source!"
|
||||
AdvSceneSwitcher.condition.video.patternMatchFail="Pattern was not found!"
|
||||
AdvSceneSwitcher.condition.video.objectMatchFail="Object was not found!"
|
||||
AdvSceneSwitcher.condition.video.modelLoadFail="Model data could not be loaded!"
|
||||
AdvSceneSwitcher.condition.video.entry="{{videoSources}} {{condition}} {{imagePath}}"
|
||||
AdvSceneSwitcher.condition.video.entry.modelPath="Model data (haar cascade classifier): {{modelDataPath}}"
|
||||
AdvSceneSwitcher.condition.video.entry.minNeighbor="Minimum neighbors: {{minNeighbors}}"
|
||||
AdvSceneSwitcher.condition.video.entry.minSize="Minimum size: {{minSizeX}} x {{minSizeY}}"
|
||||
AdvSceneSwitcher.condition.video.entry.maxSize="Maximum size: {{maxSizeX}} x {{maxSizeY}}"
|
||||
AdvSceneSwitcher.condition.video.entry.throttle="{{throttleEnable}}Reduce CPU load by performing check only every {{throttleCount}} milliseconds"
|
||||
AdvSceneSwitcher.condition.stream="Streaming"
|
||||
AdvSceneSwitcher.condition.stream.state.start="Stream running"
|
||||
AdvSceneSwitcher.condition.stream.state.stop="Stream stopped"
|
||||
AdvSceneSwitcher.condition.stream.state.starting="Stream starting"
|
||||
AdvSceneSwitcher.condition.stream.state.stopping="Stream stopping"
|
||||
AdvSceneSwitcher.condition.stream.entry="{{streamState}}"
|
||||
AdvSceneSwitcher.condition.record="Recording"
|
||||
AdvSceneSwitcher.condition.record.state.start="Recording running"
|
||||
@@ -132,9 +158,11 @@ AdvSceneSwitcher.condition.pluginState="Plugin state"
|
||||
AdvSceneSwitcher.condition.pluginState.state.sceneSwitched="Automated scene change was triggered in this interval"
|
||||
AdvSceneSwitcher.condition.pluginState.entry="{{condition}}"
|
||||
AdvSceneSwitcher.condition.timer="Timer"
|
||||
AdvSceneSwitcher.condition.timer.pause="Pause"
|
||||
AdvSceneSwitcher.condition.timer.continue="Continue"
|
||||
AdvSceneSwitcher.condition.timer.entry.line1="{{duration}} have passed"
|
||||
AdvSceneSwitcher.condition.timer.entry.line2="Time remaining: {{remaining}} seconds"
|
||||
AdvSceneSwitcher.condition.timer.entry.line3="{{reset}} {{autoReset}} Automatically reset timer after duration was reached"
|
||||
AdvSceneSwitcher.condition.timer.entry.line3="{{pauseContinue}} {{reset}} {{saveRemaining}} Save time remaining {{autoReset}} Automatically reset timer after duration was reached"
|
||||
AdvSceneSwitcher.condition.timer.reset="Reset"
|
||||
AdvSceneSwitcher.condition.macro="Macro"
|
||||
AdvSceneSwitcher.condition.macro.type.count="Count"
|
||||
@@ -185,8 +213,25 @@ AdvSceneSwitcher.condition.replay.state.started="Replay buffer started"
|
||||
AdvSceneSwitcher.condition.replay.state.saved="Replay buffer saved"
|
||||
AdvSceneSwitcher.condition.replay.entry="{{state}}"
|
||||
AdvSceneSwitcher.condition.date="Date"
|
||||
AdvSceneSwitcher.condition.date.entry.line1="{{dateTime}} {{ignoreDate}} Ignore date selection {{ignoreTime}} Ignore time selection"
|
||||
AdvSceneSwitcher.condition.date.entry.line2="{{repeat}} Repeat every {{duration}}"
|
||||
AdvSceneSwitcher.condition.date.state.at="At"
|
||||
AdvSceneSwitcher.condition.date.state.after="After"
|
||||
AdvSceneSwitcher.condition.date.state.before="Before"
|
||||
AdvSceneSwitcher.condition.date.state.between="Between"
|
||||
AdvSceneSwitcher.condition.date.entry.line1="{{condition}} {{dateTime}} {{dateTime2}} {{ignoreDate}} Ignore date component {{ignoreTime}} Ignore time component"
|
||||
AdvSceneSwitcher.condition.date.entry.line2="{{repeat}} Repeat every {{duration}} on date match"
|
||||
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.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.ended="Transition ended"
|
||||
AdvSceneSwitcher.condition.transition.durationSuffix="seconds"
|
||||
AdvSceneSwitcher.condition.transition.entry="{{conditions}}{{transitions}}{{duration}}{{durationSuffix}}"
|
||||
|
||||
; Macro Actions
|
||||
AdvSceneSwitcher.action.switchScene="Switch scene"
|
||||
@@ -222,7 +267,7 @@ AdvSceneSwitcher.action.run="Run"
|
||||
AdvSceneSwitcher.action.run.arguments="Arguments:"
|
||||
AdvSceneSwitcher.action.run.addArgument="Add argument"
|
||||
AdvSceneSwitcher.action.run.addArgumentDescription="Add new argument:"
|
||||
AdvSceneSwitcher.action.run.entry="Run {{filePath}} {{browseButton}}"
|
||||
AdvSceneSwitcher.action.run.entry="Run {{filePath}}"
|
||||
AdvSceneSwitcher.action.sceneVisibility="Scene item visibility"
|
||||
AdvSceneSwitcher.action.sceneVisibility.type.show="Show"
|
||||
AdvSceneSwitcher.action.sceneVisibility.type.hide="Hide"
|
||||
@@ -270,8 +315,9 @@ AdvSceneSwitcher.action.hotkey.leftAlt="Left Alt"
|
||||
AdvSceneSwitcher.action.hotkey.rightAlt="Right Alt"
|
||||
AdvSceneSwitcher.action.hotkey.leftMeta="Left Meta"
|
||||
AdvSceneSwitcher.action.hotkey.rightMeta="Right Meta"
|
||||
AdvSceneSwitcher.action.hotkey.onlyOBS="Send keypress only to OBS"
|
||||
AdvSceneSwitcher.action.hotkey.disabled="Cannot simulate key presses - functionality disabled!"
|
||||
AdvSceneSwitcher.action.hotkey.entry="Press {{keys}}"
|
||||
AdvSceneSwitcher.action.hotkey.entry="Press {{keys}} for {{duration}} milliseconds"
|
||||
AdvSceneSwitcher.action.sceneOrder="Scene item order"
|
||||
AdvSceneSwitcher.action.sceneOrder.type.moveUp="Move up"
|
||||
AdvSceneSwitcher.action.sceneOrder.type.moveDown="Move down"
|
||||
@@ -285,7 +331,20 @@ AdvSceneSwitcher.action.sceneTransform.entry="On {{scenes}} transform {{sources}
|
||||
AdvSceneSwitcher.action.file="File"
|
||||
AdvSceneSwitcher.action.file.type.write="Write"
|
||||
AdvSceneSwitcher.action.file.type.append="Append"
|
||||
AdvSceneSwitcher.action.file.entry="{{actions}} to {{filePath}} {{browseButton}}:"
|
||||
AdvSceneSwitcher.action.file.entry="{{actions}} to {{filePath}}:"
|
||||
AdvSceneSwitcher.action.previewScene="Switch preview scene"
|
||||
AdvSceneSwitcher.action.previewScene.entry="Switch preview scene to {{scenes}}"
|
||||
AdvSceneSwitcher.action.SceneSwap="Swap scene (Studio mode)"
|
||||
AdvSceneSwitcher.action.SceneSwap.entry="Swap preview and program scene in studio mode"
|
||||
AdvSceneSwitcher.action.transition="Transition"
|
||||
AdvSceneSwitcher.action.transition.entry.line1="{{setType}}Set transition type to {{transitions}}"
|
||||
AdvSceneSwitcher.action.transition.entry.line2="{{setDuration}}Set transition duration to {{duration}}seconds"
|
||||
AdvSceneSwitcher.action.timer="Timer"
|
||||
AdvSceneSwitcher.action.timer.type.pause="Pause"
|
||||
AdvSceneSwitcher.action.timer.type.continue="Continue"
|
||||
AdvSceneSwitcher.action.timer.type.reset="Reset"
|
||||
AdvSceneSwitcher.action.timer.type.setTimeRemaining="Set time remaining of"
|
||||
AdvSceneSwitcher.action.timer.entry="{{timerAction}} timers on {{macros}} {{duration}}"
|
||||
|
||||
; Transition Tab
|
||||
AdvSceneSwitcher.transitionTab.title="Transition"
|
||||
@@ -457,6 +516,7 @@ AdvSceneSwitcher.videoTab.help="<html><head/><body><p>This tab will allow you to
|
||||
|
||||
; Network Tab
|
||||
AdvSceneSwitcher.networkTab.title="Network"
|
||||
AdvSceneSwitcher.networkTab.description="This tab will allow you to remotely control the active scene of another OBS instance.\nPlease note that the scene names have to match exactly on all OBS instances."
|
||||
AdvSceneSwitcher.networkTab.warning="Running the server outside of a local network will allow third parties to read the active scene."
|
||||
AdvSceneSwitcher.networkTab.DisabledWarning="This functionality unfortunately had to be disabled on macOS due to library incompatibilities when running the obs-websocket plugin in parallel."
|
||||
AdvSceneSwitcher.networkTab.server="Start server (Sends scene switch messages to all connected clients)"
|
||||
@@ -541,6 +601,7 @@ AdvSceneSwitcher.selectPreviousScene="Previous Scene"
|
||||
AdvSceneSwitcher.selectCurrentScene="Current Scene"
|
||||
AdvSceneSwitcher.selectAnyScene="Any Scene"
|
||||
AdvSceneSwitcher.currentTransition="Current Transition"
|
||||
AdvSceneSwitcher.anyTransition="Any Transition"
|
||||
AdvSceneSwitcher.selectTransition="--select transition--"
|
||||
AdvSceneSwitcher.selectWindow="--select window--"
|
||||
AdvSceneSwitcher.selectSource="--select source--"
|
||||
@@ -558,6 +619,8 @@ AdvSceneSwitcher.selectWindowTip="Use \"OBS\" to specify OBS window\nUse \"Task
|
||||
|
||||
AdvSceneSwitcher.status.active="Active"
|
||||
AdvSceneSwitcher.status.inactive="Inactive"
|
||||
AdvSceneSwitcher.running="Plugin running"
|
||||
AdvSceneSwitcher.stopped="Plugin stopped"
|
||||
|
||||
AdvSceneSwitcher.unit.milliseconds="milliseconds"
|
||||
AdvSceneSwitcher.unit.secends="seconds"
|
||||
|
||||
15452
data/res/cascadeClassifiers/haarcascade_eye.xml
Normal file
15452
data/res/cascadeClassifiers/haarcascade_eye.xml
Normal file
File diff suppressed because it is too large
Load Diff
33158
data/res/cascadeClassifiers/haarcascade_eye_tree_eyeglasses.xml
Normal file
33158
data/res/cascadeClassifiers/haarcascade_eye_tree_eyeglasses.xml
Normal file
File diff suppressed because it is too large
Load Diff
26161
data/res/cascadeClassifiers/haarcascade_frontalface_alt.xml
Normal file
26161
data/res/cascadeClassifiers/haarcascade_frontalface_alt.xml
Normal file
File diff suppressed because it is too large
Load Diff
23550
data/res/cascadeClassifiers/haarcascade_frontalface_alt2.xml
Normal file
23550
data/res/cascadeClassifiers/haarcascade_frontalface_alt2.xml
Normal file
File diff suppressed because it is too large
Load Diff
103493
data/res/cascadeClassifiers/haarcascade_frontalface_alt_tree.xml
Normal file
103493
data/res/cascadeClassifiers/haarcascade_frontalface_alt_tree.xml
Normal file
File diff suppressed because it is too large
Load Diff
35712
data/res/cascadeClassifiers/haarcascade_frontalface_default.xml
Normal file
35712
data/res/cascadeClassifiers/haarcascade_frontalface_default.xml
Normal file
File diff suppressed because it is too large
Load Diff
18118
data/res/cascadeClassifiers/haarcascade_fullbody.xml
Normal file
18118
data/res/cascadeClassifiers/haarcascade_fullbody.xml
Normal file
File diff suppressed because it is too large
Load Diff
9803
data/res/cascadeClassifiers/haarcascade_lefteye_2splits.xml
Normal file
9803
data/res/cascadeClassifiers/haarcascade_lefteye_2splits.xml
Normal file
File diff suppressed because it is too large
Load Diff
15085
data/res/cascadeClassifiers/haarcascade_lowerbody.xml
Normal file
15085
data/res/cascadeClassifiers/haarcascade_lowerbody.xml
Normal file
File diff suppressed because it is too large
Load Diff
31930
data/res/cascadeClassifiers/haarcascade_profileface.xml
Normal file
31930
data/res/cascadeClassifiers/haarcascade_profileface.xml
Normal file
File diff suppressed because it is too large
Load Diff
9833
data/res/cascadeClassifiers/haarcascade_righteye_2splits.xml
Normal file
9833
data/res/cascadeClassifiers/haarcascade_righteye_2splits.xml
Normal file
File diff suppressed because it is too large
Load Diff
29767
data/res/cascadeClassifiers/haarcascade_upperbody.xml
Normal file
29767
data/res/cascadeClassifiers/haarcascade_upperbody.xml
Normal file
File diff suppressed because it is too large
Load Diff
1
deps/opencv
vendored
Submodule
1
deps/opencv
vendored
Submodule
Submodule deps/opencv added at ad6e82942b
107
external/FindLibObs.cmake
vendored
107
external/FindLibObs.cmake
vendored
@@ -1,107 +0,0 @@
|
||||
# This module can be copied and used by external plugins for OBS
|
||||
#
|
||||
# Once done these will be defined:
|
||||
#
|
||||
# LIBOBS_FOUND
|
||||
# LIBOBS_INCLUDE_DIRS
|
||||
# LIBOBS_LIBRARIES
|
||||
|
||||
find_package(PkgConfig QUIET)
|
||||
if (PKG_CONFIG_FOUND)
|
||||
pkg_check_modules(_OBS QUIET obs libobs)
|
||||
endif()
|
||||
|
||||
if(CMAKE_SIZEOF_VOID_P EQUAL 8)
|
||||
set(_lib_suffix 64)
|
||||
else()
|
||||
set(_lib_suffix 32)
|
||||
endif()
|
||||
|
||||
if(DEFINED CMAKE_BUILD_TYPE)
|
||||
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
|
||||
set(_build_type_base "debug")
|
||||
else()
|
||||
set(_build_type_base "release")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
find_path(LIBOBS_INCLUDE_DIR
|
||||
NAMES obs.h
|
||||
HINTS
|
||||
ENV obsPath${_lib_suffix}
|
||||
ENV obsPath
|
||||
${obsPath}
|
||||
PATHS
|
||||
/usr/include /usr/local/include /opt/local/include /sw/include
|
||||
PATH_SUFFIXES
|
||||
libobs
|
||||
)
|
||||
|
||||
function(find_obs_lib base_name repo_build_path lib_name)
|
||||
string(TOUPPER "${base_name}" base_name_u)
|
||||
|
||||
if(DEFINED _build_type_base)
|
||||
set(_build_type_${repo_build_path} "${_build_type_base}/${repo_build_path}")
|
||||
set(_build_type_${repo_build_path}${_lib_suffix} "${_build_type_base}${_lib_suffix}/${repo_build_path}")
|
||||
endif()
|
||||
|
||||
find_library(${base_name_u}_LIB
|
||||
NAMES ${_${base_name_u}_LIBRARIES} ${lib_name} lib${lib_name}
|
||||
HINTS
|
||||
ENV obsPath${_lib_suffix}
|
||||
ENV obsPath
|
||||
${obsPath}
|
||||
${_${base_name_u}_LIBRARY_DIRS}
|
||||
PATHS
|
||||
/usr/lib /usr/local/lib /opt/local/lib /sw/lib
|
||||
PATH_SUFFIXES
|
||||
lib${_lib_suffix} lib
|
||||
libs${_lib_suffix} libs
|
||||
bin${_lib_suffix} bin
|
||||
../lib${_lib_suffix} ../lib
|
||||
../libs${_lib_suffix} ../libs
|
||||
../bin${_lib_suffix} ../bin
|
||||
# base repo non-msvc-specific search paths
|
||||
${_build_type_${repo_build_path}}
|
||||
${_build_type_${repo_build_path}${_lib_suffix}}
|
||||
build/${repo_build_path}
|
||||
build${_lib_suffix}/${repo_build_path}
|
||||
# base repo msvc-specific search paths on windows
|
||||
build${_lib_suffix}/${repo_build_path}/Debug
|
||||
build${_lib_suffix}/${repo_build_path}/RelWithDebInfo
|
||||
build/${repo_build_path}/Debug
|
||||
build/${repo_build_path}/RelWithDebInfo
|
||||
)
|
||||
endfunction()
|
||||
|
||||
find_obs_lib(LIBOBS libobs obs)
|
||||
|
||||
if(MSVC)
|
||||
find_obs_lib(W32_PTHREADS deps/w32-pthreads w32-pthreads)
|
||||
endif()
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args(Libobs DEFAULT_MSG LIBOBS_LIB LIBOBS_INCLUDE_DIR)
|
||||
mark_as_advanced(LIBOBS_INCLUDE_DIR LIBOBS_LIB)
|
||||
|
||||
if(LIBOBS_FOUND)
|
||||
if(MSVC)
|
||||
if (NOT DEFINED W32_PTHREADS_LIB)
|
||||
message(FATAL_ERROR "Could not find the w32-pthreads library" )
|
||||
endif()
|
||||
|
||||
set(W32_PTHREADS_INCLUDE_DIR ${LIBOBS_INCLUDE_DIR}/../deps/w32-pthreads)
|
||||
endif()
|
||||
|
||||
set(LIBOBS_INCLUDE_DIRS ${LIBOBS_INCLUDE_DIR} ${W32_PTHREADS_INCLUDE_DIR})
|
||||
set(LIBOBS_LIBRARIES ${LIBOBS_LIB} ${W32_PTHREADS_LIB})
|
||||
include(${LIBOBS_INCLUDE_DIR}/../cmake/external/ObsPluginHelpers.cmake)
|
||||
|
||||
# allows external plugins to easily use/share common dependencies that are often included with libobs (such as FFmpeg)
|
||||
if(NOT DEFINED INCLUDED_LIBOBS_CMAKE_MODULES)
|
||||
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${LIBOBS_INCLUDE_DIR}/../cmake/Modules/")
|
||||
set(INCLUDED_LIBOBS_CMAKE_MODULES true)
|
||||
endif()
|
||||
else()
|
||||
message(FATAL_ERROR "Could not find the libobs library" )
|
||||
endif()
|
||||
163
external/ObsPluginHelpers.cmake
vendored
163
external/ObsPluginHelpers.cmake
vendored
@@ -1,163 +0,0 @@
|
||||
# Functions for generating external plugins
|
||||
|
||||
set(EXTERNAL_PLUGIN_OUTPUT_DIR "${CMAKE_BINARY_DIR}/rundir")
|
||||
|
||||
# Fix XCode includes to ignore warnings on system includes
|
||||
function(target_include_directories_system _target)
|
||||
if(XCODE)
|
||||
foreach(_arg ${ARGN})
|
||||
if("${_arg}" STREQUAL "PRIVATE" OR "${_arg}" STREQUAL "PUBLIC" OR "${_arg}" STREQUAL "INTERFACE")
|
||||
set(_scope ${_arg})
|
||||
else()
|
||||
target_compile_options(${_target} ${_scope} -isystem${_arg})
|
||||
endif()
|
||||
endforeach()
|
||||
else()
|
||||
target_include_directories(${_target} SYSTEM ${_scope} ${ARGN})
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
function(install_external_plugin_data_internal target source_dir target_dir)
|
||||
install(DIRECTORY ${source_dir}/
|
||||
DESTINATION "${target}/${target_dir}"
|
||||
USE_SOURCE_PERMISSIONS)
|
||||
add_custom_command(TARGET ${target} POST_BUILD
|
||||
COMMAND "${CMAKE_COMMAND}" -E copy_directory
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/${source_dir}" "${EXTERNAL_PLUGIN_OUTPUT_DIR}/$<CONFIGURATION>/${target}/${target_dir}"
|
||||
VERBATIM)
|
||||
endfunction()
|
||||
|
||||
# Installs data
|
||||
# 'target' is the destination target project being installed to
|
||||
# 'data_loc' specifies the directory of the data
|
||||
function(install_external_plugin_data target data_loc)
|
||||
install_external_plugin_data_internal(${target} ${data_loc} "data")
|
||||
endfunction()
|
||||
|
||||
# Installs data in an architecture-specific data directory on windows/linux (data/32bit or data/64bit). Does not apply for mac.
|
||||
# 'target' is the destination target project being installed to
|
||||
# 'data_loc' specifies the directory of the data being installed
|
||||
function(install_external_plugin_arch_data target data_loc)
|
||||
if(APPLE)
|
||||
set(_bit_suffix "")
|
||||
elseif(CMAKE_SIZEOF_VOID_P EQUAL 8)
|
||||
set(_bit_suffix "/64bit")
|
||||
else()
|
||||
set(_bit_suffix "/32bit")
|
||||
endif()
|
||||
|
||||
install_external_plugin_data_internal(${target} ${data_loc} "data${_bit_suffix}")
|
||||
endfunction()
|
||||
|
||||
# Installs data in the target's bin directory
|
||||
# 'target' is the destination target project being installed to
|
||||
# 'data_loc' specifies the directory of the data being installed
|
||||
function(install_external_plugin_data_to_bin target data_loc)
|
||||
if(APPLE)
|
||||
set(_bit_suffix "")
|
||||
elseif(CMAKE_SIZEOF_VOID_P EQUAL 8)
|
||||
set(_bit_suffix "/64bit")
|
||||
else()
|
||||
set(_bit_suffix "/32bit")
|
||||
endif()
|
||||
|
||||
install_external_plugin_data_internal(${target} ${data_loc} "bin${_bit_suffix}")
|
||||
endfunction()
|
||||
|
||||
# Installs an additional binary to a target
|
||||
# 'target' is the destination target project being installed to
|
||||
# 'additional_target' specifies the additional binary
|
||||
function(install_external_plugin_additional target additional_target)
|
||||
if(APPLE)
|
||||
set(_bit_suffix "")
|
||||
elseif(CMAKE_SIZEOF_VOID_P EQUAL 8)
|
||||
set(_bit_suffix "64bit/")
|
||||
else()
|
||||
set(_bit_suffix "32bit/")
|
||||
endif()
|
||||
|
||||
set_target_properties(${additional_target} PROPERTIES
|
||||
PREFIX "")
|
||||
|
||||
install(TARGETS ${additional_target}
|
||||
LIBRARY DESTINATION "bin"
|
||||
RUNTIME DESTINATION "bin")
|
||||
add_custom_command(TARGET ${additional_target} POST_BUILD
|
||||
COMMAND "${CMAKE_COMMAND}" -E copy
|
||||
"$<TARGET_FILE:${additional_target}>"
|
||||
"${EXTERNAL_PLUGIN_OUTPUT_DIR}/$<CONFIGURATION>/${target}/bin/${_bit_suffix}$<TARGET_FILE_NAME:${additional_target}>"
|
||||
VERBATIM)
|
||||
endfunction()
|
||||
|
||||
# Installs the binary of the target
|
||||
# 'target' is the target project being installed
|
||||
function(install_external_plugin target)
|
||||
install_external_plugin_additional(${target} ${target})
|
||||
endfunction()
|
||||
|
||||
# Installs the binary and data of the target
|
||||
# 'target' is the destination target project being installed to
|
||||
function(install_external_plugin_with_data target data_loc)
|
||||
install_external_plugin(${target})
|
||||
install_external_plugin_data(${target} ${data_loc})
|
||||
endfunction()
|
||||
|
||||
# Installs an additional binary to the data of a target
|
||||
# 'target' is the destination target project being installed to
|
||||
# 'additional_target' specifies the additional binary
|
||||
function(install_external_plugin_bin_to_data target additional_target)
|
||||
install(TARGETS ${additional_target}
|
||||
LIBRARY DESTINATION "data"
|
||||
RUNTIME DESTINATION "data")
|
||||
add_custom_command(TARGET ${additional_target} POST_BUILD
|
||||
COMMAND "${CMAKE_COMMAND}" -E copy
|
||||
"$<TARGET_FILE:${additional_target}>"
|
||||
"${EXTERNAL_PLUGIN_OUTPUT_DIR}/$<CONFIGURATION>/${target}/data/$<TARGET_FILE_NAME:${additional_target}>"
|
||||
VERBATIM)
|
||||
endfunction()
|
||||
|
||||
# Installs an additional binary in an architecture-specific data directory on windows/linux (data/32bit or data/64bit). Does not apply for mac.
|
||||
# 'target' is the destination target project being installed to
|
||||
# 'additional_target' specifies the additional binary
|
||||
function(install_external_plugin_bin_to_arch_data target additional_target)
|
||||
if(APPLE)
|
||||
set(_bit_suffix "")
|
||||
elseif(CMAKE_SIZEOF_VOID_P EQUAL 8)
|
||||
set(_bit_suffix "/64bit")
|
||||
else()
|
||||
set(_bit_suffix "/32bit")
|
||||
endif()
|
||||
|
||||
install(TARGETS ${additional_target}
|
||||
LIBRARY DESTINATION "data${_bit_suffix}"
|
||||
RUNTIME DESTINATION "data${_bit_suffix}")
|
||||
add_custom_command(TARGET ${additional_target} POST_BUILD
|
||||
COMMAND "${CMAKE_COMMAND}" -E copy
|
||||
"$<TARGET_FILE:${additional_target}>"
|
||||
"${EXTERNAL_PLUGIN_OUTPUT_DIR}/$<CONFIGURATION>/${target}/data${_bit_suffix}/$<TARGET_FILE_NAME:${additional_target}>"
|
||||
VERBATIM)
|
||||
endfunction()
|
||||
|
||||
# Installs an additional file in an architecture-specific data directory on windows/linux (data/32bit or data/64bit). Does not apply for mac.
|
||||
# 'target' is the destination target project being installed to
|
||||
# 'additional_target' specifies the additional binary
|
||||
function(install_external_plugin_data_file_to_arch_data target additional_target file_target)
|
||||
if(APPLE)
|
||||
set(_bit_suffix "")
|
||||
elseif(CMAKE_SIZEOF_VOID_P EQUAL 8)
|
||||
set(_bit_suffix "/64bit")
|
||||
else()
|
||||
set(_bit_suffix "/32bit")
|
||||
endif()
|
||||
|
||||
get_filename_component(file_target_name ${file_target} NAME)
|
||||
|
||||
install(TARGETS ${additional_target}
|
||||
LIBRARY DESTINATION "data${_bit_suffix}"
|
||||
RUNTIME DESTINATION "data${_bit_suffix}")
|
||||
add_custom_command(TARGET ${additional_target} POST_BUILD
|
||||
COMMAND "${CMAKE_COMMAND}" -E copy
|
||||
"${file_target}"
|
||||
"${EXTERNAL_PLUGIN_OUTPUT_DIR}/$<CONFIGURATION>/${target}/data${_bit_suffix}/${file_target_name}"
|
||||
VERBATIM)
|
||||
endfunction()
|
||||
@@ -258,13 +258,6 @@
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="Line" name="line_6">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_52">
|
||||
<item>
|
||||
@@ -313,6 +306,30 @@
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||
<item>
|
||||
<widget class="QCheckBox" name="showTrayNotifications">
|
||||
<property name="text">
|
||||
<string>AdvSceneSwitcher.generalTab.generalBehavior.showTrayNotifications</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_2">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_48">
|
||||
<item>
|
||||
@@ -364,19 +381,6 @@
|
||||
<item>
|
||||
<widget class="QListWidget" name="priorityList"/>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_30">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
@@ -3716,6 +3720,13 @@
|
||||
<string>AdvSceneSwitcher.networkTab.title</string>
|
||||
</attribute>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_30">
|
||||
<item>
|
||||
<widget class="QLabel" name="networkTabDescription">
|
||||
<property name="text">
|
||||
<string>AdvSceneSwitcher.networkTab.description</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="networkTabWarningLabel">
|
||||
<property name="text">
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#include <QAction>
|
||||
#include <QFileDialog>
|
||||
#include <regex>
|
||||
#include <filesystem>
|
||||
|
||||
#include <obs-module.h>
|
||||
#include <obs-frontend-api.h>
|
||||
@@ -36,9 +37,16 @@ bool translationAvailable()
|
||||
void AdvSceneSwitcher::loadUI()
|
||||
{
|
||||
if (!translationAvailable()) {
|
||||
(void)DisplayMessage(
|
||||
"Failed to find plug-in's 'data' directory.\n"
|
||||
"Please check installation instructions!");
|
||||
QString msg = "Failed to find plug-in's 'data' directory.\n"
|
||||
"Please check installation instructions!\n\n"
|
||||
"Data most likely expected at:\n\n";
|
||||
#ifdef _WIN32
|
||||
msg += QString::fromStdString(
|
||||
(std::filesystem::current_path().string()));
|
||||
msg += "/";
|
||||
#endif
|
||||
msg += obs_get_module_data_path(obs_current_module());
|
||||
(void)DisplayMessage(msg);
|
||||
}
|
||||
|
||||
#if __APPLE__
|
||||
@@ -104,33 +112,23 @@ static void SaveSceneSwitcher(obs_data_t *save_data, bool saving, void *)
|
||||
{
|
||||
if (saving) {
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
|
||||
switcher->Prune();
|
||||
|
||||
obs_data_t *obj = obs_data_create();
|
||||
|
||||
switcher->saveSettings(obj);
|
||||
|
||||
obs_data_set_obj(save_data, "advanced-scene-switcher", obj);
|
||||
|
||||
obs_data_release(obj);
|
||||
} else {
|
||||
switcher->m.lock();
|
||||
|
||||
obs_data_t *obj =
|
||||
obs_data_get_obj(save_data, "advanced-scene-switcher");
|
||||
|
||||
if (!obj) {
|
||||
obj = obs_data_create();
|
||||
}
|
||||
if (switcher->versionChanged(obj, g_GIT_SHA1)) {
|
||||
AskBackup(obj);
|
||||
}
|
||||
|
||||
switcher->loadSettings(obj);
|
||||
|
||||
obs_data_release(obj);
|
||||
|
||||
switcher->m.unlock();
|
||||
|
||||
// Stop the scene switcher at least once to
|
||||
@@ -166,9 +164,7 @@ void SwitcherData::Thread()
|
||||
bool setPrevSceneAfterLinger = false;
|
||||
bool macroMatch = false;
|
||||
macroSceneSwitched = false;
|
||||
|
||||
endTime = std::chrono::high_resolution_clock::now();
|
||||
|
||||
auto runTime =
|
||||
std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||
endTime - startTime);
|
||||
@@ -232,12 +228,17 @@ void SwitcherData::Thread()
|
||||
}
|
||||
}
|
||||
|
||||
// After this point we will call frontend functions
|
||||
// After this point we will call frontend functions like
|
||||
// obs_frontend_set_current_scene() and
|
||||
// obs_frontend_set_current_transition()
|
||||
//
|
||||
// During this time SaveSceneSwitcher() could be called
|
||||
// leading to a deadlock, so we have to unlock()
|
||||
// leading to a deadlock with the frontend function being stuck
|
||||
// in QMetaObject::invokeMethod() holding the mutex and
|
||||
// OBS being stuck in SaveSceneSwitcher().
|
||||
//
|
||||
// So we have to unlock() risking race conditions as these are
|
||||
// less frequent than the above described deadlock
|
||||
lock.unlock();
|
||||
|
||||
if (match) {
|
||||
@@ -360,8 +361,7 @@ void switchScene(const sceneSwitchInfo &sceneSwitch)
|
||||
}
|
||||
|
||||
obs_source_t *source = obs_weak_source_get_source(sceneSwitch.scene);
|
||||
obs_source_t *currentSource =
|
||||
obs_weak_source_get_source(switcher->currentScene);
|
||||
obs_source_t *currentSource = obs_frontend_get_current_scene();
|
||||
|
||||
if (source && source != currentSource) {
|
||||
transitionData currentTransitionData;
|
||||
@@ -411,6 +411,12 @@ void SwitcherData::Start()
|
||||
if (networkConfig.ClientEnabled) {
|
||||
client.connect(networkConfig.GetClientUri());
|
||||
}
|
||||
|
||||
if (showSystemTrayNotifications) {
|
||||
DisplayTrayMessage(
|
||||
obs_module_text("AdvSceneSwitcher.pluginName"),
|
||||
obs_module_text("AdvSceneSwitcher.running"));
|
||||
}
|
||||
}
|
||||
|
||||
void ResetMacroCounters()
|
||||
@@ -424,8 +430,9 @@ void SwitcherData::Stop()
|
||||
{
|
||||
if (th && th->isRunning()) {
|
||||
stop = true;
|
||||
transitionCv.notify_one();
|
||||
cv.notify_one();
|
||||
abortMacroWait = true;
|
||||
macroWaitCv.notify_one();
|
||||
th->wait();
|
||||
delete th;
|
||||
th = nullptr;
|
||||
@@ -436,6 +443,12 @@ void SwitcherData::Stop()
|
||||
|
||||
server.stop();
|
||||
client.disconnect();
|
||||
|
||||
if (showSystemTrayNotifications) {
|
||||
DisplayTrayMessage(
|
||||
obs_module_text("AdvSceneSwitcher.pluginName"),
|
||||
obs_module_text("AdvSceneSwitcher.stopped"));
|
||||
}
|
||||
}
|
||||
|
||||
void SwitcherData::setWaitScene()
|
||||
@@ -475,6 +488,9 @@ extern "C" void FreeSceneSwitcher()
|
||||
|
||||
void handleSceneChange()
|
||||
{
|
||||
switcher->lastSceneChangeTime =
|
||||
std::chrono::high_resolution_clock::now();
|
||||
|
||||
// Stop waiting if scene was changed
|
||||
if (switcher->sceneChangedDuringWait()) {
|
||||
switcher->cv.notify_one();
|
||||
@@ -544,6 +560,24 @@ void setReplayBufferSaved()
|
||||
switcher->replayBufferSaved = true;
|
||||
}
|
||||
|
||||
void setTranstionEnd()
|
||||
{
|
||||
switcher->lastTransitionEndTime =
|
||||
std::chrono::high_resolution_clock::now();
|
||||
}
|
||||
|
||||
void setStreamStarting()
|
||||
{
|
||||
switcher->lastStreamStartingTime =
|
||||
std::chrono::high_resolution_clock::now();
|
||||
}
|
||||
|
||||
void setStreamStopping()
|
||||
{
|
||||
switcher->lastStreamStoppingTime =
|
||||
std::chrono::high_resolution_clock::now();
|
||||
}
|
||||
|
||||
// Note to future self:
|
||||
// be careful using switcher->m here as there is potential for deadlocks when using
|
||||
// frontend functions such as obs_frontend_set_current_scene()
|
||||
@@ -575,9 +609,20 @@ static void OBSEvent(enum obs_frontend_event event, void *switcher)
|
||||
case OBS_FRONTEND_EVENT_STREAMING_STOPPED:
|
||||
resetLiveTime();
|
||||
break;
|
||||
#ifdef REPLAYBUFFER_SUPPORTED
|
||||
case OBS_FRONTEND_EVENT_REPLAY_BUFFER_SAVED:
|
||||
setReplayBufferSaved();
|
||||
break;
|
||||
#endif
|
||||
case OBS_FRONTEND_EVENT_TRANSITION_STOPPED:
|
||||
setTranstionEnd();
|
||||
break;
|
||||
case OBS_FRONTEND_EVENT_STREAMING_STARTING:
|
||||
setStreamStarting();
|
||||
break;
|
||||
case OBS_FRONTEND_EVENT_STREAMING_STOPPING:
|
||||
setStreamStopping();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -585,6 +630,7 @@ static void OBSEvent(enum obs_frontend_event event, void *switcher)
|
||||
|
||||
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(
|
||||
|
||||
@@ -46,6 +46,13 @@ double Duration::TimeRemaining()
|
||||
return (seconds * 1000 - runTime.count()) / 1000.;
|
||||
}
|
||||
|
||||
void Duration::SetTimeRemaining(double remaining)
|
||||
{
|
||||
long long msPassed = (seconds - remaining) * 1000;
|
||||
_startTime = std::chrono::high_resolution_clock::now() -
|
||||
std::chrono::milliseconds(msPassed);
|
||||
}
|
||||
|
||||
void Duration::Reset()
|
||||
{
|
||||
_startTime = {};
|
||||
|
||||
49
src/file-selection.cpp
Normal file
49
src/file-selection.cpp
Normal file
@@ -0,0 +1,49 @@
|
||||
#include "headers/file-selection.hpp"
|
||||
|
||||
#include <obs-module.h>
|
||||
#include <QLayout>
|
||||
#include <QFileDialog>
|
||||
|
||||
FileSelection::FileSelection(FileSelection::Type type, QWidget *parent)
|
||||
: _type(type), QWidget(parent)
|
||||
{
|
||||
_filePath = new QLineEdit();
|
||||
_browseButton =
|
||||
new QPushButton(obs_module_text("AdvSceneSwitcher.browse"));
|
||||
|
||||
QWidget::connect(_filePath, SIGNAL(editingFinished()), this,
|
||||
SLOT(PathChange()));
|
||||
QWidget::connect(_browseButton, SIGNAL(clicked()), this,
|
||||
SLOT(BrowseButtonClicked()));
|
||||
QHBoxLayout *layout = new QHBoxLayout;
|
||||
layout->addWidget(_filePath);
|
||||
layout->addWidget(_browseButton);
|
||||
layout->setContentsMargins(0, 0, 0, 0);
|
||||
setLayout(layout);
|
||||
}
|
||||
|
||||
void FileSelection::SetPath(const QString &path)
|
||||
{
|
||||
_filePath->setText(path);
|
||||
}
|
||||
|
||||
void FileSelection::BrowseButtonClicked()
|
||||
{
|
||||
QString path;
|
||||
if (_type == FileSelection::Type::WRITE) {
|
||||
path = QFileDialog::getSaveFileName(this);
|
||||
} else {
|
||||
path = QFileDialog::getOpenFileName(this);
|
||||
}
|
||||
if (path.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
_filePath->setText(path);
|
||||
emit PathChanged(path);
|
||||
}
|
||||
|
||||
void FileSelection::PathChange()
|
||||
{
|
||||
emit PathChanged(_filePath->text());
|
||||
}
|
||||
@@ -3,12 +3,21 @@
|
||||
#include "headers/version.h"
|
||||
|
||||
#include <QFileDialog>
|
||||
#include <QGuiApplication>
|
||||
#if __linux__
|
||||
#include <QDesktopWidget>
|
||||
#endif
|
||||
|
||||
QMetaObject::Connection inactivePluse;
|
||||
|
||||
void AdvSceneSwitcher::on_close_clicked()
|
||||
{
|
||||
done(0);
|
||||
close();
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::reject()
|
||||
{
|
||||
close();
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::UpdateNonMatchingScene(const QString &name)
|
||||
@@ -201,6 +210,15 @@ void AdvSceneSwitcher::on_saveWindowGeo_stateChanged(int state)
|
||||
switcher->saveWindowGeo = state;
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::on_showTrayNotifications_stateChanged(int state)
|
||||
{
|
||||
if (loading) {
|
||||
return;
|
||||
}
|
||||
|
||||
switcher->showSystemTrayNotifications = state;
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::on_uiHintsDisable_stateChanged(int state)
|
||||
{
|
||||
if (loading) {
|
||||
@@ -377,9 +395,23 @@ 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) {
|
||||
if (switcher->saveWindowGeo && windowPosValid(switcher->windowPos)) {
|
||||
this->resize(switcher->windowSize);
|
||||
this->move(switcher->windowPos);
|
||||
}
|
||||
@@ -480,6 +512,8 @@ void SwitcherData::saveGeneralSettings(obs_data_t *obj)
|
||||
static_cast<int>(autoStartEvent));
|
||||
|
||||
obs_data_set_bool(obj, "verbose", verbose);
|
||||
obs_data_set_bool(obj, "showSystemTrayNotifications",
|
||||
showSystemTrayNotifications);
|
||||
obs_data_set_bool(obj, "disableHints", disableHints);
|
||||
|
||||
obs_data_set_int(obj, "priority0", functionNamesByPriority[0]);
|
||||
@@ -551,6 +585,8 @@ void SwitcherData::loadGeneralSettings(obs_data_t *obj)
|
||||
obs_data_get_int(obj, "autoStartEvent"));
|
||||
|
||||
verbose = obs_data_get_bool(obj, "verbose");
|
||||
showSystemTrayNotifications =
|
||||
obs_data_get_bool(obj, "showSystemTrayNotifications");
|
||||
disableHints = obs_data_get_bool(obj, "disableHints");
|
||||
|
||||
obs_data_set_default_int(obj, "priority0", default_priority_0);
|
||||
@@ -777,6 +813,8 @@ void AdvSceneSwitcher::setupGeneralTab()
|
||||
|
||||
ui->verboseLogging->setChecked(switcher->verbose);
|
||||
ui->saveWindowGeo->setChecked(switcher->saveWindowGeo);
|
||||
ui->showTrayNotifications->setChecked(
|
||||
switcher->showSystemTrayNotifications);
|
||||
ui->uiHintsDisable->setChecked(switcher->disableHints);
|
||||
|
||||
for (int p : switcher->functionNamesByPriority) {
|
||||
@@ -864,4 +902,6 @@ void AdvSceneSwitcher::setupGeneralTab()
|
||||
QTimer *statusTimer = new QTimer(this);
|
||||
connect(statusTimer, SIGNAL(timeout()), this, SLOT(updateStatus()));
|
||||
statusTimer->start(1000);
|
||||
|
||||
setWindowTitle(windowTitle() + " - " + g_GIT_TAG);
|
||||
}
|
||||
|
||||
@@ -30,6 +30,7 @@ public:
|
||||
|
||||
AdvSceneSwitcher(QWidget *parent);
|
||||
|
||||
void reject() override;
|
||||
void closeEvent(QCloseEvent *event) override;
|
||||
|
||||
void SetStarted();
|
||||
@@ -49,6 +50,10 @@ public:
|
||||
bool addNewMacro(std::string &name);
|
||||
Macro *getSelectedMacro();
|
||||
void SetEditMacro(Macro &m);
|
||||
void HighlightAction(int idx);
|
||||
void HighlightCondition(int idx);
|
||||
void PopulateMacroActions(Macro &m, int afterIdx = 0);
|
||||
void PopulateMacroConditions(Macro &m, int afterIdx = 0);
|
||||
void ConnectControlSignals(MacroActionEdit *);
|
||||
void ConnectControlSignals(MacroConditionEdit *);
|
||||
void SwapActions(Macro *m, int pos1, int pos2);
|
||||
@@ -159,6 +164,7 @@ public slots:
|
||||
|
||||
void on_verboseLogging_stateChanged(int state);
|
||||
void on_saveWindowGeo_stateChanged(int state);
|
||||
void on_showTrayNotifications_stateChanged(int state);
|
||||
void on_uiHintsDisable_stateChanged(int state);
|
||||
|
||||
void on_exportSettings_clicked();
|
||||
|
||||
@@ -22,6 +22,7 @@ public:
|
||||
|
||||
bool DurationReached();
|
||||
double TimeRemaining();
|
||||
void SetTimeRemaining(double);
|
||||
void Reset();
|
||||
std::string ToString();
|
||||
|
||||
|
||||
30
src/headers/file-selection.hpp
Normal file
30
src/headers/file-selection.hpp
Normal file
@@ -0,0 +1,30 @@
|
||||
#pragma once
|
||||
#include <QLineEdit>
|
||||
#include <QPushButton>
|
||||
#include <QWidget>
|
||||
|
||||
class FileSelection : public QWidget {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
enum class Type {
|
||||
READ,
|
||||
WRITE,
|
||||
};
|
||||
|
||||
FileSelection(FileSelection::Type type = FileSelection::Type::READ,
|
||||
QWidget *parent = 0);
|
||||
void SetPath(const QString &);
|
||||
QPushButton *Button() { return _browseButton; }
|
||||
|
||||
private slots:
|
||||
void BrowseButtonClicked();
|
||||
void PathChange();
|
||||
signals:
|
||||
void PathChanged(const QString &);
|
||||
|
||||
private:
|
||||
Type _type;
|
||||
QLineEdit *_filePath;
|
||||
QPushButton *_browseButton;
|
||||
};
|
||||
@@ -2,6 +2,7 @@
|
||||
#include <QSpinBox>
|
||||
#include <QPlainTextEdit>
|
||||
#include "macro-action-edit.hpp"
|
||||
#include "file-selection.hpp"
|
||||
|
||||
enum class FileAction {
|
||||
WRITE,
|
||||
@@ -47,16 +48,14 @@ public:
|
||||
}
|
||||
|
||||
private slots:
|
||||
void FilePathChanged();
|
||||
void BrowseButtonClicked();
|
||||
void PathChanged(const QString &text);
|
||||
void TextChanged();
|
||||
void ActionChanged(int value);
|
||||
signals:
|
||||
void HeaderInfoChanged(const QString &);
|
||||
|
||||
protected:
|
||||
QLineEdit *_filePath;
|
||||
QPushButton *_browseButton;
|
||||
FileSelection *_filePath;
|
||||
QPlainTextEdit *_text;
|
||||
QComboBox *_actions;
|
||||
std::shared_ptr<MacroActionFile> _entryData;
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include "hotkey.hpp"
|
||||
|
||||
#include <QCheckBox>
|
||||
#include <QSpinBox>
|
||||
#include <QHBoxLayout>
|
||||
|
||||
class MacroActionHotkey : public MacroAction {
|
||||
@@ -27,6 +28,12 @@ public:
|
||||
bool _rightAlt = false;
|
||||
bool _leftMeta = false;
|
||||
bool _rightMeta = false;
|
||||
int _duration = 300; // in ms
|
||||
#ifdef __APPLE__
|
||||
bool _onlySendToObs = true;
|
||||
#else
|
||||
bool _onlySendToObs = false;
|
||||
#endif
|
||||
|
||||
private:
|
||||
static bool _registered;
|
||||
@@ -59,6 +66,8 @@ private slots:
|
||||
void RAltChanged(int state);
|
||||
void LMetaChanged(int state);
|
||||
void RMetaChanged(int state);
|
||||
void DurationChanged(int ms);
|
||||
void OnlySendToOBSChanged(int state);
|
||||
|
||||
protected:
|
||||
QComboBox *_keys;
|
||||
@@ -70,10 +79,14 @@ protected:
|
||||
QCheckBox *_rightAlt;
|
||||
QCheckBox *_leftMeta;
|
||||
QCheckBox *_rightMeta;
|
||||
QSpinBox *_duration;
|
||||
QCheckBox *_onlySendToOBS;
|
||||
QLabel *_noKeyPressSimulationWarning;
|
||||
|
||||
std::shared_ptr<MacroActionHotkey> _entryData;
|
||||
|
||||
private:
|
||||
void SetWarningVisibility();
|
||||
QHBoxLayout *_mainLayout;
|
||||
bool _loading = true;
|
||||
};
|
||||
|
||||
54
src/headers/macro-action-preview-scene.hpp
Normal file
54
src/headers/macro-action-preview-scene.hpp
Normal file
@@ -0,0 +1,54 @@
|
||||
#pragma once
|
||||
#include "macro-action-edit.hpp"
|
||||
#include "scene-selection.hpp"
|
||||
|
||||
class MacroActionPreviewScene : 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<MacroActionPreviewScene>();
|
||||
}
|
||||
|
||||
SceneSelection _scene;
|
||||
|
||||
private:
|
||||
static bool _registered;
|
||||
static const std::string id;
|
||||
};
|
||||
|
||||
class MacroActionPreviewSceneEdit : public QWidget {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
MacroActionPreviewSceneEdit(
|
||||
QWidget *parent,
|
||||
std::shared_ptr<MacroActionPreviewScene> entryData = nullptr);
|
||||
void UpdateEntryData();
|
||||
static QWidget *Create(QWidget *parent,
|
||||
std::shared_ptr<MacroAction> action)
|
||||
{
|
||||
return new MacroActionPreviewSceneEdit(
|
||||
parent,
|
||||
std::dynamic_pointer_cast<MacroActionPreviewScene>(
|
||||
action));
|
||||
}
|
||||
|
||||
private slots:
|
||||
void SceneChanged(const SceneSelection &);
|
||||
signals:
|
||||
void HeaderInfoChanged(const QString &);
|
||||
|
||||
protected:
|
||||
SceneSelectionWidget *_scenes;
|
||||
std::shared_ptr<MacroActionPreviewScene> _entryData;
|
||||
|
||||
private:
|
||||
QHBoxLayout *_mainLayout;
|
||||
bool _loading = true;
|
||||
};
|
||||
@@ -1,3 +1,5 @@
|
||||
#ifdef REPLAYBUFFER_SUPPORTED
|
||||
|
||||
#pragma once
|
||||
#include "macro-action-edit.hpp"
|
||||
#include "duration-control.hpp"
|
||||
@@ -65,3 +67,5 @@ private:
|
||||
QHBoxLayout *_mainLayout;
|
||||
bool _loading = true;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#pragma once
|
||||
#include "macro-action-edit.hpp"
|
||||
#include "file-selection.hpp"
|
||||
|
||||
#include <QLineEdit>
|
||||
#include <QPushButton>
|
||||
@@ -43,8 +44,7 @@ public:
|
||||
}
|
||||
|
||||
private slots:
|
||||
void FilePathChanged();
|
||||
void BrowseButtonClicked();
|
||||
void PathChanged(const QString &text);
|
||||
void AddArg();
|
||||
void RemoveArg();
|
||||
void ArgUp();
|
||||
@@ -56,8 +56,7 @@ protected:
|
||||
std::shared_ptr<MacroActionRun> _entryData;
|
||||
|
||||
private:
|
||||
QLineEdit *_filePath;
|
||||
QPushButton *_browseButton;
|
||||
FileSelection *_filePath;
|
||||
QListWidget *_argList;
|
||||
QPushButton *_addArg;
|
||||
QPushButton *_removeArg;
|
||||
|
||||
38
src/headers/macro-action-scene-swap.hpp
Normal file
38
src/headers/macro-action-scene-swap.hpp
Normal file
@@ -0,0 +1,38 @@
|
||||
#pragma once
|
||||
#include "macro-action-edit.hpp"
|
||||
|
||||
class MacroActionSceneSwap : 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<MacroActionSceneSwap>();
|
||||
}
|
||||
|
||||
private:
|
||||
static bool _registered;
|
||||
static const std::string id;
|
||||
};
|
||||
|
||||
class MacroActionSceneSwapEdit : public QWidget {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
MacroActionSceneSwapEdit(
|
||||
QWidget *parent,
|
||||
std::shared_ptr<MacroActionSceneSwap> entryData = nullptr);
|
||||
static QWidget *Create(QWidget *parent,
|
||||
std::shared_ptr<MacroAction> action)
|
||||
{
|
||||
return new MacroActionSceneSwapEdit(
|
||||
parent, std::dynamic_pointer_cast<MacroActionSceneSwap>(
|
||||
action));
|
||||
}
|
||||
|
||||
protected:
|
||||
std::shared_ptr<MacroActionSceneSwap> _entryData;
|
||||
};
|
||||
72
src/headers/macro-action-timer.hpp
Normal file
72
src/headers/macro-action-timer.hpp
Normal file
@@ -0,0 +1,72 @@
|
||||
#pragma once
|
||||
#include "macro-action-edit.hpp"
|
||||
#include "duration-control.hpp"
|
||||
#include "macro-selection.hpp"
|
||||
|
||||
#include <QDoubleSpinBox>
|
||||
#include <QHBoxLayout>
|
||||
|
||||
enum class TimerAction {
|
||||
PAUSE,
|
||||
CONTINUE,
|
||||
RESET,
|
||||
SET_TIME_REMAINING,
|
||||
};
|
||||
|
||||
class MacroActionTimer : public MacroRefAction {
|
||||
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<MacroActionTimer>();
|
||||
}
|
||||
Duration _duration;
|
||||
TimerAction _actionType = TimerAction::PAUSE;
|
||||
|
||||
private:
|
||||
static bool _registered;
|
||||
static const std::string id;
|
||||
};
|
||||
|
||||
class MacroActionTimerEdit : public QWidget {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
MacroActionTimerEdit(
|
||||
QWidget *parent,
|
||||
std::shared_ptr<MacroActionTimer> entryData = nullptr);
|
||||
void UpdateEntryData();
|
||||
static QWidget *Create(QWidget *parent,
|
||||
std::shared_ptr<MacroAction> action)
|
||||
{
|
||||
return new MacroActionTimerEdit(
|
||||
parent,
|
||||
std::dynamic_pointer_cast<MacroActionTimer>(action));
|
||||
}
|
||||
|
||||
private slots:
|
||||
void MacroChanged(const QString &text);
|
||||
void MacroRemove(const QString &name);
|
||||
void DurationChanged(double value);
|
||||
void DurationUnitChanged(DurationUnit unit);
|
||||
void ActionTypeChanged(int value);
|
||||
signals:
|
||||
void HeaderInfoChanged(const QString &);
|
||||
|
||||
protected:
|
||||
void SetWidgetVisibility();
|
||||
|
||||
MacroSelection *_macros;
|
||||
DurationSelection *_duration;
|
||||
QComboBox *_timerAction;
|
||||
std::shared_ptr<MacroActionTimer> _entryData;
|
||||
|
||||
private:
|
||||
QHBoxLayout *_mainLayout;
|
||||
bool _loading = true;
|
||||
};
|
||||
68
src/headers/macro-action-transition.hpp
Normal file
68
src/headers/macro-action-transition.hpp
Normal file
@@ -0,0 +1,68 @@
|
||||
#pragma once
|
||||
#include "macro-action-edit.hpp"
|
||||
#include "duration-control.hpp"
|
||||
#include "transition-selection.hpp"
|
||||
|
||||
#include <QCheckBox>
|
||||
#include <QHBoxLayout>
|
||||
|
||||
class MacroActionTransition : 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<MacroActionTransition>();
|
||||
}
|
||||
|
||||
bool _setDuration = true;
|
||||
bool _setType = true;
|
||||
TransitionSelection _transition;
|
||||
Duration _duration;
|
||||
|
||||
private:
|
||||
static bool _registered;
|
||||
static const std::string id;
|
||||
};
|
||||
|
||||
class MacroActionTransitionEdit : public QWidget {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
MacroActionTransitionEdit(
|
||||
QWidget *parent,
|
||||
std::shared_ptr<MacroActionTransition> entryData = nullptr);
|
||||
void UpdateEntryData();
|
||||
static QWidget *Create(QWidget *parent,
|
||||
std::shared_ptr<MacroAction> action)
|
||||
{
|
||||
return new MacroActionTransitionEdit(
|
||||
parent,
|
||||
std::dynamic_pointer_cast<MacroActionTransition>(
|
||||
action));
|
||||
}
|
||||
|
||||
private slots:
|
||||
void SetTypeChanged(int state);
|
||||
void SetDurationChanged(int state);
|
||||
void TransitionChanged(const TransitionSelection &);
|
||||
void DurationChanged(double seconds);
|
||||
signals:
|
||||
void HeaderInfoChanged(const QString &);
|
||||
|
||||
protected:
|
||||
QCheckBox *_setType;
|
||||
QCheckBox *_setDuration;
|
||||
TransitionSelectionWidget *_transitions;
|
||||
DurationSelection *_duration;
|
||||
QHBoxLayout *_typeLayout;
|
||||
QHBoxLayout *_durationLayout;
|
||||
std::shared_ptr<MacroActionTransition> _entryData;
|
||||
|
||||
private:
|
||||
bool _loading = true;
|
||||
};
|
||||
@@ -1,3 +1,5 @@
|
||||
#ifdef VCAM_SUPPORTED
|
||||
|
||||
#pragma once
|
||||
#include "macro-action-edit.hpp"
|
||||
|
||||
@@ -55,3 +57,5 @@ private:
|
||||
QHBoxLayout *_mainLayout;
|
||||
bool _loading = true;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -4,6 +4,14 @@
|
||||
|
||||
#include <QCheckBox>
|
||||
#include <QDateTimeEdit>
|
||||
#include <QComboBox>
|
||||
|
||||
enum class DateCondition {
|
||||
AT,
|
||||
AFTER,
|
||||
BEFORE,
|
||||
BETWEEN,
|
||||
};
|
||||
|
||||
class MacroConditionDate : public MacroCondition {
|
||||
public:
|
||||
@@ -18,10 +26,12 @@ public:
|
||||
}
|
||||
|
||||
QDateTime _dateTime;
|
||||
QDateTime _dateTime2;
|
||||
bool _ignoreDate = false;
|
||||
bool _ignoreTime = false;
|
||||
bool _repeat = false;
|
||||
Duration _duration;
|
||||
DateCondition _condition = DateCondition::AT;
|
||||
|
||||
private:
|
||||
static bool _registered;
|
||||
@@ -45,7 +55,9 @@ public:
|
||||
}
|
||||
|
||||
private slots:
|
||||
void ConditionChanged(int cond);
|
||||
void DateTimeChanged(const QDateTime &datetime);
|
||||
void DateTime2Changed(const QDateTime &datetime);
|
||||
void IgnoreDateChanged(int state);
|
||||
void IgnoreTimeChanged(int state);
|
||||
void RepeatChanged(int state);
|
||||
@@ -55,7 +67,9 @@ signals:
|
||||
void HeaderInfoChanged(const QString &);
|
||||
|
||||
protected:
|
||||
QComboBox *_condition;
|
||||
QDateTimeEdit *_dateTime;
|
||||
QDateTimeEdit *_dateTime2;
|
||||
QCheckBox *_ignoreDate;
|
||||
QCheckBox *_ignoreTime;
|
||||
QCheckBox *_repeat;
|
||||
@@ -63,5 +77,6 @@ protected:
|
||||
std::shared_ptr<MacroConditionDate> _entryData;
|
||||
|
||||
private:
|
||||
void ShowSecondDateSelection(bool visible);
|
||||
bool _loading = true;
|
||||
};
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
#pragma once
|
||||
#include "macro.hpp"
|
||||
#include "file-selection.hpp"
|
||||
|
||||
#include <QWidget>
|
||||
#include <QComboBox>
|
||||
#include <QDateTime>
|
||||
@@ -61,8 +63,7 @@ public:
|
||||
|
||||
private slots:
|
||||
void FileTypeChanged(int index);
|
||||
void FilePathChanged();
|
||||
void BrowseButtonClicked();
|
||||
void PathChanged(const QString &text);
|
||||
void MatchTextChanged();
|
||||
void UseRegexChanged(int state);
|
||||
void CheckModificationDateChanged(int state);
|
||||
@@ -72,8 +73,7 @@ signals:
|
||||
|
||||
protected:
|
||||
QComboBox *_fileType;
|
||||
QLineEdit *_filePath;
|
||||
QPushButton *_browseButton;
|
||||
FileSelection *_filePath;
|
||||
QPlainTextEdit *_matchText;
|
||||
QCheckBox *_useRegex;
|
||||
QCheckBox *_checkModificationDate;
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
#ifdef REPLAYBUFFER_SUPPORTED
|
||||
|
||||
#pragma once
|
||||
#include "macro.hpp"
|
||||
#include <QWidget>
|
||||
@@ -54,3 +56,5 @@ protected:
|
||||
private:
|
||||
bool _loading = true;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
69
src/headers/macro-condition-scene-transform.hpp
Normal file
69
src/headers/macro-condition-scene-transform.hpp
Normal file
@@ -0,0 +1,69 @@
|
||||
#pragma once
|
||||
#include "macro.hpp"
|
||||
#include "scene-selection.hpp"
|
||||
|
||||
#include <QSpinBox>
|
||||
#include <QPlainTextEdit>
|
||||
#include <QCheckBox>
|
||||
|
||||
class MacroConditionSceneTransform : 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<MacroConditionSceneTransform>();
|
||||
}
|
||||
|
||||
SceneSelection _scene;
|
||||
OBSWeakSource _source;
|
||||
bool _regex = false;
|
||||
std::string _settings = "";
|
||||
|
||||
private:
|
||||
static bool _registered;
|
||||
static const std::string id;
|
||||
};
|
||||
|
||||
class MacroConditionSceneTransformEdit : public QWidget {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
MacroConditionSceneTransformEdit(
|
||||
QWidget *parent,
|
||||
std::shared_ptr<MacroConditionSceneTransform> entryData =
|
||||
nullptr);
|
||||
void UpdateEntryData();
|
||||
static QWidget *Create(QWidget *parent,
|
||||
std::shared_ptr<MacroCondition> cond)
|
||||
{
|
||||
return new MacroConditionSceneTransformEdit(
|
||||
parent,
|
||||
std::dynamic_pointer_cast<MacroConditionSceneTransform>(
|
||||
cond));
|
||||
}
|
||||
|
||||
private slots:
|
||||
void SceneChanged(const SceneSelection &);
|
||||
void SourceChanged(const QString &text);
|
||||
void GetSettingsClicked();
|
||||
void SettingsChanged();
|
||||
void RegexChanged(int);
|
||||
signals:
|
||||
void HeaderInfoChanged(const QString &);
|
||||
|
||||
protected:
|
||||
SceneSelectionWidget *_scenes;
|
||||
QComboBox *_sources;
|
||||
QPushButton *_getSettings;
|
||||
QPlainTextEdit *_settings;
|
||||
QCheckBox *_regex;
|
||||
|
||||
std::shared_ptr<MacroConditionSceneTransform> _entryData;
|
||||
|
||||
private:
|
||||
bool _loading = true;
|
||||
};
|
||||
@@ -4,10 +4,13 @@
|
||||
|
||||
#include <QWidget>
|
||||
#include <QComboBox>
|
||||
#include <QCheckBox>
|
||||
|
||||
enum class SceneType {
|
||||
CURRENT,
|
||||
PREVIOUS,
|
||||
CHANGED,
|
||||
NOTCHANGED,
|
||||
};
|
||||
|
||||
class MacroConditionScene : public MacroCondition {
|
||||
@@ -22,10 +25,12 @@ public:
|
||||
return std::make_shared<MacroConditionScene>();
|
||||
}
|
||||
|
||||
bool _waitForTransition = false;
|
||||
SceneSelection _scene;
|
||||
SceneType _type;
|
||||
|
||||
private:
|
||||
std::chrono::high_resolution_clock::time_point _lastSceneChangeTime{};
|
||||
static bool _registered;
|
||||
static const std::string id;
|
||||
};
|
||||
@@ -49,14 +54,17 @@ public:
|
||||
private slots:
|
||||
void SceneChanged(const SceneSelection &);
|
||||
void TypeChanged(int value);
|
||||
void WaitForTransitionChanged(int state);
|
||||
signals:
|
||||
void HeaderInfoChanged(const QString &);
|
||||
|
||||
protected:
|
||||
SceneSelectionWidget *_scenes;
|
||||
QComboBox *_sceneType;
|
||||
QCheckBox *_waitForTransition;
|
||||
std::shared_ptr<MacroConditionScene> _entryData;
|
||||
|
||||
private:
|
||||
void SetWidgetVisibility();
|
||||
bool _loading = true;
|
||||
};
|
||||
|
||||
@@ -6,6 +6,8 @@
|
||||
enum class StreamState {
|
||||
STOP,
|
||||
START,
|
||||
STARTING,
|
||||
STOPPING,
|
||||
};
|
||||
|
||||
class MacroConditionStream : public MacroCondition {
|
||||
@@ -22,6 +24,9 @@ public:
|
||||
StreamState _streamState;
|
||||
|
||||
private:
|
||||
std::chrono::high_resolution_clock::time_point _lastStreamStartingTime{};
|
||||
std::chrono::high_resolution_clock::time_point _lastStreamStoppingTime{};
|
||||
|
||||
static bool _registered;
|
||||
static const std::string id;
|
||||
};
|
||||
|
||||
@@ -16,8 +16,14 @@ public:
|
||||
{
|
||||
return std::make_shared<MacroConditionTimer>();
|
||||
}
|
||||
void Pause();
|
||||
void Continue();
|
||||
void Reset();
|
||||
|
||||
Duration _duration;
|
||||
bool _paused = false;
|
||||
bool _saveRemaining = false;
|
||||
double _remaining = 0.;
|
||||
bool _oneshot = false;
|
||||
|
||||
private:
|
||||
@@ -44,14 +50,20 @@ public:
|
||||
private slots:
|
||||
void DurationChanged(double seconds);
|
||||
void DurationUnitChanged(DurationUnit unit);
|
||||
void SaveRemainingChanged(int state);
|
||||
void AutoResetChanged(int state);
|
||||
void PauseContinueClicked();
|
||||
void ResetClicked();
|
||||
void UpdateTimeRemaining();
|
||||
|
||||
protected:
|
||||
void SetPauseContinueButtonLabel();
|
||||
|
||||
DurationSelection *_duration;
|
||||
QCheckBox *_autoReset;
|
||||
QCheckBox *_saveRemaining;
|
||||
QPushButton *_reset;
|
||||
QPushButton *_pauseConinue;
|
||||
QLabel *_remaining;
|
||||
std::shared_ptr<MacroConditionTimer> _entryData;
|
||||
|
||||
|
||||
79
src/headers/macro-condition-transition.hpp
Normal file
79
src/headers/macro-condition-transition.hpp
Normal file
@@ -0,0 +1,79 @@
|
||||
#pragma once
|
||||
#include "macro.hpp"
|
||||
#include "duration-control.hpp"
|
||||
#include "transition-selection.hpp"
|
||||
|
||||
#include <QWidget>
|
||||
#include <QComboBox>
|
||||
|
||||
enum class TransitionCondition {
|
||||
CURRENT,
|
||||
DURATION,
|
||||
STARTED,
|
||||
ENDED,
|
||||
};
|
||||
|
||||
class MacroConditionTransition : 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<MacroConditionTransition>();
|
||||
}
|
||||
void ConnectToTransitionSignals();
|
||||
void DisconnectTransitionSignals();
|
||||
|
||||
TransitionCondition _condition = TransitionCondition::CURRENT;
|
||||
TransitionSelection _transition;
|
||||
Duration _duration;
|
||||
|
||||
private:
|
||||
static void TransitionStarted(void *data, calldata_t *);
|
||||
static void TransitionEnded(void *data, calldata_t *);
|
||||
|
||||
bool _started = false;
|
||||
bool _ended = false;
|
||||
std::chrono::high_resolution_clock::time_point _lastTransitionEndTime{};
|
||||
static bool _registered;
|
||||
static const std::string id;
|
||||
};
|
||||
|
||||
class MacroConditionTransitionEdit : public QWidget {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
MacroConditionTransitionEdit(
|
||||
QWidget *parent,
|
||||
std::shared_ptr<MacroConditionTransition> cond = nullptr);
|
||||
void UpdateEntryData();
|
||||
static QWidget *Create(QWidget *parent,
|
||||
std::shared_ptr<MacroCondition> cond)
|
||||
{
|
||||
return new MacroConditionTransitionEdit(
|
||||
parent,
|
||||
std::dynamic_pointer_cast<MacroConditionTransition>(
|
||||
cond));
|
||||
}
|
||||
|
||||
private slots:
|
||||
void ConditionChanged(int cond);
|
||||
void TransitionChanged(const TransitionSelection &);
|
||||
void DurationChanged(double seconds);
|
||||
signals:
|
||||
void HeaderInfoChanged(const QString &);
|
||||
|
||||
protected:
|
||||
QComboBox *_conditions;
|
||||
TransitionSelectionWidget *_transitions;
|
||||
DurationSelection *_duration;
|
||||
QLabel *_durationSuffix;
|
||||
std::shared_ptr<MacroConditionTransition> _entryData;
|
||||
|
||||
private:
|
||||
void SetWidgetVisibility();
|
||||
bool _loading = true;
|
||||
};
|
||||
@@ -1,10 +1,14 @@
|
||||
#pragma once
|
||||
#include "macro.hpp"
|
||||
#include "screenshot-helper.hpp"
|
||||
#include "file-selection.hpp"
|
||||
|
||||
#include <QWidget>
|
||||
#include <QComboBox>
|
||||
#include <QCheckBox>
|
||||
#include <QHBoxLayout>
|
||||
#include <chrono>
|
||||
#include <opencv2/opencv.hpp>
|
||||
|
||||
enum class VideoCondition {
|
||||
MATCH,
|
||||
@@ -12,8 +16,19 @@ enum class VideoCondition {
|
||||
HAS_NOT_CHANGED,
|
||||
HAS_CHANGED,
|
||||
NO_IMAGE,
|
||||
PATTERN,
|
||||
OBJECT,
|
||||
};
|
||||
|
||||
struct PatternMatchData {
|
||||
cv::Mat4b rgbaPattern;
|
||||
cv::Mat3b rgbPattern;
|
||||
cv::Mat1b mask;
|
||||
};
|
||||
|
||||
constexpr int minMinNeighbors = 3;
|
||||
constexpr int maxMinNeighbors = 6;
|
||||
|
||||
class MacroConditionVideo : public MacroCondition {
|
||||
public:
|
||||
bool CheckCondition();
|
||||
@@ -28,20 +43,69 @@ public:
|
||||
}
|
||||
void GetScreenshot();
|
||||
bool LoadImageFromFile();
|
||||
bool LoadModelData(std::string &path);
|
||||
std::string GetModelDataPath() { return _modelDataPath; }
|
||||
void ResetLastMatch() { _lastMatchResult = false; }
|
||||
|
||||
OBSWeakSource _videoSource;
|
||||
VideoCondition _condition = VideoCondition::MATCH;
|
||||
std::string _file = obs_module_text("AdvSceneSwitcher.enterPath");
|
||||
bool _useAlphaAsMask = false;
|
||||
bool _usePatternForChangedCheck = false;
|
||||
PatternMatchData _patternData;
|
||||
double _patternThreshold = 0.8;
|
||||
cv::CascadeClassifier _objectCascade;
|
||||
double _scaleFactor = 1.1;
|
||||
int _minNeighbors = minMinNeighbors;
|
||||
int _minSizeX = 0;
|
||||
int _minSizeY = 0;
|
||||
int _maxSizeX = 0;
|
||||
int _maxSizeY = 0;
|
||||
|
||||
bool _throttleEnabled = false;
|
||||
int _throttleCount = 3;
|
||||
|
||||
private:
|
||||
bool OutputChanged();
|
||||
bool ScreenshotContainsPattern();
|
||||
bool ScreenshotContainsObject();
|
||||
bool Compare();
|
||||
bool CheckShouldBeSkipped();
|
||||
|
||||
std::unique_ptr<AdvSSScreenshotObj> _screenshotData = nullptr;
|
||||
QImage _matchImage;
|
||||
std::string _modelDataPath =
|
||||
obs_get_module_data_path(obs_current_module()) +
|
||||
std::string(
|
||||
"/res/cascadeClassifiers/haarcascade_frontalface_alt.xml");
|
||||
bool _lastMatchResult = false;
|
||||
int _runCount = 0;
|
||||
|
||||
static bool _registered;
|
||||
static const std::string id;
|
||||
};
|
||||
|
||||
class ThresholdSlider : public QWidget {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
ThresholdSlider(double min = 0., double max = 1.,
|
||||
const QString &label = "threshold",
|
||||
const QString &description = "", QWidget *parent = 0);
|
||||
void SetDoubleValue(double);
|
||||
public slots:
|
||||
void NotifyValueChanged(int value);
|
||||
signals:
|
||||
void DoubleValueChanged(double value);
|
||||
|
||||
private:
|
||||
void SetDoubleValueText(double);
|
||||
QLabel *_value;
|
||||
QSlider *_slider;
|
||||
double _scale = 100.0;
|
||||
int _precision = 2;
|
||||
};
|
||||
|
||||
class MacroConditionVideoEdit : public QWidget {
|
||||
Q_OBJECT
|
||||
|
||||
@@ -59,23 +123,60 @@ public:
|
||||
}
|
||||
|
||||
void UpdatePreviewTooltip();
|
||||
void SetFilePath(const QString &text);
|
||||
|
||||
private slots:
|
||||
void SourceChanged(const QString &text);
|
||||
void ConditionChanged(int cond);
|
||||
void FilePathChanged();
|
||||
void BrowseButtonClicked();
|
||||
void ImagePathChanged(const QString &text);
|
||||
void ImageBrowseButtonClicked();
|
||||
void UsePatternForChangedCheckChanged(int value);
|
||||
void PatternThresholdChanged(double);
|
||||
void UseAlphaAsMaskChanged(int value);
|
||||
|
||||
void ModelPathChanged(const QString &text);
|
||||
void ObjectScaleThresholdChanged(double);
|
||||
void MinNeighborsChanged(int value);
|
||||
void MinSizeXChanged(int value);
|
||||
void MinSizeYChanged(int value);
|
||||
void MaxSizeXChanged(int value);
|
||||
void MaxSizeYChanged(int value);
|
||||
|
||||
void ThrottleEnableChanged(int value);
|
||||
void ThrottleCountChanged(int value);
|
||||
void ShowMatchClicked();
|
||||
signals:
|
||||
void HeaderInfoChanged(const QString &);
|
||||
|
||||
protected:
|
||||
QComboBox *_videoSelection;
|
||||
QComboBox *_condition;
|
||||
QLineEdit *_filePath;
|
||||
QPushButton *_browseButton;
|
||||
|
||||
QCheckBox *_usePatternForChangedCheck;
|
||||
FileSelection *_imagePath;
|
||||
ThresholdSlider *_patternThreshold;
|
||||
QCheckBox *_useAlphaAsMask;
|
||||
|
||||
FileSelection *_modelDataPath;
|
||||
QHBoxLayout *_modelPathLayout;
|
||||
ThresholdSlider *_objectScaleThreshold;
|
||||
QHBoxLayout *_neighborsControlLayout;
|
||||
QSpinBox *_minNeighbors;
|
||||
QLabel *_minNeighborsDescription;
|
||||
QHBoxLayout *_minSizeControlLayout;
|
||||
QSpinBox *_minSizeX;
|
||||
QSpinBox *_minSizeY;
|
||||
QHBoxLayout *_maxSizeControlLayout;
|
||||
QSpinBox *_maxSizeX;
|
||||
QSpinBox *_maxSizeY;
|
||||
|
||||
QHBoxLayout *_throttleControlLayout;
|
||||
QCheckBox *_throttleEnable;
|
||||
QSpinBox *_throttleCount;
|
||||
QPushButton *_showMatch;
|
||||
|
||||
std::shared_ptr<MacroConditionVideo> _entryData;
|
||||
|
||||
private:
|
||||
void SetWidgetVisibility();
|
||||
bool _loading = true;
|
||||
};
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
#ifdef VCAM_SUPPORTED
|
||||
|
||||
#pragma once
|
||||
#include "macro.hpp"
|
||||
#include <QWidget>
|
||||
@@ -52,3 +54,5 @@ protected:
|
||||
private:
|
||||
bool _loading = true;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -30,6 +30,7 @@ public:
|
||||
// Use this function to avoid accidental edits when scrolling through
|
||||
// list of actions and conditions
|
||||
void SetFocusPolicyOfWidgets();
|
||||
void SetCollapsed(bool collapsed);
|
||||
|
||||
protected slots:
|
||||
void HeaderInfoChanged(const QString &);
|
||||
|
||||
@@ -10,6 +10,6 @@ std::pair<int, int> getCursorPos();
|
||||
int secondsSinceLastInput();
|
||||
void GetProcessList(QStringList &processes);
|
||||
bool isInFocus(const QString &executable);
|
||||
void PressKeys(const std::vector<HotkeyType> keys);
|
||||
void PressKeys(const std::vector<HotkeyType> keys, int duration);
|
||||
void PlatformInit();
|
||||
void PlatformCleanup();
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "switch-generic.hpp"
|
||||
#include "duration-control.hpp"
|
||||
|
||||
enum class sceneTriggerType {
|
||||
NONE = 0,
|
||||
@@ -43,7 +44,7 @@ struct SceneTrigger : SceneSwitcherEntry {
|
||||
static bool pause;
|
||||
sceneTriggerType triggerType = sceneTriggerType::NONE;
|
||||
sceneTriggerAction triggerAction = sceneTriggerAction::NONE;
|
||||
double duration = 0;
|
||||
Duration duration;
|
||||
OBSWeakSource audioSource = nullptr;
|
||||
|
||||
const char *getType() { return "trigger"; }
|
||||
@@ -70,13 +71,14 @@ public:
|
||||
private slots:
|
||||
void TriggerTypeChanged(int index);
|
||||
void TriggerActionChanged(int index);
|
||||
void DurationChanged(double dur);
|
||||
void DurationChanged(double seconds);
|
||||
void DurationUnitChanged(DurationUnit unit);
|
||||
void AudioSourceChanged(const QString &text);
|
||||
|
||||
private:
|
||||
QComboBox *triggers;
|
||||
QComboBox *actions;
|
||||
QDoubleSpinBox *duration;
|
||||
DurationSelection *duration;
|
||||
QComboBox *audioSources;
|
||||
|
||||
SceneTrigger *switchData;
|
||||
|
||||
@@ -16,6 +16,7 @@ public:
|
||||
|
||||
void SetContent(QWidget *w, bool collapsed = true);
|
||||
void AddHeaderWidget(QWidget *);
|
||||
void SetCollapsed(bool);
|
||||
|
||||
protected:
|
||||
bool eventFilter(QObject *obj, QEvent *event) override;
|
||||
|
||||
@@ -35,8 +35,7 @@ struct SceneSequenceSwitch : SceneSwitcherEntry {
|
||||
bool reduce();
|
||||
SceneSequenceSwitch *extend();
|
||||
|
||||
bool checkMatch(OBSWeakSource currentScene, int &linger,
|
||||
SceneSequenceSwitch *root = nullptr);
|
||||
bool checkMatch(int &linger, SceneSequenceSwitch *root = nullptr);
|
||||
bool checkDurationMatchInterruptible();
|
||||
void prepareUninterruptibleMatch(int &linger);
|
||||
void advanceActiveSequence();
|
||||
|
||||
@@ -67,10 +67,10 @@ struct SwitcherData {
|
||||
std::mutex m;
|
||||
bool transitionActive = false;
|
||||
bool waitForTransition = false;
|
||||
std::condition_variable transitionCv;
|
||||
bool stop = false;
|
||||
bool verbose = false;
|
||||
bool disableHints = false;
|
||||
bool showSystemTrayNotifications = false;
|
||||
bool showFrame = false;
|
||||
bool transitionOverrideOverride = false;
|
||||
bool adjustActiveTransitionType = true;
|
||||
@@ -80,6 +80,10 @@ struct SwitcherData {
|
||||
obs_source_t *waitScene = nullptr;
|
||||
OBSWeakSource currentScene = nullptr;
|
||||
OBSWeakSource previousScene = nullptr;
|
||||
std::chrono::high_resolution_clock::time_point lastSceneChangeTime{};
|
||||
std::chrono::high_resolution_clock::time_point lastTransitionEndTime{};
|
||||
std::chrono::high_resolution_clock::time_point lastStreamStartingTime{};
|
||||
std::chrono::high_resolution_clock::time_point lastStreamStoppingTime{};
|
||||
OBSWeakSource lastRandomScene;
|
||||
SceneGroup *lastRandomSceneGroup;
|
||||
OBSWeakSource nonMatchingScene;
|
||||
@@ -92,6 +96,8 @@ struct SwitcherData {
|
||||
std::chrono::high_resolution_clock::time_point lastMatchTime;
|
||||
|
||||
std::deque<Macro> macros;
|
||||
std::condition_variable macroWaitCv;
|
||||
std::atomic_bool abortMacroWait = {false};
|
||||
bool macroSceneSwitched = false;
|
||||
bool replayBufferSaved = false;
|
||||
|
||||
|
||||
46
src/headers/transition-selection.hpp
Normal file
46
src/headers/transition-selection.hpp
Normal file
@@ -0,0 +1,46 @@
|
||||
#pragma once
|
||||
#include "utility.hpp"
|
||||
|
||||
#include <QComboBox>
|
||||
|
||||
enum class TransitionSelectionType {
|
||||
TRANSITION,
|
||||
CURRENT,
|
||||
ANY,
|
||||
};
|
||||
|
||||
class TransitionSelection {
|
||||
public:
|
||||
void Save(obs_data_t *obj, const char *name = "transition",
|
||||
const char *typeName = "transitionType");
|
||||
void Load(obs_data_t *obj, const char *name = "transition",
|
||||
const char *typeName = "transitionType");
|
||||
|
||||
TransitionSelectionType GetType() { return _type; }
|
||||
OBSWeakSource GetTransition();
|
||||
std::string ToString();
|
||||
|
||||
private:
|
||||
OBSWeakSource _transition;
|
||||
TransitionSelectionType _type = TransitionSelectionType::TRANSITION;
|
||||
friend class TransitionSelectionWidget;
|
||||
};
|
||||
|
||||
class TransitionSelectionWidget : public QComboBox {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
TransitionSelectionWidget(QWidget *parent, bool current = true,
|
||||
bool any = false);
|
||||
void SetTransition(TransitionSelection &);
|
||||
void Repopulate(bool current, bool any);
|
||||
signals:
|
||||
void TransitionChanged(const TransitionSelection &);
|
||||
|
||||
private slots:
|
||||
void SelectionChanged(const QString &name);
|
||||
|
||||
private:
|
||||
bool IsCurrentTransitionSelected(const QString &name);
|
||||
bool IsAnyTransitionSelected(const QString &name);
|
||||
};
|
||||
@@ -30,36 +30,36 @@ bool compareSourceSettings(const OBSWeakSource &source,
|
||||
std::vector<obs_scene_item *> getSceneItemsWithName(OBSScene scene,
|
||||
std::string &name);
|
||||
std::string getDataFilePath(const std::string &file);
|
||||
QString fromatJsonString(std::string);
|
||||
QString fromatJsonString(const char *);
|
||||
|
||||
/**
|
||||
* Populate layout with labels and widgets based on provided text
|
||||
*
|
||||
* @param text Text based on which labels are generated and widgets are placed.
|
||||
* @param layout Layout in which the widgets and labels will be placed.
|
||||
* @param placeholders Map containing a mapping of placeholder strings to widgets.
|
||||
* @param addStretch Add addStretch() to layout.
|
||||
*/
|
||||
bool matchJson(const std::string &json1, const std::string &json2,
|
||||
bool useRegex);
|
||||
QString formatJsonString(std::string);
|
||||
QString formatJsonString(const char *);
|
||||
QString escapeForRegex(QString &s);
|
||||
void loadTransformState(obs_data_t *obj, struct obs_transform_info &info,
|
||||
struct obs_sceneitem_crop &crop);
|
||||
bool saveTransformState(obs_data_t *obj, struct obs_transform_info &info,
|
||||
struct obs_sceneitem_crop &crop);
|
||||
std::string getSceneItemTransform(obs_scene_item *item);
|
||||
void placeWidgets(std::string text, QBoxLayout *layout,
|
||||
std::unordered_map<std::string, QWidget *> placeholders,
|
||||
bool addStretch = true);
|
||||
void deleteLayoutItem(QLayoutItem *item);
|
||||
void clearLayout(QLayout *layout);
|
||||
QMetaObject::Connection PulseWidget(QWidget *widget, QColor endColor,
|
||||
void clearLayout(QLayout *layout, int afterIdx = 0);
|
||||
QMetaObject::Connection PulseWidget(QWidget *widget, QColor startColor,
|
||||
QColor = QColor(0, 0, 0, 0),
|
||||
QString specifier = "QLabel ");
|
||||
QString specifier = "QLabel ",
|
||||
bool once = false);
|
||||
void listAddClicked(QListWidget *list, QWidget *newWidget,
|
||||
QPushButton *addButton = nullptr,
|
||||
QMetaObject::Connection *addHighlight = nullptr);
|
||||
bool listMoveUp(QListWidget *list);
|
||||
bool listMoveDown(QListWidget *list);
|
||||
bool DisplayMessage(const QString &msg, bool question = false);
|
||||
void DisplayTrayMessage(const QString &title, const QString &msg);
|
||||
void addSelectionEntry(QComboBox *sel, const char *description,
|
||||
bool selectable = false, const char *tooltip = "");
|
||||
void populateTransitionSelection(QComboBox *sel, bool addCurrent = true,
|
||||
bool addSelect = true,
|
||||
bool selectable = false);
|
||||
bool addAny = false);
|
||||
void populateWindowSelection(QComboBox *sel, bool addSelect = true);
|
||||
void populateAudioSelection(QComboBox *sel, bool addSelect = true);
|
||||
void populateVideoSelection(QComboBox *sel, bool addSelect = true);
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
extern const char g_GIT_SHA1[];
|
||||
extern const char g_GIT_TAG[];
|
||||
|
||||
@@ -24,6 +24,9 @@
|
||||
#include <QStringList>
|
||||
#include <QRegularExpression>
|
||||
#include <QLibrary>
|
||||
#include <proc/readproc.h>
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
|
||||
static Display *xdisplay = 0;
|
||||
|
||||
@@ -363,17 +366,78 @@ bool isFullscreen(const std::string &title)
|
||||
void GetProcessList(QStringList &processes)
|
||||
{
|
||||
processes.clear();
|
||||
for (size_t i = 0; i < getTopLevelWindows().size(); ++i) {
|
||||
std::string s = GetWindowTitle(i);
|
||||
if (s != "")
|
||||
processes << QString::fromStdString(s);
|
||||
PROCTAB *proc = openproc(PROC_FILLSTAT);
|
||||
proc_t proc_info;
|
||||
memset(&proc_info, 0, sizeof(proc_info));
|
||||
while (readproc(proc, &proc_info) != NULL) {
|
||||
QString procName(proc_info.cmd);
|
||||
if (!procName.isEmpty() && !processes.contains(proc_info.cmd)) {
|
||||
processes << QString(proc_info.cmd);
|
||||
}
|
||||
}
|
||||
closeproc(proc);
|
||||
}
|
||||
|
||||
int getForegroundProcessPid()
|
||||
{
|
||||
if (!ewmhIsSupported()) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
auto dpy = disp();
|
||||
Atom active = XInternAtom(dpy, "_NET_ACTIVE_WINDOW", true);
|
||||
Atom actualType;
|
||||
int format;
|
||||
unsigned long num, bytes;
|
||||
Window *window = 0;
|
||||
int pid = -1;
|
||||
|
||||
Window rootWin = RootWindow(dpy, 0);
|
||||
|
||||
int xstatus = XGetWindowProperty(dpy, rootWin, active, 0L, ~0L, false,
|
||||
AnyPropertyType, &actualType, &format,
|
||||
&num, &bytes, (uint8_t **)&window);
|
||||
|
||||
if (xstatus == 0 && window == nullptr) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
Atom atom, actual_type;
|
||||
int actual_format;
|
||||
unsigned long nitems;
|
||||
unsigned long bytes_after;
|
||||
unsigned char *prop;
|
||||
atom = XInternAtom(dpy, "_NET_WM_PID", True);
|
||||
auto status = XGetWindowProperty(dpy, *window, atom, 0, 1024, False,
|
||||
AnyPropertyType, &actual_type,
|
||||
&actual_format, &nitems, &bytes_after,
|
||||
&prop);
|
||||
|
||||
if (status != 0) {
|
||||
return -2;
|
||||
}
|
||||
if (!prop) {
|
||||
return -3;
|
||||
}
|
||||
|
||||
pid = prop[1] * 256;
|
||||
pid += prop[0];
|
||||
return pid;
|
||||
}
|
||||
|
||||
std::string getProcNameFromPid(int pid)
|
||||
{
|
||||
std::string path = "/proc/" + std::to_string(pid) + "/comm";
|
||||
std::ifstream t(path);
|
||||
std::stringstream buffer;
|
||||
buffer << t.rdbuf();
|
||||
return buffer.str();
|
||||
}
|
||||
|
||||
bool isInFocus(const QString &executable)
|
||||
{
|
||||
std::string current;
|
||||
GetCurrentWindowTitle(current);
|
||||
auto pid = getForegroundProcessPid();
|
||||
std::string current = getProcNameFromPid(pid);
|
||||
|
||||
// True if executable switch equals current window
|
||||
bool equals = (executable.toStdString() == current);
|
||||
@@ -524,7 +588,7 @@ static std::unordered_map<HotkeyType, long> keyTable = {
|
||||
{HotkeyType::Key_NumpadEnter, XK_KP_Enter},
|
||||
};
|
||||
|
||||
void PressKeys(const std::vector<HotkeyType> keys)
|
||||
void PressKeys(const std::vector<HotkeyType> keys, int duration)
|
||||
{
|
||||
if (!canSimulateKeyPresses) {
|
||||
return;
|
||||
@@ -545,7 +609,7 @@ void PressKeys(const std::vector<HotkeyType> keys)
|
||||
}
|
||||
XFlush(display);
|
||||
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(300));
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(duration));
|
||||
|
||||
// Release keys
|
||||
for (auto &key : keys) {
|
||||
|
||||
@@ -134,20 +134,27 @@ void AdvSceneSwitcher::AddMacroAction(int idx)
|
||||
return;
|
||||
}
|
||||
|
||||
MacroActionSwitchScene temp;
|
||||
std::string id = temp.GetId();
|
||||
|
||||
std::string id;
|
||||
if (idx - 1 >= 0) {
|
||||
id = macro->Actions().at(idx - 1)->GetId();
|
||||
} else {
|
||||
MacroActionSwitchScene temp;
|
||||
id = temp.GetId();
|
||||
}
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
auto action = macro->Actions().emplace(macro->Actions().begin() + idx,
|
||||
MacroActionFactory::Create(id));
|
||||
macro->Actions().emplace(macro->Actions().begin() + idx,
|
||||
MacroActionFactory::Create(id));
|
||||
if (idx - 1 >= 0) {
|
||||
auto data = obs_data_create();
|
||||
macro->Actions().at(idx - 1)->Save(data);
|
||||
macro->Actions().at(idx)->Load(data);
|
||||
obs_data_release(data);
|
||||
}
|
||||
macro->UpdateActionIndices();
|
||||
|
||||
// All entry pointers in existing edit widgets after the new entry will
|
||||
// be invalidated by adding the new entry so we have to recreate them
|
||||
// and because I am lazy I am recreating every widget.
|
||||
//
|
||||
// If performance should become a concern this has to be revisited.
|
||||
SetEditMacro(*macro);
|
||||
clearLayout(ui->macroEditActionLayout, idx);
|
||||
PopulateMacroActions(*macro, idx);
|
||||
HighlightAction(idx);
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::on_actionAdd_clicked()
|
||||
@@ -174,14 +181,12 @@ 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();
|
||||
macro->UpdateActionIndices();
|
||||
|
||||
// All entry pointers in existing edit widgets after the new entry will
|
||||
// be invalidated by adding the new entry so we have to recreate them
|
||||
// and because I am lazy I am recreating every widget.
|
||||
//
|
||||
// If performance should become a concern this has to be revisited.
|
||||
SetEditMacro(*macro);
|
||||
clearLayout(ui->macroEditActionLayout, idx);
|
||||
PopulateMacroActions(*macro, idx);
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::on_actionRemove_clicked()
|
||||
|
||||
@@ -86,9 +86,7 @@ MacroActionFileEdit::MacroActionFileEdit(
|
||||
QWidget *parent, std::shared_ptr<MacroActionFile> entryData)
|
||||
: QWidget(parent)
|
||||
{
|
||||
_filePath = new QLineEdit();
|
||||
_browseButton =
|
||||
new QPushButton(obs_module_text("AdvSceneSwitcher.browse"));
|
||||
_filePath = new FileSelection(FileSelection::Type::WRITE);
|
||||
_text = new QPlainTextEdit();
|
||||
_actions = new QComboBox();
|
||||
|
||||
@@ -96,10 +94,8 @@ MacroActionFileEdit::MacroActionFileEdit(
|
||||
|
||||
QWidget::connect(_actions, SIGNAL(currentIndexChanged(int)), this,
|
||||
SLOT(ActionChanged(int)));
|
||||
QWidget::connect(_filePath, SIGNAL(editingFinished()), this,
|
||||
SLOT(FilePathChanged()));
|
||||
QWidget::connect(_browseButton, SIGNAL(clicked()), this,
|
||||
SLOT(BrowseButtonClicked()));
|
||||
QWidget::connect(_filePath, SIGNAL(PathChanged(const QString &)), this,
|
||||
SLOT(PathChanged(const QString &)));
|
||||
QWidget::connect(_text, SIGNAL(textChanged()), this,
|
||||
SLOT(TextChanged()));
|
||||
;
|
||||
@@ -107,7 +103,6 @@ MacroActionFileEdit::MacroActionFileEdit(
|
||||
QHBoxLayout *entryLayout = new QHBoxLayout;
|
||||
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
|
||||
{"{{filePath}}", _filePath},
|
||||
{"{{browseButton}}", _browseButton},
|
||||
{"{{matchText}}", _text},
|
||||
{"{{actions}}", _actions},
|
||||
};
|
||||
@@ -131,41 +126,22 @@ void MacroActionFileEdit::UpdateEntryData()
|
||||
}
|
||||
|
||||
_actions->setCurrentIndex(static_cast<int>(_entryData->_action));
|
||||
_filePath->setText(QString::fromStdString(_entryData->_file));
|
||||
_filePath->SetPath(QString::fromStdString(_entryData->_file));
|
||||
_text->setPlainText(QString::fromStdString(_entryData->_text));
|
||||
}
|
||||
|
||||
void MacroActionFileEdit::FilePathChanged()
|
||||
void MacroActionFileEdit::PathChanged(const QString &text)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_file = _filePath->text().toUtf8().constData();
|
||||
_entryData->_file = text.toUtf8().constData();
|
||||
emit HeaderInfoChanged(
|
||||
QString::fromStdString(_entryData->GetShortDesc()));
|
||||
}
|
||||
|
||||
void MacroActionFileEdit::BrowseButtonClicked()
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
QString path = QFileDialog::getSaveFileName(
|
||||
this,
|
||||
tr(obs_module_text("AdvSceneSwitcher.fileTab.selectWrite")),
|
||||
QDir::currentPath(),
|
||||
tr(obs_module_text("AdvSceneSwitcher.fileTab.anyFileType")));
|
||||
if (path.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
_filePath->setText(path);
|
||||
FilePathChanged();
|
||||
}
|
||||
|
||||
void MacroActionFileEdit::TextChanged()
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
|
||||
@@ -198,7 +198,7 @@ void MacroActionFilterEdit::GetSettingsClicked()
|
||||
}
|
||||
|
||||
_settings->setPlainText(
|
||||
fromatJsonString(getSourceSettings(_entryData->_filter)));
|
||||
formatJsonString(getSourceSettings(_entryData->_filter)));
|
||||
}
|
||||
|
||||
void MacroActionFilterEdit::SettingsChanged()
|
||||
|
||||
@@ -1,18 +1,196 @@
|
||||
#include "headers/macro-action-hotkey.hpp"
|
||||
#include "headers/advanced-scene-switcher.hpp"
|
||||
#include "headers/utility.hpp"
|
||||
#include <obs-interaction.h>
|
||||
|
||||
const std::string MacroActionHotkey::id = "hotkey";
|
||||
|
||||
// TODO:
|
||||
// I can't seem to get this to work so drop support for this functionality
|
||||
// on MacOS
|
||||
#if !__APPLE__
|
||||
bool MacroActionHotkey::_registered = MacroActionFactory::Register(
|
||||
MacroActionHotkey::id,
|
||||
{MacroActionHotkey::Create, MacroActionHotkeyEdit::Create,
|
||||
"AdvSceneSwitcher.action.hotkey"});
|
||||
#endif
|
||||
|
||||
static std::unordered_map<HotkeyType, long> keyTable = {
|
||||
// Chars
|
||||
{HotkeyType::Key_A, OBS_KEY_A},
|
||||
{HotkeyType::Key_B, OBS_KEY_B},
|
||||
{HotkeyType::Key_C, OBS_KEY_C},
|
||||
{HotkeyType::Key_D, OBS_KEY_D},
|
||||
{HotkeyType::Key_E, OBS_KEY_E},
|
||||
{HotkeyType::Key_F, OBS_KEY_F},
|
||||
{HotkeyType::Key_G, OBS_KEY_G},
|
||||
{HotkeyType::Key_H, OBS_KEY_H},
|
||||
{HotkeyType::Key_I, OBS_KEY_I},
|
||||
{HotkeyType::Key_J, OBS_KEY_J},
|
||||
{HotkeyType::Key_K, OBS_KEY_K},
|
||||
{HotkeyType::Key_L, OBS_KEY_L},
|
||||
{HotkeyType::Key_M, OBS_KEY_M},
|
||||
{HotkeyType::Key_N, OBS_KEY_N},
|
||||
{HotkeyType::Key_O, OBS_KEY_O},
|
||||
{HotkeyType::Key_P, OBS_KEY_P},
|
||||
{HotkeyType::Key_Q, OBS_KEY_Q},
|
||||
{HotkeyType::Key_R, OBS_KEY_R},
|
||||
{HotkeyType::Key_S, OBS_KEY_S},
|
||||
{HotkeyType::Key_T, OBS_KEY_T},
|
||||
{HotkeyType::Key_U, OBS_KEY_U},
|
||||
{HotkeyType::Key_V, OBS_KEY_V},
|
||||
{HotkeyType::Key_W, OBS_KEY_W},
|
||||
{HotkeyType::Key_X, OBS_KEY_X},
|
||||
{HotkeyType::Key_Y, OBS_KEY_Y},
|
||||
{HotkeyType::Key_Z, OBS_KEY_Z},
|
||||
|
||||
// Numbers
|
||||
{HotkeyType::Key_0, OBS_KEY_0},
|
||||
{HotkeyType::Key_1, OBS_KEY_1},
|
||||
{HotkeyType::Key_2, OBS_KEY_2},
|
||||
{HotkeyType::Key_3, OBS_KEY_3},
|
||||
{HotkeyType::Key_4, OBS_KEY_4},
|
||||
{HotkeyType::Key_5, OBS_KEY_5},
|
||||
{HotkeyType::Key_6, OBS_KEY_6},
|
||||
{HotkeyType::Key_7, OBS_KEY_7},
|
||||
{HotkeyType::Key_8, OBS_KEY_8},
|
||||
{HotkeyType::Key_9, OBS_KEY_9},
|
||||
|
||||
{HotkeyType::Key_F1, OBS_KEY_F1},
|
||||
{HotkeyType::Key_F2, OBS_KEY_F2},
|
||||
{HotkeyType::Key_F3, OBS_KEY_F3},
|
||||
{HotkeyType::Key_F4, OBS_KEY_F4},
|
||||
{HotkeyType::Key_F5, OBS_KEY_F5},
|
||||
{HotkeyType::Key_F6, OBS_KEY_F6},
|
||||
{HotkeyType::Key_F7, OBS_KEY_F7},
|
||||
{HotkeyType::Key_F8, OBS_KEY_F8},
|
||||
{HotkeyType::Key_F9, OBS_KEY_F9},
|
||||
{HotkeyType::Key_F10, OBS_KEY_F10},
|
||||
{HotkeyType::Key_F11, OBS_KEY_F11},
|
||||
{HotkeyType::Key_F12, OBS_KEY_F12},
|
||||
{HotkeyType::Key_F13, OBS_KEY_F13},
|
||||
{HotkeyType::Key_F14, OBS_KEY_F14},
|
||||
{HotkeyType::Key_F15, OBS_KEY_F15},
|
||||
{HotkeyType::Key_F16, OBS_KEY_F16},
|
||||
{HotkeyType::Key_F17, OBS_KEY_F17},
|
||||
{HotkeyType::Key_F18, OBS_KEY_F18},
|
||||
{HotkeyType::Key_F19, OBS_KEY_F19},
|
||||
{HotkeyType::Key_F20, OBS_KEY_F20},
|
||||
{HotkeyType::Key_F21, OBS_KEY_F21},
|
||||
{HotkeyType::Key_F22, OBS_KEY_F22},
|
||||
{HotkeyType::Key_F23, OBS_KEY_F23},
|
||||
{HotkeyType::Key_F24, OBS_KEY_F24},
|
||||
|
||||
{HotkeyType::Key_Escape, OBS_KEY_ESCAPE},
|
||||
{HotkeyType::Key_Space, OBS_KEY_SPACE},
|
||||
{HotkeyType::Key_Return, OBS_KEY_RETURN},
|
||||
{HotkeyType::Key_Backspace, OBS_KEY_BACKSPACE},
|
||||
{HotkeyType::Key_Tab, OBS_KEY_TAB},
|
||||
|
||||
{HotkeyType::Key_Shift_L, OBS_KEY_SHIFT},
|
||||
{HotkeyType::Key_Shift_R, OBS_KEY_SHIFT},
|
||||
{HotkeyType::Key_Control_L, OBS_KEY_CONTROL},
|
||||
{HotkeyType::Key_Control_R, OBS_KEY_CONTROL},
|
||||
{HotkeyType::Key_Alt_L, OBS_KEY_ALT},
|
||||
{HotkeyType::Key_Alt_R, OBS_KEY_ALT},
|
||||
{HotkeyType::Key_Win_L, OBS_KEY_META},
|
||||
{HotkeyType::Key_Win_R, OBS_KEY_META},
|
||||
{HotkeyType::Key_Apps, OBS_KEY_APPLICATIONLEFT},
|
||||
|
||||
{HotkeyType::Key_CapsLock, OBS_KEY_CAPSLOCK},
|
||||
{HotkeyType::Key_NumLock, OBS_KEY_NUMLOCK},
|
||||
{HotkeyType::Key_ScrollLock, OBS_KEY_SCROLLLOCK},
|
||||
|
||||
{HotkeyType::Key_PrintScreen, OBS_KEY_PRINT},
|
||||
{HotkeyType::Key_Pause, OBS_KEY_PAUSE},
|
||||
|
||||
{HotkeyType::Key_Insert, OBS_KEY_INSERT},
|
||||
{HotkeyType::Key_Delete, OBS_KEY_DELETE},
|
||||
{HotkeyType::Key_PageUP, OBS_KEY_PAGEUP},
|
||||
{HotkeyType::Key_PageDown, OBS_KEY_PAGEDOWN},
|
||||
{HotkeyType::Key_Home, OBS_KEY_HOME},
|
||||
{HotkeyType::Key_End, OBS_KEY_END},
|
||||
|
||||
{HotkeyType::Key_Left, OBS_KEY_LEFT},
|
||||
{HotkeyType::Key_Up, OBS_KEY_UP},
|
||||
{HotkeyType::Key_Right, OBS_KEY_RIGHT},
|
||||
{HotkeyType::Key_Down, OBS_KEY_DOWN},
|
||||
|
||||
{HotkeyType::Key_Numpad0, OBS_KEY_NUM0},
|
||||
{HotkeyType::Key_Numpad1, OBS_KEY_NUM1},
|
||||
{HotkeyType::Key_Numpad2, OBS_KEY_NUM2},
|
||||
{HotkeyType::Key_Numpad3, OBS_KEY_NUM3},
|
||||
{HotkeyType::Key_Numpad4, OBS_KEY_NUM4},
|
||||
{HotkeyType::Key_Numpad5, OBS_KEY_NUM5},
|
||||
{HotkeyType::Key_Numpad6, OBS_KEY_NUM6},
|
||||
{HotkeyType::Key_Numpad7, OBS_KEY_NUM7},
|
||||
{HotkeyType::Key_Numpad8, OBS_KEY_NUM8},
|
||||
{HotkeyType::Key_Numpad9, OBS_KEY_NUM9},
|
||||
|
||||
{HotkeyType::Key_NumpadAdd, OBS_KEY_NUMPLUS},
|
||||
{HotkeyType::Key_NumpadSubtract, OBS_KEY_NUMMINUS},
|
||||
{HotkeyType::Key_NumpadMultiply, OBS_KEY_NUMASTERISK},
|
||||
{HotkeyType::Key_NumpadDivide, OBS_KEY_NUMSLASH},
|
||||
{HotkeyType::Key_NumpadDecimal, OBS_KEY_NUMPERIOD},
|
||||
//{HotkeyType::Key_NumpadEnter, ???},
|
||||
};
|
||||
|
||||
obs_key_combination keysToOBSKeycombo(const std::vector<HotkeyType> &keys)
|
||||
{
|
||||
obs_key_combination combo{};
|
||||
auto it = keyTable.find(keys.back());
|
||||
if (it != keyTable.end()) {
|
||||
combo.key = (obs_key_t)it->second;
|
||||
}
|
||||
if (keys.size() == 1) {
|
||||
return combo;
|
||||
}
|
||||
|
||||
for (int i = 0; i < keys.size() - 1; i++) {
|
||||
switch (keys[i]) {
|
||||
case HotkeyType::Key_Shift_L:
|
||||
combo.modifiers |= INTERACT_SHIFT_KEY;
|
||||
break;
|
||||
case HotkeyType::Key_Shift_R:
|
||||
combo.modifiers |= INTERACT_SHIFT_KEY;
|
||||
break;
|
||||
case HotkeyType::Key_Control_L:
|
||||
combo.modifiers |= INTERACT_CONTROL_KEY;
|
||||
break;
|
||||
case HotkeyType::Key_Control_R:
|
||||
combo.modifiers |= INTERACT_CONTROL_KEY;
|
||||
break;
|
||||
case HotkeyType::Key_Alt_L:
|
||||
combo.modifiers |= INTERACT_ALT_KEY;
|
||||
break;
|
||||
case HotkeyType::Key_Alt_R:
|
||||
combo.modifiers |= INTERACT_ALT_KEY;
|
||||
break;
|
||||
case HotkeyType::Key_Win_L:
|
||||
combo.modifiers |= INTERACT_COMMAND_KEY;
|
||||
break;
|
||||
case HotkeyType::Key_Win_R:
|
||||
combo.modifiers |= INTERACT_COMMAND_KEY;
|
||||
break;
|
||||
case HotkeyType::Key_CapsLock:
|
||||
combo.modifiers |= INTERACT_CAPS_KEY;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return combo;
|
||||
}
|
||||
|
||||
void InjectKeys(const std::vector<HotkeyType> &keys, int duration)
|
||||
{
|
||||
auto combo = keysToOBSKeycombo(keys);
|
||||
if (obs_key_combination_is_empty(combo)) {
|
||||
return;
|
||||
}
|
||||
// I am not sure why this is necessary
|
||||
obs_hotkey_inject_event(combo, false);
|
||||
|
||||
obs_hotkey_inject_event(combo, true);
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(duration));
|
||||
obs_hotkey_inject_event(combo, false);
|
||||
}
|
||||
|
||||
bool MacroActionHotkey::PerformAction()
|
||||
{
|
||||
@@ -35,13 +213,25 @@ bool MacroActionHotkey::PerformAction()
|
||||
if (_rightAlt) {
|
||||
keys.push_back(HotkeyType::Key_Alt_R);
|
||||
}
|
||||
if (_leftMeta) {
|
||||
keys.push_back(HotkeyType::Key_Win_L);
|
||||
}
|
||||
if (_rightMeta) {
|
||||
keys.push_back(HotkeyType::Key_Win_R);
|
||||
}
|
||||
if (_key != HotkeyType::Key_NoKey) {
|
||||
keys.push_back(_key);
|
||||
}
|
||||
|
||||
if (!keys.empty()) {
|
||||
std::thread t([keys]() { PressKeys(keys); });
|
||||
t.detach();
|
||||
int dur = _duration;
|
||||
if (_onlySendToObs) {
|
||||
std::thread t([keys, dur]() { InjectKeys(keys, dur); });
|
||||
t.detach();
|
||||
} else {
|
||||
std::thread t([keys, dur]() { PressKeys(keys, dur); });
|
||||
t.detach();
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -64,6 +254,8 @@ bool MacroActionHotkey::Save(obs_data_t *obj)
|
||||
obs_data_set_bool(obj, "right_alt", _rightAlt);
|
||||
obs_data_set_bool(obj, "left_meta", _leftMeta);
|
||||
obs_data_set_bool(obj, "right_meta", _rightMeta);
|
||||
obs_data_set_int(obj, "duration", _duration);
|
||||
obs_data_set_bool(obj, "onlyOBS", _onlySendToObs);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -79,6 +271,8 @@ bool MacroActionHotkey::Load(obs_data_t *obj)
|
||||
_rightAlt = obs_data_get_bool(obj, "right_alt");
|
||||
_leftMeta = obs_data_get_bool(obj, "left_meta");
|
||||
_rightMeta = obs_data_get_bool(obj, "right_meta");
|
||||
_duration = obs_data_get_int(obj, "duration");
|
||||
_onlySendToObs = obs_data_get_bool(obj, "onlyOBS");
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -213,6 +407,12 @@ MacroActionHotkeyEdit::MacroActionHotkeyEdit(
|
||||
obs_module_text("AdvSceneSwitcher.action.hotkey.leftMeta"));
|
||||
_rightMeta = new QCheckBox(
|
||||
obs_module_text("AdvSceneSwitcher.action.hotkey.rightMeta"));
|
||||
_duration = new QSpinBox();
|
||||
_duration->setMaximum(5000);
|
||||
_onlySendToOBS = new QCheckBox(
|
||||
obs_module_text("AdvSceneSwitcher.action.hotkey.onlyOBS"));
|
||||
_noKeyPressSimulationWarning = new QLabel(
|
||||
obs_module_text("AdvSceneSwitcher.action.hotkey.disabled"));
|
||||
|
||||
populateKeySelection(_keys);
|
||||
|
||||
@@ -234,10 +434,15 @@ MacroActionHotkeyEdit::MacroActionHotkeyEdit(
|
||||
SLOT(LMetaChanged(int)));
|
||||
QWidget::connect(_rightMeta, SIGNAL(stateChanged(int)), this,
|
||||
SLOT(RMetaChanged(int)));
|
||||
QWidget::connect(_duration, SIGNAL(valueChanged(int)), this,
|
||||
SLOT(DurationChanged(int)));
|
||||
QWidget::connect(_onlySendToOBS, SIGNAL(stateChanged(int)), this,
|
||||
SLOT(OnlySendToOBSChanged(int)));
|
||||
|
||||
QHBoxLayout *line1Layout = new QHBoxLayout;
|
||||
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
|
||||
{"{{keys}}", _keys},
|
||||
{"{{duration}}", _duration},
|
||||
};
|
||||
placeWidgets(obs_module_text("AdvSceneSwitcher.action.hotkey.entry"),
|
||||
line1Layout, widgetPlaceholders);
|
||||
@@ -256,11 +461,8 @@ MacroActionHotkeyEdit::MacroActionHotkeyEdit(
|
||||
QVBoxLayout *mainLayout = new QVBoxLayout;
|
||||
mainLayout->addLayout(line1Layout);
|
||||
mainLayout->addLayout(line2Layout);
|
||||
|
||||
if (!canSimulateKeyPresses) {
|
||||
mainLayout->addWidget(new QLabel(obs_module_text(
|
||||
"AdvSceneSwitcher.action.hotkey.disabled")));
|
||||
}
|
||||
mainLayout->addWidget(_onlySendToOBS);
|
||||
mainLayout->addWidget(_noKeyPressSimulationWarning);
|
||||
|
||||
setLayout(mainLayout);
|
||||
|
||||
@@ -269,6 +471,12 @@ MacroActionHotkeyEdit::MacroActionHotkeyEdit(
|
||||
_loading = false;
|
||||
}
|
||||
|
||||
void MacroActionHotkeyEdit::SetWarningVisibility()
|
||||
{
|
||||
_noKeyPressSimulationWarning->setVisible(!_entryData->_onlySendToObs &&
|
||||
!canSimulateKeyPresses);
|
||||
}
|
||||
|
||||
void MacroActionHotkeyEdit::UpdateEntryData()
|
||||
{
|
||||
if (!_entryData) {
|
||||
@@ -284,6 +492,9 @@ void MacroActionHotkeyEdit::UpdateEntryData()
|
||||
_rightAlt->setChecked(_entryData->_rightAlt);
|
||||
_leftMeta->setChecked(_entryData->_leftMeta);
|
||||
_rightMeta->setChecked(_entryData->_rightMeta);
|
||||
_duration->setValue(_entryData->_duration);
|
||||
_onlySendToOBS->setChecked(_entryData->_onlySendToObs);
|
||||
SetWarningVisibility();
|
||||
}
|
||||
|
||||
void MacroActionHotkeyEdit::LShiftChanged(int state)
|
||||
@@ -366,6 +577,27 @@ void MacroActionHotkeyEdit::RMetaChanged(int state)
|
||||
_entryData->_rightMeta = state;
|
||||
}
|
||||
|
||||
void MacroActionHotkeyEdit::DurationChanged(int ms)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_duration = ms;
|
||||
}
|
||||
|
||||
void MacroActionHotkeyEdit::OnlySendToOBSChanged(int state)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_onlySendToObs = state;
|
||||
SetWarningVisibility();
|
||||
}
|
||||
|
||||
void MacroActionHotkeyEdit::KeyChanged(int key)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
|
||||
87
src/macro-action-preview-scene.cpp
Normal file
87
src/macro-action-preview-scene.cpp
Normal file
@@ -0,0 +1,87 @@
|
||||
#include "headers/macro-action-preview-scene.hpp"
|
||||
#include "headers/advanced-scene-switcher.hpp"
|
||||
#include "headers/utility.hpp"
|
||||
|
||||
const std::string MacroActionPreviewScene::id = "preview_scene";
|
||||
|
||||
bool MacroActionPreviewScene::_registered = MacroActionFactory::Register(
|
||||
MacroActionPreviewScene::id,
|
||||
{MacroActionPreviewScene::Create, MacroActionPreviewSceneEdit::Create,
|
||||
"AdvSceneSwitcher.action.previewScene"});
|
||||
|
||||
bool MacroActionPreviewScene::PerformAction()
|
||||
{
|
||||
auto s = obs_weak_source_get_source(_scene.GetScene());
|
||||
obs_frontend_set_current_preview_scene(s);
|
||||
obs_source_release(s);
|
||||
return true;
|
||||
}
|
||||
|
||||
void MacroActionPreviewScene::LogAction()
|
||||
{
|
||||
vblog(LOG_INFO, "set preview scene to \"%s\"",
|
||||
_scene.ToString().c_str());
|
||||
}
|
||||
|
||||
bool MacroActionPreviewScene::Save(obs_data_t *obj)
|
||||
{
|
||||
MacroAction::Save(obj);
|
||||
_scene.Save(obj);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MacroActionPreviewScene::Load(obs_data_t *obj)
|
||||
{
|
||||
MacroAction::Load(obj);
|
||||
_scene.Load(obj);
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string MacroActionPreviewScene::GetShortDesc()
|
||||
{
|
||||
return _scene.ToString();
|
||||
}
|
||||
|
||||
MacroActionPreviewSceneEdit::MacroActionPreviewSceneEdit(
|
||||
QWidget *parent, std::shared_ptr<MacroActionPreviewScene> entryData)
|
||||
: QWidget(parent)
|
||||
{
|
||||
_scenes = new SceneSelectionWidget(window(), false, false, false);
|
||||
|
||||
QWidget::connect(_scenes, SIGNAL(SceneChanged(const SceneSelection &)),
|
||||
this, SLOT(SceneChanged(const SceneSelection &)));
|
||||
|
||||
QHBoxLayout *mainLayout = new QHBoxLayout;
|
||||
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
|
||||
{"{{scenes}}", _scenes},
|
||||
};
|
||||
placeWidgets(
|
||||
obs_module_text("AdvSceneSwitcher.action.previewScene.entry"),
|
||||
mainLayout, widgetPlaceholders);
|
||||
setLayout(mainLayout);
|
||||
|
||||
_entryData = entryData;
|
||||
UpdateEntryData();
|
||||
_loading = false;
|
||||
}
|
||||
|
||||
void MacroActionPreviewSceneEdit::UpdateEntryData()
|
||||
{
|
||||
if (!_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
_scenes->SetScene(_entryData->_scene);
|
||||
}
|
||||
|
||||
void MacroActionPreviewSceneEdit::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()));
|
||||
}
|
||||
@@ -1,3 +1,5 @@
|
||||
#ifdef REPLAYBUFFER_SUPPORTED
|
||||
|
||||
#include "headers/macro-action-replay-buffer.hpp"
|
||||
#include "headers/advanced-scene-switcher.hpp"
|
||||
#include "headers/utility.hpp"
|
||||
@@ -119,3 +121,5 @@ void MacroActionReplayBufferEdit::ActionChanged(int value)
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_action = static_cast<ReplayBufferAction>(value);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -65,9 +65,7 @@ MacroActionRunEdit::MacroActionRunEdit(
|
||||
QWidget *parent, std::shared_ptr<MacroActionRun> entryData)
|
||||
: QWidget(parent)
|
||||
{
|
||||
_filePath = new QLineEdit();
|
||||
_browseButton =
|
||||
new QPushButton(obs_module_text("AdvSceneSwitcher.browse"));
|
||||
_filePath = new FileSelection();
|
||||
_argList = new QListWidget();
|
||||
_addArg = new QPushButton();
|
||||
_addArg->setMaximumSize(QSize(22, 22));
|
||||
@@ -90,10 +88,8 @@ MacroActionRunEdit::MacroActionRunEdit(
|
||||
"themeID", QVariant(QString::fromUtf8("downArrowIconSmall")));
|
||||
_argDown->setFlat(true);
|
||||
|
||||
QWidget::connect(_filePath, SIGNAL(editingFinished()), this,
|
||||
SLOT(FilePathChanged()));
|
||||
QWidget::connect(_browseButton, SIGNAL(clicked()), this,
|
||||
SLOT(BrowseButtonClicked()));
|
||||
QWidget::connect(_filePath, SIGNAL(PathChanged(const QString &)), this,
|
||||
SLOT(PathChanged(const QString &)));
|
||||
QWidget::connect(_addArg, SIGNAL(clicked()), this, SLOT(AddArg()));
|
||||
QWidget::connect(_removeArg, SIGNAL(clicked()), this,
|
||||
SLOT(RemoveArg()));
|
||||
@@ -103,7 +99,6 @@ MacroActionRunEdit::MacroActionRunEdit(
|
||||
auto *entryLayout = new QHBoxLayout;
|
||||
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
|
||||
{"{{filePath}}", _filePath},
|
||||
{"{{browseButton}}", _browseButton},
|
||||
};
|
||||
placeWidgets(obs_module_text("AdvSceneSwitcher.action.run.entry"),
|
||||
entryLayout, widgetPlaceholders);
|
||||
@@ -137,40 +132,25 @@ void MacroActionRunEdit::UpdateEntryData()
|
||||
if (!_entryData) {
|
||||
return;
|
||||
}
|
||||
_filePath->setText(QString::fromStdString(_entryData->_path));
|
||||
_filePath->SetPath(QString::fromStdString(_entryData->_path));
|
||||
for (auto &arg : _entryData->_args) {
|
||||
QListWidgetItem *item = new QListWidgetItem(arg, _argList);
|
||||
item->setData(Qt::UserRole, arg);
|
||||
}
|
||||
}
|
||||
|
||||
void MacroActionRunEdit::FilePathChanged()
|
||||
void MacroActionRunEdit::PathChanged(const QString &text)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_path = _filePath->text().toUtf8().constData();
|
||||
_entryData->_path = text.toUtf8().constData();
|
||||
emit HeaderInfoChanged(
|
||||
QString::fromStdString(_entryData->GetShortDesc()));
|
||||
}
|
||||
|
||||
void MacroActionRunEdit::BrowseButtonClicked()
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
QString path = QFileDialog::getOpenFileName(this);
|
||||
if (path.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
_filePath->setText(path);
|
||||
FilePathChanged();
|
||||
}
|
||||
|
||||
void MacroActionRunEdit::AddArg()
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
|
||||
46
src/macro-action-scene-swap.cpp
Normal file
46
src/macro-action-scene-swap.cpp
Normal file
@@ -0,0 +1,46 @@
|
||||
#include "headers/macro-action-scene-swap.hpp"
|
||||
#include "headers/advanced-scene-switcher.hpp"
|
||||
#include "headers/utility.hpp"
|
||||
|
||||
const std::string MacroActionSceneSwap::id = "scene_swap";
|
||||
|
||||
bool MacroActionSceneSwap::_registered = MacroActionFactory::Register(
|
||||
MacroActionSceneSwap::id,
|
||||
{MacroActionSceneSwap::Create, MacroActionSceneSwapEdit::Create,
|
||||
"AdvSceneSwitcher.action.sceneSwap"});
|
||||
|
||||
bool MacroActionSceneSwap::PerformAction()
|
||||
{
|
||||
obs_frontend_preview_program_trigger_transition();
|
||||
return true;
|
||||
}
|
||||
|
||||
void MacroActionSceneSwap::LogAction()
|
||||
{
|
||||
vblog(LOG_INFO, "trigger preview to active scene transition");
|
||||
}
|
||||
|
||||
bool MacroActionSceneSwap::Save(obs_data_t *obj)
|
||||
{
|
||||
MacroAction::Save(obj);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MacroActionSceneSwap::Load(obs_data_t *obj)
|
||||
{
|
||||
MacroAction::Load(obj);
|
||||
return true;
|
||||
}
|
||||
|
||||
MacroActionSceneSwapEdit::MacroActionSceneSwapEdit(
|
||||
QWidget *parent, std::shared_ptr<MacroActionSceneSwap> entryData)
|
||||
: QWidget(parent)
|
||||
{
|
||||
QHBoxLayout *mainLayout = new QHBoxLayout;
|
||||
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {};
|
||||
placeWidgets(obs_module_text("AdvSceneSwitcher.action.SceneSwap.entry"),
|
||||
mainLayout, widgetPlaceholders);
|
||||
setLayout(mainLayout);
|
||||
|
||||
_entryData = entryData;
|
||||
}
|
||||
@@ -10,51 +10,6 @@ bool MacroActionSceneTransform::_registered = MacroActionFactory::Register(
|
||||
MacroActionSceneTransformEdit::Create,
|
||||
"AdvSceneSwitcher.action.sceneTransform"});
|
||||
|
||||
static void load_transform_state(obs_data_t *obj,
|
||||
struct obs_transform_info &info,
|
||||
struct obs_sceneitem_crop &crop)
|
||||
{
|
||||
obs_data_get_vec2(obj, "pos", &info.pos);
|
||||
obs_data_get_vec2(obj, "scale", &info.scale);
|
||||
info.rot = (float)obs_data_get_double(obj, "rot");
|
||||
info.alignment = (uint32_t)obs_data_get_int(obj, "alignment");
|
||||
info.bounds_type =
|
||||
(enum obs_bounds_type)obs_data_get_int(obj, "bounds_type");
|
||||
info.bounds_alignment =
|
||||
(uint32_t)obs_data_get_int(obj, "bounds_alignment");
|
||||
obs_data_get_vec2(obj, "bounds", &info.bounds);
|
||||
crop.top = (int)obs_data_get_int(obj, "top");
|
||||
crop.bottom = (int)obs_data_get_int(obj, "bottom");
|
||||
crop.left = (int)obs_data_get_int(obj, "left");
|
||||
crop.right = (int)obs_data_get_int(obj, "right");
|
||||
}
|
||||
|
||||
bool save_transform_state(obs_data_t *obj, struct obs_transform_info &info,
|
||||
struct obs_sceneitem_crop &crop)
|
||||
{
|
||||
struct vec2 pos = info.pos;
|
||||
struct vec2 scale = info.scale;
|
||||
float rot = info.rot;
|
||||
uint32_t alignment = info.alignment;
|
||||
uint32_t bounds_type = info.bounds_type;
|
||||
uint32_t bounds_alignment = info.bounds_alignment;
|
||||
struct vec2 bounds = info.bounds;
|
||||
|
||||
obs_data_set_vec2(obj, "pos", &pos);
|
||||
obs_data_set_vec2(obj, "scale", &scale);
|
||||
obs_data_set_double(obj, "rot", rot);
|
||||
obs_data_set_int(obj, "alignment", alignment);
|
||||
obs_data_set_int(obj, "bounds_type", bounds_type);
|
||||
obs_data_set_vec2(obj, "bounds", &bounds);
|
||||
obs_data_set_int(obj, "bounds_alignment", bounds_alignment);
|
||||
obs_data_set_int(obj, "top", crop.top);
|
||||
obs_data_set_int(obj, "bottom", crop.bottom);
|
||||
obs_data_set_int(obj, "left", crop.left);
|
||||
obs_data_set_int(obj, "right", crop.right);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MacroActionSceneTransform::PerformAction()
|
||||
{
|
||||
auto s = obs_weak_source_get_source(_scene.GetScene(false));
|
||||
@@ -63,24 +18,10 @@ bool MacroActionSceneTransform::PerformAction()
|
||||
auto items = getSceneItemsWithName(scene, name);
|
||||
|
||||
for (auto &item : items) {
|
||||
// Merge new settings with current settings
|
||||
struct obs_transform_info info;
|
||||
struct obs_sceneitem_crop crop;
|
||||
obs_sceneitem_get_info(item, &info);
|
||||
obs_sceneitem_get_crop(item, &crop);
|
||||
auto temp = obs_data_create();
|
||||
save_transform_state(temp, info, crop);
|
||||
auto temp2 = obs_data_create();
|
||||
save_transform_state(temp, _info, _crop);
|
||||
obs_data_apply(temp, temp2);
|
||||
load_transform_state(temp, info, crop);
|
||||
obs_data_release(temp);
|
||||
obs_data_release(temp2);
|
||||
|
||||
// Apply settings
|
||||
obs_sceneitem_defer_update_begin(item);
|
||||
obs_sceneitem_set_info(item, &info);
|
||||
obs_sceneitem_set_crop(item, &crop);
|
||||
obs_sceneitem_set_info(item, &_info);
|
||||
obs_sceneitem_set_crop(item, &_crop);
|
||||
obs_sceneitem_defer_update_end(item);
|
||||
|
||||
obs_sceneitem_release(item);
|
||||
@@ -102,7 +43,7 @@ bool MacroActionSceneTransform::Save(obs_data_t *obj)
|
||||
MacroAction::Save(obj);
|
||||
_scene.Save(obj);
|
||||
obs_data_set_string(obj, "source", GetWeakSourceName(_source).c_str());
|
||||
save_transform_state(obj, _info, _crop);
|
||||
saveTransformState(obj, _info, _crop);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -112,7 +53,7 @@ bool MacroActionSceneTransform::Load(obs_data_t *obj)
|
||||
_scene.Load(obj);
|
||||
const char *sourceName = obs_data_get_string(obj, "source");
|
||||
_source = GetWeakSourceByName(sourceName);
|
||||
load_transform_state(obj, _info, _crop);
|
||||
loadTransformState(obj, _info, _crop);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -127,7 +68,7 @@ std::string MacroActionSceneTransform::GetShortDesc()
|
||||
std::string MacroActionSceneTransform::GetSettings()
|
||||
{
|
||||
auto data = obs_data_create();
|
||||
save_transform_state(data, _info, _crop);
|
||||
saveTransformState(data, _info, _crop);
|
||||
std::string json = obs_data_get_json(data);
|
||||
obs_data_release(data);
|
||||
return json;
|
||||
@@ -139,7 +80,22 @@ void MacroActionSceneTransform::SetSettings(std::string &settings)
|
||||
if (!data) {
|
||||
return;
|
||||
}
|
||||
load_transform_state(data, _info, _crop);
|
||||
loadTransformState(data, _info, _crop);
|
||||
if (obs_data_has_user_value(data, "size")) {
|
||||
auto obj = obs_data_get_obj(data, "size");
|
||||
auto source = obs_weak_source_get_source(_source);
|
||||
if (double h = obs_data_get_double(obj, "height")) {
|
||||
_info.scale.y =
|
||||
h / double(obs_source_get_height(source));
|
||||
}
|
||||
if (double w = obs_data_get_double(obj, "width")) {
|
||||
_info.scale.x =
|
||||
w / double(obs_source_get_width(source));
|
||||
}
|
||||
obs_data_release(obj);
|
||||
obs_source_release(source);
|
||||
}
|
||||
|
||||
obs_data_release(data);
|
||||
}
|
||||
|
||||
@@ -201,7 +157,7 @@ void MacroActionSceneTransformEdit::UpdateEntryData()
|
||||
GetWeakSourceName(_entryData->_source).c_str());
|
||||
if (_entryData->_source) {
|
||||
_settings->setPlainText(
|
||||
fromatJsonString(_entryData->GetSettings()));
|
||||
formatJsonString(_entryData->GetSettings()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -247,16 +203,7 @@ void MacroActionSceneTransformEdit::GetSettingsClicked()
|
||||
return;
|
||||
}
|
||||
|
||||
struct obs_transform_info info;
|
||||
struct obs_sceneitem_crop crop;
|
||||
obs_sceneitem_get_info(item, &info);
|
||||
obs_sceneitem_get_crop(item, &crop);
|
||||
|
||||
auto data = obs_data_create();
|
||||
save_transform_state(data, info, crop);
|
||||
auto json = obs_data_get_json(data);
|
||||
_settings->setPlainText(fromatJsonString(json));
|
||||
obs_data_release(data);
|
||||
_settings->setPlainText(formatJsonString(getSceneItemTransform(item)));
|
||||
}
|
||||
|
||||
void MacroActionSceneTransformEdit::SettingsChanged()
|
||||
|
||||
@@ -173,7 +173,7 @@ void MacroActionSourceEdit::GetSettingsClicked()
|
||||
}
|
||||
|
||||
_settings->setPlainText(
|
||||
fromatJsonString(getSourceSettings(_entryData->_source)));
|
||||
formatJsonString(getSourceSettings(_entryData->_source)));
|
||||
}
|
||||
|
||||
void MacroActionSourceEdit::SettingsChanged()
|
||||
|
||||
228
src/macro-action-timer.cpp
Normal file
228
src/macro-action-timer.cpp
Normal file
@@ -0,0 +1,228 @@
|
||||
#include "headers/macro-action-timer.hpp"
|
||||
#include "headers/macro-condition-timer.hpp"
|
||||
#include "headers/advanced-scene-switcher.hpp"
|
||||
#include "headers/utility.hpp"
|
||||
|
||||
#include <random>
|
||||
|
||||
const std::string MacroActionTimer::id = "timer";
|
||||
|
||||
bool MacroActionTimer::_registered = MacroActionFactory::Register(
|
||||
MacroActionTimer::id,
|
||||
{MacroActionTimer::Create, MacroActionTimerEdit::Create,
|
||||
"AdvSceneSwitcher.action.Timer"});
|
||||
|
||||
static std::map<TimerAction, std::string> timerActions = {
|
||||
{TimerAction::PAUSE, "AdvSceneSwitcher.action.timer.type.pause"},
|
||||
{TimerAction::CONTINUE, "AdvSceneSwitcher.action.timer.type.continue"},
|
||||
{TimerAction::RESET, "AdvSceneSwitcher.action.timer.type.reset"},
|
||||
{TimerAction::SET_TIME_REMAINING,
|
||||
"AdvSceneSwitcher.action.timer.type.setTimeRemaining"},
|
||||
};
|
||||
|
||||
bool MacroActionTimer::PerformAction()
|
||||
{
|
||||
if (!_macro.get()) {
|
||||
return true;
|
||||
}
|
||||
for (auto c : _macro->Conditions()) {
|
||||
if (c->GetId() != "timer") {
|
||||
continue;
|
||||
}
|
||||
auto timerCondition =
|
||||
dynamic_cast<MacroConditionTimer *>(c.get());
|
||||
if (!timerCondition) {
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (_actionType) {
|
||||
case TimerAction::PAUSE:
|
||||
timerCondition->Pause();
|
||||
break;
|
||||
case TimerAction::CONTINUE:
|
||||
timerCondition->Continue();
|
||||
break;
|
||||
case TimerAction::RESET:
|
||||
timerCondition->Reset();
|
||||
break;
|
||||
case TimerAction::SET_TIME_REMAINING:
|
||||
timerCondition->_duration.SetTimeRemaining(
|
||||
_duration.seconds);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void MacroActionTimer::LogAction()
|
||||
{
|
||||
if (!_macro.get()) {
|
||||
return;
|
||||
}
|
||||
switch (_actionType) {
|
||||
case TimerAction::PAUSE:
|
||||
vblog(LOG_INFO, "paused timers on \"%s\"",
|
||||
_macro->Name().c_str());
|
||||
break;
|
||||
case TimerAction::CONTINUE:
|
||||
vblog(LOG_INFO, "continued timers on \"%s\"",
|
||||
_macro->Name().c_str());
|
||||
break;
|
||||
case TimerAction::RESET:
|
||||
vblog(LOG_INFO, "reset timers on \"%s\"",
|
||||
_macro->Name().c_str());
|
||||
break;
|
||||
case TimerAction::SET_TIME_REMAINING:
|
||||
vblog(LOG_INFO,
|
||||
"set time remaining of timers on \"%s\" to \"%s\"",
|
||||
_macro->Name().c_str(), _duration.ToString().c_str());
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool MacroActionTimer::Save(obs_data_t *obj)
|
||||
{
|
||||
MacroAction::Save(obj);
|
||||
_macro.Save(obj);
|
||||
_duration.Save(obj);
|
||||
obs_data_set_int(obj, "actionType", static_cast<int>(_actionType));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MacroActionTimer::Load(obs_data_t *obj)
|
||||
{
|
||||
MacroAction::Load(obj);
|
||||
_macro.Load(obj);
|
||||
_duration.Load(obj);
|
||||
_actionType =
|
||||
static_cast<TimerAction>(obs_data_get_int(obj, "actionType"));
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string MacroActionTimer::GetShortDesc()
|
||||
{
|
||||
if (_macro.get()) {
|
||||
return _macro->Name();
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
static inline void populateTypeSelection(QComboBox *list)
|
||||
{
|
||||
for (auto entry : timerActions) {
|
||||
list->addItem(obs_module_text(entry.second.c_str()));
|
||||
}
|
||||
}
|
||||
|
||||
MacroActionTimerEdit::MacroActionTimerEdit(
|
||||
QWidget *parent, std::shared_ptr<MacroActionTimer> entryData)
|
||||
: QWidget(parent)
|
||||
{
|
||||
_macros = new MacroSelection(parent);
|
||||
_duration = new DurationSelection();
|
||||
_timerAction = new QComboBox();
|
||||
|
||||
populateTypeSelection(_timerAction);
|
||||
|
||||
QWidget::connect(_macros, SIGNAL(currentTextChanged(const QString &)),
|
||||
this, SLOT(MacroChanged(const QString &)));
|
||||
QWidget::connect(parent, SIGNAL(MacroRemoved(const QString &)), this,
|
||||
SLOT(MacroRemove(const QString &)));
|
||||
QWidget::connect(_duration, SIGNAL(DurationChanged(double)), this,
|
||||
SLOT(DurationChanged(double)));
|
||||
QWidget::connect(_duration, SIGNAL(UnitChanged(DurationUnit)), this,
|
||||
SLOT(DurationUnitChanged(DurationUnit)));
|
||||
QWidget::connect(_timerAction, SIGNAL(currentIndexChanged(int)), this,
|
||||
SLOT(ActionTypeChanged(int)));
|
||||
|
||||
_mainLayout = new QHBoxLayout;
|
||||
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
|
||||
{"{{macros}}", _macros},
|
||||
{"{{duration}}", _duration},
|
||||
{"{{timerAction}}", _timerAction},
|
||||
};
|
||||
placeWidgets(obs_module_text("AdvSceneSwitcher.action.timer.entry"),
|
||||
_mainLayout, widgetPlaceholders);
|
||||
setLayout(_mainLayout);
|
||||
|
||||
_entryData = entryData;
|
||||
UpdateEntryData();
|
||||
_loading = false;
|
||||
}
|
||||
|
||||
void MacroActionTimerEdit::UpdateEntryData()
|
||||
{
|
||||
if (!_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
_macros->SetCurrentMacro(_entryData->_macro.get());
|
||||
_duration->SetDuration(_entryData->_duration);
|
||||
_timerAction->setCurrentIndex(
|
||||
static_cast<int>(_entryData->_actionType));
|
||||
SetWidgetVisibility();
|
||||
}
|
||||
|
||||
void MacroActionTimerEdit::ActionTypeChanged(int value)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_actionType = static_cast<TimerAction>(value);
|
||||
SetWidgetVisibility();
|
||||
}
|
||||
|
||||
void MacroActionTimerEdit::SetWidgetVisibility()
|
||||
{
|
||||
if (!_entryData) {
|
||||
return;
|
||||
}
|
||||
_duration->setVisible(_entryData->_actionType ==
|
||||
TimerAction::SET_TIME_REMAINING);
|
||||
adjustSize();
|
||||
}
|
||||
|
||||
void MacroActionTimerEdit::DurationChanged(double seconds)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_duration.seconds = seconds;
|
||||
}
|
||||
|
||||
void MacroActionTimerEdit::DurationUnitChanged(DurationUnit unit)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_duration.displayUnit = unit;
|
||||
}
|
||||
|
||||
void MacroActionTimerEdit::MacroChanged(const QString &text)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_macro.UpdateRef(text);
|
||||
emit HeaderInfoChanged(
|
||||
QString::fromStdString(_entryData->GetShortDesc()));
|
||||
}
|
||||
|
||||
void MacroActionTimerEdit::MacroRemove(const QString &)
|
||||
{
|
||||
if (_entryData) {
|
||||
_entryData->_macro.UpdateRef();
|
||||
}
|
||||
}
|
||||
169
src/macro-action-transition.cpp
Normal file
169
src/macro-action-transition.cpp
Normal file
@@ -0,0 +1,169 @@
|
||||
#include "headers/macro-action-transition.hpp"
|
||||
#include "headers/advanced-scene-switcher.hpp"
|
||||
#include "headers/utility.hpp"
|
||||
|
||||
const std::string MacroActionTransition::id = "transition";
|
||||
|
||||
bool MacroActionTransition::_registered = MacroActionFactory::Register(
|
||||
MacroActionTransition::id,
|
||||
{MacroActionTransition::Create, MacroActionTransitionEdit::Create,
|
||||
"AdvSceneSwitcher.action.transition"});
|
||||
|
||||
bool MacroActionTransition::PerformAction()
|
||||
{
|
||||
if (_setType) {
|
||||
auto t =
|
||||
obs_weak_source_get_source(_transition.GetTransition());
|
||||
obs_frontend_set_current_transition(t);
|
||||
obs_source_release(t);
|
||||
}
|
||||
if (_setDuration) {
|
||||
obs_frontend_set_transition_duration(_duration.seconds * 1000);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void MacroActionTransition::LogAction()
|
||||
{
|
||||
if (_setDuration) {
|
||||
vblog(LOG_INFO, "set transition duration to %s",
|
||||
_duration.ToString().c_str());
|
||||
}
|
||||
if (_setType) {
|
||||
vblog(LOG_INFO, "set transition type to \"%s\"",
|
||||
_transition.ToString().c_str());
|
||||
}
|
||||
}
|
||||
|
||||
bool MacroActionTransition::Save(obs_data_t *obj)
|
||||
{
|
||||
MacroAction::Save(obj);
|
||||
_duration.Save(obj);
|
||||
_transition.Save(obj);
|
||||
obs_data_set_bool(obj, "setDuration", _setDuration);
|
||||
obs_data_set_bool(obj, "setType", _setType);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MacroActionTransition::Load(obs_data_t *obj)
|
||||
{
|
||||
MacroAction::Load(obj);
|
||||
_duration.Load(obj);
|
||||
_transition.Load(obj);
|
||||
_setDuration = obs_data_get_bool(obj, "setDuration");
|
||||
_setType = obs_data_get_bool(obj, "setType");
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string MacroActionTransition::GetShortDesc()
|
||||
{
|
||||
return _transition.ToString();
|
||||
}
|
||||
|
||||
MacroActionTransitionEdit::MacroActionTransitionEdit(
|
||||
QWidget *parent, std::shared_ptr<MacroActionTransition> entryData)
|
||||
: QWidget(parent)
|
||||
{
|
||||
_transitions = new TransitionSelectionWidget(this, false);
|
||||
_duration = new DurationSelection(this, false);
|
||||
_setType = new QCheckBox();
|
||||
_setDuration = new QCheckBox();
|
||||
|
||||
QWidget::connect(_transitions,
|
||||
SIGNAL(TransitionChanged(const TransitionSelection &)),
|
||||
this,
|
||||
SLOT(TransitionChanged(const TransitionSelection &)));
|
||||
QWidget::connect(_duration, SIGNAL(DurationChanged(double)), this,
|
||||
SLOT(DurationChanged(double)));
|
||||
QWidget::connect(_setType, SIGNAL(stateChanged(int)), this,
|
||||
SLOT(SetTypeChanged(int)));
|
||||
QWidget::connect(_setDuration, SIGNAL(stateChanged(int)), this,
|
||||
SLOT(SetDurationChanged(int)));
|
||||
|
||||
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
|
||||
{"{{transitions}}", _transitions},
|
||||
{"{{duration}}", _duration},
|
||||
{"{{setType}}", _setType},
|
||||
{"{{setDuration}}", _setDuration},
|
||||
};
|
||||
_typeLayout = new QHBoxLayout;
|
||||
placeWidgets(obs_module_text(
|
||||
"AdvSceneSwitcher.action.transition.entry.line1"),
|
||||
_typeLayout, widgetPlaceholders);
|
||||
_durationLayout = new QHBoxLayout;
|
||||
placeWidgets(obs_module_text(
|
||||
"AdvSceneSwitcher.action.transition.entry.line2"),
|
||||
_durationLayout, widgetPlaceholders);
|
||||
QVBoxLayout *mainLayout = new QVBoxLayout;
|
||||
mainLayout->addLayout(_typeLayout);
|
||||
mainLayout->addLayout(_durationLayout);
|
||||
setLayout(mainLayout);
|
||||
|
||||
_entryData = entryData;
|
||||
UpdateEntryData();
|
||||
_loading = false;
|
||||
}
|
||||
|
||||
void MacroActionTransitionEdit::UpdateEntryData()
|
||||
{
|
||||
if (!_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
_setDuration->setChecked(_entryData->_setDuration);
|
||||
_duration->SetDuration(_entryData->_duration);
|
||||
_setType->setChecked(_entryData->_setType);
|
||||
_transitions->SetTransition(_entryData->_transition);
|
||||
_transitions->setEnabled(_entryData->_setType);
|
||||
_duration->setEnabled(_entryData->_setDuration);
|
||||
}
|
||||
|
||||
void MacroActionTransitionEdit::TransitionChanged(const TransitionSelection &t)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_transition = t;
|
||||
emit HeaderInfoChanged(
|
||||
QString::fromStdString(_entryData->GetShortDesc()));
|
||||
}
|
||||
|
||||
void MacroActionTransitionEdit::DurationChanged(double seconds)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_duration.seconds = seconds;
|
||||
}
|
||||
|
||||
void MacroActionTransitionEdit::SetTypeChanged(int state)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_setType = state;
|
||||
_transitions->setEnabled(state);
|
||||
if (state) {
|
||||
emit HeaderInfoChanged(
|
||||
QString::fromStdString(_entryData->GetShortDesc()));
|
||||
} else {
|
||||
emit HeaderInfoChanged("");
|
||||
}
|
||||
}
|
||||
|
||||
void MacroActionTransitionEdit::SetDurationChanged(int state)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_setDuration = state;
|
||||
_duration->setEnabled(state);
|
||||
}
|
||||
@@ -1,3 +1,5 @@
|
||||
#ifdef VCAM_SUPPORTED
|
||||
|
||||
#include "headers/macro-action-virtual-cam.hpp"
|
||||
#include "headers/advanced-scene-switcher.hpp"
|
||||
#include "headers/utility.hpp"
|
||||
@@ -107,3 +109,5 @@ void MacroActionVCamEdit::ActionChanged(int value)
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_action = static_cast<VCamAction>(value);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -38,10 +38,12 @@ bool MacroActionWait::PerformAction()
|
||||
sleep_duration);
|
||||
|
||||
std::unique_lock<std::mutex> lock(switcher->m);
|
||||
auto r = switcher->cv.wait_for(
|
||||
switcher->abortMacroWait = false;
|
||||
switcher->macroWaitCv.wait_for(
|
||||
lock,
|
||||
std::chrono::milliseconds((long long)(sleep_duration * 1000)));
|
||||
return r == std::cv_status::timeout;
|
||||
std::chrono::milliseconds((long long)(sleep_duration * 1000)),
|
||||
[] { return switcher->abortMacroWait.load(); });
|
||||
return !switcher->abortMacroWait;
|
||||
}
|
||||
|
||||
bool MacroActionWait::Save(obs_data_t *obj)
|
||||
|
||||
@@ -10,12 +10,6 @@ bool MacroConditionCursor::_registered = MacroConditionFactory::Register(
|
||||
{MacroConditionCursor::Create, MacroConditionCursorEdit::Create,
|
||||
"AdvSceneSwitcher.condition.cursor"});
|
||||
|
||||
// TODO: Remove in future version - just added for backwards compatibility
|
||||
static const std::string idOld = "region";
|
||||
static bool oldRegisterd = MacroConditionFactory::Register(
|
||||
idOld, {MacroConditionCursor::Create, MacroConditionCursorEdit::Create,
|
||||
"AdvSceneSwitcher.condition.cursor"});
|
||||
|
||||
static std::map<CursorCondition, std::string> cursorConditionTypes = {
|
||||
{CursorCondition::REGION,
|
||||
"AdvSceneSwitcher.condition.cursor.type.region"},
|
||||
|
||||
@@ -10,21 +10,52 @@ bool MacroConditionDate::_registered = MacroConditionFactory::Register(
|
||||
{MacroConditionDate::Create, MacroConditionDateEdit::Create,
|
||||
"AdvSceneSwitcher.condition.date", false});
|
||||
|
||||
static std::map<DateCondition, std::string> dateConditionTypes = {
|
||||
{DateCondition::AT, "AdvSceneSwitcher.condition.date.state.at"},
|
||||
{DateCondition::AFTER, "AdvSceneSwitcher.condition.date.state.after"},
|
||||
{DateCondition::BEFORE, "AdvSceneSwitcher.condition.date.state.before"},
|
||||
{DateCondition::BETWEEN,
|
||||
"AdvSceneSwitcher.condition.date.state.between"},
|
||||
};
|
||||
|
||||
bool MacroConditionDate::CheckCondition()
|
||||
{
|
||||
bool match = false;
|
||||
QDateTime cur = QDateTime::currentDateTime();
|
||||
if (_ignoreDate) {
|
||||
_dateTime.setDate(cur.date());
|
||||
_dateTime2.setDate(cur.date());
|
||||
}
|
||||
if (_ignoreTime) {
|
||||
_dateTime.setTime(cur.time());
|
||||
_dateTime2.setTime(cur.time());
|
||||
}
|
||||
|
||||
switch (_condition) {
|
||||
case DateCondition::AT:
|
||||
match = _dateTime >= cur &&
|
||||
_dateTime <= cur.addMSecs(switcher->interval);
|
||||
break;
|
||||
case DateCondition::AFTER:
|
||||
match = cur >= _dateTime;
|
||||
break;
|
||||
case DateCondition::BEFORE:
|
||||
match = cur <= _dateTime;
|
||||
break;
|
||||
case DateCondition::BETWEEN:
|
||||
if (_dateTime2 > _dateTime) {
|
||||
match = cur >= _dateTime && cur <= _dateTime2;
|
||||
} else {
|
||||
match = cur >= _dateTime2 && cur <= _dateTime;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
match = _dateTime >= cur &&
|
||||
_dateTime <= cur.addMSecs(switcher->interval);
|
||||
if (match && _repeat) {
|
||||
_dateTime = _dateTime.addSecs(_duration.seconds);
|
||||
_dateTime2 = _dateTime2.addSecs(_duration.seconds);
|
||||
}
|
||||
|
||||
return match;
|
||||
@@ -33,8 +64,11 @@ bool MacroConditionDate::CheckCondition()
|
||||
bool MacroConditionDate::Save(obs_data_t *obj)
|
||||
{
|
||||
MacroCondition::Save(obj);
|
||||
obs_data_set_int(obj, "condition", static_cast<int>(_condition));
|
||||
obs_data_set_string(obj, "dateTime",
|
||||
_dateTime.toString().toStdString().c_str());
|
||||
obs_data_set_string(obj, "dateTime2",
|
||||
_dateTime2.toString().toStdString().c_str());
|
||||
obs_data_set_bool(obj, "ignoreDate", _ignoreDate);
|
||||
obs_data_set_bool(obj, "ignoreTime", _ignoreTime);
|
||||
obs_data_set_bool(obj, "repeat", _repeat);
|
||||
@@ -46,8 +80,12 @@ bool MacroConditionDate::Save(obs_data_t *obj)
|
||||
bool MacroConditionDate::Load(obs_data_t *obj)
|
||||
{
|
||||
MacroCondition::Load(obj);
|
||||
_condition =
|
||||
static_cast<DateCondition>(obs_data_get_int(obj, "condition"));
|
||||
_dateTime = QDateTime::fromString(
|
||||
QString::fromStdString(obs_data_get_string(obj, "dateTime")));
|
||||
_dateTime2 = QDateTime::fromString(
|
||||
QString::fromStdString(obs_data_get_string(obj, "dateTime2")));
|
||||
_ignoreDate = obs_data_get_bool(obj, "ignoreDate");
|
||||
_ignoreTime = obs_data_get_bool(obj, "ignoreTime");
|
||||
_repeat = obs_data_get_bool(obj, "repeat");
|
||||
@@ -60,20 +98,35 @@ std::string MacroConditionDate::GetShortDesc()
|
||||
return _dateTime.toString().toStdString();
|
||||
}
|
||||
|
||||
static inline void populateConditionSelection(QComboBox *list)
|
||||
{
|
||||
for (auto entry : dateConditionTypes) {
|
||||
list->addItem(obs_module_text(entry.second.c_str()));
|
||||
}
|
||||
}
|
||||
|
||||
MacroConditionDateEdit::MacroConditionDateEdit(
|
||||
QWidget *parent, std::shared_ptr<MacroConditionDate> entryData)
|
||||
: QWidget(parent)
|
||||
{
|
||||
_condition = new QComboBox();
|
||||
_dateTime = new QDateTimeEdit();
|
||||
_dateTime->setCalendarPopup(true);
|
||||
_dateTime->setDisplayFormat("yyyy.MM.dd hh:mm:ss");
|
||||
_dateTime2 = new QDateTimeEdit();
|
||||
_dateTime2->setCalendarPopup(true);
|
||||
_dateTime2->setDisplayFormat("yyyy.MM.dd hh:mm:ss");
|
||||
_ignoreDate = new QCheckBox();
|
||||
_ignoreTime = new QCheckBox();
|
||||
_repeat = new QCheckBox();
|
||||
_duration = new DurationSelection();
|
||||
|
||||
QWidget::connect(_condition, SIGNAL(currentIndexChanged(int)), this,
|
||||
SLOT(ConditionChanged(int)));
|
||||
QWidget::connect(_dateTime, SIGNAL(dateTimeChanged(const QDateTime &)),
|
||||
this, SLOT(DateTimeChanged(const QDateTime &)));
|
||||
QWidget::connect(_dateTime2, SIGNAL(dateTimeChanged(const QDateTime &)),
|
||||
this, SLOT(DateTime2Changed(const QDateTime &)));
|
||||
QWidget::connect(_ignoreDate, SIGNAL(stateChanged(int)), this,
|
||||
SLOT(IgnoreDateChanged(int)));
|
||||
QWidget::connect(_ignoreTime, SIGNAL(stateChanged(int)), this,
|
||||
@@ -85,9 +138,13 @@ MacroConditionDateEdit::MacroConditionDateEdit(
|
||||
QWidget::connect(_duration, SIGNAL(UnitChanged(DurationUnit)), this,
|
||||
SLOT(DurationUnitChanged(DurationUnit)));
|
||||
|
||||
populateConditionSelection(_condition);
|
||||
|
||||
auto line1Layout = new QHBoxLayout;
|
||||
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
|
||||
{"{{condition}}", _condition},
|
||||
{"{{dateTime}}", _dateTime},
|
||||
{"{{dateTime2}}", _dateTime2},
|
||||
{"{{ignoreDate}}", _ignoreDate},
|
||||
{"{{ignoreTime}}", _ignoreTime},
|
||||
{"{{repeat}}", _repeat},
|
||||
@@ -111,6 +168,18 @@ MacroConditionDateEdit::MacroConditionDateEdit(
|
||||
_loading = false;
|
||||
}
|
||||
|
||||
void MacroConditionDateEdit::ConditionChanged(int cond)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_condition = static_cast<DateCondition>(cond);
|
||||
ShowSecondDateSelection(_entryData->_condition ==
|
||||
DateCondition::BETWEEN);
|
||||
}
|
||||
|
||||
void MacroConditionDateEdit::DateTimeChanged(const QDateTime &datetime)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
@@ -124,6 +193,16 @@ void MacroConditionDateEdit::DateTimeChanged(const QDateTime &datetime)
|
||||
QString::fromStdString(_entryData->GetShortDesc()));
|
||||
}
|
||||
|
||||
void MacroConditionDateEdit::DateTime2Changed(const QDateTime &datetime)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_dateTime2 = datetime;
|
||||
}
|
||||
|
||||
void MacroConditionDateEdit::IgnoreDateChanged(int state)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
@@ -180,10 +259,21 @@ void MacroConditionDateEdit::UpdateEntryData()
|
||||
if (!_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
_condition->setCurrentIndex(static_cast<int>(_entryData->_condition));
|
||||
_dateTime->setDateTime(_entryData->_dateTime);
|
||||
_dateTime2->setDateTime(_entryData->_dateTime2);
|
||||
_ignoreDate->setChecked(_entryData->_ignoreDate);
|
||||
_ignoreTime->setChecked(_entryData->_ignoreTime);
|
||||
_repeat->setChecked(_entryData->_repeat);
|
||||
_duration->SetDuration(_entryData->_duration);
|
||||
_duration->setDisabled(!_entryData->_repeat);
|
||||
ShowSecondDateSelection(_entryData->_condition ==
|
||||
DateCondition::BETWEEN);
|
||||
}
|
||||
|
||||
void MacroConditionDateEdit::ShowSecondDateSelection(bool visible)
|
||||
{
|
||||
_dateTime2->setVisible(visible);
|
||||
adjustSize();
|
||||
}
|
||||
|
||||
@@ -254,24 +254,31 @@ void AdvSceneSwitcher::AddMacroCondition(int idx)
|
||||
return;
|
||||
}
|
||||
|
||||
MacroConditionScene temp;
|
||||
std::string id = temp.GetId();
|
||||
|
||||
std::string id;
|
||||
if (idx - 1 >= 0) {
|
||||
id = macro->Conditions().at(idx - 1)->GetId();
|
||||
} else {
|
||||
MacroConditionScene temp;
|
||||
id = temp.GetId();
|
||||
}
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
bool root = idx == 0;
|
||||
auto cond =
|
||||
macro->Conditions().emplace(macro->Conditions().begin() + idx,
|
||||
MacroConditionFactory::Create(id));
|
||||
if (idx - 1 >= 0) {
|
||||
auto data = obs_data_create();
|
||||
macro->Conditions().at(idx - 1)->Save(data);
|
||||
macro->Conditions().at(idx)->Load(data);
|
||||
obs_data_release(data);
|
||||
}
|
||||
auto logic = root ? LogicType::ROOT_NONE : LogicType::NONE;
|
||||
(*cond)->SetLogicType(logic);
|
||||
macro->UpdateConditionIndices();
|
||||
|
||||
// All entry pointers in existing edit widgets after the new entry will
|
||||
// be invalidated by adding the new entry so we have to recreate them
|
||||
// and because I am lazy I am recreating every widget.
|
||||
//
|
||||
// If performance should become a concern this has to be revisited.
|
||||
SetEditMacro(*macro);
|
||||
clearLayout(ui->macroEditConditionLayout, idx);
|
||||
PopulateMacroConditions(*macro, idx);
|
||||
HighlightCondition(idx);
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::on_conditionAdd_clicked()
|
||||
@@ -300,21 +307,13 @@ void AdvSceneSwitcher::RemoveMacroCondition(int idx)
|
||||
macro->Conditions().erase(macro->Conditions().begin() + idx);
|
||||
macro->UpdateConditionIndices();
|
||||
|
||||
if (macro->Conditions().size() == 0) {
|
||||
ui->macroEditConditionHelp->setVisible(true);
|
||||
}
|
||||
|
||||
if (idx == 0 && macro->Conditions().size() > 0) {
|
||||
auto newRoot = macro->Conditions().at(0);
|
||||
newRoot->SetLogicType(LogicType::ROOT_NONE);
|
||||
}
|
||||
|
||||
// All entry pointers in existing edit widgets after the new entry will
|
||||
// be invalidated by adding the new entry so we have to recreate them
|
||||
// and because I am lazy I am recreating every widget.
|
||||
//
|
||||
// If performance should become a concern this has to be revisited.
|
||||
SetEditMacro(*macro);
|
||||
clearLayout(ui->macroEditConditionLayout, idx);
|
||||
PopulateMacroConditions(*macro, idx);
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::on_conditionRemove_clicked()
|
||||
|
||||
@@ -137,10 +137,7 @@ MacroConditionFileEdit::MacroConditionFileEdit(
|
||||
: QWidget(parent)
|
||||
{
|
||||
_fileType = new QComboBox();
|
||||
_filePath = new QLineEdit();
|
||||
_browseButton =
|
||||
new QPushButton(obs_module_text("AdvSceneSwitcher.browse"));
|
||||
_browseButton->setStyleSheet("border:1px solid gray;");
|
||||
_filePath = new FileSelection();
|
||||
_matchText = new QPlainTextEdit();
|
||||
_useRegex = new QCheckBox(
|
||||
obs_module_text("AdvSceneSwitcher.fileTab.useRegExp"));
|
||||
@@ -151,10 +148,8 @@ MacroConditionFileEdit::MacroConditionFileEdit(
|
||||
|
||||
QWidget::connect(_fileType, SIGNAL(currentIndexChanged(int)), this,
|
||||
SLOT(FileTypeChanged(int)));
|
||||
QWidget::connect(_filePath, SIGNAL(editingFinished()), this,
|
||||
SLOT(FilePathChanged()));
|
||||
QWidget::connect(_browseButton, SIGNAL(clicked()), this,
|
||||
SLOT(BrowseButtonClicked()));
|
||||
QWidget::connect(_filePath, SIGNAL(PathChanged(const QString &)), this,
|
||||
SLOT(PathChanged(const QString &)));
|
||||
QWidget::connect(_matchText, SIGNAL(textChanged()), this,
|
||||
SLOT(MatchTextChanged()));
|
||||
QWidget::connect(_useRegex, SIGNAL(stateChanged(int)), this,
|
||||
@@ -170,7 +165,6 @@ MacroConditionFileEdit::MacroConditionFileEdit(
|
||||
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
|
||||
{"{{fileType}}", _fileType},
|
||||
{"{{filePath}}", _filePath},
|
||||
{"{{browseButton}}", _browseButton},
|
||||
{"{{matchText}}", _matchText},
|
||||
{"{{useRegex}}", _useRegex},
|
||||
{"{{checkModificationDate}}", _checkModificationDate},
|
||||
@@ -209,7 +203,7 @@ void MacroConditionFileEdit::UpdateEntryData()
|
||||
|
||||
_fileType->setCurrentIndex(static_cast<int>(_entryData->_fileType));
|
||||
|
||||
_filePath->setText(QString::fromStdString(_entryData->_file));
|
||||
_filePath->SetPath(QString::fromStdString(_entryData->_file));
|
||||
_matchText->setPlainText(QString::fromStdString(_entryData->_text));
|
||||
_useRegex->setChecked(_entryData->_useRegex);
|
||||
_checkModificationDate->setChecked(_entryData->_useTime);
|
||||
@@ -225,10 +219,10 @@ void MacroConditionFileEdit::FileTypeChanged(int index)
|
||||
FileType type = static_cast<FileType>(index);
|
||||
|
||||
if (type == FileType::LOCAL) {
|
||||
_browseButton->setDisabled(false);
|
||||
_filePath->Button()->setDisabled(false);
|
||||
_checkModificationDate->setDisabled(false);
|
||||
} else {
|
||||
_browseButton->setDisabled(true);
|
||||
_filePath->Button()->setDisabled(true);
|
||||
_checkModificationDate->setDisabled(true);
|
||||
}
|
||||
|
||||
@@ -236,37 +230,18 @@ void MacroConditionFileEdit::FileTypeChanged(int index)
|
||||
_entryData->_fileType = type;
|
||||
}
|
||||
|
||||
void MacroConditionFileEdit::FilePathChanged()
|
||||
void MacroConditionFileEdit::PathChanged(const QString &text)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_file = _filePath->text().toUtf8().constData();
|
||||
_entryData->_file = text.toUtf8().constData();
|
||||
emit HeaderInfoChanged(
|
||||
QString::fromStdString(_entryData->GetShortDesc()));
|
||||
}
|
||||
|
||||
void MacroConditionFileEdit::BrowseButtonClicked()
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
QString path = QFileDialog::getOpenFileName(
|
||||
this,
|
||||
tr(obs_module_text("AdvSceneSwitcher.fileTab.selectRead")),
|
||||
QDir::currentPath(),
|
||||
tr(obs_module_text("AdvSceneSwitcher.fileTab.anyFileType")));
|
||||
if (path.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
_filePath->setText(path);
|
||||
FilePathChanged();
|
||||
}
|
||||
|
||||
void MacroConditionFileEdit::MatchTextChanged()
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
|
||||
@@ -191,8 +191,11 @@ void MacroConditionFilterEdit::GetSettingsClicked()
|
||||
return;
|
||||
}
|
||||
|
||||
_settings->setPlainText(
|
||||
fromatJsonString(getSourceSettings(_entryData->_filter)));
|
||||
QString json = formatJsonString(getSourceSettings(_entryData->_filter));
|
||||
if (_entryData->_regex) {
|
||||
json = escapeForRegex(json);
|
||||
}
|
||||
_settings->setPlainText(json);
|
||||
}
|
||||
|
||||
void MacroConditionFilterEdit::SettingsChanged()
|
||||
|
||||
@@ -10,12 +10,6 @@ bool MacroConditionMacro::_registered = MacroConditionFactory::Register(
|
||||
{MacroConditionMacro::Create, MacroConditionMacroEdit::Create,
|
||||
"AdvSceneSwitcher.condition.macro"});
|
||||
|
||||
// TODO: Remove in future version - just added for backward compatibility
|
||||
static std::string idOld = "counter";
|
||||
static bool oldRegisterd = MacroConditionFactory::Register(
|
||||
idOld, {MacroConditionMacro::Create, MacroConditionMacroEdit::Create,
|
||||
"AdvSceneSwitcher.condition.macro"});
|
||||
|
||||
static std::map<MacroConditionMacroType, std::string> macroConditionTypes = {
|
||||
{MacroConditionMacroType::COUNT,
|
||||
"AdvSceneSwitcher.condition.macro.type.count"},
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
#ifdef REPLAYBUFFER_SUPPORTED
|
||||
|
||||
#include "headers/macro-condition-edit.hpp"
|
||||
#include "headers/macro-condition-replay-buffer.hpp"
|
||||
#include "headers/utility.hpp"
|
||||
@@ -106,3 +108,5 @@ void MacroConditionReplayBufferEdit::UpdateEntryData()
|
||||
|
||||
_state->setCurrentIndex(static_cast<int>(_entryData->_state));
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -3,8 +3,6 @@
|
||||
#include "headers/utility.hpp"
|
||||
#include "headers/advanced-scene-switcher.hpp"
|
||||
|
||||
#include <regex>
|
||||
|
||||
const std::string MacroConditionSceneOrder::id = "scene_order";
|
||||
|
||||
bool MacroConditionSceneOrder::_registered = MacroConditionFactory::Register(
|
||||
|
||||
205
src/macro-condition-scene-transform.cpp
Normal file
205
src/macro-condition-scene-transform.cpp
Normal file
@@ -0,0 +1,205 @@
|
||||
#include "headers/macro-condition-edit.hpp"
|
||||
#include "headers/macro-condition-scene-transform.hpp"
|
||||
#include "headers/utility.hpp"
|
||||
#include "headers/advanced-scene-switcher.hpp"
|
||||
|
||||
const std::string MacroConditionSceneTransform::id = "scene_transform";
|
||||
|
||||
bool MacroConditionSceneTransform::_registered =
|
||||
MacroConditionFactory::Register(
|
||||
MacroConditionSceneTransform::id,
|
||||
{MacroConditionSceneTransform::Create,
|
||||
MacroConditionSceneTransformEdit::Create,
|
||||
"AdvSceneSwitcher.condition.sceneTransform"});
|
||||
|
||||
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);
|
||||
|
||||
for (auto &item : items) {
|
||||
auto json = getSceneItemTransform(item);
|
||||
if (matchJson(json, _settings, _regex)) {
|
||||
ret = true;
|
||||
}
|
||||
obs_sceneitem_release(item);
|
||||
}
|
||||
|
||||
obs_source_release(s);
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool MacroConditionSceneTransform::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, "settings", _settings.c_str());
|
||||
obs_data_set_bool(obj, "regex", _regex);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MacroConditionSceneTransform::Load(obs_data_t *obj)
|
||||
{
|
||||
MacroCondition::Load(obj);
|
||||
_scene.Load(obj);
|
||||
const char *sourceName = obs_data_get_string(obj, "source");
|
||||
_source = GetWeakSourceByName(sourceName);
|
||||
_settings = obs_data_get_string(obj, "settings");
|
||||
_regex = obs_data_get_bool(obj, "regex");
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string MacroConditionSceneTransform::GetShortDesc()
|
||||
{
|
||||
if (_source) {
|
||||
return _scene.ToString() + " - " + GetWeakSourceName(_source);
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
MacroConditionSceneTransformEdit::MacroConditionSceneTransformEdit(
|
||||
QWidget *parent,
|
||||
std::shared_ptr<MacroConditionSceneTransform> entryData)
|
||||
: QWidget(parent)
|
||||
{
|
||||
_scenes = new SceneSelectionWidget(window(), false, false, true);
|
||||
_sources = new QComboBox();
|
||||
_getSettings = new QPushButton(obs_module_text(
|
||||
"AdvSceneSwitcher.condition.sceneTransform.getTransform"));
|
||||
_settings = new QPlainTextEdit();
|
||||
_regex = new QCheckBox(obs_module_text(
|
||||
"AdvSceneSwitcher.condition.sceneTransform.regex"));
|
||||
|
||||
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(_getSettings, SIGNAL(clicked()), this,
|
||||
SLOT(GetSettingsClicked()));
|
||||
QWidget::connect(_settings, SIGNAL(textChanged()), this,
|
||||
SLOT(SettingsChanged()));
|
||||
QWidget::connect(_regex, SIGNAL(stateChanged(int)), this,
|
||||
SLOT(RegexChanged(int)));
|
||||
|
||||
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
|
||||
{"{{scenes}}", _scenes}, {"{{sources}}", _sources},
|
||||
{"{{settings}}", _settings}, {"{{getSettings}}", _getSettings},
|
||||
{"{{regex}}", _regex},
|
||||
};
|
||||
|
||||
QHBoxLayout *line1Layout = new QHBoxLayout;
|
||||
placeWidgets(
|
||||
obs_module_text(
|
||||
"AdvSceneSwitcher.condition.sceneTransform.entry.line1"),
|
||||
line1Layout, widgetPlaceholders);
|
||||
QHBoxLayout *line2Layout = new QHBoxLayout;
|
||||
placeWidgets(
|
||||
obs_module_text(
|
||||
"AdvSceneSwitcher.condition.sceneTransform.entry.line2"),
|
||||
line2Layout, widgetPlaceholders, false);
|
||||
QHBoxLayout *line3Layout = new QHBoxLayout;
|
||||
placeWidgets(
|
||||
obs_module_text(
|
||||
"AdvSceneSwitcher.condition.sceneTransform.entry.line3"),
|
||||
line3Layout, widgetPlaceholders);
|
||||
|
||||
QVBoxLayout *mainLayout = new QVBoxLayout;
|
||||
mainLayout->addLayout(line1Layout);
|
||||
mainLayout->addLayout(line2Layout);
|
||||
mainLayout->addLayout(line3Layout);
|
||||
setLayout(mainLayout);
|
||||
|
||||
_entryData = entryData;
|
||||
UpdateEntryData();
|
||||
_loading = false;
|
||||
}
|
||||
|
||||
void MacroConditionSceneTransformEdit::UpdateEntryData()
|
||||
{
|
||||
if (!_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
_scenes->SetScene(_entryData->_scene);
|
||||
populateSceneItemSelection(_sources, _entryData->_scene);
|
||||
_sources->setCurrentText(
|
||||
GetWeakSourceName(_entryData->_source).c_str());
|
||||
_regex->setChecked(_entryData->_regex);
|
||||
if (_entryData->_source) {
|
||||
_settings->setPlainText(
|
||||
QString::fromStdString(_entryData->_settings));
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
void MacroConditionSceneTransformEdit::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()));
|
||||
}
|
||||
|
||||
void MacroConditionSceneTransformEdit::GetSettingsClicked()
|
||||
{
|
||||
if (_loading || !_entryData || !_entryData->_scene.GetScene(false) ||
|
||||
!_entryData->_source) {
|
||||
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) {
|
||||
return;
|
||||
}
|
||||
|
||||
QString json = formatJsonString(getSceneItemTransform(item));
|
||||
if (_entryData->_regex) {
|
||||
json = escapeForRegex(json);
|
||||
}
|
||||
_settings->setPlainText(json);
|
||||
}
|
||||
|
||||
void MacroConditionSceneTransformEdit::SettingsChanged()
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_settings = _settings->toPlainText().toStdString();
|
||||
}
|
||||
|
||||
void MacroConditionSceneTransformEdit::RegexChanged(int state)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_regex = state;
|
||||
}
|
||||
@@ -13,18 +13,44 @@ bool MacroConditionScene::_registered = MacroConditionFactory::Register(
|
||||
static std::map<SceneType, std::string> sceneTypes = {
|
||||
{SceneType::CURRENT, "AdvSceneSwitcher.condition.scene.type.current"},
|
||||
{SceneType::PREVIOUS, "AdvSceneSwitcher.condition.scene.type.previous"},
|
||||
{SceneType::CHANGED, "AdvSceneSwitcher.condition.scene.type.changed"},
|
||||
{SceneType::NOTCHANGED,
|
||||
"AdvSceneSwitcher.condition.scene.type.notChanged"},
|
||||
};
|
||||
|
||||
bool MacroConditionScene::CheckCondition()
|
||||
{
|
||||
bool sceneMatch = false;
|
||||
if (_type == SceneType::CURRENT) {
|
||||
sceneMatch = switcher->currentScene == _scene.GetScene(false);
|
||||
} else {
|
||||
sceneMatch = switcher->previousScene == _scene.GetScene(false);
|
||||
bool sceneChanged = _lastSceneChangeTime !=
|
||||
switcher->lastSceneChangeTime;
|
||||
if (sceneChanged) {
|
||||
_lastSceneChangeTime = switcher->lastSceneChangeTime;
|
||||
}
|
||||
|
||||
return sceneMatch;
|
||||
switch (_type) {
|
||||
case SceneType::CURRENT:
|
||||
if (_waitForTransition) {
|
||||
return switcher->currentScene == _scene.GetScene(false);
|
||||
} else {
|
||||
|
||||
bool match = false;
|
||||
auto current = obs_frontend_get_current_scene();
|
||||
auto weak = obs_source_get_weak_source(current);
|
||||
match = weak == _scene.GetScene(false);
|
||||
obs_weak_source_release(weak);
|
||||
obs_source_release(current);
|
||||
return match;
|
||||
}
|
||||
case SceneType::PREVIOUS:
|
||||
return switcher->previousScene == _scene.GetScene(false);
|
||||
case SceneType::CHANGED:
|
||||
return sceneChanged;
|
||||
case SceneType::NOTCHANGED:
|
||||
return !sceneChanged;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool MacroConditionScene::Save(obs_data_t *obj)
|
||||
@@ -32,6 +58,7 @@ 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);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -40,6 +67,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;
|
||||
} else {
|
||||
_waitForTransition =
|
||||
obs_data_get_bool(obj, "waitForTransition");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -61,11 +94,15 @@ MacroConditionSceneEdit::MacroConditionSceneEdit(
|
||||
{
|
||||
_scenes = new SceneSelectionWidget(window(), false, false, false);
|
||||
_sceneType = new QComboBox();
|
||||
_waitForTransition = new QCheckBox(obs_module_text(
|
||||
"AdvSceneSwitcher.condition.scene.waitForTransition"));
|
||||
|
||||
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)));
|
||||
|
||||
populateTypeSelection(_sceneType);
|
||||
|
||||
@@ -73,6 +110,7 @@ MacroConditionSceneEdit::MacroConditionSceneEdit(
|
||||
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
|
||||
{"{{scenes}}", _scenes},
|
||||
{"{{sceneType}}", _sceneType},
|
||||
{"{{waitForTransition}}", _waitForTransition},
|
||||
};
|
||||
placeWidgets(obs_module_text("AdvSceneSwitcher.condition.scene.entry"),
|
||||
mainLayout, widgetPlaceholders);
|
||||
@@ -103,6 +141,24 @@ void MacroConditionSceneEdit::TypeChanged(int value)
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_type = static_cast<SceneType>(value);
|
||||
SetWidgetVisibility();
|
||||
}
|
||||
|
||||
void MacroConditionSceneEdit::WaitForTransitionChanged(int state)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_waitForTransition = state;
|
||||
}
|
||||
|
||||
void MacroConditionSceneEdit::SetWidgetVisibility()
|
||||
{
|
||||
_scenes->setVisible(_entryData->_type == SceneType::CURRENT ||
|
||||
_entryData->_type == SceneType::PREVIOUS);
|
||||
_waitForTransition->setVisible(_entryData->_type == SceneType::CURRENT);
|
||||
}
|
||||
|
||||
void MacroConditionSceneEdit::UpdateEntryData()
|
||||
@@ -113,4 +169,6 @@ void MacroConditionSceneEdit::UpdateEntryData()
|
||||
|
||||
_scenes->SetScene(_entryData->_scene);
|
||||
_sceneType->setCurrentIndex(static_cast<int>(_entryData->_type));
|
||||
_waitForTransition->setChecked(_entryData->_waitForTransition);
|
||||
SetWidgetVisibility();
|
||||
}
|
||||
|
||||
@@ -170,8 +170,11 @@ void MacroConditionSourceEdit::GetSettingsClicked()
|
||||
return;
|
||||
}
|
||||
|
||||
_settings->setPlainText(
|
||||
fromatJsonString(getSourceSettings(_entryData->_source)));
|
||||
QString json = formatJsonString(getSourceSettings(_entryData->_source));
|
||||
if (_entryData->_regex) {
|
||||
json = escapeForRegex(json);
|
||||
}
|
||||
_settings->setPlainText(json);
|
||||
}
|
||||
|
||||
void MacroConditionSourceEdit::SettingsChanged()
|
||||
|
||||
@@ -13,11 +13,21 @@ bool MacroConditionStream::_registered = MacroConditionFactory::Register(
|
||||
static std::map<StreamState, std::string> streamStates = {
|
||||
{StreamState::STOP, "AdvSceneSwitcher.condition.stream.state.stop"},
|
||||
{StreamState::START, "AdvSceneSwitcher.condition.stream.state.start"},
|
||||
{StreamState::STARTING,
|
||||
"AdvSceneSwitcher.condition.stream.state.starting"},
|
||||
{StreamState::STOPPING,
|
||||
"AdvSceneSwitcher.condition.stream.state.stopping"},
|
||||
};
|
||||
|
||||
bool MacroConditionStream::CheckCondition()
|
||||
{
|
||||
bool stateMatch = false;
|
||||
|
||||
bool streamStarting = switcher->lastStreamStartingTime !=
|
||||
_lastStreamStartingTime;
|
||||
bool streamStopping = switcher->lastStreamStoppingTime !=
|
||||
_lastStreamStoppingTime;
|
||||
|
||||
switch (_streamState) {
|
||||
case StreamState::STOP:
|
||||
stateMatch = !obs_frontend_streaming_active();
|
||||
@@ -25,9 +35,22 @@ bool MacroConditionStream::CheckCondition()
|
||||
case StreamState::START:
|
||||
stateMatch = obs_frontend_streaming_active();
|
||||
break;
|
||||
case StreamState::STARTING:
|
||||
stateMatch = streamStarting;
|
||||
break;
|
||||
case StreamState::STOPPING:
|
||||
stateMatch = streamStopping;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (streamStarting) {
|
||||
_lastStreamStartingTime = switcher->lastStreamStartingTime;
|
||||
}
|
||||
if (streamStopping) {
|
||||
_lastStreamStoppingTime = switcher->lastStreamStoppingTime;
|
||||
}
|
||||
return stateMatch;
|
||||
}
|
||||
|
||||
|
||||
@@ -10,14 +10,11 @@ bool MacroConditionTimer::_registered = MacroConditionFactory::Register(
|
||||
{MacroConditionTimer::Create, MacroConditionTimerEdit::Create,
|
||||
"AdvSceneSwitcher.condition.timer", false});
|
||||
|
||||
// TODO: Remove in future version - just added for backward compatibility
|
||||
static const std::string idOld = "interval";
|
||||
static bool oldRegisterd = MacroConditionFactory::Register(
|
||||
idOld, {MacroConditionTimer::Create, MacroConditionTimerEdit::Create,
|
||||
"AdvSceneSwitcher.condition.timer", false});
|
||||
|
||||
bool MacroConditionTimer::CheckCondition()
|
||||
{
|
||||
if (_paused) {
|
||||
return _remaining == 0.;
|
||||
}
|
||||
if (_duration.DurationReached()) {
|
||||
if (!_oneshot) {
|
||||
_duration.Reset();
|
||||
@@ -31,6 +28,16 @@ bool MacroConditionTimer::Save(obs_data_t *obj)
|
||||
{
|
||||
MacroCondition::Save(obj);
|
||||
_duration.Save(obj);
|
||||
if (!_paused) {
|
||||
_remaining = _duration.TimeRemaining();
|
||||
}
|
||||
if (_saveRemaining) {
|
||||
obs_data_set_double(obj, "remaining", _remaining);
|
||||
} else {
|
||||
obs_data_set_double(obj, "remaining", _duration.seconds);
|
||||
}
|
||||
obs_data_set_bool(obj, "saveRemaining", _saveRemaining);
|
||||
obs_data_set_bool(obj, "paused", _paused);
|
||||
obs_data_set_bool(obj, "oneshot", _oneshot);
|
||||
return true;
|
||||
}
|
||||
@@ -39,20 +46,49 @@ bool MacroConditionTimer::Load(obs_data_t *obj)
|
||||
{
|
||||
MacroCondition::Load(obj);
|
||||
_duration.Load(obj);
|
||||
_remaining = obs_data_get_double(obj, "remaining");
|
||||
_paused = obs_data_get_bool(obj, "paused");
|
||||
_saveRemaining = obs_data_get_bool(obj, "saveRemaining");
|
||||
if (!obs_data_has_user_value(obj, "oneshot")) {
|
||||
_oneshot = false;
|
||||
} else {
|
||||
_oneshot = obs_data_get_bool(obj, "oneshot");
|
||||
}
|
||||
_duration.SetTimeRemaining(_remaining);
|
||||
return true;
|
||||
}
|
||||
|
||||
void MacroConditionTimer::Pause()
|
||||
{
|
||||
if (!_paused) {
|
||||
_paused = true;
|
||||
_remaining = _duration.TimeRemaining();
|
||||
}
|
||||
}
|
||||
|
||||
void MacroConditionTimer::Continue()
|
||||
{
|
||||
if (_paused) {
|
||||
_paused = false;
|
||||
_duration.SetTimeRemaining(_remaining);
|
||||
}
|
||||
}
|
||||
|
||||
void MacroConditionTimer::Reset()
|
||||
{
|
||||
_remaining = _duration.seconds;
|
||||
_duration.Reset();
|
||||
}
|
||||
|
||||
MacroConditionTimerEdit::MacroConditionTimerEdit(
|
||||
QWidget *parent, std::shared_ptr<MacroConditionTimer> entryData)
|
||||
: QWidget(parent)
|
||||
{
|
||||
_duration = new DurationSelection();
|
||||
_autoReset = new QCheckBox();
|
||||
_saveRemaining = new QCheckBox();
|
||||
_pauseConinue = new QPushButton(
|
||||
obs_module_text("AdvSceneSwitcher.condition.timer.pause"));
|
||||
_reset = new QPushButton(
|
||||
obs_module_text("AdvSceneSwitcher.condition.timer.reset"));
|
||||
_remaining = new QLabel();
|
||||
@@ -61,16 +97,22 @@ MacroConditionTimerEdit::MacroConditionTimerEdit(
|
||||
SLOT(DurationChanged(double)));
|
||||
QWidget::connect(_duration, SIGNAL(UnitChanged(DurationUnit)), this,
|
||||
SLOT(DurationUnitChanged(DurationUnit)));
|
||||
QWidget::connect(_pauseConinue, SIGNAL(clicked()), this,
|
||||
SLOT(PauseContinueClicked()));
|
||||
QWidget::connect(_reset, SIGNAL(clicked()), this, SLOT(ResetClicked()));
|
||||
QWidget::connect(_autoReset, SIGNAL(stateChanged(int)), this,
|
||||
SLOT(AutoResetChanged(int)));
|
||||
QWidget::connect(_saveRemaining, SIGNAL(stateChanged(int)), this,
|
||||
SLOT(SaveRemainingChanged(int)));
|
||||
|
||||
auto line1Layout = new QHBoxLayout;
|
||||
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
|
||||
{"{{duration}}", _duration},
|
||||
{"{{autoReset}}", _autoReset},
|
||||
{"{{remaining}}", _remaining},
|
||||
{"{{pauseContinue}}", _pauseConinue},
|
||||
{"{{reset}}", _reset},
|
||||
{"{{saveRemaining}}", _saveRemaining},
|
||||
};
|
||||
placeWidgets(
|
||||
obs_module_text("AdvSceneSwitcher.condition.timer.entry.line1"),
|
||||
@@ -118,6 +160,16 @@ void MacroConditionTimerEdit::DurationUnitChanged(DurationUnit unit)
|
||||
_entryData->_duration.displayUnit = unit;
|
||||
}
|
||||
|
||||
void MacroConditionTimerEdit::SaveRemainingChanged(int state)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_saveRemaining = state;
|
||||
}
|
||||
|
||||
void MacroConditionTimerEdit::AutoResetChanged(int state)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
@@ -128,6 +180,23 @@ void MacroConditionTimerEdit::AutoResetChanged(int state)
|
||||
_entryData->_oneshot = !state;
|
||||
}
|
||||
|
||||
void MacroConditionTimerEdit::PauseContinueClicked()
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
if (_entryData->_paused) {
|
||||
timer.start(1000);
|
||||
_entryData->Continue();
|
||||
} else {
|
||||
_entryData->Pause();
|
||||
timer.stop();
|
||||
}
|
||||
SetPauseContinueButtonLabel();
|
||||
}
|
||||
|
||||
void MacroConditionTimerEdit::ResetClicked()
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
@@ -135,7 +204,7 @@ void MacroConditionTimerEdit::ResetClicked()
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_duration.Reset();
|
||||
_entryData->Reset();
|
||||
}
|
||||
|
||||
void MacroConditionTimerEdit::UpdateTimeRemaining()
|
||||
@@ -144,8 +213,28 @@ void MacroConditionTimerEdit::UpdateTimeRemaining()
|
||||
_remaining->setText("-");
|
||||
return;
|
||||
}
|
||||
_remaining->setText(
|
||||
QString::number(_entryData->_duration.TimeRemaining()));
|
||||
|
||||
if (_entryData->_paused) {
|
||||
_remaining->setText(QString::number(_entryData->_remaining));
|
||||
} else {
|
||||
_remaining->setText(
|
||||
QString::number(_entryData->_duration.TimeRemaining()));
|
||||
}
|
||||
}
|
||||
|
||||
void MacroConditionTimerEdit::SetPauseContinueButtonLabel()
|
||||
{
|
||||
if (!_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (_entryData->_paused) {
|
||||
_pauseConinue->setText(obs_module_text(
|
||||
"AdvSceneSwitcher.condition.timer.continue"));
|
||||
} else {
|
||||
_pauseConinue->setText(obs_module_text(
|
||||
"AdvSceneSwitcher.condition.timer.pause"));
|
||||
}
|
||||
}
|
||||
|
||||
void MacroConditionTimerEdit::UpdateEntryData()
|
||||
@@ -156,4 +245,6 @@ void MacroConditionTimerEdit::UpdateEntryData()
|
||||
|
||||
_duration->SetDuration(_entryData->_duration);
|
||||
_autoReset->setChecked(!_entryData->_oneshot);
|
||||
_saveRemaining->setChecked(_entryData->_saveRemaining);
|
||||
SetPauseContinueButtonLabel();
|
||||
}
|
||||
|
||||
272
src/macro-condition-transition.cpp
Normal file
272
src/macro-condition-transition.cpp
Normal file
@@ -0,0 +1,272 @@
|
||||
#include "headers/macro-condition-edit.hpp"
|
||||
#include "headers/macro-condition-transition.hpp"
|
||||
#include "headers/utility.hpp"
|
||||
#include "headers/advanced-scene-switcher.hpp"
|
||||
|
||||
const std::string MacroConditionTransition::id = "transition";
|
||||
|
||||
bool MacroConditionTransition::_registered = MacroConditionFactory::Register(
|
||||
MacroConditionTransition::id,
|
||||
{MacroConditionTransition::Create, MacroConditionTransitionEdit::Create,
|
||||
"AdvSceneSwitcher.condition.transition", false});
|
||||
|
||||
static std::map<TransitionCondition, std::string> filterConditionTypes = {
|
||||
{TransitionCondition::CURRENT,
|
||||
"AdvSceneSwitcher.condition.transition.type.current"},
|
||||
{TransitionCondition::DURATION,
|
||||
"AdvSceneSwitcher.condition.transition.type.duration"},
|
||||
{TransitionCondition::STARTED,
|
||||
"AdvSceneSwitcher.condition.transition.type.started"},
|
||||
{TransitionCondition::ENDED,
|
||||
"AdvSceneSwitcher.condition.transition.type.ended"},
|
||||
};
|
||||
|
||||
bool isCurrentTransition(OBSWeakSource &t)
|
||||
{
|
||||
bool match;
|
||||
auto tSource = obs_frontend_get_current_transition();
|
||||
auto tWeakSource = obs_source_get_weak_source(tSource);
|
||||
match = t == tWeakSource;
|
||||
obs_weak_source_release(tWeakSource);
|
||||
obs_source_release(tSource);
|
||||
return match;
|
||||
}
|
||||
|
||||
// Relies on the fact that switcher->currentScene will only be updated on event
|
||||
// OBS_FRONTEND_EVENT_SCENE_CHANGED but obs_frontend_get_current_scene() will
|
||||
// already return the scene to be transitioned to.
|
||||
bool anySceneTransitionStarted()
|
||||
{
|
||||
bool ret;
|
||||
auto currentSceneSrouce = obs_frontend_get_current_scene();
|
||||
auto currentScene = obs_source_get_weak_source(currentSceneSrouce);
|
||||
ret = switcher->currentScene != currentScene;
|
||||
obs_weak_source_release(currentScene);
|
||||
obs_source_release(currentSceneSrouce);
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool MacroConditionTransition::CheckCondition()
|
||||
{
|
||||
bool anyTransitionEnded = _lastTransitionEndTime !=
|
||||
switcher->lastTransitionEndTime;
|
||||
bool transitionStarted = false;
|
||||
bool transitionEnded = false;
|
||||
|
||||
if (_transition.GetType() == TransitionSelectionType::ANY) {
|
||||
transitionStarted = anySceneTransitionStarted();
|
||||
transitionEnded = anyTransitionEnded;
|
||||
} else {
|
||||
transitionStarted = _started;
|
||||
transitionEnded = _ended;
|
||||
}
|
||||
|
||||
bool ret = false;
|
||||
switch (_condition) {
|
||||
case TransitionCondition::CURRENT: {
|
||||
auto transition = _transition.GetTransition();
|
||||
ret = isCurrentTransition(transition);
|
||||
break;
|
||||
}
|
||||
case TransitionCondition::DURATION:
|
||||
ret = _duration.seconds * 1000 ==
|
||||
obs_frontend_get_transition_duration();
|
||||
break;
|
||||
case TransitionCondition::STARTED:
|
||||
ret = transitionStarted;
|
||||
break;
|
||||
case TransitionCondition::ENDED:
|
||||
ret = transitionEnded;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
// Reset for next interval
|
||||
if (_started) {
|
||||
_started = false;
|
||||
}
|
||||
if (_ended) {
|
||||
_ended = false;
|
||||
}
|
||||
if (anyTransitionEnded) {
|
||||
_lastTransitionEndTime = switcher->lastTransitionEndTime;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void MacroConditionTransition::ConnectToTransitionSignals()
|
||||
{
|
||||
auto source = obs_weak_source_get_source(_transition.GetTransition());
|
||||
auto sh = obs_source_get_signal_handler(source);
|
||||
signal_handler_connect(sh, "transition_start", TransitionStarted, this);
|
||||
signal_handler_connect(sh, "transition_stop", TransitionEnded, this);
|
||||
obs_source_release(source);
|
||||
}
|
||||
|
||||
void MacroConditionTransition::DisconnectTransitionSignals()
|
||||
{
|
||||
auto source = obs_weak_source_get_source(_transition.GetTransition());
|
||||
auto sh = obs_source_get_signal_handler(source);
|
||||
signal_handler_disconnect(sh, "transition_start", TransitionStarted,
|
||||
this);
|
||||
signal_handler_disconnect(sh, "transition_stop", TransitionEnded, this);
|
||||
obs_source_release(source);
|
||||
}
|
||||
|
||||
void MacroConditionTransition::TransitionStarted(void *data, calldata_t *)
|
||||
{
|
||||
auto *transitionCond = static_cast<MacroConditionTransition *>(data);
|
||||
transitionCond->_started = true;
|
||||
}
|
||||
|
||||
void MacroConditionTransition::TransitionEnded(void *data, calldata_t *)
|
||||
{
|
||||
auto *transitionCond = static_cast<MacroConditionTransition *>(data);
|
||||
transitionCond->_ended = true;
|
||||
}
|
||||
|
||||
bool MacroConditionTransition::Save(obs_data_t *obj)
|
||||
{
|
||||
MacroCondition::Save(obj);
|
||||
obs_data_set_int(obj, "condition", static_cast<int>(_condition));
|
||||
_transition.Save(obj);
|
||||
_duration.Save(obj);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MacroConditionTransition::Load(obs_data_t *obj)
|
||||
{
|
||||
MacroCondition::Load(obj);
|
||||
_condition = static_cast<TransitionCondition>(
|
||||
obs_data_get_int(obj, "condition"));
|
||||
_transition.Load(obj);
|
||||
_duration.Load(obj);
|
||||
ConnectToTransitionSignals();
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string MacroConditionTransition::GetShortDesc()
|
||||
{
|
||||
return _transition.ToString();
|
||||
}
|
||||
|
||||
static inline void populateConditionSelection(QComboBox *list)
|
||||
{
|
||||
for (auto entry : filterConditionTypes) {
|
||||
list->addItem(obs_module_text(entry.second.c_str()));
|
||||
}
|
||||
}
|
||||
|
||||
MacroConditionTransitionEdit::MacroConditionTransitionEdit(
|
||||
QWidget *parent, std::shared_ptr<MacroConditionTransition> entryData)
|
||||
: QWidget(parent)
|
||||
{
|
||||
_conditions = new QComboBox();
|
||||
_transitions = new TransitionSelectionWidget(this, true, true);
|
||||
_duration = new DurationSelection(this, false);
|
||||
_durationSuffix = new QLabel(obs_module_text(
|
||||
"AdvSceneSwitcher.condition.transition.durationSuffix"));
|
||||
|
||||
populateConditionSelection(_conditions);
|
||||
|
||||
QWidget::connect(_conditions, SIGNAL(currentIndexChanged(int)), this,
|
||||
SLOT(ConditionChanged(int)));
|
||||
QWidget::connect(_transitions,
|
||||
SIGNAL(TransitionChanged(const TransitionSelection &)),
|
||||
this,
|
||||
SLOT(TransitionChanged(const TransitionSelection &)));
|
||||
QWidget::connect(_duration, SIGNAL(DurationChanged(double)), this,
|
||||
SLOT(DurationChanged(double)));
|
||||
|
||||
QHBoxLayout *mainLayout = new QHBoxLayout;
|
||||
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
|
||||
{"{{conditions}}", _conditions},
|
||||
{"{{transitions}}", _transitions},
|
||||
{"{{duration}}", _duration},
|
||||
{"{{durationSuffix}}", _durationSuffix},
|
||||
};
|
||||
placeWidgets(
|
||||
obs_module_text("AdvSceneSwitcher.condition.transition.entry"),
|
||||
mainLayout, widgetPlaceholders);
|
||||
setLayout(mainLayout);
|
||||
|
||||
_entryData = entryData;
|
||||
UpdateEntryData();
|
||||
_loading = false;
|
||||
}
|
||||
|
||||
void MacroConditionTransitionEdit::ConditionChanged(int index)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_condition =
|
||||
static_cast<TransitionCondition>(index);
|
||||
}
|
||||
SetWidgetVisibility();
|
||||
emit HeaderInfoChanged(
|
||||
QString::fromStdString(_entryData->GetShortDesc()));
|
||||
}
|
||||
|
||||
void MacroConditionTransitionEdit::TransitionChanged(
|
||||
const TransitionSelection &t)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->DisconnectTransitionSignals();
|
||||
_entryData->_transition = t;
|
||||
_entryData->ConnectToTransitionSignals();
|
||||
emit HeaderInfoChanged(
|
||||
QString::fromStdString(_entryData->GetShortDesc()));
|
||||
}
|
||||
|
||||
void MacroConditionTransitionEdit::DurationChanged(double seconds)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_duration.seconds = seconds;
|
||||
}
|
||||
|
||||
void MacroConditionTransitionEdit::SetWidgetVisibility()
|
||||
{
|
||||
if (!_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
_transitions->setVisible(
|
||||
_entryData->_condition == TransitionCondition::CURRENT ||
|
||||
_entryData->_condition == TransitionCondition::STARTED ||
|
||||
_entryData->_condition == TransitionCondition::ENDED);
|
||||
_duration->setVisible(_entryData->_condition ==
|
||||
TransitionCondition::DURATION);
|
||||
_durationSuffix->setVisible(_entryData->_condition ==
|
||||
TransitionCondition::DURATION);
|
||||
|
||||
bool addCurrent = _entryData->_condition ==
|
||||
TransitionCondition::DURATION;
|
||||
bool addAny = _entryData->_condition == TransitionCondition::STARTED ||
|
||||
_entryData->_condition == TransitionCondition::ENDED;
|
||||
_transitions->Repopulate(addCurrent, addAny);
|
||||
}
|
||||
|
||||
void MacroConditionTransitionEdit::UpdateEntryData()
|
||||
{
|
||||
if (!_entryData) {
|
||||
return;
|
||||
}
|
||||
SetWidgetVisibility();
|
||||
_conditions->setCurrentIndex(static_cast<int>(_entryData->_condition));
|
||||
_transitions->SetTransition(_entryData->_transition);
|
||||
_duration->SetDuration(_entryData->_duration);
|
||||
}
|
||||
@@ -26,26 +26,66 @@ static std::map<VideoCondition, std::string> conditionTypes = {
|
||||
"AdvSceneSwitcher.condition.video.condition.hasChanged"},
|
||||
{VideoCondition::NO_IMAGE,
|
||||
"AdvSceneSwitcher.condition.video.condition.noImage"},
|
||||
{VideoCondition::PATTERN,
|
||||
"AdvSceneSwitcher.condition.video.condition.pattern"},
|
||||
{VideoCondition::OBJECT,
|
||||
"AdvSceneSwitcher.condition.video.condition.object"},
|
||||
};
|
||||
|
||||
cv::CascadeClassifier initObjectCascade(std::string &path)
|
||||
{
|
||||
cv::CascadeClassifier cascade;
|
||||
try {
|
||||
cascade.load(path);
|
||||
} catch (...) {
|
||||
blog(LOG_WARNING, "failed to load model data \"%s\"",
|
||||
path.c_str());
|
||||
}
|
||||
return cascade;
|
||||
}
|
||||
|
||||
bool requiresFileInput(VideoCondition t)
|
||||
{
|
||||
return t == VideoCondition::MATCH || t == VideoCondition::DIFFER;
|
||||
return t == VideoCondition::MATCH || t == VideoCondition::DIFFER ||
|
||||
t == VideoCondition::PATTERN;
|
||||
}
|
||||
|
||||
bool MacroConditionVideo::CheckShouldBeSkipped()
|
||||
{
|
||||
if (_condition != VideoCondition::PATTERN &&
|
||||
_condition != VideoCondition::OBJECT) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (_throttleEnabled) {
|
||||
if (_runCount <= _throttleCount) {
|
||||
_runCount++;
|
||||
return true;
|
||||
} else {
|
||||
_runCount = 0;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool MacroConditionVideo::CheckCondition()
|
||||
{
|
||||
bool match = false;
|
||||
|
||||
if (_screenshotData) {
|
||||
if (_screenshotData->done) {
|
||||
match = Compare();
|
||||
if (CheckShouldBeSkipped()) {
|
||||
return _lastMatchResult;
|
||||
}
|
||||
|
||||
if (!requiresFileInput(_condition)) {
|
||||
_matchImage = std::move(_screenshotData->image);
|
||||
}
|
||||
_screenshotData.reset(nullptr);
|
||||
if (_screenshotData && _screenshotData->done) {
|
||||
match = Compare();
|
||||
_lastMatchResult = match;
|
||||
|
||||
if (!requiresFileInput(_condition)) {
|
||||
_matchImage = std::move(_screenshotData->image);
|
||||
}
|
||||
_screenshotData.reset(nullptr);
|
||||
} else {
|
||||
match = _lastMatchResult;
|
||||
}
|
||||
|
||||
GetScreenshot();
|
||||
@@ -59,20 +99,69 @@ bool MacroConditionVideo::Save(obs_data_t *obj)
|
||||
GetWeakSourceName(_videoSource).c_str());
|
||||
obs_data_set_int(obj, "condition", static_cast<int>(_condition));
|
||||
obs_data_set_string(obj, "filePath", _file.c_str());
|
||||
obs_data_set_bool(obj, "usePatternForChangedCheck",
|
||||
_usePatternForChangedCheck);
|
||||
obs_data_set_double(obj, "threshold", _patternThreshold);
|
||||
obs_data_set_bool(obj, "useAlphaAsMask", _useAlphaAsMask);
|
||||
obs_data_set_string(obj, "modelDataPath", _modelDataPath.c_str());
|
||||
obs_data_set_double(obj, "scaleFactor", _scaleFactor);
|
||||
obs_data_set_int(obj, "minNeighbors", _minNeighbors);
|
||||
obs_data_set_int(obj, "minSizeX", _minSizeX);
|
||||
obs_data_set_int(obj, "minSizeY", _minSizeY);
|
||||
obs_data_set_int(obj, "maxSizeX", _maxSizeX);
|
||||
obs_data_set_int(obj, "maxSizeY", _maxSizeY);
|
||||
obs_data_set_bool(obj, "throttleEnabled", _throttleEnabled);
|
||||
obs_data_set_int(obj, "throttleCount", _throttleCount);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool isScaleFactorValid(double scaleFactor)
|
||||
{
|
||||
return scaleFactor > 1.;
|
||||
}
|
||||
|
||||
bool isMinNeighborsValid(int minNeighbors)
|
||||
{
|
||||
return minNeighbors >= minMinNeighbors &&
|
||||
minNeighbors <= maxMinNeighbors;
|
||||
}
|
||||
|
||||
bool MacroConditionVideo::Load(obs_data_t *obj)
|
||||
{
|
||||
MacroCondition::Load(obj);
|
||||
const char *videoSourceName = obs_data_get_string(obj, "videoSource");
|
||||
_videoSource = GetWeakSourceByName(videoSourceName);
|
||||
_condition =
|
||||
static_cast<VideoCondition>(obs_data_get_int(obj, "condition"));
|
||||
_file = obs_data_get_string(obj, "filePath");
|
||||
_usePatternForChangedCheck =
|
||||
obs_data_get_bool(obj, "usePatternForChangedCheck");
|
||||
_patternThreshold = obs_data_get_double(obj, "threshold");
|
||||
_useAlphaAsMask = obs_data_get_bool(obj, "useAlphaAsMask");
|
||||
_modelDataPath = obs_data_get_string(obj, "modelDataPath");
|
||||
_scaleFactor = obs_data_get_double(obj, "scaleFactor");
|
||||
if (!isScaleFactorValid(_scaleFactor)) {
|
||||
_scaleFactor = 1.1;
|
||||
}
|
||||
_minNeighbors = obs_data_get_int(obj, "minNeighbors");
|
||||
if (!isMinNeighborsValid(_minNeighbors)) {
|
||||
_minNeighbors = minMinNeighbors;
|
||||
}
|
||||
_minSizeX = obs_data_get_int(obj, "minSizeX");
|
||||
_minSizeY = obs_data_get_int(obj, "minSizeY");
|
||||
_maxSizeX = obs_data_get_int(obj, "maxSizeX");
|
||||
_maxSizeY = obs_data_get_int(obj, "maxSizeY");
|
||||
_throttleEnabled = obs_data_get_bool(obj, "throttleEnabled");
|
||||
_throttleCount = obs_data_get_int(obj, "throttleCount");
|
||||
|
||||
if (requiresFileInput(_condition)) {
|
||||
(void)LoadImageFromFile();
|
||||
}
|
||||
|
||||
if (_condition == VideoCondition::OBJECT) {
|
||||
LoadModelData(_modelDataPath);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -91,6 +180,44 @@ void MacroConditionVideo::GetScreenshot()
|
||||
obs_source_release(source);
|
||||
}
|
||||
|
||||
// Assumption is that QImage uses Format_RGBA8888.
|
||||
// Conversion from: https://github.com/dbzhang800/QtOpenCV
|
||||
cv::Mat QImageToMat(const QImage &img)
|
||||
{
|
||||
if (img.isNull()) {
|
||||
return cv::Mat();
|
||||
}
|
||||
return cv::Mat(img.height(), img.width(), CV_8UC(img.depth() / 8),
|
||||
(uchar *)img.bits(), img.bytesPerLine());
|
||||
}
|
||||
|
||||
QImage MatToQImage(const cv::Mat &mat)
|
||||
{
|
||||
if (mat.empty()) {
|
||||
return QImage();
|
||||
}
|
||||
return QImage(mat.data, mat.cols, mat.rows, mat.step,
|
||||
QImage::Format::Format_RGBA8888);
|
||||
}
|
||||
|
||||
PatternMatchData createPatternData(QImage &pattern)
|
||||
{
|
||||
PatternMatchData data;
|
||||
if (pattern.isNull()) {
|
||||
return data;
|
||||
}
|
||||
|
||||
data.rgbaPattern = QImageToMat(pattern);
|
||||
std::vector<cv::Mat1b> rgbaChannelsPattern;
|
||||
cv::split(data.rgbaPattern, rgbaChannelsPattern);
|
||||
std::vector<cv::Mat1b> rgbChanlesPattern(
|
||||
rgbaChannelsPattern.begin(), rgbaChannelsPattern.begin() + 3);
|
||||
cv::merge(rgbChanlesPattern, data.rgbPattern);
|
||||
cv::threshold(rgbaChannelsPattern[3], data.mask, 0, 255,
|
||||
cv::THRESH_BINARY);
|
||||
return data;
|
||||
}
|
||||
|
||||
bool MacroConditionVideo::LoadImageFromFile()
|
||||
{
|
||||
if (!_matchImage.load(QString::fromStdString(_file))) {
|
||||
@@ -98,11 +225,108 @@ bool MacroConditionVideo::LoadImageFromFile()
|
||||
_file.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
_matchImage =
|
||||
_matchImage.convertToFormat(QImage::Format::Format_RGBX8888);
|
||||
_matchImage.convertToFormat(QImage::Format::Format_RGBA8888);
|
||||
_patternData = createPatternData(_matchImage);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MacroConditionVideo::LoadModelData(std::string &path)
|
||||
{
|
||||
_modelDataPath = path;
|
||||
_objectCascade = initObjectCascade(path);
|
||||
return !_objectCascade.empty();
|
||||
}
|
||||
|
||||
void matchPattern(QImage &img, PatternMatchData &patternData, double threshold,
|
||||
cv::Mat &result, bool useAlphaAsMask = true)
|
||||
{
|
||||
if (img.isNull() || patternData.rgbaPattern.empty()) {
|
||||
return;
|
||||
}
|
||||
if (img.height() < patternData.rgbaPattern.rows ||
|
||||
img.width() < patternData.rgbaPattern.cols) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto i = QImageToMat(img);
|
||||
|
||||
if (useAlphaAsMask) {
|
||||
std::vector<cv::Mat1b> rgbaChannelsImage;
|
||||
cv::split(i, rgbaChannelsImage);
|
||||
std::vector<cv::Mat1b> rgbChanlesImage(
|
||||
rgbaChannelsImage.begin(),
|
||||
rgbaChannelsImage.begin() + 3);
|
||||
|
||||
cv::Mat3b rgbImage;
|
||||
cv::merge(rgbChanlesImage, rgbImage);
|
||||
|
||||
cv::matchTemplate(rgbImage, patternData.rgbPattern, result,
|
||||
cv::TM_CCORR_NORMED, patternData.mask);
|
||||
cv::threshold(result, result, threshold, 0, cv::THRESH_TOZERO);
|
||||
|
||||
} else {
|
||||
cv::matchTemplate(i, patternData.rgbaPattern, result,
|
||||
cv::TM_CCOEFF_NORMED);
|
||||
cv::threshold(result, result, threshold, 0, cv::THRESH_TOZERO);
|
||||
}
|
||||
}
|
||||
|
||||
void matchPattern(QImage &img, QImage &pattern, double threshold,
|
||||
cv::Mat &result, bool useAlphaAsMask)
|
||||
{
|
||||
auto data = createPatternData(pattern);
|
||||
matchPattern(img, data, threshold, result, useAlphaAsMask);
|
||||
}
|
||||
|
||||
bool MacroConditionVideo::ScreenshotContainsPattern()
|
||||
{
|
||||
cv::Mat result;
|
||||
matchPattern(_screenshotData->image, _patternData, _patternThreshold,
|
||||
result, _useAlphaAsMask);
|
||||
return countNonZero(result) > 0;
|
||||
}
|
||||
|
||||
bool MacroConditionVideo::OutputChanged()
|
||||
{
|
||||
if (_usePatternForChangedCheck) {
|
||||
cv::Mat result;
|
||||
_patternData = createPatternData(_matchImage);
|
||||
matchPattern(_screenshotData->image, _patternData,
|
||||
_patternThreshold, result, _useAlphaAsMask);
|
||||
return countNonZero(result) == 0;
|
||||
}
|
||||
return _screenshotData->image != _matchImage;
|
||||
}
|
||||
|
||||
std::vector<cv::Rect> matchObject(QImage &img, cv::CascadeClassifier &cascade,
|
||||
double scaleFactor, int minNeighbors,
|
||||
cv::Size minSize, cv::Size maxSize)
|
||||
{
|
||||
if (img.isNull() || cascade.empty()) {
|
||||
return {};
|
||||
}
|
||||
|
||||
auto i = QImageToMat(img);
|
||||
cv::Mat frameGray;
|
||||
cv::cvtColor(i, frameGray, cv::COLOR_BGR2GRAY);
|
||||
equalizeHist(frameGray, frameGray);
|
||||
std::vector<cv::Rect> objects;
|
||||
cascade.detectMultiScale(frameGray, objects, scaleFactor, minNeighbors,
|
||||
0, minSize, maxSize);
|
||||
return objects;
|
||||
}
|
||||
|
||||
bool MacroConditionVideo::ScreenshotContainsObject()
|
||||
{
|
||||
auto objects = matchObject(_screenshotData->image, _objectCascade,
|
||||
_scaleFactor, _minNeighbors,
|
||||
{_minSizeX, _minSizeY},
|
||||
{_maxSizeX, _maxSizeY});
|
||||
return objects.size() > 0;
|
||||
}
|
||||
|
||||
bool MacroConditionVideo::Compare()
|
||||
{
|
||||
switch (_condition) {
|
||||
@@ -111,11 +335,15 @@ bool MacroConditionVideo::Compare()
|
||||
case VideoCondition::DIFFER:
|
||||
return _screenshotData->image != _matchImage;
|
||||
case VideoCondition::HAS_CHANGED:
|
||||
return _screenshotData->image != _matchImage;
|
||||
return OutputChanged();
|
||||
case VideoCondition::HAS_NOT_CHANGED:
|
||||
return _screenshotData->image == _matchImage;
|
||||
return !OutputChanged();
|
||||
case VideoCondition::NO_IMAGE:
|
||||
return _screenshotData->image.isNull();
|
||||
case VideoCondition::PATTERN:
|
||||
return ScreenshotContainsPattern();
|
||||
case VideoCondition::OBJECT:
|
||||
return ScreenshotContainsObject();
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -129,42 +357,205 @@ static inline void populateConditionSelection(QComboBox *list)
|
||||
}
|
||||
}
|
||||
|
||||
ThresholdSlider::ThresholdSlider(double min, double max, const QString &label,
|
||||
const QString &description, QWidget *parent)
|
||||
: QWidget(parent)
|
||||
{
|
||||
_slider = new QSlider();
|
||||
_slider->setOrientation(Qt::Horizontal);
|
||||
_slider->setRange(min * _scale, max * _scale);
|
||||
_value = new QLabel();
|
||||
QString labelText = label + QString("0.");
|
||||
for (int i = 0; i < _precision; i++) {
|
||||
labelText.append(QString("0"));
|
||||
}
|
||||
_value->setText(labelText);
|
||||
connect(_slider, SIGNAL(valueChanged(int)), this,
|
||||
SLOT(NotifyValueChanged(int)));
|
||||
QVBoxLayout *mainLayout = new QVBoxLayout();
|
||||
QHBoxLayout *sliderLayout = new QHBoxLayout();
|
||||
sliderLayout->addWidget(_value);
|
||||
sliderLayout->addWidget(_slider);
|
||||
mainLayout->addLayout(sliderLayout);
|
||||
if (!description.isEmpty()) {
|
||||
mainLayout->addWidget(new QLabel(description));
|
||||
}
|
||||
mainLayout->setContentsMargins(0, 0, 0, 0);
|
||||
setLayout(mainLayout);
|
||||
}
|
||||
|
||||
void ThresholdSlider::SetDoubleValue(double value)
|
||||
{
|
||||
_slider->setValue(value * _scale);
|
||||
SetDoubleValueText(value);
|
||||
}
|
||||
|
||||
void ThresholdSlider::NotifyValueChanged(int value)
|
||||
{
|
||||
double doubleValue = value / _scale;
|
||||
SetDoubleValueText(doubleValue);
|
||||
emit DoubleValueChanged(doubleValue);
|
||||
}
|
||||
|
||||
void ThresholdSlider::SetDoubleValueText(double value)
|
||||
{
|
||||
QString labelText = _value->text();
|
||||
labelText.chop(_precision + 2); // 2 for the part left of the "."
|
||||
labelText.append(QString::number(value, 'f', _precision));
|
||||
_value->setText(labelText);
|
||||
}
|
||||
|
||||
MacroConditionVideoEdit::MacroConditionVideoEdit(
|
||||
QWidget *parent, std::shared_ptr<MacroConditionVideo> entryData)
|
||||
: QWidget(parent)
|
||||
{
|
||||
_videoSelection = new QComboBox();
|
||||
_condition = new QComboBox();
|
||||
_filePath = new QLineEdit();
|
||||
_browseButton =
|
||||
new QPushButton(obs_module_text("AdvSceneSwitcher.browse"));
|
||||
|
||||
_filePath->setFixedWidth(100);
|
||||
_imagePath = new FileSelection();
|
||||
_imagePath->Button()->disconnect();
|
||||
_usePatternForChangedCheck = new QCheckBox(obs_module_text(
|
||||
"AdvSceneSwitcher.condition.video.usePatternForChangedCheck"));
|
||||
_usePatternForChangedCheck->setToolTip(obs_module_text(
|
||||
"AdvSceneSwitcher.condition.video.usePatternForChangedCheck.tooltip"));
|
||||
_patternThreshold = new ThresholdSlider(
|
||||
0., 1.,
|
||||
obs_module_text(
|
||||
"AdvSceneSwitcher.condition.video.patternThreshold"),
|
||||
obs_module_text(
|
||||
"AdvSceneSwitcher.condition.video.patternThresholdDescription"));
|
||||
_useAlphaAsMask = new QCheckBox(obs_module_text(
|
||||
"AdvSceneSwitcher.condition.video.patternThresholdUseAlphaAsMask"));
|
||||
|
||||
_browseButton->setStyleSheet("border:1px solid gray;");
|
||||
_modelDataPath = new FileSelection();
|
||||
_objectScaleThreshold = new ThresholdSlider(
|
||||
1.1, 5.,
|
||||
obs_module_text(
|
||||
"AdvSceneSwitcher.condition.video.objectScaleThreshold"),
|
||||
obs_module_text(
|
||||
"AdvSceneSwitcher.condition.video.objectScaleThresholdDescription"));
|
||||
_minNeighbors = new QSpinBox();
|
||||
_minNeighbors->setMinimum(minMinNeighbors);
|
||||
_minNeighbors->setMaximum(maxMinNeighbors);
|
||||
_minNeighborsDescription = new QLabel(obs_module_text(
|
||||
"AdvSceneSwitcher.condition.video.minNeighborDescription"));
|
||||
_minSizeX = new QSpinBox();
|
||||
_minSizeY = new QSpinBox();
|
||||
_minSizeX->setMaximum(1024);
|
||||
_minSizeY->setMaximum(1024);
|
||||
_maxSizeX = new QSpinBox();
|
||||
_maxSizeY = new QSpinBox();
|
||||
_maxSizeX->setMaximum(4096);
|
||||
_maxSizeY->setMaximum(4096);
|
||||
|
||||
_throttleEnable = new QCheckBox();
|
||||
_throttleCount = new QSpinBox();
|
||||
_throttleCount->setMinimum(1 * switcher->interval);
|
||||
_throttleCount->setMaximum(10 * switcher->interval);
|
||||
_throttleCount->setSingleStep(switcher->interval);
|
||||
_showMatch = new QPushButton(
|
||||
obs_module_text("AdvSceneSwitcher.condition.video.showMatch"));
|
||||
|
||||
QWidget::connect(_videoSelection,
|
||||
SIGNAL(currentTextChanged(const QString &)), this,
|
||||
SLOT(SourceChanged(const QString &)));
|
||||
QWidget::connect(_condition, SIGNAL(currentIndexChanged(int)), this,
|
||||
SLOT(ConditionChanged(int)));
|
||||
QWidget::connect(_filePath, SIGNAL(editingFinished()), this,
|
||||
SLOT(FilePathChanged()));
|
||||
QWidget::connect(_browseButton, SIGNAL(clicked()), this,
|
||||
SLOT(BrowseButtonClicked()));
|
||||
QWidget::connect(_imagePath, SIGNAL(PathChanged(const QString &)), this,
|
||||
SLOT(ImagePathChanged(const QString &)));
|
||||
QWidget::connect(_imagePath->Button(), SIGNAL(clicked()), this,
|
||||
SLOT(ImageBrowseButtonClicked()));
|
||||
QWidget::connect(_usePatternForChangedCheck, SIGNAL(stateChanged(int)),
|
||||
this, SLOT(UsePatternForChangedCheckChanged(int)));
|
||||
QWidget::connect(_patternThreshold, SIGNAL(DoubleValueChanged(double)),
|
||||
this, SLOT(PatternThresholdChanged(double)));
|
||||
QWidget::connect(_useAlphaAsMask, SIGNAL(stateChanged(int)), this,
|
||||
SLOT(UseAlphaAsMaskChanged(int)));
|
||||
QWidget::connect(_objectScaleThreshold,
|
||||
SIGNAL(DoubleValueChanged(double)), this,
|
||||
SLOT(ObjectScaleThresholdChanged(double)));
|
||||
QWidget::connect(_minNeighbors, SIGNAL(valueChanged(int)), this,
|
||||
SLOT(MinNeighborsChanged(int)));
|
||||
QWidget::connect(_minSizeX, SIGNAL(valueChanged(int)), this,
|
||||
SLOT(MinSizeXChanged(int)));
|
||||
QWidget::connect(_minSizeY, SIGNAL(valueChanged(int)), this,
|
||||
SLOT(MinSizeYChanged(int)));
|
||||
QWidget::connect(_maxSizeX, SIGNAL(valueChanged(int)), this,
|
||||
SLOT(MaxSizeXChanged(int)));
|
||||
QWidget::connect(_maxSizeY, SIGNAL(valueChanged(int)), this,
|
||||
SLOT(MaxSizeYChanged(int)));
|
||||
QWidget::connect(_modelDataPath, SIGNAL(PathChanged(const QString &)),
|
||||
this, SLOT(ModelPathChanged(const QString &)));
|
||||
QWidget::connect(_throttleEnable, SIGNAL(stateChanged(int)), this,
|
||||
SLOT(ThrottleEnableChanged(int)));
|
||||
QWidget::connect(_throttleCount, SIGNAL(valueChanged(int)), this,
|
||||
SLOT(ThrottleCountChanged(int)));
|
||||
QWidget::connect(_showMatch, SIGNAL(clicked()), this,
|
||||
SLOT(ShowMatchClicked()));
|
||||
|
||||
populateVideoSelection(_videoSelection);
|
||||
populateConditionSelection(_condition);
|
||||
|
||||
QHBoxLayout *mainLayout = new QHBoxLayout;
|
||||
QHBoxLayout *entryLine1Layout = new QHBoxLayout;
|
||||
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
|
||||
{"{{videoSources}}", _videoSelection},
|
||||
{"{{condition}}", _condition},
|
||||
{"{{filePath}}", _filePath},
|
||||
{"{{browseButton}}", _browseButton},
|
||||
{"{{imagePath}}", _imagePath},
|
||||
{"{{minNeighbors}}", _minNeighbors},
|
||||
{"{{minSizeX}}", _minSizeX},
|
||||
{"{{minSizeY}}", _minSizeY},
|
||||
{"{{maxSizeX}}", _maxSizeX},
|
||||
{"{{maxSizeY}}", _maxSizeY},
|
||||
{"{{modelDataPath}}", _modelDataPath},
|
||||
{"{{throttleEnable}}", _throttleEnable},
|
||||
{"{{throttleCount}}", _throttleCount},
|
||||
};
|
||||
placeWidgets(obs_module_text("AdvSceneSwitcher.condition.video.entry"),
|
||||
mainLayout, widgetPlaceholders);
|
||||
entryLine1Layout, widgetPlaceholders);
|
||||
|
||||
_modelPathLayout = new QHBoxLayout;
|
||||
placeWidgets(
|
||||
obs_module_text(
|
||||
"AdvSceneSwitcher.condition.video.entry.modelPath"),
|
||||
_modelPathLayout, widgetPlaceholders);
|
||||
|
||||
_neighborsControlLayout = new QHBoxLayout;
|
||||
placeWidgets(
|
||||
obs_module_text(
|
||||
"AdvSceneSwitcher.condition.video.entry.minNeighbor"),
|
||||
_neighborsControlLayout, widgetPlaceholders);
|
||||
|
||||
_minSizeControlLayout = new QHBoxLayout;
|
||||
placeWidgets(obs_module_text(
|
||||
"AdvSceneSwitcher.condition.video.entry.minSize"),
|
||||
_minSizeControlLayout, widgetPlaceholders);
|
||||
|
||||
_maxSizeControlLayout = new QHBoxLayout;
|
||||
placeWidgets(obs_module_text(
|
||||
"AdvSceneSwitcher.condition.video.entry.maxSize"),
|
||||
_maxSizeControlLayout, widgetPlaceholders);
|
||||
|
||||
_throttleControlLayout = new QHBoxLayout;
|
||||
placeWidgets(obs_module_text(
|
||||
"AdvSceneSwitcher.condition.video.entry.throttle"),
|
||||
_throttleControlLayout, widgetPlaceholders);
|
||||
|
||||
QHBoxLayout *showMatchLayout = new QHBoxLayout;
|
||||
showMatchLayout->addWidget(_showMatch);
|
||||
showMatchLayout->addStretch();
|
||||
QVBoxLayout *mainLayout = new QVBoxLayout;
|
||||
mainLayout->addLayout(entryLine1Layout);
|
||||
mainLayout->addWidget(_usePatternForChangedCheck);
|
||||
mainLayout->addWidget(_patternThreshold);
|
||||
mainLayout->addWidget(_useAlphaAsMask);
|
||||
mainLayout->addLayout(_modelPathLayout);
|
||||
mainLayout->addWidget(_objectScaleThreshold);
|
||||
mainLayout->addLayout(_neighborsControlLayout);
|
||||
mainLayout->addWidget(_minNeighborsDescription);
|
||||
mainLayout->addLayout(_minSizeControlLayout);
|
||||
mainLayout->addLayout(_maxSizeControlLayout);
|
||||
mainLayout->addLayout(showMatchLayout);
|
||||
mainLayout->addLayout(_throttleControlLayout);
|
||||
setLayout(mainLayout);
|
||||
|
||||
_entryData = entryData;
|
||||
@@ -198,12 +589,6 @@ void MacroConditionVideoEdit::UpdatePreviewTooltip()
|
||||
this->setToolTip(html);
|
||||
}
|
||||
|
||||
void MacroConditionVideoEdit::SetFilePath(const QString &text)
|
||||
{
|
||||
_filePath->setText(text);
|
||||
FilePathChanged();
|
||||
}
|
||||
|
||||
void MacroConditionVideoEdit::SourceChanged(const QString &text)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
@@ -212,6 +597,7 @@ void MacroConditionVideoEdit::SourceChanged(const QString &text)
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_videoSource = GetWeakSourceByQString(text);
|
||||
_entryData->ResetLastMatch();
|
||||
emit HeaderInfoChanged(
|
||||
QString::fromStdString(_entryData->GetShortDesc()));
|
||||
}
|
||||
@@ -224,14 +610,8 @@ void MacroConditionVideoEdit::ConditionChanged(int cond)
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_condition = static_cast<VideoCondition>(cond);
|
||||
|
||||
if (requiresFileInput(_entryData->_condition)) {
|
||||
_filePath->show();
|
||||
_browseButton->show();
|
||||
} else {
|
||||
_filePath->hide();
|
||||
_browseButton->hide();
|
||||
}
|
||||
_entryData->ResetLastMatch();
|
||||
SetWidgetVisibility();
|
||||
|
||||
// Reload image data to avoid incorrect matches.
|
||||
//
|
||||
@@ -241,22 +621,28 @@ void MacroConditionVideoEdit::ConditionChanged(int cond)
|
||||
if (_entryData->LoadImageFromFile()) {
|
||||
UpdatePreviewTooltip();
|
||||
}
|
||||
|
||||
if (_entryData->_condition == VideoCondition::OBJECT) {
|
||||
auto path = _entryData->GetModelDataPath();
|
||||
_entryData->_objectCascade = initObjectCascade(path);
|
||||
}
|
||||
}
|
||||
|
||||
void MacroConditionVideoEdit::FilePathChanged()
|
||||
void MacroConditionVideoEdit::ImagePathChanged(const QString &text)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_file = _filePath->text().toUtf8().constData();
|
||||
_entryData->_file = text.toUtf8().constData();
|
||||
_entryData->ResetLastMatch();
|
||||
if (_entryData->LoadImageFromFile()) {
|
||||
UpdatePreviewTooltip();
|
||||
}
|
||||
}
|
||||
|
||||
void MacroConditionVideoEdit::BrowseButtonClicked()
|
||||
void MacroConditionVideoEdit::ImageBrowseButtonClicked()
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
@@ -309,13 +695,299 @@ void MacroConditionVideoEdit::BrowseButtonClicked()
|
||||
std::this_thread::sleep_for(std::chrono::seconds(1));
|
||||
}
|
||||
if (!screenshot->done) {
|
||||
DisplayMessage("Failed to get screenshot of source!");
|
||||
DisplayMessage(obs_module_text(
|
||||
"AdvSceneSwitcher.condition.video.screenshotFail"));
|
||||
return;
|
||||
}
|
||||
|
||||
screenshot->image.save(path);
|
||||
}
|
||||
SetFilePath(path);
|
||||
_imagePath->SetPath(path);
|
||||
ImagePathChanged(path);
|
||||
}
|
||||
|
||||
void MacroConditionVideoEdit::UsePatternForChangedCheckChanged(int value)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_usePatternForChangedCheck = value;
|
||||
_patternThreshold->setVisible(value);
|
||||
adjustSize();
|
||||
}
|
||||
|
||||
void MacroConditionVideoEdit::PatternThresholdChanged(double value)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_patternThreshold = value;
|
||||
}
|
||||
|
||||
void MacroConditionVideoEdit::UseAlphaAsMaskChanged(int value)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_useAlphaAsMask = value;
|
||||
_entryData->LoadImageFromFile();
|
||||
}
|
||||
|
||||
void MacroConditionVideoEdit::ObjectScaleThresholdChanged(double value)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_scaleFactor = value;
|
||||
}
|
||||
|
||||
void MacroConditionVideoEdit::MinNeighborsChanged(int value)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_minNeighbors = value;
|
||||
}
|
||||
|
||||
void MacroConditionVideoEdit::MinSizeXChanged(int value)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_minSizeX = value;
|
||||
}
|
||||
|
||||
void MacroConditionVideoEdit::MinSizeYChanged(int value)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_minSizeY = value;
|
||||
}
|
||||
|
||||
void MacroConditionVideoEdit::MaxSizeXChanged(int value)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_maxSizeX = value;
|
||||
}
|
||||
|
||||
void MacroConditionVideoEdit::MaxSizeYChanged(int value)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_maxSizeY = value;
|
||||
}
|
||||
|
||||
void MacroConditionVideoEdit::ThrottleEnableChanged(int value)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_throttleEnabled = value;
|
||||
_throttleCount->setEnabled(value);
|
||||
}
|
||||
|
||||
void MacroConditionVideoEdit::ThrottleCountChanged(int value)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_throttleCount = value / switcher->interval;
|
||||
}
|
||||
|
||||
QImage markPatterns(cv::Mat &matchResult, QImage &image, QImage &pattern)
|
||||
{
|
||||
auto matchImg = QImageToMat(image);
|
||||
for (int row = 0; row < matchResult.rows - 1; row++) {
|
||||
for (int col = 0; col < matchResult.cols - 1; col++) {
|
||||
if (matchResult.at<float>(row, col) != 0.0) {
|
||||
rectangle(matchImg, {col, row},
|
||||
cv::Point(col + pattern.width(),
|
||||
row + pattern.height()),
|
||||
cv::Scalar(255, 0, 0, 255), 2, 8, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
return MatToQImage(matchImg);
|
||||
}
|
||||
|
||||
QImage markObjects(QImage &image, std::vector<cv::Rect> &objects)
|
||||
{
|
||||
auto frame = QImageToMat(image);
|
||||
for (size_t i = 0; i < objects.size(); i++) {
|
||||
rectangle(frame, cv::Point(objects[i].x, objects[i].y),
|
||||
cv::Point(objects[i].x + objects[i].width,
|
||||
objects[i].y + objects[i].height),
|
||||
cv::Scalar(255, 0, 0, 255), 2, 8, 0);
|
||||
}
|
||||
return MatToQImage(frame);
|
||||
}
|
||||
|
||||
void MacroConditionVideoEdit::ShowMatchClicked()
|
||||
{
|
||||
auto source = obs_weak_source_get_source(_entryData->_videoSource);
|
||||
auto screenshot = std::make_unique<AdvSSScreenshotObj>(source);
|
||||
obs_source_release(source);
|
||||
if (!screenshot->done) {
|
||||
std::this_thread::sleep_for(std::chrono::seconds(1));
|
||||
}
|
||||
if (!screenshot->done) {
|
||||
DisplayMessage(obs_module_text(
|
||||
"AdvSceneSwitcher.condition.video.screenshotFail"));
|
||||
return;
|
||||
}
|
||||
|
||||
QImage markedIamge;
|
||||
if (_entryData->_condition == VideoCondition::PATTERN) {
|
||||
cv::Mat result;
|
||||
QImage pattern = _entryData->GetMatchImage();
|
||||
matchPattern(screenshot->image, pattern,
|
||||
_entryData->_patternThreshold, result,
|
||||
_entryData->_useAlphaAsMask);
|
||||
if (countNonZero(result) == 0) {
|
||||
DisplayMessage(obs_module_text(
|
||||
"AdvSceneSwitcher.condition.video.patternMatchFail"));
|
||||
return;
|
||||
}
|
||||
markedIamge = markPatterns(result, screenshot->image, pattern);
|
||||
} else if (_entryData->_condition == VideoCondition::OBJECT) {
|
||||
auto objects = matchObject(
|
||||
screenshot->image, _entryData->_objectCascade,
|
||||
_entryData->_scaleFactor, _entryData->_minNeighbors,
|
||||
{_entryData->_minSizeX, _entryData->_minSizeY},
|
||||
{_entryData->_maxSizeX, _entryData->_maxSizeY});
|
||||
if (objects.empty()) {
|
||||
DisplayMessage(obs_module_text(
|
||||
"AdvSceneSwitcher.condition.video.objectMatchFail"));
|
||||
return;
|
||||
}
|
||||
markedIamge = markObjects(screenshot->image, objects);
|
||||
}
|
||||
|
||||
QLabel *label = new QLabel;
|
||||
label->setPixmap(QPixmap::fromImage(markedIamge));
|
||||
QVBoxLayout *layout = new QVBoxLayout;
|
||||
layout->addWidget(label);
|
||||
QDialog dialog;
|
||||
dialog.setLayout(layout);
|
||||
dialog.setWindowTitle("Advanced Scene Switcher");
|
||||
dialog.exec();
|
||||
}
|
||||
|
||||
void MacroConditionVideoEdit::ModelPathChanged(const QString &text)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
bool dataLoaded = false;
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
std::string path = text.toStdString();
|
||||
dataLoaded = _entryData->LoadModelData(path);
|
||||
}
|
||||
if (!dataLoaded) {
|
||||
DisplayMessage(obs_module_text(
|
||||
"AdvSceneSwitcher.condition.video.modelLoadFail"));
|
||||
}
|
||||
}
|
||||
|
||||
bool needsShowMatch(VideoCondition cond)
|
||||
{
|
||||
return cond == VideoCondition::PATTERN ||
|
||||
cond == VideoCondition::OBJECT;
|
||||
}
|
||||
|
||||
bool needsObjectControls(VideoCondition cond)
|
||||
{
|
||||
return cond == VideoCondition::OBJECT;
|
||||
}
|
||||
|
||||
bool needsThrottleControls(VideoCondition cond)
|
||||
{
|
||||
return cond == VideoCondition::PATTERN ||
|
||||
cond == VideoCondition::OBJECT;
|
||||
}
|
||||
|
||||
bool needsThreshold(VideoCondition cond)
|
||||
{
|
||||
return cond == VideoCondition::PATTERN ||
|
||||
cond == VideoCondition::HAS_CHANGED ||
|
||||
cond == VideoCondition::HAS_NOT_CHANGED;
|
||||
}
|
||||
|
||||
bool patternControlIsOptional(VideoCondition cond)
|
||||
{
|
||||
return cond == VideoCondition::HAS_CHANGED ||
|
||||
cond == VideoCondition::HAS_NOT_CHANGED;
|
||||
}
|
||||
|
||||
void setLayoutVisible(QLayout *layout, bool visible)
|
||||
{
|
||||
for (int i = 0; i < layout->count(); ++i) {
|
||||
QWidget *widget = layout->itemAt(i)->widget();
|
||||
if (widget != NULL) {
|
||||
widget->setVisible(visible);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MacroConditionVideoEdit::SetWidgetVisibility()
|
||||
{
|
||||
_imagePath->setVisible(requiresFileInput(_entryData->_condition));
|
||||
_usePatternForChangedCheck->setVisible(
|
||||
patternControlIsOptional(_entryData->_condition));
|
||||
_patternThreshold->setVisible(needsThreshold(_entryData->_condition));
|
||||
_useAlphaAsMask->setVisible(_entryData->_condition ==
|
||||
VideoCondition::PATTERN);
|
||||
_showMatch->setVisible(needsShowMatch(_entryData->_condition));
|
||||
_objectScaleThreshold->setVisible(
|
||||
needsObjectControls(_entryData->_condition));
|
||||
setLayoutVisible(_neighborsControlLayout,
|
||||
needsObjectControls(_entryData->_condition));
|
||||
_minNeighborsDescription->setVisible(
|
||||
needsObjectControls(_entryData->_condition));
|
||||
setLayoutVisible(_minSizeControlLayout,
|
||||
needsObjectControls(_entryData->_condition));
|
||||
setLayoutVisible(_maxSizeControlLayout,
|
||||
needsObjectControls(_entryData->_condition));
|
||||
setLayoutVisible(_modelPathLayout,
|
||||
needsObjectControls(_entryData->_condition));
|
||||
setLayoutVisible(_throttleControlLayout,
|
||||
needsThrottleControls(_entryData->_condition));
|
||||
|
||||
if (_entryData->_condition == VideoCondition::HAS_CHANGED ||
|
||||
_entryData->_condition == VideoCondition::HAS_NOT_CHANGED) {
|
||||
_patternThreshold->setVisible(
|
||||
_entryData->_usePatternForChangedCheck);
|
||||
}
|
||||
|
||||
adjustSize();
|
||||
}
|
||||
|
||||
void MacroConditionVideoEdit::UpdateEntryData()
|
||||
@@ -327,10 +999,20 @@ void MacroConditionVideoEdit::UpdateEntryData()
|
||||
_videoSelection->setCurrentText(
|
||||
GetWeakSourceName(_entryData->_videoSource).c_str());
|
||||
_condition->setCurrentIndex(static_cast<int>(_entryData->_condition));
|
||||
_filePath->setText(QString::fromStdString(_entryData->_file));
|
||||
|
||||
if (!requiresFileInput(_entryData->_condition)) {
|
||||
_filePath->hide();
|
||||
_browseButton->hide();
|
||||
}
|
||||
_imagePath->SetPath(QString::fromStdString(_entryData->_file));
|
||||
_usePatternForChangedCheck->setChecked(
|
||||
_entryData->_usePatternForChangedCheck);
|
||||
_patternThreshold->SetDoubleValue(_entryData->_patternThreshold);
|
||||
_useAlphaAsMask->setChecked(_entryData->_useAlphaAsMask);
|
||||
_modelDataPath->SetPath(_entryData->GetModelDataPath().c_str());
|
||||
_objectScaleThreshold->SetDoubleValue(_entryData->_scaleFactor);
|
||||
_minNeighbors->setValue(_entryData->_minNeighbors);
|
||||
_minSizeX->setValue(_entryData->_minSizeX);
|
||||
_minSizeY->setValue(_entryData->_minSizeY);
|
||||
_maxSizeX->setValue(_entryData->_maxSizeX);
|
||||
_maxSizeY->setValue(_entryData->_maxSizeY);
|
||||
_throttleEnable->setChecked(_entryData->_throttleEnabled);
|
||||
_throttleCount->setValue(_entryData->_throttleCount *
|
||||
switcher->interval);
|
||||
SetWidgetVisibility();
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
#ifdef VCAM_SUPPORTED
|
||||
|
||||
#include "headers/macro-condition-edit.hpp"
|
||||
#include "headers/macro-condition-virtual-cam.hpp"
|
||||
#include "headers/utility.hpp"
|
||||
@@ -99,3 +101,5 @@ void MacroConditionVCamEdit::UpdateEntryData()
|
||||
|
||||
_states->setCurrentIndex(static_cast<int>(_entryData->_state));
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include <obs.hpp>
|
||||
#include <QEvent>
|
||||
#include <QLabel>
|
||||
#include <QScrollBar>
|
||||
|
||||
bool MacroSegment::Save(obs_data_t *obj)
|
||||
{
|
||||
@@ -136,6 +137,16 @@ void MacroSegmentEdit::SetFocusPolicyOfWidgets()
|
||||
QList<QWidget *> widgets = this->findChildren<QWidget *>();
|
||||
for (auto w : widgets) {
|
||||
w->setFocusPolicy(Qt::StrongFocus);
|
||||
// Ignore QScrollBar as there is no danger of accidentally modifying anything
|
||||
// and long expanded QComboBox would be difficult to interact with otherwise.
|
||||
if (qobject_cast<QScrollBar *>(w)) {
|
||||
continue;
|
||||
}
|
||||
w->installEventFilter(new MouseWheelWidgetAdjustmentGuard(w));
|
||||
}
|
||||
}
|
||||
|
||||
void MacroSegmentEdit::SetCollapsed(bool collapsed)
|
||||
{
|
||||
_section->SetCollapsed(collapsed);
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include "headers/name-dialog.hpp"
|
||||
#include "headers/utility.hpp"
|
||||
|
||||
#include <QColor>
|
||||
#include <QMenu>
|
||||
|
||||
static QMetaObject::Connection addPulse;
|
||||
@@ -80,6 +81,8 @@ void AdvSceneSwitcher::on_macroRemove_clicked()
|
||||
QString name;
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
switcher->abortMacroWait = true;
|
||||
switcher->macroWaitCv.notify_one();
|
||||
int idx = ui->macros->currentRow();
|
||||
QString::fromStdString(switcher->macros[idx].Name());
|
||||
switcher->macros.erase(switcher->macros.begin() + idx);
|
||||
@@ -167,42 +170,74 @@ void AdvSceneSwitcher::on_macroName_editingFinished()
|
||||
emit MacroRenamed(oldName, newName);
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::PopulateMacroActions(Macro &m, int afterIdx)
|
||||
{
|
||||
bool root = afterIdx == 0;
|
||||
auto &actions = m.Actions();
|
||||
for (; afterIdx < actions.size(); afterIdx++) {
|
||||
auto newEntry = new MacroActionEdit(this, &actions[afterIdx],
|
||||
actions[afterIdx]->GetId());
|
||||
ConnectControlSignals(newEntry);
|
||||
ui->macroEditActionLayout->addWidget(newEntry);
|
||||
ui->macroEditActionHelp->setVisible(false);
|
||||
root = false;
|
||||
}
|
||||
ui->macroEditActionHelp->setVisible(actions.size() == 0);
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::PopulateMacroConditions(Macro &m, int afterIdx)
|
||||
{
|
||||
bool root = afterIdx == 0;
|
||||
auto &conditions = m.Conditions();
|
||||
for (; afterIdx < conditions.size(); afterIdx++) {
|
||||
auto newEntry = new MacroConditionEdit(
|
||||
this, &conditions[afterIdx],
|
||||
conditions[afterIdx]->GetId(), root);
|
||||
ConnectControlSignals(newEntry);
|
||||
ui->macroEditConditionLayout->addWidget(newEntry);
|
||||
ui->macroEditConditionHelp->setVisible(false);
|
||||
root = false;
|
||||
}
|
||||
ui->macroEditConditionHelp->setVisible(conditions.size() == 0);
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::SetEditMacro(Macro &m)
|
||||
{
|
||||
ui->macroName->setText(m.Name().c_str());
|
||||
clearLayout(ui->macroEditConditionLayout);
|
||||
clearLayout(ui->macroEditActionLayout);
|
||||
|
||||
bool root = true;
|
||||
for (auto &c : m.Conditions()) {
|
||||
auto newEntry =
|
||||
new MacroConditionEdit(this, &c, c->GetId(), root);
|
||||
ConnectControlSignals(newEntry);
|
||||
ui->macroEditConditionLayout->addWidget(newEntry);
|
||||
ui->macroEditConditionHelp->setVisible(false);
|
||||
root = false;
|
||||
}
|
||||
|
||||
for (auto &a : m.Actions()) {
|
||||
auto newEntry = new MacroActionEdit(this, &a, a->GetId());
|
||||
ConnectControlSignals(newEntry);
|
||||
ui->macroEditActionLayout->addWidget(newEntry);
|
||||
ui->macroEditActionHelp->setVisible(false);
|
||||
}
|
||||
|
||||
PopulateMacroConditions(m);
|
||||
PopulateMacroActions(m);
|
||||
ui->macroEdit->setDisabled(false);
|
||||
}
|
||||
|
||||
if (m.Conditions().size() == 0) {
|
||||
ui->macroEditConditionHelp->setVisible(true);
|
||||
} else {
|
||||
ui->macroEditConditionHelp->setVisible(false);
|
||||
void AdvSceneSwitcher::HighlightAction(int idx)
|
||||
{
|
||||
auto item = ui->macroEditActionLayout->itemAt(idx);
|
||||
if (!item) {
|
||||
return;
|
||||
}
|
||||
auto widget = item->widget();
|
||||
if (!widget) {
|
||||
return;
|
||||
}
|
||||
PulseWidget(widget, QColor(Qt::green), QColor(0, 0, 0, 0), "QLabel ",
|
||||
true);
|
||||
}
|
||||
|
||||
if (m.Actions().size() == 0) {
|
||||
ui->macroEditActionHelp->setVisible(true);
|
||||
} else {
|
||||
ui->macroEditActionHelp->setVisible(false);
|
||||
void AdvSceneSwitcher::HighlightCondition(int idx)
|
||||
{
|
||||
auto item = ui->macroEditConditionLayout->itemAt(idx);
|
||||
if (!item) {
|
||||
return;
|
||||
}
|
||||
auto widget = item->widget();
|
||||
if (!widget) {
|
||||
return;
|
||||
}
|
||||
PulseWidget(widget, QColor(Qt::green), QColor(0, 0, 0, 0), "QLabel ",
|
||||
true);
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::ConnectControlSignals(MacroActionEdit *c)
|
||||
@@ -369,17 +404,25 @@ void AdvSceneSwitcher::CopyMacro()
|
||||
ui->macros->setCurrentItem(item);
|
||||
}
|
||||
|
||||
void setCollapsedStateOfSegmentsIn(QLayout *layout, bool collapse)
|
||||
{
|
||||
QLayoutItem *item = nullptr;
|
||||
for (int i = 0; i < layout->count(); i++) {
|
||||
item = layout->itemAt(i);
|
||||
auto segment = dynamic_cast<MacroSegmentEdit *>(item->widget());
|
||||
if (segment) {
|
||||
segment->SetCollapsed(collapse);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::ExpandAllActions()
|
||||
{
|
||||
auto m = getSelectedMacro();
|
||||
if (!m) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (auto &a : m->Actions()) {
|
||||
a->SetCollapsed(false);
|
||||
}
|
||||
SetEditMacro(*m);
|
||||
setCollapsedStateOfSegmentsIn(ui->macroEditActionLayout, false);
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::ExpandAllConditions()
|
||||
@@ -388,11 +431,7 @@ void AdvSceneSwitcher::ExpandAllConditions()
|
||||
if (!m) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (auto &c : m->Conditions()) {
|
||||
c->SetCollapsed(false);
|
||||
}
|
||||
SetEditMacro(*m);
|
||||
setCollapsedStateOfSegmentsIn(ui->macroEditConditionLayout, false);
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::CollapseAllActions()
|
||||
@@ -401,11 +440,7 @@ void AdvSceneSwitcher::CollapseAllActions()
|
||||
if (!m) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (auto &a : m->Actions()) {
|
||||
a->SetCollapsed(true);
|
||||
}
|
||||
SetEditMacro(*m);
|
||||
setCollapsedStateOfSegmentsIn(ui->macroEditActionLayout, true);
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::CollapseAllConditions()
|
||||
@@ -414,9 +449,5 @@ void AdvSceneSwitcher::CollapseAllConditions()
|
||||
if (!m) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (auto &c : m->Conditions()) {
|
||||
c->SetCollapsed(true);
|
||||
}
|
||||
SetEditMacro(*m);
|
||||
setCollapsedStateOfSegmentsIn(ui->macroEditConditionLayout, true);
|
||||
}
|
||||
|
||||
@@ -79,7 +79,7 @@ bool Macro::CeckMatch()
|
||||
break;
|
||||
default:
|
||||
blog(LOG_WARNING,
|
||||
"ignoring unkown condition check for '%s'",
|
||||
"ignoring unknown condition check for '%s'",
|
||||
_name.c_str());
|
||||
break;
|
||||
}
|
||||
@@ -107,13 +107,13 @@ bool Macro::PerformAction()
|
||||
{
|
||||
bool ret = true;
|
||||
for (auto &a : _actions) {
|
||||
ret = ret && a->PerformAction();
|
||||
a->LogAction();
|
||||
ret = ret && a->PerformAction();
|
||||
if (!ret) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (ret && _count != std::numeric_limits<int>::max()) {
|
||||
if (_count != std::numeric_limits<int>::max()) {
|
||||
_count++;
|
||||
}
|
||||
return ret;
|
||||
@@ -249,7 +249,7 @@ bool Macro::Load(obs_data_t *obj)
|
||||
setValidLogic(c, root, _name);
|
||||
} else {
|
||||
blog(LOG_WARNING,
|
||||
"discarding condition entry with unkown id (%s) for macro %s",
|
||||
"discarding condition entry with unknown id (%s) for macro %s",
|
||||
id.c_str(), _name.c_str());
|
||||
}
|
||||
|
||||
@@ -273,7 +273,7 @@ bool Macro::Load(obs_data_t *obj)
|
||||
_actions.back()->Load(array_obj);
|
||||
} else {
|
||||
blog(LOG_WARNING,
|
||||
"discarding action entry with unkown id (%s) for macro %s",
|
||||
"discarding action entry with unknown id (%s) for macro %s",
|
||||
id.c_str(), _name.c_str());
|
||||
}
|
||||
|
||||
@@ -454,71 +454,8 @@ void SwitcherData::saveMacros(obs_data_t *obj)
|
||||
obs_data_array_release(macroArray);
|
||||
}
|
||||
|
||||
// Temporary helper functions to convert old settings format to new one
|
||||
static std::unordered_map<int, std::string> actionIntToActionString = {
|
||||
{2, "audio"}, {4, "recording"}, {5, "replay_buffer"}, {6, "run"},
|
||||
{3, "streaming"}, {0, "scene_switch"}, {1, "wait"},
|
||||
};
|
||||
|
||||
static void replaceActionIds(obs_data_t *obj)
|
||||
{
|
||||
obs_data_array_t *actions = obs_data_get_array(obj, "actions");
|
||||
size_t count = obs_data_array_count(actions);
|
||||
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
obs_data_t *array_obj = obs_data_array_item(actions, i);
|
||||
auto oldId = obs_data_get_int(array_obj, "id");
|
||||
obs_data_set_string(array_obj, "id",
|
||||
actionIntToActionString[oldId].c_str());
|
||||
obs_data_release(array_obj);
|
||||
}
|
||||
obs_data_array_release(actions);
|
||||
}
|
||||
|
||||
static std::unordered_map<int, std::string> conditionIntToConditionString = {
|
||||
{3, "audio"}, {4, "file"}, {10, "idle"}, {5, "media"},
|
||||
{11, "plugin_state"}, {9, "process"}, {8, "recording"}, {2, "region"},
|
||||
{0, "scene"}, {7, "streaming"}, {6, "video"}, {1, "window"},
|
||||
};
|
||||
|
||||
static void replaceConditionIds(obs_data_t *obj)
|
||||
{
|
||||
obs_data_array_t *conditions = obs_data_get_array(obj, "conditions");
|
||||
size_t count = obs_data_array_count(conditions);
|
||||
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
obs_data_t *array_obj = obs_data_array_item(conditions, i);
|
||||
auto oldId = obs_data_get_int(array_obj, "id");
|
||||
obs_data_set_string(
|
||||
array_obj, "id",
|
||||
conditionIntToConditionString[oldId].c_str());
|
||||
obs_data_release(array_obj);
|
||||
}
|
||||
obs_data_array_release(conditions);
|
||||
}
|
||||
|
||||
static void convertOldMacroIdsToString(obs_data_t *obj)
|
||||
{
|
||||
obs_data_array_t *macroArray = obs_data_get_array(obj, "macros");
|
||||
size_t count = obs_data_array_count(macroArray);
|
||||
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
obs_data_t *array_obj = obs_data_array_item(macroArray, i);
|
||||
replaceActionIds(array_obj);
|
||||
replaceConditionIds(array_obj);
|
||||
obs_data_release(array_obj);
|
||||
}
|
||||
obs_data_array_release(macroArray);
|
||||
}
|
||||
|
||||
void SwitcherData::loadMacros(obs_data_t *obj)
|
||||
{
|
||||
// TODO: Remove conversion helper in future version
|
||||
std::string previousVersion = obs_data_get_string(obj, "version");
|
||||
if (previousVersion == "2ce0b35921be892c987c7dbb5fc90db38f15f0a6") {
|
||||
convertOldMacroIdsToString(obj);
|
||||
}
|
||||
|
||||
macros.clear();
|
||||
|
||||
obs_data_array_t *macroArray = obs_data_get_array(obj, "macros");
|
||||
@@ -555,7 +492,7 @@ bool SwitcherData::checkMacros()
|
||||
|
||||
bool SwitcherData::runMacros()
|
||||
{
|
||||
for (auto &m : macros) {
|
||||
for (auto m : macros) {
|
||||
if (m.Matched()) {
|
||||
vblog(LOG_INFO, "running macro: %s", m.Name().c_str());
|
||||
if (!m.PerformAction()) {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user