Compare commits

...

33 Commits

Author SHA1 Message Date
WarmUpTill
084c263260 always match checkRandom() if random tab only has one entry 2020-12-10 20:14:07 +01:00
WarmUpTill
a1c592f1ec format 2020-12-10 20:14:07 +01:00
WarmUpTill
41f34f8f40 remove runtime of check() functions from interval sleep 2020-12-10 20:14:07 +01:00
WarmUpTill
9711390aac add no match delay option (#78) 2020-12-06 14:10:17 +01:00
WarmUpTill
25f28b9796 Add media state played to end (#58) 2020-12-06 14:09:21 +01:00
WarmUpTill
a1484f9621 fix warning 2020-12-06 14:03:04 +01:00
WarmUpTill
b54506c5ee add 'OBS' entry to GetWindowList() on Windows(#76) 2020-11-28 16:23:17 +01:00
WarmUpTill
4af7540497 hide guide frames when switching to a different tab (#75) 2020-11-28 12:36:58 +01:00
Guillaume Turchini
ba15e6812f Add a log when auto stopping stream / record (#74)
* Add a log when auto stopping stream / record

I had a hard time deciphering why the stream stopped automatically for someone when using the plugin as the logs contained nothing indicating an error or an action from the plugin.
He activated the checkbox to auto stop stream when a scene is activated (which I didn't know) so the plugin was doing exactly what asked.
I think adding a log is a good idea for an action as important as stopping the stream.
I added the same for starting stream for the same reasons.
2020-11-27 17:34:17 +01:00
WarmUpTill
22a90c7d64 fix crash on deleting last screen region entry when guide frame was active (#70) 2020-11-24 22:57:52 +01:00
WarmUpTill
f6c3c65e26 resolve potential deadlocks (#66)
A deadlock could occur if the save callback is called just before the frontend set functions are used.
Thus unlock() is necessary before setting current scene or transition.
(e.g. if start recording is activated just before using the above functions, as this apparently triggers a save() call)
2020-11-21 01:40:24 +01:00
WarmUpTill
4f6275619a Recommend building in-tree 2020-11-20 22:02:12 +01:00
WarmUpTill
d461342835 Update github actions (#65)
* fix macOS build

* remove unused BUILD_CAPTIONS variable
2020-11-20 22:00:33 +01:00
shugen002
16c4a2d5a9 Adjust UI And Improve (#63) 2020-11-20 19:10:06 +01:00
WarmUpTill
45a880d8f8 Add version info log (#61)
add generated version info log on startup of the plugin using the "GetGitRevisionDescription" cmake module from https://github.com/rpavlik/cmake-modules
2020-11-15 20:34:11 +01:00
WarmUpTill
bcf132685b Update CI (#60)
* Remove deprecated use of set-env in Github Actions

* use newer versions of checkout / upload-artifact / setup-msbuild
2020-11-14 19:32:16 +01:00
WarmUpTill
e5c1e81d81 clean up and silence warnings 2020-11-14 18:45:25 +01:00
WarmUpTill
2ae97cd90e Merge branch 'master' of https://github.com/WarmUpTill/SceneSwitcher 2020-11-14 17:11:21 +01:00
WarmUpTill
e5fffa0b82 log if sequence is canceled if verbose 2020-11-10 18:31:42 +01:00
WarmUpTill
a0119426d6 remove dead code 2020-11-10 18:31:42 +01:00
WarmUpTill
45e5738d2c do not hold main lock in callback handling 2020-11-10 18:31:42 +01:00
WarmUpTill
cdd1259f12 style changes 2020-11-09 22:20:58 +01:00
WarmUpTill
e5d6f2eef0 Merge branch 'master' of https://github.com/WarmUpTill/SceneSwitcher 2020-11-07 18:57:00 +01:00
WarmUpTill
e026bd7420 fix crash on first startup on fresh install of OBS 2020-11-07 18:56:08 +01:00
WarmUpTill
4cc1a53244 fix build by enabling BUILD_CAPTIONS (#56) 2020-11-02 19:44:30 +01:00
WarmUpTill
8eb99a3fde fix comment 2020-11-02 19:06:51 +01:00
WarmUpTill
bf30a058ee fix crash on deleting scene sequences
This crash could occur when a scene sequence was currently in progress and the entries were changed while in the 'wait for ...' state.
2020-11-02 19:05:19 +01:00
WarmUpTill
0c6a7b140e add comment about use of 'matched' 2020-11-02 19:00:50 +01:00
WarmUpTill
d279b35086 fix crash on importing scene sequence
This crash could occur when a scene sequence was currently in progress and the entries were changed while in the 'wait for ...' state.
2020-11-02 18:38:26 +01:00
WarmUpTill
8e26148255 fix media states being retriggered multiple times (#54) 2020-11-01 18:53:48 +01:00
WarmUpTill
1a3ed0d09c add clang format check to CI and adjust formatting (#55) 2020-11-01 12:39:14 +01:00
WarmUpTill
214821b69f UI rework (#44)
* rework general tab UI

* rework audio tab UI

* rework media tab UI

* rework time tab UI

* rework random tab UI

* rework window tab UI

* rework sreen region tab UI

* rework sequence tab UI

* rework transition tab UI

* adjust log messages to new format

* fix include path and warning

* highlight widgets to guide users (can be disabled)

* add helper frame for screen region tab

* rename main SceneSwitcher widget to AdvSceneSwitcher to resolve naming conflicts with the frontend tools on certain platforms (e.g. Debian 10)

* add media state 'any'

* adjust media switch handling to support vlc source
2020-10-31 16:25:44 +01:00
WarmUpTill
7edb6eb6c1 add handling of window title of metro apps 2020-10-24 15:23:08 +02:00
46 changed files with 4786 additions and 4328 deletions

View File

@@ -19,12 +19,12 @@ jobs:
MACOS_DEPS_VERSION: '2020-08-30' MACOS_DEPS_VERSION: '2020-08-30'
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v2 uses: actions/checkout@v2.3.4
with: with:
repository: obsproject/obs-studio repository: obsproject/obs-studio
submodules: 'recursive' submodules: 'recursive'
- name: "Checkout plugin" - name: "Checkout plugin"
uses: actions/checkout@v2 uses: actions/checkout@v2.3.4
with: with:
path: UI/frontend-plugins/${{ env.PLUGIN_NAME }} path: UI/frontend-plugins/${{ env.PLUGIN_NAME }}
- name: Fetch Git Tags - name: Fetch Git Tags
@@ -34,12 +34,15 @@ jobs:
- name: 'Install prerequisites (Homebrew)' - name: 'Install prerequisites (Homebrew)'
shell: bash shell: bash
run: | run: |
cd UI/frontend-plugins/${{ env.PLUGIN_NAME }}/CI/macos if [ -d /usr/local/opt/openssl@1.0.2t ]; then
brew untap local/openssl brew uninstall openssl@1.0.2t
brew untap local/python2 brew untap local/openssl
brew update fi
brew bundle if [ -d /usr/local/opt/python@2.7.17 ]; then
cd - brew uninstall python@2.7.17
brew untap local/python2
fi
brew bundle --file ./CI/scripts/macos/Brewfile
- name: 'Install prerequisite: Pre-built dependencies' - name: 'Install prerequisite: Pre-built dependencies'
if: steps.deps-cache.outputs.cache-hit != 'true' if: steps.deps-cache.outputs.cache-hit != 'true'
shell: bash shell: bash
@@ -59,7 +62,7 @@ jobs:
echo "add_subdirectory(${{ env.PLUGIN_NAME }})" >> UI/frontend-plugins/CMakeLists.txt echo "add_subdirectory(${{ env.PLUGIN_NAME }})" >> UI/frontend-plugins/CMakeLists.txt
mkdir ./build mkdir ./build
cd ./build cd ./build
cmake -DCMAKE_OSX_DEPLOYMENT_TARGET=10.11 -DDISABLE_PYTHON=ON -DDepsPath="/tmp/obsdeps" -DQTDIR="/tmp/obsdeps" .. cmake -DCMAKE_OSX_DEPLOYMENT_TARGET=10.13 -DDISABLE_PYTHON=ON -DDepsPath="/tmp/obsdeps" -DQTDIR="/tmp/obsdeps" ..
cd - cd -
- name: Build - name: Build
shell: bash shell: bash
@@ -86,14 +89,14 @@ jobs:
install_name_tool -change /tmp/obsdeps/lib/QtCore.framework/Versions/5/QtCore @executable_path/../Frameworks/QtCore.framework/Versions/5/QtCore ../../../build/UI/frontend-plugins/SceneSwitcher/advanced-scene-switcher.so install_name_tool -change /tmp/obsdeps/lib/QtCore.framework/Versions/5/QtCore @executable_path/../Frameworks/QtCore.framework/Versions/5/QtCore ../../../build/UI/frontend-plugins/SceneSwitcher/advanced-scene-switcher.so
FILE_DATE=$(date +%Y-%m-%d) FILE_DATE=$(date +%Y-%m-%d)
FILE_NAME=${{ env.PLUGIN_NAME }}-$FILE_DATE-${{ github.sha }}-macos.pkg FILE_NAME=${{ env.PLUGIN_NAME }}-$FILE_DATE-${{ github.sha }}-macos.pkg
echo "::set-env name=FILE_NAME::${FILE_NAME}" echo "FILE_NAME=${FILE_NAME}" >> $GITHUB_ENV
packagesbuild ./CI/macos/${{ env.PLUGIN_NAME }}.pkgproj packagesbuild ./CI/macos/${{ env.PLUGIN_NAME }}.pkgproj
cd - cd -
mkdir ./nightly mkdir ./nightly
mv UI/frontend-plugins/${{ env.PLUGIN_NAME }}/${{ env.PLUGIN_NAME }}.pkg ./nightly/${FILE_NAME} mv UI/frontend-plugins/${{ env.PLUGIN_NAME }}/${{ env.PLUGIN_NAME }}.pkg ./nightly/${FILE_NAME}
- name: Publish - name: Publish
if: success() if: success()
uses: actions/upload-artifact@v2-preview uses: actions/upload-artifact@v2.2.1
with: with:
name: '${{ env.FILE_NAME }}' name: '${{ env.FILE_NAME }}'
path: ./nightly/*.pkg path: ./nightly/*.pkg
@@ -102,12 +105,12 @@ jobs:
runs-on: [ubuntu-latest] runs-on: [ubuntu-latest]
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v2 uses: actions/checkout@v2.3.4
with: with:
repository: obsproject/obs-studio repository: obsproject/obs-studio
submodules: 'recursive' submodules: 'recursive'
- name: "Checkout plugin" - name: "Checkout plugin"
uses: actions/checkout@v2 uses: actions/checkout@v2.3.4
with: with:
path: UI/frontend-plugins/${{ env.PLUGIN_NAME }} path: UI/frontend-plugins/${{ env.PLUGIN_NAME }}
- name: Add plugin to obs cmake - name: Add plugin to obs cmake
@@ -166,7 +169,7 @@ jobs:
run: | run: |
mkdir ./build mkdir ./build
cd ./build cd ./build
cmake -DUNIX_STRUCTURE=0 -DCMAKE_INSTALL_PREFIX="${{ github.workspace }}/obs-studio-portable" -DBUILD_CAPTIONS=OFF -DWITH_RTMPS=OFF -DBUILD_BROWSER=OFF .. cmake -DUNIX_STRUCTURE=0 -DCMAKE_INSTALL_PREFIX="${{ github.workspace }}/obs-studio-portable" -DWITH_RTMPS=OFF -DBUILD_BROWSER=OFF ..
- name: 'Build' - name: 'Build'
shell: bash shell: bash
working-directory: ${{ github.workspace }}/build working-directory: ${{ github.workspace }}/build
@@ -176,12 +179,12 @@ jobs:
run: | run: |
FILE_DATE=$(date +%Y-%m-%d) FILE_DATE=$(date +%Y-%m-%d)
FILE_NAME=${{ env.PLUGIN_NAME }}-$FILE_DATE-${{ github.sha }}-linux64.tar.gz FILE_NAME=${{ env.PLUGIN_NAME }}-$FILE_DATE-${{ github.sha }}-linux64.tar.gz
echo "::set-env name=FILE_NAME::${FILE_NAME}" echo "FILE_NAME=${FILE_NAME}" >> $GITHUB_ENV
mkdir -p ./${{ env.PLUGIN_NAME }}/bin/64bit/ mkdir -p ./${{ env.PLUGIN_NAME }}/bin/64bit/
mv ./build/UI/frontend-plugins/${{ env.PLUGIN_NAME }}/${{ env.LIB_NAME }}.so ./${{ env.PLUGIN_NAME }}/bin/64bit/${{ env.LIB_NAME }}.so mv ./build/UI/frontend-plugins/${{ env.PLUGIN_NAME }}/${{ env.LIB_NAME }}.so ./${{ env.PLUGIN_NAME }}/bin/64bit/${{ env.LIB_NAME }}.so
tar -cvzf "${FILE_NAME}" ${{ env.PLUGIN_NAME }} tar -cvzf "${FILE_NAME}" ${{ env.PLUGIN_NAME }}
- name: 'Publish' - name: 'Publish'
uses: actions/upload-artifact@v2-preview uses: actions/upload-artifact@v2.2.1
with: with:
name: '${{ env.FILE_NAME }}' name: '${{ env.FILE_NAME }}'
path: '*.tar.gz' path: '*.tar.gz'
@@ -194,14 +197,14 @@ jobs:
CMAKE_SYSTEM_VERSION: "10.0.18363.657" CMAKE_SYSTEM_VERSION: "10.0.18363.657"
steps: steps:
- name: Add msbuild to PATH - name: Add msbuild to PATH
uses: microsoft/setup-msbuild@v1.0.0 uses: microsoft/setup-msbuild@v1.0.2
- name: Checkout obs - name: Checkout obs
uses: actions/checkout@v2 uses: actions/checkout@v2.3.4
with: with:
repository: obsproject/obs-studio repository: obsproject/obs-studio
submodules: 'recursive' submodules: 'recursive'
- name: Checkout plugin - name: Checkout plugin
uses: actions/checkout@v2 uses: actions/checkout@v2.3.4
with: with:
path: UI/frontend-plugins/${{ env.PLUGIN_NAME}} path: UI/frontend-plugins/${{ env.PLUGIN_NAME}}
- name: Add plugin to obs cmake - name: Add plugin to obs cmake
@@ -222,7 +225,7 @@ jobs:
mkdir ./build mkdir ./build
mkdir ./build64 mkdir ./build64
cd ./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 .. cmake -G"${{ env.CMAKE_GENERATOR }}" -A"x64" -DCMAKE_SYSTEM_VERSION="${{ env.CMAKE_SYSTEM_VERSION }}" -DBUILD_BROWSER=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 - name: Build
run: msbuild /m /p:Configuration=RelWithDebInfo .\build64\obs-studio.sln run: msbuild /m /p:Configuration=RelWithDebInfo .\build64\obs-studio.sln
- name: Package - name: Package
@@ -230,12 +233,12 @@ jobs:
run: | run: |
$env:FILE_DATE=(Get-Date -UFormat "%F") $env:FILE_DATE=(Get-Date -UFormat "%F")
$env:FILE_NAME="${{ env.PLUGIN_NAME }}-${env:FILE_DATE}-${{ github.sha }}-win64.zip" $env:FILE_NAME="${{ env.PLUGIN_NAME }}-${env:FILE_DATE}-${{ github.sha }}-win64.zip"
echo "::set-env name=FILE_NAME::${env:FILE_NAME}" echo "FILE_NAME=${env:FILE_NAME}" >> ${env:GITHUB_ENV}
robocopy .\build64\rundir\RelWithDebInfo\obs-plugins\64bit\ .\build\obs-plugins\64bit ${{ env.LIB_NAME }}.* /E /XF .gitignore robocopy .\build64\rundir\RelWithDebInfo\obs-plugins\64bit\ .\build\obs-plugins\64bit ${{ env.LIB_NAME }}.* /E /XF .gitignore
7z a ${env:FILE_NAME} .\build\* 7z a ${env:FILE_NAME} .\build\*
- name: Publish - name: Publish
if: success() if: success()
uses: actions/upload-artifact@v2-preview uses: actions/upload-artifact@v2.2.1
with: with:
name: '${{ env.FILE_NAME }}' name: '${{ env.FILE_NAME }}'
path: '*-win64.zip' path: '*-win64.zip'
@@ -248,14 +251,14 @@ jobs:
CMAKE_SYSTEM_VERSION: "10.0.18363.657" CMAKE_SYSTEM_VERSION: "10.0.18363.657"
steps: steps:
- name: Add msbuild to PATH - name: Add msbuild to PATH
uses: microsoft/setup-msbuild@v1.0.0 uses: microsoft/setup-msbuild@v1.0.2
- name: Checkout obs - name: Checkout obs
uses: actions/checkout@v2 uses: actions/checkout@v2.3.4
with: with:
repository: obsproject/obs-studio repository: obsproject/obs-studio
submodules: 'recursive' submodules: 'recursive'
- name: Checkout plugin - name: Checkout plugin
uses: actions/checkout@v2 uses: actions/checkout@v2.3.4
with: with:
path: UI/frontend-plugins/${{ env.PLUGIN_NAME }} path: UI/frontend-plugins/${{ env.PLUGIN_NAME }}
- name: Add plugin to obs cmake - name: Add plugin to obs cmake
@@ -276,7 +279,7 @@ jobs:
mkdir ./build mkdir ./build
mkdir ./build32 mkdir ./build32
cd ./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 .. cmake -G"${{ env.CMAKE_GENERATOR }}" -A"Win32" -DCMAKE_SYSTEM_VERSION="${{ env.CMAKE_SYSTEM_VERSION }}" -DBUILD_BROWSER=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 ..
- name: Build - name: Build
run: msbuild /m /p:Configuration=RelWithDebInfo .\build32\obs-studio.sln run: msbuild /m /p:Configuration=RelWithDebInfo .\build32\obs-studio.sln
- name: Package - name: Package
@@ -284,12 +287,12 @@ jobs:
run: | run: |
$env:FILE_DATE=(Get-Date -UFormat "%F") $env:FILE_DATE=(Get-Date -UFormat "%F")
$env:FILE_NAME="${{ env.PLUGIN_NAME }}-${env:FILE_DATE}-${{ github.sha }}-win32.zip" $env:FILE_NAME="${{ env.PLUGIN_NAME }}-${env:FILE_DATE}-${{ github.sha }}-win32.zip"
echo "::set-env name=FILE_NAME::${env:FILE_NAME}" echo "FILE_NAME=${env:FILE_NAME}" >> ${env:GITHUB_ENV}
robocopy .\build32\rundir\RelWithDebInfo\obs-plugins\32bit\ .\build\obs-plugins\32bit ${{ env.LIB_NAME }}.* /E /XF .gitignore robocopy .\build32\rundir\RelWithDebInfo\obs-plugins\32bit\ .\build\obs-plugins\32bit ${{ env.LIB_NAME }}.* /E /XF .gitignore
7z a ${env:FILE_NAME} .\build\* 7z a ${env:FILE_NAME} .\build\*
- name: Publish - name: Publish
if: success() if: success()
uses: actions/upload-artifact@v2-preview uses: actions/upload-artifact@v2.2.1
with: with:
name: '${{ env.FILE_NAME }}' name: '${{ env.FILE_NAME }}'
path: '*-win32.zip' path: '*-win32.zip'

32
.github/workflows/clang-format.yml vendored Normal file
View File

@@ -0,0 +1,32 @@
name: Clang Format Check
on: [push, pull_request]
jobs:
ubuntu64:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2
with:
submodules: 'recursive'
- name: Install clang format
run: |
# gets us newer clang
sudo bash -c "cat >> /etc/apt/sources.list" << LLVMAPT
# 3.8
deb http://apt.llvm.org/xenial/ llvm-toolchain-xenial-8 main
deb-src http://apt.llvm.org/xenial/ llvm-toolchain-xenial-8 main
LLVMAPT
wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key|sudo apt-key add -
sudo apt-get -qq update
sudo apt-get install -y clang-format-8
- name: Check the Formatting
run: |
./CI/formatcode.sh
./CI/check-format.sh

11
CI/check-format.sh Executable file
View File

@@ -0,0 +1,11 @@
#!/bin/bash
dirty=$(git ls-files --modified)
set +x
if [[ $dirty ]]; then
echo "================================="
echo "Files were not formatted properly"
echo "$dirty"
echo "================================="
exit 1
fi

31
CI/formatcode.sh Executable file
View File

@@ -0,0 +1,31 @@
#!/usr/bin/env bash
# Original source https://github.com/Project-OSRM/osrm-backend/blob/master/scripts/format.sh
set +x
set -o errexit
set -o pipefail
set -o nounset
# Runs the Clang Formatter in parallel on the code base.
# Return codes:
# - 1 there are files to be formatted
# - 0 everything looks fine
# Get CPU count
OS=$(uname)
NPROC=1
if [[ $OS = "Linux" || $OS = "Darwin" ]] ; then
NPROC=$(getconf _NPROCESSORS_ONLN)
fi
# Discover clang-format
if type clang-format-8 2> /dev/null ; then
CLANG_FORMAT=clang-format-8
else
CLANG_FORMAT=clang-format
fi
find .. -type d \( -path ./deps \
-o -path ./cmake \
-o -path ./build \) -prune -type f -o -name '*.h' -or -name '*.hpp' -or -name '*.m' -or -name '*.mm' -or -name '*.c' -or -name '*.cpp' \
| xargs -L100 -P${NPROC} ${CLANG_FORMAT} -i -style=file -fallback-style=none

View File

@@ -2,6 +2,12 @@ cmake_minimum_required(VERSION 3.5)
project(advanced-scene-switcher) project(advanced-scene-switcher)
# generate version info
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake/")
include(GetGitRevisionDescription)
get_git_head_revision(GIT_REFSPEC GIT_SHA1)
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/cmake/version.cpp.in" "${CMAKE_CURRENT_BINARY_DIR}/src/version.cpp" @ONLY)
if(BUILD_OUT_OF_TREE) if(BUILD_OUT_OF_TREE)
# out of tree builds # out of tree builds
# credits to c3r1c3 # credits to c3r1c3
@@ -46,8 +52,9 @@ if(BUILD_OUT_OF_TREE)
src/headers/switch-time.hpp src/headers/switch-time.hpp
src/headers/switch-transitions.hpp src/headers/switch-transitions.hpp
src/headers/switch-window.hpp src/headers/switch-window.hpp
src/headers/swtich-sequence.hpp src/headers/switch-sequence.hpp
src/headers/switch-generic.hpp src/headers/switch-generic.hpp
src/headers/version.h
) )
set(advanced-scene-switcher_SOURCES set(advanced-scene-switcher_SOURCES
@@ -70,7 +77,10 @@ if(BUILD_OUT_OF_TREE)
src/switch-random.cpp src/switch-random.cpp
src/switch-time.cpp src/switch-time.cpp
src/switch-audio.cpp src/switch-audio.cpp
src/switch-generic.cpp
src/curl-helper.cpp src/curl-helper.cpp
src/volume-control.cpp
src/version.cpp
) )
set(advanced-scene-switcher_UI set(advanced-scene-switcher_UI
@@ -194,8 +204,9 @@ else ()
src/headers/switch-time.hpp src/headers/switch-time.hpp
src/headers/switch-transitions.hpp src/headers/switch-transitions.hpp
src/headers/switch-window.hpp src/headers/switch-window.hpp
src/headers/swtich-sequence.hpp src/headers/switch-sequence.hpp
src/headers/switch-generic.hpp src/headers/switch-generic.hpp
src/headers/version.h
) )
set(advanced-scene-switcher_SOURCES set(advanced-scene-switcher_SOURCES
@@ -218,8 +229,10 @@ else ()
src/switch-random.cpp src/switch-random.cpp
src/switch-time.cpp src/switch-time.cpp
src/switch-audio.cpp src/switch-audio.cpp
src/switch-generic.cpp
src/curl-helper.cpp src/curl-helper.cpp
src/volume-control.cpp src/volume-control.cpp
src/version.cpp
) )
set(advanced-scene-switcher_UI set(advanced-scene-switcher_UI

View File

@@ -3,7 +3,7 @@ An automated scene switcher for OBS Studio
More information can be found on https://obsproject.com/forum/resources/automatic-scene-switching.395/ More information can be found on https://obsproject.com/forum/resources/automatic-scene-switching.395/
## Compiling in tree ## Compiling in tree (recommended)
Add the "SceneSwitcher" source directory to your obs-studio source directory under obs-studio/UI/frontend-plugins/: Add the "SceneSwitcher" source directory to your obs-studio source directory under obs-studio/UI/frontend-plugins/:
``` ```
cd obs-studio/UI/frontend-plugins/ cd obs-studio/UI/frontend-plugins/

View File

@@ -0,0 +1,279 @@
# - Returns a version string from Git
#
# These functions force a re-configure on each git commit so that you can
# trust the values of the variables in your build system.
#
# get_git_head_revision(<refspecvar> <hashvar> [<additional arguments to git describe> ...])
#
# Returns the refspec and sha hash of the current head revision
#
# git_describe(<var> [<additional arguments to git describe> ...])
#
# Returns the results of git describe on the source tree, and adjusting
# the output so that it tests false if an error occurs.
#
# git_describe_working_tree(<var> [<additional arguments to git describe> ...])
#
# Returns the results of git describe on the working tree (--dirty option),
# and adjusting the output so that it tests false if an error occurs.
#
# git_get_exact_tag(<var> [<additional arguments to git describe> ...])
#
# Returns the results of git describe --exact-match on the source tree,
# and adjusting the output so that it tests false if there was no exact
# matching tag.
#
# git_local_changes(<var>)
#
# Returns either "CLEAN" or "DIRTY" with respect to uncommitted changes.
# Uses the return code of "git diff-index --quiet HEAD --".
# Does not regard untracked files.
#
# Requires CMake 2.6 or newer (uses the 'function' command)
#
# Original Author:
# 2009-2020 Ryan Pavlik <ryan.pavlik@gmail.com> <abiryan@ryand.net>
# http://academic.cleardefinition.com
#
# Copyright 2009-2013, Iowa State University.
# Copyright 2013-2020, Ryan Pavlik
# Copyright 2013-2020, Contributors
# SPDX-License-Identifier: BSL-1.0
# Distributed under the Boost Software License, Version 1.0.
# (See accompanying file LICENSE_1_0.txt or copy at
# http://www.boost.org/LICENSE_1_0.txt)
if(__get_git_revision_description)
return()
endif()
set(__get_git_revision_description YES)
# We must run the following at "include" time, not at function call time,
# to find the path to this module rather than the path to a calling list file
get_filename_component(_gitdescmoddir ${CMAKE_CURRENT_LIST_FILE} PATH)
# Function _git_find_closest_git_dir finds the next closest .git directory
# that is part of any directory in the path defined by _start_dir.
# The result is returned in the parent scope variable whose name is passed
# as variable _git_dir_var. If no .git directory can be found, the
# function returns an empty string via _git_dir_var.
#
# Example: Given a path C:/bla/foo/bar and assuming C:/bla/.git exists and
# neither foo nor bar contain a file/directory .git. This wil return
# C:/bla/.git
#
function(_git_find_closest_git_dir _start_dir _git_dir_var)
set(cur_dir "${_start_dir}")
set(git_dir "${_start_dir}/.git")
while(NOT EXISTS "${git_dir}")
# .git dir not found, search parent directories
set(git_previous_parent "${cur_dir}")
get_filename_component(cur_dir ${cur_dir} DIRECTORY)
if(cur_dir STREQUAL git_previous_parent)
# We have reached the root directory, we are not in git
set(${_git_dir_var}
""
PARENT_SCOPE)
return()
endif()
set(git_dir "${cur_dir}/.git")
endwhile()
set(${_git_dir_var}
"${git_dir}"
PARENT_SCOPE)
endfunction()
function(get_git_head_revision _refspecvar _hashvar)
_git_find_closest_git_dir("${CMAKE_CURRENT_SOURCE_DIR}" GIT_DIR)
if(NOT "${GIT_DIR}" STREQUAL "")
file(RELATIVE_PATH _relative_to_source_dir "${CMAKE_SOURCE_DIR}"
"${GIT_DIR}")
if("${_relative_to_source_dir}" MATCHES "[.][.]")
# We've gone above the CMake root dir.
set(GIT_DIR "")
endif()
endif()
if("${GIT_DIR}" STREQUAL "")
set(${_refspecvar}
"GITDIR-NOTFOUND"
PARENT_SCOPE)
set(${_hashvar}
"GITDIR-NOTFOUND"
PARENT_SCOPE)
return()
endif()
# Check if the current source dir is a git submodule or a worktree.
# In both cases .git is a file instead of a directory.
#
if(NOT IS_DIRECTORY ${GIT_DIR})
# The following git command will return a non empty string that
# points to the super project working tree if the current
# source dir is inside a git submodule.
# Otherwise the command will return an empty string.
#
execute_process(
COMMAND "${GIT_EXECUTABLE}" rev-parse
--show-superproject-working-tree
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
OUTPUT_VARIABLE out
ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE)
if(NOT "${out}" STREQUAL "")
# If out is empty, GIT_DIR/CMAKE_CURRENT_SOURCE_DIR is in a submodule
file(READ ${GIT_DIR} submodule)
string(REGEX REPLACE "gitdir: (.*)$" "\\1" GIT_DIR_RELATIVE
${submodule})
string(STRIP ${GIT_DIR_RELATIVE} GIT_DIR_RELATIVE)
get_filename_component(SUBMODULE_DIR ${GIT_DIR} PATH)
get_filename_component(GIT_DIR ${SUBMODULE_DIR}/${GIT_DIR_RELATIVE}
ABSOLUTE)
set(HEAD_SOURCE_FILE "${GIT_DIR}/HEAD")
else()
# GIT_DIR/CMAKE_CURRENT_SOURCE_DIR is in a worktree
file(READ ${GIT_DIR} worktree_ref)
# The .git directory contains a path to the worktree information directory
# inside the parent git repo of the worktree.
#
string(REGEX REPLACE "gitdir: (.*)$" "\\1" git_worktree_dir
${worktree_ref})
string(STRIP ${git_worktree_dir} git_worktree_dir)
_git_find_closest_git_dir("${git_worktree_dir}" GIT_DIR)
set(HEAD_SOURCE_FILE "${git_worktree_dir}/HEAD")
endif()
else()
set(HEAD_SOURCE_FILE "${GIT_DIR}/HEAD")
endif()
set(GIT_DATA "${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/git-data")
if(NOT EXISTS "${GIT_DATA}")
file(MAKE_DIRECTORY "${GIT_DATA}")
endif()
if(NOT EXISTS "${HEAD_SOURCE_FILE}")
return()
endif()
set(HEAD_FILE "${GIT_DATA}/HEAD")
configure_file("${HEAD_SOURCE_FILE}" "${HEAD_FILE}" COPYONLY)
configure_file("${_gitdescmoddir}/GetGitRevisionDescription.cmake.in"
"${GIT_DATA}/grabRef.cmake" @ONLY)
include("${GIT_DATA}/grabRef.cmake")
set(${_refspecvar}
"${HEAD_REF}"
PARENT_SCOPE)
set(${_hashvar}
"${HEAD_HASH}"
PARENT_SCOPE)
endfunction()
function(git_describe _var)
if(NOT GIT_FOUND)
find_package(Git QUIET)
endif()
get_git_head_revision(refspec hash)
if(NOT GIT_FOUND)
set(${_var}
"GIT-NOTFOUND"
PARENT_SCOPE)
return()
endif()
if(NOT hash)
set(${_var}
"HEAD-HASH-NOTFOUND"
PARENT_SCOPE)
return()
endif()
# TODO sanitize
#if((${ARGN}" MATCHES "&&") OR
# (ARGN MATCHES "||") OR
# (ARGN MATCHES "\\;"))
# message("Please report the following error to the project!")
# message(FATAL_ERROR "Looks like someone's doing something nefarious with git_describe! Passed arguments ${ARGN}")
#endif()
#message(STATUS "Arguments to execute_process: ${ARGN}")
execute_process(
COMMAND "${GIT_EXECUTABLE}" describe --tags --always ${hash} ${ARGN}
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
RESULT_VARIABLE res
OUTPUT_VARIABLE out
ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE)
if(NOT res EQUAL 0)
set(out "${out}-${res}-NOTFOUND")
endif()
set(${_var}
"${out}"
PARENT_SCOPE)
endfunction()
function(git_describe_working_tree _var)
if(NOT GIT_FOUND)
find_package(Git QUIET)
endif()
if(NOT GIT_FOUND)
set(${_var}
"GIT-NOTFOUND"
PARENT_SCOPE)
return()
endif()
execute_process(
COMMAND "${GIT_EXECUTABLE}" describe --dirty ${ARGN}
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
RESULT_VARIABLE res
OUTPUT_VARIABLE out
ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE)
if(NOT res EQUAL 0)
set(out "${out}-${res}-NOTFOUND")
endif()
set(${_var}
"${out}"
PARENT_SCOPE)
endfunction()
function(git_get_exact_tag _var)
git_describe(out --exact-match ${ARGN})
set(${_var}
"${out}"
PARENT_SCOPE)
endfunction()
function(git_local_changes _var)
if(NOT GIT_FOUND)
find_package(Git QUIET)
endif()
get_git_head_revision(refspec hash)
if(NOT GIT_FOUND)
set(${_var}
"GIT-NOTFOUND"
PARENT_SCOPE)
return()
endif()
if(NOT hash)
set(${_var}
"HEAD-HASH-NOTFOUND"
PARENT_SCOPE)
return()
endif()
execute_process(
COMMAND "${GIT_EXECUTABLE}" diff-index --quiet HEAD --
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
RESULT_VARIABLE res
OUTPUT_VARIABLE out
ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE)
if(res EQUAL 0)
set(${_var}
"CLEAN"
PARENT_SCOPE)
else()
set(${_var}
"DIRTY"
PARENT_SCOPE)
endif()
endfunction()

View File

@@ -0,0 +1,41 @@
#
# Internal file for GetGitRevisionDescription.cmake
#
# Requires CMake 2.6 or newer (uses the 'function' command)
#
# Original Author:
# 2009-2010 Ryan Pavlik <rpavlik@iastate.edu> <abiryan@ryand.net>
# http://academic.cleardefinition.com
# Iowa State University HCI Graduate Program/VRAC
#
# Copyright Iowa State University 2009-2010.
# Distributed under the Boost Software License, Version 1.0.
# (See accompanying file LICENSE_1_0.txt or copy at
# http://www.boost.org/LICENSE_1_0.txt)
set(HEAD_HASH)
file(READ "@HEAD_FILE@" HEAD_CONTENTS LIMIT 1024)
string(STRIP "${HEAD_CONTENTS}" HEAD_CONTENTS)
if(HEAD_CONTENTS MATCHES "ref")
# named branch
string(REPLACE "ref: " "" HEAD_REF "${HEAD_CONTENTS}")
if(EXISTS "@GIT_DIR@/${HEAD_REF}")
configure_file("@GIT_DIR@/${HEAD_REF}" "@GIT_DATA@/head-ref" COPYONLY)
else()
configure_file("@GIT_DIR@/packed-refs" "@GIT_DATA@/packed-refs" COPYONLY)
file(READ "@GIT_DATA@/packed-refs" PACKED_REFS)
if(${PACKED_REFS} MATCHES "([0-9a-z]*) ${HEAD_REF}")
set(HEAD_HASH "${CMAKE_MATCH_1}")
endif()
endif()
else()
# detached HEAD
configure_file("@GIT_DIR@/HEAD" "@GIT_DATA@/head-ref" COPYONLY)
endif()
if(NOT HEAD_HASH)
file(READ "@GIT_DATA@/head-ref" HEAD_HASH LIMIT 1024)
string(STRIP "${HEAD_HASH}" HEAD_HASH)
endif()

3
cmake/version.cpp.in Normal file
View File

@@ -0,0 +1,3 @@
#include "src/headers/version.h"
#define GIT_SHA1 "@GIT_SHA1@"
const char g_GIT_SHA1[] = GIT_SHA1;

File diff suppressed because it is too large Load Diff

View File

@@ -1,30 +1,23 @@
#include <QMainWindow> #include <QMainWindow>
#include <QDir>
#include <QAction> #include <QAction>
#include <QtGui/qstandarditemmodel.h>
#include <condition_variable> #include <QPropertyAnimation>
#include <chrono> #include <QGraphicsColorizeEffect>
#include <vector>
#include <mutex>
#include <thread>
#include <obs-frontend-api.h> #include <obs-frontend-api.h>
#include <obs-module.h>
#include <obs.hpp>
#include <util/util.hpp> #include <util/util.hpp>
#include "headers/switcher-data-structs.hpp"
#include "headers/advanced-scene-switcher.hpp" #include "headers/advanced-scene-switcher.hpp"
#include "headers/utility.hpp"
#include "headers/curl-helper.hpp" #include "headers/curl-helper.hpp"
#include "headers/version.h"
SwitcherData *switcher = nullptr; SwitcherData *switcher = nullptr;
/******************************************************************************** /********************************************************************************
* Create the Advanced Scene Switcher settings window * Create the Advanced Scene Switcher settings window
********************************************************************************/ ********************************************************************************/
SceneSwitcher::SceneSwitcher(QWidget *parent) AdvSceneSwitcher::AdvSceneSwitcher(QWidget *parent)
: QDialog(parent), ui(new Ui_SceneSwitcher) : QDialog(parent), ui(new Ui_AdvSceneSwitcher)
{ {
ui->setupUi(this); ui->setupUi(this);
@@ -34,46 +27,7 @@ SceneSwitcher::SceneSwitcher(QWidget *parent)
loadUI(); loadUI();
} }
void SceneSwitcher::populateSceneSelection(QComboBox *sel, bool addPrevious) void AdvSceneSwitcher::loadUI()
{
BPtr<char *> scenes = obs_frontend_get_scene_names();
char **temp = scenes;
while (*temp) {
const char *name = *temp;
sel->addItem(name);
temp++;
}
if (addPrevious)
sel->addItem(previous_scene_name);
}
void SceneSwitcher::populateTransitionSelection(QComboBox *sel)
{
obs_frontend_source_list *transitions = new obs_frontend_source_list();
obs_frontend_get_transitions(transitions);
for (size_t i = 0; i < transitions->sources.num; i++) {
const char *name =
obs_source_get_name(transitions->sources.array[i]);
sel->addItem(name);
}
obs_frontend_source_list_free(transitions);
}
void SceneSwitcher::populateWindowSelection(QComboBox *sel)
{
std::vector<std::string> windows;
GetWindowList(windows);
sort(windows.begin(), windows.end());
for (std::string &window : windows) {
sel->addItem(window.c_str());
}
}
void SceneSwitcher::loadUI()
{ {
#if __APPLE__ #if __APPLE__
setMinimumHeight(700); setMinimumHeight(700);
@@ -97,6 +51,210 @@ void SceneSwitcher::loadUI()
loading = false; loading = false;
} }
/********************************************************************************
* UI helpers
********************************************************************************/
void addSelectionEntry(QComboBox *sel, const char *description,
const char *tooltip = "")
{
sel->addItem(description);
if (strcmp(tooltip, "") != 0) {
sel->setItemData(0, tooltip, Qt::ToolTipRole);
}
QStandardItemModel *model =
qobject_cast<QStandardItemModel *>(sel->model());
QModelIndex firstIndex =
model->index(0, sel->modelColumn(), sel->rootModelIndex());
QStandardItem *firstItem = model->itemFromIndex(firstIndex);
firstItem->setSelectable(false);
firstItem->setEnabled(false);
}
void AdvSceneSwitcher::populateSceneSelection(QComboBox *sel, bool addPrevious,
bool addSelect)
{
if (addSelect)
addSelectionEntry(sel, "--select scene--",
"invalid entries will not be saved");
BPtr<char *> scenes = obs_frontend_get_scene_names();
char **temp = scenes;
while (*temp) {
const char *name = *temp;
sel->addItem(name);
temp++;
}
if (addPrevious)
sel->addItem(previous_scene_name);
}
void AdvSceneSwitcher::populateTransitionSelection(QComboBox *sel,
bool addSelect)
{
if (addSelect)
addSelectionEntry(sel, "--select transition--");
obs_frontend_source_list *transitions = new obs_frontend_source_list();
obs_frontend_get_transitions(transitions);
for (size_t i = 0; i < transitions->sources.num; i++) {
const char *name =
obs_source_get_name(transitions->sources.array[i]);
sel->addItem(name);
}
obs_frontend_source_list_free(transitions);
}
void AdvSceneSwitcher::populateWindowSelection(QComboBox *sel, bool addSelect)
{
if (addSelect)
addSelectionEntry(sel, "--select window--");
std::vector<std::string> windows;
GetWindowList(windows);
sort(windows.begin(), windows.end());
for (std::string &window : windows) {
sel->addItem(window.c_str());
}
#ifdef WIN32
sel->setItemData(
0,
"Use \"OBS\" to specify OBS window\nUse \"Task Switching\"to specify ALT + TAB",
Qt::ToolTipRole);
#endif
}
void AdvSceneSwitcher::populateAudioSelection(QComboBox *sel, bool addSelect)
{
if (addSelect)
addSelectionEntry(sel, "--select audio source--",
"invalid entries will not be saved");
auto sourceEnum = [](void *data, obs_source_t *source) -> bool /* -- */
{
QComboBox *combo = reinterpret_cast<QComboBox *>(data);
uint32_t flags = obs_source_get_output_flags(source);
if ((flags & OBS_SOURCE_AUDIO) != 0) {
const char *name = obs_source_get_name(source);
combo->addItem(name);
}
return true;
};
obs_enum_sources(sourceEnum, sel);
}
void AdvSceneSwitcher::populateMediaSelection(QComboBox *sel, bool addSelect)
{
if (addSelect)
addSelectionEntry(sel, "--select media source--",
"invalid entries will not be saved");
auto sourceEnum = [](void *data, obs_source_t *source) -> bool /* -- */
{
QComboBox *combo = reinterpret_cast<QComboBox *>(data);
std::string sourceId = obs_source_get_id(source);
if (sourceId.compare("ffmpeg_source") == 0 ||
sourceId.compare("vlc_source") == 0) {
const char *name = obs_source_get_name(source);
combo->addItem(name);
}
return true;
};
obs_enum_sources(sourceEnum, sel);
}
void AdvSceneSwitcher::populateProcessSelection(QComboBox *sel, bool addSelect)
{
if (addSelect)
addSelectionEntry(sel, "--select process--");
QStringList processes;
GetProcessList(processes);
for (QString &process : processes)
sel->addItem(process);
}
bool AdvSceneSwitcher::listMoveUp(QListWidget *list)
{
int index = list->currentRow();
if (index == -1 || index == 0)
return false;
QWidget *row = list->itemWidget(list->currentItem());
QListWidgetItem *itemN = list->currentItem()->clone();
list->insertItem(index - 1, itemN);
list->setItemWidget(itemN, row);
list->takeItem(index + 1);
list->setCurrentRow(index - 1);
return true;
}
bool AdvSceneSwitcher::listMoveDown(QListWidget *list)
{
int index = list->currentRow();
if (index == -1 || index == list->count() - 1)
return false;
QWidget *row = list->itemWidget(list->currentItem());
QListWidgetItem *itemN = list->currentItem()->clone();
list->insertItem(index + 2, itemN);
list->setItemWidget(itemN, row);
list->takeItem(index);
list->setCurrentRow(index + 1);
return true;
}
QMetaObject::Connection AdvSceneSwitcher::PulseWidget(QWidget *widget,
QColor endColor,
QColor startColor,
QString specifier)
{
if (switcher->disableHints)
return QMetaObject::Connection();
widget->setStyleSheet(specifier + "{ \
border-style: outset; \
border-width: 2px; \
border-radius: 10px; \
border-color: rgb(0,0,0,0) \
}");
QGraphicsColorizeEffect *eEffect = new QGraphicsColorizeEffect(widget);
widget->setGraphicsEffect(eEffect);
QPropertyAnimation *paAnimation =
new QPropertyAnimation(eEffect, "color");
paAnimation->setStartValue(startColor);
paAnimation->setEndValue(endColor);
paAnimation->setDuration(1000);
// play backward to return to original state on timer end
paAnimation->setDirection(QAbstractAnimation::Backward);
auto con = QWidget::connect(
paAnimation, &QPropertyAnimation::finished, [paAnimation]() {
QTimer::singleShot(1000, [paAnimation] {
paAnimation->start();
});
});
paAnimation->start();
return con;
}
/******************************************************************************** /********************************************************************************
* Saving and loading * Saving and loading
********************************************************************************/ ********************************************************************************/
@@ -169,40 +327,51 @@ static void SaveSceneSwitcher(obs_data_t *save_data, bool saving, void *)
********************************************************************************/ ********************************************************************************/
void SwitcherData::Thread() void SwitcherData::Thread()
{ {
blog(LOG_INFO, "Advanced Scene Switcher started"); blog(LOG_INFO, "started");
int sleep = 0; int sleep = 0;
std::chrono::milliseconds duration;
auto startTime = std::chrono::high_resolution_clock::now();
auto endTime = std::chrono::high_resolution_clock::now();
while (true) { while (true) {
startLoop:
std::unique_lock<std::mutex> lock(m); std::unique_lock<std::mutex> lock(m);
bool match = false; bool match = false;
OBSWeakSource scene; OBSWeakSource scene;
OBSWeakSource transition; OBSWeakSource transition;
std::chrono::milliseconds duration;
if (sleep > interval) { bool defTransitionMatch = false;
OBSWeakSource defTransition;
endTime = std::chrono::high_resolution_clock::now();
auto runTime =
std::chrono::duration_cast<std::chrono::milliseconds>(
endTime - startTime);
if (sleep) {
duration = std::chrono::milliseconds(sleep); duration = std::chrono::milliseconds(sleep);
if (verbose)
blog(LOG_INFO,
"Advanced Scene Switcher sleep for %d",
sleep);
} else { } else {
duration = std::chrono::milliseconds(interval); duration =
if (verbose) std::chrono::milliseconds(interval) - runTime;
blog(LOG_INFO, if (duration.count() < 0)
"Advanced Scene Switcher sleep for %d", duration = std::chrono::milliseconds(0);
interval);
} }
sleep = 0;
switcher->Prune();
writeSceneInfoToFile();
//sleep for a bit //sleep for a bit
if (verbose)
blog(LOG_INFO, "sleep for %d", duration.count());
cv.wait_for(lock, duration); cv.wait_for(lock, duration);
startTime = std::chrono::high_resolution_clock::now();
sleep = 0;
switcher->Prune();
if (switcher->stop) { if (switcher->stop) {
break; break;
} }
setDefaultSceneTransitions();
if (autoStopEnable) { if (autoStopEnable) {
autoStopStreamAndRecording(); autoStopStreamAndRecording();
} }
@@ -215,6 +384,8 @@ void SwitcherData::Thread()
continue; continue;
} }
checkDefaultSceneTransitions(defTransitionMatch, defTransition);
for (int switchFuncName : functionNamesByPriority) { for (int switchFuncName : functionNamesByPriority) {
switch (switchFuncName) { switch (switchFuncName) {
case read_file_func: case read_file_func:
@@ -228,7 +399,6 @@ void SwitcherData::Thread()
case exe_func: case exe_func:
checkExeSwitch(match, scene, transition); checkExeSwitch(match, scene, transition);
break; break;
case screen_region_func: case screen_region_func:
checkScreenRegionSwitch(match, scene, checkScreenRegionSwitch(match, scene,
transition); transition);
@@ -239,11 +409,7 @@ void SwitcherData::Thread()
break; break;
case round_trip_func: case round_trip_func:
checkSceneSequence(match, scene, transition, checkSceneSequence(match, scene, transition,
lock); lock);
if (sceneChangedDuringWait()) //scene might have changed during the sleep
{
goto startLoop;
}
break; break;
case media_func: case media_func:
checkMediaSwitch(match, scene, transition); checkMediaSwitch(match, scene, transition);
@@ -264,65 +430,52 @@ void SwitcherData::Thread()
} }
} }
if (!match && switchIfNotMatching == SWITCH && checkNoMatchSwitch(match, scene, transition, sleep);
nonMatchingScene) {
match = true; // After this point we will call frontend functions
scene = nonMatchingScene; // obs_frontend_set_current_scene() and
transition = nullptr; // obs_frontend_set_current_transition()
} //
if (!match && switchIfNotMatching == RANDOM_SWITCH) { // During this time SaveSceneSwitcher() could be called
checkRandom(match, scene, transition, sleep); // leading to a deadlock, so we have to unlock()
lock.unlock();
if (!match && defTransitionMatch) {
setCurrentDefTransition(defTransition);
} }
if (match) { if (match) {
switchScene(scene, transition, switchScene(scene, transition,
tansitionOverrideOverride, lock); tansitionOverrideOverride);
} }
writeSceneInfoToFile();
} }
endLoop: endLoop:
blog(LOG_INFO, "Advanced Scene Switcher stopped"); blog(LOG_INFO, "stopped");
} }
void switchScene(OBSWeakSource &scene, OBSWeakSource &transition, void switchScene(OBSWeakSource &scene, OBSWeakSource &transition,
bool &transitionOverrideOverride, bool &transitionOverrideOverride)
std::unique_lock<std::mutex> &lock)
{ {
obs_source_t *source = obs_weak_source_get_source(scene); obs_source_t *source = obs_weak_source_get_source(scene);
obs_source_t *currentSource = obs_frontend_get_current_scene(); obs_source_t *currentSource = obs_frontend_get_current_scene();
if (source && source != currentSource) { if (source && source != currentSource) {
// this call might block when OBS_FRONTEND_EVENT_SCENE_CHANGED is active and mutex is held
// thus unlock mutex to avoid deadlock
lock.unlock();
transitionData td; transitionData td;
setNextTransition(scene, currentSource, transition, setNextTransition(scene, currentSource, transition,
transitionOverrideOverride, td); transitionOverrideOverride, td);
obs_frontend_set_current_scene(source); obs_frontend_set_current_scene(source);
if (transitionOverrideOverride) if (transitionOverrideOverride)
restoreTransitionOverride(source, td); restoreTransitionOverride(source, td);
lock.lock();
if (switcher->verbose) if (switcher->verbose)
blog(LOG_INFO, blog(LOG_INFO, "switched scene");
"Advanced Scene Switcher switched scene");
} }
obs_source_release(currentSource); obs_source_release(currentSource);
obs_source_release(source); obs_source_release(source);
} }
bool SwitcherData::sceneChangedDuringWait()
{
bool r = false;
obs_source_t *currentSource = obs_frontend_get_current_scene();
if (!currentSource)
return true;
obs_source_release(currentSource);
if (waitScene && currentSource != waitScene)
r = true;
return r;
}
void SwitcherData::Start() void SwitcherData::Start()
{ {
if (!(th && th->isRunning())) { if (!(th && th->isRunning())) {
@@ -345,6 +498,15 @@ void SwitcherData::Stop()
} }
} }
bool SwitcherData::sceneChangedDuringWait()
{
obs_source_t *currentSource = obs_frontend_get_current_scene();
if (!currentSource)
return true;
obs_source_release(currentSource);
return (waitScene && currentSource != waitScene);
}
/******************************************************************************** /********************************************************************************
* OBS module setup * OBS module setup
********************************************************************************/ ********************************************************************************/
@@ -363,7 +525,6 @@ extern "C" void FreeSceneSwitcher()
void handleSceneChange(SwitcherData *s) void handleSceneChange(SwitcherData *s)
{ {
std::lock_guard<std::mutex> lock(s->m);
//stop waiting if scene was manually changed //stop waiting if scene was manually changed
if (s->sceneChangedDuringWait()) if (s->sceneChangedDuringWait())
s->cv.notify_one(); s->cv.notify_one();
@@ -373,15 +534,20 @@ void handleSceneChange(SwitcherData *s)
obs_weak_source_t *ws = obs_source_get_weak_source(source); obs_weak_source_t *ws = obs_source_get_weak_source(source);
obs_source_release(source); obs_source_release(source);
obs_weak_source_release(ws); obs_weak_source_release(ws);
if (source && s->PreviousScene2 != ws) { if (source && s->previousScene2 != ws) {
s->previousScene = s->PreviousScene2; s->previousScene = s->previousScene2;
s->PreviousScene2 = ws; s->previousScene2 = ws;
} }
//reset autostart //reset events only hanled on scene change
s->autoStartedRecently = false; s->autoStartedRecently = false;
} }
void handleTransitionStop(SwitcherData *s)
{
s->checkedDefTransition = false;
}
void setLiveTime(SwitcherData *s) void setLiveTime(SwitcherData *s)
{ {
s->liveTime = QDateTime::currentDateTime(); s->liveTime = QDateTime::currentDateTime();
@@ -392,6 +558,9 @@ void resetLiveTime(SwitcherData *s)
s->liveTime = QDateTime(); s->liveTime = QDateTime();
} }
// 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()
static void OBSEvent(enum obs_frontend_event event, void *switcher) static void OBSEvent(enum obs_frontend_event event, void *switcher)
{ {
switch (event) { switch (event) {
@@ -401,6 +570,9 @@ static void OBSEvent(enum obs_frontend_event event, void *switcher)
case OBS_FRONTEND_EVENT_SCENE_CHANGED: case OBS_FRONTEND_EVENT_SCENE_CHANGED:
handleSceneChange((SwitcherData *)switcher); handleSceneChange((SwitcherData *)switcher);
break; break;
case OBS_FRONTEND_EVENT_TRANSITION_STOPPED:
handleTransitionStop((SwitcherData *)switcher);
break;
case OBS_FRONTEND_EVENT_RECORDING_STARTED: case OBS_FRONTEND_EVENT_RECORDING_STARTED:
case OBS_FRONTEND_EVENT_STREAMING_STARTED: case OBS_FRONTEND_EVENT_STREAMING_STARTED:
setLiveTime((SwitcherData *)switcher); setLiveTime((SwitcherData *)switcher);
@@ -416,6 +588,8 @@ static void OBSEvent(enum obs_frontend_event event, void *switcher)
extern "C" void InitSceneSwitcher() extern "C" void InitSceneSwitcher()
{ {
blog(LOG_INFO, "version: %s", g_GIT_SHA1);
QAction *action = (QAction *)obs_frontend_add_tools_menu_qaction( QAction *action = (QAction *)obs_frontend_add_tools_menu_qaction(
"Advanced Scene Switcher"); "Advanced Scene Switcher");
@@ -429,7 +603,7 @@ extern "C" void InitSceneSwitcher()
QMainWindow *window = QMainWindow *window =
(QMainWindow *)obs_frontend_get_main_window(); (QMainWindow *)obs_frontend_get_main_window();
SceneSwitcher ss(window); AdvSceneSwitcher ss(window);
ss.exec(); ss.exec();
}; };

View File

@@ -23,11 +23,11 @@ bool resolveCurl()
(cleanupFunction)loaded_curl_lib->resolve("curl_easy_cleanup"); (cleanupFunction)loaded_curl_lib->resolve("curl_easy_cleanup");
if (f_curl_init && f_curl_setopt && f_curl_perform && f_curl_cleanup) { if (f_curl_init && f_curl_setopt && f_curl_perform && f_curl_cleanup) {
blog(LOG_INFO, "curl loaded successfully"); blog(LOG_INFO, "[adv-ss] curl loaded successfully");
return true; return true;
} }
blog(LOG_INFO, "curl symbols not resolved"); blog(LOG_INFO, "[adv-ss] curl symbols not resolved");
return false; return false;
} }
@@ -43,13 +43,14 @@ bool loadCurl()
#endif #endif
for (QString path : locations) { for (QString path : locations) {
blog(LOG_INFO, "trying '%s'", path.toUtf8().constData()); blog(LOG_INFO, "[adv-ss] trying '%s'",
path.toUtf8().constData());
QFileInfo libPath( QFileInfo libPath(
QDir(path).absoluteFilePath(curl_library_name)); QDir(path).absoluteFilePath(curl_library_name));
if (libPath.exists() && libPath.isFile()) { if (libPath.exists() && libPath.isFile()) {
QString libFilePath = libPath.absoluteFilePath(); QString libFilePath = libPath.absoluteFilePath();
blog(LOG_INFO, "found curl library at '%s'", blog(LOG_INFO, "[adv-ss] found curl library at '%s'",
libFilePath.toUtf8().constData()); libFilePath.toUtf8().constData());
loaded_curl_lib = new QLibrary(libFilePath, nullptr); loaded_curl_lib = new QLibrary(libFilePath, nullptr);
@@ -62,6 +63,6 @@ bool loadCurl()
} }
} }
blog(LOG_ERROR, "can't find the curl library"); blog(LOG_WARNING, "[adv-ss] can't find the curl library");
return false; return false;
} }

View File

@@ -1,16 +1,17 @@
#include <obs-module.h>
#include <QFileDialog> #include <QFileDialog>
#include <QTextStream>
#include <QMessageBox> #include <QMessageBox>
#include "headers/advanced-scene-switcher.hpp" #include "headers/advanced-scene-switcher.hpp"
#include "headers/utility.hpp"
void SceneSwitcher::on_close_clicked() QMetaObject::Connection inactivePluse;
void AdvSceneSwitcher::on_close_clicked()
{ {
done(0); done(0);
} }
void SceneSwitcher::UpdateNonMatchingScene(const QString &name) void AdvSceneSwitcher::UpdateNonMatchingScene(const QString &name)
{ {
obs_source_t *scene = obs_get_source_by_name(name.toUtf8().constData()); obs_source_t *scene = obs_get_source_by_name(name.toUtf8().constData());
obs_weak_source_t *ws = obs_source_get_weak_source(scene); obs_weak_source_t *ws = obs_source_get_weak_source(scene);
@@ -21,7 +22,7 @@ void SceneSwitcher::UpdateNonMatchingScene(const QString &name)
obs_source_release(scene); obs_source_release(scene);
} }
void SceneSwitcher::on_noMatchDontSwitch_clicked() void AdvSceneSwitcher::on_noMatchDontSwitch_clicked()
{ {
if (loading) if (loading)
return; return;
@@ -29,9 +30,10 @@ void SceneSwitcher::on_noMatchDontSwitch_clicked()
std::lock_guard<std::mutex> lock(switcher->m); std::lock_guard<std::mutex> lock(switcher->m);
switcher->switchIfNotMatching = NO_SWITCH; switcher->switchIfNotMatching = NO_SWITCH;
ui->noMatchSwitchScene->setEnabled(false); ui->noMatchSwitchScene->setEnabled(false);
ui->randomDisabledWarning->setVisible(true);
} }
void SceneSwitcher::on_noMatchSwitch_clicked() void AdvSceneSwitcher::on_noMatchSwitch_clicked()
{ {
if (loading) if (loading)
return; return;
@@ -40,9 +42,10 @@ void SceneSwitcher::on_noMatchSwitch_clicked()
switcher->switchIfNotMatching = SWITCH; switcher->switchIfNotMatching = SWITCH;
ui->noMatchSwitchScene->setEnabled(true); ui->noMatchSwitchScene->setEnabled(true);
UpdateNonMatchingScene(ui->noMatchSwitchScene->currentText()); UpdateNonMatchingScene(ui->noMatchSwitchScene->currentText());
ui->randomDisabledWarning->setVisible(true);
} }
void SceneSwitcher::on_noMatchRandomSwitch_clicked() void AdvSceneSwitcher::on_noMatchRandomSwitch_clicked()
{ {
if (loading) if (loading)
return; return;
@@ -50,9 +53,18 @@ void SceneSwitcher::on_noMatchRandomSwitch_clicked()
std::lock_guard<std::mutex> lock(switcher->m); std::lock_guard<std::mutex> lock(switcher->m);
switcher->switchIfNotMatching = RANDOM_SWITCH; switcher->switchIfNotMatching = RANDOM_SWITCH;
ui->noMatchSwitchScene->setEnabled(false); ui->noMatchSwitchScene->setEnabled(false);
ui->randomDisabledWarning->setVisible(false);
} }
void SceneSwitcher::on_startupBehavior_currentIndexChanged(int index) void AdvSceneSwitcher::on_noMatchDelay_valueChanged(double i)
{
if (loading)
return;
std::lock_guard<std::mutex> lock(switcher->m);
switcher->noMatchDelay = i;
}
void AdvSceneSwitcher::on_startupBehavior_currentIndexChanged(int index)
{ {
if (loading) if (loading)
return; return;
@@ -61,7 +73,8 @@ void SceneSwitcher::on_startupBehavior_currentIndexChanged(int index)
switcher->startupBehavior = (StartupBehavior)index; switcher->startupBehavior = (StartupBehavior)index;
} }
void SceneSwitcher::on_noMatchSwitchScene_currentTextChanged(const QString &text) void AdvSceneSwitcher::on_noMatchSwitchScene_currentTextChanged(
const QString &text)
{ {
if (loading) if (loading)
return; return;
@@ -70,7 +83,7 @@ void SceneSwitcher::on_noMatchSwitchScene_currentTextChanged(const QString &text
UpdateNonMatchingScene(text); UpdateNonMatchingScene(text);
} }
void SceneSwitcher::on_checkInterval_valueChanged(int value) void AdvSceneSwitcher::on_checkInterval_valueChanged(int value)
{ {
if (loading) if (loading)
return; return;
@@ -79,19 +92,22 @@ void SceneSwitcher::on_checkInterval_valueChanged(int value)
switcher->interval = value; switcher->interval = value;
} }
void SceneSwitcher::SetStarted() void AdvSceneSwitcher::SetStarted()
{ {
ui->toggleStartButton->setText("Stop"); ui->toggleStartButton->setText("Stop");
ui->pluginRunningText->setText("Active"); ui->pluginRunningText->setText("Active");
ui->pluginRunningText->disconnect(inactivePluse);
} }
void SceneSwitcher::SetStopped() void AdvSceneSwitcher::SetStopped()
{ {
ui->toggleStartButton->setText("Start"); ui->toggleStartButton->setText("Start");
ui->pluginRunningText->setText("Inactive"); ui->pluginRunningText->setText("Inactive");
inactivePluse = PulseWidget(ui->pluginRunningText, QColor(Qt::red),
QColor(0, 0, 0, 0), "QLabel ");
} }
void SceneSwitcher::on_toggleStartButton_clicked() void AdvSceneSwitcher::on_toggleStartButton_clicked()
{ {
if (switcher->th && switcher->th->isRunning()) { if (switcher->th && switcher->th->isRunning()) {
switcher->Stop(); switcher->Stop();
@@ -102,12 +118,12 @@ void SceneSwitcher::on_toggleStartButton_clicked()
} }
} }
void SceneSwitcher::closeEvent(QCloseEvent *) void AdvSceneSwitcher::closeEvent(QCloseEvent *)
{ {
obs_frontend_save(); obs_frontend_save();
} }
void SceneSwitcher::on_autoStopScenes_currentTextChanged(const QString &text) void AdvSceneSwitcher::on_autoStopScenes_currentTextChanged(const QString &text)
{ {
if (loading) if (loading)
return; return;
@@ -116,7 +132,7 @@ void SceneSwitcher::on_autoStopScenes_currentTextChanged(const QString &text)
UpdateAutoStopScene(text); UpdateAutoStopScene(text);
} }
void SceneSwitcher::on_autoStopSceneCheckBox_stateChanged(int state) void AdvSceneSwitcher::on_autoStopSceneCheckBox_stateChanged(int state)
{ {
if (loading) if (loading)
return; return;
@@ -133,7 +149,7 @@ void SceneSwitcher::on_autoStopSceneCheckBox_stateChanged(int state)
} }
} }
void SceneSwitcher::UpdateAutoStopScene(const QString &name) void AdvSceneSwitcher::UpdateAutoStopScene(const QString &name)
{ {
obs_source_t *scene = obs_get_source_by_name(name.toUtf8().constData()); obs_source_t *scene = obs_get_source_by_name(name.toUtf8().constData());
obs_weak_source_t *ws = obs_source_get_weak_source(scene); obs_weak_source_t *ws = obs_source_get_weak_source(scene);
@@ -150,16 +166,24 @@ void SwitcherData::autoStopStreamAndRecording()
obs_weak_source_t *ws = obs_source_get_weak_source(currentSource); obs_weak_source_t *ws = obs_source_get_weak_source(currentSource);
if (ws && autoStopScene == ws) { if (ws && autoStopScene == ws) {
if (obs_frontend_streaming_active()) if (obs_frontend_streaming_active()) {
blog(LOG_INFO,
"Stopping stream because scene '%s' is active",
obs_source_get_name(currentSource));
obs_frontend_streaming_stop(); obs_frontend_streaming_stop();
if (obs_frontend_recording_active()) }
if (obs_frontend_recording_active()) {
blog(LOG_INFO,
"Stopping record because scene '%s' is active",
obs_source_get_name(currentSource));
obs_frontend_recording_stop(); obs_frontend_recording_stop();
}
} }
obs_source_release(currentSource); obs_source_release(currentSource);
obs_weak_source_release(ws); obs_weak_source_release(ws);
} }
void SceneSwitcher::on_autoStartType_currentIndexChanged(int index) void AdvSceneSwitcher::on_autoStartType_currentIndexChanged(int index)
{ {
if (loading) if (loading)
return; return;
@@ -167,7 +191,7 @@ void SceneSwitcher::on_autoStartType_currentIndexChanged(int index)
switcher->autoStartType = (AutoStartType)index; switcher->autoStartType = (AutoStartType)index;
} }
void SceneSwitcher::on_autoStartScenes_currentTextChanged(const QString &text) void AdvSceneSwitcher::on_autoStartScenes_currentTextChanged(const QString &text)
{ {
if (loading) if (loading)
return; return;
@@ -176,7 +200,7 @@ void SceneSwitcher::on_autoStartScenes_currentTextChanged(const QString &text)
UpdateAutoStartScene(text); UpdateAutoStartScene(text);
} }
void SceneSwitcher::on_autoStartSceneCheckBox_stateChanged(int state) void AdvSceneSwitcher::on_autoStartSceneCheckBox_stateChanged(int state)
{ {
if (loading) if (loading)
return; return;
@@ -196,7 +220,7 @@ void SceneSwitcher::on_autoStartSceneCheckBox_stateChanged(int state)
} }
} }
void SceneSwitcher::UpdateAutoStartScene(const QString &name) void AdvSceneSwitcher::UpdateAutoStartScene(const QString &name)
{ {
obs_source_t *scene = obs_get_source_by_name(name.toUtf8().constData()); obs_source_t *scene = obs_get_source_by_name(name.toUtf8().constData());
obs_weak_source_t *ws = obs_source_get_weak_source(scene); obs_weak_source_t *ws = obs_source_get_weak_source(scene);
@@ -218,12 +242,20 @@ void SwitcherData::autoStartStreamRecording()
if (ws && autoStartScene == ws) { if (ws && autoStartScene == ws) {
if ((switcher->autoStartType == STREAMING || if ((switcher->autoStartType == STREAMING ||
switcher->autoStartType == RECORINDGSTREAMING) && switcher->autoStartType == RECORINDGSTREAMING) &&
!obs_frontend_streaming_active()) !obs_frontend_streaming_active()) {
blog(LOG_INFO,
"Starting stream because scene '%s' is active",
obs_source_get_name(currentSource));
obs_frontend_streaming_start(); obs_frontend_streaming_start();
}
if ((switcher->autoStartType == RECORDING || if ((switcher->autoStartType == RECORDING ||
switcher->autoStartType == RECORINDGSTREAMING) && switcher->autoStartType == RECORINDGSTREAMING) &&
!obs_frontend_recording_active()) !obs_frontend_recording_active()) {
blog(LOG_INFO,
"Starting record because scene '%s' is active",
obs_source_get_name(currentSource));
obs_frontend_recording_start(); obs_frontend_recording_start();
}
} }
obs_source_release(currentSource); obs_source_release(currentSource);
obs_weak_source_release(ws); obs_weak_source_release(ws);
@@ -231,16 +263,21 @@ void SwitcherData::autoStartStreamRecording()
autoStartedRecently = true; autoStartedRecently = true;
} }
void SceneSwitcher::on_verboseLogging_stateChanged(int state) void AdvSceneSwitcher::on_verboseLogging_stateChanged(int state)
{ {
if (loading) if (loading)
return; return;
std::lock_guard<std::mutex> lock(switcher->m);
switcher->verbose = state; switcher->verbose = state;
} }
void SceneSwitcher::on_exportSettings_clicked() void AdvSceneSwitcher::on_uiHintsDisable_stateChanged(int state)
{
if (loading)
return;
switcher->disableHints = state;
}
void AdvSceneSwitcher::on_exportSettings_clicked()
{ {
QString directory = QFileDialog::getSaveFileName( QString directory = QFileDialog::getSaveFileName(
this, tr("Export Advanced Scene Switcher settings to file ..."), this, tr("Export Advanced Scene Switcher settings to file ..."),
@@ -274,7 +311,7 @@ void SceneSwitcher::on_exportSettings_clicked()
obs_data_release(obj); obs_data_release(obj);
} }
void SceneSwitcher::on_importSettings_clicked() void AdvSceneSwitcher::on_importSettings_clicked()
{ {
std::lock_guard<std::mutex> lock(switcher->m); std::lock_guard<std::mutex> lock(switcher->m);
@@ -324,86 +361,93 @@ void SceneSwitcher::on_importSettings_clicked()
close(); close();
} }
int findTabIndex(QTabBar *bar, int pos) int findTabIndex(QTabWidget *tabWidget, int pos)
{ {
int at = -1; int at = -1;
QString tabName = ""; QString tabName = "";
switch (pos) { switch (pos) {
case 0: case 0:
tabName = "General"; tabName = "generalTab";
break; break;
case 1: case 1:
tabName = "Transition"; tabName = "transitionsTab";
break; break;
case 2: case 2:
tabName = "Pause"; tabName = "pauseScenesTab";
break; break;
case 3: case 3:
tabName = "Title"; tabName = "windowTitleTab";
break; break;
case 4: case 4:
tabName = "Executable"; tabName = "executableTab";
break; break;
case 5: case 5:
tabName = "Region"; tabName = "screenRegionTab";
break; break;
case 6: case 6:
tabName = "Media"; tabName = "mediaTab";
break; break;
case 7: case 7:
tabName = "File"; tabName = "fileTab";
break; break;
case 8: case 8:
tabName = "Random"; tabName = "randomTab";
break; break;
case 9: case 9:
tabName = "Time"; tabName = "timeTab";
break; break;
case 10: case 10:
tabName = "Idle"; tabName = "idleTab";
break; break;
case 11: case 11:
tabName = "Sequence"; tabName = "sceneSequenceTab";
break; break;
case 12: case 12:
tabName = "Audio"; tabName = "audioTab";
break; break;
} }
for (int i = 0; i < bar->count(); ++i) { QWidget *page = tabWidget->findChild<QWidget *>(tabName);
if (bar->tabText(i).compare(tabName) == 0) { if (page) {
at = i; at = tabWidget->indexOf(page);
break;
}
} }
if (at == -1) if (at == -1)
blog(LOG_INFO, "Advanced Scene Switcher failed to find tab %s", blog(LOG_INFO, "failed to find tab %s",
tabName.toUtf8().constData()); tabName.toUtf8().constData());
return at; return at;
} }
void SceneSwitcher::setTabOrder() void AdvSceneSwitcher::setTabOrder()
{ {
QTabBar *bar = ui->tabWidget->tabBar(); QTabBar *bar = ui->tabWidget->tabBar();
for (int i = 0; i < bar->count(); ++i) { for (int i = 0; i < bar->count(); ++i) {
int curPos = findTabIndex(bar, switcher->tabOrder[i]); int curPos = findTabIndex(ui->tabWidget, switcher->tabOrder[i]);
if (i != curPos && curPos != -1) if (i != curPos && curPos != -1)
bar->moveTab(curPos, i); bar->moveTab(curPos, i);
} }
connect(bar, &QTabBar::tabMoved, this, &SceneSwitcher::on_tabMoved); connect(bar, &QTabBar::tabMoved, this, &AdvSceneSwitcher::on_tabMoved);
} }
void SceneSwitcher::on_tabMoved(int from, int to) void AdvSceneSwitcher::on_tabMoved(int from, int to)
{ {
if (loading) if (loading)
return; return;
std::swap(switcher->tabOrder[from], switcher->tabOrder[to]); std::swap(switcher->tabOrder[from], switcher->tabOrder[to]);
} }
void AdvSceneSwitcher::on_tabWidget_currentChanged(int index)
{
UNUSED_PARAMETER(index);
switcher->showFrame = false;
clearFrames(ui->screenRegionSwitches);
SetShowFrames();
}
void SwitcherData::saveGeneralSettings(obs_data_t *obj) void SwitcherData::saveGeneralSettings(obs_data_t *obj)
{ {
obs_data_set_int(obj, "interval", switcher->interval); obs_data_set_int(obj, "interval", switcher->interval);
@@ -414,6 +458,7 @@ void SwitcherData::saveGeneralSettings(obs_data_t *obj)
nonMatchingSceneName.c_str()); nonMatchingSceneName.c_str());
obs_data_set_int(obj, "switch_if_not_matching", obs_data_set_int(obj, "switch_if_not_matching",
switcher->switchIfNotMatching); switcher->switchIfNotMatching);
obs_data_set_double(obj, "noMatchDelay", switcher->noMatchDelay);
obs_data_set_bool(obj, "active", !switcher->stop); obs_data_set_bool(obj, "active", !switcher->stop);
obs_data_set_int(obj, "startup_behavior", switcher->startupBehavior); obs_data_set_int(obj, "startup_behavior", switcher->startupBehavior);
@@ -432,6 +477,7 @@ void SwitcherData::saveGeneralSettings(obs_data_t *obj)
autoStartSceneName.c_str()); autoStartSceneName.c_str());
obs_data_set_bool(obj, "verbose", switcher->verbose); obs_data_set_bool(obj, "verbose", switcher->verbose);
obs_data_set_bool(obj, "disableHints", switcher->disableHints);
obs_data_set_int(obj, "priority0", obs_data_set_int(obj, "priority0",
switcher->functionNamesByPriority[0]); switcher->functionNamesByPriority[0]);
@@ -454,6 +500,14 @@ void SwitcherData::saveGeneralSettings(obs_data_t *obj)
obs_data_set_int(obj, "threadPriority", switcher->threadPriority); obs_data_set_int(obj, "threadPriority", switcher->threadPriority);
// After fresh install of OBS the vector can be empty
// as save() might be called before first load()
if (switcher->tabOrder.size() < 13) {
switcher->tabOrder = std::vector<int>(13);
std::iota(switcher->tabOrder.begin(), switcher->tabOrder.end(),
0);
}
obs_data_set_int(obj, "generalTabPos", switcher->tabOrder[0]); obs_data_set_int(obj, "generalTabPos", switcher->tabOrder[0]);
obs_data_set_int(obj, "transitionTabPos", switcher->tabOrder[1]); obs_data_set_int(obj, "transitionTabPos", switcher->tabOrder[1]);
obs_data_set_int(obj, "pauseTabPos", switcher->tabOrder[2]); obs_data_set_int(obj, "pauseTabPos", switcher->tabOrder[2]);
@@ -481,6 +535,7 @@ void SwitcherData::loadGeneralSettings(obs_data_t *obj)
obs_data_get_string(obj, "non_matching_scene"); obs_data_get_string(obj, "non_matching_scene");
switcher->nonMatchingScene = switcher->nonMatchingScene =
GetWeakSourceByName(nonMatchingScene.c_str()); GetWeakSourceByName(nonMatchingScene.c_str());
switcher->noMatchDelay = obs_data_get_double(obj, "noMatchDelay");
switcher->stop = !obs_data_get_bool(obj, "active"); switcher->stop = !obs_data_get_bool(obj, "active");
switcher->startupBehavior = switcher->startupBehavior =
@@ -503,6 +558,7 @@ void SwitcherData::loadGeneralSettings(obs_data_t *obj)
switcher->autoStartScene = GetWeakSourceByName(autoStartScene.c_str()); switcher->autoStartScene = GetWeakSourceByName(autoStartScene.c_str());
switcher->verbose = obs_data_get_bool(obj, "verbose"); switcher->verbose = obs_data_get_bool(obj, "verbose");
switcher->disableHints = obs_data_get_bool(obj, "disableHints");
obs_data_set_default_int(obj, "priority0", default_priority_0); obs_data_set_default_int(obj, "priority0", default_priority_0);
obs_data_set_default_int(obj, "priority1", default_priority_1); obs_data_set_default_int(obj, "priority1", default_priority_1);
@@ -590,7 +646,30 @@ void SwitcherData::loadGeneralSettings(obs_data_t *obj)
(int)(obs_data_get_int(obj, "audioTabPos"))); (int)(obs_data_get_int(obj, "audioTabPos")));
} }
void SceneSwitcher::setupGeneralTab() void SwitcherData::checkNoMatchSwitch(bool &match, OBSWeakSource &scene,
OBSWeakSource &transition, int &sleep)
{
if (match) {
noMatchCount = 0;
return;
}
if ((noMatchCount * interval) / 1000.0 < noMatchDelay) {
noMatchCount++;
return;
}
if (switchIfNotMatching == SWITCH && nonMatchingScene) {
match = true;
scene = nonMatchingScene;
transition = nullptr;
}
if (switchIfNotMatching == RANDOM_SWITCH) {
checkRandom(match, scene, transition, sleep);
}
}
void AdvSceneSwitcher::setupGeneralTab()
{ {
populateSceneSelection(ui->noMatchSwitchScene, false); populateSceneSelection(ui->noMatchSwitchScene, false);
populateSceneSelection(ui->autoStopScenes, false); populateSceneSelection(ui->autoStopScenes, false);
@@ -608,6 +687,9 @@ void SceneSwitcher::setupGeneralTab()
} }
ui->noMatchSwitchScene->setCurrentText( ui->noMatchSwitchScene->setCurrentText(
GetWeakSourceName(switcher->nonMatchingScene).c_str()); GetWeakSourceName(switcher->nonMatchingScene).c_str());
ui->noMatchDelay->setValue(switcher->noMatchDelay);
ui->noMatchDelay->setToolTip(
"Will only ever be as accurate as the configured check interval.");
ui->checkInterval->setValue(switcher->interval); ui->checkInterval->setValue(switcher->interval);
ui->autoStopSceneCheckBox->setChecked(switcher->autoStopEnable); ui->autoStopSceneCheckBox->setChecked(switcher->autoStopEnable);
@@ -638,6 +720,7 @@ void SceneSwitcher::setupGeneralTab()
} }
ui->verboseLogging->setChecked(switcher->verbose); ui->verboseLogging->setChecked(switcher->verbose);
ui->uiHintsDisable->setChecked(switcher->disableHints);
for (int p : switcher->functionNamesByPriority) { for (int p : switcher->functionNamesByPriority) {
std::string s = ""; std::string s = "";

View File

@@ -1,63 +1,48 @@
#pragma once #pragma once
#include <QDialog>
#include <memory>
#include <vector>
#include <string>
#ifdef BUILD_OUT_OF_TREE #ifdef BUILD_OUT_OF_TREE
#include "../../forms/ui_advanced-scene-switcher.h" #include "../../forms/ui_advanced-scene-switcher.h"
#else #else
#include "ui_advanced-scene-switcher.h" #include "ui_advanced-scene-switcher.h"
#endif #endif
#include "switcher-data-structs.hpp" #include "switcher-data-structs.hpp"
#include "volume-control.hpp"
#define blog(level, msg, ...) blog(level, "[adv-ss] " msg, ##__VA_ARGS__)
class QCloseEvent; class QCloseEvent;
/******************************************************************************* /*******************************************************************************
* Advanced Scene Switcher window * Advanced Scene Switcher window
*******************************************************************************/ *******************************************************************************/
class SceneSwitcher : public QDialog { class AdvSceneSwitcher : public QDialog {
Q_OBJECT Q_OBJECT
public: public:
std::unique_ptr<Ui_SceneSwitcher> ui; std::unique_ptr<Ui_AdvSceneSwitcher> ui;
VolControl *volMeter = nullptr;
bool loading = true; bool loading = true;
SceneSwitcher(QWidget *parent); AdvSceneSwitcher(QWidget *parent);
void closeEvent(QCloseEvent *event) override; void closeEvent(QCloseEvent *event) override;
void SetStarted(); void SetStarted();
void SetStopped(); void SetStopped();
int FindByData(const QString &window); void SetShowFrames();
int ScreenRegionFindByData(const QString &region); void SetHideFrames();
void clearFrames(QListWidget *list);
int PauseScenesFindByData(const QString &scene); int PauseScenesFindByData(const QString &scene);
int PauseWindowsFindByData(const QString &window); int PauseWindowsFindByData(const QString &window);
int IgnoreWindowsFindByData(const QString &window); int IgnoreWindowsFindByData(const QString &window);
int SceneSequenceFindByData(const QString &scene1);
int SceneTransitionsFindByData(const QString &scene1,
const QString &scene2);
int DefaultTransitionsFindByData(const QString &scene);
int executableFindByData(const QString &exe);
int IgnoreIdleWindowsFindByData(const QString &window); int IgnoreIdleWindowsFindByData(const QString &window);
int randomFindByData(const QString &scene);
int timeFindByData(const timeTrigger &trigger, const QTime &time);
int audioFindByData(const QString &source, const int &volume);
void UpdateNonMatchingScene(const QString &name); void UpdateNonMatchingScene(const QString &name);
void UpdateAutoStopScene(const QString &name); void UpdateAutoStopScene(const QString &name);
void UpdateAutoStartScene(const QString &name); void UpdateAutoStartScene(const QString &name);
void UpdateIdleDataTransition(const QString &name); void UpdateIdleDataTransition(const QString &name);
void UpdateIdleDataScene(const QString &name); void UpdateIdleDataScene(const QString &name);
void SetAudioVolumeMeter(const QString &name);
void loadUI(); void loadUI();
void populateSceneSelection(QComboBox *sel, bool addPrevious);
void populateTransitionSelection(QComboBox *sel);
void populateWindowSelection(QComboBox *sel);
void setupGeneralTab(); void setupGeneralTab();
void setupTitleTab(); void setupTitleTab();
void setupExecutableTab(); void setupExecutableTab();
@@ -73,22 +58,44 @@ public:
void setupAudioTab(); void setupAudioTab();
void setTabOrder(); void setTabOrder();
static void populateSceneSelection(QComboBox *sel,
bool addPrevious = false,
bool addSelect = true);
static void populateTransitionSelection(QComboBox *sel,
bool addSelect = true);
static void populateWindowSelection(QComboBox *sel,
bool addSelect = true);
static void populateAudioSelection(QComboBox *sel,
bool addSelect = true);
static void populateMediaSelection(QComboBox *sel,
bool addSelect = true);
static void populateProcessSelection(QComboBox *sel,
bool addSelect = true);
QMetaObject::Connection PulseWidget(QWidget *widget, QColor endColor,
QColor = QColor(0, 0, 0, 0),
QString specifier = "QLabel ");
bool listMoveUp(QListWidget *list);
bool listMoveDown(QListWidget *list);
public slots: public slots:
void on_switches_currentRowChanged(int idx); void on_windowUp_clicked();
void on_up_clicked(); void on_windowDown_clicked();
void on_down_clicked(); void on_windowAdd_clicked();
void on_add_clicked(); void on_windowRemove_clicked();
void on_remove_clicked();
void on_noMatchDontSwitch_clicked(); void on_noMatchDontSwitch_clicked();
void on_noMatchSwitch_clicked(); void on_noMatchSwitch_clicked();
void on_noMatchRandomSwitch_clicked(); void on_noMatchRandomSwitch_clicked();
void on_noMatchDelay_valueChanged(double i);
void on_startupBehavior_currentIndexChanged(int index); void on_startupBehavior_currentIndexChanged(int index);
void on_noMatchSwitchScene_currentTextChanged(const QString &text); void on_noMatchSwitchScene_currentTextChanged(const QString &text);
void on_checkInterval_valueChanged(int value); void on_checkInterval_valueChanged(int value);
void on_toggleStartButton_clicked(); void on_toggleStartButton_clicked();
void on_tabMoved(int from, int to); void on_tabMoved(int from, int to);
void on_tabWidget_currentChanged(int index);
void on_screenRegions_currentRowChanged(int idx); void on_screenRegionSwitches_currentRowChanged(int idx);
void on_showFrame_clicked();
void on_screenRegionAdd_clicked(); void on_screenRegionAdd_clicked();
void on_screenRegionRemove_clicked(); void on_screenRegionRemove_clicked();
void on_screenRegionUp_clicked(); void on_screenRegionUp_clicked();
@@ -106,14 +113,12 @@ public slots:
void on_ignoreWindowsAdd_clicked(); void on_ignoreWindowsAdd_clicked();
void on_ignoreWindowsRemove_clicked(); void on_ignoreWindowsRemove_clicked();
void on_sceneSequences_currentRowChanged(int idx);
void on_sceneSequenceAdd_clicked(); void on_sceneSequenceAdd_clicked();
void on_sceneSequenceRemove_clicked(); void on_sceneSequenceRemove_clicked();
void on_sceneSequenceSave_clicked(); void on_sceneSequenceSave_clicked();
void on_sceneSequenceLoad_clicked(); void on_sceneSequenceLoad_clicked();
void on_sceneSequenceUp_clicked(); void on_sceneSequenceUp_clicked();
void on_sceneSequenceDown_clicked(); void on_sceneSequenceDown_clicked();
void on_sceneSequenceDelayUnits_currentIndexChanged(int index);
void on_autoStopSceneCheckBox_stateChanged(int state); void on_autoStopSceneCheckBox_stateChanged(int state);
void on_autoStopScenes_currentTextChanged(const QString &text); void on_autoStopScenes_currentTextChanged(const QString &text);
@@ -123,16 +128,19 @@ public slots:
void on_autoStartScenes_currentTextChanged(const QString &text); void on_autoStartScenes_currentTextChanged(const QString &text);
void on_verboseLogging_stateChanged(int state); void on_verboseLogging_stateChanged(int state);
void on_uiHintsDisable_stateChanged(int state);
void on_exportSettings_clicked(); void on_exportSettings_clicked();
void on_importSettings_clicked(); void on_importSettings_clicked();
void on_sceneTransitions_currentRowChanged(int idx);
void on_transitionsAdd_clicked(); void on_transitionsAdd_clicked();
void on_transitionsRemove_clicked(); void on_transitionsRemove_clicked();
void on_defaultTransitions_currentRowChanged(int idx); void on_transitionsUp_clicked();
void on_transitionsDown_clicked();
void on_defaultTransitionsAdd_clicked(); void on_defaultTransitionsAdd_clicked();
void on_defaultTransitionsRemove_clicked(); void on_defaultTransitionsRemove_clicked();
void on_defaultTransitionsUp_clicked();
void on_defaultTransitionsDown_clicked();
void on_transitionOverridecheckBox_stateChanged(int state); void on_transitionOverridecheckBox_stateChanged(int state);
void on_browseButton_clicked(); void on_browseButton_clicked();
@@ -146,7 +154,6 @@ public slots:
void on_executableDown_clicked(); void on_executableDown_clicked();
void on_executableAdd_clicked(); void on_executableAdd_clicked();
void on_executableRemove_clicked(); void on_executableRemove_clicked();
void on_executables_currentRowChanged(int idx);
void on_idleCheckBox_stateChanged(int state); void on_idleCheckBox_stateChanged(int state);
void on_idleTransitions_currentTextChanged(const QString &text); void on_idleTransitions_currentTextChanged(const QString &text);
@@ -158,7 +165,6 @@ public slots:
void on_randomAdd_clicked(); void on_randomAdd_clicked();
void on_randomRemove_clicked(); void on_randomRemove_clicked();
void on_randomScenesList_currentRowChanged(int idx);
void on_fileAdd_clicked(); void on_fileAdd_clicked();
void on_fileRemove_clicked(); void on_fileRemove_clicked();
@@ -167,14 +173,11 @@ public slots:
void on_fileUp_clicked(); void on_fileUp_clicked();
void on_fileDown_clicked(); void on_fileDown_clicked();
void on_mediaSwitches_currentRowChanged(int idx);
void on_mediaAdd_clicked(); void on_mediaAdd_clicked();
void on_mediaRemove_clicked(); void on_mediaRemove_clicked();
void on_mediaUp_clicked(); void on_mediaUp_clicked();
void on_mediaDown_clicked(); void on_mediaDown_clicked();
void on_mediaTimeRestrictions_currentIndexChanged(int idx);
void on_timeSwitches_currentRowChanged(int idx);
void on_timeAdd_clicked(); void on_timeAdd_clicked();
void on_timeRemove_clicked(); void on_timeRemove_clicked();
void on_timeUp_clicked(); void on_timeUp_clicked();
@@ -184,8 +187,6 @@ public slots:
void on_audioRemove_clicked(); void on_audioRemove_clicked();
void on_audioUp_clicked(); void on_audioUp_clicked();
void on_audioDown_clicked(); void on_audioDown_clicked();
void on_audioSwitches_currentRowChanged(int idx);
void on_audioSources_currentTextChanged(const QString &text);
void on_priorityUp_clicked(); void on_priorityUp_clicked();
void on_priorityDown_clicked(); void on_priorityDown_clicked();
@@ -226,13 +227,6 @@ bool isInFocus(const QString &executable);
/******************************************************************************** /********************************************************************************
* Sceneswitch helper * Sceneswitch helper
********************************************************************************/ ********************************************************************************/
struct obs_weak_source;
typedef struct obs_weak_source obs_weak_source_t;
typedef struct transitionData {
std::string name = "";
int duration = 0;
} transitionData;
void setNextTransition(OBSWeakSource &targetScene, obs_source_t *currentSource, void setNextTransition(OBSWeakSource &targetScene, obs_source_t *currentSource,
OBSWeakSource &transition, OBSWeakSource &transition,
@@ -240,10 +234,8 @@ void setNextTransition(OBSWeakSource &targetScene, obs_source_t *currentSource,
void overwriteTransitionOverride(obs_weak_source_t *sceneWs, void overwriteTransitionOverride(obs_weak_source_t *sceneWs,
obs_source_t *transition, transitionData &td); obs_source_t *transition, transitionData &td);
void restoreTransitionOverride(obs_source_t *scene, transitionData td); void restoreTransitionOverride(obs_source_t *scene, transitionData td);
void switchScene(OBSWeakSource &scene, OBSWeakSource &transition, void switchScene(OBSWeakSource &scene, OBSWeakSource &transition,
bool &transitionOverrideOverride, bool &transitionOverrideOverride);
std::unique_lock<std::mutex> &lock);
/******************************************************************************** /********************************************************************************
* Main SwitcherData * Main SwitcherData

View File

@@ -1,115 +1,65 @@
#pragma once #pragma once
#include <string> #include <QSpinBox>
#include "utility.hpp"
#include "switch-generic.hpp" #include "switch-generic.hpp"
#include "volume-control.hpp"
constexpr auto audio_func = 8; constexpr auto audio_func = 8;
constexpr auto default_priority_8 = audio_func; constexpr auto default_priority_8 = audio_func;
struct AudioSwitch : virtual SceneSwitcherEntry { struct AudioSwitch : virtual SceneSwitcherEntry {
OBSWeakSource audioSource; OBSWeakSource audioSource = nullptr;
int volumeThreshold; int volumeThreshold = 0;
float peak; float peak = -1;
std::string audioSwitchStr; obs_volmeter_t *volmeter = nullptr;
obs_volmeter_t *volmeter;
const char *getType() { return "audio"; } const char *getType() { return "audio"; }
bool initialized();
bool valid();
static void setVolumeLevel(void *data, static void setVolumeLevel(void *data,
const float magnitude[MAX_AUDIO_CHANNELS], const float magnitude[MAX_AUDIO_CHANNELS],
const float peak[MAX_AUDIO_CHANNELS], const float peak[MAX_AUDIO_CHANNELS],
const float inputPeak[MAX_AUDIO_CHANNELS]) const float inputPeak[MAX_AUDIO_CHANNELS]);
{ void resetVolmeter();
UNUSED_PARAMETER(magnitude);
UNUSED_PARAMETER(inputPeak);
AudioSwitch *s = static_cast<AudioSwitch *>(data);
s->peak = peak[0]; AudioSwitch(){};
for (int i = 1; i < MAX_AUDIO_CHANNELS; i++) AudioSwitch(OBSWeakSource scene_, OBSWeakSource transition_,
if (peak[i] > s->peak) OBSWeakSource audioSource_, int volumeThreshold_,
s->peak = peak[i]; bool usePreviousScene_);
} AudioSwitch(const AudioSwitch &other);
AudioSwitch(AudioSwitch &&other);
bool valid() ~AudioSwitch();
{ AudioSwitch &operator=(const AudioSwitch &other);
return (usePreviousScene || WeakSourceValid(scene)) && AudioSwitch &operator=(AudioSwitch &&other) noexcept;
WeakSourceValid(audioSource) && friend void swap(AudioSwitch &first, AudioSwitch &second);
WeakSourceValid(transition);
}
inline AudioSwitch(OBSWeakSource scene_, OBSWeakSource transition_,
OBSWeakSource audioSource_, int volumeThreshold_,
bool usePreviousScene_, std::string audioSwitchStr_)
: SceneSwitcherEntry(scene_, transition_, usePreviousScene_),
audioSource(audioSource_),
volumeThreshold(volumeThreshold_),
audioSwitchStr(audioSwitchStr_)
{
volmeter = obs_volmeter_create(OBS_FADER_LOG);
obs_volmeter_add_callback(volmeter, setVolumeLevel, this);
obs_source_t *as = obs_weak_source_get_source(audioSource);
if (!obs_volmeter_attach_source(volmeter, as)) {
const char *name = obs_source_get_name(as);
blog(LOG_WARNING,
"failed to attach volmeter to source %s", name);
}
obs_source_release(as);
}
AudioSwitch(const AudioSwitch &other)
: SceneSwitcherEntry(other.scene, other.transition,
other.usePreviousScene),
audioSource(other.audioSource),
volumeThreshold(other.volumeThreshold),
audioSwitchStr(other.audioSwitchStr)
{
volmeter = obs_volmeter_create(OBS_FADER_LOG);
obs_volmeter_add_callback(volmeter, setVolumeLevel, this);
obs_source_t *as =
obs_weak_source_get_source(other.audioSource);
if (!obs_volmeter_attach_source(volmeter, as)) {
const char *name = obs_source_get_name(as);
blog(LOG_WARNING,
"failed to attach volmeter to source %s", name);
}
obs_source_release(as);
}
AudioSwitch(AudioSwitch &&other)
: SceneSwitcherEntry(other.scene, other.transition,
other.usePreviousScene),
audioSource(other.audioSource),
volumeThreshold(other.volumeThreshold),
audioSwitchStr(other.audioSwitchStr),
volmeter(other.volmeter)
{
other.volmeter = nullptr;
}
inline ~AudioSwitch()
{
obs_volmeter_remove_callback(volmeter, setVolumeLevel, this);
obs_volmeter_destroy(volmeter);
}
AudioSwitch &operator=(const AudioSwitch &other)
{
return *this = AudioSwitch(other);
}
AudioSwitch &operator=(AudioSwitch &&other) noexcept
{
if (this == &other) {
return *this;
}
obs_volmeter_destroy(volmeter);
volmeter = other.volmeter;
other.volmeter = nullptr;
return *this;
}
}; };
static inline QString MakeAudioSwitchName(const QString &scene, class AudioSwitchWidget : public SwitchWidget {
const QString &transition, Q_OBJECT
const QString &audioSrouce,
const int &volume); public:
AudioSwitchWidget(AudioSwitch *s);
void UpdateVolmeterSource();
AudioSwitch *getSwitchData();
void setSwitchData(AudioSwitch *s);
static void swapSwitchData(AudioSwitchWidget *as1,
AudioSwitchWidget *as2);
private slots:
void SourceChanged(const QString &text);
void VolumeThresholdChanged(int vol);
private:
QComboBox *audioSources;
QSpinBox *audioVolumeThreshold;
VolControl *volMeter;
QLabel *whenLabel;
QLabel *aboveLabel;
QLabel *switchLabel;
QLabel *usingLabel;
AudioSwitch *switchData;
};

View File

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

View File

@@ -1,6 +1,4 @@
#pragma once #pragma once
#include <string>
#include "utility.hpp"
#include "switch-generic.hpp" #include "switch-generic.hpp"
constexpr auto read_file_func = 0; constexpr auto read_file_func = 0;

View File

@@ -1,48 +1,50 @@
#pragma once #pragma once
#include <string>
#include <obs.hpp> #include <obs.hpp>
#include <QComboBox>
struct SceneSwitcherEntry { struct SceneSwitcherEntry {
OBSWeakSource scene; OBSWeakSource scene = nullptr;
OBSWeakSource transition; OBSWeakSource transition = nullptr;
bool usePreviousScene; bool usePreviousScene = false;
virtual bool valid()
{
return (usePreviousScene || WeakSourceValid(scene)) &&
WeakSourceValid(transition);
}
virtual const char *getType() = 0; virtual const char *getType() = 0;
virtual bool initialized();
virtual bool valid();
virtual void logMatch();
// TODO inline SceneSwitcherEntry() {}
//virtual bool checkMatch() = 0;
virtual void logMatch()
{
obs_source_t *s = obs_weak_source_get_source(scene);
const char *sceneName = obs_source_get_name(s);
obs_source_release(s);
blog(LOG_INFO,
"Advanced Scene Switcher match for '%s' - switch to scene '%s'",
getType(), sceneName);
}
inline SceneSwitcherEntry() : usePreviousScene(false) {}
inline SceneSwitcherEntry(OBSWeakSource scene_, inline SceneSwitcherEntry(OBSWeakSource scene_,
OBSWeakSource transition_, OBSWeakSource transition_,
bool usePreviousScene_) bool usePreviousScene_ = false)
: scene(scene_), : scene(scene_),
transition(transition_), transition(transition_),
usePreviousScene(usePreviousScene_) usePreviousScene(usePreviousScene_)
{ {
} }
inline SceneSwitcherEntry(OBSWeakSource scene_,
OBSWeakSource transition_) virtual ~SceneSwitcherEntry() {}
: scene(scene_), };
transition(transition_),
usePreviousScene(false) class SwitchWidget : public QWidget {
{ Q_OBJECT
}
public:
SwitchWidget(SceneSwitcherEntry *s, bool usePreviousScene = true);
virtual SceneSwitcherEntry *getSwitchData();
virtual void setSwitchData(SceneSwitcherEntry *s);
static void swapSwitchData(SwitchWidget *s1, SwitchWidget *s2);
private slots:
void SceneChanged(const QString &text);
void TransitionChanged(const QString &text);
protected:
bool loading = true;
QComboBox *scenes;
QComboBox *transitions;
SceneSwitcherEntry *switchData;
}; };

View File

@@ -1,6 +1,4 @@
#pragma once #pragma once
#include <string>
#include "utility.hpp"
#include "switch-generic.hpp" #include "switch-generic.hpp"
constexpr auto default_idle_time = 60; constexpr auto default_idle_time = 60;

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,7 +1,6 @@
#pragma once #pragma once
#include <string> #include <QTimeEdit>
#include <QDateTime>
#include "utility.hpp"
#include "switch-generic.hpp" #include "switch-generic.hpp"
constexpr auto time_func = 7; constexpr auto time_func = 7;
@@ -20,24 +19,43 @@ typedef enum {
} timeTrigger; } timeTrigger;
struct TimeSwitch : SceneSwitcherEntry { struct TimeSwitch : SceneSwitcherEntry {
timeTrigger trigger; timeTrigger trigger = ANY_DAY;
QTime time; QTime time = QTime();
std::string timeSwitchStr;
const char *getType() { return "time"; } const char *getType() { return "time"; }
inline TimeSwitch(){};
inline TimeSwitch(OBSWeakSource scene_, OBSWeakSource transition_, inline TimeSwitch(OBSWeakSource scene_, OBSWeakSource transition_,
timeTrigger trigger_, QTime time_, timeTrigger trigger_, QTime time_,
bool usePreviousScene_, std::string timeSwitchStr_) bool usePreviousScene_)
: SceneSwitcherEntry(scene_, transition_, usePreviousScene_), : SceneSwitcherEntry(scene_, transition_, usePreviousScene_),
trigger(trigger_), trigger(trigger_),
time(time_), time(time_)
timeSwitchStr(timeSwitchStr_)
{ {
} }
}; };
static inline QString MakeTimeSwitchName(const QString &scene, class TimeSwitchWidget : public SwitchWidget {
const QString &transition, Q_OBJECT
const timeTrigger &trigger,
const QTime &time); public:
TimeSwitchWidget(TimeSwitch *s);
TimeSwitch *getSwitchData();
void setSwitchData(TimeSwitch *s);
static void swapSwitchData(TimeSwitchWidget *s1, TimeSwitchWidget *s2);
private slots:
void TriggerChanged(int index);
void TimeChanged(const QTime &time);
private:
QComboBox *triggers;
QTimeEdit *time;
QLabel *atLabel;
QLabel *switchLabel;
QLabel *usingLabel;
TimeSwitch *switchData;
};

View File

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

View File

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

View File

@@ -1,7 +1,7 @@
#pragma once #pragma once
#include <condition_variable> #include <condition_variable>
#include <string>
#include <vector> #include <vector>
#include <deque>
#include <mutex> #include <mutex>
#include <QDateTime> #include <QDateTime>
#include <QThread> #include <QThread>
@@ -17,7 +17,7 @@
#include "switch-time.hpp" #include "switch-time.hpp"
#include "switch-transitions.hpp" #include "switch-transitions.hpp"
#include "switch-window.hpp" #include "switch-window.hpp"
#include "swtich-sequence.hpp" #include "switch-sequence.hpp"
constexpr auto default_interval = 300; constexpr auto default_interval = 300;
constexpr auto previous_scene_name = "Previous Scene"; constexpr auto previous_scene_name = "Previous Scene";
@@ -30,6 +30,12 @@ typedef enum {
RECORINDGSTREAMING = 2 RECORINDGSTREAMING = 2
} AutoStartType; } AutoStartType;
typedef struct transitionData {
std::string name = "";
int duration = 0;
} transitionData;
class SwitcherThread; class SwitcherThread;
/******************************************************************************** /********************************************************************************
@@ -45,23 +51,27 @@ struct SwitcherData {
std::condition_variable transitionCv; std::condition_variable transitionCv;
bool stop = false; bool stop = false;
bool verbose = false; bool verbose = false;
bool disableHints = false;
bool showFrame = false;
bool tansitionOverrideOverride = false; bool tansitionOverrideOverride = false;
int interval = default_interval; int interval = default_interval;
obs_source_t *waitScene = NULL; obs_source_t *waitScene = nullptr;
OBSWeakSource previousScene = NULL; OBSWeakSource previousScene = nullptr;
OBSWeakSource PreviousScene2 = NULL; OBSWeakSource previousScene2 = nullptr;
OBSWeakSource lastRandomScene; OBSWeakSource lastRandomScene;
OBSWeakSource nonMatchingScene; OBSWeakSource nonMatchingScene;
NoMatch switchIfNotMatching = NO_SWITCH; NoMatch switchIfNotMatching = NO_SWITCH;
double noMatchDelay;
double noMatchCount = 0;
StartupBehavior startupBehavior = PERSIST; StartupBehavior startupBehavior = PERSIST;
std::vector<WindowSceneSwitch> windowSwitches; std::deque<WindowSwitch> windowSwitches;
std::vector<std::string> ignoreIdleWindows; std::vector<std::string> ignoreIdleWindows;
std::string lastTitle; std::string lastTitle;
std::vector<ScreenRegionSwitch> screenRegionSwitches; std::deque<ScreenRegionSwitch> screenRegionSwitches;
std::vector<OBSWeakSource> pauseScenesSwitches; std::vector<OBSWeakSource> pauseScenesSwitches;
@@ -69,17 +79,17 @@ struct SwitcherData {
std::vector<std::string> ignoreWindowsSwitches; std::vector<std::string> ignoreWindowsSwitches;
std::vector<SceneSequenceSwitch> sceneSequenceSwitches; std::deque<SceneSequenceSwitch> sceneSequenceSwitches;
int sceneSequenceMultiplier = 1;
std::vector<RandomSwitch> randomSwitches; std::deque<RandomSwitch> randomSwitches;
IdleData idleData;
FileIOData fileIO; FileIOData fileIO;
IdleData idleData;
std::vector<FileSwitch> fileSwitches; std::vector<FileSwitch> fileSwitches;
CURL *curl = nullptr; CURL *curl = nullptr;
std::vector<ExecutableSceneSwitch> executableSwitches; std::deque<ExecutableSwitch> executableSwitches;
bool autoStopEnable = false; bool autoStopEnable = false;
OBSWeakSource autoStopScene; OBSWeakSource autoStopScene;
@@ -89,15 +99,16 @@ struct SwitcherData {
OBSWeakSource autoStartScene; OBSWeakSource autoStartScene;
bool autoStartedRecently = false; bool autoStartedRecently = false;
std::vector<SceneTransition> sceneTransitions; std::deque<SceneTransition> sceneTransitions;
std::vector<DefaultSceneTransition> defaultSceneTransitions; std::deque<DefaultSceneTransition> defaultSceneTransitions;
bool checkedDefTransition = false;
std::vector<MediaSwitch> mediaSwitches; std::deque<MediaSwitch> mediaSwitches;
std::vector<TimeSwitch> timeSwitches; std::deque<TimeSwitch> timeSwitches;
QDateTime liveTime; QDateTime liveTime;
std::vector<AudioSwitch> audioSwitches; std::deque<AudioSwitch> audioSwitches;
std::vector<int> functionNamesByPriority = std::vector<int>{ std::vector<int> functionNamesByPriority = std::vector<int>{
default_priority_0, default_priority_1, default_priority_2, default_priority_0, default_priority_1, default_priority_2,
@@ -134,9 +145,9 @@ struct SwitcherData {
std::vector<int> tabOrder; std::vector<int> tabOrder;
bool hotkeysRegistered = false; bool hotkeysRegistered = false;
obs_hotkey_id startHotkey; obs_hotkey_id startHotkey = 0;
obs_hotkey_id stopHotkey; obs_hotkey_id stopHotkey = 0;
obs_hotkey_id toggleHotkey; obs_hotkey_id toggleHotkey = 0;
void Thread(); void Thread();
void Start(); void Start();
@@ -145,13 +156,15 @@ struct SwitcherData {
bool sceneChangedDuringWait(); bool sceneChangedDuringWait();
bool prioFuncsValid(); bool prioFuncsValid();
void writeSceneInfoToFile(); void writeSceneInfoToFile();
void setDefaultSceneTransitions();
void autoStopStreamAndRecording(); void autoStopStreamAndRecording();
void autoStartStreamRecording(); void autoStartStreamRecording();
bool checkPause(); bool checkPause();
void checkDefaultSceneTransitions(bool &match,
OBSWeakSource &transition);
void setCurrentDefTransition(OBSWeakSource &transition);
void checkSceneSequence(bool &match, OBSWeakSource &scene, void checkSceneSequence(bool &match, OBSWeakSource &scene,
OBSWeakSource &transition, OBSWeakSource &transition,
std::unique_lock<std::mutex> &lock); std::unique_lock<std::mutex> &lock);
void checkIdleSwitch(bool &match, OBSWeakSource &scene, void checkIdleSwitch(bool &match, OBSWeakSource &scene,
OBSWeakSource &transition); OBSWeakSource &transition);
void checkWindowTitleSwitch(bool &match, OBSWeakSource &scene, void checkWindowTitleSwitch(bool &match, OBSWeakSource &scene,
@@ -172,6 +185,8 @@ struct SwitcherData {
OBSWeakSource &transition); OBSWeakSource &transition);
void checkAudioSwitch(bool &match, OBSWeakSource &scene, void checkAudioSwitch(bool &match, OBSWeakSource &scene,
OBSWeakSource &transition); OBSWeakSource &transition);
void checkNoMatchSwitch(bool &match, OBSWeakSource &scene,
OBSWeakSource &transition, int &sleep);
void saveWindowTitleSwitches(obs_data_t *obj); void saveWindowTitleSwitches(obs_data_t *obj);
void saveScreenRegionSwitches(obs_data_t *obj); void saveScreenRegionSwitches(obs_data_t *obj);
@@ -204,6 +219,7 @@ struct SwitcherData {
void loadHotkeys(obs_data_t *obj); void loadHotkeys(obs_data_t *obj);
void Prune(); void Prune();
inline ~SwitcherData() { Stop(); } inline ~SwitcherData() { Stop(); }
}; };

View File

@@ -1,44 +0,0 @@
#pragma once
#include <string>
#include "utility.hpp"
#include "switch-generic.hpp"
constexpr auto round_trip_func = 1;
constexpr auto default_priority_1 = round_trip_func;
struct SceneSequenceSwitch : SceneSwitcherEntry {
OBSWeakSource startScene;
double delay;
std::string sceneSequenceStr;
const char *getType() { return "sequence"; }
bool valid()
{
return WeakSourceValid(startScene) &&
(usePreviousScene || WeakSourceValid(scene)) &&
WeakSourceValid(transition);
}
void logSleep(int dur)
{
blog(LOG_INFO, "Advanced Scene Switcher sequence sleep %d",
dur);
}
inline SceneSequenceSwitch(OBSWeakSource startScene_,
OBSWeakSource scene_,
OBSWeakSource transition_, double delay_,
bool usePreviousScene_, std::string str)
: SceneSwitcherEntry(scene_, transition_, usePreviousScene_),
startScene(startScene_),
delay(delay_),
sceneSequenceStr(str)
{
}
};
static inline QString MakeSceneSequenceSwitchName(const QString &scene1,
const QString &scene2,
const QString &transition,
double delay);

1
src/headers/version.h Normal file
View File

@@ -0,0 +1 @@
extern const char g_GIT_SHA1[];

View File

@@ -17,15 +17,16 @@ void GetWindowList(std::vector<std::string> &windows)
for (NSDictionary *app in apps) { for (NSDictionary *app in apps) {
// Construct string from NSString accounting for nil // Construct string from NSString accounting for nil
std::string name([[app objectForKey:@"kCGWindowName"] std::string name([[app objectForKey:@"kCGWindowName"]
UTF8String], UTF8String],
[[app objectForKey:@"kCGWindowName"] [[app objectForKey:@"kCGWindowName"]
lengthOfBytesUsingEncoding: lengthOfBytesUsingEncoding:
NSUTF8StringEncoding]); NSUTF8StringEncoding]);
std::string owner([[app objectForKey:@"kCGWindowOwnerName"] std::string owner(
UTF8String], [[app objectForKey:@"kCGWindowOwnerName"]
[[app objectForKey:@"kCGWindowOwnerName"] UTF8String],
lengthOfBytesUsingEncoding: [[app objectForKey:@"kCGWindowOwnerName"]
NSUTF8StringEncoding]); lengthOfBytesUsingEncoding:
NSUTF8StringEncoding]);
// Check if name exists // Check if name exists
if (!name.empty() && if (!name.empty() &&
@@ -53,15 +54,16 @@ void GetWindowList(QStringList &windows)
for (NSDictionary *app in apps) { for (NSDictionary *app in apps) {
// Construct string from NSString accounting for nil // Construct string from NSString accounting for nil
std::string name([[app objectForKey:@"kCGWindowName"] std::string name([[app objectForKey:@"kCGWindowName"]
UTF8String], UTF8String],
[[app objectForKey:@"kCGWindowName"] [[app objectForKey:@"kCGWindowName"]
lengthOfBytesUsingEncoding: lengthOfBytesUsingEncoding:
NSUTF8StringEncoding]); NSUTF8StringEncoding]);
std::string owner([[app objectForKey:@"kCGWindowOwnerName"] std::string owner(
UTF8String], [[app objectForKey:@"kCGWindowOwnerName"]
[[app objectForKey:@"kCGWindowOwnerName"] UTF8String],
lengthOfBytesUsingEncoding: [[app objectForKey:@"kCGWindowOwnerName"]
NSUTF8StringEncoding]); lengthOfBytesUsingEncoding:
NSUTF8StringEncoding]);
// Check if name exists // Check if name exists
if (!name.empty() && if (!name.empty() &&
@@ -144,15 +146,16 @@ bool isFullscreen(std::string &title)
for (NSDictionary *app in apps) { for (NSDictionary *app in apps) {
// Construct string from NSString accounting for nil // Construct string from NSString accounting for nil
std::string name([[app objectForKey:@"kCGWindowName"] std::string name([[app objectForKey:@"kCGWindowName"]
UTF8String], UTF8String],
[[app objectForKey:@"kCGWindowName"] [[app objectForKey:@"kCGWindowName"]
lengthOfBytesUsingEncoding: lengthOfBytesUsingEncoding:
NSUTF8StringEncoding]); NSUTF8StringEncoding]);
std::string owner([[app objectForKey:@"kCGWindowOwnerName"] std::string owner(
UTF8String], [[app objectForKey:@"kCGWindowOwnerName"]
[[app objectForKey:@"kCGWindowOwnerName"] UTF8String],
lengthOfBytesUsingEncoding: [[app objectForKey:@"kCGWindowOwnerName"]
NSUTF8StringEncoding]); lengthOfBytesUsingEncoding:
NSUTF8StringEncoding]);
// True if switch equals app // True if switch equals app
bool equals = (title == name || title == owner); bool equals = (title == name || title == owner);

View File

@@ -1,187 +1,80 @@
#include "headers/advanced-scene-switcher.hpp" #include "headers/advanced-scene-switcher.hpp"
#include "headers/volume-control.hpp" #include "headers/volume-control.hpp"
#include "headers/utility.hpp"
void SceneSwitcher::on_audioSwitches_currentRowChanged(int idx) static QMetaObject::Connection addPulse;
void AdvSceneSwitcher::on_audioAdd_clicked()
{ {
if (loading) ui->audioAdd->disconnect(addPulse);
return;
if (idx == -1)
return;
QListWidgetItem *item = ui->audioSwitches->item(idx);
QString audioScenestr = item->data(Qt::UserRole).toString();
std::lock_guard<std::mutex> lock(switcher->m); std::lock_guard<std::mutex> lock(switcher->m);
for (auto &s : switcher->audioSwitches) { switcher->audioSwitches.emplace_back();
if (audioScenestr.compare(s.audioSwitchStr.c_str()) == 0) {
QString sceneName = GetWeakSourceName(s.scene).c_str(); QListWidgetItem *item;
QString audioSrouceName = item = new QListWidgetItem(ui->audioSwitches);
GetWeakSourceName(s.audioSource).c_str(); ui->audioSwitches->addItem(item);
QString transitionName = AudioSwitchWidget *sw =
GetWeakSourceName(s.transition).c_str(); new AudioSwitchWidget(&switcher->audioSwitches.back());
ui->audioScenes->setCurrentText(sceneName); item->setSizeHint(sw->minimumSizeHint());
ui->audioTransitions->setCurrentText(transitionName); ui->audioSwitches->setItemWidget(item, sw);
ui->audioSources->setCurrentText(audioSrouceName);
ui->audioVolumeThreshold->setValue(s.volumeThreshold);
volMeter->GetSlider()->setValue(s.volumeThreshold);
break;
}
}
} }
void SceneSwitcher::on_audioSources_currentTextChanged(const QString &text) void AdvSceneSwitcher::on_audioRemove_clicked()
{
SetAudioVolumeMeter(text);
}
void SceneSwitcher::SetAudioVolumeMeter(const QString &name)
{
obs_source_t *soruce =
obs_get_source_by_name(name.toUtf8().constData());
if (!soruce) {
return;
}
if (volMeter) {
ui->audioControlLayout->removeWidget(volMeter);
delete volMeter;
}
volMeter = new VolControl(soruce);
ui->audioControlLayout->addWidget(volMeter);
obs_source_release(soruce);
QWidget::connect(volMeter->GetSlider(), SIGNAL(valueChanged(int)),
ui->audioVolumeThreshold, SLOT(setValue(int)));
QWidget::connect(ui->audioVolumeThreshold, SIGNAL(valueChanged(int)),
volMeter->GetSlider(), SLOT(setValue(int)));
}
int SceneSwitcher::audioFindByData(const QString &source, const int &volume)
{
QRegExp rx(MakeAudioSwitchName(QStringLiteral(".*"),
QStringLiteral(".*"), source, volume));
int count = ui->audioSwitches->count();
for (int i = 0; i < count; i++) {
QListWidgetItem *item = ui->audioSwitches->item(i);
QString str = item->data(Qt::UserRole).toString();
if (rx.exactMatch(str))
return i;
}
return -1;
}
void SceneSwitcher::on_audioAdd_clicked()
{
QString sceneName = ui->audioScenes->currentText();
QString transitionName = ui->audioTransitions->currentText();
QString audioSourceName = ui->audioSources->currentText();
int vol = ui->audioVolumeThreshold->value();
if (sceneName.isEmpty() || audioSourceName.isEmpty())
return;
OBSWeakSource source = GetWeakSourceByQString(sceneName);
OBSWeakSource audioSource = GetWeakSourceByQString(audioSourceName);
OBSWeakSource transition = GetWeakTransitionByQString(transitionName);
QString text = MakeAudioSwitchName(sceneName, transitionName,
audioSourceName, vol);
QVariant v = QVariant::fromValue(text);
int idx = audioFindByData(audioSourceName, vol);
if (idx == -1) {
std::lock_guard<std::mutex> lock(switcher->m);
switcher->audioSwitches.emplace_back(
source, transition, audioSource, vol,
(sceneName == QString(previous_scene_name)),
text.toUtf8().constData());
QListWidgetItem *item =
new QListWidgetItem(text, ui->audioSwitches);
item->setData(Qt::UserRole, v);
} else {
QListWidgetItem *item = ui->audioSwitches->item(idx);
item->setText(text);
item->setData(Qt::UserRole, v);
{
std::lock_guard<std::mutex> lock(switcher->m);
for (auto &s : switcher->audioSwitches) {
if (s.audioSource == audioSource &&
s.volumeThreshold == vol) {
s.scene = source;
s.transition = transition;
s.usePreviousScene =
(sceneName ==
QString(previous_scene_name));
s.audioSwitchStr =
text.toUtf8().constData();
break;
}
}
}
}
}
void SceneSwitcher::on_audioRemove_clicked()
{ {
QListWidgetItem *item = ui->audioSwitches->currentItem(); QListWidgetItem *item = ui->audioSwitches->currentItem();
if (!item) if (!item)
return; return;
std::string text =
item->data(Qt::UserRole).toString().toUtf8().constData();
{ {
std::lock_guard<std::mutex> lock(switcher->m); std::lock_guard<std::mutex> lock(switcher->m);
int idx = ui->audioSwitches->currentRow();
auto &switches = switcher->audioSwitches; auto &switches = switcher->audioSwitches;
switches.erase(switches.begin() + idx);
for (auto it = switches.begin(); it != switches.end(); ++it) {
auto &s = *it;
if (s.audioSwitchStr == text) {
switches.erase(it);
break;
}
}
} }
delete item; delete item;
} }
void SceneSwitcher::on_audioUp_clicked() void AdvSceneSwitcher::on_audioUp_clicked()
{ {
int index = ui->audioSwitches->currentRow(); int index = ui->audioSwitches->currentRow();
if (index != -1 && index != 0) { if (!listMoveUp(ui->audioSwitches))
ui->audioSwitches->insertItem( return;
index - 1, ui->audioSwitches->takeItem(index));
ui->audioSwitches->setCurrentRow(index - 1);
std::lock_guard<std::mutex> lock(switcher->m); AudioSwitchWidget *s1 =
(AudioSwitchWidget *)ui->audioSwitches->itemWidget(
ui->audioSwitches->item(index));
AudioSwitchWidget *s2 =
(AudioSwitchWidget *)ui->audioSwitches->itemWidget(
ui->audioSwitches->item(index - 1));
AudioSwitchWidget::swapSwitchData(s1, s2);
iter_swap(switcher->audioSwitches.begin() + index, std::lock_guard<std::mutex> lock(switcher->m);
switcher->audioSwitches.begin() + index - 1);
} std::swap(switcher->audioSwitches[index],
switcher->audioSwitches[index - 1]);
} }
void SceneSwitcher::on_audioDown_clicked() void AdvSceneSwitcher::on_audioDown_clicked()
{ {
int index = ui->audioSwitches->currentRow(); int index = ui->audioSwitches->currentRow();
if (index != -1 && index != ui->audioSwitches->count() - 1) {
ui->audioSwitches->insertItem(
index + 1, ui->audioSwitches->takeItem(index));
ui->audioSwitches->setCurrentRow(index + 1);
std::lock_guard<std::mutex> lock(switcher->m); if (!listMoveDown(ui->audioSwitches))
return;
iter_swap(switcher->audioSwitches.begin() + index, AudioSwitchWidget *s1 =
switcher->audioSwitches.begin() + index + 1); (AudioSwitchWidget *)ui->audioSwitches->itemWidget(
} ui->audioSwitches->item(index));
AudioSwitchWidget *s2 =
(AudioSwitchWidget *)ui->audioSwitches->itemWidget(
ui->audioSwitches->item(index + 1));
AudioSwitchWidget::swapSwitchData(s1, s2);
std::lock_guard<std::mutex> lock(switcher->m);
std::swap(switcher->audioSwitches[index],
switcher->audioSwitches[index + 1]);
} }
void SwitcherData::checkAudioSwitch(bool &match, OBSWeakSource &scene, void SwitcherData::checkAudioSwitch(bool &match, OBSWeakSource &scene,
@@ -191,12 +84,15 @@ void SwitcherData::checkAudioSwitch(bool &match, OBSWeakSource &scene,
return; return;
for (AudioSwitch &s : audioSwitches) { for (AudioSwitch &s : audioSwitches) {
if (!s.initialized())
continue;
obs_source_t *as = obs_weak_source_get_source(s.audioSource); obs_source_t *as = obs_weak_source_get_source(s.audioSource);
bool audioActive = obs_source_active(as); bool audioActive = obs_source_active(as);
obs_source_release(as); obs_source_release(as);
// peak will have a value from -60 db to 0 db // peak will have a value from -60 db to 0 db
bool volumeThresholdreached = (s.peak + 60) * 1.7 > bool volumeThresholdreached = ((double)s.peak + 60) * 1.7 >
s.volumeThreshold; s.volumeThreshold;
if (volumeThresholdreached && audioActive) { if (volumeThresholdreached && audioActive) {
@@ -222,7 +118,8 @@ void SwitcherData::saveAudioSwitches(obs_data_t *obj)
obs_weak_source_get_source(s.transition); obs_weak_source_get_source(s.transition);
obs_source_t *audioSrouce = obs_source_t *audioSrouce =
obs_weak_source_get_source(s.audioSource); obs_weak_source_get_source(s.audioSource);
if ((s.usePreviousScene || sceneSource) && transition) { if ((s.usePreviousScene || sceneSource) && transition &&
audioSrouce) {
const char *sceneName = const char *sceneName =
obs_source_get_name(sceneSource); obs_source_get_name(sceneSource);
const char *transitionName = const char *transitionName =
@@ -268,68 +165,278 @@ void SwitcherData::loadAudioSwitches(obs_data_t *obj)
obs_data_get_string(array_obj, "audioSource"); obs_data_get_string(array_obj, "audioSource");
int vol = obs_data_get_int(array_obj, "volume"); int vol = obs_data_get_int(array_obj, "volume");
std::string audioSwitchStr =
MakeAudioSwitchName(scene, transition, audioSource, vol)
.toUtf8()
.constData();
switcher->audioSwitches.emplace_back( switcher->audioSwitches.emplace_back(
GetWeakSourceByName(scene), GetWeakSourceByName(scene),
GetWeakTransitionByName(transition), GetWeakTransitionByName(transition),
GetWeakSourceByName(audioSource), vol, GetWeakSourceByName(audioSource), vol,
(strcmp(scene, previous_scene_name) == 0), (strcmp(scene, previous_scene_name) == 0));
audioSwitchStr);
obs_data_release(array_obj); obs_data_release(array_obj);
} }
obs_data_array_release(audioArray); obs_data_array_release(audioArray);
} }
void SceneSwitcher::setupAudioTab() void AdvSceneSwitcher::setupAudioTab()
{ {
populateSceneSelection(ui->audioScenes, true);
populateTransitionSelection(ui->audioTransitions);
auto sourceEnum = [](void *data, obs_source_t *source) -> bool /* -- */
{
QComboBox *combo = reinterpret_cast<QComboBox *>(data);
uint32_t flags = obs_source_get_output_flags(source);
if ((flags & OBS_SOURCE_AUDIO) != 0) {
const char *name = obs_source_get_name(source);
combo->addItem(name);
}
return true;
};
obs_enum_sources(sourceEnum, ui->audioSources);
for (auto &s : switcher->audioSwitches) { for (auto &s : switcher->audioSwitches) {
std::string sceneName = (s.usePreviousScene) QListWidgetItem *item;
? previous_scene_name item = new QListWidgetItem(ui->audioSwitches);
: GetWeakSourceName(s.scene); ui->audioSwitches->addItem(item);
std::string transitionName = GetWeakSourceName(s.transition); AudioSwitchWidget *sw = new AudioSwitchWidget(&s);
std::string audioSourceName = GetWeakSourceName(s.audioSource); item->setSizeHint(sw->minimumSizeHint());
QString listText = MakeAudioSwitchName(sceneName.c_str(), ui->audioSwitches->setItemWidget(item, sw);
transitionName.c_str(),
audioSourceName.c_str(),
s.volumeThreshold);
QListWidgetItem *item =
new QListWidgetItem(listText, ui->audioSwitches);
item->setData(Qt::UserRole, listText);
} }
if (switcher->audioSwitches.size() == 0)
addPulse = PulseWidget(ui->audioAdd, QColor(Qt::green));
} }
static inline QString MakeAudioSwitchName(const QString &scene, void AudioSwitch::setVolumeLevel(void *data,
const QString &transition, const float magnitude[MAX_AUDIO_CHANNELS],
const QString &audioSrouce, const float peak[MAX_AUDIO_CHANNELS],
const int &volume) const float inputPeak[MAX_AUDIO_CHANNELS])
{ {
QString switchName = QStringLiteral("When volume of ") + audioSrouce + UNUSED_PARAMETER(magnitude);
QStringLiteral(" is above ") + UNUSED_PARAMETER(inputPeak);
QString::number(volume) + AudioSwitch *s = static_cast<AudioSwitch *>(data);
QStringLiteral("% switch to ") + scene +
QStringLiteral(" using ") + transition; s->peak = peak[0];
return switchName; for (int i = 1; i < MAX_AUDIO_CHANNELS; i++)
if (peak[i] > s->peak)
s->peak = peak[i];
}
void AudioSwitch::resetVolmeter()
{
obs_volmeter_remove_callback(volmeter, setVolumeLevel, this);
obs_volmeter_destroy(volmeter);
volmeter = obs_volmeter_create(OBS_FADER_LOG);
obs_volmeter_add_callback(volmeter, setVolumeLevel, this);
obs_source_t *as = obs_weak_source_get_source(audioSource);
if (!obs_volmeter_attach_source(volmeter, as)) {
const char *name = obs_source_get_name(as);
blog(LOG_WARNING, "failed to attach volmeter to source %s",
name);
}
obs_source_release(as);
}
bool AudioSwitch::initialized()
{
return SceneSwitcherEntry::initialized() && audioSource;
}
bool AudioSwitch::valid()
{
return !initialized() ||
(SceneSwitcherEntry::valid() && WeakSourceValid(audioSource));
}
AudioSwitch::AudioSwitch(OBSWeakSource scene_, OBSWeakSource transition_,
OBSWeakSource audioSource_, int volumeThreshold_,
bool usePreviousScene_)
: SceneSwitcherEntry(scene_, transition_, usePreviousScene_),
audioSource(audioSource_),
volumeThreshold(volumeThreshold_)
{
volmeter = obs_volmeter_create(OBS_FADER_LOG);
obs_volmeter_add_callback(volmeter, setVolumeLevel, this);
obs_source_t *as = obs_weak_source_get_source(audioSource);
if (!obs_volmeter_attach_source(volmeter, as)) {
const char *name = obs_source_get_name(as);
blog(LOG_WARNING, "failed to attach volmeter to source %s",
name);
}
obs_source_release(as);
}
AudioSwitch::AudioSwitch(const AudioSwitch &other)
: SceneSwitcherEntry(other.scene, other.transition,
other.usePreviousScene),
audioSource(other.audioSource),
volumeThreshold(other.volumeThreshold)
{
volmeter = obs_volmeter_create(OBS_FADER_LOG);
obs_volmeter_add_callback(volmeter, setVolumeLevel, this);
obs_source_t *as = obs_weak_source_get_source(other.audioSource);
if (!obs_volmeter_attach_source(volmeter, as)) {
const char *name = obs_source_get_name(as);
blog(LOG_WARNING, "failed to attach volmeter to source %s",
name);
}
obs_source_release(as);
}
AudioSwitch::AudioSwitch(AudioSwitch &&other)
: SceneSwitcherEntry(other.scene, other.transition,
other.usePreviousScene),
audioSource(other.audioSource),
volumeThreshold(other.volumeThreshold),
volmeter(other.volmeter)
{
other.volmeter = nullptr;
}
AudioSwitch::~AudioSwitch()
{
obs_volmeter_remove_callback(volmeter, setVolumeLevel, this);
obs_volmeter_destroy(volmeter);
}
AudioSwitch &AudioSwitch::operator=(const AudioSwitch &other)
{
AudioSwitch t(other);
swap(*this, t);
return *this = AudioSwitch(other);
}
AudioSwitch &AudioSwitch::operator=(AudioSwitch &&other) noexcept
{
if (this == &other) {
return *this;
}
swap(*this, other);
obs_volmeter_remove_callback(other.volmeter, setVolumeLevel, this);
obs_volmeter_destroy(other.volmeter);
other.volmeter = nullptr;
return *this;
}
void swap(AudioSwitch &first, AudioSwitch &second)
{
std::swap(first.scene, second.scene);
std::swap(first.transition, second.transition);
std::swap(first.usePreviousScene, second.usePreviousScene);
std::swap(first.audioSource, second.audioSource);
std::swap(first.volumeThreshold, second.volumeThreshold);
std::swap(first.peak, second.peak);
std::swap(first.volmeter, second.volmeter);
first.resetVolmeter();
second.resetVolmeter();
}
AudioSwitchWidget::AudioSwitchWidget(AudioSwitch *s) : SwitchWidget(s)
{
audioSources = new QComboBox();
audioVolumeThreshold = new QSpinBox();
obs_source_t *soruce = nullptr;
if (s)
soruce = obs_weak_source_get_source(s->audioSource);
volMeter = new VolControl(soruce);
obs_source_release(soruce);
whenLabel = new QLabel("When the volume of");
aboveLabel = new QLabel("is above");
switchLabel = new QLabel("switch to");
usingLabel = new QLabel("using");
audioVolumeThreshold->setSuffix("%");
audioVolumeThreshold->setMaximum(100);
audioVolumeThreshold->setMinimum(0);
QWidget::connect(volMeter->GetSlider(), SIGNAL(valueChanged(int)),
audioVolumeThreshold, SLOT(setValue(int)));
QWidget::connect(audioVolumeThreshold, SIGNAL(valueChanged(int)),
volMeter->GetSlider(), SLOT(setValue(int)));
QWidget::connect(audioVolumeThreshold, SIGNAL(valueChanged(int)), this,
SLOT(VolumeThresholdChanged(int)));
QWidget::connect(audioSources,
SIGNAL(currentTextChanged(const QString &)), this,
SLOT(SourceChanged(const QString &)));
AdvSceneSwitcher::populateAudioSelection(audioSources);
if (s) {
audioSources->setCurrentText(
GetWeakSourceName(s->audioSource).c_str());
audioVolumeThreshold->setValue(s->volumeThreshold);
}
setStyleSheet("* { background-color: transparent; }");
QHBoxLayout *switchLayout = new QHBoxLayout;
switchLayout->addWidget(whenLabel);
switchLayout->addWidget(audioSources);
switchLayout->addWidget(aboveLabel);
switchLayout->addWidget(audioVolumeThreshold);
switchLayout->addWidget(switchLabel);
switchLayout->addWidget(scenes);
switchLayout->addWidget(usingLabel);
switchLayout->addWidget(transitions);
switchLayout->addStretch();
QVBoxLayout *mainLayout = new QVBoxLayout;
mainLayout->addLayout(switchLayout);
mainLayout->addWidget(volMeter);
setLayout(mainLayout);
switchData = s;
loading = false;
}
AudioSwitch *AudioSwitchWidget::getSwitchData()
{
return switchData;
}
void AudioSwitchWidget::setSwitchData(AudioSwitch *s)
{
switchData = s;
}
void AudioSwitchWidget::swapSwitchData(AudioSwitchWidget *s1,
AudioSwitchWidget *s2)
{
SwitchWidget::swapSwitchData(s1, s2);
AudioSwitch *t = s1->getSwitchData();
s1->setSwitchData(s2->getSwitchData());
s2->setSwitchData(t);
}
void AudioSwitchWidget::UpdateVolmeterSource()
{
delete volMeter;
obs_source_t *soruce =
obs_weak_source_get_source(switchData->audioSource);
volMeter = new VolControl(soruce);
obs_source_release(soruce);
QLayout *layout = this->layout();
layout->addWidget(volMeter);
QWidget::connect(volMeter->GetSlider(), SIGNAL(valueChanged(int)),
audioVolumeThreshold, SLOT(setValue(int)));
QWidget::connect(audioVolumeThreshold, SIGNAL(valueChanged(int)),
volMeter->GetSlider(), SLOT(setValue(int)));
// Slider will default to 0 so set it manually once
volMeter->GetSlider()->setValue(switchData->volumeThreshold);
}
void AudioSwitchWidget::SourceChanged(const QString &text)
{
if (loading || !switchData)
return;
std::lock_guard<std::mutex> lock(switcher->m);
switchData->audioSource = GetWeakSourceByQString(text);
switchData->resetVolmeter();
UpdateVolmeterSource();
}
void AudioSwitchWidget::VolumeThresholdChanged(int vol)
{
if (loading || !switchData)
return;
std::lock_guard<std::mutex> lock(switcher->m);
switchData->volumeThreshold = vol;
} }

View File

@@ -1,148 +1,81 @@
#include "headers/advanced-scene-switcher.hpp" #include "headers/advanced-scene-switcher.hpp"
#include "headers/utility.hpp"
int SceneSwitcher::executableFindByData(const QString &exe) static QMetaObject::Connection addPulse;
void AdvSceneSwitcher::on_executableAdd_clicked()
{ {
int count = ui->executables->count(); ui->executableAdd->disconnect(addPulse);
for (int i = 0; i < count; i++) {
QListWidgetItem *item = ui->executables->item(i);
QString itemExe = item->data(Qt::UserRole).toString();
if (itemExe == exe)
return i;
}
return -1;
}
void SceneSwitcher::on_executables_currentRowChanged(int idx)
{
if (loading)
return;
if (idx == -1)
return;
QListWidgetItem *item = ui->executables->item(idx);
QString exec = item->data(Qt::UserRole).toString();
std::lock_guard<std::mutex> lock(switcher->m); std::lock_guard<std::mutex> lock(switcher->m);
for (auto &s : switcher->executableSwitches) { switcher->executableSwitches.emplace_back();
if (exec.compare(s.exe) == 0) {
QString sceneName = GetWeakSourceName(s.scene).c_str(); QListWidgetItem *item;
QString transitionName = item = new QListWidgetItem(ui->executables);
GetWeakSourceName(s.transition).c_str(); ui->executables->addItem(item);
ui->executableScenes->setCurrentText(sceneName); ExecutableSwitchWidget *sw = new ExecutableSwitchWidget(
ui->executable->setCurrentText(exec); &switcher->executableSwitches.back());
ui->executableTransitions->setCurrentText( item->setSizeHint(sw->minimumSizeHint());
transitionName); ui->executables->setItemWidget(item, sw);
ui->requiresFocusCheckBox->setChecked(s.inFocus);
break;
}
}
} }
void SceneSwitcher::on_executableUp_clicked() void AdvSceneSwitcher::on_executableRemove_clicked()
{
int index = ui->executables->currentRow();
if (index != -1 && index != 0) {
ui->executables->insertItem(index - 1,
ui->executables->takeItem(index));
ui->executables->setCurrentRow(index - 1);
std::lock_guard<std::mutex> lock(switcher->m);
iter_swap(switcher->executableSwitches.begin() + index,
switcher->executableSwitches.begin() + index - 1);
}
}
void SceneSwitcher::on_executableDown_clicked()
{
int index = ui->executables->currentRow();
if (index != -1 && index != ui->executables->count() - 1) {
ui->executables->insertItem(index + 1,
ui->executables->takeItem(index));
ui->executables->setCurrentRow(index + 1);
std::lock_guard<std::mutex> lock(switcher->m);
iter_swap(switcher->executableSwitches.begin() + index,
switcher->executableSwitches.begin() + index + 1);
}
}
void SceneSwitcher::on_executableAdd_clicked()
{
QString sceneName = ui->executableScenes->currentText();
QString exeName = ui->executable->currentText();
QString transitionName = ui->executableTransitions->currentText();
bool inFocus = ui->requiresFocusCheckBox->isChecked();
if (exeName.isEmpty() || sceneName.isEmpty())
return;
OBSWeakSource source = GetWeakSourceByQString(sceneName);
OBSWeakSource transition = GetWeakTransitionByQString(transitionName);
QVariant v = QVariant::fromValue(exeName);
QString text = MakeSwitchNameExecutable(sceneName, exeName,
transitionName, inFocus);
int idx = executableFindByData(exeName);
if (idx == -1) {
std::lock_guard<std::mutex> lock(switcher->m);
switcher->executableSwitches.emplace_back(
source, transition, exeName.toUtf8().constData(),
inFocus);
QListWidgetItem *item =
new QListWidgetItem(text, ui->executables);
item->setData(Qt::UserRole, v);
} else {
QListWidgetItem *item = ui->executables->item(idx);
item->setText(text);
{
std::lock_guard<std::mutex> lock(switcher->m);
for (auto &s : switcher->executableSwitches) {
if (s.exe == exeName) {
s.scene = source;
s.transition = transition;
s.inFocus = inFocus;
break;
}
}
}
}
}
void SceneSwitcher::on_executableRemove_clicked()
{ {
QListWidgetItem *item = ui->executables->currentItem(); QListWidgetItem *item = ui->executables->currentItem();
if (!item) if (!item)
return; return;
QString exe = item->data(Qt::UserRole).toString();
{ {
std::lock_guard<std::mutex> lock(switcher->m); std::lock_guard<std::mutex> lock(switcher->m);
int idx = ui->executables->currentRow();
auto &switches = switcher->executableSwitches; auto &switches = switcher->executableSwitches;
switches.erase(switches.begin() + idx);
for (auto it = switches.begin(); it != switches.end(); ++it) {
auto &s = *it;
if (s.exe == exe) {
switches.erase(it);
break;
}
}
} }
delete item; delete item;
} }
void AdvSceneSwitcher::on_executableUp_clicked()
{
int index = ui->executables->currentRow();
if (!listMoveUp(ui->executables))
return;
ExecutableSwitchWidget *s1 =
(ExecutableSwitchWidget *)ui->executables->itemWidget(
ui->executables->item(index));
ExecutableSwitchWidget *s2 =
(ExecutableSwitchWidget *)ui->executables->itemWidget(
ui->executables->item(index - 1));
ExecutableSwitchWidget::swapSwitchData(s1, s2);
std::lock_guard<std::mutex> lock(switcher->m);
std::swap(switcher->executableSwitches[index],
switcher->executableSwitches[index - 1]);
}
void AdvSceneSwitcher::on_executableDown_clicked()
{
int index = ui->executables->currentRow();
if (!listMoveDown(ui->executables))
return;
ExecutableSwitchWidget *s1 =
(ExecutableSwitchWidget *)ui->executables->itemWidget(
ui->executables->item(index));
ExecutableSwitchWidget *s2 =
(ExecutableSwitchWidget *)ui->executables->itemWidget(
ui->executables->item(index + 1));
ExecutableSwitchWidget::swapSwitchData(s1, s2);
std::lock_guard<std::mutex> lock(switcher->m);
std::swap(switcher->executableSwitches[index],
switcher->executableSwitches[index + 1]);
}
void SwitcherData::checkExeSwitch(bool &match, OBSWeakSource &scene, void SwitcherData::checkExeSwitch(bool &match, OBSWeakSource &scene,
OBSWeakSource &transition) OBSWeakSource &transition)
{ {
@@ -170,7 +103,9 @@ void SwitcherData::checkExeSwitch(bool &match, OBSWeakSource &scene,
// Check for match // Check for match
GetProcessList(runningProcesses); GetProcessList(runningProcesses);
for (ExecutableSceneSwitch &s : executableSwitches) { for (ExecutableSwitch &s : executableSwitches) {
if (!s.initialized())
continue;
// True if executable switch is running (direct) // True if executable switch is running (direct)
bool equals = runningProcesses.contains(s.exe); bool equals = runningProcesses.contains(s.exe);
// True if executable switch is running (regex) // True if executable switch is running (regex)
@@ -199,7 +134,7 @@ void SwitcherData::checkExeSwitch(bool &match, OBSWeakSource &scene,
void SwitcherData::saveExecutableSwitches(obs_data_t *obj) void SwitcherData::saveExecutableSwitches(obs_data_t *obj)
{ {
obs_data_array_t *executableArray = obs_data_array_create(); obs_data_array_t *executableArray = obs_data_array_create();
for (ExecutableSceneSwitch &s : switcher->executableSwitches) { for (ExecutableSwitch &s : switcher->executableSwitches) {
obs_data_t *array_obj = obs_data_create(); obs_data_t *array_obj = obs_data_create();
obs_source_t *source = obs_weak_source_get_source(s.scene); obs_source_t *source = obs_weak_source_get_source(s.scene);
@@ -217,10 +152,9 @@ void SwitcherData::saveExecutableSwitches(obs_data_t *obj)
s.exe.toUtf8()); s.exe.toUtf8());
obs_data_set_bool(array_obj, "infocus", s.inFocus); obs_data_set_bool(array_obj, "infocus", s.inFocus);
obs_data_array_push_back(executableArray, array_obj); obs_data_array_push_back(executableArray, array_obj);
obs_source_release(source);
obs_source_release(transition);
} }
obs_source_release(source);
obs_source_release(transition);
obs_data_release(array_obj); obs_data_release(array_obj);
} }
obs_data_set_array(obj, "executableSwitches", executableArray); obs_data_set_array(obj, "executableSwitches", executableArray);
@@ -253,39 +187,98 @@ void SwitcherData::loadExecutableSwitches(obs_data_t *obj)
obs_data_array_release(executableArray); obs_data_array_release(executableArray);
} }
void SceneSwitcher::setupExecutableTab() void AdvSceneSwitcher::setupExecutableTab()
{ {
populateSceneSelection(ui->executableScenes, false);
populateTransitionSelection(ui->executableTransitions);
QStringList processes;
GetProcessList(processes);
for (QString &process : processes)
ui->executable->addItem(process);
for (auto &s : switcher->executableSwitches) { for (auto &s : switcher->executableSwitches) {
std::string sceneName = GetWeakSourceName(s.scene); QListWidgetItem *item;
std::string transitionName = GetWeakSourceName(s.transition); item = new QListWidgetItem(ui->executables);
QString text = MakeSwitchNameExecutable(sceneName.c_str(), ui->executables->addItem(item);
s.exe, ExecutableSwitchWidget *sw = new ExecutableSwitchWidget(&s);
transitionName.c_str(), item->setSizeHint(sw->minimumSizeHint());
s.inFocus); ui->executables->setItemWidget(item, sw);
QListWidgetItem *item =
new QListWidgetItem(text, ui->executables);
item->setData(Qt::UserRole, s.exe);
} }
if (switcher->executableSwitches.size() == 0)
addPulse = PulseWidget(ui->executableAdd, QColor(Qt::green));
} }
static inline QString MakeSwitchNameExecutable(const QString &scene, ExecutableSwitchWidget::ExecutableSwitchWidget(ExecutableSwitch *s)
const QString &value, : SwitchWidget(s, false)
const QString &transition,
bool inFocus)
{ {
if (!inFocus) processes = new QComboBox();
return QStringLiteral("[") + scene + QStringLiteral(", ") + requiresFocus = new QCheckBox("only if focused");
transition + QStringLiteral("]: ") + value;
return QStringLiteral("[") + scene + QStringLiteral(", ") + transition + whenLabel = new QLabel("When");
QStringLiteral("]: ") + value + switchLabel = new QLabel("is running switch to");
QStringLiteral(" (only if focused)"); usingLabel = new QLabel("using");
QWidget::connect(processes, SIGNAL(currentTextChanged(const QString &)),
this, SLOT(ProcessChanged(const QString &)));
QWidget::connect(requiresFocus, SIGNAL(stateChanged(int)), this,
SLOT(FocusChanged(int)));
AdvSceneSwitcher::populateProcessSelection(processes);
processes->setEditable(true);
processes->setMaxVisibleItems(20);
if (s) {
processes->setCurrentText(s->exe);
requiresFocus->setChecked(s->inFocus);
}
setStyleSheet("* { background-color: transparent; }");
QHBoxLayout *mainLayout = new QHBoxLayout;
mainLayout->addWidget(whenLabel);
mainLayout->addWidget(processes);
mainLayout->addWidget(switchLabel);
mainLayout->addWidget(scenes);
mainLayout->addWidget(usingLabel);
mainLayout->addWidget(transitions);
mainLayout->addWidget(requiresFocus);
mainLayout->addStretch();
setLayout(mainLayout);
switchData = s;
loading = false;
}
ExecutableSwitch *ExecutableSwitchWidget::getSwitchData()
{
return switchData;
}
void ExecutableSwitchWidget::setSwitchData(ExecutableSwitch *s)
{
switchData = s;
}
void ExecutableSwitchWidget::swapSwitchData(ExecutableSwitchWidget *s1,
ExecutableSwitchWidget *s2)
{
SwitchWidget::swapSwitchData(s1, s2);
ExecutableSwitch *t = s1->getSwitchData();
s1->setSwitchData(s2->getSwitchData());
s2->setSwitchData(t);
}
void ExecutableSwitchWidget::ProcessChanged(const QString &text)
{
if (loading || !switchData)
return;
std::lock_guard<std::mutex> lock(switcher->m);
switchData->exe = text;
}
void ExecutableSwitchWidget::FocusChanged(int state)
{
if (loading || !switchData)
return;
std::lock_guard<std::mutex> lock(switcher->m);
switchData->inFocus = state;
} }

View File

@@ -5,11 +5,12 @@
#include "headers/advanced-scene-switcher.hpp" #include "headers/advanced-scene-switcher.hpp"
#include "headers/curl-helper.hpp" #include "headers/curl-helper.hpp"
#include "headers/utility.hpp"
#define LOCAL_FILE_IDX 0 #define LOCAL_FILE_IDX 0
#define REMOTE_FILE_IDX 1 #define REMOTE_FILE_IDX 1
void SceneSwitcher::on_browseButton_clicked() void AdvSceneSwitcher::on_browseButton_clicked()
{ {
QString path = QFileDialog::getOpenFileName( QString path = QFileDialog::getOpenFileName(
this, tr("Select a file to write to ..."), QDir::currentPath(), this, tr("Select a file to write to ..."), QDir::currentPath(),
@@ -18,7 +19,7 @@ void SceneSwitcher::on_browseButton_clicked()
ui->writePathLineEdit->setText(path); ui->writePathLineEdit->setText(path);
} }
void SceneSwitcher::on_readFileCheckBox_stateChanged(int state) void AdvSceneSwitcher::on_readFileCheckBox_stateChanged(int state)
{ {
if (loading) if (loading)
return; return;
@@ -35,7 +36,7 @@ void SceneSwitcher::on_readFileCheckBox_stateChanged(int state)
} }
} }
void SceneSwitcher::on_readPathLineEdit_textChanged(const QString &text) void AdvSceneSwitcher::on_readPathLineEdit_textChanged(const QString &text)
{ {
if (loading) if (loading)
return; return;
@@ -49,7 +50,7 @@ void SceneSwitcher::on_readPathLineEdit_textChanged(const QString &text)
switcher->fileIO.readPath = text.toUtf8().constData(); switcher->fileIO.readPath = text.toUtf8().constData();
} }
void SceneSwitcher::on_writePathLineEdit_textChanged(const QString &text) void AdvSceneSwitcher::on_writePathLineEdit_textChanged(const QString &text)
{ {
if (loading) if (loading)
return; return;
@@ -63,7 +64,7 @@ void SceneSwitcher::on_writePathLineEdit_textChanged(const QString &text)
switcher->fileIO.writePath = text.toUtf8().constData(); switcher->fileIO.writePath = text.toUtf8().constData();
} }
void SceneSwitcher::on_browseButton_2_clicked() void AdvSceneSwitcher::on_browseButton_2_clicked()
{ {
QString path = QFileDialog::getOpenFileName( QString path = QFileDialog::getOpenFileName(
this, tr("Select a file to read from ..."), QDir::currentPath(), this, tr("Select a file to read from ..."), QDir::currentPath(),
@@ -223,7 +224,7 @@ void SwitcherData::checkFileContent(bool &match, OBSWeakSource &scene,
} }
} }
void SceneSwitcher::on_browseButton_3_clicked() void AdvSceneSwitcher::on_browseButton_3_clicked()
{ {
QString path = QFileDialog::getOpenFileName( QString path = QFileDialog::getOpenFileName(
this, tr("Select a file to read from ..."), QDir::currentPath(), this, tr("Select a file to read from ..."), QDir::currentPath(),
@@ -232,7 +233,7 @@ void SceneSwitcher::on_browseButton_3_clicked()
ui->filePathLineEdit->setText(path); ui->filePathLineEdit->setText(path);
} }
void SceneSwitcher::on_fileType_currentIndexChanged(int idx) void AdvSceneSwitcher::on_fileType_currentIndexChanged(int idx)
{ {
if (idx == -1) if (idx == -1)
return; return;
@@ -248,7 +249,7 @@ void SceneSwitcher::on_fileType_currentIndexChanged(int idx)
} }
} }
void SceneSwitcher::on_fileAdd_clicked() void AdvSceneSwitcher::on_fileAdd_clicked()
{ {
QString sceneName = ui->fileScenes->currentText(); QString sceneName = ui->fileScenes->currentText();
QString transitionName = ui->fileTransitions->currentText(); QString transitionName = ui->fileTransitions->currentText();
@@ -280,7 +281,7 @@ void SceneSwitcher::on_fileAdd_clicked()
useRegex, useTime); useRegex, useTime);
} }
void SceneSwitcher::on_fileRemove_clicked() void AdvSceneSwitcher::on_fileRemove_clicked()
{ {
QListWidgetItem *item = ui->fileScenesList->currentItem(); QListWidgetItem *item = ui->fileScenesList->currentItem();
if (!item) if (!item)
@@ -299,7 +300,7 @@ void SceneSwitcher::on_fileRemove_clicked()
qDeleteAll(ui->fileScenesList->selectedItems()); qDeleteAll(ui->fileScenesList->selectedItems());
} }
void SceneSwitcher::on_fileScenesList_currentRowChanged(int idx) void AdvSceneSwitcher::on_fileScenesList_currentRowChanged(int idx)
{ {
if (loading) if (loading)
return; return;
@@ -327,7 +328,7 @@ void SceneSwitcher::on_fileScenesList_currentRowChanged(int idx)
ui->fileContentTimeCheckBox->setChecked(s.useTime); ui->fileContentTimeCheckBox->setChecked(s.useTime);
} }
void SceneSwitcher::on_fileUp_clicked() void AdvSceneSwitcher::on_fileUp_clicked()
{ {
int index = ui->fileScenesList->currentRow(); int index = ui->fileScenesList->currentRow();
if (index != -1 && index != 0) { if (index != -1 && index != 0) {
@@ -342,7 +343,7 @@ void SceneSwitcher::on_fileUp_clicked()
} }
} }
void SceneSwitcher::on_fileDown_clicked() void AdvSceneSwitcher::on_fileDown_clicked()
{ {
int index = ui->fileScenesList->currentRow(); int index = ui->fileScenesList->currentRow();
if (index != -1 && index != ui->fileScenesList->count() - 1) { if (index != -1 && index != ui->fileScenesList->count() - 1) {
@@ -380,10 +381,9 @@ void SwitcherData::saveFileSwitches(obs_data_t *obj)
obs_data_set_bool(array_obj, "useRegex", s.useRegex); obs_data_set_bool(array_obj, "useRegex", s.useRegex);
obs_data_set_bool(array_obj, "useTime", s.useTime); obs_data_set_bool(array_obj, "useTime", s.useTime);
obs_data_array_push_back(fileArray, array_obj); obs_data_array_push_back(fileArray, array_obj);
obs_source_release(source);
obs_source_release(transition);
} }
obs_source_release(source);
obs_source_release(transition);
obs_data_release(array_obj); obs_data_release(array_obj);
} }
obs_data_set_array(obj, "fileSwitches", fileArray); obs_data_set_array(obj, "fileSwitches", fileArray);
@@ -431,7 +431,7 @@ void SwitcherData::loadFileSwitches(obs_data_t *obj)
switcher->fileIO.writePath = obs_data_get_string(obj, "writePath"); switcher->fileIO.writePath = obs_data_get_string(obj, "writePath");
} }
void SceneSwitcher::setupFileTab() void AdvSceneSwitcher::setupFileTab()
{ {
populateSceneSelection(ui->fileScenes, false); populateSceneSelection(ui->fileScenes, false);
populateTransitionSelection(ui->fileTransitions); populateTransitionSelection(ui->fileTransitions);

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

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

View File

@@ -1,6 +1,7 @@
#include <regex> #include <regex>
#include "headers/advanced-scene-switcher.hpp" #include "headers/advanced-scene-switcher.hpp"
#include "headers/utility.hpp"
void SwitcherData::checkIdleSwitch(bool &match, OBSWeakSource &scene, void SwitcherData::checkIdleSwitch(bool &match, OBSWeakSource &scene,
OBSWeakSource &transition) OBSWeakSource &transition)
@@ -50,7 +51,7 @@ void SwitcherData::checkIdleSwitch(bool &match, OBSWeakSource &scene,
idleData.alreadySwitched = false; idleData.alreadySwitched = false;
} }
void SceneSwitcher::on_idleCheckBox_stateChanged(int state) void AdvSceneSwitcher::on_idleCheckBox_stateChanged(int state)
{ {
if (loading) if (loading)
return; return;
@@ -74,13 +75,13 @@ void SceneSwitcher::on_idleCheckBox_stateChanged(int state)
} }
} }
void SceneSwitcher::UpdateIdleDataTransition(const QString &name) void AdvSceneSwitcher::UpdateIdleDataTransition(const QString &name)
{ {
obs_weak_source_t *transition = GetWeakTransitionByQString(name); obs_weak_source_t *transition = GetWeakTransitionByQString(name);
switcher->idleData.transition = transition; switcher->idleData.transition = transition;
} }
void SceneSwitcher::UpdateIdleDataScene(const QString &name) void AdvSceneSwitcher::UpdateIdleDataScene(const QString &name)
{ {
switcher->idleData.usePreviousScene = (name == previous_scene_name); switcher->idleData.usePreviousScene = (name == previous_scene_name);
obs_source_t *scene = obs_get_source_by_name(name.toUtf8().constData()); obs_source_t *scene = obs_get_source_by_name(name.toUtf8().constData());
@@ -92,7 +93,7 @@ void SceneSwitcher::UpdateIdleDataScene(const QString &name)
obs_source_release(scene); obs_source_release(scene);
} }
void SceneSwitcher::on_idleTransitions_currentTextChanged(const QString &text) void AdvSceneSwitcher::on_idleTransitions_currentTextChanged(const QString &text)
{ {
if (loading) if (loading)
return; return;
@@ -101,7 +102,7 @@ void SceneSwitcher::on_idleTransitions_currentTextChanged(const QString &text)
UpdateIdleDataTransition(text); UpdateIdleDataTransition(text);
} }
void SceneSwitcher::on_idleScenes_currentTextChanged(const QString &text) void AdvSceneSwitcher::on_idleScenes_currentTextChanged(const QString &text)
{ {
if (loading) if (loading)
return; return;
@@ -110,7 +111,7 @@ void SceneSwitcher::on_idleScenes_currentTextChanged(const QString &text)
UpdateIdleDataScene(text); UpdateIdleDataScene(text);
} }
void SceneSwitcher::on_idleSpinBox_valueChanged(int i) void AdvSceneSwitcher::on_idleSpinBox_valueChanged(int i)
{ {
if (loading) if (loading)
return; return;
@@ -118,7 +119,7 @@ void SceneSwitcher::on_idleSpinBox_valueChanged(int i)
switcher->idleData.time = i; switcher->idleData.time = i;
} }
void SceneSwitcher::on_ignoreIdleWindows_currentRowChanged(int idx) void AdvSceneSwitcher::on_ignoreIdleWindows_currentRowChanged(int idx)
{ {
if (loading) if (loading)
return; return;
@@ -138,7 +139,7 @@ void SceneSwitcher::on_ignoreIdleWindows_currentRowChanged(int idx)
} }
} }
void SceneSwitcher::on_ignoreIdleAdd_clicked() void AdvSceneSwitcher::on_ignoreIdleAdd_clicked()
{ {
QString windowName = ui->ignoreIdleWindowsWindows->currentText(); QString windowName = ui->ignoreIdleWindowsWindows->currentText();
@@ -162,7 +163,7 @@ void SceneSwitcher::on_ignoreIdleAdd_clicked()
} }
} }
void SceneSwitcher::on_ignoreIdleRemove_clicked() void AdvSceneSwitcher::on_ignoreIdleRemove_clicked()
{ {
QListWidgetItem *item = ui->ignoreIdleWindows->currentItem(); QListWidgetItem *item = ui->ignoreIdleWindows->currentItem();
if (!item) if (!item)
@@ -187,7 +188,7 @@ void SceneSwitcher::on_ignoreIdleRemove_clicked()
delete item; delete item;
} }
int SceneSwitcher::IgnoreIdleWindowsFindByData(const QString &window) int AdvSceneSwitcher::IgnoreIdleWindowsFindByData(const QString &window)
{ {
int count = ui->ignoreIdleWindows->count(); int count = ui->ignoreIdleWindows->count();
int idx = -1; int idx = -1;
@@ -264,7 +265,7 @@ void SwitcherData::loadIdleSwitches(obs_data_t *obj)
(idleSceneName == previous_scene_name); (idleSceneName == previous_scene_name);
} }
void SceneSwitcher::setupIdleTab() void AdvSceneSwitcher::setupIdleTab()
{ {
populateSceneSelection(ui->idleScenes, true); populateSceneSelection(ui->idleScenes, true);
populateTransitionSelection(ui->idleTransitions); populateTransitionSelection(ui->idleTransitions);

View File

@@ -1,163 +1,178 @@
#include "headers/advanced-scene-switcher.hpp" #include "headers/advanced-scene-switcher.hpp"
#include "headers/utility.hpp"
void SceneSwitcher::on_mediaSwitches_currentRowChanged(int idx) static QMetaObject::Connection addPulse;
constexpr auto media_played_to_end_idx = 8;
constexpr auto media_any_idx = 9;
void AdvSceneSwitcher::on_mediaAdd_clicked()
{ {
if (loading) ui->mediaAdd->disconnect(addPulse);
return;
if (idx == -1)
return;
QListWidgetItem *item = ui->mediaSwitches->item(idx);
QString mediaSceneStr = item->data(Qt::UserRole).toString();
std::lock_guard<std::mutex> lock(switcher->m); std::lock_guard<std::mutex> lock(switcher->m);
for (auto &s : switcher->mediaSwitches) { switcher->mediaSwitches.emplace_back();
if (mediaSceneStr.compare(s.mediaSwitchStr.c_str()) == 0) {
QString sceneName = GetWeakSourceName(s.scene).c_str(); QListWidgetItem *item;
QString sourceName = item = new QListWidgetItem(ui->mediaSwitches);
GetWeakSourceName(s.source).c_str(); ui->mediaSwitches->addItem(item);
QString transitionName = MediaSwitchWidget *sw =
GetWeakSourceName(s.transition).c_str(); new MediaSwitchWidget(&switcher->mediaSwitches.back());
ui->mediaScenes->setCurrentText(sceneName); item->setSizeHint(sw->minimumSizeHint());
ui->mediaSources->setCurrentText(sourceName); ui->mediaSwitches->setItemWidget(item, sw);
ui->mediaTransitions->setCurrentText(transitionName);
ui->mediaStates->setCurrentIndex(s.state);
ui->mediaTimeRestrictions->setCurrentIndex(
s.restriction);
ui->mediaTime->setValue(s.time);
break;
}
}
} }
void SceneSwitcher::on_mediaAdd_clicked() void AdvSceneSwitcher::on_mediaRemove_clicked()
{
QString sourceName = ui->mediaSources->currentText();
QString sceneName = ui->mediaScenes->currentText();
QString transitionName = ui->mediaTransitions->currentText();
obs_media_state state =
(obs_media_state)ui->mediaStates->currentIndex();
time_restriction restriction =
(time_restriction)ui->mediaTimeRestrictions->currentIndex();
uint64_t time = ui->mediaTime->value();
if (sceneName.isEmpty() || transitionName.isEmpty() ||
sourceName.isEmpty())
return;
OBSWeakSource source = GetWeakSourceByQString(sourceName);
OBSWeakSource scene = GetWeakSourceByQString(sceneName);
OBSWeakSource transition = GetWeakTransitionByQString(transitionName);
QString switchText = MakeMediaSwitchName(sourceName, sceneName,
transitionName, state,
restriction, time);
QVariant v = QVariant::fromValue(switchText);
QListWidgetItem *item =
new QListWidgetItem(switchText, ui->mediaSwitches);
item->setData(Qt::UserRole, v);
std::lock_guard<std::mutex> lock(switcher->m);
switcher->mediaSwitches.emplace_back(
scene, source, transition, state, restriction, time,
(sceneName == QString(previous_scene_name)),
switchText.toUtf8().constData());
}
void SceneSwitcher::on_mediaRemove_clicked()
{ {
QListWidgetItem *item = ui->mediaSwitches->currentItem(); QListWidgetItem *item = ui->mediaSwitches->currentItem();
if (!item) if (!item)
return; return;
std::string mediaStr =
item->data(Qt::UserRole).toString().toUtf8().constData();
{ {
std::lock_guard<std::mutex> lock(switcher->m); std::lock_guard<std::mutex> lock(switcher->m);
int idx = ui->mediaSwitches->currentRow();
auto &switches = switcher->mediaSwitches; auto &switches = switcher->mediaSwitches;
switches.erase(switches.begin() + idx);
for (auto it = switches.begin(); it != switches.end(); ++it) {
auto &s = *it;
if (s.mediaSwitchStr == mediaStr) {
switches.erase(it);
break;
}
}
} }
delete item; delete item;
} }
void SceneSwitcher::on_mediaUp_clicked() void AdvSceneSwitcher::on_mediaUp_clicked()
{ {
int index = ui->mediaSwitches->currentRow(); int index = ui->mediaSwitches->currentRow();
if (index != -1 && index != 0) { if (!listMoveUp(ui->mediaSwitches))
ui->mediaSwitches->insertItem(
index - 1, ui->mediaSwitches->takeItem(index));
ui->mediaSwitches->setCurrentRow(index - 1);
std::lock_guard<std::mutex> lock(switcher->m);
iter_swap(switcher->mediaSwitches.begin() + index,
switcher->mediaSwitches.begin() + index - 1);
}
}
void SceneSwitcher::on_mediaDown_clicked()
{
int index = ui->mediaSwitches->currentRow();
if (index != -1 && index != ui->mediaSwitches->count() - 1) {
ui->mediaSwitches->insertItem(
index + 1, ui->mediaSwitches->takeItem(index));
ui->mediaSwitches->setCurrentRow(index + 1);
std::lock_guard<std::mutex> lock(switcher->m);
iter_swap(switcher->mediaSwitches.begin() + index,
switcher->mediaSwitches.begin() + index + 1);
}
}
void SceneSwitcher::on_mediaTimeRestrictions_currentIndexChanged(int idx)
{
if (idx == -1)
return; return;
if ((time_restriction)ui->mediaTimeRestrictions->currentIndex() == MediaSwitchWidget *s1 =
TIME_RESTRICTION_NONE) { (MediaSwitchWidget *)ui->mediaSwitches->itemWidget(
ui->mediaTime->setDisabled(true); ui->mediaSwitches->item(index));
} else { MediaSwitchWidget *s2 =
ui->mediaTime->setDisabled(false); (MediaSwitchWidget *)ui->mediaSwitches->itemWidget(
} ui->mediaSwitches->item(index - 1));
MediaSwitchWidget::swapSwitchData(s1, s2);
std::lock_guard<std::mutex> lock(switcher->m);
std::swap(switcher->mediaSwitches[index],
switcher->mediaSwitches[index - 1]);
}
void AdvSceneSwitcher::on_mediaDown_clicked()
{
int index = ui->mediaSwitches->currentRow();
if (!listMoveDown(ui->mediaSwitches))
return;
MediaSwitchWidget *s1 =
(MediaSwitchWidget *)ui->mediaSwitches->itemWidget(
ui->mediaSwitches->item(index));
MediaSwitchWidget *s2 =
(MediaSwitchWidget *)ui->mediaSwitches->itemWidget(
ui->mediaSwitches->item(index + 1));
MediaSwitchWidget::swapSwitchData(s1, s2);
std::lock_guard<std::mutex> lock(switcher->m);
std::swap(switcher->mediaSwitches[index],
switcher->mediaSwitches[index + 1]);
} }
void SwitcherData::checkMediaSwitch(bool &match, OBSWeakSource &scene, void SwitcherData::checkMediaSwitch(bool &match, OBSWeakSource &scene,
OBSWeakSource &transition) OBSWeakSource &transition)
{ {
for (MediaSwitch &mediaSwitch : mediaSwitches) { for (MediaSwitch &mediaSwitch : mediaSwitches) {
if (!mediaSwitch.initialized())
continue;
obs_source_t *source = obs_source_t *source =
obs_weak_source_get_source(mediaSwitch.source); obs_weak_source_get_source(mediaSwitch.source);
auto duration = obs_source_media_get_duration(source); auto duration = obs_source_media_get_duration(source);
auto time = obs_source_media_get_time(source); auto time = obs_source_media_get_time(source);
obs_media_state state = obs_source_media_get_state(source); obs_media_state state = obs_source_media_get_state(source);
bool matched = obs_source_release(source);
state == mediaSwitch.state &&
(mediaSwitch.restriction == TIME_RESTRICTION_NONE || bool matchedStopped = mediaSwitch.state ==
(mediaSwitch.restriction == TIME_RESTRICTION_LONGER && OBS_MEDIA_STATE_STOPPED &&
time > mediaSwitch.time) || mediaSwitch.stopped;
(mediaSwitch.restriction == TIME_RESTRICTION_SHORTER &&
time < mediaSwitch.time) || // The signal for the state ended is intentionally not used here
(mediaSwitch.restriction == // so matchedEnded can be used to specify the end of a VLC playlist
TIME_RESTRICTION_REMAINING_SHORTER && // by two consequtive matches of OBS_MEDIA_STATE_ENDED
duration > time && //
duration - time < mediaSwitch.time) || // This was done to reduce the likelyhood of interpreting a single
(mediaSwitch.restriction == // OBS_MEDIA_STATE_ENDED caught by obs_source_media_get_state()
TIME_RESTRICTION_REMAINING_LONGER && // as the end of the playlist of the VLC source, while actually being
duration > time && // in the middle of switching to the next item of the playlist
duration - time > mediaSwitch.time)); //
// If there is a separate obs_media_sate in the future for the
// "end of playlist reached" signal the line below can be used
// and an additional check for this new singal can be introduced
//
// bool matchedEnded = mediaSwitch.state == OBS_MEDIA_STATE_ENDED &&
// mediaSwitch.ended;
bool ended = false;
if (state == OBS_MEDIA_STATE_ENDED) {
ended = mediaSwitch.previousStateEnded;
mediaSwitch.previousStateEnded = true;
} else {
mediaSwitch.previousStateEnded = false;
}
bool matchedEnded =
ended && (mediaSwitch.state == OBS_MEDIA_STATE_ENDED);
// match if playedToEnd was true in last interval
// and playback is currently ended
bool matchedPlayedToEnd = mediaSwitch.state ==
media_played_to_end_idx &&
mediaSwitch.playedToEnd && ended;
// interval * 2 to make sure not to miss any state changes
// which happened during check of the conditions
mediaSwitch.playedToEnd = mediaSwitch.playedToEnd ||
(duration - time <= interval * 2);
// reset
if (ended)
mediaSwitch.playedToEnd = false;
// reset for next check
mediaSwitch.stopped = false;
mediaSwitch.ended = false;
bool matchedState = ((state == mediaSwitch.state) ||
mediaSwitch.anyState) ||
matchedStopped || matchedEnded ||
matchedPlayedToEnd;
bool matchedTimeNone =
(mediaSwitch.restriction == TIME_RESTRICTION_NONE);
bool matchedTimeLonger =
(mediaSwitch.restriction == TIME_RESTRICTION_LONGER) &&
(time > mediaSwitch.time);
bool matchedTimeShorter =
(mediaSwitch.restriction == TIME_RESTRICTION_SHORTER) &&
(time < mediaSwitch.time);
bool matchedTimeRemainLonger =
(mediaSwitch.restriction ==
TIME_RESTRICTION_REMAINING_LONGER) &&
(duration > time && duration - time > mediaSwitch.time);
bool matchedTimeRemainShorter =
(mediaSwitch.restriction ==
TIME_RESTRICTION_REMAINING_SHORTER) &&
(duration > time && duration - time < mediaSwitch.time);
bool matchedTime = matchedTimeNone || matchedTimeLonger ||
matchedTimeShorter ||
matchedTimeRemainLonger ||
matchedTimeRemainShorter;
bool matched = matchedState && matchedTime;
if (matched && !mediaSwitch.matched) { if (matched && !mediaSwitch.matched) {
match = true; match = true;
scene = (mediaSwitch.usePreviousScene) scene = (mediaSwitch.usePreviousScene)
@@ -168,8 +183,11 @@ void SwitcherData::checkMediaSwitch(bool &match, OBSWeakSource &scene,
if (verbose) if (verbose)
mediaSwitch.logMatch(); mediaSwitch.logMatch();
} }
mediaSwitch.matched = matched; mediaSwitch.matched = matched;
obs_source_release(source);
if (match)
break;
} }
} }
@@ -233,113 +251,312 @@ void SwitcherData::loadMediaSwitches(obs_data_t *obj)
"restriction"); "restriction");
uint64_t time = obs_data_get_int(array_obj, "time"); uint64_t time = obs_data_get_int(array_obj, "time");
std::string mediaStr = MakeMediaSwitchName(source, scene,
transition, state,
restriction, time)
.toUtf8()
.constData();
switcher->mediaSwitches.emplace_back( switcher->mediaSwitches.emplace_back(
GetWeakSourceByName(scene), GetWeakSourceByName(source), GetWeakSourceByName(scene), GetWeakSourceByName(source),
GetWeakTransitionByName(transition), state, restriction, GetWeakTransitionByName(transition), state, restriction,
time, (strcmp(scene, previous_scene_name) == 0), time, (strcmp(scene, previous_scene_name) == 0));
mediaStr);
obs_data_release(array_obj); obs_data_release(array_obj);
} }
obs_data_array_release(mediaArray); obs_data_array_release(mediaArray);
} }
void SceneSwitcher::setupMediaTab() void AdvSceneSwitcher::setupMediaTab()
{ {
populateSceneSelection(ui->mediaScenes, true);
populateTransitionSelection(ui->mediaTransitions);
auto sourceEnum = [](void *data, obs_source_t *source) -> bool /* -- */
{
QComboBox *combo = reinterpret_cast<QComboBox *>(data);
std::string sourceId = obs_source_get_id(source);
if (sourceId.compare("ffmpeg_source") == 0 ||
sourceId.compare("vlc_source") == 0) {
const char *name = obs_source_get_name(source);
combo->addItem(name);
}
return true;
};
obs_enum_sources(sourceEnum, ui->mediaSources);
ui->mediaStates->addItem("None");
ui->mediaStates->addItem("Playing");
ui->mediaStates->addItem("Opening");
ui->mediaStates->addItem("Buffering");
ui->mediaStates->addItem("Paused");
ui->mediaStates->addItem("Stopped");
ui->mediaStates->addItem("Ended");
ui->mediaStates->addItem("Error");
ui->mediaTimeRestrictions->addItem("None");
ui->mediaTimeRestrictions->addItem("Time shorter");
ui->mediaTimeRestrictions->addItem("Time longer");
ui->mediaTimeRestrictions->addItem("Time remaining shorter");
ui->mediaTimeRestrictions->addItem("Time remaining longer");
for (auto &s : switcher->mediaSwitches) { for (auto &s : switcher->mediaSwitches) {
std::string sourceName = GetWeakSourceName(s.source); QListWidgetItem *item;
std::string sceneName = (s.usePreviousScene) item = new QListWidgetItem(ui->mediaSwitches);
? previous_scene_name ui->mediaSwitches->addItem(item);
: GetWeakSourceName(s.scene); MediaSwitchWidget *sw = new MediaSwitchWidget(&s);
std::string transitionName = GetWeakSourceName(s.transition); item->setSizeHint(sw->minimumSizeHint());
QString listText = MakeMediaSwitchName( ui->mediaSwitches->setItemWidget(item, sw);
sourceName.c_str(), sceneName.c_str(),
transitionName.c_str(), s.state, s.restriction, s.time);
QListWidgetItem *item =
new QListWidgetItem(listText, ui->mediaSwitches);
item->setData(Qt::UserRole, listText);
} }
if (switcher->mediaSwitches.size() == 0)
addPulse = PulseWidget(ui->mediaAdd, QColor(Qt::green));
} }
static inline QString bool MediaSwitch::initialized()
MakeMediaSwitchName(const QString &source, const QString &scene,
const QString &transition, obs_media_state state,
time_restriction restriction, uint64_t time)
{ {
QString switchName = QStringLiteral("Switch to ") + scene + return SceneSwitcherEntry::initialized() && source;
QStringLiteral(" using ") + transition + }
QStringLiteral(" if ") + source +
QStringLiteral(" state is "); bool MediaSwitch::valid()
if (state == OBS_MEDIA_STATE_NONE) { {
switchName += QStringLiteral("none"); return !initialized() ||
} else if (state == OBS_MEDIA_STATE_PLAYING) { (SceneSwitcherEntry::valid() && WeakSourceValid(source));
switchName += QStringLiteral("playing"); }
} else if (state == OBS_MEDIA_STATE_OPENING) {
switchName += QStringLiteral("opening"); void MediaSwitch::clearSignalHandler()
} else if (state == OBS_MEDIA_STATE_BUFFERING) { {
switchName += QStringLiteral("buffering"); obs_source_t *mediasource = obs_weak_source_get_source(source);
} else if (state == OBS_MEDIA_STATE_PAUSED) { signal_handler_t *sh = obs_source_get_signal_handler(mediasource);
switchName += QStringLiteral("paused"); signal_handler_disconnect(sh, "media_stopped", MediaStopped, this);
} else if (state == OBS_MEDIA_STATE_STOPPED) { signal_handler_disconnect(sh, "media_ended", MediaEnded, this);
switchName += QStringLiteral("stopped"); obs_source_release(mediasource);
} else if (state == OBS_MEDIA_STATE_ENDED) { }
switchName += QStringLiteral("ended");
} else if (state == OBS_MEDIA_STATE_ERROR) { void MediaSwitch::resetSignalHandler()
switchName += QStringLiteral("error"); {
} obs_source_t *mediasource = obs_weak_source_get_source(source);
if (restriction == TIME_RESTRICTION_SHORTER) { signal_handler_t *sh = obs_source_get_signal_handler(mediasource);
switchName += QStringLiteral(" and time shorter than "); signal_handler_disconnect(sh, "media_stopped", MediaStopped, this);
} else if (restriction == TIME_RESTRICTION_LONGER) { signal_handler_disconnect(sh, "media_ended", MediaEnded, this);
switchName += QStringLiteral(" and time longer than "); signal_handler_connect(sh, "media_stopped", MediaStopped, this);
} else if (restriction == TIME_RESTRICTION_REMAINING_SHORTER) { signal_handler_connect(sh, "media_ended", MediaEnded, this);
switchName += obs_source_release(mediasource);
QStringLiteral(" and time remaining shorter than "); }
} else if (restriction == TIME_RESTRICTION_REMAINING_LONGER) {
switchName += void MediaSwitch::MediaStopped(void *data, calldata_t *)
QStringLiteral(" and time remaining longer than "); {
} MediaSwitch *media = static_cast<MediaSwitch *>(data);
if (restriction != TIME_RESTRICTION_NONE) { media->stopped = true;
switchName += std::to_string(time).c_str(); }
switchName += QStringLiteral(" ms");
} void MediaSwitch::MediaEnded(void *data, calldata_t *)
return switchName; {
MediaSwitch *media = static_cast<MediaSwitch *>(data);
media->ended = true;
}
inline MediaSwitch::MediaSwitch(OBSWeakSource scene_, OBSWeakSource source_,
OBSWeakSource transition_,
obs_media_state state_,
time_restriction restriction_, uint64_t time_,
bool usePreviousScene_)
: SceneSwitcherEntry(scene_, transition_, usePreviousScene_),
source(source_),
state(state_),
restriction(restriction_),
time(time_)
{
anyState = state == media_any_idx;
obs_source_t *mediasource = obs_weak_source_get_source(source);
signal_handler_t *sh = obs_source_get_signal_handler(mediasource);
signal_handler_connect(sh, "media_stopped", MediaStopped, this);
signal_handler_connect(sh, "media_ended", MediaEnded, this);
obs_source_release(mediasource);
}
MediaSwitch::MediaSwitch(const MediaSwitch &other)
: SceneSwitcherEntry(other.scene, other.transition,
other.usePreviousScene),
source(other.source),
state(other.state),
restriction(other.restriction),
time(other.time)
{
anyState = state == media_any_idx;
obs_source_t *mediasource = obs_weak_source_get_source(source);
signal_handler_t *sh = obs_source_get_signal_handler(mediasource);
signal_handler_connect(sh, "media_stopped", MediaStopped, this);
signal_handler_connect(sh, "media_ended", MediaEnded, this);
obs_source_release(mediasource);
}
MediaSwitch::MediaSwitch(MediaSwitch &&other)
: SceneSwitcherEntry(other.scene, other.transition,
other.usePreviousScene),
source(other.source),
state(other.state),
anyState(other.anyState),
restriction(other.restriction),
time(other.time)
{
}
MediaSwitch::~MediaSwitch()
{
obs_source_t *mediasource = obs_weak_source_get_source(source);
signal_handler_t *sh = obs_source_get_signal_handler(mediasource);
signal_handler_disconnect(sh, "media_stopped", MediaStopped, this);
signal_handler_disconnect(sh, "media_ended", MediaEnded, this);
obs_source_release(mediasource);
}
MediaSwitch &MediaSwitch::operator=(const MediaSwitch &other)
{
MediaSwitch t(other);
swap(*this, t);
return *this = MediaSwitch(other);
}
MediaSwitch &MediaSwitch::operator=(MediaSwitch &&other) noexcept
{
if (this == &other) {
return *this;
}
swap(*this, other);
obs_source_t *mediasource = obs_weak_source_get_source(other.source);
signal_handler_t *sh = obs_source_get_signal_handler(mediasource);
signal_handler_disconnect(sh, "media_stopped", MediaStopped, &other);
signal_handler_disconnect(sh, "media_ended", MediaEnded, &other);
obs_source_release(mediasource);
return *this;
}
void swap(MediaSwitch &first, MediaSwitch &second)
{
std::swap(first.scene, second.scene);
std::swap(first.transition, second.transition);
std::swap(first.usePreviousScene, second.usePreviousScene);
std::swap(first.source, second.source);
std::swap(first.state, second.state);
std::swap(first.restriction, second.restriction);
std::swap(first.time, second.time);
std::swap(first.anyState, second.anyState);
first.resetSignalHandler();
second.resetSignalHandler();
}
void populateMediaStates(QComboBox *list)
{
list->addItem("None");
list->addItem("Playing");
list->addItem("Opening");
list->addItem("Buffering");
list->addItem("Paused");
list->addItem("Stopped");
list->addItem("Ended");
list->addItem("Error");
list->addItem("Played to end");
list->addItem("Any");
}
void populateTimeRestrictions(QComboBox *list)
{
list->addItem("None");
list->addItem("Time shorter");
list->addItem("Time longer");
list->addItem("Time remaining shorter");
list->addItem("Time remaining longer");
}
MediaSwitchWidget::MediaSwitchWidget(MediaSwitch *s) : SwitchWidget(s)
{
meidaSources = new QComboBox();
states = new QComboBox();
timeRestrictions = new QComboBox();
time = new QSpinBox();
whenLabel = new QLabel("When");
stateLabel = new QLabel("state is");
andLabel = new QLabel("and");
switchLabel = new QLabel("switch to");
usingLabel = new QLabel("using");
time->setSuffix("ms");
time->setMaximum(99999999);
time->setMinimum(0);
QWidget::connect(meidaSources,
SIGNAL(currentTextChanged(const QString &)), this,
SLOT(SourceChanged(const QString &)));
QWidget::connect(states, SIGNAL(currentIndexChanged(int)), this,
SLOT(StateChanged(int)));
QWidget::connect(timeRestrictions, SIGNAL(currentIndexChanged(int)),
this, SLOT(TimeRestrictionChanged(int)));
QWidget::connect(time, SIGNAL(valueChanged(int)), this,
SLOT(TimeChanged(int)));
AdvSceneSwitcher::populateMediaSelection(meidaSources);
populateMediaStates(states);
populateTimeRestrictions(timeRestrictions);
if (s) {
meidaSources->setCurrentText(
GetWeakSourceName(s->source).c_str());
states->setCurrentIndex(s->state);
timeRestrictions->setCurrentIndex(s->restriction);
time->setValue(s->time);
if (s->restriction == TIME_RESTRICTION_NONE)
time->setDisabled(true);
}
setStyleSheet("* { background-color: transparent; }");
QHBoxLayout *mainLayout = new QHBoxLayout;
mainLayout->addWidget(whenLabel);
mainLayout->addWidget(meidaSources);
mainLayout->addWidget(stateLabel);
mainLayout->addWidget(states);
mainLayout->addWidget(andLabel);
mainLayout->addWidget(timeRestrictions);
mainLayout->addWidget(time);
mainLayout->addWidget(switchLabel);
mainLayout->addWidget(scenes);
mainLayout->addWidget(usingLabel);
mainLayout->addWidget(transitions);
mainLayout->addStretch();
setLayout(mainLayout);
switchData = s;
loading = false;
}
MediaSwitch *MediaSwitchWidget::getSwitchData()
{
return switchData;
}
void MediaSwitchWidget::setSwitchData(MediaSwitch *s)
{
switchData = s;
}
void MediaSwitchWidget::swapSwitchData(MediaSwitchWidget *s1,
MediaSwitchWidget *s2)
{
SwitchWidget::swapSwitchData(s1, s2);
MediaSwitch *t = s1->getSwitchData();
s1->setSwitchData(s2->getSwitchData());
s2->setSwitchData(t);
}
void MediaSwitchWidget::SourceChanged(const QString &text)
{
if (loading || !switchData)
return;
std::lock_guard<std::mutex> lock(switcher->m);
switchData->clearSignalHandler();
switchData->source = GetWeakSourceByQString(text);
switchData->resetSignalHandler();
}
void MediaSwitchWidget::StateChanged(int index)
{
if (loading || !switchData)
return;
std::lock_guard<std::mutex> lock(switcher->m);
switchData->state = (obs_media_state)index;
switchData->anyState = switchData->state == media_any_idx;
}
void MediaSwitchWidget::TimeRestrictionChanged(int index)
{
if (loading || !switchData)
return;
if ((time_restriction)index == TIME_RESTRICTION_NONE)
time->setDisabled(true);
else
time->setDisabled(false);
std::lock_guard<std::mutex> lock(switcher->m);
switchData->restriction = (time_restriction)index;
}
void MediaSwitchWidget::TimeChanged(int time)
{
if (loading || !switchData)
return;
std::lock_guard<std::mutex> lock(switcher->m);
switchData->time = time;
} }

View File

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

View File

@@ -2,7 +2,7 @@
#include "headers/advanced-scene-switcher.hpp" #include "headers/advanced-scene-switcher.hpp"
void SceneSwitcher::on_threadPriority_currentTextChanged(const QString &text) void AdvSceneSwitcher::on_threadPriority_currentTextChanged(const QString &text)
{ {
if (loading || ui->threadPriority->count() != if (loading || ui->threadPriority->count() !=
(int)switcher->threadPriorities.size()) (int)switcher->threadPriorities.size())
@@ -18,7 +18,7 @@ void SceneSwitcher::on_threadPriority_currentTextChanged(const QString &text)
} }
} }
void SceneSwitcher::on_priorityUp_clicked() void AdvSceneSwitcher::on_priorityUp_clicked()
{ {
int currentIndex = ui->priorityList->currentRow(); int currentIndex = ui->priorityList->currentRow();
if (currentIndex != -1 && currentIndex != 0) { if (currentIndex != -1 && currentIndex != 0) {
@@ -35,7 +35,7 @@ void SceneSwitcher::on_priorityUp_clicked()
} }
} }
void SceneSwitcher::on_priorityDown_clicked() void AdvSceneSwitcher::on_priorityDown_clicked()
{ {
int currentIndex = ui->priorityList->currentRow(); int currentIndex = ui->priorityList->currentRow();
if (currentIndex != -1 && if (currentIndex != -1 &&

View File

@@ -1,115 +1,37 @@
#include <random> #include <random>
#include "headers/advanced-scene-switcher.hpp" #include "headers/advanced-scene-switcher.hpp"
#include "headers/utility.hpp"
void SceneSwitcher::on_randomScenesList_currentRowChanged(int idx) static QMetaObject::Connection addPulse;
void AdvSceneSwitcher::on_randomAdd_clicked()
{ {
if (loading) ui->randomAdd->disconnect(addPulse);
return;
if (idx == -1)
return;
QListWidgetItem *item = ui->randomScenesList->item(idx);
QString randomSceneStr = item->data(Qt::UserRole).toString();
std::lock_guard<std::mutex> lock(switcher->m); std::lock_guard<std::mutex> lock(switcher->m);
for (auto &s : switcher->randomSwitches) { switcher->randomSwitches.emplace_back();
if (randomSceneStr.compare(s.randomSwitchStr.c_str()) == 0) {
QString sceneName = GetWeakSourceName(s.scene).c_str(); QListWidgetItem *item;
QString transitionName = item = new QListWidgetItem(ui->randomSwitches);
GetWeakSourceName(s.transition).c_str(); ui->randomSwitches->addItem(item);
ui->randomScenes->setCurrentText(sceneName); RandomSwitchWidget *sw =
ui->randomSpinBox->setValue(s.delay); new RandomSwitchWidget(&switcher->randomSwitches.back());
ui->randomTransitions->setCurrentText(transitionName); item->setSizeHint(sw->minimumSizeHint());
break; ui->randomSwitches->setItemWidget(item, sw);
}
}
} }
int SceneSwitcher::randomFindByData(const QString &randomStr) void AdvSceneSwitcher::on_randomRemove_clicked()
{ {
int count = ui->randomScenesList->count(); QListWidgetItem *item = ui->randomSwitches->currentItem();
for (int i = 0; i < count; i++) {
QListWidgetItem *item = ui->randomScenesList->item(i);
QString str = item->data(Qt::UserRole).toString();
if (str == randomStr)
return i;
}
return -1;
}
void SceneSwitcher::on_randomAdd_clicked()
{
QString sceneName = ui->randomScenes->currentText();
QString transitionName = ui->randomTransitions->currentText();
double delay = ui->randomSpinBox->value();
if (sceneName.isEmpty())
return;
OBSWeakSource source = GetWeakSourceByQString(sceneName);
OBSWeakSource transition = GetWeakTransitionByQString(transitionName);
QString text = MakeRandomSwitchName(sceneName, transitionName, delay);
QVariant v = QVariant::fromValue(text);
int idx = randomFindByData(text);
if (idx == -1) {
std::lock_guard<std::mutex> lock(switcher->m);
switcher->randomSwitches.emplace_back(
source, transition, delay, text.toUtf8().constData());
QListWidgetItem *item =
new QListWidgetItem(text, ui->randomScenesList);
item->setData(Qt::UserRole, v);
} else {
QListWidgetItem *item = ui->randomScenesList->item(idx);
item->setText(text);
{
std::lock_guard<std::mutex> lock(switcher->m);
for (auto &s : switcher->randomSwitches) {
if (s.scene == source) {
s.delay = delay;
s.transition = transition;
s.randomSwitchStr =
text.toUtf8().constData();
;
break;
}
}
}
ui->randomScenesList->sortItems();
}
}
void SceneSwitcher::on_randomRemove_clicked()
{
QListWidgetItem *item = ui->randomScenesList->currentItem();
if (!item) if (!item)
return; return;
std::string text =
item->data(Qt::UserRole).toString().toUtf8().constData();
{ {
std::lock_guard<std::mutex> lock(switcher->m); std::lock_guard<std::mutex> lock(switcher->m);
int idx = ui->randomSwitches->currentRow();
auto &switches = switcher->randomSwitches; auto &switches = switcher->randomSwitches;
switches.erase(switches.begin() + idx);
for (auto it = switches.begin(); it != switches.end(); ++it) {
auto &s = *it;
if (s.randomSwitchStr == text) {
switches.erase(it);
break;
}
}
} }
delete item; delete item;
@@ -121,12 +43,15 @@ void SwitcherData::checkRandom(bool &match, OBSWeakSource &scene,
if (randomSwitches.size() == 0) if (randomSwitches.size() == 0)
return; return;
std::vector<RandomSwitch> rs(randomSwitches); std::deque<RandomSwitch> rs(randomSwitches);
std::random_device rng; std::random_device rng;
std::mt19937 urng(rng()); std::mt19937 urng(rng());
std::shuffle(rs.begin(), rs.end(), urng); std::shuffle(rs.begin(), rs.end(), urng);
for (RandomSwitch &r : rs) { for (RandomSwitch &r : rs) {
if (r.scene == lastRandomScene) if (!r.initialized())
continue;
if (r.scene == lastRandomScene && randomSwitches.size() != 1)
continue; continue;
scene = r.scene; scene = r.scene;
transition = r.transition; transition = r.transition;
@@ -159,10 +84,9 @@ void SwitcherData::saveRandomSwitches(obs_data_t *obj)
transitionName); transitionName);
obs_data_set_double(array_obj, "delay", s.delay); obs_data_set_double(array_obj, "delay", s.delay);
obs_data_array_push_back(randomArray, array_obj); obs_data_array_push_back(randomArray, array_obj);
obs_source_release(source);
obs_source_release(transition);
} }
obs_source_release(source);
obs_source_release(transition);
obs_data_release(array_obj); obs_data_release(array_obj);
} }
obs_data_set_array(obj, "randomSwitches", randomArray); obs_data_set_array(obj, "randomSwitches", randomArray);
@@ -185,43 +109,97 @@ void SwitcherData::loadRandomSwitches(obs_data_t *obj)
obs_data_get_string(array_obj, "transition"); obs_data_get_string(array_obj, "transition");
double delay = obs_data_get_double(array_obj, "delay"); double delay = obs_data_get_double(array_obj, "delay");
std::string randomSwitchStr =
MakeRandomSwitchName(scene, transition, delay)
.toUtf8()
.constData();
switcher->randomSwitches.emplace_back( switcher->randomSwitches.emplace_back(
GetWeakSourceByName(scene), GetWeakSourceByName(scene),
GetWeakTransitionByName(transition), delay, GetWeakTransitionByName(transition), delay);
randomSwitchStr);
obs_data_release(array_obj); obs_data_release(array_obj);
} }
obs_data_array_release(randomArray); obs_data_array_release(randomArray);
} }
void SceneSwitcher::setupRandomTab() void AdvSceneSwitcher::setupRandomTab()
{ {
populateSceneSelection(ui->randomScenes, false);
populateTransitionSelection(ui->randomTransitions);
for (auto &s : switcher->randomSwitches) { for (auto &s : switcher->randomSwitches) {
std::string sceneName = GetWeakSourceName(s.scene); QListWidgetItem *item;
std::string transitionName = GetWeakSourceName(s.transition); item = new QListWidgetItem(ui->randomSwitches);
QString text = MakeRandomSwitchName( ui->randomSwitches->addItem(item);
sceneName.c_str(), transitionName.c_str(), s.delay); RandomSwitchWidget *sw = new RandomSwitchWidget(&s);
item->setSizeHint(sw->minimumSizeHint());
QListWidgetItem *item = ui->randomSwitches->setItemWidget(item, sw);
new QListWidgetItem(text, ui->randomScenesList);
item->setData(Qt::UserRole, text);
} }
if (switcher->randomSwitches.size() == 0)
addPulse = PulseWidget(ui->randomAdd, QColor(Qt::green));
if (switcher->switchIfNotMatching != RANDOM_SWITCH)
PulseWidget(ui->randomDisabledWarning, QColor(Qt::red),
QColor(0, 0, 0, 0), "QLabel ");
else
ui->randomDisabledWarning->setVisible(false);
} }
static inline QString MakeRandomSwitchName(const QString &scene, RandomSwitchWidget::RandomSwitchWidget(RandomSwitch *s) : SwitchWidget(s, false)
const QString &transition,
double &delay)
{ {
return QStringLiteral("[") + scene + QStringLiteral(", ") + transition + delay = new QDoubleSpinBox();
QStringLiteral("]: ") + QString::number(delay) +
QStringLiteral(" seconds"); switchLabel = new QLabel("If no switch condition is met switch to ");
usingLabel = new QLabel("using");
forLabel = new QLabel("for");
QWidget::connect(delay, SIGNAL(valueChanged(double)), this,
SLOT(DelayChanged(double)));
delay->setSuffix("s");
delay->setMaximum(999999999.9);
if (s) {
delay->setValue(s->delay);
}
setStyleSheet("* { background-color: transparent; }");
QHBoxLayout *mainLayout = new QHBoxLayout;
mainLayout->addWidget(switchLabel);
mainLayout->addWidget(scenes);
mainLayout->addWidget(usingLabel);
mainLayout->addWidget(transitions);
mainLayout->addWidget(forLabel);
mainLayout->addWidget(delay);
mainLayout->addStretch();
setLayout(mainLayout);
switchData = s;
loading = false;
}
RandomSwitch *RandomSwitchWidget::getSwitchData()
{
return switchData;
}
void RandomSwitchWidget::setSwitchData(RandomSwitch *s)
{
switchData = s;
}
void RandomSwitchWidget::swapSwitchData(RandomSwitchWidget *s1,
RandomSwitchWidget *s2)
{
SwitchWidget::swapSwitchData(s1, s2);
RandomSwitch *t = s1->getSwitchData();
s1->setSwitchData(s2->getSwitchData());
s2->setSwitchData(t);
}
void RandomSwitchWidget::DelayChanged(double d)
{
if (loading || !switchData)
return;
std::lock_guard<std::mutex> lock(switcher->m);
switchData->delay = d;
} }

View File

@@ -1,6 +1,136 @@
#include <QTimer> #include <QTimer>
#include "headers/advanced-scene-switcher.hpp" #include "headers/advanced-scene-switcher.hpp"
#include "headers/utility.hpp"
static QMetaObject::Connection addPulse;
void AdvSceneSwitcher::clearFrames(QListWidget *list)
{
for (int i = 0; i < list->count(); ++i) {
ScreenRegionWidget *sw =
(ScreenRegionWidget *)list->itemWidget(list->item(i));
sw->hideFrame();
}
}
void showCurrentFrame(QListWidget *list)
{
QListWidgetItem *item = list->currentItem();
if (!item)
return;
ScreenRegionWidget *sw = (ScreenRegionWidget *)list->itemWidget(item);
sw->showFrame();
}
void AdvSceneSwitcher::SetShowFrames()
{
ui->showFrame->setText("Show guide frames");
}
void AdvSceneSwitcher::SetHideFrames()
{
ui->showFrame->setText("Hide guide frames");
}
void AdvSceneSwitcher::on_showFrame_clicked()
{
switcher->showFrame = !switcher->showFrame;
if (switcher->showFrame) {
SetHideFrames();
showCurrentFrame(ui->screenRegionSwitches);
} else {
SetShowFrames();
clearFrames(ui->screenRegionSwitches);
}
}
void AdvSceneSwitcher::on_screenRegionSwitches_currentRowChanged(int idx)
{
if (loading || idx == -1)
return;
if (switcher->showFrame) {
clearFrames(ui->screenRegionSwitches);
showCurrentFrame(ui->screenRegionSwitches);
}
}
void AdvSceneSwitcher::on_screenRegionAdd_clicked()
{
ui->screenRegionAdd->disconnect(addPulse);
std::lock_guard<std::mutex> lock(switcher->m);
switcher->screenRegionSwitches.emplace_back();
QListWidgetItem *item;
item = new QListWidgetItem(ui->screenRegionSwitches);
ui->screenRegionSwitches->addItem(item);
ScreenRegionWidget *sw =
new ScreenRegionWidget(&switcher->screenRegionSwitches.back());
item->setSizeHint(sw->minimumSizeHint());
ui->screenRegionSwitches->setItemWidget(item, sw);
}
void AdvSceneSwitcher::on_screenRegionRemove_clicked()
{
QListWidgetItem *item = ui->screenRegionSwitches->currentItem();
if (!item)
return;
{
std::lock_guard<std::mutex> lock(switcher->m);
int idx = ui->screenRegionSwitches->currentRow();
auto &switches = switcher->screenRegionSwitches;
switches.erase(switches.begin() + idx);
}
delete item;
}
void AdvSceneSwitcher::on_screenRegionUp_clicked()
{
int index = ui->screenRegionSwitches->currentRow();
if (!listMoveUp(ui->screenRegionSwitches))
return;
ScreenRegionWidget *s1 =
(ScreenRegionWidget *)ui->screenRegionSwitches->itemWidget(
ui->screenRegionSwitches->item(index));
ScreenRegionWidget *s2 =
(ScreenRegionWidget *)ui->screenRegionSwitches->itemWidget(
ui->screenRegionSwitches->item(index - 1));
ScreenRegionWidget::swapSwitchData(s1, s2);
std::lock_guard<std::mutex> lock(switcher->m);
std::swap(switcher->screenRegionSwitches[index],
switcher->screenRegionSwitches[index - 1]);
}
void AdvSceneSwitcher::on_screenRegionDown_clicked()
{
int index = ui->screenRegionSwitches->currentRow();
if (!listMoveDown(ui->screenRegionSwitches))
return;
ScreenRegionWidget *s1 =
(ScreenRegionWidget *)ui->screenRegionSwitches->itemWidget(
ui->screenRegionSwitches->item(index));
ScreenRegionWidget *s2 =
(ScreenRegionWidget *)ui->screenRegionSwitches->itemWidget(
ui->screenRegionSwitches->item(index + 1));
ScreenRegionWidget::swapSwitchData(s1, s2);
std::lock_guard<std::mutex> lock(switcher->m);
std::swap(switcher->screenRegionSwitches[index],
switcher->screenRegionSwitches[index + 1]);
}
void SwitcherData::checkScreenRegionSwitch(bool &match, OBSWeakSource &scene, void SwitcherData::checkScreenRegionSwitch(bool &match, OBSWeakSource &scene,
OBSWeakSource &transition) OBSWeakSource &transition)
@@ -9,6 +139,9 @@ void SwitcherData::checkScreenRegionSwitch(bool &match, OBSWeakSource &scene,
int minRegionSize = 99999; int minRegionSize = 99999;
for (auto &s : screenRegionSwitches) { for (auto &s : screenRegionSwitches) {
if (!s.initialized())
continue;
if (cursorPos.first >= s.minX && cursorPos.second >= s.minY && if (cursorPos.first >= s.minX && cursorPos.second >= s.minY &&
cursorPos.first <= s.maxX && cursorPos.second <= s.maxY) { cursorPos.first <= s.maxX && cursorPos.second <= s.maxY) {
int regionSize = (s.maxX - s.minX) + (s.maxY - s.minY); int regionSize = (s.maxX - s.minX) + (s.maxY - s.minY);
@@ -26,170 +159,13 @@ void SwitcherData::checkScreenRegionSwitch(bool &match, OBSWeakSource &scene,
} }
} }
void SceneSwitcher::updateScreenRegionCursorPos() void AdvSceneSwitcher::updateScreenRegionCursorPos()
{ {
std::pair<int, int> position = getCursorPos(); std::pair<int, int> position = getCursorPos();
ui->cursorXPosition->setText(QString::number(position.first)); ui->cursorXPosition->setText(QString::number(position.first));
ui->cursorYPosition->setText(QString::number(position.second)); ui->cursorYPosition->setText(QString::number(position.second));
} }
void SceneSwitcher::on_screenRegionAdd_clicked()
{
QString sceneName = ui->screenRegionScenes->currentText();
QString transitionName = ui->screenRegionsTransitions->currentText();
if (sceneName.isEmpty())
return;
int minX = ui->screenRegionMinX->value();
int minY = ui->screenRegionMinY->value();
int maxX = ui->screenRegionMaxX->value();
int maxY = ui->screenRegionMaxY->value();
std::string regionStr =
std::to_string(minX) + ", " + std::to_string(minY) + " x " +
std::to_string(maxX) + ", " + std::to_string(maxY);
QString region = QString::fromStdString(regionStr);
OBSWeakSource source = GetWeakSourceByQString(sceneName);
OBSWeakSource transition = GetWeakTransitionByQString(transitionName);
QVariant v = QVariant::fromValue(region);
QString text = MakeScreenRegionSwitchName(sceneName, transitionName,
minX, minY, maxX, maxY);
int idx = ScreenRegionFindByData(region);
if (idx == -1) {
QListWidgetItem *item =
new QListWidgetItem(text, ui->screenRegions);
item->setData(Qt::UserRole, v);
std::lock_guard<std::mutex> lock(switcher->m);
switcher->screenRegionSwitches.emplace_back(
source, transition, minX, minY, maxX, maxY, regionStr);
} else {
QListWidgetItem *item = ui->screenRegions->item(idx);
item->setText(text);
std::string curRegion = region.toUtf8().constData();
{
std::lock_guard<std::mutex> lock(switcher->m);
for (auto &s : switcher->screenRegionSwitches) {
if (s.regionStr == curRegion) {
s.scene = source;
s.transition = transition;
break;
}
}
}
}
}
void SceneSwitcher::on_screenRegionRemove_clicked()
{
QListWidgetItem *item = ui->screenRegions->currentItem();
if (!item)
return;
std::string region =
item->data(Qt::UserRole).toString().toUtf8().constData();
{
std::lock_guard<std::mutex> lock(switcher->m);
auto &switches = switcher->screenRegionSwitches;
for (auto it = switches.begin(); it != switches.end(); ++it) {
auto &s = *it;
if (s.regionStr == region) {
switches.erase(it);
break;
}
}
}
delete item;
}
void SceneSwitcher::on_screenRegions_currentRowChanged(int idx)
{
if (loading)
return;
if (idx == -1)
return;
QListWidgetItem *item = ui->screenRegions->item(idx);
QString region = item->data(Qt::UserRole).toString();
std::lock_guard<std::mutex> lock(switcher->m);
for (auto &s : switcher->screenRegionSwitches) {
if (region.compare(s.regionStr.c_str()) == 0) {
std::string name = GetWeakSourceName(s.scene);
std::string transitionName =
GetWeakSourceName(s.transition);
ui->screenRegionScenes->setCurrentText(name.c_str());
ui->screenRegionsTransitions->setCurrentText(
transitionName.c_str());
ui->screenRegionMinX->setValue(s.minX);
ui->screenRegionMinY->setValue(s.minY);
ui->screenRegionMaxX->setValue(s.maxX);
ui->screenRegionMaxY->setValue(s.maxY);
break;
}
}
}
int SceneSwitcher::ScreenRegionFindByData(const QString &region)
{
int count = ui->screenRegions->count();
int idx = -1;
for (int i = 0; i < count; i++) {
QListWidgetItem *item = ui->screenRegions->item(i);
QString itemRegion = item->data(Qt::UserRole).toString();
if (itemRegion == region) {
idx = i;
break;
}
}
return idx;
}
void SceneSwitcher::on_screenRegionUp_clicked()
{
int index = ui->screenRegions->currentRow();
if (index != -1 && index != 0) {
ui->screenRegions->insertItem(
index - 1, ui->screenRegions->takeItem(index));
ui->screenRegions->setCurrentRow(index - 1);
std::lock_guard<std::mutex> lock(switcher->m);
iter_swap(switcher->screenRegionSwitches.begin() + index,
switcher->screenRegionSwitches.begin() + index - 1);
}
}
void SceneSwitcher::on_screenRegionDown_clicked()
{
int index = ui->screenRegions->currentRow();
if (index != -1 && index != ui->screenRegions->count() - 1) {
ui->screenRegions->insertItem(
index + 1, ui->screenRegions->takeItem(index));
ui->screenRegions->setCurrentRow(index + 1);
std::lock_guard<std::mutex> lock(switcher->m);
iter_swap(switcher->screenRegionSwitches.begin() + index,
switcher->screenRegionSwitches.begin() + index + 1);
}
}
void SwitcherData::saveScreenRegionSwitches(obs_data_t *obj) void SwitcherData::saveScreenRegionSwitches(obs_data_t *obj)
{ {
obs_data_array_t *screenRegionArray = obs_data_array_create(); obs_data_array_t *screenRegionArray = obs_data_array_create();
@@ -212,10 +188,9 @@ void SwitcherData::saveScreenRegionSwitches(obs_data_t *obj)
obs_data_set_int(array_obj, "maxX", s.maxX); obs_data_set_int(array_obj, "maxX", s.maxX);
obs_data_set_int(array_obj, "maxY", s.maxY); obs_data_set_int(array_obj, "maxY", s.maxY);
obs_data_array_push_back(screenRegionArray, array_obj); obs_data_array_push_back(screenRegionArray, array_obj);
obs_source_release(source);
obs_source_release(transition);
} }
obs_source_release(source);
obs_source_release(transition);
obs_data_release(array_obj); obs_data_release(array_obj);
} }
obs_data_set_array(obj, "screenRegion", screenRegionArray); obs_data_set_array(obj, "screenRegion", screenRegionArray);
@@ -243,39 +218,30 @@ void SwitcherData::loadScreenRegionSwitches(obs_data_t *obj)
int maxX = obs_data_get_int(array_obj, "maxX"); int maxX = obs_data_get_int(array_obj, "maxX");
int maxY = obs_data_get_int(array_obj, "maxY"); int maxY = obs_data_get_int(array_obj, "maxY");
std::string regionStr =
MakeScreenRegionSwitchName(scene, transition, minX,
minY, maxX, maxY)
.toUtf8()
.constData();
switcher->screenRegionSwitches.emplace_back( switcher->screenRegionSwitches.emplace_back(
GetWeakSourceByName(scene), GetWeakSourceByName(scene),
GetWeakTransitionByName(transition), minX, minY, maxX, GetWeakTransitionByName(transition), minX, minY, maxX,
maxY, regionStr); maxY);
obs_data_release(array_obj); obs_data_release(array_obj);
} }
obs_data_array_release(screenRegionArray); obs_data_array_release(screenRegionArray);
} }
void SceneSwitcher::setupRegionTab() void AdvSceneSwitcher::setupRegionTab()
{ {
populateSceneSelection(ui->screenRegionScenes, false);
populateTransitionSelection(ui->screenRegionsTransitions);
for (auto &s : switcher->screenRegionSwitches) { for (auto &s : switcher->screenRegionSwitches) {
std::string sceneName = GetWeakSourceName(s.scene); QListWidgetItem *item;
std::string transitionName = GetWeakSourceName(s.transition); item = new QListWidgetItem(ui->screenRegionSwitches);
QString text = MakeScreenRegionSwitchName( ui->screenRegionSwitches->addItem(item);
sceneName.c_str(), transitionName.c_str(), s.minX, ScreenRegionWidget *sw = new ScreenRegionWidget(&s);
s.minY, s.maxX, s.maxY); item->setSizeHint(sw->minimumSizeHint());
ui->screenRegionSwitches->setItemWidget(item, sw);
QListWidgetItem *item =
new QListWidgetItem(text, ui->screenRegions);
item->setData(Qt::UserRole, s.regionStr.c_str());
} }
if (switcher->screenRegionSwitches.size() == 0)
addPulse = PulseWidget(ui->screenRegionAdd, QColor(Qt::green));
// screen region cursor position // screen region cursor position
QTimer *screenRegionTimer = new QTimer(this); QTimer *screenRegionTimer = new QTimer(this);
connect(screenRegionTimer, SIGNAL(timeout()), this, connect(screenRegionTimer, SIGNAL(timeout()), this,
@@ -283,14 +249,155 @@ void SceneSwitcher::setupRegionTab()
screenRegionTimer->start(1000); screenRegionTimer->start(1000);
} }
static inline QString MakeScreenRegionSwitchName(const QString &scene, ScreenRegionWidget::ScreenRegionWidget(ScreenRegionSwitch *s)
const QString &transition, : SwitchWidget(s, false)
int minX, int minY, int maxX,
int maxY)
{ {
return QStringLiteral("[") + scene + QStringLiteral(", ") + transition + minX = new QSpinBox();
QStringLiteral("]: ") + QString::number(minX) + minY = new QSpinBox();
QStringLiteral(", ") + QString::number(minY) + maxX = new QSpinBox();
QStringLiteral(" x ") + QString::number(maxX) + maxY = new QSpinBox();
QStringLiteral(", ") + QString::number(maxY);
cursorLabel = new QLabel("If cursor is in");
xLabel = new QLabel("x");
switchLabel = new QLabel("switch to");
usingLabel = new QLabel("using");
minX->setPrefix("Min X: ");
minY->setPrefix("Min Y: ");
maxX->setPrefix("Max X: ");
maxY->setPrefix("Max Y: ");
minX->setMinimum(-1000000);
minY->setMinimum(-1000000);
maxX->setMinimum(-1000000);
maxY->setMinimum(-1000000);
minX->setMaximum(1000000);
minY->setMaximum(1000000);
maxX->setMaximum(1000000);
maxY->setMaximum(1000000);
QWidget::connect(minX, SIGNAL(valueChanged(int)), this,
SLOT(MinXChanged(int)));
QWidget::connect(minY, SIGNAL(valueChanged(int)), this,
SLOT(MinYChanged(int)));
QWidget::connect(maxX, SIGNAL(valueChanged(int)), this,
SLOT(MaxXChanged(int)));
QWidget::connect(maxY, SIGNAL(valueChanged(int)), this,
SLOT(MaxYChanged(int)));
if (s) {
minX->setValue(s->minX);
minY->setValue(s->minY);
maxX->setValue(s->maxX);
maxY->setValue(s->maxY);
}
setStyleSheet("* { background-color: transparent; }");
QHBoxLayout *mainLayout = new QHBoxLayout;
mainLayout->addWidget(cursorLabel);
mainLayout->addWidget(minX);
mainLayout->addWidget(minY);
mainLayout->addWidget(xLabel);
mainLayout->addWidget(maxX);
mainLayout->addWidget(maxY);
mainLayout->addWidget(switchLabel);
mainLayout->addWidget(scenes);
mainLayout->addWidget(usingLabel);
mainLayout->addWidget(transitions);
mainLayout->addStretch();
setLayout(mainLayout);
switchData = s;
loading = false;
}
ScreenRegionSwitch *ScreenRegionWidget::getSwitchData()
{
return switchData;
}
void ScreenRegionWidget::setSwitchData(ScreenRegionSwitch *s)
{
switchData = s;
}
void ScreenRegionWidget::swapSwitchData(ScreenRegionWidget *s1,
ScreenRegionWidget *s2)
{
SwitchWidget::swapSwitchData(s1, s2);
ScreenRegionSwitch *t = s1->getSwitchData();
s1->setSwitchData(s2->getSwitchData());
s2->setSwitchData(t);
}
void ScreenRegionWidget::showFrame()
{
drawFrame();
helperFrame.show();
}
void ScreenRegionWidget::hideFrame()
{
helperFrame.hide();
}
void ScreenRegionWidget::MinXChanged(int pos)
{
if (loading || !switchData)
return;
std::lock_guard<std::mutex> lock(switcher->m);
switchData->minX = pos;
drawFrame();
}
void ScreenRegionWidget::MinYChanged(int pos)
{
if (loading || !switchData)
return;
std::lock_guard<std::mutex> lock(switcher->m);
switchData->minY = pos;
drawFrame();
}
void ScreenRegionWidget::MaxXChanged(int pos)
{
if (loading || !switchData)
return;
std::lock_guard<std::mutex> lock(switcher->m);
switchData->maxX = pos;
drawFrame();
}
void ScreenRegionWidget::MaxYChanged(int pos)
{
if (loading || !switchData)
return;
std::lock_guard<std::mutex> lock(switcher->m);
switchData->maxY = pos;
drawFrame();
}
void ScreenRegionWidget::drawFrame()
{
helperFrame.setFrameStyle(QFrame::Box | QFrame::Plain);
helperFrame.setWindowFlags(Qt::FramelessWindowHint | Qt::Tool |
Qt::WindowTransparentForInput |
Qt::WindowDoesNotAcceptFocus |
Qt::WindowStaysOnTopHint);
helperFrame.setAttribute(Qt::WA_TranslucentBackground, true);
if (switchData)
helperFrame.setGeometry(switchData->minX, switchData->minY,
switchData->maxX - switchData->minX,
switchData->maxY - switchData->minY);
} }

View File

@@ -3,132 +3,93 @@
#include <QMessageBox> #include <QMessageBox>
#include "headers/advanced-scene-switcher.hpp" #include "headers/advanced-scene-switcher.hpp"
#include "headers/utility.hpp"
constexpr auto seconds_index = 0; static QMetaObject::Connection addPulse;
constexpr auto minutes_index = 1;
constexpr auto hours_index = 2;
void SceneSwitcher::on_sceneSequenceDelayUnits_currentIndexChanged(int index) void AdvSceneSwitcher::on_sceneSequenceAdd_clicked()
{ {
if (loading) ui->sceneSequenceAdd->disconnect(addPulse);
return;
std::lock_guard<std::mutex> lock(switcher->m); std::lock_guard<std::mutex> lock(switcher->m);
switcher->sceneSequenceSwitches.emplace_back();
double val = ui->sceneSequenceSpinBox->value(); QListWidgetItem *item;
item = new QListWidgetItem(ui->sceneSequenceSwitches);
switch (switcher->sceneSequenceMultiplier) { ui->sceneSequenceSwitches->addItem(item);
case 1: SequenceWidget *sw =
val /= pow(60, index); new SequenceWidget(&switcher->sceneSequenceSwitches.back());
break; item->setSizeHint(sw->minimumSizeHint());
case 60: ui->sceneSequenceSwitches->setItemWidget(item, sw);
val /= pow(60, index - 1);
break;
case 3600:
val /= pow(60, index - 2);
break;
}
ui->sceneSequenceSpinBox->setValue(val);
switch (index) {
case seconds_index:
switcher->sceneSequenceMultiplier = 1;
break;
case minutes_index:
switcher->sceneSequenceMultiplier = 60;
break;
case hours_index:
switcher->sceneSequenceMultiplier = 3600;
break;
}
} }
void SceneSwitcher::on_sceneSequenceAdd_clicked() void AdvSceneSwitcher::on_sceneSequenceRemove_clicked()
{ {
QString scene1Name = ui->sceneSequenceScenes1->currentText(); QListWidgetItem *item = ui->sceneSequenceSwitches->currentItem();
QString scene2Name = ui->sceneSequenceScenes2->currentText();
QString transitionName = ui->sceneSequenceTransitions->currentText();
if (scene1Name.isEmpty() || scene2Name.isEmpty())
return;
double delay = ui->sceneSequenceSpinBox->value() *
switcher->sceneSequenceMultiplier;
if (scene1Name == scene2Name)
return;
OBSWeakSource source1 = GetWeakSourceByQString(scene1Name);
OBSWeakSource source2 = GetWeakSourceByQString(scene2Name);
OBSWeakSource transition = GetWeakTransitionByQString(transitionName);
QString text = MakeSceneSequenceSwitchName(scene1Name, scene2Name,
transitionName, delay);
QVariant v = QVariant::fromValue(text);
int idx = SceneSequenceFindByData(scene1Name);
if (idx == -1) {
QListWidgetItem *item =
new QListWidgetItem(text, ui->sceneSequences);
item->setData(Qt::UserRole, v);
std::lock_guard<std::mutex> lock(switcher->m);
switcher->sceneSequenceSwitches.emplace_back(
source1, source2, transition, delay,
(scene2Name == QString(previous_scene_name)),
text.toUtf8().constData());
} else {
QListWidgetItem *item = ui->sceneSequences->item(idx);
item->setText(text);
{
std::lock_guard<std::mutex> lock(switcher->m);
for (auto &s : switcher->sceneSequenceSwitches) {
if (s.startScene == source1) {
s.scene = source2;
s.delay = delay;
s.transition = transition;
s.usePreviousScene =
(scene2Name ==
QString(previous_scene_name));
s.sceneSequenceStr =
text.toUtf8().constData();
break;
}
}
}
}
}
void SceneSwitcher::on_sceneSequenceRemove_clicked()
{
QListWidgetItem *item = ui->sceneSequences->currentItem();
if (!item) if (!item)
return; return;
std::string text =
item->data(Qt::UserRole).toString().toUtf8().constData();
{ {
// might be in waiting state of sequence
// causing invalid access after wakeup
// thus we need to stop the main thread before delete
bool wasRunning = !switcher->stop;
switcher->Stop();
std::lock_guard<std::mutex> lock(switcher->m); std::lock_guard<std::mutex> lock(switcher->m);
int idx = ui->sceneSequenceSwitches->currentRow();
auto &switches = switcher->sceneSequenceSwitches; auto &switches = switcher->sceneSequenceSwitches;
switches.erase(switches.begin() + idx);
for (auto it = switches.begin(); it != switches.end(); ++it) { if (wasRunning)
auto &s = *it; switcher->Start();
if (s.sceneSequenceStr == text) {
switches.erase(it);
break;
}
}
} }
delete item; delete item;
} }
void SceneSwitcher::on_sceneSequenceSave_clicked() void AdvSceneSwitcher::on_sceneSequenceUp_clicked()
{
int index = ui->sceneSequenceSwitches->currentRow();
if (!listMoveUp(ui->sceneSequenceSwitches))
return;
SequenceWidget *s1 =
(SequenceWidget *)ui->sceneSequenceSwitches->itemWidget(
ui->sceneSequenceSwitches->item(index));
SequenceWidget *s2 =
(SequenceWidget *)ui->sceneSequenceSwitches->itemWidget(
ui->sceneSequenceSwitches->item(index - 1));
SequenceWidget::swapSwitchData(s1, s2);
std::lock_guard<std::mutex> lock(switcher->m);
std::swap(switcher->sceneSequenceSwitches[index],
switcher->sceneSequenceSwitches[index - 1]);
}
void AdvSceneSwitcher::on_sceneSequenceDown_clicked()
{
int index = ui->sceneSequenceSwitches->currentRow();
if (!listMoveDown(ui->sceneSequenceSwitches))
return;
SequenceWidget *s1 =
(SequenceWidget *)ui->sceneSequenceSwitches->itemWidget(
ui->sceneSequenceSwitches->item(index));
SequenceWidget *s2 =
(SequenceWidget *)ui->sceneSequenceSwitches->itemWidget(
ui->sceneSequenceSwitches->item(index + 1));
SequenceWidget::swapSwitchData(s1, s2);
std::lock_guard<std::mutex> lock(switcher->m);
std::swap(switcher->sceneSequenceSwitches[index],
switcher->sceneSequenceSwitches[index + 1]);
}
void AdvSceneSwitcher::on_sceneSequenceSave_clicked()
{ {
QString directory = QFileDialog::getSaveFileName( QString directory = QFileDialog::getSaveFileName(
this, tr("Save Scene Sequence to file ..."), this, tr("Save Scene Sequence to file ..."),
@@ -145,54 +106,8 @@ void SceneSwitcher::on_sceneSequenceSave_clicked()
obs_data_release(obj); obs_data_release(obj);
} }
// to be removed once support for old file format is dropped void AdvSceneSwitcher::on_sceneSequenceLoad_clicked()
bool oldSceneRoundTripLoad(QFile *file)
{ {
QTextStream in(file);
std::vector<QString> lines;
std::vector<SceneSequenceSwitch> newSceneRoundTripSwitch;
while (!in.atEnd()) {
QString line = in.readLine();
lines.push_back(line);
if (lines.size() == 5) {
OBSWeakSource scene1 = GetWeakSourceByQString(lines[0]);
OBSWeakSource scene2 = GetWeakSourceByQString(lines[1]);
OBSWeakSource transition =
GetWeakTransitionByQString(lines[4]);
if (WeakSourceValid(scene1) &&
((lines[1] == QString(previous_scene_name)) ||
(WeakSourceValid(scene2))) &&
WeakSourceValid(transition)) {
newSceneRoundTripSwitch.emplace_back(
SceneSequenceSwitch(
GetWeakSourceByQString(lines[0]),
GetWeakSourceByQString(lines[1]),
GetWeakTransitionByQString(
lines[4]),
lines[2].toDouble() / 1000.,
(lines[1] ==
QString(previous_scene_name)),
lines[3].toStdString()));
}
lines.clear();
}
}
if (lines.size() != 0 || newSceneRoundTripSwitch.size() == 0)
return false;
switcher->sceneSequenceSwitches.clear();
switcher->sceneSequenceSwitches = newSceneRoundTripSwitch;
return true;
}
void SceneSwitcher::on_sceneSequenceLoad_clicked()
{
std::lock_guard<std::mutex> lock(switcher->m);
QString directory = QFileDialog::getOpenFileName( QString directory = QFileDialog::getOpenFileName(
this, tr("Select a file to read Scene Sequence from ..."), this, tr("Select a file to read Scene Sequence from ..."),
QDir::currentPath(), tr("Text files (*.txt)")); QDir::currentPath(), tr("Text files (*.txt)"));
@@ -208,26 +123,23 @@ void SceneSwitcher::on_sceneSequenceLoad_clicked()
if (!obj) { if (!obj) {
QMessageBox Msgbox; QMessageBox Msgbox;
QString txt = Msgbox.setText(
"Advanced Scene Switcher failed to import settings!\n"; "Advanced Scene Switcher failed to import settings!");
txt += "Falling back to old format.";
Msgbox.setText(txt);
Msgbox.exec(); Msgbox.exec();
if (oldSceneRoundTripLoad(&file)) {
QString txt =
"Advanced Scene Switcher settings imported successfully!\n";
txt += "Please resave settings as support for old format will be dropped in next version.";
Msgbox.setText(txt);
Msgbox.exec();
close();
} else {
Msgbox.setText(
"Advanced Scene Switcher failed to import settings!");
Msgbox.exec();
}
return; return;
} }
// might be in waiting state of sequence
// causing invalid access after wakeup
// thus we need to stop the main thread before delete
bool wasRunning = !switcher->stop;
switcher->Stop();
switcher->loadSceneSequenceSwitches(obj); switcher->loadSceneSequenceSwitches(obj);
if (wasRunning)
switcher->Start();
obs_data_release(obj); obs_data_release(obj);
QMessageBox Msgbox; QMessageBox Msgbox;
@@ -238,13 +150,16 @@ void SceneSwitcher::on_sceneSequenceLoad_clicked()
} }
void SwitcherData::checkSceneSequence(bool &match, OBSWeakSource &scene, void SwitcherData::checkSceneSequence(bool &match, OBSWeakSource &scene,
OBSWeakSource &transition, OBSWeakSource &transition,
std::unique_lock<std::mutex> &lock) std::unique_lock<std::mutex> &lock)
{ {
obs_source_t *currentSource = obs_frontend_get_current_scene(); obs_source_t *currentSource = obs_frontend_get_current_scene();
obs_weak_source_t *ws = obs_source_get_weak_source(currentSource); obs_weak_source_t *ws = obs_source_get_weak_source(currentSource);
for (SceneSequenceSwitch &s : sceneSequenceSwitches) { for (SceneSequenceSwitch &s : sceneSequenceSwitches) {
if (!s.initialized())
continue;
if (s.startScene == ws) { if (s.startScene == ws) {
int dur = s.delay * 1000 - interval; int dur = s.delay * 1000 - interval;
if (dur > 0) { if (dur > 0) {
@@ -268,7 +183,10 @@ void SwitcherData::checkSceneSequence(bool &match, OBSWeakSource &scene,
transition = s.transition; transition = s.transition;
if (verbose) if (verbose)
s.logMatch(); s.logMatch();
} else if (verbose) {
blog(LOG_INFO, "sequence canceled");
} }
obs_source_release(currentSource2); obs_source_release(currentSource2);
break; break;
} }
@@ -277,87 +195,6 @@ void SwitcherData::checkSceneSequence(bool &match, OBSWeakSource &scene,
obs_weak_source_release(ws); obs_weak_source_release(ws);
} }
void SceneSwitcher::on_sceneSequences_currentRowChanged(int idx)
{
if (loading)
return;
if (idx == -1)
return;
QListWidgetItem *item = ui->sceneSequences->item(idx);
QString sceneSequence = item->text();
std::lock_guard<std::mutex> lock(switcher->m);
for (auto &s : switcher->sceneSequenceSwitches) {
if (sceneSequence.compare(s.sceneSequenceStr.c_str()) == 0) {
std::string scene1 = GetWeakSourceName(s.startScene);
std::string scene2 = GetWeakSourceName(s.scene);
std::string transitionName =
GetWeakSourceName(s.transition);
double delay = s.delay /
switcher->sceneSequenceMultiplier;
ui->sceneSequenceScenes1->setCurrentText(
scene1.c_str());
ui->sceneSequenceScenes2->setCurrentText(
scene2.c_str());
ui->sceneSequenceTransitions->setCurrentText(
transitionName.c_str());
ui->sceneSequenceSpinBox->setValue(delay);
break;
}
}
}
int SceneSwitcher::SceneSequenceFindByData(const QString &scene1)
{
QRegExp rx(scene1 + " ->.*");
int count = ui->sceneSequences->count();
int idx = -1;
for (int i = 0; i < count; i++) {
QListWidgetItem *item = ui->sceneSequences->item(i);
QString itemString = item->data(Qt::UserRole).toString();
if (rx.exactMatch(itemString)) {
idx = i;
break;
}
}
return idx;
}
void SceneSwitcher::on_sceneSequenceUp_clicked()
{
int index = ui->sceneSequences->currentRow();
if (index != -1 && index != 0) {
ui->sceneSequences->insertItem(
index - 1, ui->sceneSequences->takeItem(index));
ui->sceneSequences->setCurrentRow(index - 1);
std::lock_guard<std::mutex> lock(switcher->m);
iter_swap(switcher->sceneSequenceSwitches.begin() + index,
switcher->sceneSequenceSwitches.begin() + index - 1);
}
}
void SceneSwitcher::on_sceneSequenceDown_clicked()
{
int index = ui->sceneSequences->currentRow();
if (index != -1 && index != ui->sceneSequences->count() - 1) {
ui->sceneSequences->insertItem(
index + 1, ui->sceneSequences->takeItem(index));
ui->sceneSequences->setCurrentRow(index + 1);
std::lock_guard<std::mutex> lock(switcher->m);
iter_swap(switcher->sceneSequenceSwitches.begin() + index,
switcher->sceneSequenceSwitches.begin() + index + 1);
}
}
void SwitcherData::saveSceneSequenceSwitches(obs_data_t *obj) void SwitcherData::saveSceneSequenceSwitches(obs_data_t *obj)
{ {
obs_data_array_t *sceneSequenceArray = obs_data_array_create(); obs_data_array_t *sceneSequenceArray = obs_data_array_create();
@@ -383,13 +220,14 @@ void SwitcherData::saveSceneSequenceSwitches(obs_data_t *obj)
obs_data_set_string(array_obj, "transition", obs_data_set_string(array_obj, "transition",
transitionName); transitionName);
obs_data_set_double(array_obj, "delay", s.delay); obs_data_set_double(array_obj, "delay", s.delay);
obs_data_array_push_back(sceneSequenceArray, obs_data_set_int(array_obj, "delayMultiplier",
array_obj); s.delayMultiplier);
obs_source_release(source1); obs_data_array_push_back(sceneSequenceArray, array_obj);
obs_source_release(source2);
obs_source_release(transition);
} }
obs_source_release(source1);
obs_source_release(source2);
obs_source_release(transition);
obs_data_release(array_obj); obs_data_release(array_obj);
} }
obs_data_set_array(obj, "sceneRoundTrip", sceneSequenceArray); obs_data_set_array(obj, "sceneRoundTrip", sceneSequenceArray);
@@ -414,94 +252,189 @@ void SwitcherData::loadSceneSequenceSwitches(obs_data_t *obj)
obs_data_get_string(array_obj, "sceneRoundTripScene2"); obs_data_get_string(array_obj, "sceneRoundTripScene2");
const char *transition = const char *transition =
obs_data_get_string(array_obj, "transition"); obs_data_get_string(array_obj, "transition");
double delay = obs_data_get_double(array_obj, "delay");
double delay = 0; int delayMultiplier =
obs_data_get_int(array_obj, "delayMultiplier");
// To be removed in future version if (delayMultiplier == 0 ||
// to be compatible with older versions (delayMultiplier != 1 && delayMultiplier % 60 != 0))
if (!obs_data_has_user_value(array_obj, "delay")) { delayMultiplier = 1;
delay = double(obs_data_get_int(array_obj,
"sceneRoundTripDelay"));
delay = delay * 1000. +
double(obs_data_get_int(
array_obj, "sceneRoundTripDelayMs"));
delay /= 1000.;
} else {
delay = obs_data_get_double(array_obj, "delay");
}
std::string str = MakeSceneSequenceSwitchName(
scene1, scene2, transition, delay)
.toUtf8()
.constData();
const char *sceneSequenceStr = str.c_str();
switcher->sceneSequenceSwitches.emplace_back( switcher->sceneSequenceSwitches.emplace_back(
GetWeakSourceByName(scene1), GetWeakSourceByName(scene1),
GetWeakSourceByName(scene2), GetWeakSourceByName(scene2),
GetWeakTransitionByName(transition), delay, GetWeakTransitionByName(transition), delay,
(strcmp(scene2, previous_scene_name) == 0), delayMultiplier,
sceneSequenceStr); (strcmp(scene2, previous_scene_name) == 0));
obs_data_release(array_obj); obs_data_release(array_obj);
} }
obs_data_array_release(sceneSequenceArray); obs_data_array_release(sceneSequenceArray);
} }
void SceneSwitcher::setupSequenceTab() void AdvSceneSwitcher::setupSequenceTab()
{ {
populateSceneSelection(ui->sceneSequenceScenes1, false);
populateSceneSelection(ui->sceneSequenceScenes2, true);
populateTransitionSelection(ui->sceneSequenceTransitions);
double smallestDelay = double(switcher->interval) / 1000;
for (auto &s : switcher->sceneSequenceSwitches) { for (auto &s : switcher->sceneSequenceSwitches) {
std::string sceneName1 = GetWeakSourceName(s.startScene); QListWidgetItem *item;
std::string sceneName2 = (s.usePreviousScene) item = new QListWidgetItem(ui->sceneSequenceSwitches);
? previous_scene_name ui->sceneSequenceSwitches->addItem(item);
: GetWeakSourceName(s.scene); SequenceWidget *sw = new SequenceWidget(&s);
std::string transitionName = GetWeakSourceName(s.transition); item->setSizeHint(sw->minimumSizeHint());
QString text = MakeSceneSequenceSwitchName( ui->sceneSequenceSwitches->setItemWidget(item, sw);
sceneName1.c_str(), sceneName2.c_str(),
transitionName.c_str(), s.delay);
QListWidgetItem *item =
new QListWidgetItem(text, ui->sceneSequences);
item->setData(Qt::UserRole, text);
if (s.delay < smallestDelay)
smallestDelay = s.delay;
} }
(smallestDelay * 1000 < switcher->interval)
? ui->intervalWarning->setVisible(true)
: ui->intervalWarning->setVisible(false);
ui->sceneSequenceDelayUnits->addItem("seconds"); if (switcher->sceneSequenceSwitches.size() == 0)
ui->sceneSequenceDelayUnits->addItem("minutes"); addPulse = PulseWidget(ui->sceneSequenceAdd, QColor(Qt::green));
ui->sceneSequenceDelayUnits->addItem("hours"); }
switch (switcher->sceneSequenceMultiplier) { bool SceneSequenceSwitch::initialized()
case 1: {
ui->sceneSequenceDelayUnits->setCurrentIndex(0); return SceneSwitcherEntry::initialized() && startScene;
}
bool SceneSequenceSwitch::valid()
{
return !initialized() ||
(SceneSwitcherEntry::valid() && WeakSourceValid(startScene));
}
void SceneSequenceSwitch::logSleep(int dur)
{
blog(LOG_INFO, "sequence sleep %d", dur);
}
void populateDelayUnits(QComboBox *list)
{
list->addItem("seconds");
list->addItem("minutes");
list->addItem("hours");
}
SequenceWidget::SequenceWidget(SceneSequenceSwitch *s) : SwitchWidget(s)
{
delay = new QDoubleSpinBox();
delayUnits = new QComboBox();
startScenes = new QComboBox();
whenLabel = new QLabel("When");
switchLabel = new QLabel("is active switch to");
afterLabel = new QLabel("after");
usingLabel = new QLabel("using");
QWidget::connect(delay, SIGNAL(valueChanged(double)), this,
SLOT(DelayChanged(double)));
QWidget::connect(delayUnits, SIGNAL(currentIndexChanged(int)), this,
SLOT(DelayUnitsChanged(int)));
QWidget::connect(startScenes,
SIGNAL(currentTextChanged(const QString &)), this,
SLOT(StartSceneChanged(const QString &)));
delay->setMaximum(99999.000000);
AdvSceneSwitcher::populateSceneSelection(startScenes, false);
populateDelayUnits(delayUnits);
if (s) {
switch (s->delayMultiplier) {
case 1:
delayUnits->setCurrentIndex(0);
break;
case 60:
delayUnits->setCurrentIndex(1);
break;
case 3600:
delayUnits->setCurrentIndex(2);
break;
default:
delayUnits->setCurrentIndex(0);
}
startScenes->setCurrentText(
GetWeakSourceName(s->startScene).c_str());
}
setStyleSheet("* { background-color: transparent; }");
QHBoxLayout *mainLayout = new QHBoxLayout;
mainLayout->addWidget(whenLabel);
mainLayout->addWidget(startScenes);
mainLayout->addWidget(switchLabel);
mainLayout->addWidget(scenes);
mainLayout->addWidget(afterLabel);
mainLayout->addWidget(delay);
mainLayout->addWidget(delayUnits);
mainLayout->addWidget(usingLabel);
mainLayout->addWidget(transitions);
mainLayout->addStretch();
setLayout(mainLayout);
switchData = s;
loading = false;
UpdateDelay();
}
SceneSequenceSwitch *SequenceWidget::getSwitchData()
{
return switchData;
}
void SequenceWidget::setSwitchData(SceneSequenceSwitch *s)
{
switchData = s;
}
void SequenceWidget::swapSwitchData(SequenceWidget *s1, SequenceWidget *s2)
{
SwitchWidget::swapSwitchData(s1, s2);
SceneSequenceSwitch *t = s1->getSwitchData();
s1->setSwitchData(s2->getSwitchData());
s2->setSwitchData(t);
}
void SequenceWidget::UpdateDelay()
{
if (switchData)
delay->setValue(switchData->delay /
switchData->delayMultiplier);
}
void SequenceWidget::DelayChanged(double delay)
{
if (loading || !switchData)
return;
switchData->delay = delay * switchData->delayMultiplier;
}
void SequenceWidget::DelayUnitsChanged(int idx)
{
if (loading || !switchData)
return;
delay_units unit = (delay_units)idx;
switch (unit) {
case SECONDS:
switchData->delayMultiplier = 1;
break; break;
case 60: case MINUTES:
ui->sceneSequenceDelayUnits->setCurrentIndex(1); switchData->delayMultiplier = 60;
break; break;
case 3600: case HOURS:
ui->sceneSequenceDelayUnits->setCurrentIndex(2); switchData->delayMultiplier = 60 * 60;
break; break;
default: default:
break; break;
} }
UpdateDelay();
} }
static inline QString MakeSceneSequenceSwitchName(const QString &scene1, void SequenceWidget::StartSceneChanged(const QString &text)
const QString &scene2,
const QString &transition,
double delay)
{ {
return scene1 + QStringLiteral(" -> wait for ") + if (loading || !switchData)
QString::number(delay) + QStringLiteral(" seconds -> ") + return;
scene2 + QStringLiteral(" (using ") + transition + std::lock_guard<std::mutex> lock(switcher->m);
QStringLiteral(" transition)"); switchData->startScene = GetWeakSourceByQString(text);
} }

View File

@@ -1,173 +1,75 @@
#include "headers/advanced-scene-switcher.hpp" #include "headers/advanced-scene-switcher.hpp"
#include "headers/utility.hpp"
static std::vector<std::pair<std::string, timeTrigger>> triggerTable = { static QMetaObject::Connection addPulse;
{"On any day", timeTrigger::ANY_DAY},
{"Mondays", timeTrigger::MONDAY},
{"Tuesdays", timeTrigger::TUSEDAY},
{"Wednesdays", timeTrigger::WEDNESDAY},
{"Thursdays", timeTrigger::THURSDAY},
{"Fridays", timeTrigger::FRIDAY},
{"Saturdays", timeTrigger::SATURDAY},
{"Sundays", timeTrigger::SUNDAY},
{"Atfer streaming/recording start", timeTrigger::LIVE}};
void SceneSwitcher::on_timeSwitches_currentRowChanged(int idx) void AdvSceneSwitcher::on_timeAdd_clicked()
{ {
if (loading) ui->timeAdd->disconnect(addPulse);
return;
if (idx == -1)
return;
QListWidgetItem *item = ui->timeSwitches->item(idx);
QString timeScenestr = item->data(Qt::UserRole).toString();
std::lock_guard<std::mutex> lock(switcher->m); std::lock_guard<std::mutex> lock(switcher->m);
for (auto &s : switcher->timeSwitches) { switcher->timeSwitches.emplace_back();
if (timeScenestr.compare(s.timeSwitchStr.c_str()) == 0) {
QString sceneName = GetWeakSourceName(s.scene).c_str(); QListWidgetItem *item;
QString transitionName = item = new QListWidgetItem(ui->timeSwitches);
GetWeakSourceName(s.transition).c_str(); ui->timeSwitches->addItem(item);
ui->timeScenes->setCurrentText(sceneName); TimeSwitchWidget *sw =
ui->timeTrigger->setCurrentIndex(s.trigger); new TimeSwitchWidget(&switcher->timeSwitches.back());
ui->timeEdit->setTime(s.time); item->setSizeHint(sw->minimumSizeHint());
ui->timeTransitions->setCurrentText(transitionName); ui->timeSwitches->setItemWidget(item, sw);
break;
}
}
} }
int SceneSwitcher::timeFindByData(const timeTrigger &trigger, const QTime &time) void AdvSceneSwitcher::on_timeRemove_clicked()
{
QRegExp rx(MakeTimeSwitchName(QStringLiteral(".*"),
QStringLiteral(".*"), trigger, time));
int count = ui->timeSwitches->count();
for (int i = 0; i < count; i++) {
QListWidgetItem *item = ui->timeSwitches->item(i);
QString str = item->data(Qt::UserRole).toString();
if (rx.exactMatch(str))
return i;
}
return -1;
}
void SceneSwitcher::on_timeAdd_clicked()
{
QString sceneName = ui->timeScenes->currentText();
QString transitionName = ui->timeTransitions->currentText();
QTime time = ui->timeEdit->time();
if (sceneName.isEmpty())
return;
OBSWeakSource source = GetWeakSourceByQString(sceneName);
OBSWeakSource transition = GetWeakTransitionByQString(transitionName);
std::string triggerStr = ui->timeTrigger->currentText().toStdString();
auto it = std::find_if(
triggerTable.begin(), triggerTable.end(),
[&triggerStr](
const std::pair<std::string, timeTrigger> &element) {
return element.first == triggerStr;
});
timeTrigger trigger = it->second;
QString text =
MakeTimeSwitchName(sceneName, transitionName, trigger, time);
QVariant v = QVariant::fromValue(text);
int idx = timeFindByData(trigger, time);
if (idx == -1) {
std::lock_guard<std::mutex> lock(switcher->m);
switcher->timeSwitches.emplace_back(
source, transition, trigger, time,
(sceneName == QString(previous_scene_name)),
text.toUtf8().constData());
QListWidgetItem *item =
new QListWidgetItem(text, ui->timeSwitches);
item->setData(Qt::UserRole, v);
} else {
QListWidgetItem *item = ui->timeSwitches->item(idx);
item->setText(text);
item->setData(Qt::UserRole, v);
{
std::lock_guard<std::mutex> lock(switcher->m);
for (auto &s : switcher->timeSwitches) {
if (s.trigger == trigger && s.time == time) {
s.scene = source;
s.transition = transition;
s.usePreviousScene =
(sceneName ==
QString(previous_scene_name));
s.timeSwitchStr =
text.toUtf8().constData();
break;
}
}
}
}
}
void SceneSwitcher::on_timeRemove_clicked()
{ {
QListWidgetItem *item = ui->timeSwitches->currentItem(); QListWidgetItem *item = ui->timeSwitches->currentItem();
if (!item) if (!item)
return; return;
std::string text =
item->data(Qt::UserRole).toString().toUtf8().constData();
{ {
std::lock_guard<std::mutex> lock(switcher->m); std::lock_guard<std::mutex> lock(switcher->m);
int idx = ui->timeSwitches->currentRow();
auto &switches = switcher->timeSwitches; auto &switches = switcher->timeSwitches;
switches.erase(switches.begin() + idx);
for (auto it = switches.begin(); it != switches.end(); ++it) {
auto &s = *it;
if (s.timeSwitchStr == text) {
switches.erase(it);
break;
}
}
} }
delete item; delete item;
} }
void SceneSwitcher::on_timeUp_clicked() void AdvSceneSwitcher::on_timeUp_clicked()
{ {
int index = ui->timeSwitches->currentRow(); int index = ui->timeSwitches->currentRow();
if (index != -1 && index != 0) { if (!listMoveUp(ui->timeSwitches))
ui->timeSwitches->insertItem(index - 1, return;
ui->timeSwitches->takeItem(index));
ui->timeSwitches->setCurrentRow(index - 1);
std::lock_guard<std::mutex> lock(switcher->m); TimeSwitchWidget *s1 = (TimeSwitchWidget *)ui->timeSwitches->itemWidget(
ui->timeSwitches->item(index));
TimeSwitchWidget *s2 = (TimeSwitchWidget *)ui->timeSwitches->itemWidget(
ui->timeSwitches->item(index - 1));
TimeSwitchWidget::swapSwitchData(s1, s2);
iter_swap(switcher->timeSwitches.begin() + index, std::lock_guard<std::mutex> lock(switcher->m);
switcher->timeSwitches.begin() + index - 1);
} std::swap(switcher->timeSwitches[index],
switcher->timeSwitches[index - 1]);
} }
void SceneSwitcher::on_timeDown_clicked() void AdvSceneSwitcher::on_timeDown_clicked()
{ {
int index = ui->timeSwitches->currentRow(); int index = ui->timeSwitches->currentRow();
if (index != -1 && index != ui->timeSwitches->count() - 1) {
ui->timeSwitches->insertItem(index + 1,
ui->timeSwitches->takeItem(index));
ui->timeSwitches->setCurrentRow(index + 1);
std::lock_guard<std::mutex> lock(switcher->m); if (!listMoveDown(ui->timeSwitches))
return;
iter_swap(switcher->timeSwitches.begin() + index, TimeSwitchWidget *s1 = (TimeSwitchWidget *)ui->timeSwitches->itemWidget(
switcher->timeSwitches.begin() + index + 1); ui->timeSwitches->item(index));
} TimeSwitchWidget *s2 = (TimeSwitchWidget *)ui->timeSwitches->itemWidget(
ui->timeSwitches->item(index + 1));
TimeSwitchWidget::swapSwitchData(s1, s2);
std::lock_guard<std::mutex> lock(switcher->m);
std::swap(switcher->timeSwitches[index],
switcher->timeSwitches[index + 1]);
} }
bool timesAreInInterval(QTime &time1, QTime &time2, int &interval) bool timesAreInInterval(QTime &time1, QTime &time2, int &interval)
@@ -212,6 +114,9 @@ void SwitcherData::checkTimeSwitch(bool &match, OBSWeakSource &scene,
return; return;
for (TimeSwitch &s : timeSwitches) { for (TimeSwitch &s : timeSwitches) {
if (!s.initialized())
continue;
if (s.trigger == LIVE) if (s.trigger == LIVE)
match = checkLiveTime(s, liveTime, interval); match = checkLiveTime(s, liveTime, interval);
else else
@@ -282,91 +187,123 @@ void SwitcherData::loadTimeSwitches(obs_data_t *obj)
QTime time = QTime::fromString( QTime time = QTime::fromString(
obs_data_get_string(array_obj, "time")); obs_data_get_string(array_obj, "time"));
std::string timeSwitchStr =
MakeTimeSwitchName(scene, transition, trigger, time)
.toUtf8()
.constData();
switcher->timeSwitches.emplace_back( switcher->timeSwitches.emplace_back(
GetWeakSourceByName(scene), GetWeakSourceByName(scene),
GetWeakTransitionByName(transition), trigger, time, GetWeakTransitionByName(transition), trigger, time,
(strcmp(scene, previous_scene_name) == 0), (strcmp(scene, previous_scene_name) == 0));
timeSwitchStr);
obs_data_release(array_obj); obs_data_release(array_obj);
} }
obs_data_array_release(timeArray); obs_data_array_release(timeArray);
} }
void SceneSwitcher::setupTimeTab() void AdvSceneSwitcher::setupTimeTab()
{ {
populateSceneSelection(ui->timeScenes, true); for (auto &s : switcher->timeSwitches) {
populateTransitionSelection(ui->timeTransitions); QListWidgetItem *item;
item = new QListWidgetItem(ui->timeSwitches);
for (auto t : triggerTable) { ui->timeSwitches->addItem(item);
ui->timeTrigger->addItem(t.first.c_str()); TimeSwitchWidget *sw = new TimeSwitchWidget(&s);
item->setSizeHint(sw->minimumSizeHint());
ui->timeSwitches->setItemWidget(item, sw);
} }
// assuming the streaming / recording entry is always last if (switcher->timeSwitches.size() == 0)
ui->timeTrigger->setItemData( addPulse = PulseWidget(ui->timeAdd, QColor(Qt::green));
(int)triggerTable.size() - 1, }
void populateTriggers(QComboBox *list)
{
list->addItem("On any day");
list->addItem("Mondays");
list->addItem("Tuesdays");
list->addItem("Wednesdays");
list->addItem("Thursdays");
list->addItem("Fridays");
list->addItem("Saturdays");
list->addItem("Sundays");
list->addItem("Atfer streaming/recording start");
list->setItemData(
8,
"The time relative to the start of streaming / recording will be used", "The time relative to the start of streaming / recording will be used",
Qt::ToolTipRole); Qt::ToolTipRole);
for (auto &s : switcher->timeSwitches) {
std::string sceneName = (s.usePreviousScene)
? previous_scene_name
: GetWeakSourceName(s.scene);
std::string transitionName = GetWeakSourceName(s.transition);
QString listText = MakeTimeSwitchName(sceneName.c_str(),
transitionName.c_str(),
s.trigger, s.time);
QListWidgetItem *item =
new QListWidgetItem(listText, ui->timeSwitches);
item->setData(Qt::UserRole, listText);
}
} }
static inline QString MakeTimeSwitchName(const QString &scene, TimeSwitchWidget::TimeSwitchWidget(TimeSwitch *s) : SwitchWidget(s)
const QString &transition,
const timeTrigger &trigger,
const QTime &time)
{ {
QString switchName; triggers = new QComboBox();
time = new QTimeEdit();
switch (trigger) { atLabel = new QLabel("at");
case ANY_DAY: switchLabel = new QLabel("switch to");
switchName = QStringLiteral("On any weekday"); usingLabel = new QLabel("using");
break;
case MONDAY: QWidget::connect(triggers, SIGNAL(currentIndexChanged(int)), this,
switchName = QStringLiteral("Mondays"); SLOT(TriggerChanged(int)));
break; QWidget::connect(time, SIGNAL(timeChanged(const QTime &)), this,
case TUSEDAY: SLOT(TimeChanged(const QTime &)));
switchName = QStringLiteral("Tusedays");
break; populateTriggers(triggers);
case WEDNESDAY: time->setDisplayFormat("HH:mm:ss");
switchName = QStringLiteral("Wednesdays");
break; if (s) {
case THURSDAY: triggers->setCurrentIndex(s->trigger);
switchName = QStringLiteral("Thursdays"); time->setTime(s->time);
break;
case FRIDAY:
switchName = QStringLiteral("Fridays");
break;
case SATURDAY:
switchName = QStringLiteral("Saturdays");
break;
case SUNDAY:
switchName = QStringLiteral("Sundays");
break;
case LIVE:
switchName = QStringLiteral(
"Relative to starting streaming / recording");
break;
} }
switchName += QStringLiteral(" at ") + time.toString() +
QStringLiteral(" switch to ") + scene + setStyleSheet("* { background-color: transparent; }");
QStringLiteral(" using ") + transition;
return switchName; QHBoxLayout *mainLayout = new QHBoxLayout;
mainLayout->addWidget(triggers);
mainLayout->addWidget(atLabel);
mainLayout->addWidget(time);
mainLayout->addWidget(switchLabel);
mainLayout->addWidget(scenes);
mainLayout->addWidget(usingLabel);
mainLayout->addWidget(transitions);
mainLayout->addStretch();
setLayout(mainLayout);
switchData = s;
loading = false;
}
TimeSwitch *TimeSwitchWidget::getSwitchData()
{
return switchData;
}
void TimeSwitchWidget::setSwitchData(TimeSwitch *s)
{
switchData = s;
}
void TimeSwitchWidget::swapSwitchData(TimeSwitchWidget *s1,
TimeSwitchWidget *s2)
{
SwitchWidget::swapSwitchData(s1, s2);
TimeSwitch *t = s1->getSwitchData();
s1->setSwitchData(s2->getSwitchData());
s2->setSwitchData(t);
}
void TimeSwitchWidget::TriggerChanged(int index)
{
if (loading || !switchData)
return;
std::lock_guard<std::mutex> lock(switcher->m);
switchData->trigger = (timeTrigger)index;
}
void TimeSwitchWidget::TimeChanged(const QTime &time)
{
if (loading || !switchData)
return;
std::lock_guard<std::mutex> lock(switcher->m);
switchData->time = time;
} }

View File

@@ -1,274 +1,184 @@
#include "headers/advanced-scene-switcher.hpp" #include "headers/advanced-scene-switcher.hpp"
#include "headers/utility.hpp"
void SceneSwitcher::on_transitionsAdd_clicked() void AdvSceneSwitcher::on_transitionsAdd_clicked()
{ {
QString scene1Name = ui->transitionsScene1->currentText(); std::lock_guard<std::mutex> lock(switcher->m);
QString scene2Name = ui->transitionsScene2->currentText(); switcher->sceneTransitions.emplace_back();
QString transitionName = ui->transitionsTransitions->currentText();
if (scene1Name.isEmpty() || scene2Name.isEmpty()) QListWidgetItem *item;
return; item = new QListWidgetItem(ui->sceneTransitions);
ui->sceneTransitions->addItem(item);
if (scene1Name == scene2Name) TransitionSwitchWidget *sw =
return; new TransitionSwitchWidget(&switcher->sceneTransitions.back());
item->setSizeHint(sw->minimumSizeHint());
OBSWeakSource source1 = GetWeakSourceByQString(scene1Name); ui->sceneTransitions->setItemWidget(item, sw);
OBSWeakSource source2 = GetWeakSourceByQString(scene2Name);
OBSWeakSource transition = GetWeakTransitionByQString(transitionName);
QString text =
MakeSceneTransitionName(scene1Name, scene2Name, transitionName);
QVariant v = QVariant::fromValue(text);
int idx = SceneTransitionsFindByData(scene1Name, scene2Name);
if (idx == -1) {
QListWidgetItem *item =
new QListWidgetItem(text, ui->sceneTransitions);
item->setData(Qt::UserRole, v);
std::lock_guard<std::mutex> lock(switcher->m);
switcher->sceneTransitions.emplace_back(
source1, source2, transition,
text.toUtf8().constData());
} else {
QListWidgetItem *item = ui->sceneTransitions->item(idx);
item->setText(text);
{
std::lock_guard<std::mutex> lock(switcher->m);
for (auto &s : switcher->sceneTransitions) {
if (s.scene == source1 && s.scene2 == source2) {
s.transition = transition;
s.sceneTransitionStr =
text.toUtf8().constData();
break;
}
}
}
ui->sceneTransitions->sortItems();
}
} }
void SceneSwitcher::on_transitionsRemove_clicked() void AdvSceneSwitcher::on_transitionsRemove_clicked()
{ {
QListWidgetItem *item = ui->sceneTransitions->currentItem(); QListWidgetItem *item = ui->sceneTransitions->currentItem();
if (!item) if (!item)
return; return;
std::string text =
item->data(Qt::UserRole).toString().toUtf8().constData();
{ {
std::lock_guard<std::mutex> lock(switcher->m); std::lock_guard<std::mutex> lock(switcher->m);
int idx = ui->sceneTransitions->currentRow();
auto &switches = switcher->sceneTransitions; auto &switches = switcher->sceneTransitions;
switches.erase(switches.begin() + idx);
for (auto it = switches.begin(); it != switches.end(); ++it) {
auto &s = *it;
if (s.sceneTransitionStr == text) {
switches.erase(it);
break;
}
}
} }
delete item; delete item;
} }
void SceneSwitcher::on_defaultTransitionsAdd_clicked() void AdvSceneSwitcher::on_transitionsUp_clicked()
{ {
QString sceneName = ui->defaultTransitionsScene->currentText(); int index = ui->sceneTransitions->currentRow();
QString transitionName = if (!listMoveUp(ui->sceneTransitions))
ui->defaultTransitionsTransitions->currentText();
if (sceneName.isEmpty() || transitionName.isEmpty())
return; return;
OBSWeakSource source = GetWeakSourceByQString(sceneName); TransitionSwitchWidget *s1 =
OBSWeakSource transition = GetWeakTransitionByQString(transitionName); (TransitionSwitchWidget *)ui->sceneTransitions->itemWidget(
ui->sceneTransitions->item(index));
TransitionSwitchWidget *s2 =
(TransitionSwitchWidget *)ui->sceneTransitions->itemWidget(
ui->sceneTransitions->item(index - 1));
TransitionSwitchWidget::swapSwitchData(s1, s2);
QString text = std::lock_guard<std::mutex> lock(switcher->m);
MakeDefaultSceneTransitionName(sceneName, transitionName);
QVariant v = QVariant::fromValue(text);
int idx = DefaultTransitionsFindByData(sceneName); std::swap(switcher->sceneTransitions[index],
switcher->sceneTransitions[index - 1]);
if (idx == -1) {
QListWidgetItem *item =
new QListWidgetItem(text, ui->defaultTransitions);
item->setData(Qt::UserRole, v);
std::lock_guard<std::mutex> lock(switcher->m);
switcher->defaultSceneTransitions.emplace_back(
source, transition, text.toUtf8().constData());
} else {
QListWidgetItem *item = ui->defaultTransitions->item(idx);
item->setText(text);
{
std::lock_guard<std::mutex> lock(switcher->m);
for (auto &s : switcher->defaultSceneTransitions) {
if (s.scene == source) {
s.transition = transition;
s.sceneTransitionStr =
text.toUtf8().constData();
break;
}
}
}
ui->defaultTransitions->sortItems();
}
} }
void SceneSwitcher::on_defaultTransitionsRemove_clicked() void AdvSceneSwitcher::on_transitionsDown_clicked()
{
int index = ui->sceneTransitions->currentRow();
if (!listMoveDown(ui->sceneTransitions))
return;
TransitionSwitchWidget *s1 =
(TransitionSwitchWidget *)ui->sceneTransitions->itemWidget(
ui->sceneTransitions->item(index));
TransitionSwitchWidget *s2 =
(TransitionSwitchWidget *)ui->sceneTransitions->itemWidget(
ui->sceneTransitions->item(index + 1));
TransitionSwitchWidget::swapSwitchData(s1, s2);
std::lock_guard<std::mutex> lock(switcher->m);
std::swap(switcher->sceneTransitions[index],
switcher->sceneTransitions[index + 1]);
}
void AdvSceneSwitcher::on_defaultTransitionsAdd_clicked()
{
std::lock_guard<std::mutex> lock(switcher->m);
switcher->defaultSceneTransitions.emplace_back();
QListWidgetItem *item;
item = new QListWidgetItem(ui->defaultTransitions);
ui->defaultTransitions->addItem(item);
DefTransitionSwitchWidget *sw = new DefTransitionSwitchWidget(
&switcher->defaultSceneTransitions.back());
item->setSizeHint(sw->minimumSizeHint());
ui->defaultTransitions->setItemWidget(item, sw);
}
void AdvSceneSwitcher::on_defaultTransitionsRemove_clicked()
{ {
QListWidgetItem *item = ui->defaultTransitions->currentItem(); QListWidgetItem *item = ui->defaultTransitions->currentItem();
if (!item) if (!item)
return; return;
std::string text =
item->data(Qt::UserRole).toString().toUtf8().constData();
{ {
std::lock_guard<std::mutex> lock(switcher->m); std::lock_guard<std::mutex> lock(switcher->m);
int idx = ui->defaultTransitions->currentRow();
auto &switches = switcher->defaultSceneTransitions; auto &switches = switcher->defaultSceneTransitions;
switches.erase(switches.begin() + idx);
for (auto it = switches.begin(); it != switches.end(); ++it) {
auto &s = *it;
if (s.sceneTransitionStr == text) {
switches.erase(it);
break;
}
}
} }
delete item; delete item;
} }
void SwitcherData::setDefaultSceneTransitions() void AdvSceneSwitcher::on_defaultTransitionsUp_clicked()
{ {
int index = ui->defaultTransitions->currentRow();
if (!listMoveUp(ui->defaultTransitions))
return;
TransitionSwitchWidget *s1 =
(TransitionSwitchWidget *)ui->defaultTransitions->itemWidget(
ui->defaultTransitions->item(index));
TransitionSwitchWidget *s2 =
(TransitionSwitchWidget *)ui->defaultTransitions->itemWidget(
ui->defaultTransitions->item(index - 1));
TransitionSwitchWidget::swapSwitchData(s1, s2);
std::lock_guard<std::mutex> lock(switcher->m);
std::swap(switcher->defaultSceneTransitions[index],
switcher->defaultSceneTransitions[index - 1]);
}
void AdvSceneSwitcher::on_defaultTransitionsDown_clicked()
{
int index = ui->defaultTransitions->currentRow();
if (!listMoveDown(ui->defaultTransitions))
return;
DefTransitionSwitchWidget *s1 =
(DefTransitionSwitchWidget *)ui->defaultTransitions->itemWidget(
ui->defaultTransitions->item(index));
DefTransitionSwitchWidget *s2 =
(DefTransitionSwitchWidget *)ui->defaultTransitions->itemWidget(
ui->defaultTransitions->item(index + 1));
DefTransitionSwitchWidget::swapSwitchData(s1, s2);
std::lock_guard<std::mutex> lock(switcher->m);
std::swap(switcher->defaultSceneTransitions[index],
switcher->defaultSceneTransitions[index + 1]);
}
void SwitcherData::checkDefaultSceneTransitions(bool &match,
OBSWeakSource &transition)
{
if (checkedDefTransition)
return;
obs_source_t *currentSource = obs_frontend_get_current_scene(); obs_source_t *currentSource = obs_frontend_get_current_scene();
obs_weak_source_t *ws = obs_source_get_weak_source(currentSource); obs_weak_source_t *ws = obs_source_get_weak_source(currentSource);
for (DefaultSceneTransition &s : defaultSceneTransitions) { for (DefaultSceneTransition &s : defaultSceneTransitions) {
if (s.scene == ws) { if (s.scene == ws) {
obs_source_t *transition = if (!s.initialized())
obs_weak_source_get_source(s.transition); continue;
//This might cancel the current transition
//There is no way to be sure when the previous transition finished match = true;
obs_frontend_set_current_transition(transition); transition = s.transition;
if (verbose) if (verbose)
if (verbose) s.logMatch();
s.logMatch();
obs_source_release(transition);
break; break;
} }
} }
obs_source_release(currentSource); obs_source_release(currentSource);
obs_weak_source_release(ws); obs_weak_source_release(ws);
checkedDefTransition = true;
} }
int SceneSwitcher::SceneTransitionsFindByData(const QString &scene1, void SwitcherData::setCurrentDefTransition(OBSWeakSource &transition)
const QString &scene2)
{ {
QRegExp rx(scene1 + " --- .* --> " + scene2); obs_source_t *transitionSource = obs_weak_source_get_source(transition);
int count = ui->sceneTransitions->count(); obs_frontend_set_current_transition(transitionSource);
int idx = -1; obs_source_release(transitionSource);
for (int i = 0; i < count; i++) {
QListWidgetItem *item = ui->sceneTransitions->item(i);
QString itemString = item->data(Qt::UserRole).toString();
if (rx.exactMatch(itemString)) {
idx = i;
break;
}
}
return idx;
} }
int SceneSwitcher::DefaultTransitionsFindByData(const QString &scene) void AdvSceneSwitcher::on_transitionOverridecheckBox_stateChanged(int state)
{
QRegExp rx(scene + " --> .*");
int count = ui->defaultTransitions->count();
int idx = -1;
for (int i = 0; i < count; i++) {
QListWidgetItem *item = ui->defaultTransitions->item(i);
QString itemString = item->data(Qt::UserRole).toString();
if (rx.exactMatch(itemString)) {
idx = i;
break;
}
}
return idx;
}
void SceneSwitcher::on_sceneTransitions_currentRowChanged(int idx)
{
if (loading)
return;
if (idx == -1)
return;
QListWidgetItem *item = ui->sceneTransitions->item(idx);
QString sceneTransition = item->data(Qt::UserRole).toString();
std::lock_guard<std::mutex> lock(switcher->m);
for (auto &s : switcher->sceneTransitions) {
if (sceneTransition.compare(s.sceneTransitionStr.c_str()) ==
0) {
std::string scene1 = GetWeakSourceName(s.scene);
std::string scene2 = GetWeakSourceName(s.scene2);
std::string transitionName =
GetWeakSourceName(s.transition);
ui->transitionsScene1->setCurrentText(scene1.c_str());
ui->transitionsScene2->setCurrentText(scene2.c_str());
ui->transitionsTransitions->setCurrentText(
transitionName.c_str());
break;
}
}
}
void SceneSwitcher::on_defaultTransitions_currentRowChanged(int idx)
{
if (loading)
return;
if (idx == -1)
return;
QListWidgetItem *item = ui->defaultTransitions->item(idx);
QString sceneTransition = item->data(Qt::UserRole).toString();
std::lock_guard<std::mutex> lock(switcher->m);
for (auto &s : switcher->defaultSceneTransitions) {
if (sceneTransition.compare(s.sceneTransitionStr.c_str()) ==
0) {
std::string scene = GetWeakSourceName(s.scene);
std::string transitionName =
GetWeakSourceName(s.transition);
ui->defaultTransitionsScene->setCurrentText(
scene.c_str());
ui->defaultTransitionsTransitions->setCurrentText(
transitionName.c_str());
break;
}
}
}
void SceneSwitcher::on_transitionOverridecheckBox_stateChanged(int state)
{ {
if (loading) if (loading)
return; return;
@@ -287,8 +197,13 @@ obs_weak_source_t *getNextTransition(obs_weak_source_t *scene1,
obs_weak_source_t *ws = nullptr; obs_weak_source_t *ws = nullptr;
if (scene1 && scene2) { if (scene1 && scene2) {
for (SceneTransition &t : switcher->sceneTransitions) { for (SceneTransition &t : switcher->sceneTransitions) {
if (t.scene == scene1 && t.scene2 == scene2) if (!t.initialized())
continue;
if (t.scene == scene1 && t.scene2 == scene2) {
ws = t.transition; ws = t.transition;
break;
}
} }
obs_weak_source_addref(ws); obs_weak_source_addref(ws);
} }
@@ -373,11 +288,10 @@ void SwitcherData::saveSceneTransitions(obs_data_t *obj)
transitionName); transitionName);
obs_data_array_push_back(sceneTransitionsArray, obs_data_array_push_back(sceneTransitionsArray,
array_obj); array_obj);
obs_source_release(source1);
obs_source_release(source2);
obs_source_release(transition);
} }
obs_source_release(source1);
obs_source_release(source2);
obs_source_release(transition);
obs_data_release(array_obj); obs_data_release(array_obj);
} }
obs_data_set_array(obj, "sceneTransitions", sceneTransitionsArray); obs_data_set_array(obj, "sceneTransitions", sceneTransitionsArray);
@@ -399,10 +313,9 @@ void SwitcherData::saveSceneTransitions(obs_data_t *obj)
transitionName); transitionName);
obs_data_array_push_back(defaultTransitionsArray, obs_data_array_push_back(defaultTransitionsArray,
array_obj); array_obj);
obs_source_release(source);
obs_source_release(transition);
} }
obs_source_release(source);
obs_source_release(transition);
obs_data_release(array_obj); obs_data_release(array_obj);
} }
obs_data_set_array(obj, "defaultTransitions", defaultTransitionsArray); obs_data_set_array(obj, "defaultTransitions", defaultTransitionsArray);
@@ -429,16 +342,10 @@ void SwitcherData::loadSceneTransitions(obs_data_t *obj)
const char *transition = const char *transition =
obs_data_get_string(array_obj, "transition"); obs_data_get_string(array_obj, "transition");
std::string sceneTransitionsStr =
MakeSceneTransitionName(scene1, scene2, transition)
.toUtf8()
.constData();
switcher->sceneTransitions.emplace_back( switcher->sceneTransitions.emplace_back(
GetWeakSourceByName(scene1), GetWeakSourceByName(scene1),
GetWeakSourceByName(scene2), GetWeakSourceByName(scene2),
GetWeakTransitionByName(transition), GetWeakTransitionByName(transition));
sceneTransitionsStr);
obs_data_release(array_obj); obs_data_release(array_obj);
} }
@@ -458,15 +365,9 @@ void SwitcherData::loadSceneTransitions(obs_data_t *obj)
const char *transition = const char *transition =
obs_data_get_string(array_obj, "transition"); obs_data_get_string(array_obj, "transition");
std::string sceneTransitionsStr =
MakeDefaultSceneTransitionName(scene, transition)
.toUtf8()
.constData();
switcher->defaultSceneTransitions.emplace_back( switcher->defaultSceneTransitions.emplace_back(
GetWeakSourceByName(scene), GetWeakSourceByName(scene),
GetWeakTransitionByName(transition), GetWeakTransitionByName(transition));
sceneTransitionsStr);
obs_data_release(array_obj); obs_data_release(array_obj);
} }
@@ -476,52 +377,147 @@ void SwitcherData::loadSceneTransitions(obs_data_t *obj)
obs_data_get_bool(obj, "tansitionOverrideOverride"); obs_data_get_bool(obj, "tansitionOverrideOverride");
} }
void SceneSwitcher::setupTransitionsTab() void AdvSceneSwitcher::setupTransitionsTab()
{ {
populateSceneSelection(ui->transitionsScene1, false);
populateSceneSelection(ui->transitionsScene2, false);
populateSceneSelection(ui->defaultTransitionsScene, false);
populateTransitionSelection(ui->transitionsTransitions);
populateTransitionSelection(ui->defaultTransitionsTransitions);
for (auto &s : switcher->sceneTransitions) { for (auto &s : switcher->sceneTransitions) {
std::string sceneName1 = GetWeakSourceName(s.scene); QListWidgetItem *item;
std::string sceneName2 = GetWeakSourceName(s.scene2); item = new QListWidgetItem(ui->sceneTransitions);
std::string transitionName = GetWeakSourceName(s.transition); ui->sceneTransitions->addItem(item);
QString text = MakeSceneTransitionName(sceneName1.c_str(), TransitionSwitchWidget *sw = new TransitionSwitchWidget(&s);
sceneName2.c_str(), item->setSizeHint(sw->minimumSizeHint());
transitionName.c_str()); ui->sceneTransitions->setItemWidget(item, sw);
QListWidgetItem *item =
new QListWidgetItem(text, ui->sceneTransitions);
item->setData(Qt::UserRole, text);
} }
for (auto &s : switcher->defaultSceneTransitions) { for (auto &s : switcher->defaultSceneTransitions) {
std::string sceneName = GetWeakSourceName(s.scene); QListWidgetItem *item;
std::string transitionName = GetWeakSourceName(s.transition); item = new QListWidgetItem(ui->defaultTransitions);
QString text = MakeDefaultSceneTransitionName( ui->defaultTransitions->addItem(item);
sceneName.c_str(), transitionName.c_str()); DefTransitionSwitchWidget *sw =
new DefTransitionSwitchWidget(&s);
QListWidgetItem *item = item->setSizeHint(sw->minimumSizeHint());
new QListWidgetItem(text, ui->defaultTransitions); ui->defaultTransitions->setItemWidget(item, sw);
item->setData(Qt::UserRole, text);
} }
ui->transitionOverridecheckBox->setChecked( ui->transitionOverridecheckBox->setChecked(
switcher->tansitionOverrideOverride); switcher->tansitionOverrideOverride);
} }
static inline QString MakeSceneTransitionName(const QString &scene1, bool SceneTransition::initialized()
const QString &scene2,
const QString &transition)
{ {
return scene1 + QStringLiteral(" --- ") + transition + return SceneSwitcherEntry::initialized() && scene2;
QStringLiteral(" --> ") + scene2;
} }
static inline QString MakeDefaultSceneTransitionName(const QString &scene, bool SceneTransition::valid()
const QString &transition)
{ {
return scene + QStringLiteral(" --> ") + transition; return !initialized() ||
(SceneSwitcherEntry::valid() && WeakSourceValid(scene2));
}
TransitionSwitchWidget::TransitionSwitchWidget(SceneTransition *s)
: SwitchWidget(s, false)
{
scenes2 = new QComboBox();
switchLabel = new QLabel("Switch from scene");
toLabel = new QLabel("to scene");
usingLabel = new QLabel("using transition");
QWidget::connect(scenes2, SIGNAL(currentTextChanged(const QString &)),
this, SLOT(Scene2Changed(const QString &)));
AdvSceneSwitcher::populateSceneSelection(scenes2, false);
if (s) {
scenes2->setCurrentText(GetWeakSourceName(s->scene2).c_str());
}
setStyleSheet("* { background-color: transparent; }");
QHBoxLayout *mainLayout = new QHBoxLayout;
mainLayout->addWidget(switchLabel);
mainLayout->addWidget(scenes);
mainLayout->addWidget(toLabel);
mainLayout->addWidget(scenes2);
mainLayout->addWidget(usingLabel);
mainLayout->addWidget(transitions);
mainLayout->addStretch();
setLayout(mainLayout);
switchData = s;
loading = false;
}
SceneTransition *TransitionSwitchWidget::getSwitchData()
{
return switchData;
}
void TransitionSwitchWidget::setSwitchData(SceneTransition *s)
{
switchData = s;
}
void TransitionSwitchWidget::swapSwitchData(TransitionSwitchWidget *s1,
TransitionSwitchWidget *s2)
{
SwitchWidget::swapSwitchData(s1, s2);
SceneTransition *t = s1->getSwitchData();
s1->setSwitchData(s2->getSwitchData());
s2->setSwitchData(t);
}
void TransitionSwitchWidget::Scene2Changed(const QString &text)
{
if (loading || !switchData)
return;
std::lock_guard<std::mutex> lock(switcher->m);
switchData->scene2 = GetWeakSourceByQString(text);
}
DefTransitionSwitchWidget::DefTransitionSwitchWidget(DefaultSceneTransition *s)
: SwitchWidget(s, false)
{
whenLabel = new QLabel("When scene");
switchLabel =
new QLabel("is active change default scene transition to ");
setStyleSheet("* { background-color: transparent; }");
QHBoxLayout *mainLayout = new QHBoxLayout;
mainLayout->addWidget(whenLabel);
mainLayout->addWidget(scenes);
mainLayout->addWidget(switchLabel);
mainLayout->addWidget(transitions);
mainLayout->addStretch();
setLayout(mainLayout);
switchData = s;
loading = false;
}
DefaultSceneTransition *DefTransitionSwitchWidget::getSwitchData()
{
return switchData;
}
void DefTransitionSwitchWidget::setSwitchData(DefaultSceneTransition *s)
{
switchData = s;
}
void DefTransitionSwitchWidget::swapSwitchData(DefTransitionSwitchWidget *s1,
DefTransitionSwitchWidget *s2)
{
SwitchWidget::swapSwitchData(s1, s2);
DefaultSceneTransition *t = s1->getSwitchData();
s1->setSwitchData(s2->getSwitchData());
s2->setSwitchData(t);
} }

View File

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

View File

@@ -4,7 +4,7 @@
void SwitcherData::Prune() void SwitcherData::Prune()
{ {
for (size_t i = 0; i < windowSwitches.size(); i++) { for (size_t i = 0; i < windowSwitches.size(); i++) {
WindowSceneSwitch &s = windowSwitches[i]; WindowSwitch &s = windowSwitches[i];
if (!s.valid()) if (!s.valid())
windowSwitches.erase(windowSwitches.begin() + i--); windowSwitches.erase(windowSwitches.begin() + i--);
} }
@@ -60,7 +60,7 @@ void SwitcherData::Prune()
} }
for (size_t i = 0; i < executableSwitches.size(); i++) { for (size_t i = 0; i < executableSwitches.size(); i++) {
ExecutableSceneSwitch &s = executableSwitches[i]; ExecutableSwitch &s = executableSwitches[i];
if (!s.valid()) if (!s.valid())
executableSwitches.erase(executableSwitches.begin() + executableSwitches.erase(executableSwitches.begin() +
i--); i--);

View File

@@ -3,13 +3,16 @@
#include "../headers/advanced-scene-switcher.hpp" #include "../headers/advanced-scene-switcher.hpp"
#include <TlHelp32.h> #include <TlHelp32.h>
#include <Psapi.h> #include <Psapi.h>
#include <locale>
#include <codecvt>
#include <string>
using namespace std; #define MAX_SEARCH 1000
static bool GetWindowTitle(HWND window, string &title) static bool GetWindowTitle(HWND window, std::string &title)
{ {
size_t len = (size_t)GetWindowTextLengthW(window); size_t len = (size_t)GetWindowTextLengthW(window);
wstring wtitle; std::wstring wtitle;
wtitle.resize(len); wtitle.resize(len);
if (!GetWindowTextW(window, &wtitle[0], (int)len + 1)) if (!GetWindowTextW(window, &wtitle[0], (int)len + 1))
@@ -40,45 +43,67 @@ static bool WindowValid(HWND window)
return true; return true;
} }
void GetWindowList(vector<string> &windows) BOOL CALLBACK GetTitleCB(HWND hwnd, LPARAM lParam)
{ {
windows.resize(0); if (!WindowValid(hwnd)) {
return TRUE;
}
HWND window = GetWindow(GetDesktopWindow(), GW_CHILD); std::string title;
GetWindowTitle(hwnd, title);
if (title.empty())
return TRUE;
while (window) { std::vector<std::string> &titles =
string title; *reinterpret_cast<std::vector<std::string> *>(lParam);
if (WindowValid(window) && GetWindowTitle(window, title)) titles.push_back(title);
windows.emplace_back(title);
window = GetNextWindow(window, GW_HWNDNEXT); return TRUE;
}
VOID EnumWindowsWithMetro(__in WNDENUMPROC lpEnumFunc, __in LPARAM lParam)
{
HWND childWindow = NULL;
int i = 0;
while (i < MAX_SEARCH &&
(childWindow = FindWindowEx(NULL, childWindow, NULL, NULL))) {
if (!lpEnumFunc(childWindow, lParam))
return;
i++;
} }
} }
// Overloaded void GetWindowList(std::vector<std::string> &windows)
{
windows.resize(0);
EnumWindowsWithMetro(GetTitleCB, reinterpret_cast<LPARAM>(&windows));
}
void GetWindowList(QStringList &windows) void GetWindowList(QStringList &windows)
{ {
windows.clear(); windows.clear();
HWND window = GetWindow(GetDesktopWindow(), GW_CHILD); std::vector<std::string> w;
GetWindowList(w);
while (window) { for (auto window : w) {
string title; windows << QString::fromStdString(window);
if (WindowValid(window) && GetWindowTitle(window, title))
windows << QString::fromStdString(title);
window = GetNextWindow(window, GW_HWNDNEXT);
} }
// Add entry for OBS Studio itself, see GetCurrentWindowTitle
windows << QString("OBS");
} }
void GetCurrentWindowTitle(string &title) void GetCurrentWindowTitle(std::string &title)
{ {
HWND window = GetForegroundWindow(); HWND window = GetForegroundWindow();
DWORD pid; DWORD pid;
DWORD thid; DWORD thid;
thid = GetWindowThreadProcessId(window, &pid); thid = GetWindowThreadProcessId(window, &pid);
/*GetWindowText will freeze if the control it is reading was created in another thread. // GetWindowText will freeze if the control it is reading was created in another thread.
It does not directly read the control.Instead, // It does not directly read the control.
it waits for the thread that created the control to process a WM_GETTEXT message. // Instead it waits for the thread that created the control to process a WM_GETTEXT message.
So if that thread is frozen in a WaitFor... call you have a deadlock.*/ // So if that thread is frozen in a WaitFor... call you have a deadlock.
if (GetCurrentProcessId() == pid) { if (GetCurrentProcessId() == pid) {
title = "OBS"; title = "OBS";
return; return;
@@ -97,22 +122,26 @@ std::pair<int, int> getCursorPos()
return pos; return pos;
} }
HWND getHWNDfromTitle(std::string title)
{
HWND hwnd = NULL;
std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> converter;
std::wstring wTitle = converter.from_bytes(title);
hwnd = FindWindowEx(NULL, NULL, NULL, wTitle.c_str());
return hwnd;
}
bool isMaximized(std::string &title) bool isMaximized(std::string &title)
{ {
RECT appBounds; RECT appBounds;
MONITORINFO monitorInfo = {0}; MONITORINFO monitorInfo = {0};
HWND hwnd = NULL;
HWND hwnd = GetWindow(GetDesktopWindow(), GW_CHILD); hwnd = getHWNDfromTitle(title);
while (hwnd) { if (!hwnd)
string wtitle; return false;
if (WindowValid(hwnd) && GetWindowTitle(hwnd, wtitle) &&
(wtitle == title ||
QString::fromStdString(wtitle).contains(QRegularExpression(
QString::fromStdString(title)))))
break;
hwnd = GetNextWindow(hwnd, GW_HWNDNEXT);
}
monitorInfo.cbSize = sizeof(MONITORINFO); monitorInfo.cbSize = sizeof(MONITORINFO);
GetMonitorInfo(MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST), GetMonitorInfo(MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST),
@@ -138,17 +167,11 @@ bool isFullscreen(std::string &title)
RECT appBounds; RECT appBounds;
MONITORINFO monitorInfo = {0}; MONITORINFO monitorInfo = {0};
HWND hwnd = GetWindow(GetDesktopWindow(), GW_CHILD); HWND hwnd = NULL;
while (hwnd) { hwnd = getHWNDfromTitle(title);
string wtitle;
if (WindowValid(hwnd) && GetWindowTitle(hwnd, wtitle) &&
(wtitle == title ||
QString::fromStdString(wtitle).contains(QRegularExpression(
QString::fromStdString(title)))))
break;
hwnd = GetNextWindow(hwnd, GW_HWNDNEXT); if (!hwnd)
} return false;
monitorInfo.cbSize = sizeof(MONITORINFO); monitorInfo.cbSize = sizeof(MONITORINFO);
GetMonitorInfo(MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST), GetMonitorInfo(MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST),