mirror of
https://github.com/WarmUpTill/SceneSwitcher.git
synced 2026-09-07 09:05:57 -05:00
Compare commits
158 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
255d10e603 | ||
|
|
ab3e93ab84 | ||
|
|
3b5f88c14f | ||
|
|
2f33b6bdc3 | ||
|
|
3c1e240722 | ||
|
|
91831a1075 | ||
|
|
ccc824abc1 | ||
|
|
6f39d4648b | ||
|
|
74cb400426 | ||
|
|
910b1dde99 | ||
|
|
e462d7f420 | ||
|
|
0cdd8bf1d0 | ||
|
|
f8ad8c31be | ||
|
|
93c889c215 | ||
|
|
f8b505fa4e | ||
|
|
1516263d2a | ||
|
|
79f5ec340d | ||
|
|
cd54cc17c0 | ||
|
|
098159dc0f | ||
|
|
a4418996f4 | ||
|
|
340cb559ee | ||
|
|
eeefd8279f | ||
|
|
e62dc91cd7 | ||
|
|
536041abd2 | ||
|
|
d3ab9e01c5 | ||
|
|
2113025877 | ||
|
|
6aaffeb5a4 | ||
|
|
2a92df4f3c | ||
|
|
560044bbbf | ||
|
|
4bd8cdf5a0 | ||
|
|
c17cbb66d9 | ||
|
|
8d23dd46b0 | ||
|
|
bf70bd7c60 | ||
|
|
1a144d84f5 | ||
|
|
6810ba71e8 | ||
|
|
bbe6a30506 | ||
|
|
08c668f90a | ||
|
|
3151a7542f | ||
|
|
7bfde0a39b | ||
|
|
7347be370e | ||
|
|
66d847c047 | ||
|
|
c9f1f9624b | ||
|
|
8e4a8b7e9d | ||
|
|
e693794549 | ||
|
|
7a924438bc | ||
|
|
7458e9f2d5 | ||
|
|
da2e11e35e | ||
|
|
9f7ca3e4c7 | ||
|
|
2f77d99161 | ||
|
|
acd2e2863a | ||
|
|
513fe0728c | ||
|
|
cba39633aa | ||
|
|
c6a26d862f | ||
|
|
9386109168 | ||
|
|
4b86849356 | ||
|
|
8fd263f44a | ||
|
|
5590e97f4f | ||
|
|
98c3131017 | ||
|
|
f03fca85aa | ||
|
|
7fef29dafb | ||
|
|
9f03264672 | ||
|
|
5082f5944e | ||
|
|
b981a446b6 | ||
|
|
5ab8199e03 | ||
|
|
e6a3f98ab7 | ||
|
|
5e60cefd1c | ||
|
|
fabb7406bd | ||
|
|
cfb0a44b1f | ||
|
|
78ed7415f5 | ||
|
|
d6380b865e | ||
|
|
80cc8ba06c | ||
|
|
f1d5b110b8 | ||
|
|
2e475b8059 | ||
|
|
99b5c16406 | ||
|
|
55d8303ecf | ||
|
|
23201100fa | ||
|
|
3959e41e88 | ||
|
|
f0cf5a5c19 | ||
|
|
87da123c19 | ||
|
|
2a0e9e9d9a | ||
|
|
9bedeedcb6 | ||
|
|
a88a5bb2da | ||
|
|
cfd6be2ba0 | ||
|
|
3658efff20 | ||
|
|
8331b50c5a | ||
|
|
7a343c2070 | ||
|
|
a7a8adb91b | ||
|
|
e61905d02f | ||
|
|
f00c244bfa | ||
|
|
463b54f1fb | ||
|
|
70894f9302 | ||
|
|
de5b6f8eb4 | ||
|
|
be83232caa | ||
|
|
e4b640903d | ||
|
|
a4de8c0c59 | ||
|
|
ac051a42a8 | ||
|
|
e33e93fd18 | ||
|
|
d5052c94ca | ||
|
|
f0aa76517a | ||
|
|
a2543f1b85 | ||
|
|
5f87a61177 | ||
|
|
451bb6518c | ||
|
|
318502d8d0 | ||
|
|
e9b1ef58ba | ||
|
|
26c24d4d53 | ||
|
|
3654f39247 | ||
|
|
b45b38cf6a | ||
|
|
228a18eb77 | ||
|
|
4ed12db12b | ||
|
|
b20b894b2f | ||
|
|
7c052f2633 | ||
|
|
9305576789 | ||
|
|
38bd59c1fd | ||
|
|
7f84de64d8 | ||
|
|
5ae90fe913 | ||
|
|
115a90196c | ||
|
|
5e92a7bcdf | ||
|
|
922cf95119 | ||
|
|
05540b61a1 | ||
|
|
218ad13160 | ||
|
|
6e18eebb25 | ||
|
|
b7058f97a9 | ||
|
|
b0c00dfa98 | ||
|
|
7a074aa548 | ||
|
|
51c740bdc6 | ||
|
|
826a25926c | ||
|
|
e2cd39315f | ||
|
|
7665bf8872 | ||
|
|
14d78100c1 | ||
|
|
92d75bbddb | ||
|
|
14d6e6f5bf | ||
|
|
78fa18f22a | ||
|
|
bbf7d4acf5 | ||
|
|
299f7c00e0 | ||
|
|
70c0a8ed57 | ||
|
|
d41868c660 | ||
|
|
3381e6d62f | ||
|
|
6338aaa891 | ||
|
|
104f74bf39 | ||
|
|
9cc2875455 | ||
|
|
e466bf9750 | ||
|
|
42dcd9dec4 | ||
|
|
c934b5c7ff | ||
|
|
dd2d70bb9c | ||
|
|
ad052e9323 | ||
|
|
086d07f678 | ||
|
|
c606869d41 | ||
|
|
12512f7d0a | ||
|
|
1ed924a39c | ||
|
|
59ed240311 | ||
|
|
712767b824 | ||
|
|
bb4035824b | ||
|
|
a1d385f5dc | ||
|
|
3c9b1f7ad8 | ||
|
|
fc66509e50 | ||
|
|
62f1109256 | ||
|
|
25540aee2b | ||
|
|
53aa828c9e |
107
.clang-format
Normal file
107
.clang-format
Normal file
@@ -0,0 +1,107 @@
|
|||||||
|
# please use clang-format version 8 or later
|
||||||
|
|
||||||
|
Standard: Cpp11
|
||||||
|
AccessModifierOffset: -8
|
||||||
|
AlignAfterOpenBracket: Align
|
||||||
|
AlignConsecutiveAssignments: false
|
||||||
|
AlignConsecutiveDeclarations: false
|
||||||
|
AlignEscapedNewlines: Left
|
||||||
|
AlignOperands: true
|
||||||
|
AlignTrailingComments: true
|
||||||
|
#AllowAllArgumentsOnNextLine: false # requires clang-format 9
|
||||||
|
#AllowAllConstructorInitializersOnNextLine: false # requires clang-format 9
|
||||||
|
AllowAllParametersOfDeclarationOnNextLine: false
|
||||||
|
AllowShortBlocksOnASingleLine: false
|
||||||
|
AllowShortCaseLabelsOnASingleLine: false
|
||||||
|
AllowShortFunctionsOnASingleLine: Inline
|
||||||
|
AllowShortIfStatementsOnASingleLine: false
|
||||||
|
#AllowShortLambdasOnASingleLine: Inline # requires clang-format 9
|
||||||
|
AllowShortLoopsOnASingleLine: false
|
||||||
|
AlwaysBreakAfterDefinitionReturnType: None
|
||||||
|
AlwaysBreakAfterReturnType: None
|
||||||
|
AlwaysBreakBeforeMultilineStrings: false
|
||||||
|
AlwaysBreakTemplateDeclarations: false
|
||||||
|
BinPackArguments: true
|
||||||
|
BinPackParameters: true
|
||||||
|
BraceWrapping:
|
||||||
|
AfterClass: false
|
||||||
|
AfterControlStatement: false
|
||||||
|
AfterEnum: false
|
||||||
|
AfterFunction: true
|
||||||
|
AfterNamespace: false
|
||||||
|
AfterObjCDeclaration: false
|
||||||
|
AfterStruct: false
|
||||||
|
AfterUnion: false
|
||||||
|
AfterExternBlock: false
|
||||||
|
BeforeCatch: false
|
||||||
|
BeforeElse: false
|
||||||
|
IndentBraces: false
|
||||||
|
SplitEmptyFunction: true
|
||||||
|
SplitEmptyRecord: true
|
||||||
|
SplitEmptyNamespace: true
|
||||||
|
BreakBeforeBinaryOperators: None
|
||||||
|
BreakBeforeBraces: Custom
|
||||||
|
BreakBeforeTernaryOperators: true
|
||||||
|
BreakConstructorInitializers: BeforeColon
|
||||||
|
BreakStringLiterals: false # apparently unpredictable
|
||||||
|
ColumnLimit: 80
|
||||||
|
CompactNamespaces: false
|
||||||
|
ConstructorInitializerAllOnOneLineOrOnePerLine: true
|
||||||
|
ConstructorInitializerIndentWidth: 8
|
||||||
|
ContinuationIndentWidth: 8
|
||||||
|
Cpp11BracedListStyle: true
|
||||||
|
DerivePointerAlignment: false
|
||||||
|
DisableFormat: false
|
||||||
|
FixNamespaceComments: false
|
||||||
|
ForEachMacros:
|
||||||
|
- 'json_object_foreach'
|
||||||
|
- 'json_object_foreach_safe'
|
||||||
|
- 'json_array_foreach'
|
||||||
|
IncludeBlocks: Preserve
|
||||||
|
IndentCaseLabels: false
|
||||||
|
IndentPPDirectives: None
|
||||||
|
IndentWidth: 8
|
||||||
|
IndentWrappedFunctionNames: false
|
||||||
|
KeepEmptyLinesAtTheStartOfBlocks: true
|
||||||
|
MaxEmptyLinesToKeep: 1
|
||||||
|
NamespaceIndentation: None
|
||||||
|
#ObjCBinPackProtocolList: Auto # requires clang-format 7
|
||||||
|
ObjCBlockIndentWidth: 8
|
||||||
|
ObjCSpaceAfterProperty: true
|
||||||
|
ObjCSpaceBeforeProtocolList: true
|
||||||
|
|
||||||
|
PenaltyBreakAssignment: 10
|
||||||
|
PenaltyBreakBeforeFirstCallParameter: 30
|
||||||
|
PenaltyBreakComment: 10
|
||||||
|
PenaltyBreakFirstLessLess: 0
|
||||||
|
PenaltyBreakString: 10
|
||||||
|
PenaltyExcessCharacter: 100
|
||||||
|
PenaltyReturnTypeOnItsOwnLine: 60
|
||||||
|
|
||||||
|
PointerAlignment: Right
|
||||||
|
ReflowComments: false
|
||||||
|
SortIncludes: false
|
||||||
|
SortUsingDeclarations: false
|
||||||
|
SpaceAfterCStyleCast: false
|
||||||
|
#SpaceAfterLogicalNot: false # requires clang-format 9
|
||||||
|
SpaceAfterTemplateKeyword: false
|
||||||
|
SpaceBeforeAssignmentOperators: true
|
||||||
|
#SpaceBeforeCtorInitializerColon: true # requires clang-format 7
|
||||||
|
#SpaceBeforeInheritanceColon: true # requires clang-format 7
|
||||||
|
SpaceBeforeParens: ControlStatements
|
||||||
|
#SpaceBeforeRangeBasedForLoopColon: true # requires clang-format 7
|
||||||
|
SpaceInEmptyParentheses: false
|
||||||
|
SpacesBeforeTrailingComments: 1
|
||||||
|
SpacesInAngles: false
|
||||||
|
SpacesInCStyleCastParentheses: false
|
||||||
|
SpacesInContainerLiterals: false
|
||||||
|
SpacesInParentheses: false
|
||||||
|
SpacesInSquareBrackets: false
|
||||||
|
#StatementMacros: # requires clang-format 8
|
||||||
|
# - 'Q_OBJECT'
|
||||||
|
TabWidth: 8
|
||||||
|
#TypenameMacros: # requires clang-format 9
|
||||||
|
# - 'DARRAY'
|
||||||
|
UseTab: ForContinuationAndIndentation
|
||||||
|
---
|
||||||
|
Language: ObjC
|
||||||
285
.github/workflows/build.yml
vendored
Normal file
285
.github/workflows/build.yml
vendored
Normal file
@@ -0,0 +1,285 @@
|
|||||||
|
name: build obs plugin
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [ master ]
|
||||||
|
pull_request:
|
||||||
|
branches: [ master ]
|
||||||
|
|
||||||
|
env:
|
||||||
|
PLUGIN_NAME: SceneSwitcher
|
||||||
|
LIB_NAME: advanced-scene-switcher
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
macos64:
|
||||||
|
name: "macOS 64-bit"
|
||||||
|
runs-on: [macos-latest]
|
||||||
|
env:
|
||||||
|
QT_VERSION: 5.14.1
|
||||||
|
OSX_DEPS_VERSION: '2020-04-07'
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
with:
|
||||||
|
repository: obsproject/obs-studio
|
||||||
|
submodules: 'recursive'
|
||||||
|
- name: "Checkout plugin"
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
with:
|
||||||
|
path: UI/frontend-plugins/${{ env.PLUGIN_NAME }}
|
||||||
|
- name: Fetch Git Tags
|
||||||
|
run: |
|
||||||
|
cd UI/frontend-plugins/${{ env.PLUGIN_NAME }}
|
||||||
|
git fetch --prune --tags --unshallow
|
||||||
|
- name: 'Install prerequisites (Homebrew)'
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
cd UI/frontend-plugins/${{ env.PLUGIN_NAME }}/CI/macos
|
||||||
|
brew update
|
||||||
|
brew bundle
|
||||||
|
cd -
|
||||||
|
- name: 'Install prerequisite: Pre-built dependencies'
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
curl -L -O https://github.com/obsproject/obs-deps/releases/download/${{ env.OSX_DEPS_VERSION }}/osx-deps-${{ env.OSX_DEPS_VERSION }}.tar.gz
|
||||||
|
tar -xf ./osx-deps-${{ env.OSX_DEPS_VERSION }}.tar.gz -C "/tmp"
|
||||||
|
- name: Configure
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
echo "add_subdirectory(${{ env.PLUGIN_NAME }})" >> UI/frontend-plugins/CMakeLists.txt
|
||||||
|
mkdir ./build
|
||||||
|
cd ./build
|
||||||
|
cmake -DCMAKE_OSX_DEPLOYMENT_TARGET=10.11 -DDepsPath="/tmp/obsdeps" -DQTDIR="/usr/local/Cellar/qt/${{ env.QT_VERSION }}" ..
|
||||||
|
cd -
|
||||||
|
- name: Build
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
set -e
|
||||||
|
cd ./build
|
||||||
|
make -j4
|
||||||
|
cd -
|
||||||
|
- name: 'Install prerequisite: Packages app'
|
||||||
|
if: success()
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
curl -L -O https://s3-us-west-2.amazonaws.com/obs-nightly/Packages.pkg
|
||||||
|
sudo installer -pkg ./Packages.pkg -target /
|
||||||
|
- name: Package
|
||||||
|
if: success()
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
cd UI/frontend-plugins/${{ env.PLUGIN_NAME }}
|
||||||
|
install_name_tool -change @rpath/libobs-frontend-api.dylib @executable_path/../Frameworks/libobs-frontend-api.dylib ../../../build/UI/frontend-plugins/SceneSwitcher/advanced-scene-switcher.so
|
||||||
|
install_name_tool -change @rpath/libobs.0.dylib @executable_path/../Frameworks/libobs.0.dylib ../../../build/UI/frontend-plugins/SceneSwitcher/advanced-scene-switcher.so
|
||||||
|
install_name_tool -change /usr/local/opt/qt/lib/QtWidgets.framework/Versions/5/QtWidgets @executable_path/../Frameworks/QtWidgets.framework/Versions/5/QtWidgets ../../../build/UI/frontend-plugins/SceneSwitcher/advanced-scene-switcher.so
|
||||||
|
install_name_tool -change /usr/local/opt/qt/lib/QtGui.framework/Versions/5/QtGui @executable_path/../Frameworks/QtGui.framework/Versions/5/QtGui ../../../build/UI/frontend-plugins/SceneSwitcher/advanced-scene-switcher.so
|
||||||
|
install_name_tool -change /usr/local/opt/qt/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_NAME=${{ env.PLUGIN_NAME }}-$FILE_DATE-${{ github.sha }}-macos.pkg
|
||||||
|
echo "::set-env name=FILE_NAME::${FILE_NAME}"
|
||||||
|
packagesbuild ./CI/macos/${{ env.PLUGIN_NAME }}.pkgproj
|
||||||
|
cd -
|
||||||
|
mkdir ./nightly
|
||||||
|
mv UI/frontend-plugins/${{ env.PLUGIN_NAME }}/${{ env.PLUGIN_NAME }}.pkg ./nightly/${FILE_NAME}
|
||||||
|
- name: Publish
|
||||||
|
if: success()
|
||||||
|
uses: actions/upload-artifact@v2-preview
|
||||||
|
with:
|
||||||
|
name: '${{ env.FILE_NAME }}'
|
||||||
|
path: ./nightly/*.pkg
|
||||||
|
ubuntu64:
|
||||||
|
name: 'Linux/Ubuntu 64-bit'
|
||||||
|
runs-on: [ubuntu-latest]
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
with:
|
||||||
|
repository: obsproject/obs-studio
|
||||||
|
submodules: 'recursive'
|
||||||
|
- name: "Checkout plugin"
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
with:
|
||||||
|
path: UI/frontend-plugins/${{ env.PLUGIN_NAME }}
|
||||||
|
- name: Add plugin to obs cmake
|
||||||
|
shell: bash
|
||||||
|
run: echo "add_subdirectory(${{ env.PLUGIN_NAME }})" >> UI/frontend-plugins/CMakeLists.txt
|
||||||
|
- name: Fetch Git Tags
|
||||||
|
run: git fetch --prune --tags --unshallow
|
||||||
|
- name: Install prerequisites (Apt)
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
sudo dpkg --add-architecture amd64
|
||||||
|
sudo apt-get -qq update
|
||||||
|
sudo apt-get install -y \
|
||||||
|
build-essential \
|
||||||
|
checkinstall \
|
||||||
|
cmake \
|
||||||
|
libasound2-dev \
|
||||||
|
libavcodec-dev \
|
||||||
|
libavdevice-dev \
|
||||||
|
libavfilter-dev \
|
||||||
|
libavformat-dev \
|
||||||
|
libavutil-dev \
|
||||||
|
libcurl4-openssl-dev \
|
||||||
|
libfdk-aac-dev \
|
||||||
|
libfontconfig-dev \
|
||||||
|
libfreetype6-dev \
|
||||||
|
libgl1-mesa-dev \
|
||||||
|
libjack-jackd2-dev \
|
||||||
|
libjansson-dev \
|
||||||
|
libluajit-5.1-dev \
|
||||||
|
libpulse-dev \
|
||||||
|
libqt5x11extras5-dev \
|
||||||
|
libspeexdsp-dev \
|
||||||
|
libswresample-dev \
|
||||||
|
libswscale-dev \
|
||||||
|
libudev-dev \
|
||||||
|
libv4l-dev \
|
||||||
|
libva-dev \
|
||||||
|
libvlc-dev \
|
||||||
|
libx11-dev \
|
||||||
|
libx264-dev \
|
||||||
|
libxcb-randr0-dev \
|
||||||
|
libxcb-shm0-dev \
|
||||||
|
libxcb-xinerama0-dev \
|
||||||
|
libxcomposite-dev \
|
||||||
|
libxinerama-dev \
|
||||||
|
libmbedtls-dev \
|
||||||
|
pkg-config \
|
||||||
|
python3-dev \
|
||||||
|
qtbase5-dev \
|
||||||
|
libqt5svg5-dev \
|
||||||
|
swig \
|
||||||
|
libxss-dev
|
||||||
|
- name: 'Configure'
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
mkdir ./build
|
||||||
|
cd ./build
|
||||||
|
cmake -DUNIX_STRUCTURE=0 -DCMAKE_INSTALL_PREFIX="${{ github.workspace }}/obs-studio-portable" -DBUILD_CAPTIONS=OFF -DWITH_RTMPS=OFF -DBUILD_BROWSER=OFF ..
|
||||||
|
- name: 'Build'
|
||||||
|
shell: bash
|
||||||
|
working-directory: ${{ github.workspace }}/build
|
||||||
|
run: make -j4
|
||||||
|
- name: 'Package'
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
FILE_DATE=$(date +%Y-%m-%d)
|
||||||
|
FILE_NAME=${{ env.PLUGIN_NAME }}-$FILE_DATE-${{ github.sha }}-linux64.tar.gz
|
||||||
|
echo "::set-env name=FILE_NAME::${FILE_NAME}"
|
||||||
|
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
|
||||||
|
tar -cvzf "${FILE_NAME}" ${{ env.PLUGIN_NAME }}
|
||||||
|
- name: 'Publish'
|
||||||
|
uses: actions/upload-artifact@v2-preview
|
||||||
|
with:
|
||||||
|
name: '${{ env.FILE_NAME }}'
|
||||||
|
path: '*.tar.gz'
|
||||||
|
win64:
|
||||||
|
name: Windows 64-bit
|
||||||
|
runs-on: [windows-latest]
|
||||||
|
env:
|
||||||
|
QT_VERSION: 5.10.1
|
||||||
|
CMAKE_GENERATOR: "Visual Studio 16 2019"
|
||||||
|
CMAKE_SYSTEM_VERSION: "10.0.18363.657"
|
||||||
|
steps:
|
||||||
|
- name: Add msbuild to PATH
|
||||||
|
uses: microsoft/setup-msbuild@v1.0.0
|
||||||
|
- name: Checkout obs
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
with:
|
||||||
|
repository: obsproject/obs-studio
|
||||||
|
submodules: 'recursive'
|
||||||
|
- name: Checkout plugin
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
with:
|
||||||
|
path: UI/frontend-plugins/${{ env.PLUGIN_NAME}}
|
||||||
|
- name: Add plugin to obs cmake
|
||||||
|
shell: cmd
|
||||||
|
run: echo add_subdirectory(${{ env.PLUGIN_NAME }}) >> UI/frontend-plugins/CMakeLists.txt
|
||||||
|
- name: Fetch Git Tags
|
||||||
|
run: git fetch --prune --tags --unshallow
|
||||||
|
- name: 'Install prerequisite: QT'
|
||||||
|
run: |
|
||||||
|
curl -kLO https://cdn-fastly.obsproject.com/downloads/Qt_${{ env.QT_VERSION }}.7z -f --retry 5 -C -
|
||||||
|
7z x Qt_${{ env.QT_VERSION }}.7z -o"${{ github.workspace }}/cmbuild/QT"
|
||||||
|
- name: 'Install prerequisite: Pre-built dependencies'
|
||||||
|
run: |
|
||||||
|
curl -kLO https://cdn-fastly.obsproject.com/downloads/dependencies2017.zip -f --retry 5 -C -
|
||||||
|
7z x dependencies2017.zip -o"${{ github.workspace }}/cmbuild/deps"
|
||||||
|
- name: Configure
|
||||||
|
run: |
|
||||||
|
mkdir ./build
|
||||||
|
mkdir ./build64
|
||||||
|
cd ./build64
|
||||||
|
cmake -G"${{ env.CMAKE_GENERATOR }}" -A"x64" -DCMAKE_SYSTEM_VERSION="${{ env.CMAKE_SYSTEM_VERSION }}" -DBUILD_BROWSER=false -DBUILD_CAPTIONS=false -DCOMPILE_D3D12_HOOK=false -DDepsPath="${{ github.workspace }}/cmbuild/deps/win64" -DQTDIR="${{ github.workspace }}/cmbuild/QT/${{ env.QT_VERSION }}/msvc2017_64" -DCOPIED_DEPENDENCIES=FALSE -DCOPY_DEPENDENCIES=TRUE ..
|
||||||
|
- name: Build
|
||||||
|
run: msbuild /m /p:Configuration=RelWithDebInfo .\build64\obs-studio.sln
|
||||||
|
- name: Package
|
||||||
|
if: success()
|
||||||
|
run: |
|
||||||
|
$env:FILE_DATE=(Get-Date -UFormat "%F")
|
||||||
|
$env:FILE_NAME="${{ env.PLUGIN_NAME }}-${env:FILE_DATE}-${{ github.sha }}-win64.zip"
|
||||||
|
echo "::set-env name=FILE_NAME::${env:FILE_NAME}"
|
||||||
|
robocopy .\build64\rundir\RelWithDebInfo\obs-plugins\64bit\ .\build\obs-plugins\64bit ${{ env.LIB_NAME }}.* /E /XF .gitignore
|
||||||
|
7z a ${env:FILE_NAME} .\build\*
|
||||||
|
- name: Publish
|
||||||
|
if: success()
|
||||||
|
uses: actions/upload-artifact@v2-preview
|
||||||
|
with:
|
||||||
|
name: '${{ env.FILE_NAME }}'
|
||||||
|
path: '*-win64.zip'
|
||||||
|
win32:
|
||||||
|
name: Windows 32-bit
|
||||||
|
runs-on: [windows-latest]
|
||||||
|
env:
|
||||||
|
QT_VERSION: 5.10.1
|
||||||
|
CMAKE_GENERATOR: "Visual Studio 16 2019"
|
||||||
|
CMAKE_SYSTEM_VERSION: "10.0.18363.657"
|
||||||
|
steps:
|
||||||
|
- name: Add msbuild to PATH
|
||||||
|
uses: microsoft/setup-msbuild@v1.0.0
|
||||||
|
- name: Checkout obs
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
with:
|
||||||
|
repository: obsproject/obs-studio
|
||||||
|
submodules: 'recursive'
|
||||||
|
- name: Checkout plugin
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
with:
|
||||||
|
path: UI/frontend-plugins/${{ env.PLUGIN_NAME }}
|
||||||
|
- name: Add plugin to obs cmake
|
||||||
|
shell: cmd
|
||||||
|
run: echo add_subdirectory(${{ env.PLUGIN_NAME }}) >> UI/frontend-plugins/CMakeLists.txt
|
||||||
|
- name: Fetch Git Tags
|
||||||
|
run: git fetch --prune --tags --unshallow
|
||||||
|
- name: 'Install prerequisite: QT'
|
||||||
|
run: |
|
||||||
|
curl -kLO https://cdn-fastly.obsproject.com/downloads/Qt_${{ env.QT_VERSION }}.7z -f --retry 5 -C -
|
||||||
|
7z x Qt_${{ env.QT_VERSION }}.7z -o"${{ github.workspace }}/cmbuild/QT"
|
||||||
|
- name: 'Install prerequisite: Pre-built dependencies'
|
||||||
|
run: |
|
||||||
|
curl -kLO https://cdn-fastly.obsproject.com/downloads/dependencies2017.zip -f --retry 5 -C -
|
||||||
|
7z x dependencies2017.zip -o"${{ github.workspace }}/cmbuild/deps"
|
||||||
|
- name: Configure
|
||||||
|
run: |
|
||||||
|
mkdir ./build
|
||||||
|
mkdir ./build32
|
||||||
|
cd ./build32
|
||||||
|
cmake -G"${{ env.CMAKE_GENERATOR }}" -A"Win32" -DCMAKE_SYSTEM_VERSION="${{ env.CMAKE_SYSTEM_VERSION }}" -DBUILD_BROWSER=false -DBUILD_CAPTIONS=false -DCOMPILE_D3D12_HOOK=false -DDepsPath="${{ github.workspace }}/cmbuild/deps/win32" -DQTDIR="${{ github.workspace }}/cmbuild/QT/${{ env.QT_VERSION }}/msvc2017" -DCOPIED_DEPENDENCIES=FALSE -DCOPY_DEPENDENCIES=TRUE ..
|
||||||
|
- name: Build
|
||||||
|
run: msbuild /m /p:Configuration=RelWithDebInfo .\build32\obs-studio.sln
|
||||||
|
- name: Package
|
||||||
|
if: success()
|
||||||
|
run: |
|
||||||
|
$env:FILE_DATE=(Get-Date -UFormat "%F")
|
||||||
|
$env:FILE_NAME="${{ env.PLUGIN_NAME }}-${env:FILE_DATE}-${{ github.sha }}-win32.zip"
|
||||||
|
echo "::set-env name=FILE_NAME::${env:FILE_NAME}"
|
||||||
|
robocopy .\build32\rundir\RelWithDebInfo\obs-plugins\32bit\ .\build\obs-plugins\32bit ${{ env.LIB_NAME }}.* /E /XF .gitignore
|
||||||
|
7z a ${env:FILE_NAME} .\build\*
|
||||||
|
- name: Publish
|
||||||
|
if: success()
|
||||||
|
uses: actions/upload-artifact@v2-preview
|
||||||
|
with:
|
||||||
|
name: '${{ env.FILE_NAME }}'
|
||||||
|
path: '*-win32.zip'
|
||||||
98
.gitignore
vendored
Normal file
98
.gitignore
vendored
Normal file
@@ -0,0 +1,98 @@
|
|||||||
|
# Prerequisites #
|
||||||
|
#################
|
||||||
|
*.d
|
||||||
|
|
||||||
|
# Object files #
|
||||||
|
################
|
||||||
|
*.o
|
||||||
|
*.ko
|
||||||
|
*.obj
|
||||||
|
*.elf
|
||||||
|
*.slo
|
||||||
|
*.lo
|
||||||
|
*.obj
|
||||||
|
|
||||||
|
# Linker output #
|
||||||
|
#################
|
||||||
|
*.ilk
|
||||||
|
*.map
|
||||||
|
*.exp
|
||||||
|
|
||||||
|
# Precompiled Headers #
|
||||||
|
#######################
|
||||||
|
*.gch
|
||||||
|
*.pch
|
||||||
|
|
||||||
|
# Libraries #
|
||||||
|
#############
|
||||||
|
*.lib
|
||||||
|
*.a
|
||||||
|
*.la
|
||||||
|
*.lo
|
||||||
|
*.lai
|
||||||
|
|
||||||
|
# Shared objects (inc. Windows DLLs) #
|
||||||
|
######################################
|
||||||
|
*.dll
|
||||||
|
*.so
|
||||||
|
*.so.*
|
||||||
|
*.dylib
|
||||||
|
|
||||||
|
# Executables #
|
||||||
|
###############
|
||||||
|
*.exe
|
||||||
|
*.out
|
||||||
|
*.app
|
||||||
|
*.i*86
|
||||||
|
*.x86_64
|
||||||
|
*.hex
|
||||||
|
|
||||||
|
# Debug files #
|
||||||
|
###############
|
||||||
|
*.dSYM/
|
||||||
|
*.su
|
||||||
|
*.idb
|
||||||
|
*.pdb
|
||||||
|
|
||||||
|
# CMake temp files #
|
||||||
|
####################
|
||||||
|
CMakeLists.txt.user
|
||||||
|
CMakeCache.txt
|
||||||
|
CMakeFiles
|
||||||
|
CMakeScripts
|
||||||
|
Testing
|
||||||
|
Makefile
|
||||||
|
cmake_install.cmake
|
||||||
|
install_manifest.txt
|
||||||
|
compile_commands.json
|
||||||
|
CTestTestfile.cmake_install
|
||||||
|
|
||||||
|
# OS generated files #
|
||||||
|
######################
|
||||||
|
.DS_Store
|
||||||
|
.DS_Store?
|
||||||
|
._*
|
||||||
|
.Spotlight-V100
|
||||||
|
.Trashes
|
||||||
|
ehthumbs.db
|
||||||
|
Thumbs.db
|
||||||
|
*.directory
|
||||||
|
|
||||||
|
# backup text files generated by an editor #
|
||||||
|
############################################
|
||||||
|
**/*~
|
||||||
|
|
||||||
|
# Various IDE Files #
|
||||||
|
#####################
|
||||||
|
.classpath
|
||||||
|
.project
|
||||||
|
.settings
|
||||||
|
.idea
|
||||||
|
.metadata
|
||||||
|
*.iml
|
||||||
|
*.ipr
|
||||||
|
*.sublime*
|
||||||
|
|
||||||
|
# Directories #
|
||||||
|
###############
|
||||||
|
build/
|
||||||
6
CI/macos/Brewfile
Normal file
6
CI/macos/Brewfile
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
brew "jack"
|
||||||
|
brew "speexdsp"
|
||||||
|
brew "cmake"
|
||||||
|
brew "freetype"
|
||||||
|
brew "fdk-aac"
|
||||||
|
brew "https://gist.githubusercontent.com/DDRBoxman/9c7a2b08933166f4b61ed9a44b242609/raw/ef4de6c587c6bd7f50210eccd5bd51ff08e6de13/qt.rb"
|
||||||
760
CI/macos/SceneSwitcher.pkgproj
Normal file
760
CI/macos/SceneSwitcher.pkgproj
Normal file
@@ -0,0 +1,760 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>PROJECT</key>
|
||||||
|
<dict>
|
||||||
|
<key>PACKAGE_FILES</key>
|
||||||
|
<dict>
|
||||||
|
<key>DEFAULT_INSTALL_LOCATION</key>
|
||||||
|
<string>/</string>
|
||||||
|
<key>HIERARCHY</key>
|
||||||
|
<dict>
|
||||||
|
<key>CHILDREN</key>
|
||||||
|
<array>
|
||||||
|
<dict>
|
||||||
|
<key>CHILDREN</key>
|
||||||
|
<array>
|
||||||
|
<dict>
|
||||||
|
<key>CHILDREN</key>
|
||||||
|
<array>
|
||||||
|
<dict>
|
||||||
|
<key>CHILDREN</key>
|
||||||
|
<array>
|
||||||
|
<dict>
|
||||||
|
<key>CHILDREN</key>
|
||||||
|
<array>
|
||||||
|
<dict>
|
||||||
|
<key>CHILDREN</key>
|
||||||
|
<array>
|
||||||
|
<dict>
|
||||||
|
<key>CHILDREN</key>
|
||||||
|
<array>
|
||||||
|
<dict>
|
||||||
|
<key>CHILDREN</key>
|
||||||
|
<array/>
|
||||||
|
<key>GID</key>
|
||||||
|
<integer>80</integer>
|
||||||
|
<key>PATH</key>
|
||||||
|
<string>../../../../../build/UI/frontend-plugins/SceneSwitcher/advanced-scene-switcher.so</string>
|
||||||
|
<key>PATH_TYPE</key>
|
||||||
|
<integer>3</integer>
|
||||||
|
<key>PERMISSIONS</key>
|
||||||
|
<integer>493</integer>
|
||||||
|
<key>TYPE</key>
|
||||||
|
<integer>3</integer>
|
||||||
|
<key>UID</key>
|
||||||
|
<integer>0</integer>
|
||||||
|
</dict>
|
||||||
|
</array>
|
||||||
|
<key>GID</key>
|
||||||
|
<integer>80</integer>
|
||||||
|
<key>PATH</key>
|
||||||
|
<string>bin</string>
|
||||||
|
<key>PATH_TYPE</key>
|
||||||
|
<integer>0</integer>
|
||||||
|
<key>PERMISSIONS</key>
|
||||||
|
<integer>493</integer>
|
||||||
|
<key>TYPE</key>
|
||||||
|
<integer>2</integer>
|
||||||
|
<key>UID</key>
|
||||||
|
<integer>0</integer>
|
||||||
|
</dict>
|
||||||
|
</array>
|
||||||
|
<key>GID</key>
|
||||||
|
<integer>80</integer>
|
||||||
|
<key>PATH</key>
|
||||||
|
<string>advanced-scene-switcher</string>
|
||||||
|
<key>PATH_TYPE</key>
|
||||||
|
<integer>0</integer>
|
||||||
|
<key>PERMISSIONS</key>
|
||||||
|
<integer>493</integer>
|
||||||
|
<key>TYPE</key>
|
||||||
|
<integer>2</integer>
|
||||||
|
<key>UID</key>
|
||||||
|
<integer>0</integer>
|
||||||
|
</dict>
|
||||||
|
</array>
|
||||||
|
<key>GID</key>
|
||||||
|
<integer>80</integer>
|
||||||
|
<key>PATH</key>
|
||||||
|
<string>plugins</string>
|
||||||
|
<key>PATH_TYPE</key>
|
||||||
|
<integer>0</integer>
|
||||||
|
<key>PERMISSIONS</key>
|
||||||
|
<integer>493</integer>
|
||||||
|
<key>TYPE</key>
|
||||||
|
<integer>2</integer>
|
||||||
|
<key>UID</key>
|
||||||
|
<integer>0</integer>
|
||||||
|
</dict>
|
||||||
|
</array>
|
||||||
|
<key>GID</key>
|
||||||
|
<integer>80</integer>
|
||||||
|
<key>PATH</key>
|
||||||
|
<string>obs-studio</string>
|
||||||
|
<key>PATH_TYPE</key>
|
||||||
|
<integer>0</integer>
|
||||||
|
<key>PERMISSIONS</key>
|
||||||
|
<integer>493</integer>
|
||||||
|
<key>TYPE</key>
|
||||||
|
<integer>2</integer>
|
||||||
|
<key>UID</key>
|
||||||
|
<integer>0</integer>
|
||||||
|
</dict>
|
||||||
|
</array>
|
||||||
|
<key>GID</key>
|
||||||
|
<integer>80</integer>
|
||||||
|
<key>PATH</key>
|
||||||
|
<string>Application Support</string>
|
||||||
|
<key>PATH_TYPE</key>
|
||||||
|
<integer>0</integer>
|
||||||
|
<key>PERMISSIONS</key>
|
||||||
|
<integer>493</integer>
|
||||||
|
<key>TYPE</key>
|
||||||
|
<integer>1</integer>
|
||||||
|
<key>UID</key>
|
||||||
|
<integer>0</integer>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>CHILDREN</key>
|
||||||
|
<array/>
|
||||||
|
<key>GID</key>
|
||||||
|
<integer>0</integer>
|
||||||
|
<key>PATH</key>
|
||||||
|
<string>Automator</string>
|
||||||
|
<key>PATH_TYPE</key>
|
||||||
|
<integer>0</integer>
|
||||||
|
<key>PERMISSIONS</key>
|
||||||
|
<integer>493</integer>
|
||||||
|
<key>TYPE</key>
|
||||||
|
<integer>1</integer>
|
||||||
|
<key>UID</key>
|
||||||
|
<integer>0</integer>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>CHILDREN</key>
|
||||||
|
<array/>
|
||||||
|
<key>GID</key>
|
||||||
|
<integer>0</integer>
|
||||||
|
<key>PATH</key>
|
||||||
|
<string>Documentation</string>
|
||||||
|
<key>PATH_TYPE</key>
|
||||||
|
<integer>0</integer>
|
||||||
|
<key>PERMISSIONS</key>
|
||||||
|
<integer>493</integer>
|
||||||
|
<key>TYPE</key>
|
||||||
|
<integer>1</integer>
|
||||||
|
<key>UID</key>
|
||||||
|
<integer>0</integer>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>CHILDREN</key>
|
||||||
|
<array/>
|
||||||
|
<key>GID</key>
|
||||||
|
<integer>0</integer>
|
||||||
|
<key>PATH</key>
|
||||||
|
<string>Extensions</string>
|
||||||
|
<key>PATH_TYPE</key>
|
||||||
|
<integer>0</integer>
|
||||||
|
<key>PERMISSIONS</key>
|
||||||
|
<integer>493</integer>
|
||||||
|
<key>TYPE</key>
|
||||||
|
<integer>1</integer>
|
||||||
|
<key>UID</key>
|
||||||
|
<integer>0</integer>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>CHILDREN</key>
|
||||||
|
<array/>
|
||||||
|
<key>GID</key>
|
||||||
|
<integer>0</integer>
|
||||||
|
<key>PATH</key>
|
||||||
|
<string>Filesystems</string>
|
||||||
|
<key>PATH_TYPE</key>
|
||||||
|
<integer>0</integer>
|
||||||
|
<key>PERMISSIONS</key>
|
||||||
|
<integer>493</integer>
|
||||||
|
<key>TYPE</key>
|
||||||
|
<integer>1</integer>
|
||||||
|
<key>UID</key>
|
||||||
|
<integer>0</integer>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>CHILDREN</key>
|
||||||
|
<array/>
|
||||||
|
<key>GID</key>
|
||||||
|
<integer>0</integer>
|
||||||
|
<key>PATH</key>
|
||||||
|
<string>Frameworks</string>
|
||||||
|
<key>PATH_TYPE</key>
|
||||||
|
<integer>0</integer>
|
||||||
|
<key>PERMISSIONS</key>
|
||||||
|
<integer>493</integer>
|
||||||
|
<key>TYPE</key>
|
||||||
|
<integer>1</integer>
|
||||||
|
<key>UID</key>
|
||||||
|
<integer>0</integer>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>CHILDREN</key>
|
||||||
|
<array/>
|
||||||
|
<key>GID</key>
|
||||||
|
<integer>0</integer>
|
||||||
|
<key>PATH</key>
|
||||||
|
<string>Input Methods</string>
|
||||||
|
<key>PATH_TYPE</key>
|
||||||
|
<integer>0</integer>
|
||||||
|
<key>PERMISSIONS</key>
|
||||||
|
<integer>493</integer>
|
||||||
|
<key>TYPE</key>
|
||||||
|
<integer>1</integer>
|
||||||
|
<key>UID</key>
|
||||||
|
<integer>0</integer>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>CHILDREN</key>
|
||||||
|
<array/>
|
||||||
|
<key>GID</key>
|
||||||
|
<integer>0</integer>
|
||||||
|
<key>PATH</key>
|
||||||
|
<string>Internet Plug-Ins</string>
|
||||||
|
<key>PATH_TYPE</key>
|
||||||
|
<integer>0</integer>
|
||||||
|
<key>PERMISSIONS</key>
|
||||||
|
<integer>493</integer>
|
||||||
|
<key>TYPE</key>
|
||||||
|
<integer>1</integer>
|
||||||
|
<key>UID</key>
|
||||||
|
<integer>0</integer>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>CHILDREN</key>
|
||||||
|
<array/>
|
||||||
|
<key>GID</key>
|
||||||
|
<integer>0</integer>
|
||||||
|
<key>PATH</key>
|
||||||
|
<string>LaunchAgents</string>
|
||||||
|
<key>PATH_TYPE</key>
|
||||||
|
<integer>0</integer>
|
||||||
|
<key>PERMISSIONS</key>
|
||||||
|
<integer>493</integer>
|
||||||
|
<key>TYPE</key>
|
||||||
|
<integer>1</integer>
|
||||||
|
<key>UID</key>
|
||||||
|
<integer>0</integer>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>CHILDREN</key>
|
||||||
|
<array/>
|
||||||
|
<key>GID</key>
|
||||||
|
<integer>0</integer>
|
||||||
|
<key>PATH</key>
|
||||||
|
<string>LaunchDaemons</string>
|
||||||
|
<key>PATH_TYPE</key>
|
||||||
|
<integer>0</integer>
|
||||||
|
<key>PERMISSIONS</key>
|
||||||
|
<integer>493</integer>
|
||||||
|
<key>TYPE</key>
|
||||||
|
<integer>1</integer>
|
||||||
|
<key>UID</key>
|
||||||
|
<integer>0</integer>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>CHILDREN</key>
|
||||||
|
<array/>
|
||||||
|
<key>GID</key>
|
||||||
|
<integer>0</integer>
|
||||||
|
<key>PATH</key>
|
||||||
|
<string>PreferencePanes</string>
|
||||||
|
<key>PATH_TYPE</key>
|
||||||
|
<integer>0</integer>
|
||||||
|
<key>PERMISSIONS</key>
|
||||||
|
<integer>493</integer>
|
||||||
|
<key>TYPE</key>
|
||||||
|
<integer>1</integer>
|
||||||
|
<key>UID</key>
|
||||||
|
<integer>0</integer>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>CHILDREN</key>
|
||||||
|
<array/>
|
||||||
|
<key>GID</key>
|
||||||
|
<integer>0</integer>
|
||||||
|
<key>PATH</key>
|
||||||
|
<string>Preferences</string>
|
||||||
|
<key>PATH_TYPE</key>
|
||||||
|
<integer>0</integer>
|
||||||
|
<key>PERMISSIONS</key>
|
||||||
|
<integer>493</integer>
|
||||||
|
<key>TYPE</key>
|
||||||
|
<integer>1</integer>
|
||||||
|
<key>UID</key>
|
||||||
|
<integer>0</integer>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>CHILDREN</key>
|
||||||
|
<array/>
|
||||||
|
<key>GID</key>
|
||||||
|
<integer>80</integer>
|
||||||
|
<key>PATH</key>
|
||||||
|
<string>Printers</string>
|
||||||
|
<key>PATH_TYPE</key>
|
||||||
|
<integer>0</integer>
|
||||||
|
<key>PERMISSIONS</key>
|
||||||
|
<integer>493</integer>
|
||||||
|
<key>TYPE</key>
|
||||||
|
<integer>1</integer>
|
||||||
|
<key>UID</key>
|
||||||
|
<integer>0</integer>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>CHILDREN</key>
|
||||||
|
<array/>
|
||||||
|
<key>GID</key>
|
||||||
|
<integer>0</integer>
|
||||||
|
<key>PATH</key>
|
||||||
|
<string>PrivilegedHelperTools</string>
|
||||||
|
<key>PATH_TYPE</key>
|
||||||
|
<integer>0</integer>
|
||||||
|
<key>PERMISSIONS</key>
|
||||||
|
<integer>1005</integer>
|
||||||
|
<key>TYPE</key>
|
||||||
|
<integer>1</integer>
|
||||||
|
<key>UID</key>
|
||||||
|
<integer>0</integer>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>CHILDREN</key>
|
||||||
|
<array/>
|
||||||
|
<key>GID</key>
|
||||||
|
<integer>0</integer>
|
||||||
|
<key>PATH</key>
|
||||||
|
<string>QuickLook</string>
|
||||||
|
<key>PATH_TYPE</key>
|
||||||
|
<integer>0</integer>
|
||||||
|
<key>PERMISSIONS</key>
|
||||||
|
<integer>493</integer>
|
||||||
|
<key>TYPE</key>
|
||||||
|
<integer>1</integer>
|
||||||
|
<key>UID</key>
|
||||||
|
<integer>0</integer>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>CHILDREN</key>
|
||||||
|
<array/>
|
||||||
|
<key>GID</key>
|
||||||
|
<integer>0</integer>
|
||||||
|
<key>PATH</key>
|
||||||
|
<string>QuickTime</string>
|
||||||
|
<key>PATH_TYPE</key>
|
||||||
|
<integer>0</integer>
|
||||||
|
<key>PERMISSIONS</key>
|
||||||
|
<integer>493</integer>
|
||||||
|
<key>TYPE</key>
|
||||||
|
<integer>1</integer>
|
||||||
|
<key>UID</key>
|
||||||
|
<integer>0</integer>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>CHILDREN</key>
|
||||||
|
<array/>
|
||||||
|
<key>GID</key>
|
||||||
|
<integer>0</integer>
|
||||||
|
<key>PATH</key>
|
||||||
|
<string>Screen Savers</string>
|
||||||
|
<key>PATH_TYPE</key>
|
||||||
|
<integer>0</integer>
|
||||||
|
<key>PERMISSIONS</key>
|
||||||
|
<integer>493</integer>
|
||||||
|
<key>TYPE</key>
|
||||||
|
<integer>1</integer>
|
||||||
|
<key>UID</key>
|
||||||
|
<integer>0</integer>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>CHILDREN</key>
|
||||||
|
<array/>
|
||||||
|
<key>GID</key>
|
||||||
|
<integer>0</integer>
|
||||||
|
<key>PATH</key>
|
||||||
|
<string>Scripts</string>
|
||||||
|
<key>PATH_TYPE</key>
|
||||||
|
<integer>0</integer>
|
||||||
|
<key>PERMISSIONS</key>
|
||||||
|
<integer>493</integer>
|
||||||
|
<key>TYPE</key>
|
||||||
|
<integer>1</integer>
|
||||||
|
<key>UID</key>
|
||||||
|
<integer>0</integer>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>CHILDREN</key>
|
||||||
|
<array/>
|
||||||
|
<key>GID</key>
|
||||||
|
<integer>0</integer>
|
||||||
|
<key>PATH</key>
|
||||||
|
<string>Services</string>
|
||||||
|
<key>PATH_TYPE</key>
|
||||||
|
<integer>0</integer>
|
||||||
|
<key>PERMISSIONS</key>
|
||||||
|
<integer>493</integer>
|
||||||
|
<key>TYPE</key>
|
||||||
|
<integer>1</integer>
|
||||||
|
<key>UID</key>
|
||||||
|
<integer>0</integer>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>CHILDREN</key>
|
||||||
|
<array/>
|
||||||
|
<key>GID</key>
|
||||||
|
<integer>0</integer>
|
||||||
|
<key>PATH</key>
|
||||||
|
<string>Widgets</string>
|
||||||
|
<key>PATH_TYPE</key>
|
||||||
|
<integer>0</integer>
|
||||||
|
<key>PERMISSIONS</key>
|
||||||
|
<integer>493</integer>
|
||||||
|
<key>TYPE</key>
|
||||||
|
<integer>1</integer>
|
||||||
|
<key>UID</key>
|
||||||
|
<integer>0</integer>
|
||||||
|
</dict>
|
||||||
|
</array>
|
||||||
|
<key>GID</key>
|
||||||
|
<integer>0</integer>
|
||||||
|
<key>PATH</key>
|
||||||
|
<string>Library</string>
|
||||||
|
<key>PATH_TYPE</key>
|
||||||
|
<integer>0</integer>
|
||||||
|
<key>PERMISSIONS</key>
|
||||||
|
<integer>493</integer>
|
||||||
|
<key>TYPE</key>
|
||||||
|
<integer>1</integer>
|
||||||
|
<key>UID</key>
|
||||||
|
<integer>0</integer>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>CHILDREN</key>
|
||||||
|
<array>
|
||||||
|
<dict>
|
||||||
|
<key>CHILDREN</key>
|
||||||
|
<array/>
|
||||||
|
<key>GID</key>
|
||||||
|
<integer>0</integer>
|
||||||
|
<key>PATH</key>
|
||||||
|
<string>Shared</string>
|
||||||
|
<key>PATH_TYPE</key>
|
||||||
|
<integer>0</integer>
|
||||||
|
<key>PERMISSIONS</key>
|
||||||
|
<integer>1023</integer>
|
||||||
|
<key>TYPE</key>
|
||||||
|
<integer>1</integer>
|
||||||
|
<key>UID</key>
|
||||||
|
<integer>0</integer>
|
||||||
|
</dict>
|
||||||
|
</array>
|
||||||
|
<key>GID</key>
|
||||||
|
<integer>80</integer>
|
||||||
|
<key>PATH</key>
|
||||||
|
<string>Users</string>
|
||||||
|
<key>PATH_TYPE</key>
|
||||||
|
<integer>0</integer>
|
||||||
|
<key>PERMISSIONS</key>
|
||||||
|
<integer>493</integer>
|
||||||
|
<key>TYPE</key>
|
||||||
|
<integer>1</integer>
|
||||||
|
<key>UID</key>
|
||||||
|
<integer>0</integer>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>CHILDREN</key>
|
||||||
|
<array/>
|
||||||
|
<key>GID</key>
|
||||||
|
<integer>80</integer>
|
||||||
|
<key>PATH</key>
|
||||||
|
<string>Applications</string>
|
||||||
|
<key>PATH_TYPE</key>
|
||||||
|
<integer>0</integer>
|
||||||
|
<key>PERMISSIONS</key>
|
||||||
|
<integer>509</integer>
|
||||||
|
<key>TYPE</key>
|
||||||
|
<integer>1</integer>
|
||||||
|
<key>UID</key>
|
||||||
|
<integer>0</integer>
|
||||||
|
</dict>
|
||||||
|
</array>
|
||||||
|
<key>GID</key>
|
||||||
|
<integer>0</integer>
|
||||||
|
<key>PATH</key>
|
||||||
|
<string>/</string>
|
||||||
|
<key>PATH_TYPE</key>
|
||||||
|
<integer>0</integer>
|
||||||
|
<key>PERMISSIONS</key>
|
||||||
|
<integer>493</integer>
|
||||||
|
<key>TYPE</key>
|
||||||
|
<integer>1</integer>
|
||||||
|
<key>UID</key>
|
||||||
|
<integer>0</integer>
|
||||||
|
</dict>
|
||||||
|
<key>PAYLOAD_TYPE</key>
|
||||||
|
<integer>0</integer>
|
||||||
|
<key>PRESERVE_EXTENDED_ATTRIBUTES</key>
|
||||||
|
<false/>
|
||||||
|
<key>SHOW_INVISIBLE</key>
|
||||||
|
<false/>
|
||||||
|
<key>SPLIT_FORKS</key>
|
||||||
|
<true/>
|
||||||
|
<key>TREAT_MISSING_FILES_AS_WARNING</key>
|
||||||
|
<false/>
|
||||||
|
<key>VERSION</key>
|
||||||
|
<integer>5</integer>
|
||||||
|
</dict>
|
||||||
|
<key>PACKAGE_SCRIPTS</key>
|
||||||
|
<dict>
|
||||||
|
<key>POSTINSTALL_PATH</key>
|
||||||
|
<dict>
|
||||||
|
<key>PATH_TYPE</key>
|
||||||
|
<integer>0</integer>
|
||||||
|
</dict>
|
||||||
|
<key>PREINSTALL_PATH</key>
|
||||||
|
<dict>
|
||||||
|
<key>PATH_TYPE</key>
|
||||||
|
<integer>0</integer>
|
||||||
|
</dict>
|
||||||
|
<key>RESOURCES</key>
|
||||||
|
<array/>
|
||||||
|
</dict>
|
||||||
|
<key>PACKAGE_SETTINGS</key>
|
||||||
|
<dict>
|
||||||
|
<key>AUTHENTICATION</key>
|
||||||
|
<integer>1</integer>
|
||||||
|
<key>CONCLUSION_ACTION</key>
|
||||||
|
<integer>0</integer>
|
||||||
|
<key>FOLLOW_SYMBOLIC_LINKS</key>
|
||||||
|
<false/>
|
||||||
|
<key>IDENTIFIER</key>
|
||||||
|
<string>com.warmuptill.SceneSwitcher</string>
|
||||||
|
<key>LOCATION</key>
|
||||||
|
<integer>0</integer>
|
||||||
|
<key>NAME</key>
|
||||||
|
<string></string>
|
||||||
|
<key>OVERWRITE_PERMISSIONS</key>
|
||||||
|
<false/>
|
||||||
|
<key>PAYLOAD_SIZE</key>
|
||||||
|
<integer>-1</integer>
|
||||||
|
<key>REFERENCE_PATH</key>
|
||||||
|
<string></string>
|
||||||
|
<key>RELOCATABLE</key>
|
||||||
|
<false/>
|
||||||
|
<key>USE_HFS+_COMPRESSION</key>
|
||||||
|
<false/>
|
||||||
|
<key>VERSION</key>
|
||||||
|
<string>0.0.2</string>
|
||||||
|
</dict>
|
||||||
|
<key>PROJECT_COMMENTS</key>
|
||||||
|
<dict>
|
||||||
|
<key>NOTES</key>
|
||||||
|
<data>
|
||||||
|
PCFET0NUWVBFIGh0bWwgUFVCTElDICItLy9XM0MvL0RURCBIVE1M
|
||||||
|
IDQuMDEvL0VOIiAiaHR0cDovL3d3dy53My5vcmcvVFIvaHRtbDQv
|
||||||
|
c3RyaWN0LmR0ZCI+CjxodG1sPgo8aGVhZD4KPG1ldGEgaHR0cC1l
|
||||||
|
cXVpdj0iQ29udGVudC1UeXBlIiBjb250ZW50PSJ0ZXh0L2h0bWw7
|
||||||
|
IGNoYXJzZXQ9VVRGLTgiPgo8bWV0YSBodHRwLWVxdWl2PSJDb250
|
||||||
|
ZW50LVN0eWxlLVR5cGUiIGNvbnRlbnQ9InRleHQvY3NzIj4KPHRp
|
||||||
|
dGxlPjwvdGl0bGU+CjxtZXRhIG5hbWU9IkdlbmVyYXRvciIgY29u
|
||||||
|
dGVudD0iQ29jb2EgSFRNTCBXcml0ZXIiPgo8bWV0YSBuYW1lPSJD
|
||||||
|
b2NvYVZlcnNpb24iIGNvbnRlbnQ9IjE0MDQuMTMiPgo8c3R5bGUg
|
||||||
|
dHlwZT0idGV4dC9jc3MiPgo8L3N0eWxlPgo8L2hlYWQ+Cjxib2R5
|
||||||
|
Pgo8L2JvZHk+CjwvaHRtbD4K
|
||||||
|
</data>
|
||||||
|
</dict>
|
||||||
|
<key>PROJECT_SETTINGS</key>
|
||||||
|
<dict>
|
||||||
|
<key>BUILD_PATH</key>
|
||||||
|
<dict>
|
||||||
|
<key>PATH</key>
|
||||||
|
<string>../..</string>
|
||||||
|
<key>PATH_TYPE</key>
|
||||||
|
<integer>1</integer>
|
||||||
|
</dict>
|
||||||
|
<key>EXCLUDED_FILES</key>
|
||||||
|
<array>
|
||||||
|
<dict>
|
||||||
|
<key>PATTERNS_ARRAY</key>
|
||||||
|
<array>
|
||||||
|
<dict>
|
||||||
|
<key>REGULAR_EXPRESSION</key>
|
||||||
|
<false/>
|
||||||
|
<key>STRING</key>
|
||||||
|
<string>.DS_Store</string>
|
||||||
|
<key>TYPE</key>
|
||||||
|
<integer>0</integer>
|
||||||
|
</dict>
|
||||||
|
</array>
|
||||||
|
<key>PROTECTED</key>
|
||||||
|
<true/>
|
||||||
|
<key>PROXY_NAME</key>
|
||||||
|
<string>Remove .DS_Store files</string>
|
||||||
|
<key>PROXY_TOOLTIP</key>
|
||||||
|
<string>Remove ".DS_Store" files created by the Finder.</string>
|
||||||
|
<key>STATE</key>
|
||||||
|
<true/>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>PATTERNS_ARRAY</key>
|
||||||
|
<array>
|
||||||
|
<dict>
|
||||||
|
<key>REGULAR_EXPRESSION</key>
|
||||||
|
<false/>
|
||||||
|
<key>STRING</key>
|
||||||
|
<string>.pbdevelopment</string>
|
||||||
|
<key>TYPE</key>
|
||||||
|
<integer>0</integer>
|
||||||
|
</dict>
|
||||||
|
</array>
|
||||||
|
<key>PROTECTED</key>
|
||||||
|
<true/>
|
||||||
|
<key>PROXY_NAME</key>
|
||||||
|
<string>Remove .pbdevelopment files</string>
|
||||||
|
<key>PROXY_TOOLTIP</key>
|
||||||
|
<string>Remove ".pbdevelopment" files created by ProjectBuilder or Xcode.</string>
|
||||||
|
<key>STATE</key>
|
||||||
|
<true/>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>PATTERNS_ARRAY</key>
|
||||||
|
<array>
|
||||||
|
<dict>
|
||||||
|
<key>REGULAR_EXPRESSION</key>
|
||||||
|
<false/>
|
||||||
|
<key>STRING</key>
|
||||||
|
<string>CVS</string>
|
||||||
|
<key>TYPE</key>
|
||||||
|
<integer>1</integer>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>REGULAR_EXPRESSION</key>
|
||||||
|
<false/>
|
||||||
|
<key>STRING</key>
|
||||||
|
<string>.cvsignore</string>
|
||||||
|
<key>TYPE</key>
|
||||||
|
<integer>0</integer>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>REGULAR_EXPRESSION</key>
|
||||||
|
<false/>
|
||||||
|
<key>STRING</key>
|
||||||
|
<string>.cvspass</string>
|
||||||
|
<key>TYPE</key>
|
||||||
|
<integer>0</integer>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>REGULAR_EXPRESSION</key>
|
||||||
|
<false/>
|
||||||
|
<key>STRING</key>
|
||||||
|
<string>.svn</string>
|
||||||
|
<key>TYPE</key>
|
||||||
|
<integer>1</integer>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>REGULAR_EXPRESSION</key>
|
||||||
|
<false/>
|
||||||
|
<key>STRING</key>
|
||||||
|
<string>.git</string>
|
||||||
|
<key>TYPE</key>
|
||||||
|
<integer>1</integer>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>REGULAR_EXPRESSION</key>
|
||||||
|
<false/>
|
||||||
|
<key>STRING</key>
|
||||||
|
<string>.gitignore</string>
|
||||||
|
<key>TYPE</key>
|
||||||
|
<integer>0</integer>
|
||||||
|
</dict>
|
||||||
|
</array>
|
||||||
|
<key>PROTECTED</key>
|
||||||
|
<true/>
|
||||||
|
<key>PROXY_NAME</key>
|
||||||
|
<string>Remove SCM metadata</string>
|
||||||
|
<key>PROXY_TOOLTIP</key>
|
||||||
|
<string>Remove helper files and folders used by the CVS, SVN or Git Source Code Management systems.</string>
|
||||||
|
<key>STATE</key>
|
||||||
|
<true/>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>PATTERNS_ARRAY</key>
|
||||||
|
<array>
|
||||||
|
<dict>
|
||||||
|
<key>REGULAR_EXPRESSION</key>
|
||||||
|
<false/>
|
||||||
|
<key>STRING</key>
|
||||||
|
<string>classes.nib</string>
|
||||||
|
<key>TYPE</key>
|
||||||
|
<integer>0</integer>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>REGULAR_EXPRESSION</key>
|
||||||
|
<false/>
|
||||||
|
<key>STRING</key>
|
||||||
|
<string>designable.db</string>
|
||||||
|
<key>TYPE</key>
|
||||||
|
<integer>0</integer>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>REGULAR_EXPRESSION</key>
|
||||||
|
<false/>
|
||||||
|
<key>STRING</key>
|
||||||
|
<string>info.nib</string>
|
||||||
|
<key>TYPE</key>
|
||||||
|
<integer>0</integer>
|
||||||
|
</dict>
|
||||||
|
</array>
|
||||||
|
<key>PROTECTED</key>
|
||||||
|
<true/>
|
||||||
|
<key>PROXY_NAME</key>
|
||||||
|
<string>Optimize nib files</string>
|
||||||
|
<key>PROXY_TOOLTIP</key>
|
||||||
|
<string>Remove "classes.nib", "info.nib" and "designable.nib" files within .nib bundles.</string>
|
||||||
|
<key>STATE</key>
|
||||||
|
<true/>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>PATTERNS_ARRAY</key>
|
||||||
|
<array>
|
||||||
|
<dict>
|
||||||
|
<key>REGULAR_EXPRESSION</key>
|
||||||
|
<false/>
|
||||||
|
<key>STRING</key>
|
||||||
|
<string>Resources Disabled</string>
|
||||||
|
<key>TYPE</key>
|
||||||
|
<integer>1</integer>
|
||||||
|
</dict>
|
||||||
|
</array>
|
||||||
|
<key>PROTECTED</key>
|
||||||
|
<true/>
|
||||||
|
<key>PROXY_NAME</key>
|
||||||
|
<string>Remove Resources Disabled folders</string>
|
||||||
|
<key>PROXY_TOOLTIP</key>
|
||||||
|
<string>Remove "Resources Disabled" folders.</string>
|
||||||
|
<key>STATE</key>
|
||||||
|
<true/>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>SEPARATOR</key>
|
||||||
|
<true/>
|
||||||
|
</dict>
|
||||||
|
</array>
|
||||||
|
<key>NAME</key>
|
||||||
|
<string>SceneSwitcher</string>
|
||||||
|
<key>PAYLOAD_ONLY</key>
|
||||||
|
<false/>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
<key>TYPE</key>
|
||||||
|
<integer>1</integer>
|
||||||
|
<key>VERSION</key>
|
||||||
|
<integer>2</integer>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
||||||
305
CMakeLists.txt
305
CMakeLists.txt
@@ -1,77 +1,234 @@
|
|||||||
|
cmake_minimum_required(VERSION 3.5)
|
||||||
|
|
||||||
project(advanced-scene-switcher)
|
project(advanced-scene-switcher)
|
||||||
|
|
||||||
if(APPLE)
|
if(BUILD_OUT_OF_TREE)
|
||||||
find_library(COCOA Cocoa)
|
# out of tree builds
|
||||||
include_directories(${COCOA})
|
# credits to c3r1c3
|
||||||
|
set(CMAKE_PREFIX_PATH "${QTDIR}")
|
||||||
|
set(CMAKE_INCLUDE_CURRENT_DIR ON)
|
||||||
|
list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/external")
|
||||||
|
|
||||||
|
include(external/FindLibObs.cmake)
|
||||||
|
find_package(LibObs REQUIRED)
|
||||||
|
find_package(Qt5Core REQUIRED)
|
||||||
|
find_package(Qt5Widgets REQUIRED)
|
||||||
|
find_package(Libcurl REQUIRED)
|
||||||
|
find_path(LIBOBS_FRONTEND_INCLUDE_DIR HINTS ${LIBOBS_INCLUDE_DIRS})
|
||||||
|
find_file(LIBOBS_FRONTEND_API_LIB NAMES libobs-frontend-api.* HINTS ${LIBOBS_LIB} )
|
||||||
|
|
||||||
|
if(APPLE)
|
||||||
|
find_library(COCOA Cocoa)
|
||||||
|
find_package(Qt5MacExtras REQUIRED)
|
||||||
|
include_directories(${COCOA})
|
||||||
|
add_link_options("-v")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(UNIX AND NOT APPLE)
|
||||||
|
find_package(X11 REQUIRED)
|
||||||
|
link_libraries(${X11_LIBRARIES})
|
||||||
|
include_directories(${X11_INCLUDE_DIR})
|
||||||
|
endif()
|
||||||
|
|
||||||
|
set(advanced-scene-switcher_HEADERS
|
||||||
|
${advanced-scene-switcher_HEADERS}
|
||||||
|
src/headers/advanced-scene-switcher.hpp
|
||||||
|
src/headers/switcher-data-structs.hpp
|
||||||
|
src/headers/utility.hpp
|
||||||
|
)
|
||||||
|
|
||||||
|
set(advanced-scene-switcher_SOURCES
|
||||||
|
${advanced-scene-switcher_SOURCES}
|
||||||
|
src/advanced-scene-switcher.cpp
|
||||||
|
src/advanced-scene-switcher-module.c
|
||||||
|
src/scene-transitions.cpp
|
||||||
|
src/screen-region-switch.cpp
|
||||||
|
src/priority.cpp
|
||||||
|
src/executable-switch.cpp
|
||||||
|
src/idle-switch.cpp
|
||||||
|
src/scene-round-trip.cpp
|
||||||
|
src/file-switch.cpp
|
||||||
|
src/window-title-switch.cpp
|
||||||
|
src/media-switch.cpp
|
||||||
|
src/hotkey.cpp
|
||||||
|
src/general.cpp
|
||||||
|
src/pause-switch.cpp
|
||||||
|
src/random.cpp
|
||||||
|
src/time-switch.cpp
|
||||||
|
)
|
||||||
|
|
||||||
|
set(advanced-scene-switcher_UI
|
||||||
|
${advanced-scene-switcher_UI}
|
||||||
|
forms/advanced-scene-switcher.ui
|
||||||
|
)
|
||||||
|
|
||||||
|
if(WIN32)
|
||||||
|
set(advanced-scene-switcher_PLATFORM_SOURCES
|
||||||
|
src/win/advanced-scene-switcher-win.cpp)
|
||||||
|
|
||||||
|
elseif(APPLE)
|
||||||
|
set(advanced-scene-switcher_PLATFORM_SOURCES
|
||||||
|
src/osx/advanced-scene-switcher-osx.mm)
|
||||||
|
set_source_files_properties(advanced-scene-switcher-osx.mm
|
||||||
|
PROPERTIES COMPILE_FLAGS "-fobjc-arc")
|
||||||
|
set(advanced-scene-switcher_PLATFORM_LIBS
|
||||||
|
${COCOA})
|
||||||
|
|
||||||
|
elseif("${CMAKE_SYSTEM_NAME}" MATCHES "Linux")
|
||||||
|
set(advanced-scene-switcher_PLATFORM_SOURCES
|
||||||
|
src/linux/advanced-scene-switcher-nix.cpp)
|
||||||
|
set(advanced-scene-switcher_PLATFORM_LIBS
|
||||||
|
Xss)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
qt5_wrap_ui(advanced-scene-switcher_UI_HEADERS
|
||||||
|
${advanced-scene-switcher_UI}
|
||||||
|
${advanced-scene-switcher_PLATFORM_UI})
|
||||||
|
|
||||||
|
add_library(advanced-scene-switcher MODULE
|
||||||
|
${advanced-scene-switcher_HEADERS}
|
||||||
|
${advanced-scene-switcher_SOURCES}
|
||||||
|
${advanced-scene-switcher_UI_HEADERS}
|
||||||
|
${advanced-scene-switcher_PLATFORM_SOURCES}
|
||||||
|
${advanced-scene-switcher_PLATFORM_HEADERS}
|
||||||
|
)
|
||||||
|
|
||||||
|
include_directories(
|
||||||
|
"${LIBOBS_INCLUDE_DIRS}"
|
||||||
|
"${LIBOBS_FRONTEND_INCLUDE_DIR}"
|
||||||
|
${Qt5Core_INCLUDES}
|
||||||
|
${Qt5Widgets_INCLUDES}
|
||||||
|
${LIBCURL_INCLUDE_DIRS})
|
||||||
|
|
||||||
|
target_link_libraries(advanced-scene-switcher
|
||||||
|
${advanced-scene-switcher_PLATFORM_LIBS}
|
||||||
|
${LIBOBS_LIB}
|
||||||
|
${LIBOBS_FRONTEND_API_LIB}
|
||||||
|
Qt5::Core
|
||||||
|
Qt5::Widgets
|
||||||
|
${LIBCURL_LIBRARIES})
|
||||||
|
|
||||||
|
# Additional commands to install the module in the correct place.
|
||||||
|
# Find all the translation files so we can copy them to the correct
|
||||||
|
# place later on.
|
||||||
|
file(GLOB ASS_TRANSLATION_FILES
|
||||||
|
"data/locale/*.ini"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Windows
|
||||||
|
if(WIN32)
|
||||||
|
string(REGEX REPLACE "/RTC(su|[1su])" "" CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG}")
|
||||||
|
message("${CMAKE_CXX_FLAGS_RELEASE}")
|
||||||
|
|
||||||
|
find_package(w32-pthreads REQUIRED)
|
||||||
|
|
||||||
|
target_link_libraries(advanced-scene-switcher
|
||||||
|
w32-pthreads)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# OSX
|
||||||
|
if(APPLE)
|
||||||
|
set_target_properties(advanced-scene-switcher PROPERTIES PREFIX "")
|
||||||
|
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Linux
|
||||||
|
if(UNIX AND NOT APPLE)
|
||||||
|
set(CMAKE_C_FLAGS "-Wall -Wextra -Wvla -Wno-unused-function -Werror-implicit-function-declaration -Wno-missing-braces -Wno-missing-field-initializers ${CMAKE_C_FLAGS} -std=c99 -fno-strict-aliasing")
|
||||||
|
|
||||||
|
if(ARCH EQUAL 64)
|
||||||
|
set(ARCH_NAME "x86_64")
|
||||||
|
else()
|
||||||
|
set(ARCH_NAME "i686")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
set_target_properties(advanced-scene-switcher PROPERTIES PREFIX "")
|
||||||
|
|
||||||
|
install(TARGETS advanced-scene-switcher
|
||||||
|
LIBRARY DESTINATION ${CMAKE_INSTALL_PREFIX}/lib/obs-plugins)
|
||||||
|
install(FILES ${ASS_TRANSLATION_FILES}
|
||||||
|
DESTINATION "${CMAKE_INSTALL_PREFIX}/share/obs/obs-plugins/advanced-scene-switcher/locale")
|
||||||
|
endif()
|
||||||
|
else ()
|
||||||
|
# in-tree build
|
||||||
|
if(APPLE)
|
||||||
|
find_library(COCOA Cocoa)
|
||||||
|
include_directories(${COCOA})
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(UNIX AND NOT APPLE)
|
||||||
|
find_package(X11 REQUIRED)
|
||||||
|
link_libraries(${X11_LIBRARIES})
|
||||||
|
include_directories(${X11_INCLUDE_DIR})
|
||||||
|
endif()
|
||||||
|
|
||||||
|
set(advanced-scene-switcher_HEADERS
|
||||||
|
${advanced-scene-switcher_HEADERS}
|
||||||
|
src/headers/advanced-scene-switcher.hpp
|
||||||
|
src/headers/switcher-data-structs.hpp
|
||||||
|
src/headers/utility.hpp
|
||||||
|
)
|
||||||
|
|
||||||
|
set(advanced-scene-switcher_SOURCES
|
||||||
|
${advanced-scene-switcher_SOURCES}
|
||||||
|
src/advanced-scene-switcher.cpp
|
||||||
|
src/advanced-scene-switcher-module.c
|
||||||
|
src/scene-transitions.cpp
|
||||||
|
src/screen-region-switch.cpp
|
||||||
|
src/priority.cpp
|
||||||
|
src/executable-switch.cpp
|
||||||
|
src/idle-switch.cpp
|
||||||
|
src/scene-round-trip.cpp
|
||||||
|
src/file-switch.cpp
|
||||||
|
src/window-title-switch.cpp
|
||||||
|
src/media-switch.cpp
|
||||||
|
src/hotkey.cpp
|
||||||
|
src/general.cpp
|
||||||
|
src/pause-switch.cpp
|
||||||
|
src/random.cpp
|
||||||
|
src/time-switch.cpp
|
||||||
|
)
|
||||||
|
|
||||||
|
set(advanced-scene-switcher_UI
|
||||||
|
${advanced-scene-switcher_UI}
|
||||||
|
forms/advanced-scene-switcher.ui
|
||||||
|
)
|
||||||
|
|
||||||
|
if(WIN32)
|
||||||
|
set(advanced-scene-switcher_PLATFORM_SOURCES
|
||||||
|
src/win/advanced-scene-switcher-win.cpp)
|
||||||
|
elseif(APPLE)
|
||||||
|
set(advanced-scene-switcher_PLATFORM_SOURCES
|
||||||
|
src/osx/advanced-scene-switcher-osx.mm)
|
||||||
|
set_source_files_properties(advanced-scene-switcher-osx.mm
|
||||||
|
PROPERTIES COMPILE_FLAGS "-fobjc-arc")
|
||||||
|
set(advanced-scene-switcher_PLATFORM_LIBS
|
||||||
|
${COCOA})
|
||||||
|
else()
|
||||||
|
set(advanced-scene-switcher_PLATFORM_SOURCES
|
||||||
|
src/linux/advanced-scene-switcher-nix.cpp)
|
||||||
|
set(advanced-scene-switcher_PLATFORM_LIBS
|
||||||
|
Xss)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
qt5_wrap_ui(advanced-scene-switcher_UI_HEADERS
|
||||||
|
${advanced-scene-switcher_UI}
|
||||||
|
${advanced-scene-switcher_PLATFORM_UI})
|
||||||
|
|
||||||
|
add_library(advanced-scene-switcher MODULE
|
||||||
|
${advanced-scene-switcher_HEADERS}
|
||||||
|
${advanced-scene-switcher_SOURCES}
|
||||||
|
${advanced-scene-switcher_UI_HEADERS}
|
||||||
|
${advanced-scene-switcher_PLATFORM_SOURCES}
|
||||||
|
${advanced-scene-switcher_PLATFORM_HEADERS}
|
||||||
|
)
|
||||||
|
|
||||||
|
target_link_libraries(advanced-scene-switcher
|
||||||
|
${advanced-scene-switcher_PLATFORM_LIBS}
|
||||||
|
obs-frontend-api
|
||||||
|
Qt5::Widgets
|
||||||
|
libobs
|
||||||
|
${LIBCURL_LIBRARIES})
|
||||||
|
|
||||||
|
install_obs_plugin(advanced-scene-switcher data)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(UNIX)
|
|
||||||
find_package(X11 REQUIRED)
|
|
||||||
link_libraries(${X11_LIBRARIES})
|
|
||||||
include_directories(${X11_INCLUDE_DIR})
|
|
||||||
endif()
|
|
||||||
|
|
||||||
set(advanced-scene-switcher_HEADERS
|
|
||||||
${advanced-scene-switcher_HEADERS}
|
|
||||||
advanced-scene-switcher.hpp
|
|
||||||
switcher-data-structs.hpp
|
|
||||||
utility.hpp
|
|
||||||
)
|
|
||||||
set(advanced-scene-switcher_SOURCES
|
|
||||||
${advanced-scene-switcher_SOURCES}
|
|
||||||
advanced-scene-switcher.cpp
|
|
||||||
advanced-scene-switcher-module.c
|
|
||||||
scene-transitions.cpp
|
|
||||||
screen-region-switch.cpp
|
|
||||||
priority.cpp
|
|
||||||
executable-switch.cpp
|
|
||||||
idle-switch.cpp
|
|
||||||
scene-round-trip.cpp
|
|
||||||
file-switch.cpp
|
|
||||||
window-title-switch.cpp
|
|
||||||
hotkey.cpp
|
|
||||||
general.cpp
|
|
||||||
pause-switch.cpp
|
|
||||||
)
|
|
||||||
set(advanced-scene-switcher_UI
|
|
||||||
${advanced-scene-switcher_UI}
|
|
||||||
forms/advanced-scene-switcher.ui
|
|
||||||
)
|
|
||||||
|
|
||||||
if(WIN32)
|
|
||||||
set(advanced-scene-switcher_PLATFORM_SOURCES
|
|
||||||
advanced-scene-switcher-win.cpp)
|
|
||||||
|
|
||||||
elseif(APPLE)
|
|
||||||
set(advanced-scene-switcher_PLATFORM_SOURCES
|
|
||||||
advanced-scene-switcher-osx.mm)
|
|
||||||
set_source_files_properties(advanced-scene-switcher-osx.mm
|
|
||||||
PROPERTIES COMPILE_FLAGS "-fobjc-arc")
|
|
||||||
|
|
||||||
set(advanced-scene-switcher_PLATFORM_LIBS
|
|
||||||
${COCOA})
|
|
||||||
else()
|
|
||||||
set(advanced-scene-switcher_PLATFORM_SOURCES
|
|
||||||
advanced-scene-switcher-nix.cpp)
|
|
||||||
set(advanced-scene-switcher_PLATFORM_LIBS
|
|
||||||
Xss)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
qt5_wrap_ui(advanced-scene-switcher_UI_HEADERS
|
|
||||||
${advanced-scene-switcher_UI}
|
|
||||||
${advanced-scene-switcher_PLATFORM_UI})
|
|
||||||
|
|
||||||
add_library(advanced-scene-switcher MODULE
|
|
||||||
${advanced-scene-switcher_HEADERS}
|
|
||||||
${advanced-scene-switcher_SOURCES}
|
|
||||||
${advanced-scene-switcher_UI_HEADERS}
|
|
||||||
${advanced-scene-switcher_PLATFORM_SOURCES}
|
|
||||||
${advanced-scene-switcher_PLATFORM_HEADERS}
|
|
||||||
)
|
|
||||||
target_link_libraries(advanced-scene-switcher
|
|
||||||
${advanced-scene-switcher_PLATFORM_LIBS}
|
|
||||||
obs-frontend-api
|
|
||||||
Qt5::Widgets
|
|
||||||
libobs)
|
|
||||||
|
|
||||||
install_obs_plugin_with_data(advanced-scene-switcher data)
|
|
||||||
|
|||||||
114
README.md
114
README.md
@@ -2,3 +2,117 @@
|
|||||||
An automated scene switcher for OBS Studio
|
An automated scene switcher for OBS Studio
|
||||||
|
|
||||||
More information can be found on https://obsproject.com/forum/resources/automatic-scene-switching.395/
|
More information can be found on https://obsproject.com/forum/resources/automatic-scene-switching.395/
|
||||||
|
|
||||||
|
## Compiling in tree
|
||||||
|
Add the "SceneSwitcher" source directory to your obs-studio source directory under obs-studio/UI/frontend-plugins/:
|
||||||
|
```
|
||||||
|
cd obs-studio/UI/frontend-plugins/
|
||||||
|
git clone https://github.com/WarmUpTill/SceneSwitcher.git
|
||||||
|
```
|
||||||
|
|
||||||
|
Then modify the obs-studio/UI/frontend-plugins/CMakeLists.txt file and add an entry for the scene switcher:
|
||||||
|
add_subdirectory(SceneSwitcher)
|
||||||
|
|
||||||
|
## Compiling out of tree
|
||||||
|
### Prerequisites
|
||||||
|
You'll need CMake and a working development environment for OBS Studio installed
|
||||||
|
on your computer. Once you've done that, do the following:
|
||||||
|
```
|
||||||
|
git clone https://github.com/WarmUpTill/SceneSwitcher.git
|
||||||
|
cd SceneSwitcher
|
||||||
|
mkdir build && cd build
|
||||||
|
```
|
||||||
|
|
||||||
|
### Windows
|
||||||
|
In cmake-gui, you'll have to set these CMake variables :
|
||||||
|
- **BUILD_OUT_OF_TREE** (bool) : true
|
||||||
|
- **LIBOBS_INCLUDE_DIR** (path) : location of the libobs subfolder in the source
|
||||||
|
code of OBS Studio, located at [source_directory]/libobs/.
|
||||||
|
- **LIBOBS_LIB** (filepath) : location of the obs.dll file (usually set to
|
||||||
|
C:\Program Files\obs-studio\bin\64bit\obs.dll)
|
||||||
|
|
||||||
|
Assuming that you installed Qt via the regular Qt App way:
|
||||||
|
- **Qt5Core_DIR** (path) : C:/Qt/5.10.1/msvc2017_64/lib/cmake/Qt5Core
|
||||||
|
- **Qt5Gui_DIR** (path): C:/Qt/5.10.1/msvc2017_64/lib/cmake/Qt5Gui
|
||||||
|
- **Qt5Widgets_DIR** (path) : C:/Qt/5.10.1/msvc2017_64/lib/cmake/Qt5Widgets
|
||||||
|
|
||||||
|
- **LIBOBS_FRONTEND_API_LIB** (filepath) : location of the libobs-frontend-api.dll
|
||||||
|
file, usually C:/Program Files/obs-studio/bin/64bit/obs-frontend-api.dll
|
||||||
|
(usually in the same place as LIBOBS_LIB)
|
||||||
|
- **LIBOBS_FRONTEND_INCLUDE_DIR** (path) : location of the obs-frontend-api
|
||||||
|
subfolder in the source code of OBS Studio, located at [source_directory]/UI/obs-frontend-api.
|
||||||
|
|
||||||
|
Just keep hitting configure until all the vars are filled out. Then hit generate.
|
||||||
|
|
||||||
|
### Linux
|
||||||
|
Most versions of Linux you can use cmake-gui or the command line.
|
||||||
|
|
||||||
|
**For the command line:**
|
||||||
|
```
|
||||||
|
cmake -DBUILD_OUT_OF_TREE=1 -DLIBOBS_INCLUDE_DIR="<path to the libobs/ sub-folder in obs-studio's source code>"
|
||||||
|
-DLIBOBS_FRONTEND_INCLUDE_DIR="<path to the UI/obs-frontend-api/ sub-folder in obs-studio's source code>"
|
||||||
|
-DLIBOBS_FRONTEND_API_LIB="< path to the libobs-frontend-api.so file (usually in the same place as LIBOBS_LIB)>"
|
||||||
|
-DCMAKE_INSTALL_PREFIX=/usr ..
|
||||||
|
make -j4
|
||||||
|
sudo make install
|
||||||
|
```
|
||||||
|
|
||||||
|
For cmake-gui you'll have to set the following variables:
|
||||||
|
- **BUILD_OUT_OF_TREE** (bool) : true
|
||||||
|
- **LIBOBS_INCLUDE_DIR** (path) : location of the libobs subfolder in the source
|
||||||
|
code of OBS Studio, located at [source_directory]/libobs/.
|
||||||
|
- **LIBOBS_LIB** (filepath) : location of the libobs.so file (usually CMake finds
|
||||||
|
this, but if not it'll usually be in /usr/lib/libobs.so)
|
||||||
|
|
||||||
|
Assuming that you installed Qt via your system package manager, it should be
|
||||||
|
found automatically. If not, then usually you'll find it in something like:
|
||||||
|
- **Qt5Core_DIR** (path) : /usr/lib64/cmake/Qt5Core
|
||||||
|
- **Qt5Gui_DIR** (path): /usr/lib64/cmake/Qt5Gui
|
||||||
|
- **Qt5Widgets_DIR** (path) : /usr/lib64/cmake/Qt5Widgets
|
||||||
|
|
||||||
|
- **LIBOBS_FRONTEND_API_LIB** (filepath) : location of the libobs-frontend-api.so
|
||||||
|
file (usually in the same place as LIBOBS_LIB)
|
||||||
|
- **LIBOBS_FRONTEND_INCLUDE_DIR** (path) : location of the obs-frontend-api
|
||||||
|
subfolder in the source code of OBS Studio, located at
|
||||||
|
[source_directory]/UI/obs-frontend-api.
|
||||||
|
|
||||||
|
Just keep hitting configure until all the vars are filled out. Then hit generate.
|
||||||
|
|
||||||
|
Then open a terminal in the build folder and type:
|
||||||
|
```
|
||||||
|
make -j4
|
||||||
|
sudo make install
|
||||||
|
```
|
||||||
|
NOTE: The Linux version of this plugin is dependent on libXScrnSaver.
|
||||||
|
|
||||||
|
### OS X
|
||||||
|
In cmake-gui, you'll have to set these CMake variables :
|
||||||
|
- **BUILD_OUT_OF_TREE** (bool) : true
|
||||||
|
- **LIBOBS_INCLUDE_DIR** (path) : location of the libobs subfolder in the source
|
||||||
|
code of OBS Studio, located at [source_directory]/libobs/.
|
||||||
|
- **LIBOBS_LIB** (filepath) : location of the libobs.0.dylib file (usually
|
||||||
|
in /Applications/OBS.app/Contents/Resources/bin/libobs.0.dylib)
|
||||||
|
|
||||||
|
Assuming that you installed Qt via the regular Qt App way:
|
||||||
|
- **Qt5Core_DIR** (path) : Usually /Applications/Qt/5.10.1/clang_64/lib/cmake/Qt5Core
|
||||||
|
- **Qt5Widgets_DIR** (path) : Usually /Applications/Qt/5.10.1/clang_64/lib/cmake/Qt5Widgets
|
||||||
|
- **Qt5MacExtras_DIR** (path) : Usually /Applications/Qt/5.10.1/clang_64/lib/cmake/Qt5MacExtras
|
||||||
|
|
||||||
|
- **LIBOBS_FRONTEND_API_LIB** (filepath) : location of the libobs-frontend-api.0.dylib
|
||||||
|
file (usually in usually in /Applications/OBS.app/Contents/Resources/bin/libobs-frontend-api.0.dylib)
|
||||||
|
- **LIBOBS_FRONTEND_INCLUDE_DIR** (path) : location of the obs-frontend-api subfolder
|
||||||
|
in the source code of OBS Studio, located at [source_directory]/UI/obs-frontend-api.
|
||||||
|
|
||||||
|
Just keep hitting configure until all the vars are filled out. Then hit generate.
|
||||||
|
|
||||||
|
Open xcode (or a terminal, depending on the build type you chose), build and copy
|
||||||
|
the advanced-scene-switcher.so file to /Applications/OBS.app/Contents/PlugIns
|
||||||
|
|
||||||
|
Note that you might have to adjust the library search paths using the install_name_tool if you want the plugin to run on machines other than your build machine:
|
||||||
|
```
|
||||||
|
install_name_tool -change @rpath/libobs-frontend-api.dylib @executable_path/../Frameworks/libobs-frontend-api.dylib UI/frontend-plugins/SceneSwitcher/advanced-scene-switcher.so
|
||||||
|
install_name_tool -change @rpath/libobs.0.dylib @executable_path/../Frameworks/libobs.0.dylib UI/frontend-plugins/SceneSwitcher/advanced-scene-switcher.so
|
||||||
|
install_name_tool -change /usr/local/opt/qt5/lib/QtWidgets.framework/Versions/5/QtWidgets @executable_path/../Frameworks/QtWidgets.framework/Versions/5/QtWidgets UI/frontend-plugins/SceneSwitcher/advanced-scene-switcher.so
|
||||||
|
install_name_tool -change /usr/local/opt/qt5/lib/QtGui.framework/Versions/5/QtGui @executable_path/../Frameworks/QtGui.framework/Versions/5/QtGui UI/frontend-plugins/SceneSwitcher/advanced-scene-switcher.so
|
||||||
|
install_name_tool -change /usr/local/opt/qt5/lib/QtCore.framework/Versions/5/QtCore @executable_path/../Frameworks/QtCore.framework/Versions/5/QtCore UI/frontend-plugins/SceneSwitcher/advanced-scene-switcher.so
|
||||||
|
```
|
||||||
|
|||||||
@@ -1,322 +0,0 @@
|
|||||||
#include <X11/Xlib.h>
|
|
||||||
#include <X11/Xatom.h>
|
|
||||||
#include <X11/Xutil.h>
|
|
||||||
#include <X11/extensions/scrnsaver.h>
|
|
||||||
#undef Bool
|
|
||||||
#undef CursorShape
|
|
||||||
#undef Expose
|
|
||||||
#undef KeyPress
|
|
||||||
#undef KeyRelease
|
|
||||||
#undef FocusIn
|
|
||||||
#undef FocusOut
|
|
||||||
#undef FontChange
|
|
||||||
#undef None
|
|
||||||
#undef Status
|
|
||||||
#undef Unsorted
|
|
||||||
#include <util/platform.h>
|
|
||||||
#include "advanced-scene-switcher.hpp"
|
|
||||||
|
|
||||||
using namespace std;
|
|
||||||
|
|
||||||
static Display* xdisplay = 0;
|
|
||||||
|
|
||||||
Display *disp()
|
|
||||||
{
|
|
||||||
if (!xdisplay)
|
|
||||||
xdisplay = XOpenDisplay(NULL);
|
|
||||||
|
|
||||||
return xdisplay;
|
|
||||||
}
|
|
||||||
|
|
||||||
void cleanupDisplay()
|
|
||||||
{
|
|
||||||
if (!xdisplay)
|
|
||||||
return;
|
|
||||||
|
|
||||||
XCloseDisplay(xdisplay);
|
|
||||||
xdisplay = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool ewmhIsSupported()
|
|
||||||
{
|
|
||||||
Display *display = disp();
|
|
||||||
Atom netSupportingWmCheck = XInternAtom(display,
|
|
||||||
"_NET_SUPPORTING_WM_CHECK", true);
|
|
||||||
Atom actualType;
|
|
||||||
int format = 0;
|
|
||||||
unsigned long num = 0, bytes = 0;
|
|
||||||
unsigned char *data = NULL;
|
|
||||||
Window ewmh_window = 0;
|
|
||||||
|
|
||||||
int status = XGetWindowProperty(
|
|
||||||
display,
|
|
||||||
DefaultRootWindow(display),
|
|
||||||
netSupportingWmCheck,
|
|
||||||
0L,
|
|
||||||
1L,
|
|
||||||
false,
|
|
||||||
XA_WINDOW,
|
|
||||||
&actualType,
|
|
||||||
&format,
|
|
||||||
&num,
|
|
||||||
&bytes,
|
|
||||||
&data);
|
|
||||||
|
|
||||||
if (status == Success) {
|
|
||||||
if (num > 0) {
|
|
||||||
ewmh_window = ((Window*)data)[0];
|
|
||||||
}
|
|
||||||
if (data) {
|
|
||||||
XFree(data);
|
|
||||||
data = NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ewmh_window) {
|
|
||||||
status = XGetWindowProperty(
|
|
||||||
display,
|
|
||||||
ewmh_window,
|
|
||||||
netSupportingWmCheck,
|
|
||||||
0L,
|
|
||||||
1L,
|
|
||||||
false,
|
|
||||||
XA_WINDOW,
|
|
||||||
&actualType,
|
|
||||||
&format,
|
|
||||||
&num,
|
|
||||||
&bytes,
|
|
||||||
&data);
|
|
||||||
if (status != Success || num == 0 ||
|
|
||||||
ewmh_window != ((Window*)data)[0]) {
|
|
||||||
ewmh_window = 0;
|
|
||||||
}
|
|
||||||
if (status == Success && data) {
|
|
||||||
XFree(data);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return ewmh_window != 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static std::vector<Window> getTopLevelWindows()
|
|
||||||
{
|
|
||||||
std::vector<Window> res;
|
|
||||||
|
|
||||||
res.resize(0);
|
|
||||||
|
|
||||||
if (!ewmhIsSupported()) {
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
Atom netClList = XInternAtom(disp(), "_NET_CLIENT_LIST", true);
|
|
||||||
Atom actualType;
|
|
||||||
int format;
|
|
||||||
unsigned long num, bytes;
|
|
||||||
Window* data = 0;
|
|
||||||
|
|
||||||
for (int i = 0; i < ScreenCount(disp()); ++i) {
|
|
||||||
Window rootWin = RootWindow(disp(), i);
|
|
||||||
|
|
||||||
int status = XGetWindowProperty(
|
|
||||||
disp(),
|
|
||||||
rootWin,
|
|
||||||
netClList,
|
|
||||||
0L,
|
|
||||||
~0L,
|
|
||||||
false,
|
|
||||||
AnyPropertyType,
|
|
||||||
&actualType,
|
|
||||||
&format,
|
|
||||||
&num,
|
|
||||||
&bytes,
|
|
||||||
(uint8_t**)&data);
|
|
||||||
|
|
||||||
if (status != Success) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (unsigned long i = 0; i < num; ++i)
|
|
||||||
res.emplace_back(data[i]);
|
|
||||||
|
|
||||||
XFree(data);
|
|
||||||
}
|
|
||||||
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
static std::string GetWindowTitle(size_t i)
|
|
||||||
{
|
|
||||||
Window w = getTopLevelWindows().at(i);
|
|
||||||
std::string windowTitle;
|
|
||||||
char* name;
|
|
||||||
|
|
||||||
int status = XFetchName(disp(), w, &name);
|
|
||||||
if (status >= Success && name != nullptr)
|
|
||||||
{
|
|
||||||
std::string str(name);
|
|
||||||
windowTitle = str;
|
|
||||||
}
|
|
||||||
|
|
||||||
XFree(name);
|
|
||||||
|
|
||||||
return windowTitle;
|
|
||||||
}
|
|
||||||
|
|
||||||
void GetWindowList(vector<string> &windows)
|
|
||||||
{
|
|
||||||
windows.resize(0);
|
|
||||||
|
|
||||||
for (size_t i = 0; i < getTopLevelWindows().size(); ++i){
|
|
||||||
if (GetWindowTitle(i) != "")
|
|
||||||
windows.emplace_back(GetWindowTitle(i));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void GetCurrentWindowTitle(string &title)
|
|
||||||
{
|
|
||||||
if (!ewmhIsSupported()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Atom active = XInternAtom(disp(), "_NET_ACTIVE_WINDOW", true);
|
|
||||||
Atom actualType;
|
|
||||||
int format;
|
|
||||||
unsigned long num, bytes;
|
|
||||||
Window* data = 0;
|
|
||||||
char* name;
|
|
||||||
|
|
||||||
Window rootWin = RootWindow(disp(), 0);
|
|
||||||
|
|
||||||
XGetWindowProperty(
|
|
||||||
disp(),
|
|
||||||
rootWin,
|
|
||||||
active,
|
|
||||||
0L,
|
|
||||||
~0L,
|
|
||||||
false,
|
|
||||||
AnyPropertyType,
|
|
||||||
&actualType,
|
|
||||||
&format,
|
|
||||||
&num,
|
|
||||||
&bytes,
|
|
||||||
(uint8_t**)&data);
|
|
||||||
|
|
||||||
int status = XFetchName(disp(), data[0], &name);
|
|
||||||
|
|
||||||
if (status >= Success && name != nullptr) {
|
|
||||||
std::string str(name);
|
|
||||||
title = str;
|
|
||||||
}
|
|
||||||
|
|
||||||
XFree(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
pair<int, int> getCursorPos()
|
|
||||||
{
|
|
||||||
pair<int, int> pos(0, 0);
|
|
||||||
Display *dpy;
|
|
||||||
Window root;
|
|
||||||
Window ret_root;
|
|
||||||
Window ret_child;
|
|
||||||
int root_x;
|
|
||||||
int root_y;
|
|
||||||
int win_x;
|
|
||||||
int win_y;
|
|
||||||
unsigned int mask;
|
|
||||||
|
|
||||||
dpy = XOpenDisplay(NULL);
|
|
||||||
root = XDefaultRootWindow(dpy);
|
|
||||||
|
|
||||||
if(XQueryPointer(dpy, root, &ret_root, &ret_child, &root_x, &root_y,
|
|
||||||
&win_x, &win_y, &mask))
|
|
||||||
{
|
|
||||||
pos = pair<int, int> (root_x,root_y);
|
|
||||||
}
|
|
||||||
return pos;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool isFullscreen()
|
|
||||||
{
|
|
||||||
if (!ewmhIsSupported()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
Atom active = XInternAtom(disp(), "_NET_ACTIVE_WINDOW", true);
|
|
||||||
Atom actualType;
|
|
||||||
int format;
|
|
||||||
unsigned long num, bytes;
|
|
||||||
Window* data = 0;
|
|
||||||
|
|
||||||
Window rootWin = RootWindow(disp(), 0);
|
|
||||||
XGetWindowProperty(
|
|
||||||
disp(),
|
|
||||||
rootWin,
|
|
||||||
active,
|
|
||||||
0L,
|
|
||||||
~0L,
|
|
||||||
false,
|
|
||||||
AnyPropertyType,
|
|
||||||
&actualType,
|
|
||||||
&format,
|
|
||||||
&num,
|
|
||||||
&bytes,
|
|
||||||
(uint8_t**)&data);
|
|
||||||
|
|
||||||
|
|
||||||
XWindowAttributes window_attributes_return;
|
|
||||||
XWindowAttributes screen_attributes_return;
|
|
||||||
|
|
||||||
XGetWindowAttributes(disp(), rootWin, &screen_attributes_return);
|
|
||||||
XGetWindowAttributes(disp(), data[0], &window_attributes_return);
|
|
||||||
|
|
||||||
//menu bar is always 24 pixels in height
|
|
||||||
return (window_attributes_return.width >= screen_attributes_return.width &&
|
|
||||||
window_attributes_return.height + 24 >= screen_attributes_return.height) ? true : false;
|
|
||||||
}
|
|
||||||
|
|
||||||
//exe switch is not quite what is expected but it works for now
|
|
||||||
void GetProcessList(QStringList &processes)
|
|
||||||
{
|
|
||||||
processes.clear();
|
|
||||||
for (size_t i = 0; i < getTopLevelWindows().size(); ++i){
|
|
||||||
string s = GetWindowTitle(i);
|
|
||||||
if (s != "")
|
|
||||||
processes << QString::fromStdString(s);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool isInFocus(const QString &exeToCheck)
|
|
||||||
{
|
|
||||||
string curWindow;
|
|
||||||
GetCurrentWindowTitle(curWindow);
|
|
||||||
|
|
||||||
return (QString::compare(
|
|
||||||
QString::fromStdString(curWindow),
|
|
||||||
exeToCheck,
|
|
||||||
Qt::CaseInsensitive) == 0) ? true : false;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int secondsSinceLastInput()
|
|
||||||
{
|
|
||||||
time_t idle_time;
|
|
||||||
static XScreenSaverInfo *mit_info;
|
|
||||||
Display *display;
|
|
||||||
int screen;
|
|
||||||
|
|
||||||
mit_info = XScreenSaverAllocInfo();
|
|
||||||
|
|
||||||
if((display=XOpenDisplay(NULL)) == NULL)
|
|
||||||
{
|
|
||||||
return(-1);
|
|
||||||
}
|
|
||||||
screen = DefaultScreen(display);
|
|
||||||
XScreenSaverQueryInfo(display, RootWindow(display,screen), mit_info);
|
|
||||||
idle_time = (mit_info->idle) / 1000;
|
|
||||||
XFree(mit_info);
|
|
||||||
XCloseDisplay(display);
|
|
||||||
|
|
||||||
return idle_time;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1,146 +0,0 @@
|
|||||||
#import <AppKit/AppKit.h>
|
|
||||||
#import <CoreFoundation/CoreFoundation.h>
|
|
||||||
#import <CoreGraphics/CGEvent.h>
|
|
||||||
#import <Carbon/Carbon.h>
|
|
||||||
#include <Carbon/Carbon.h>
|
|
||||||
#include <util/platform.h>
|
|
||||||
#include "advanced-scene-switcher.hpp"
|
|
||||||
|
|
||||||
|
|
||||||
void GetWindowList(vector<string> &windows)
|
|
||||||
{
|
|
||||||
windows.resize(0);
|
|
||||||
|
|
||||||
@autoreleasepool {
|
|
||||||
NSWorkspace *ws = [NSWorkspace sharedWorkspace];
|
|
||||||
NSArray *array = [ws runningApplications];
|
|
||||||
for (NSRunningApplication *app in array) {
|
|
||||||
NSString *name = app.localizedName;
|
|
||||||
if (!name)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
const char *str = name.UTF8String;
|
|
||||||
if (str && *str)
|
|
||||||
windows.emplace_back(str);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void GetCurrentWindowTitle(string &title)
|
|
||||||
{
|
|
||||||
title.resize(0);
|
|
||||||
|
|
||||||
@autoreleasepool {
|
|
||||||
NSWorkspace *ws = [NSWorkspace sharedWorkspace];
|
|
||||||
NSRunningApplication *app = [ws frontmostApplication];
|
|
||||||
if (app) {
|
|
||||||
NSString *name = app.localizedName;
|
|
||||||
if (!name)
|
|
||||||
return;
|
|
||||||
|
|
||||||
const char *str = name.UTF8String;
|
|
||||||
if (str && *str)
|
|
||||||
title = str;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pair<int, int> getCursorPos() {
|
|
||||||
pair<int, int> pos(0, 0);
|
|
||||||
CGEventRef event = CGEventCreate(NULL);
|
|
||||||
CGPoint cursorPos = CGEventGetLocation(event);
|
|
||||||
CFRelease(event);
|
|
||||||
pos.first = cursorPos.x;
|
|
||||||
pos.second = cursorPos.y;
|
|
||||||
return pos;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool isFullscreen() {
|
|
||||||
@autoreleasepool {
|
|
||||||
AXValueRef temp;
|
|
||||||
CGSize windowSize;
|
|
||||||
CGPoint windowPosition;
|
|
||||||
AXUIElementRef frontMostApp;
|
|
||||||
AXUIElementRef frontMostWindow;
|
|
||||||
|
|
||||||
pid_t pid;
|
|
||||||
ProcessSerialNumber psn;
|
|
||||||
@try {
|
|
||||||
GetFrontProcess(&psn);
|
|
||||||
GetProcessPID(&psn, &pid);
|
|
||||||
frontMostApp = AXUIElementCreateApplication(pid);
|
|
||||||
|
|
||||||
AXUIElementCopyAttributeValue(
|
|
||||||
frontMostApp, kAXFocusedWindowAttribute, (CFTypeRef *)&frontMostWindow);
|
|
||||||
|
|
||||||
// Get the window size and position
|
|
||||||
AXUIElementCopyAttributeValue(
|
|
||||||
frontMostWindow, kAXSizeAttribute, (CFTypeRef *)&temp);
|
|
||||||
AXValueGetValue(temp, kAXValueTypeCGSize, &windowSize);
|
|
||||||
CFRelease(temp);
|
|
||||||
|
|
||||||
AXUIElementCopyAttributeValue(
|
|
||||||
frontMostWindow, kAXPositionAttribute, (CFTypeRef *)&temp);
|
|
||||||
AXValueGetValue(temp, kAXValueTypeCGPoint, &windowPosition);
|
|
||||||
CFRelease(temp);
|
|
||||||
|
|
||||||
CGRect screenBound = CGDisplayBounds(CGMainDisplayID());
|
|
||||||
CGSize screenSize = screenBound.size;
|
|
||||||
|
|
||||||
if((windowSize.width == screenSize.width) && (windowSize.height == screenSize.height) &&
|
|
||||||
(windowPosition.x == 0) && (windowPosition.y == 0))
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
@catch (...) {
|
|
||||||
// deal with the exception
|
|
||||||
}
|
|
||||||
@catch (NSException *exception) {
|
|
||||||
// deal with the exception
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
int secondsSinceLastInput()
|
|
||||||
{
|
|
||||||
double time = CGEventSourceSecondsSinceLastEventType(kCGEventSourceStateCombinedSessionState, kCGAnyInputEventType) + 0.5;
|
|
||||||
return (int) time;
|
|
||||||
}
|
|
||||||
|
|
||||||
void GetProcessList(QStringList& list)
|
|
||||||
{
|
|
||||||
list.clear();
|
|
||||||
@autoreleasepool {
|
|
||||||
NSWorkspace *ws = [NSWorkspace sharedWorkspace];
|
|
||||||
NSArray *array = [ws runningApplications];
|
|
||||||
for (NSRunningApplication *app in array) {
|
|
||||||
NSString *name = app.localizedName;
|
|
||||||
if (!name)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
const char *str = name.UTF8String;
|
|
||||||
if (str && *str)
|
|
||||||
list << (str);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool isInFocus(QString const& appQName)
|
|
||||||
{
|
|
||||||
QByteArray ba = appQName.toLocal8Bit();
|
|
||||||
const char * appName = ba.data();
|
|
||||||
@autoreleasepool {
|
|
||||||
NSWorkspace *ws = [NSWorkspace sharedWorkspace];
|
|
||||||
NSRunningApplication *app = [ws frontmostApplication];
|
|
||||||
if (app) {
|
|
||||||
NSString *name = app.localizedName;
|
|
||||||
if (!name)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
const char *str = name.UTF8String;
|
|
||||||
return (str && *str && strcmp(appName,str) == 0 )? true : false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,123 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <QDialog>
|
|
||||||
#include <memory>
|
|
||||||
#include <vector>
|
|
||||||
#include <string>
|
|
||||||
#include "ui_advanced-scene-switcher.h"
|
|
||||||
#include "switcher-data-structs.hpp"
|
|
||||||
|
|
||||||
struct obs_weak_source;
|
|
||||||
typedef struct obs_weak_source obs_weak_source_t;
|
|
||||||
|
|
||||||
class QCloseEvent;
|
|
||||||
|
|
||||||
class SceneSwitcher : public QDialog {
|
|
||||||
Q_OBJECT
|
|
||||||
|
|
||||||
public:
|
|
||||||
std::unique_ptr<Ui_SceneSwitcher> ui;
|
|
||||||
bool loading = true;
|
|
||||||
|
|
||||||
SceneSwitcher(QWidget *parent);
|
|
||||||
|
|
||||||
void closeEvent(QCloseEvent *event) override;
|
|
||||||
|
|
||||||
void SetStarted();
|
|
||||||
void SetStopped();
|
|
||||||
|
|
||||||
int FindByData(const QString &window);
|
|
||||||
int ScreenRegionFindByData(const QString ®ion);
|
|
||||||
int PauseScenesFindByData(const QString &scene);
|
|
||||||
int PauseWindowsFindByData(const QString &window);
|
|
||||||
int IgnoreWindowsFindByData(const QString &window);
|
|
||||||
int SceneRoundTripFindByData(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);
|
|
||||||
|
|
||||||
void UpdateNonMatchingScene(const QString &name);
|
|
||||||
void UpdateAutoStopScene(const QString &name);
|
|
||||||
void UpdateIdleDataTransition(const QString& name);
|
|
||||||
void UpdateIdleDataScene(const QString& name);
|
|
||||||
|
|
||||||
public slots:
|
|
||||||
void on_switches_currentRowChanged(int idx);
|
|
||||||
void on_add_clicked();
|
|
||||||
void on_remove_clicked();
|
|
||||||
void on_noMatchDontSwitch_clicked();
|
|
||||||
void on_noMatchSwitch_clicked();
|
|
||||||
void on_startAtLaunch_toggled(bool value);
|
|
||||||
void on_noMatchSwitchScene_currentTextChanged(const QString &text);
|
|
||||||
void on_checkInterval_valueChanged(int value);
|
|
||||||
void on_toggleStartButton_clicked();
|
|
||||||
|
|
||||||
void on_screenRegions_currentRowChanged(int idx);
|
|
||||||
void on_screenRegionAdd_clicked();
|
|
||||||
void on_screenRegionRemove_clicked();
|
|
||||||
|
|
||||||
void on_pauseScenes_currentRowChanged(int idx);
|
|
||||||
void on_pauseScenesAdd_clicked();
|
|
||||||
void on_pauseScenesRemove_clicked();
|
|
||||||
|
|
||||||
void on_pauseWindows_currentRowChanged(int idx);
|
|
||||||
void on_pauseWindowsAdd_clicked();
|
|
||||||
void on_pauseWindowsRemove_clicked();
|
|
||||||
|
|
||||||
void on_ignoreWindows_currentRowChanged(int idx);
|
|
||||||
void on_ignoreWindowsAdd_clicked();
|
|
||||||
void on_ignoreWindowsRemove_clicked();
|
|
||||||
|
|
||||||
void on_sceneRoundTrips_currentRowChanged(int idx);
|
|
||||||
void on_sceneRoundTripAdd_clicked();
|
|
||||||
void on_sceneRoundTripRemove_clicked();
|
|
||||||
void on_sceneRoundTripSave_clicked();
|
|
||||||
void on_sceneRoundTripLoad_clicked();
|
|
||||||
|
|
||||||
void on_autoStopSceneCheckBox_stateChanged(int state);
|
|
||||||
void on_autoStopScenes_currentTextChanged(const QString &text);
|
|
||||||
|
|
||||||
void on_sceneTransitions_currentRowChanged(int idx);
|
|
||||||
void on_transitionsAdd_clicked();
|
|
||||||
void on_transitionsRemove_clicked();
|
|
||||||
void on_defaultTransitions_currentRowChanged(int idx);
|
|
||||||
void on_defaultTransitionsAdd_clicked();
|
|
||||||
void on_defaultTransitionsRemove_clicked();
|
|
||||||
|
|
||||||
void on_browseButton_clicked();
|
|
||||||
void on_readFileCheckBox_stateChanged(int state);
|
|
||||||
void on_readPathLineEdit_textChanged(const QString & text);
|
|
||||||
void on_writePathLineEdit_textChanged(const QString & text);
|
|
||||||
void on_browseButton_2_clicked();
|
|
||||||
|
|
||||||
void on_executableAdd_clicked();
|
|
||||||
void on_executableRemove_clicked();
|
|
||||||
void on_executables_currentRowChanged(int idx);
|
|
||||||
|
|
||||||
void on_idleCheckBox_stateChanged(int state);
|
|
||||||
void on_idleTransitions_currentTextChanged(const QString& text);
|
|
||||||
void on_idleScenes_currentTextChanged(const QString& text);
|
|
||||||
void on_idleSpinBox_valueChanged(int i);
|
|
||||||
void on_ignoreIdleWindows_currentRowChanged(int idx);
|
|
||||||
void on_ignoreIdleAdd_clicked();
|
|
||||||
void on_ignoreIdleRemove_clicked();
|
|
||||||
|
|
||||||
void on_priorityUp_clicked();
|
|
||||||
void on_priorityDown_clicked();
|
|
||||||
|
|
||||||
void updateScreenRegionCursorPos();
|
|
||||||
|
|
||||||
void on_close_clicked();
|
|
||||||
};
|
|
||||||
|
|
||||||
void GetWindowList(std::vector<std::string> &windows);
|
|
||||||
void GetCurrentWindowTitle(std::string &title);
|
|
||||||
pair<int, int> getCursorPos();
|
|
||||||
bool isFullscreen();
|
|
||||||
int secondsSinceLastInput();
|
|
||||||
bool isInFocus(const QString &exeToCheck);
|
|
||||||
void GetProcessList(QStringList &processes);
|
|
||||||
|
|
||||||
struct SwitcherData;
|
|
||||||
extern SwitcherData* switcher;
|
|
||||||
@@ -1,140 +0,0 @@
|
|||||||
#include "advanced-scene-switcher.hpp"
|
|
||||||
|
|
||||||
int SceneSwitcher::executableFindByData(const QString& exe)
|
|
||||||
{
|
|
||||||
int count = ui->executables->count();
|
|
||||||
|
|
||||||
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();
|
|
||||||
|
|
||||||
lock_guard<mutex> lock(switcher->m);
|
|
||||||
for (auto& s : switcher->executableSwitches)
|
|
||||||
{
|
|
||||||
if (exec.compare(s.mExe) == 0)
|
|
||||||
{
|
|
||||||
QString sceneName = GetWeakSourceName(s.mScene).c_str();
|
|
||||||
QString transitionName = GetWeakSourceName(s.mTransition).c_str();
|
|
||||||
ui->executableScenes->setCurrentText(sceneName);
|
|
||||||
ui->executable->setCurrentText(exec);
|
|
||||||
ui->executableTransitions->setCurrentText(transitionName);
|
|
||||||
ui->requiresFocusCheckBox->setChecked(s.mInFocus);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
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)
|
|
||||||
{
|
|
||||||
lock_guard<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);
|
|
||||||
|
|
||||||
{
|
|
||||||
lock_guard<mutex> lock(switcher->m);
|
|
||||||
for (auto& s : switcher->executableSwitches)
|
|
||||||
{
|
|
||||||
if (s.mExe == exeName)
|
|
||||||
{
|
|
||||||
s.mScene = source;
|
|
||||||
s.mTransition = transition;
|
|
||||||
s.mInFocus = inFocus;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ui->executables->sortItems();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void SceneSwitcher::on_executableRemove_clicked()
|
|
||||||
{
|
|
||||||
QListWidgetItem* item = ui->executables->currentItem();
|
|
||||||
if (!item)
|
|
||||||
return;
|
|
||||||
|
|
||||||
QString exe = item->data(Qt::UserRole).toString();
|
|
||||||
|
|
||||||
{
|
|
||||||
lock_guard<mutex> lock(switcher->m);
|
|
||||||
auto& switches = switcher->executableSwitches;
|
|
||||||
|
|
||||||
for (auto it = switches.begin(); it != switches.end(); ++it)
|
|
||||||
{
|
|
||||||
auto& s = *it;
|
|
||||||
|
|
||||||
if (s.mExe == exe)
|
|
||||||
{
|
|
||||||
switches.erase(it);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
delete item;
|
|
||||||
}
|
|
||||||
|
|
||||||
void SwitcherData::checkExeSwitch(bool& match, OBSWeakSource& scene, OBSWeakSource& transition)
|
|
||||||
{
|
|
||||||
QStringList runningProcesses;
|
|
||||||
GetProcessList(runningProcesses);
|
|
||||||
for (ExecutableSceneSwitch& s : executableSwitches)
|
|
||||||
{
|
|
||||||
if (runningProcesses.contains(s.mExe))
|
|
||||||
{
|
|
||||||
scene = s.mScene;
|
|
||||||
transition = s.mTransition;
|
|
||||||
match = !s.mInFocus || (s.mInFocus && isInFocus(s.mExe));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
107
external/FindLibObs.cmake
vendored
Normal file
107
external/FindLibObs.cmake
vendored
Normal file
@@ -0,0 +1,107 @@
|
|||||||
|
# This module can be copied and used by external plugins for OBS
|
||||||
|
#
|
||||||
|
# Once done these will be defined:
|
||||||
|
#
|
||||||
|
# LIBOBS_FOUND
|
||||||
|
# LIBOBS_INCLUDE_DIRS
|
||||||
|
# LIBOBS_LIBRARIES
|
||||||
|
|
||||||
|
find_package(PkgConfig QUIET)
|
||||||
|
if (PKG_CONFIG_FOUND)
|
||||||
|
pkg_check_modules(_OBS QUIET obs libobs)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(CMAKE_SIZEOF_VOID_P EQUAL 8)
|
||||||
|
set(_lib_suffix 64)
|
||||||
|
else()
|
||||||
|
set(_lib_suffix 32)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(DEFINED CMAKE_BUILD_TYPE)
|
||||||
|
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
|
||||||
|
set(_build_type_base "debug")
|
||||||
|
else()
|
||||||
|
set(_build_type_base "release")
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
find_path(LIBOBS_INCLUDE_DIR
|
||||||
|
NAMES obs.h
|
||||||
|
HINTS
|
||||||
|
ENV obsPath${_lib_suffix}
|
||||||
|
ENV obsPath
|
||||||
|
${obsPath}
|
||||||
|
PATHS
|
||||||
|
/usr/include /usr/local/include /opt/local/include /sw/include
|
||||||
|
PATH_SUFFIXES
|
||||||
|
libobs
|
||||||
|
)
|
||||||
|
|
||||||
|
function(find_obs_lib base_name repo_build_path lib_name)
|
||||||
|
string(TOUPPER "${base_name}" base_name_u)
|
||||||
|
|
||||||
|
if(DEFINED _build_type_base)
|
||||||
|
set(_build_type_${repo_build_path} "${_build_type_base}/${repo_build_path}")
|
||||||
|
set(_build_type_${repo_build_path}${_lib_suffix} "${_build_type_base}${_lib_suffix}/${repo_build_path}")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
find_library(${base_name_u}_LIB
|
||||||
|
NAMES ${_${base_name_u}_LIBRARIES} ${lib_name} lib${lib_name}
|
||||||
|
HINTS
|
||||||
|
ENV obsPath${_lib_suffix}
|
||||||
|
ENV obsPath
|
||||||
|
${obsPath}
|
||||||
|
${_${base_name_u}_LIBRARY_DIRS}
|
||||||
|
PATHS
|
||||||
|
/usr/lib /usr/local/lib /opt/local/lib /sw/lib
|
||||||
|
PATH_SUFFIXES
|
||||||
|
lib${_lib_suffix} lib
|
||||||
|
libs${_lib_suffix} libs
|
||||||
|
bin${_lib_suffix} bin
|
||||||
|
../lib${_lib_suffix} ../lib
|
||||||
|
../libs${_lib_suffix} ../libs
|
||||||
|
../bin${_lib_suffix} ../bin
|
||||||
|
# base repo non-msvc-specific search paths
|
||||||
|
${_build_type_${repo_build_path}}
|
||||||
|
${_build_type_${repo_build_path}${_lib_suffix}}
|
||||||
|
build/${repo_build_path}
|
||||||
|
build${_lib_suffix}/${repo_build_path}
|
||||||
|
# base repo msvc-specific search paths on windows
|
||||||
|
build${_lib_suffix}/${repo_build_path}/Debug
|
||||||
|
build${_lib_suffix}/${repo_build_path}/RelWithDebInfo
|
||||||
|
build/${repo_build_path}/Debug
|
||||||
|
build/${repo_build_path}/RelWithDebInfo
|
||||||
|
)
|
||||||
|
endfunction()
|
||||||
|
|
||||||
|
find_obs_lib(LIBOBS libobs obs)
|
||||||
|
|
||||||
|
if(MSVC)
|
||||||
|
find_obs_lib(W32_PTHREADS deps/w32-pthreads w32-pthreads)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
include(FindPackageHandleStandardArgs)
|
||||||
|
find_package_handle_standard_args(Libobs DEFAULT_MSG LIBOBS_LIB LIBOBS_INCLUDE_DIR)
|
||||||
|
mark_as_advanced(LIBOBS_INCLUDE_DIR LIBOBS_LIB)
|
||||||
|
|
||||||
|
if(LIBOBS_FOUND)
|
||||||
|
if(MSVC)
|
||||||
|
if (NOT DEFINED W32_PTHREADS_LIB)
|
||||||
|
message(FATAL_ERROR "Could not find the w32-pthreads library" )
|
||||||
|
endif()
|
||||||
|
|
||||||
|
set(W32_PTHREADS_INCLUDE_DIR ${LIBOBS_INCLUDE_DIR}/../deps/w32-pthreads)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
set(LIBOBS_INCLUDE_DIRS ${LIBOBS_INCLUDE_DIR} ${W32_PTHREADS_INCLUDE_DIR})
|
||||||
|
set(LIBOBS_LIBRARIES ${LIBOBS_LIB} ${W32_PTHREADS_LIB})
|
||||||
|
include(${LIBOBS_INCLUDE_DIR}/../cmake/external/ObsPluginHelpers.cmake)
|
||||||
|
|
||||||
|
# allows external plugins to easily use/share common dependencies that are often included with libobs (such as FFmpeg)
|
||||||
|
if(NOT DEFINED INCLUDED_LIBOBS_CMAKE_MODULES)
|
||||||
|
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${LIBOBS_INCLUDE_DIR}/../cmake/Modules/")
|
||||||
|
set(INCLUDED_LIBOBS_CMAKE_MODULES true)
|
||||||
|
endif()
|
||||||
|
else()
|
||||||
|
message(FATAL_ERROR "Could not find the libobs library" )
|
||||||
|
endif()
|
||||||
163
external/ObsPluginHelpers.cmake
vendored
Normal file
163
external/ObsPluginHelpers.cmake
vendored
Normal file
@@ -0,0 +1,163 @@
|
|||||||
|
# Functions for generating external plugins
|
||||||
|
|
||||||
|
set(EXTERNAL_PLUGIN_OUTPUT_DIR "${CMAKE_BINARY_DIR}/rundir")
|
||||||
|
|
||||||
|
# Fix XCode includes to ignore warnings on system includes
|
||||||
|
function(target_include_directories_system _target)
|
||||||
|
if(XCODE)
|
||||||
|
foreach(_arg ${ARGN})
|
||||||
|
if("${_arg}" STREQUAL "PRIVATE" OR "${_arg}" STREQUAL "PUBLIC" OR "${_arg}" STREQUAL "INTERFACE")
|
||||||
|
set(_scope ${_arg})
|
||||||
|
else()
|
||||||
|
target_compile_options(${_target} ${_scope} -isystem${_arg})
|
||||||
|
endif()
|
||||||
|
endforeach()
|
||||||
|
else()
|
||||||
|
target_include_directories(${_target} SYSTEM ${_scope} ${ARGN})
|
||||||
|
endif()
|
||||||
|
endfunction()
|
||||||
|
|
||||||
|
function(install_external_plugin_data_internal target source_dir target_dir)
|
||||||
|
install(DIRECTORY ${source_dir}/
|
||||||
|
DESTINATION "${target}/${target_dir}"
|
||||||
|
USE_SOURCE_PERMISSIONS)
|
||||||
|
add_custom_command(TARGET ${target} POST_BUILD
|
||||||
|
COMMAND "${CMAKE_COMMAND}" -E copy_directory
|
||||||
|
"${CMAKE_CURRENT_SOURCE_DIR}/${source_dir}" "${EXTERNAL_PLUGIN_OUTPUT_DIR}/$<CONFIGURATION>/${target}/${target_dir}"
|
||||||
|
VERBATIM)
|
||||||
|
endfunction()
|
||||||
|
|
||||||
|
# Installs data
|
||||||
|
# 'target' is the destination target project being installed to
|
||||||
|
# 'data_loc' specifies the directory of the data
|
||||||
|
function(install_external_plugin_data target data_loc)
|
||||||
|
install_external_plugin_data_internal(${target} ${data_loc} "data")
|
||||||
|
endfunction()
|
||||||
|
|
||||||
|
# Installs data in an architecture-specific data directory on windows/linux (data/32bit or data/64bit). Does not apply for mac.
|
||||||
|
# 'target' is the destination target project being installed to
|
||||||
|
# 'data_loc' specifies the directory of the data being installed
|
||||||
|
function(install_external_plugin_arch_data target data_loc)
|
||||||
|
if(APPLE)
|
||||||
|
set(_bit_suffix "")
|
||||||
|
elseif(CMAKE_SIZEOF_VOID_P EQUAL 8)
|
||||||
|
set(_bit_suffix "/64bit")
|
||||||
|
else()
|
||||||
|
set(_bit_suffix "/32bit")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
install_external_plugin_data_internal(${target} ${data_loc} "data${_bit_suffix}")
|
||||||
|
endfunction()
|
||||||
|
|
||||||
|
# Installs data in the target's bin directory
|
||||||
|
# 'target' is the destination target project being installed to
|
||||||
|
# 'data_loc' specifies the directory of the data being installed
|
||||||
|
function(install_external_plugin_data_to_bin target data_loc)
|
||||||
|
if(APPLE)
|
||||||
|
set(_bit_suffix "")
|
||||||
|
elseif(CMAKE_SIZEOF_VOID_P EQUAL 8)
|
||||||
|
set(_bit_suffix "/64bit")
|
||||||
|
else()
|
||||||
|
set(_bit_suffix "/32bit")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
install_external_plugin_data_internal(${target} ${data_loc} "bin${_bit_suffix}")
|
||||||
|
endfunction()
|
||||||
|
|
||||||
|
# Installs an additional binary to a target
|
||||||
|
# 'target' is the destination target project being installed to
|
||||||
|
# 'additional_target' specifies the additional binary
|
||||||
|
function(install_external_plugin_additional target additional_target)
|
||||||
|
if(APPLE)
|
||||||
|
set(_bit_suffix "")
|
||||||
|
elseif(CMAKE_SIZEOF_VOID_P EQUAL 8)
|
||||||
|
set(_bit_suffix "64bit/")
|
||||||
|
else()
|
||||||
|
set(_bit_suffix "32bit/")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
set_target_properties(${additional_target} PROPERTIES
|
||||||
|
PREFIX "")
|
||||||
|
|
||||||
|
install(TARGETS ${additional_target}
|
||||||
|
LIBRARY DESTINATION "bin"
|
||||||
|
RUNTIME DESTINATION "bin")
|
||||||
|
add_custom_command(TARGET ${additional_target} POST_BUILD
|
||||||
|
COMMAND "${CMAKE_COMMAND}" -E copy
|
||||||
|
"$<TARGET_FILE:${additional_target}>"
|
||||||
|
"${EXTERNAL_PLUGIN_OUTPUT_DIR}/$<CONFIGURATION>/${target}/bin/${_bit_suffix}$<TARGET_FILE_NAME:${additional_target}>"
|
||||||
|
VERBATIM)
|
||||||
|
endfunction()
|
||||||
|
|
||||||
|
# Installs the binary of the target
|
||||||
|
# 'target' is the target project being installed
|
||||||
|
function(install_external_plugin target)
|
||||||
|
install_external_plugin_additional(${target} ${target})
|
||||||
|
endfunction()
|
||||||
|
|
||||||
|
# Installs the binary and data of the target
|
||||||
|
# 'target' is the destination target project being installed to
|
||||||
|
function(install_external_plugin_with_data target data_loc)
|
||||||
|
install_external_plugin(${target})
|
||||||
|
install_external_plugin_data(${target} ${data_loc})
|
||||||
|
endfunction()
|
||||||
|
|
||||||
|
# Installs an additional binary to the data of a target
|
||||||
|
# 'target' is the destination target project being installed to
|
||||||
|
# 'additional_target' specifies the additional binary
|
||||||
|
function(install_external_plugin_bin_to_data target additional_target)
|
||||||
|
install(TARGETS ${additional_target}
|
||||||
|
LIBRARY DESTINATION "data"
|
||||||
|
RUNTIME DESTINATION "data")
|
||||||
|
add_custom_command(TARGET ${additional_target} POST_BUILD
|
||||||
|
COMMAND "${CMAKE_COMMAND}" -E copy
|
||||||
|
"$<TARGET_FILE:${additional_target}>"
|
||||||
|
"${EXTERNAL_PLUGIN_OUTPUT_DIR}/$<CONFIGURATION>/${target}/data/$<TARGET_FILE_NAME:${additional_target}>"
|
||||||
|
VERBATIM)
|
||||||
|
endfunction()
|
||||||
|
|
||||||
|
# Installs an additional binary in an architecture-specific data directory on windows/linux (data/32bit or data/64bit). Does not apply for mac.
|
||||||
|
# 'target' is the destination target project being installed to
|
||||||
|
# 'additional_target' specifies the additional binary
|
||||||
|
function(install_external_plugin_bin_to_arch_data target additional_target)
|
||||||
|
if(APPLE)
|
||||||
|
set(_bit_suffix "")
|
||||||
|
elseif(CMAKE_SIZEOF_VOID_P EQUAL 8)
|
||||||
|
set(_bit_suffix "/64bit")
|
||||||
|
else()
|
||||||
|
set(_bit_suffix "/32bit")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
install(TARGETS ${additional_target}
|
||||||
|
LIBRARY DESTINATION "data${_bit_suffix}"
|
||||||
|
RUNTIME DESTINATION "data${_bit_suffix}")
|
||||||
|
add_custom_command(TARGET ${additional_target} POST_BUILD
|
||||||
|
COMMAND "${CMAKE_COMMAND}" -E copy
|
||||||
|
"$<TARGET_FILE:${additional_target}>"
|
||||||
|
"${EXTERNAL_PLUGIN_OUTPUT_DIR}/$<CONFIGURATION>/${target}/data${_bit_suffix}/$<TARGET_FILE_NAME:${additional_target}>"
|
||||||
|
VERBATIM)
|
||||||
|
endfunction()
|
||||||
|
|
||||||
|
# Installs an additional file in an architecture-specific data directory on windows/linux (data/32bit or data/64bit). Does not apply for mac.
|
||||||
|
# 'target' is the destination target project being installed to
|
||||||
|
# 'additional_target' specifies the additional binary
|
||||||
|
function(install_external_plugin_data_file_to_arch_data target additional_target file_target)
|
||||||
|
if(APPLE)
|
||||||
|
set(_bit_suffix "")
|
||||||
|
elseif(CMAKE_SIZEOF_VOID_P EQUAL 8)
|
||||||
|
set(_bit_suffix "/64bit")
|
||||||
|
else()
|
||||||
|
set(_bit_suffix "/32bit")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
get_filename_component(file_target_name ${file_target} NAME)
|
||||||
|
|
||||||
|
install(TARGETS ${additional_target}
|
||||||
|
LIBRARY DESTINATION "data${_bit_suffix}"
|
||||||
|
RUNTIME DESTINATION "data${_bit_suffix}")
|
||||||
|
add_custom_command(TARGET ${additional_target} POST_BUILD
|
||||||
|
COMMAND "${CMAKE_COMMAND}" -E copy
|
||||||
|
"${file_target}"
|
||||||
|
"${EXTERNAL_PLUGIN_OUTPUT_DIR}/$<CONFIGURATION>/${target}/data${_bit_suffix}/${file_target_name}"
|
||||||
|
VERBATIM)
|
||||||
|
endfunction()
|
||||||
113
file-switch.cpp
113
file-switch.cpp
@@ -1,113 +0,0 @@
|
|||||||
#include <QFileDialog>
|
|
||||||
#include <QTextStream>
|
|
||||||
#include <obs.hpp>
|
|
||||||
#include "advanced-scene-switcher.hpp"
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void SceneSwitcher::on_browseButton_clicked()
|
|
||||||
{
|
|
||||||
QString directory = QFileDialog::getOpenFileName(
|
|
||||||
this, tr("Select a file to write to ..."), QDir::currentPath(), tr("Text files (*.txt)"));
|
|
||||||
if (!directory.isEmpty())
|
|
||||||
ui->writePathLineEdit->setText(directory);
|
|
||||||
}
|
|
||||||
|
|
||||||
void SceneSwitcher::on_readFileCheckBox_stateChanged(int state)
|
|
||||||
{
|
|
||||||
if (loading)
|
|
||||||
return;
|
|
||||||
|
|
||||||
lock_guard<mutex> lock(switcher->m);
|
|
||||||
if (!state)
|
|
||||||
{
|
|
||||||
ui->browseButton_2->setDisabled(true);
|
|
||||||
ui->readPathLineEdit->setDisabled(true);
|
|
||||||
switcher->fileIO.readEnabled = false;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ui->browseButton_2->setDisabled(false);
|
|
||||||
ui->readPathLineEdit->setDisabled(false);
|
|
||||||
switcher->fileIO.readEnabled = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void SceneSwitcher::on_readPathLineEdit_textChanged(const QString& text)
|
|
||||||
{
|
|
||||||
if (loading)
|
|
||||||
return;
|
|
||||||
|
|
||||||
lock_guard<mutex> lock(switcher->m);
|
|
||||||
if (text.isEmpty())
|
|
||||||
{
|
|
||||||
switcher->fileIO.readEnabled = false;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
switcher->fileIO.readEnabled = true;
|
|
||||||
switcher->fileIO.readPath = text.toUtf8().constData();
|
|
||||||
}
|
|
||||||
|
|
||||||
void SceneSwitcher::on_writePathLineEdit_textChanged(const QString& text)
|
|
||||||
{
|
|
||||||
if (loading)
|
|
||||||
return;
|
|
||||||
|
|
||||||
lock_guard<mutex> lock(switcher->m);
|
|
||||||
if (text.isEmpty())
|
|
||||||
{
|
|
||||||
switcher->fileIO.writeEnabled = false;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
switcher->fileIO.writeEnabled = true;
|
|
||||||
switcher->fileIO.writePath = text.toUtf8().constData();
|
|
||||||
}
|
|
||||||
|
|
||||||
void SceneSwitcher::on_browseButton_2_clicked()
|
|
||||||
{
|
|
||||||
QString directory = QFileDialog::getOpenFileName(
|
|
||||||
this, tr("Select a file to read from ..."), QDir::currentPath(), tr("Text files (*.txt)"));
|
|
||||||
if (!directory.isEmpty())
|
|
||||||
ui->readPathLineEdit->setText(directory);
|
|
||||||
}
|
|
||||||
|
|
||||||
void SwitcherData::writeSceneInfoToFile()
|
|
||||||
{
|
|
||||||
obs_source_t* currentSource = obs_frontend_get_current_scene();
|
|
||||||
|
|
||||||
QFile file(QString::fromStdString(fileIO.writePath));
|
|
||||||
if (file.open(QIODevice::WriteOnly))
|
|
||||||
{
|
|
||||||
const char* msg = obs_source_get_name(currentSource);
|
|
||||||
file.write(msg, qstrlen(msg));
|
|
||||||
file.close();
|
|
||||||
}
|
|
||||||
obs_source_release(currentSource);
|
|
||||||
}
|
|
||||||
|
|
||||||
obs_weak_source_t* getNextTransition(obs_weak_source_t* scene1, obs_weak_source_t* scene2);
|
|
||||||
|
|
||||||
void SwitcherData::checkSwitchInfoFromFile(bool& match, OBSWeakSource& scene, OBSWeakSource& transition)
|
|
||||||
{
|
|
||||||
if (!fileIO.readEnabled)
|
|
||||||
return;
|
|
||||||
|
|
||||||
QFile file(QString::fromStdString(fileIO.readPath));
|
|
||||||
if (file.open(QIODevice::ReadOnly))
|
|
||||||
{
|
|
||||||
QTextStream in(&file);
|
|
||||||
QString sceneStr = in.readLine();
|
|
||||||
obs_source_t* sceneRead = obs_get_source_by_name(sceneStr.toUtf8().constData());
|
|
||||||
if (sceneRead){
|
|
||||||
obs_weak_source_t* sceneReadWs = obs_source_get_weak_source(sceneRead);
|
|
||||||
|
|
||||||
match = true;
|
|
||||||
scene = sceneReadWs;
|
|
||||||
transition = nullptr;
|
|
||||||
|
|
||||||
obs_weak_source_release(sceneReadWs);
|
|
||||||
obs_source_release(sceneRead);
|
|
||||||
}
|
|
||||||
file.close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
File diff suppressed because it is too large
Load Diff
95
general.cpp
95
general.cpp
@@ -1,95 +0,0 @@
|
|||||||
#include "advanced-scene-switcher.hpp"
|
|
||||||
#include <obs-module.h>
|
|
||||||
|
|
||||||
void SceneSwitcher::on_close_clicked()
|
|
||||||
{
|
|
||||||
done(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
void SceneSwitcher::on_startAtLaunch_toggled(bool value)
|
|
||||||
{
|
|
||||||
if (loading)
|
|
||||||
return;
|
|
||||||
|
|
||||||
lock_guard<mutex> lock(switcher->m);
|
|
||||||
switcher->startAtLaunch = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
void SceneSwitcher::UpdateNonMatchingScene(const QString& name)
|
|
||||||
{
|
|
||||||
obs_source_t* scene = obs_get_source_by_name(name.toUtf8().constData());
|
|
||||||
obs_weak_source_t* ws = obs_source_get_weak_source(scene);
|
|
||||||
|
|
||||||
switcher->nonMatchingScene = ws;
|
|
||||||
|
|
||||||
obs_weak_source_release(ws);
|
|
||||||
obs_source_release(scene);
|
|
||||||
}
|
|
||||||
|
|
||||||
void SceneSwitcher::on_noMatchDontSwitch_clicked()
|
|
||||||
{
|
|
||||||
if (loading)
|
|
||||||
return;
|
|
||||||
|
|
||||||
lock_guard<mutex> lock(switcher->m);
|
|
||||||
switcher->switchIfNotMatching = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void SceneSwitcher::on_noMatchSwitch_clicked()
|
|
||||||
{
|
|
||||||
if (loading)
|
|
||||||
return;
|
|
||||||
|
|
||||||
lock_guard<mutex> lock(switcher->m);
|
|
||||||
switcher->switchIfNotMatching = true;
|
|
||||||
UpdateNonMatchingScene(ui->noMatchSwitchScene->currentText());
|
|
||||||
}
|
|
||||||
|
|
||||||
void SceneSwitcher::on_noMatchSwitchScene_currentTextChanged(const QString& text)
|
|
||||||
{
|
|
||||||
if (loading)
|
|
||||||
return;
|
|
||||||
|
|
||||||
lock_guard<mutex> lock(switcher->m);
|
|
||||||
UpdateNonMatchingScene(text);
|
|
||||||
}
|
|
||||||
|
|
||||||
void SceneSwitcher::on_checkInterval_valueChanged(int value)
|
|
||||||
{
|
|
||||||
if (loading)
|
|
||||||
return;
|
|
||||||
|
|
||||||
lock_guard<mutex> lock(switcher->m);
|
|
||||||
switcher->interval = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
void SceneSwitcher::SetStarted()
|
|
||||||
{
|
|
||||||
ui->toggleStartButton->setText(obs_module_text("Stop"));
|
|
||||||
ui->pluginRunningText->setText(obs_module_text("Active"));
|
|
||||||
}
|
|
||||||
|
|
||||||
void SceneSwitcher::SetStopped()
|
|
||||||
{
|
|
||||||
ui->toggleStartButton->setText(obs_module_text("Start"));
|
|
||||||
ui->pluginRunningText->setText(obs_module_text("Inactive"));
|
|
||||||
}
|
|
||||||
|
|
||||||
void SceneSwitcher::on_toggleStartButton_clicked()
|
|
||||||
{
|
|
||||||
if (switcher->th.joinable())
|
|
||||||
{
|
|
||||||
switcher->Stop();
|
|
||||||
SetStopped();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
switcher->Start();
|
|
||||||
SetStarted();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void SceneSwitcher::closeEvent(QCloseEvent*)
|
|
||||||
{
|
|
||||||
obs_frontend_save();
|
|
||||||
}
|
|
||||||
72
hotkey.cpp
72
hotkey.cpp
@@ -1,72 +0,0 @@
|
|||||||
#include <obs-module.h>
|
|
||||||
#include "advanced-scene-switcher.hpp"
|
|
||||||
|
|
||||||
void startStopHotkeyFunc(void* data, obs_hotkey_id id, obs_hotkey_t* hotkey, bool pressed)
|
|
||||||
{
|
|
||||||
UNUSED_PARAMETER(data);
|
|
||||||
UNUSED_PARAMETER(hotkey);
|
|
||||||
|
|
||||||
if (pressed)
|
|
||||||
{
|
|
||||||
if (switcher->th.joinable())
|
|
||||||
switcher->Stop();
|
|
||||||
else
|
|
||||||
switcher->Start();
|
|
||||||
}
|
|
||||||
|
|
||||||
obs_data_array* hotkeyData = obs_hotkey_save(id);
|
|
||||||
|
|
||||||
if (hotkeyData != NULL)
|
|
||||||
{
|
|
||||||
char* path = obs_module_config_path("");
|
|
||||||
ofstream file;
|
|
||||||
file.open(string(path).append("hotkey.txt"), ofstream::trunc);
|
|
||||||
if (file.is_open())
|
|
||||||
{
|
|
||||||
size_t num = obs_data_array_count(hotkeyData);
|
|
||||||
for (size_t i = 0; i < num; i++)
|
|
||||||
{
|
|
||||||
obs_data_t* data = obs_data_array_item(hotkeyData, i);
|
|
||||||
string temp = obs_data_get_json(data);
|
|
||||||
obs_data_release(data);
|
|
||||||
file << temp;
|
|
||||||
}
|
|
||||||
file.close();
|
|
||||||
}
|
|
||||||
bfree(path);
|
|
||||||
}
|
|
||||||
obs_data_array_release(hotkeyData);
|
|
||||||
}
|
|
||||||
|
|
||||||
string loadConfigFile(string filename)
|
|
||||||
{
|
|
||||||
ifstream settingsFile;
|
|
||||||
char* path = obs_module_config_path("");
|
|
||||||
string value;
|
|
||||||
|
|
||||||
settingsFile.open(string(path).append(filename));
|
|
||||||
if (settingsFile.is_open())
|
|
||||||
{
|
|
||||||
settingsFile.seekg(0, ios::end);
|
|
||||||
value.reserve(settingsFile.tellg());
|
|
||||||
settingsFile.seekg(0, ios::beg);
|
|
||||||
value.assign((istreambuf_iterator<char>(settingsFile)), istreambuf_iterator<char>());
|
|
||||||
settingsFile.close();
|
|
||||||
}
|
|
||||||
bfree(path);
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
void loadKeybinding(obs_hotkey_id hotkeyId)
|
|
||||||
{
|
|
||||||
string bindings = loadConfigFile("hotkey.txt");
|
|
||||||
if (!bindings.empty())
|
|
||||||
{
|
|
||||||
obs_data_array_t* hotkeyData = obs_data_array_create();
|
|
||||||
obs_data_t* data = obs_data_create_from_json(bindings.c_str());
|
|
||||||
obs_data_array_insert(hotkeyData, 0, data);
|
|
||||||
obs_data_release(data);
|
|
||||||
obs_hotkey_load(hotkeyId, hotkeyData);
|
|
||||||
obs_data_array_release(hotkeyData);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
210
idle-switch.cpp
210
idle-switch.cpp
@@ -1,210 +0,0 @@
|
|||||||
#include "advanced-scene-switcher.hpp"
|
|
||||||
|
|
||||||
void SwitcherData::checkIdleSwitch(bool& match, OBSWeakSource& scene, OBSWeakSource& transition)
|
|
||||||
{
|
|
||||||
if (!idleData.idleEnable)
|
|
||||||
return;
|
|
||||||
|
|
||||||
string title;
|
|
||||||
bool ignoreIdle = false;
|
|
||||||
//lock.unlock();
|
|
||||||
GetCurrentWindowTitle(title);
|
|
||||||
//lock.lock();
|
|
||||||
|
|
||||||
for (string& window : ignoreIdleWindows)
|
|
||||||
{
|
|
||||||
if (window == title)
|
|
||||||
{
|
|
||||||
ignoreIdle = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!ignoreIdle)
|
|
||||||
{
|
|
||||||
for (string& window : ignoreIdleWindows)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
bool matches = regex_match(title, regex(window));
|
|
||||||
if (matches)
|
|
||||||
{
|
|
||||||
ignoreIdle = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (const regex_error&)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!ignoreIdle && secondsSinceLastInput() > idleData.time)
|
|
||||||
{
|
|
||||||
scene = idleData.scene;
|
|
||||||
transition = idleData.transition;
|
|
||||||
match = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void SceneSwitcher::on_idleCheckBox_stateChanged(int state)
|
|
||||||
{
|
|
||||||
if (loading)
|
|
||||||
return;
|
|
||||||
|
|
||||||
lock_guard<mutex> lock(switcher->m);
|
|
||||||
if (!state)
|
|
||||||
{
|
|
||||||
ui->idleScenes->setDisabled(true);
|
|
||||||
ui->idleSpinBox->setDisabled(true);
|
|
||||||
ui->idleTransitions->setDisabled(true);
|
|
||||||
|
|
||||||
switcher->idleData.idleEnable = false;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ui->idleScenes->setDisabled(false);
|
|
||||||
ui->idleSpinBox->setDisabled(false);
|
|
||||||
ui->idleTransitions->setDisabled(false);
|
|
||||||
|
|
||||||
switcher->idleData.idleEnable = true;
|
|
||||||
|
|
||||||
UpdateIdleDataTransition(ui->idleTransitions->currentText());
|
|
||||||
UpdateIdleDataScene(ui->idleScenes->currentText());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void SceneSwitcher::UpdateIdleDataTransition(const QString& name)
|
|
||||||
{
|
|
||||||
obs_weak_source_t* transition = GetWeakTransitionByQString(name);
|
|
||||||
switcher->idleData.transition = transition;
|
|
||||||
}
|
|
||||||
|
|
||||||
void SceneSwitcher::UpdateIdleDataScene(const QString& name)
|
|
||||||
{
|
|
||||||
obs_source_t* scene = obs_get_source_by_name(name.toUtf8().constData());
|
|
||||||
obs_weak_source_t* ws = obs_source_get_weak_source(scene);
|
|
||||||
|
|
||||||
switcher->idleData.scene = ws;
|
|
||||||
|
|
||||||
obs_weak_source_release(ws);
|
|
||||||
obs_source_release(scene);
|
|
||||||
}
|
|
||||||
|
|
||||||
void SceneSwitcher::on_idleTransitions_currentTextChanged(const QString& text)
|
|
||||||
{
|
|
||||||
if (loading)
|
|
||||||
return;
|
|
||||||
|
|
||||||
lock_guard<mutex> lock(switcher->m);
|
|
||||||
UpdateIdleDataTransition(text);
|
|
||||||
}
|
|
||||||
|
|
||||||
void SceneSwitcher::on_idleScenes_currentTextChanged(const QString& text)
|
|
||||||
{
|
|
||||||
if (loading)
|
|
||||||
return;
|
|
||||||
|
|
||||||
lock_guard<mutex> lock(switcher->m);
|
|
||||||
UpdateIdleDataScene(text);
|
|
||||||
}
|
|
||||||
|
|
||||||
void SceneSwitcher::on_idleSpinBox_valueChanged(int i)
|
|
||||||
{
|
|
||||||
if (loading)
|
|
||||||
return;
|
|
||||||
lock_guard<mutex> lock(switcher->m);
|
|
||||||
switcher->idleData.time = i;
|
|
||||||
}
|
|
||||||
|
|
||||||
void SceneSwitcher::on_ignoreIdleWindows_currentRowChanged(int idx)
|
|
||||||
{
|
|
||||||
if (loading)
|
|
||||||
return;
|
|
||||||
if (idx == -1)
|
|
||||||
return;
|
|
||||||
|
|
||||||
QListWidgetItem* item = ui->ignoreIdleWindows->item(idx);
|
|
||||||
|
|
||||||
QString window = item->data(Qt::UserRole).toString();
|
|
||||||
|
|
||||||
lock_guard<mutex> lock(switcher->m);
|
|
||||||
for (auto& w : switcher->ignoreIdleWindows)
|
|
||||||
{
|
|
||||||
if (window.compare(w.c_str()) == 0)
|
|
||||||
{
|
|
||||||
ui->ignoreIdleWindowsWindows->setCurrentText(w.c_str());
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void SceneSwitcher::on_ignoreIdleAdd_clicked()
|
|
||||||
{
|
|
||||||
QString windowName = ui->ignoreIdleWindowsWindows->currentText();
|
|
||||||
|
|
||||||
if (windowName.isEmpty())
|
|
||||||
return;
|
|
||||||
|
|
||||||
QVariant v = QVariant::fromValue(windowName);
|
|
||||||
|
|
||||||
QList<QListWidgetItem*> items = ui->ignoreIdleWindows->findItems(windowName, Qt::MatchExactly);
|
|
||||||
|
|
||||||
if (items.size() == 0)
|
|
||||||
{
|
|
||||||
QListWidgetItem* item = new QListWidgetItem(windowName, ui->ignoreIdleWindows);
|
|
||||||
item->setData(Qt::UserRole, v);
|
|
||||||
|
|
||||||
lock_guard<mutex> lock(switcher->m);
|
|
||||||
switcher->ignoreIdleWindows.emplace_back(windowName.toUtf8().constData());
|
|
||||||
ui->ignoreIdleWindows->sortItems();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void SceneSwitcher::on_ignoreIdleRemove_clicked()
|
|
||||||
{
|
|
||||||
QListWidgetItem* item = ui->ignoreIdleWindows->currentItem();
|
|
||||||
if (!item)
|
|
||||||
return;
|
|
||||||
|
|
||||||
QString windowName = item->data(Qt::UserRole).toString();
|
|
||||||
|
|
||||||
{
|
|
||||||
lock_guard<mutex> lock(switcher->m);
|
|
||||||
auto& windows = switcher->ignoreIdleWindows;
|
|
||||||
|
|
||||||
for (auto it = windows.begin(); it != windows.end(); ++it)
|
|
||||||
{
|
|
||||||
auto& s = *it;
|
|
||||||
|
|
||||||
if (s == windowName.toUtf8().constData())
|
|
||||||
{
|
|
||||||
windows.erase(it);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
delete item;
|
|
||||||
}
|
|
||||||
|
|
||||||
int SceneSwitcher::IgnoreIdleWindowsFindByData(const QString& window)
|
|
||||||
{
|
|
||||||
int count = ui->ignoreIdleWindows->count();
|
|
||||||
int idx = -1;
|
|
||||||
|
|
||||||
for (int i = 0; i < count; i++)
|
|
||||||
{
|
|
||||||
QListWidgetItem* item = ui->ignoreIdleWindows->item(i);
|
|
||||||
QString itemRegion = item->data(Qt::UserRole).toString();
|
|
||||||
|
|
||||||
if (itemRegion == window)
|
|
||||||
{
|
|
||||||
idx = i;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return idx;
|
|
||||||
}
|
|
||||||
242
pause-switch.cpp
242
pause-switch.cpp
@@ -1,242 +0,0 @@
|
|||||||
#include "advanced-scene-switcher.hpp"
|
|
||||||
|
|
||||||
void SceneSwitcher::on_pauseScenesAdd_clicked()
|
|
||||||
{
|
|
||||||
QString sceneName = ui->pauseScenesScenes->currentText();
|
|
||||||
|
|
||||||
if (sceneName.isEmpty())
|
|
||||||
return;
|
|
||||||
|
|
||||||
OBSWeakSource source = GetWeakSourceByQString(sceneName);
|
|
||||||
QVariant v = QVariant::fromValue(sceneName);
|
|
||||||
|
|
||||||
QList<QListWidgetItem*> items = ui->pauseScenes->findItems(sceneName, Qt::MatchExactly);
|
|
||||||
|
|
||||||
if (items.size() == 0)
|
|
||||||
{
|
|
||||||
QListWidgetItem* item = new QListWidgetItem(sceneName, ui->pauseScenes);
|
|
||||||
item->setData(Qt::UserRole, v);
|
|
||||||
|
|
||||||
lock_guard<mutex> lock(switcher->m);
|
|
||||||
switcher->pauseScenesSwitches.emplace_back(source);
|
|
||||||
ui->pauseScenes->sortItems();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void SceneSwitcher::on_pauseScenesRemove_clicked()
|
|
||||||
{
|
|
||||||
QListWidgetItem* item = ui->pauseScenes->currentItem();
|
|
||||||
if (!item)
|
|
||||||
return;
|
|
||||||
|
|
||||||
QString pauseScene = item->data(Qt::UserRole).toString();
|
|
||||||
|
|
||||||
{
|
|
||||||
lock_guard<mutex> lock(switcher->m);
|
|
||||||
auto& switches = switcher->pauseScenesSwitches;
|
|
||||||
|
|
||||||
for (auto it = switches.begin(); it != switches.end(); ++it)
|
|
||||||
{
|
|
||||||
auto& s = *it;
|
|
||||||
|
|
||||||
if (s == GetWeakSourceByQString(pauseScene))
|
|
||||||
{
|
|
||||||
switches.erase(it);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
delete item;
|
|
||||||
}
|
|
||||||
|
|
||||||
void SceneSwitcher::on_pauseWindowsAdd_clicked()
|
|
||||||
{
|
|
||||||
QString windowName = ui->pauseWindowsWindows->currentText();
|
|
||||||
|
|
||||||
if (windowName.isEmpty())
|
|
||||||
return;
|
|
||||||
|
|
||||||
QVariant v = QVariant::fromValue(windowName);
|
|
||||||
|
|
||||||
QList<QListWidgetItem*> items = ui->pauseWindows->findItems(windowName, Qt::MatchExactly);
|
|
||||||
|
|
||||||
if (items.size() == 0)
|
|
||||||
{
|
|
||||||
QListWidgetItem* item = new QListWidgetItem(windowName, ui->pauseWindows);
|
|
||||||
item->setData(Qt::UserRole, v);
|
|
||||||
|
|
||||||
lock_guard<mutex> lock(switcher->m);
|
|
||||||
switcher->pauseWindowsSwitches.emplace_back(windowName.toUtf8().constData());
|
|
||||||
ui->pauseWindows->sortItems();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void SceneSwitcher::on_pauseWindowsRemove_clicked()
|
|
||||||
{
|
|
||||||
QListWidgetItem* item = ui->pauseWindows->currentItem();
|
|
||||||
if (!item)
|
|
||||||
return;
|
|
||||||
|
|
||||||
QString windowName = item->data(Qt::UserRole).toString();
|
|
||||||
|
|
||||||
{
|
|
||||||
lock_guard<mutex> lock(switcher->m);
|
|
||||||
auto& switches = switcher->pauseWindowsSwitches;
|
|
||||||
|
|
||||||
for (auto it = switches.begin(); it != switches.end(); ++it)
|
|
||||||
{
|
|
||||||
auto& s = *it;
|
|
||||||
|
|
||||||
if (s == windowName.toUtf8().constData())
|
|
||||||
{
|
|
||||||
switches.erase(it);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
delete item;
|
|
||||||
}
|
|
||||||
|
|
||||||
void SceneSwitcher::on_pauseScenes_currentRowChanged(int idx)
|
|
||||||
{
|
|
||||||
if (loading)
|
|
||||||
return;
|
|
||||||
if (idx == -1)
|
|
||||||
return;
|
|
||||||
|
|
||||||
QListWidgetItem* item = ui->pauseScenes->item(idx);
|
|
||||||
|
|
||||||
QString scene = item->data(Qt::UserRole).toString();
|
|
||||||
|
|
||||||
lock_guard<mutex> lock(switcher->m);
|
|
||||||
for (auto& s : switcher->pauseScenesSwitches)
|
|
||||||
{
|
|
||||||
string name = GetWeakSourceName(s);
|
|
||||||
if (scene.compare(name.c_str()) == 0)
|
|
||||||
{
|
|
||||||
ui->pauseScenesScenes->setCurrentText(name.c_str());
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void SceneSwitcher::on_pauseWindows_currentRowChanged(int idx)
|
|
||||||
{
|
|
||||||
if (loading)
|
|
||||||
return;
|
|
||||||
if (idx == -1)
|
|
||||||
return;
|
|
||||||
|
|
||||||
QListWidgetItem* item = ui->pauseWindows->item(idx);
|
|
||||||
|
|
||||||
QString window = item->data(Qt::UserRole).toString();
|
|
||||||
|
|
||||||
lock_guard<mutex> lock(switcher->m);
|
|
||||||
for (auto& s : switcher->pauseWindowsSwitches)
|
|
||||||
{
|
|
||||||
if (window.compare(s.c_str()) == 0)
|
|
||||||
{
|
|
||||||
ui->pauseWindowsWindows->setCurrentText(s.c_str());
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int SceneSwitcher::PauseScenesFindByData(const QString& scene)
|
|
||||||
{
|
|
||||||
int count = ui->pauseScenes->count();
|
|
||||||
int idx = -1;
|
|
||||||
|
|
||||||
for (int i = 0; i < count; i++)
|
|
||||||
{
|
|
||||||
QListWidgetItem* item = ui->pauseScenes->item(i);
|
|
||||||
QString itemRegion = item->data(Qt::UserRole).toString();
|
|
||||||
|
|
||||||
if (itemRegion == scene)
|
|
||||||
{
|
|
||||||
idx = i;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return idx;
|
|
||||||
}
|
|
||||||
|
|
||||||
int SceneSwitcher::PauseWindowsFindByData(const QString& window)
|
|
||||||
{
|
|
||||||
int count = ui->pauseWindows->count();
|
|
||||||
int idx = -1;
|
|
||||||
|
|
||||||
for (int i = 0; i < count; i++)
|
|
||||||
{
|
|
||||||
QListWidgetItem* item = ui->pauseWindows->item(i);
|
|
||||||
QString itemRegion = item->data(Qt::UserRole).toString();
|
|
||||||
|
|
||||||
if (itemRegion == window)
|
|
||||||
{
|
|
||||||
idx = i;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return idx;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool SwitcherData::checkPause()
|
|
||||||
{
|
|
||||||
bool pause = false;
|
|
||||||
obs_source_t* currentSource = obs_frontend_get_current_scene();
|
|
||||||
obs_weak_source_t* ws = obs_source_get_weak_source(currentSource);
|
|
||||||
|
|
||||||
for (OBSWeakSource& s : pauseScenesSwitches)
|
|
||||||
{
|
|
||||||
if (s == ws)
|
|
||||||
{
|
|
||||||
pause = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
obs_source_release(currentSource);
|
|
||||||
obs_weak_source_release(ws);
|
|
||||||
|
|
||||||
string title;
|
|
||||||
if (!pause)
|
|
||||||
{
|
|
||||||
//lock.unlock();
|
|
||||||
GetCurrentWindowTitle(title);
|
|
||||||
//lock.lock();
|
|
||||||
for (string& window : pauseWindowsSwitches)
|
|
||||||
{
|
|
||||||
if (window == title)
|
|
||||||
{
|
|
||||||
pause = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!pause)
|
|
||||||
{
|
|
||||||
//lock.unlock();
|
|
||||||
GetCurrentWindowTitle(title);
|
|
||||||
//lock.lock();
|
|
||||||
for (string& window : pauseWindowsSwitches)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
bool matches = regex_match(title, regex(window));
|
|
||||||
if (matches)
|
|
||||||
{
|
|
||||||
pause = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (const regex_error&)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return pause;
|
|
||||||
}
|
|
||||||
57
priority.cpp
57
priority.cpp
@@ -1,57 +0,0 @@
|
|||||||
#include <algorithm>
|
|
||||||
#include "advanced-scene-switcher.hpp"
|
|
||||||
|
|
||||||
void SceneSwitcher::on_priorityUp_clicked()
|
|
||||||
{
|
|
||||||
int currentIndex = ui->priorityList->currentRow();
|
|
||||||
if (currentIndex != -1 && currentIndex != 0)
|
|
||||||
{
|
|
||||||
ui->priorityList->insertItem(currentIndex - 1 ,ui->priorityList->takeItem(currentIndex));
|
|
||||||
ui->priorityList->setCurrentRow(currentIndex -1);
|
|
||||||
lock_guard<mutex> lock(switcher->m);
|
|
||||||
|
|
||||||
iter_swap(switcher->functionNamesByPriority.begin() + currentIndex, switcher->functionNamesByPriority.begin() + currentIndex - 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void SceneSwitcher::on_priorityDown_clicked()
|
|
||||||
{
|
|
||||||
int currentIndex = ui->priorityList->currentRow();
|
|
||||||
if (currentIndex != -1 && currentIndex != ui->priorityList->count() - 1)
|
|
||||||
{
|
|
||||||
ui->priorityList->insertItem(currentIndex + 1, ui->priorityList->takeItem(currentIndex));
|
|
||||||
ui->priorityList->setCurrentRow(currentIndex + 1);
|
|
||||||
lock_guard<mutex> lock(switcher->m);
|
|
||||||
|
|
||||||
iter_swap(switcher->functionNamesByPriority.begin() + currentIndex, switcher->functionNamesByPriority.begin() + currentIndex + 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool prioStrValid(string s)
|
|
||||||
{
|
|
||||||
if (s == WINDOW_TITLE_FUNC)
|
|
||||||
return true;
|
|
||||||
if (s == SCREEN_REGION_FUNC)
|
|
||||||
return true;
|
|
||||||
if (s == ROUND_TRIP_FUNC)
|
|
||||||
return true;
|
|
||||||
if (s == (IDLE_FUNC))
|
|
||||||
return true;
|
|
||||||
if (s == EXE_FUNC)
|
|
||||||
return true;
|
|
||||||
if (s == READ_FILE_FUNC)
|
|
||||||
return true;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
bool SwitcherData::prioFuncsValid()
|
|
||||||
{
|
|
||||||
for (string f : functionNamesByPriority)
|
|
||||||
{
|
|
||||||
if (!prioStrValid(f))
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -1,308 +0,0 @@
|
|||||||
#include <QFileDialog>
|
|
||||||
#include <QTextStream>
|
|
||||||
#include <obs.hpp>
|
|
||||||
|
|
||||||
#include "advanced-scene-switcher.hpp"
|
|
||||||
|
|
||||||
obs_weak_source_t* getNextTransition(obs_weak_source_t* scene1, obs_weak_source_t* scene2);
|
|
||||||
|
|
||||||
void SceneSwitcher::on_sceneRoundTripAdd_clicked()
|
|
||||||
{
|
|
||||||
QString scene1Name = ui->sceneRoundTripScenes1->currentText();
|
|
||||||
QString scene2Name = ui->sceneRoundTripScenes2->currentText();
|
|
||||||
QString transitionName = ui->sceneRoundTripTransitions->currentText();
|
|
||||||
|
|
||||||
if (scene1Name.isEmpty() || scene2Name.isEmpty())
|
|
||||||
return;
|
|
||||||
|
|
||||||
int delay = ui->sceneRoundTripSpinBox->value();
|
|
||||||
|
|
||||||
if (scene1Name == scene2Name)
|
|
||||||
return;
|
|
||||||
|
|
||||||
OBSWeakSource source1 = GetWeakSourceByQString(scene1Name);
|
|
||||||
OBSWeakSource source2 = GetWeakSourceByQString(scene2Name);
|
|
||||||
OBSWeakSource transition = GetWeakTransitionByQString(transitionName);
|
|
||||||
|
|
||||||
QString text = MakeSceneRoundTripSwitchName(scene1Name, scene2Name, transitionName, delay);
|
|
||||||
QVariant v = QVariant::fromValue(text);
|
|
||||||
|
|
||||||
int idx = SceneRoundTripFindByData(scene1Name);
|
|
||||||
|
|
||||||
if (idx == -1)
|
|
||||||
{
|
|
||||||
QListWidgetItem* item = new QListWidgetItem(text, ui->sceneRoundTrips);
|
|
||||||
item->setData(Qt::UserRole, v);
|
|
||||||
|
|
||||||
lock_guard<mutex> lock(switcher->m);
|
|
||||||
switcher->sceneRoundTripSwitches.emplace_back(
|
|
||||||
source1, source2, transition, delay, text.toUtf8().constData());
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
QListWidgetItem* item = ui->sceneRoundTrips->item(idx);
|
|
||||||
item->setText(text);
|
|
||||||
|
|
||||||
{
|
|
||||||
lock_guard<mutex> lock(switcher->m);
|
|
||||||
for (auto& s : switcher->sceneRoundTripSwitches)
|
|
||||||
{
|
|
||||||
if (s.scene1 == source1 && s.scene2 == source2)
|
|
||||||
{
|
|
||||||
s.delay = delay;
|
|
||||||
s.transition = transition;
|
|
||||||
s.sceneRoundTripStr = text.toUtf8().constData();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ui->sceneRoundTrips->sortItems();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void SceneSwitcher::on_sceneRoundTripRemove_clicked()
|
|
||||||
{
|
|
||||||
QListWidgetItem* item = ui->sceneRoundTrips->currentItem();
|
|
||||||
if (!item)
|
|
||||||
return;
|
|
||||||
|
|
||||||
string text = item->data(Qt::UserRole).toString().toUtf8().constData();
|
|
||||||
|
|
||||||
{
|
|
||||||
lock_guard<mutex> lock(switcher->m);
|
|
||||||
auto& switches = switcher->sceneRoundTripSwitches;
|
|
||||||
|
|
||||||
for (auto it = switches.begin(); it != switches.end(); ++it)
|
|
||||||
{
|
|
||||||
auto& s = *it;
|
|
||||||
|
|
||||||
if (s.sceneRoundTripStr == text)
|
|
||||||
{
|
|
||||||
switches.erase(it);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
delete item;
|
|
||||||
}
|
|
||||||
|
|
||||||
void SceneSwitcher::on_autoStopSceneCheckBox_stateChanged(int state)
|
|
||||||
{
|
|
||||||
if (loading)
|
|
||||||
return;
|
|
||||||
|
|
||||||
lock_guard<mutex> lock(switcher->m);
|
|
||||||
if (!state)
|
|
||||||
{
|
|
||||||
ui->autoStopScenes->setDisabled(true);
|
|
||||||
switcher->autoStopEnable = false;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ui->autoStopScenes->setDisabled(false);
|
|
||||||
switcher->autoStopEnable = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void SceneSwitcher::UpdateAutoStopScene(const QString& name)
|
|
||||||
{
|
|
||||||
obs_source_t* scene = obs_get_source_by_name(name.toUtf8().constData());
|
|
||||||
obs_weak_source_t* ws = obs_source_get_weak_source(scene);
|
|
||||||
|
|
||||||
switcher->autoStopScene = ws;
|
|
||||||
|
|
||||||
obs_weak_source_release(ws);
|
|
||||||
obs_source_release(scene);
|
|
||||||
}
|
|
||||||
|
|
||||||
void SceneSwitcher::on_autoStopScenes_currentTextChanged(const QString& text)
|
|
||||||
{
|
|
||||||
if (loading)
|
|
||||||
return;
|
|
||||||
|
|
||||||
lock_guard<mutex> lock(switcher->m);
|
|
||||||
UpdateAutoStopScene(text);
|
|
||||||
}
|
|
||||||
|
|
||||||
void SceneSwitcher::on_sceneRoundTripSave_clicked()
|
|
||||||
{
|
|
||||||
QString directory = QFileDialog::getSaveFileName(
|
|
||||||
this, tr("Save Scene Round Trip to file ..."), QDir::currentPath(), tr("Text files (*.txt)"));
|
|
||||||
if (!directory.isEmpty())
|
|
||||||
{
|
|
||||||
QFile file(directory);
|
|
||||||
if (!file.open(QIODevice::WriteOnly | QIODevice::Text))
|
|
||||||
return;
|
|
||||||
QTextStream out(&file);
|
|
||||||
for (SceneRoundTripSwitch s : switcher->sceneRoundTripSwitches)
|
|
||||||
{
|
|
||||||
out << QString::fromStdString(GetWeakSourceName(s.scene1)) << "\n";
|
|
||||||
out << QString::fromStdString(GetWeakSourceName(s.scene2)) << "\n";
|
|
||||||
out << s.delay << "\n";
|
|
||||||
out << QString::fromStdString(s.sceneRoundTripStr) << "\n";
|
|
||||||
out << QString::fromStdString(GetWeakSourceName(s.transition)) << "\n";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void SceneSwitcher::on_sceneRoundTripLoad_clicked()
|
|
||||||
{
|
|
||||||
lock_guard<mutex> lock(switcher->m);
|
|
||||||
|
|
||||||
QString directory = QFileDialog::getOpenFileName(
|
|
||||||
this, tr("Select a file to read Scene Round Trip from ..."), QDir::currentPath(), tr("Text files (*.txt)"));
|
|
||||||
if (!directory.isEmpty())
|
|
||||||
{
|
|
||||||
QFile file(directory);
|
|
||||||
if (!file.open(QIODevice::ReadOnly | QIODevice::Text))
|
|
||||||
return;
|
|
||||||
|
|
||||||
QTextStream in(&file);
|
|
||||||
vector<QString> lines;
|
|
||||||
|
|
||||||
vector<SceneRoundTripSwitch> 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) && WeakSourceValid(scene2)
|
|
||||||
&& WeakSourceValid(transition))
|
|
||||||
{
|
|
||||||
newSceneRoundTripSwitch.emplace_back(SceneRoundTripSwitch(
|
|
||||||
GetWeakSourceByQString(lines[0]),
|
|
||||||
GetWeakSourceByQString(lines[1]),
|
|
||||||
GetWeakTransitionByQString(lines[4]),
|
|
||||||
lines[2].toInt(),
|
|
||||||
lines[3].toStdString()));
|
|
||||||
}
|
|
||||||
lines.clear();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//unvalid amount of lines in file or nothing valid read
|
|
||||||
if (lines.size() != 0 || newSceneRoundTripSwitch.size() == 0)
|
|
||||||
return;
|
|
||||||
|
|
||||||
switcher->sceneRoundTripSwitches.clear();
|
|
||||||
ui->sceneRoundTrips->clear();
|
|
||||||
switcher->sceneRoundTripSwitches = newSceneRoundTripSwitch;
|
|
||||||
for (SceneRoundTripSwitch s : switcher->sceneRoundTripSwitches)
|
|
||||||
{
|
|
||||||
QListWidgetItem* item = new QListWidgetItem(QString::fromStdString(s.sceneRoundTripStr), ui->sceneRoundTrips);
|
|
||||||
item->setData(Qt::UserRole, QString::fromStdString(s.sceneRoundTripStr));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void SwitcherData::checkSceneRoundTrip(bool& match, OBSWeakSource& scene, OBSWeakSource& transition, unique_lock<mutex>& lock)
|
|
||||||
{
|
|
||||||
bool sceneRoundTripActive = false;
|
|
||||||
obs_source_t* currentSource = obs_frontend_get_current_scene();
|
|
||||||
obs_weak_source_t* ws = obs_source_get_weak_source(currentSource);
|
|
||||||
|
|
||||||
for (SceneRoundTripSwitch& s : sceneRoundTripSwitches)
|
|
||||||
{
|
|
||||||
if (s.scene1 == ws)
|
|
||||||
{
|
|
||||||
sceneRoundTripActive = true;
|
|
||||||
int dur = s.delay * 1000 - interval;
|
|
||||||
if (dur > 0)
|
|
||||||
{
|
|
||||||
string s = obs_source_get_name(currentSource);
|
|
||||||
waitSceneName = s;
|
|
||||||
cv.wait_for(lock, chrono::milliseconds(dur));
|
|
||||||
}
|
|
||||||
obs_source_t* currentSource2 = obs_frontend_get_current_scene();
|
|
||||||
|
|
||||||
// only switch if user hasn't changed scene manually
|
|
||||||
if (currentSource == currentSource2)
|
|
||||||
{
|
|
||||||
match = true;
|
|
||||||
scene = s.scene2;
|
|
||||||
transition = s.transition;
|
|
||||||
}
|
|
||||||
obs_source_release(currentSource2);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
obs_source_release(currentSource);
|
|
||||||
obs_weak_source_release(ws);
|
|
||||||
}
|
|
||||||
|
|
||||||
void SceneSwitcher::on_sceneRoundTrips_currentRowChanged(int idx)
|
|
||||||
{
|
|
||||||
if (loading)
|
|
||||||
return;
|
|
||||||
if (idx == -1)
|
|
||||||
return;
|
|
||||||
|
|
||||||
QListWidgetItem* item = ui->sceneRoundTrips->item(idx);
|
|
||||||
|
|
||||||
QString sceneRoundTrip = item->data(Qt::UserRole).toString();
|
|
||||||
|
|
||||||
lock_guard<mutex> lock(switcher->m);
|
|
||||||
for (auto& s : switcher->sceneRoundTripSwitches)
|
|
||||||
{
|
|
||||||
if (sceneRoundTrip.compare(s.sceneRoundTripStr.c_str()) == 0)
|
|
||||||
{
|
|
||||||
string scene1 = GetWeakSourceName(s.scene1);
|
|
||||||
string scene2 = GetWeakSourceName(s.scene2);
|
|
||||||
string transitionName = GetWeakSourceName(s.transition);
|
|
||||||
int delay = s.delay;
|
|
||||||
ui->sceneRoundTripScenes1->setCurrentText(scene1.c_str());
|
|
||||||
ui->sceneRoundTripScenes2->setCurrentText(scene2.c_str());
|
|
||||||
ui->sceneRoundTripTransitions->setCurrentText(transitionName.c_str());
|
|
||||||
ui->sceneRoundTripSpinBox->setValue(delay);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int SceneSwitcher::SceneRoundTripFindByData(const QString& scene1)
|
|
||||||
{
|
|
||||||
QRegExp rx(scene1 + " -> wait for [0-9]* seconds -> .*");
|
|
||||||
int count = ui->sceneRoundTrips->count();
|
|
||||||
int idx = -1;
|
|
||||||
|
|
||||||
for (int i = 0; i < count; i++)
|
|
||||||
{
|
|
||||||
QListWidgetItem* item = ui->sceneRoundTrips->item(i);
|
|
||||||
QString itemString = item->data(Qt::UserRole).toString();
|
|
||||||
|
|
||||||
if (rx.exactMatch(itemString))
|
|
||||||
{
|
|
||||||
idx = i;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return idx;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void SwitcherData::autoStopStreamAndRecording()
|
|
||||||
{
|
|
||||||
obs_source_t* currentSource = obs_frontend_get_current_scene();
|
|
||||||
obs_weak_source_t* ws = obs_source_get_weak_source(currentSource);
|
|
||||||
|
|
||||||
if (ws && autoStopScene == ws)
|
|
||||||
{
|
|
||||||
if (obs_frontend_streaming_active())
|
|
||||||
obs_frontend_streaming_stop();
|
|
||||||
if (obs_frontend_recording_active())
|
|
||||||
obs_frontend_recording_stop();
|
|
||||||
}
|
|
||||||
obs_source_release(currentSource);
|
|
||||||
obs_weak_source_release(ws);
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -1,283 +0,0 @@
|
|||||||
#include "advanced-scene-switcher.hpp"
|
|
||||||
|
|
||||||
void SceneSwitcher::on_transitionsAdd_clicked()
|
|
||||||
{
|
|
||||||
QString scene1Name = ui->transitionsScene1->currentText();
|
|
||||||
QString scene2Name = ui->transitionsScene2->currentText();
|
|
||||||
QString transitionName = ui->transitionsTransitions->currentText();
|
|
||||||
|
|
||||||
if (scene1Name.isEmpty() || scene2Name.isEmpty())
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (scene1Name == scene2Name)
|
|
||||||
return;
|
|
||||||
|
|
||||||
OBSWeakSource source1 = GetWeakSourceByQString(scene1Name);
|
|
||||||
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);
|
|
||||||
|
|
||||||
lock_guard<mutex> lock(switcher->m);
|
|
||||||
switcher->sceneTransitions.emplace_back(
|
|
||||||
source1, source2, transition, text.toUtf8().constData());
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
QListWidgetItem* item = ui->sceneTransitions->item(idx);
|
|
||||||
item->setText(text);
|
|
||||||
|
|
||||||
{
|
|
||||||
lock_guard<mutex> lock(switcher->m);
|
|
||||||
for (auto& s : switcher->sceneTransitions)
|
|
||||||
{
|
|
||||||
if (s.scene1 == source1 && s.scene2 == source2)
|
|
||||||
{
|
|
||||||
s.transition = transition;
|
|
||||||
s.sceneTransitionStr = text.toUtf8().constData();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ui->sceneTransitions->sortItems();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void SceneSwitcher::on_transitionsRemove_clicked()
|
|
||||||
{
|
|
||||||
QListWidgetItem* item = ui->sceneTransitions->currentItem();
|
|
||||||
if (!item)
|
|
||||||
return;
|
|
||||||
|
|
||||||
string text = item->data(Qt::UserRole).toString().toUtf8().constData();
|
|
||||||
|
|
||||||
{
|
|
||||||
lock_guard<mutex> lock(switcher->m);
|
|
||||||
auto& switches = switcher->sceneTransitions;
|
|
||||||
|
|
||||||
for (auto it = switches.begin(); it != switches.end(); ++it)
|
|
||||||
{
|
|
||||||
auto& s = *it;
|
|
||||||
|
|
||||||
if (s.sceneTransitionStr == text)
|
|
||||||
{
|
|
||||||
switches.erase(it);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
delete item;
|
|
||||||
}
|
|
||||||
|
|
||||||
void SceneSwitcher::on_defaultTransitionsAdd_clicked()
|
|
||||||
{
|
|
||||||
QString sceneName = ui->defaultTransitionsScene->currentText();
|
|
||||||
QString transitionName = ui->defaultTransitionsTransitions->currentText();
|
|
||||||
|
|
||||||
if (sceneName.isEmpty() || transitionName.isEmpty())
|
|
||||||
return;
|
|
||||||
|
|
||||||
OBSWeakSource source = GetWeakSourceByQString(sceneName);
|
|
||||||
OBSWeakSource transition = GetWeakTransitionByQString(transitionName);
|
|
||||||
|
|
||||||
QString text = MakeDefaultSceneTransitionName(sceneName, transitionName);
|
|
||||||
QVariant v = QVariant::fromValue(text);
|
|
||||||
|
|
||||||
int idx = DefaultTransitionsFindByData(sceneName);
|
|
||||||
|
|
||||||
if (idx == -1)
|
|
||||||
{
|
|
||||||
QListWidgetItem* item = new QListWidgetItem(text, ui->defaultTransitions);
|
|
||||||
item->setData(Qt::UserRole, v);
|
|
||||||
|
|
||||||
lock_guard<mutex> lock(switcher->m);
|
|
||||||
switcher->defaultSceneTransitions.emplace_back(
|
|
||||||
source, transition, text.toUtf8().constData());
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
QListWidgetItem* item = ui->defaultTransitions->item(idx);
|
|
||||||
item->setText(text);
|
|
||||||
|
|
||||||
{
|
|
||||||
lock_guard<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()
|
|
||||||
{
|
|
||||||
QListWidgetItem* item = ui->defaultTransitions->currentItem();
|
|
||||||
if (!item)
|
|
||||||
return;
|
|
||||||
|
|
||||||
string text = item->data(Qt::UserRole).toString().toUtf8().constData();
|
|
||||||
|
|
||||||
{
|
|
||||||
lock_guard<mutex> lock(switcher->m);
|
|
||||||
auto& switches = switcher->defaultSceneTransitions;
|
|
||||||
|
|
||||||
for (auto it = switches.begin(); it != switches.end(); ++it)
|
|
||||||
{
|
|
||||||
auto& s = *it;
|
|
||||||
|
|
||||||
if (s.sceneTransitionStr == text)
|
|
||||||
{
|
|
||||||
switches.erase(it);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
delete item;
|
|
||||||
}
|
|
||||||
|
|
||||||
void SwitcherData::setDefaultSceneTransitions(unique_lock<mutex>& lock)
|
|
||||||
{
|
|
||||||
obs_source_t* currentSource = obs_frontend_get_current_scene();
|
|
||||||
obs_weak_source_t* ws = obs_source_get_weak_source(currentSource);
|
|
||||||
|
|
||||||
for (DefaultSceneTransition& s : defaultSceneTransitions)
|
|
||||||
{
|
|
||||||
if (s.scene == ws)
|
|
||||||
{
|
|
||||||
obs_source_t* transition = obs_weak_source_get_source(s.transition);
|
|
||||||
//sleep planned but no fitting OBS event to measure transition time
|
|
||||||
obs_frontend_set_current_transition(transition);
|
|
||||||
obs_source_release(transition);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
obs_source_release(currentSource);
|
|
||||||
obs_weak_source_release(ws);
|
|
||||||
}
|
|
||||||
|
|
||||||
int SceneSwitcher::SceneTransitionsFindByData(const QString& scene1, const QString& scene2)
|
|
||||||
{
|
|
||||||
QRegExp rx(scene1 + " --- .* --> " + scene2);
|
|
||||||
int count = ui->sceneTransitions->count();
|
|
||||||
int idx = -1;
|
|
||||||
|
|
||||||
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)
|
|
||||||
{
|
|
||||||
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();
|
|
||||||
|
|
||||||
lock_guard<mutex> lock(switcher->m);
|
|
||||||
for (auto& s : switcher->sceneTransitions)
|
|
||||||
{
|
|
||||||
if (sceneTransition.compare(s.sceneTransitionStr.c_str()) == 0)
|
|
||||||
{
|
|
||||||
string scene1 = GetWeakSourceName(s.scene1);
|
|
||||||
string scene2 = GetWeakSourceName(s.scene2);
|
|
||||||
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();
|
|
||||||
|
|
||||||
lock_guard<mutex> lock(switcher->m);
|
|
||||||
for (auto& s : switcher->defaultSceneTransitions)
|
|
||||||
{
|
|
||||||
if (sceneTransition.compare(s.sceneTransitionStr.c_str()) == 0)
|
|
||||||
{
|
|
||||||
string scene = GetWeakSourceName(s.scene);
|
|
||||||
string transitionName = GetWeakSourceName(s.transition);
|
|
||||||
ui->defaultTransitionsScene->setCurrentText(scene.c_str());
|
|
||||||
ui->defaultTransitionsTransitions->setCurrentText(transitionName.c_str());
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
obs_weak_source_t* getNextTransition(obs_weak_source_t* scene1, obs_weak_source_t* scene2)
|
|
||||||
{
|
|
||||||
obs_weak_source_t* ws = nullptr;
|
|
||||||
if (scene1 && scene2)
|
|
||||||
{
|
|
||||||
for (SceneTransition& t : switcher->sceneTransitions)
|
|
||||||
{
|
|
||||||
if (t.scene1 == scene1 && t.scene2 == scene2)
|
|
||||||
ws = t.transition;
|
|
||||||
}
|
|
||||||
obs_weak_source_addref(ws);
|
|
||||||
}
|
|
||||||
return ws;
|
|
||||||
}
|
|
||||||
@@ -1,165 +0,0 @@
|
|||||||
#include "advanced-scene-switcher.hpp"
|
|
||||||
|
|
||||||
void SwitcherData::checkScreenRegionSwitch(bool& match, OBSWeakSource& scene, OBSWeakSource& transition)
|
|
||||||
{
|
|
||||||
pair<int, int> cursorPos = getCursorPos();
|
|
||||||
int minRegionSize = 99999;
|
|
||||||
|
|
||||||
for (auto& s : screenRegionSwitches)
|
|
||||||
{
|
|
||||||
if (cursorPos.first >= s.minX && cursorPos.second >= s.minY
|
|
||||||
&& cursorPos.first <= s.maxX && cursorPos.second <= s.maxY)
|
|
||||||
{
|
|
||||||
int regionSize = (s.maxX - s.minX) + (s.maxY - s.minY);
|
|
||||||
if (regionSize < minRegionSize)
|
|
||||||
{
|
|
||||||
match = true;
|
|
||||||
scene = s.scene;
|
|
||||||
transition = s.transition;
|
|
||||||
minRegionSize = regionSize;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void SceneSwitcher::updateScreenRegionCursorPos()
|
|
||||||
{
|
|
||||||
pair<int, int> position = getCursorPos();
|
|
||||||
ui->cursorXPosition->setText(QString::number(position.first));
|
|
||||||
;
|
|
||||||
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();
|
|
||||||
|
|
||||||
string regionStr = to_string(minX) + ", " + to_string(minY) + " x " + to_string(maxX) + ", "
|
|
||||||
+ 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);
|
|
||||||
|
|
||||||
lock_guard<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);
|
|
||||||
|
|
||||||
string curRegion = region.toUtf8().constData();
|
|
||||||
|
|
||||||
{
|
|
||||||
lock_guard<mutex> lock(switcher->m);
|
|
||||||
for (auto& s : switcher->screenRegionSwitches)
|
|
||||||
{
|
|
||||||
if (s.regionStr == curRegion)
|
|
||||||
{
|
|
||||||
s.scene = source;
|
|
||||||
s.transition = transition;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ui->screenRegions->sortItems();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void SceneSwitcher::on_screenRegionRemove_clicked()
|
|
||||||
{
|
|
||||||
QListWidgetItem* item = ui->screenRegions->currentItem();
|
|
||||||
if (!item)
|
|
||||||
return;
|
|
||||||
|
|
||||||
string region = item->data(Qt::UserRole).toString().toUtf8().constData();
|
|
||||||
|
|
||||||
{
|
|
||||||
lock_guard<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();
|
|
||||||
|
|
||||||
lock_guard<mutex> lock(switcher->m);
|
|
||||||
for (auto& s : switcher->screenRegionSwitches)
|
|
||||||
{
|
|
||||||
if (region.compare(s.regionStr.c_str()) == 0)
|
|
||||||
{
|
|
||||||
string name = GetWeakSourceName(s.scene);
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
@@ -1,7 +1,6 @@
|
|||||||
#include <obs-module.h>
|
#include <obs-module.h>
|
||||||
|
|
||||||
OBS_DECLARE_MODULE()
|
OBS_DECLARE_MODULE()
|
||||||
OBS_MODULE_USE_DEFAULT_LOCALE("frontend-tools", "en-US")
|
|
||||||
|
|
||||||
void InitSceneSwitcher();
|
void InitSceneSwitcher();
|
||||||
void FreeSceneSwitcher();
|
void FreeSceneSwitcher();
|
||||||
414
src/advanced-scene-switcher.cpp
Normal file
414
src/advanced-scene-switcher.cpp
Normal file
@@ -0,0 +1,414 @@
|
|||||||
|
#include <QMainWindow>
|
||||||
|
#include <QDir>
|
||||||
|
#include <QAction>
|
||||||
|
|
||||||
|
#include <condition_variable>
|
||||||
|
#include <chrono>
|
||||||
|
#include <vector>
|
||||||
|
#include <mutex>
|
||||||
|
#include <thread>
|
||||||
|
|
||||||
|
#include <obs-frontend-api.h>
|
||||||
|
#include <obs-module.h>
|
||||||
|
#include <obs.hpp>
|
||||||
|
#include <util/util.hpp>
|
||||||
|
|
||||||
|
#include "headers/switcher-data-structs.hpp"
|
||||||
|
#include "headers/advanced-scene-switcher.hpp"
|
||||||
|
#include "headers/utility.hpp"
|
||||||
|
|
||||||
|
SwitcherData *switcher = nullptr;
|
||||||
|
|
||||||
|
/********************************************************************************
|
||||||
|
* Create the Advanced Scene Switcher settings window
|
||||||
|
********************************************************************************/
|
||||||
|
SceneSwitcher::SceneSwitcher(QWidget *parent)
|
||||||
|
: QDialog(parent), ui(new Ui_SceneSwitcher)
|
||||||
|
{
|
||||||
|
ui->setupUi(this);
|
||||||
|
|
||||||
|
std::lock_guard<std::mutex> lock(switcher->m);
|
||||||
|
|
||||||
|
switcher->Prune();
|
||||||
|
loadUI();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SceneSwitcher::populateSceneSelection(QComboBox *sel, bool addPrevious)
|
||||||
|
{
|
||||||
|
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__
|
||||||
|
setMinimumHeight(700);
|
||||||
|
#endif
|
||||||
|
setupGeneralTab();
|
||||||
|
setupTitleTab();
|
||||||
|
setupExecutableTab();
|
||||||
|
setupRegionTab();
|
||||||
|
setupPauseTab();
|
||||||
|
setupSequenceTab();
|
||||||
|
setupTransitionsTab();
|
||||||
|
setupIdleTab();
|
||||||
|
setupRandomTab();
|
||||||
|
setupMediaTab();
|
||||||
|
setupFileTab();
|
||||||
|
setupTimeTab();
|
||||||
|
|
||||||
|
setTabOrder();
|
||||||
|
|
||||||
|
loading = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/********************************************************************************
|
||||||
|
* Saving and loading
|
||||||
|
********************************************************************************/
|
||||||
|
static void SaveSceneSwitcher(obs_data_t *save_data, bool saving, void *)
|
||||||
|
{
|
||||||
|
if (saving) {
|
||||||
|
std::lock_guard<std::mutex> lock(switcher->m);
|
||||||
|
|
||||||
|
switcher->Prune();
|
||||||
|
|
||||||
|
obs_data_t *obj = obs_data_create();
|
||||||
|
|
||||||
|
switcher->saveWindowTitleSwitches(obj);
|
||||||
|
switcher->saveScreenRegionSwitches(obj);
|
||||||
|
switcher->savePauseSwitches(obj);
|
||||||
|
switcher->saveSceneRoundTripSwitches(obj);
|
||||||
|
switcher->saveSceneTransitions(obj);
|
||||||
|
switcher->saveIdleSwitches(obj);
|
||||||
|
switcher->saveExecutableSwitches(obj);
|
||||||
|
switcher->saveRandomSwitches(obj);
|
||||||
|
switcher->saveFileSwitches(obj);
|
||||||
|
switcher->saveMediaSwitches(obj);
|
||||||
|
switcher->saveTimeSwitches(obj);
|
||||||
|
switcher->saveGeneralSettings(obj);
|
||||||
|
|
||||||
|
obs_data_set_obj(save_data, "advanced-scene-switcher", obj);
|
||||||
|
|
||||||
|
obs_data_release(obj);
|
||||||
|
} else {
|
||||||
|
switcher->m.lock();
|
||||||
|
|
||||||
|
obs_data_t *obj =
|
||||||
|
obs_data_get_obj(save_data, "advanced-scene-switcher");
|
||||||
|
|
||||||
|
if (!obj)
|
||||||
|
obj = obs_data_create();
|
||||||
|
|
||||||
|
switcher->loadWindowTitleSwitches(obj);
|
||||||
|
switcher->loadScreenRegionSwitches(obj);
|
||||||
|
switcher->loadPauseSwitches(obj);
|
||||||
|
switcher->loadSceneRoundTripSwitches(obj);
|
||||||
|
switcher->loadSceneTransitions(obj);
|
||||||
|
switcher->loadIdleSwitches(obj);
|
||||||
|
switcher->loadExecutableSwitches(obj);
|
||||||
|
switcher->loadRandomSwitches(obj);
|
||||||
|
switcher->loadFileSwitches(obj);
|
||||||
|
switcher->loadMediaSwitches(obj);
|
||||||
|
switcher->loadTimeSwitches(obj);
|
||||||
|
switcher->loadGeneralSettings(obj);
|
||||||
|
|
||||||
|
obs_data_release(obj);
|
||||||
|
|
||||||
|
switcher->m.unlock();
|
||||||
|
|
||||||
|
if (switcher->stop)
|
||||||
|
switcher->Stop();
|
||||||
|
else
|
||||||
|
switcher->Start();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/********************************************************************************
|
||||||
|
* Main switcher thread
|
||||||
|
********************************************************************************/
|
||||||
|
void SwitcherData::Thread()
|
||||||
|
{
|
||||||
|
blog(LOG_INFO, "Advanced Scene Switcher started");
|
||||||
|
//to avoid scene duplication when rapidly switching scene collection
|
||||||
|
std::this_thread::sleep_for(std::chrono::seconds(2));
|
||||||
|
|
||||||
|
int sleep = 0;
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
startLoop:
|
||||||
|
std::unique_lock<std::mutex> lock(m);
|
||||||
|
bool match = false;
|
||||||
|
OBSWeakSource scene;
|
||||||
|
OBSWeakSource transition;
|
||||||
|
std::chrono::milliseconds duration;
|
||||||
|
if (sleep > interval) {
|
||||||
|
duration = std::chrono::milliseconds(sleep);
|
||||||
|
if (verbose)
|
||||||
|
blog(LOG_INFO,
|
||||||
|
"Advanced Scene Switcher sleep for %d",
|
||||||
|
sleep);
|
||||||
|
} else {
|
||||||
|
duration = std::chrono::milliseconds(interval);
|
||||||
|
if (verbose)
|
||||||
|
blog(LOG_INFO,
|
||||||
|
"Advanced Scene Switcher sleep for %d",
|
||||||
|
interval);
|
||||||
|
}
|
||||||
|
sleep = 0;
|
||||||
|
switcher->Prune();
|
||||||
|
writeSceneInfoToFile();
|
||||||
|
//sleep for a bit
|
||||||
|
cv.wait_for(lock, duration);
|
||||||
|
if (switcher->stop) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
setDefaultSceneTransitions();
|
||||||
|
if (autoStopEnable) {
|
||||||
|
autoStopStreamAndRecording();
|
||||||
|
}
|
||||||
|
if (checkPause()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int switchFuncName : functionNamesByPriority) {
|
||||||
|
switch (switchFuncName) {
|
||||||
|
case READ_FILE_FUNC:
|
||||||
|
checkSwitchInfoFromFile(match, scene,
|
||||||
|
transition);
|
||||||
|
checkFileContent(match, scene, transition);
|
||||||
|
break;
|
||||||
|
case IDLE_FUNC:
|
||||||
|
checkIdleSwitch(match, scene, transition);
|
||||||
|
break;
|
||||||
|
case EXE_FUNC:
|
||||||
|
checkExeSwitch(match, scene, transition);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SCREEN_REGION_FUNC:
|
||||||
|
checkScreenRegionSwitch(match, scene,
|
||||||
|
transition);
|
||||||
|
break;
|
||||||
|
case WINDOW_TITLE_FUNC:
|
||||||
|
checkWindowTitleSwitch(match, scene,
|
||||||
|
transition);
|
||||||
|
break;
|
||||||
|
case ROUND_TRIP_FUNC:
|
||||||
|
checkSceneRoundTrip(match, scene, transition,
|
||||||
|
lock);
|
||||||
|
if (sceneChangedDuringWait()) //scene might have changed during the sleep
|
||||||
|
{
|
||||||
|
goto startLoop;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case MEDIA_FUNC:
|
||||||
|
checkMediaSwitch(match, scene, transition);
|
||||||
|
break;
|
||||||
|
case TIME_FUNC:
|
||||||
|
checkTimeSwitch(match, scene, transition);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (switcher->stop) {
|
||||||
|
goto endLoop;
|
||||||
|
}
|
||||||
|
if (match) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!match && switchIfNotMatching == SWITCH &&
|
||||||
|
nonMatchingScene) {
|
||||||
|
match = true;
|
||||||
|
scene = nonMatchingScene;
|
||||||
|
transition = nullptr;
|
||||||
|
}
|
||||||
|
if (!match && switchIfNotMatching == RANDOM_SWITCH) {
|
||||||
|
checkRandom(match, scene, transition, sleep);
|
||||||
|
}
|
||||||
|
if (match) {
|
||||||
|
switchScene(scene, transition,
|
||||||
|
tansitionOverrideOverride, lock);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
endLoop:
|
||||||
|
blog(LOG_INFO, "Advanced Scene Switcher stopped");
|
||||||
|
}
|
||||||
|
|
||||||
|
void switchScene(OBSWeakSource &scene, OBSWeakSource &transition,
|
||||||
|
bool &transitionOverrideOverride,
|
||||||
|
std::unique_lock<std::mutex> &lock)
|
||||||
|
{
|
||||||
|
obs_source_t *source = obs_weak_source_get_source(scene);
|
||||||
|
obs_source_t *currentSource = obs_frontend_get_current_scene();
|
||||||
|
|
||||||
|
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;
|
||||||
|
setNextTransition(scene, currentSource, transition,
|
||||||
|
transitionOverrideOverride, td);
|
||||||
|
obs_frontend_set_current_scene(source);
|
||||||
|
if (transitionOverrideOverride)
|
||||||
|
restoreTransitionOverride(source, td);
|
||||||
|
lock.lock();
|
||||||
|
|
||||||
|
if (switcher->verbose)
|
||||||
|
blog(LOG_INFO,
|
||||||
|
"Advanced Scene Switcher switched scene");
|
||||||
|
}
|
||||||
|
obs_source_release(currentSource);
|
||||||
|
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()
|
||||||
|
{
|
||||||
|
if (!(th && th->isRunning())) {
|
||||||
|
stop = false;
|
||||||
|
switcher->th = new SwitcherThread();
|
||||||
|
switcher->th->start(
|
||||||
|
(QThread::Priority)switcher->threadPriority);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SwitcherData::Stop()
|
||||||
|
{
|
||||||
|
if (th && th->isRunning()) {
|
||||||
|
switcher->stop = true;
|
||||||
|
transitionCv.notify_one();
|
||||||
|
cv.notify_one();
|
||||||
|
th->wait();
|
||||||
|
delete th;
|
||||||
|
th = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/********************************************************************************
|
||||||
|
* OBS module setup
|
||||||
|
********************************************************************************/
|
||||||
|
extern "C" void FreeSceneSwitcher()
|
||||||
|
{
|
||||||
|
delete switcher;
|
||||||
|
switcher = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void handleSceneChange(SwitcherData *s)
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(s->m);
|
||||||
|
//stop waiting if scene was manually changed
|
||||||
|
if (s->sceneChangedDuringWait())
|
||||||
|
s->cv.notify_one();
|
||||||
|
|
||||||
|
//set previous scene
|
||||||
|
obs_source_t *source = obs_frontend_get_current_scene();
|
||||||
|
obs_weak_source_t *ws = obs_source_get_weak_source(source);
|
||||||
|
obs_source_release(source);
|
||||||
|
obs_weak_source_release(ws);
|
||||||
|
if (source && s->PreviousScene2 != ws) {
|
||||||
|
s->previousScene = s->PreviousScene2;
|
||||||
|
s->PreviousScene2 = ws;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void OBSEvent(enum obs_frontend_event event, void *switcher)
|
||||||
|
{
|
||||||
|
switch (event) {
|
||||||
|
case OBS_FRONTEND_EVENT_EXIT:
|
||||||
|
FreeSceneSwitcher();
|
||||||
|
break;
|
||||||
|
case OBS_FRONTEND_EVENT_SCENE_CHANGED:
|
||||||
|
handleSceneChange((SwitcherData *)switcher);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" void InitSceneSwitcher()
|
||||||
|
{
|
||||||
|
QAction *action = (QAction *)obs_frontend_add_tools_menu_qaction(
|
||||||
|
"Advanced Scene Switcher");
|
||||||
|
|
||||||
|
switcher = new SwitcherData;
|
||||||
|
|
||||||
|
auto cb = []() {
|
||||||
|
QMainWindow *window =
|
||||||
|
(QMainWindow *)obs_frontend_get_main_window();
|
||||||
|
|
||||||
|
SceneSwitcher ss(window);
|
||||||
|
ss.exec();
|
||||||
|
};
|
||||||
|
|
||||||
|
obs_frontend_add_save_callback(SaveSceneSwitcher, nullptr);
|
||||||
|
obs_frontend_add_event_callback(OBSEvent, switcher);
|
||||||
|
|
||||||
|
action->connect(action, &QAction::triggered, cb);
|
||||||
|
|
||||||
|
char *path = obs_module_config_path("");
|
||||||
|
QDir dir(path);
|
||||||
|
if (!dir.exists()) {
|
||||||
|
dir.mkpath(".");
|
||||||
|
}
|
||||||
|
bfree(path);
|
||||||
|
|
||||||
|
obs_hotkey_id toggleHotkeyId = obs_hotkey_register_frontend(
|
||||||
|
"startStopToggleSwitcherHotkey",
|
||||||
|
"Toggle Start/Stop for the Advanced Scene Switcher",
|
||||||
|
startStopToggleHotkeyFunc, NULL);
|
||||||
|
loadKeybinding(toggleHotkeyId, TOGGLE_HOTKEY_PATH);
|
||||||
|
obs_hotkey_id startHotkeyId = obs_hotkey_register_frontend(
|
||||||
|
"startSwitcherHotkey", "Start the Advanced Scene Switcher",
|
||||||
|
startHotkeyFunc, NULL);
|
||||||
|
loadKeybinding(startHotkeyId, START_HOTKEY_PATH);
|
||||||
|
obs_hotkey_id stopHotkeyId = obs_hotkey_register_frontend(
|
||||||
|
"stopSwitcherHotkey", "Stop the Advanced Scene Switcher",
|
||||||
|
stopHotkeyFunc, NULL);
|
||||||
|
loadKeybinding(stopHotkeyId, STOP_HOTKEY_PATH);
|
||||||
|
}
|
||||||
280
src/executable-switch.cpp
Normal file
280
src/executable-switch.cpp
Normal file
@@ -0,0 +1,280 @@
|
|||||||
|
#include "headers/advanced-scene-switcher.hpp"
|
||||||
|
|
||||||
|
int SceneSwitcher::executableFindByData(const QString &exe)
|
||||||
|
{
|
||||||
|
int count = ui->executables->count();
|
||||||
|
|
||||||
|
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);
|
||||||
|
for (auto &s : switcher->executableSwitches) {
|
||||||
|
if (exec.compare(s.mExe) == 0) {
|
||||||
|
QString sceneName = GetWeakSourceName(s.mScene).c_str();
|
||||||
|
QString transitionName =
|
||||||
|
GetWeakSourceName(s.mTransition).c_str();
|
||||||
|
ui->executableScenes->setCurrentText(sceneName);
|
||||||
|
ui->executable->setCurrentText(exec);
|
||||||
|
ui->executableTransitions->setCurrentText(
|
||||||
|
transitionName);
|
||||||
|
ui->requiresFocusCheckBox->setChecked(s.mInFocus);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SceneSwitcher::on_executableUp_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.mExe == exeName) {
|
||||||
|
s.mScene = source;
|
||||||
|
s.mTransition = transition;
|
||||||
|
s.mInFocus = inFocus;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SceneSwitcher::on_executableRemove_clicked()
|
||||||
|
{
|
||||||
|
QListWidgetItem *item = ui->executables->currentItem();
|
||||||
|
if (!item)
|
||||||
|
return;
|
||||||
|
|
||||||
|
QString exe = item->data(Qt::UserRole).toString();
|
||||||
|
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(switcher->m);
|
||||||
|
auto &switches = switcher->executableSwitches;
|
||||||
|
|
||||||
|
for (auto it = switches.begin(); it != switches.end(); ++it) {
|
||||||
|
auto &s = *it;
|
||||||
|
|
||||||
|
if (s.mExe == exe) {
|
||||||
|
switches.erase(it);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
delete item;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SwitcherData::checkExeSwitch(bool &match, OBSWeakSource &scene,
|
||||||
|
OBSWeakSource &transition)
|
||||||
|
{
|
||||||
|
std::string title;
|
||||||
|
QStringList runningProcesses;
|
||||||
|
bool ignored = false;
|
||||||
|
|
||||||
|
// Check if current window is ignored
|
||||||
|
GetCurrentWindowTitle(title);
|
||||||
|
for (auto &window : ignoreWindowsSwitches) {
|
||||||
|
// True if ignored switch equals title
|
||||||
|
bool equals = (title == window);
|
||||||
|
// True if ignored switch matches title
|
||||||
|
bool matches = QString::fromStdString(title).contains(
|
||||||
|
QRegularExpression(QString::fromStdString(window)));
|
||||||
|
|
||||||
|
if (equals || matches) {
|
||||||
|
ignored = true;
|
||||||
|
title = lastTitle;
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
lastTitle = title;
|
||||||
|
|
||||||
|
// Check for match
|
||||||
|
GetProcessList(runningProcesses);
|
||||||
|
for (ExecutableSceneSwitch &s : executableSwitches) {
|
||||||
|
// True if executable switch is running (direct)
|
||||||
|
bool equals = runningProcesses.contains(s.mExe);
|
||||||
|
// True if executable switch is running (regex)
|
||||||
|
bool matches = (runningProcesses.indexOf(
|
||||||
|
QRegularExpression(s.mExe)) != -1);
|
||||||
|
// True if focus is disabled OR switch is focused
|
||||||
|
bool focus = (!s.mInFocus || isInFocus(s.mExe));
|
||||||
|
// True if current window is ignored AND switch equals OR matches last window
|
||||||
|
bool ignore =
|
||||||
|
(ignored && (title == s.mExe.toStdString() ||
|
||||||
|
QString::fromStdString(title).contains(
|
||||||
|
QRegularExpression(s.mExe))));
|
||||||
|
|
||||||
|
if ((equals || matches) && (focus || ignore)) {
|
||||||
|
match = true;
|
||||||
|
scene = s.mScene;
|
||||||
|
transition = s.mTransition;
|
||||||
|
|
||||||
|
if (verbose)
|
||||||
|
blog(LOG_INFO,
|
||||||
|
"Advanced Scene Switcher exec match");
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SwitcherData::saveExecutableSwitches(obs_data_t *obj)
|
||||||
|
{
|
||||||
|
obs_data_array_t *executableArray = obs_data_array_create();
|
||||||
|
for (ExecutableSceneSwitch &s : switcher->executableSwitches) {
|
||||||
|
obs_data_t *array_obj = obs_data_create();
|
||||||
|
|
||||||
|
obs_source_t *source = obs_weak_source_get_source(s.mScene);
|
||||||
|
obs_source_t *transition =
|
||||||
|
obs_weak_source_get_source(s.mTransition);
|
||||||
|
|
||||||
|
if (source && transition) {
|
||||||
|
const char *sceneName = obs_source_get_name(source);
|
||||||
|
const char *transitionName =
|
||||||
|
obs_source_get_name(transition);
|
||||||
|
obs_data_set_string(array_obj, "scene", sceneName);
|
||||||
|
obs_data_set_string(array_obj, "transition",
|
||||||
|
transitionName);
|
||||||
|
obs_data_set_string(array_obj, "exefile",
|
||||||
|
s.mExe.toUtf8());
|
||||||
|
obs_data_set_bool(array_obj, "infocus", s.mInFocus);
|
||||||
|
obs_data_array_push_back(executableArray, array_obj);
|
||||||
|
obs_source_release(source);
|
||||||
|
obs_source_release(transition);
|
||||||
|
}
|
||||||
|
|
||||||
|
obs_data_release(array_obj);
|
||||||
|
}
|
||||||
|
obs_data_set_array(obj, "executableSwitches", executableArray);
|
||||||
|
obs_data_array_release(executableArray);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SwitcherData::loadExecutableSwitches(obs_data_t *obj)
|
||||||
|
{
|
||||||
|
switcher->executableSwitches.clear();
|
||||||
|
|
||||||
|
obs_data_array_t *executableArray =
|
||||||
|
obs_data_get_array(obj, "executableSwitches");
|
||||||
|
size_t count = obs_data_array_count(executableArray);
|
||||||
|
|
||||||
|
for (size_t i = 0; i < count; i++) {
|
||||||
|
obs_data_t *array_obj = obs_data_array_item(executableArray, i);
|
||||||
|
|
||||||
|
const char *scene = obs_data_get_string(array_obj, "scene");
|
||||||
|
const char *transition =
|
||||||
|
obs_data_get_string(array_obj, "transition");
|
||||||
|
const char *exe = obs_data_get_string(array_obj, "exefile");
|
||||||
|
bool infocus = obs_data_get_bool(array_obj, "infocus");
|
||||||
|
|
||||||
|
switcher->executableSwitches.emplace_back(
|
||||||
|
GetWeakSourceByName(scene),
|
||||||
|
GetWeakTransitionByName(transition), exe, infocus);
|
||||||
|
|
||||||
|
obs_data_release(array_obj);
|
||||||
|
}
|
||||||
|
obs_data_array_release(executableArray);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SceneSwitcher::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) {
|
||||||
|
std::string sceneName = GetWeakSourceName(s.mScene);
|
||||||
|
std::string transitionName = GetWeakSourceName(s.mTransition);
|
||||||
|
QString text = MakeSwitchNameExecutable(sceneName.c_str(),
|
||||||
|
s.mExe,
|
||||||
|
transitionName.c_str(),
|
||||||
|
s.mInFocus);
|
||||||
|
|
||||||
|
QListWidgetItem *item =
|
||||||
|
new QListWidgetItem(text, ui->executables);
|
||||||
|
item->setData(Qt::UserRole, s.mExe);
|
||||||
|
}
|
||||||
|
}
|
||||||
475
src/file-switch.cpp
Normal file
475
src/file-switch.cpp
Normal file
@@ -0,0 +1,475 @@
|
|||||||
|
#include <QFileDialog>
|
||||||
|
#include <QTextStream>
|
||||||
|
#include <QDateTime>
|
||||||
|
#include <obs.hpp>
|
||||||
|
#include <curl/curl.h>
|
||||||
|
|
||||||
|
#include "headers/advanced-scene-switcher.hpp"
|
||||||
|
|
||||||
|
#define LOCAL_FILE_IDX 0
|
||||||
|
#define REMOTE_FILE_IDX 1
|
||||||
|
|
||||||
|
void SceneSwitcher::on_browseButton_clicked()
|
||||||
|
{
|
||||||
|
QString path = QFileDialog::getOpenFileName(
|
||||||
|
this, tr("Select a file to write to ..."), QDir::currentPath(),
|
||||||
|
tr("Text files (*.txt)"));
|
||||||
|
if (!path.isEmpty())
|
||||||
|
ui->writePathLineEdit->setText(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SceneSwitcher::on_readFileCheckBox_stateChanged(int state)
|
||||||
|
{
|
||||||
|
if (loading)
|
||||||
|
return;
|
||||||
|
|
||||||
|
std::lock_guard<std::mutex> lock(switcher->m);
|
||||||
|
if (!state) {
|
||||||
|
ui->browseButton_2->setDisabled(true);
|
||||||
|
ui->readPathLineEdit->setDisabled(true);
|
||||||
|
switcher->fileIO.readEnabled = false;
|
||||||
|
} else {
|
||||||
|
ui->browseButton_2->setDisabled(false);
|
||||||
|
ui->readPathLineEdit->setDisabled(false);
|
||||||
|
switcher->fileIO.readEnabled = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SceneSwitcher::on_readPathLineEdit_textChanged(const QString &text)
|
||||||
|
{
|
||||||
|
if (loading)
|
||||||
|
return;
|
||||||
|
|
||||||
|
std::lock_guard<std::mutex> lock(switcher->m);
|
||||||
|
if (text.isEmpty()) {
|
||||||
|
switcher->fileIO.readEnabled = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
switcher->fileIO.readEnabled = true;
|
||||||
|
switcher->fileIO.readPath = text.toUtf8().constData();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SceneSwitcher::on_writePathLineEdit_textChanged(const QString &text)
|
||||||
|
{
|
||||||
|
if (loading)
|
||||||
|
return;
|
||||||
|
|
||||||
|
std::lock_guard<std::mutex> lock(switcher->m);
|
||||||
|
if (text.isEmpty()) {
|
||||||
|
switcher->fileIO.writeEnabled = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
switcher->fileIO.writeEnabled = true;
|
||||||
|
switcher->fileIO.writePath = text.toUtf8().constData();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SceneSwitcher::on_browseButton_2_clicked()
|
||||||
|
{
|
||||||
|
QString path = QFileDialog::getOpenFileName(
|
||||||
|
this, tr("Select a file to read from ..."), QDir::currentPath(),
|
||||||
|
tr("Any files (*.*)"));
|
||||||
|
if (!path.isEmpty())
|
||||||
|
ui->readPathLineEdit->setText(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SwitcherData::writeSceneInfoToFile()
|
||||||
|
{
|
||||||
|
if (!fileIO.writeEnabled || fileIO.writePath.empty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
obs_source_t *currentSource = obs_frontend_get_current_scene();
|
||||||
|
|
||||||
|
QFile file(QString::fromStdString(fileIO.writePath));
|
||||||
|
if (file.open(QIODevice::WriteOnly)) {
|
||||||
|
const char *msg = obs_source_get_name(currentSource);
|
||||||
|
file.write(msg, qstrlen(msg));
|
||||||
|
file.close();
|
||||||
|
}
|
||||||
|
obs_source_release(currentSource);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SwitcherData::checkSwitchInfoFromFile(bool &match, OBSWeakSource &scene,
|
||||||
|
OBSWeakSource &transition)
|
||||||
|
{
|
||||||
|
if (!fileIO.readEnabled || fileIO.readPath.empty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
QFile file(QString::fromStdString(fileIO.readPath));
|
||||||
|
if (file.open(QIODevice::ReadOnly)) {
|
||||||
|
QTextStream in(&file);
|
||||||
|
QString sceneStr = in.readLine();
|
||||||
|
obs_source_t *sceneRead =
|
||||||
|
obs_get_source_by_name(sceneStr.toUtf8().constData());
|
||||||
|
if (sceneRead) {
|
||||||
|
obs_weak_source_t *sceneReadWs =
|
||||||
|
obs_source_get_weak_source(sceneRead);
|
||||||
|
|
||||||
|
match = true;
|
||||||
|
scene = sceneReadWs;
|
||||||
|
transition = nullptr;
|
||||||
|
|
||||||
|
obs_weak_source_release(sceneReadWs);
|
||||||
|
obs_source_release(sceneRead);
|
||||||
|
}
|
||||||
|
file.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static size_t WriteCallback(void *contents, size_t size, size_t nmemb,
|
||||||
|
void *userp)
|
||||||
|
{
|
||||||
|
((std::string *)userp)->append((char *)contents, size * nmemb);
|
||||||
|
return size * nmemb;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string getRemoteData(std::string &url)
|
||||||
|
{
|
||||||
|
CURL *curl;
|
||||||
|
CURLcode res;
|
||||||
|
std::string readBuffer;
|
||||||
|
|
||||||
|
curl = curl_easy_init();
|
||||||
|
if (curl) {
|
||||||
|
curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
|
||||||
|
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback);
|
||||||
|
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &readBuffer);
|
||||||
|
res = curl_easy_perform(curl);
|
||||||
|
curl_easy_cleanup(curl);
|
||||||
|
}
|
||||||
|
return readBuffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool compareIgnoringLineEnding(QString &s1, QString &s2)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
Im using QTextStream here so the conversion between different lineendings is done by QT.
|
||||||
|
QT itself uses only the linefeed internally so the input by the user is always using that,
|
||||||
|
but the files selected by the user might use different line endings.
|
||||||
|
If you are reading this and know of a cleaner way to do this, please let me know :)
|
||||||
|
*/
|
||||||
|
QTextStream s1stream(&s1);
|
||||||
|
QTextStream s2stream(&s2);
|
||||||
|
|
||||||
|
while (!s1stream.atEnd() && !s2stream.atEnd()) {
|
||||||
|
QString s1s = s1stream.readLine();
|
||||||
|
QString s2s = s2stream.readLine();
|
||||||
|
if (QString::compare(s1s, s2s, Qt::CaseSensitive) != 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool checkRemoteFileContent(FileSwitch &s)
|
||||||
|
{
|
||||||
|
std::string data = getRemoteData(s.file);
|
||||||
|
bool ret = false;
|
||||||
|
if (s.useRegex) {
|
||||||
|
ret = QString::fromStdString(data).contains(
|
||||||
|
QRegularExpression(QString::fromStdString(s.text)));
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
QString t = QString::fromStdString(s.text);
|
||||||
|
QString d = QString::fromStdString(data);
|
||||||
|
|
||||||
|
return compareIgnoringLineEnding(t, d);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool checkLocalFileContent(FileSwitch &s)
|
||||||
|
{
|
||||||
|
bool equal = false;
|
||||||
|
QString t = QString::fromStdString(s.text);
|
||||||
|
QFile file(QString::fromStdString(s.file));
|
||||||
|
if (!file.open(QIODevice::ReadOnly))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (s.useTime) {
|
||||||
|
QDateTime newLastMod = QFileInfo(file).lastModified();
|
||||||
|
if (s.lastMod == newLastMod)
|
||||||
|
return false;
|
||||||
|
s.lastMod = newLastMod;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (s.useRegex) {
|
||||||
|
QTextStream in(&file);
|
||||||
|
QRegExp rx(t);
|
||||||
|
equal = rx.exactMatch(in.readAll());
|
||||||
|
} else {
|
||||||
|
QTextStream in(&file);
|
||||||
|
QString filedata = in.readAll();
|
||||||
|
equal = compareIgnoringLineEnding(filedata, t);
|
||||||
|
}
|
||||||
|
file.close();
|
||||||
|
return equal;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SwitcherData::checkFileContent(bool &match, OBSWeakSource &scene,
|
||||||
|
OBSWeakSource &transition)
|
||||||
|
{
|
||||||
|
for (FileSwitch &s : fileSwitches) {
|
||||||
|
bool equal = false;
|
||||||
|
if (s.remote) {
|
||||||
|
equal = checkRemoteFileContent(s);
|
||||||
|
} else {
|
||||||
|
equal = checkLocalFileContent(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (equal) {
|
||||||
|
scene = s.scene;
|
||||||
|
transition = s.transition;
|
||||||
|
match = true;
|
||||||
|
|
||||||
|
if (verbose)
|
||||||
|
blog(LOG_INFO,
|
||||||
|
"Advanced Scene Switcher file match");
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SceneSwitcher::on_browseButton_3_clicked()
|
||||||
|
{
|
||||||
|
QString path = QFileDialog::getOpenFileName(
|
||||||
|
this, tr("Select a file to read from ..."), QDir::currentPath(),
|
||||||
|
tr("Any files (*.*)"));
|
||||||
|
if (!path.isEmpty())
|
||||||
|
ui->filePathLineEdit->setText(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SceneSwitcher::on_fileType_currentIndexChanged(int idx)
|
||||||
|
{
|
||||||
|
if (idx == -1)
|
||||||
|
return;
|
||||||
|
if (idx == LOCAL_FILE_IDX) {
|
||||||
|
ui->browseButton_3->setDisabled(false);
|
||||||
|
ui->fileContentTimeCheckBox->setDisabled(false);
|
||||||
|
ui->remoteFileWarningLabel->hide();
|
||||||
|
}
|
||||||
|
if (idx == REMOTE_FILE_IDX) {
|
||||||
|
ui->browseButton_3->setDisabled(true);
|
||||||
|
ui->fileContentTimeCheckBox->setDisabled(true);
|
||||||
|
ui->remoteFileWarningLabel->show();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SceneSwitcher::on_fileAdd_clicked()
|
||||||
|
{
|
||||||
|
QString sceneName = ui->fileScenes->currentText();
|
||||||
|
QString transitionName = ui->fileTransitions->currentText();
|
||||||
|
QString fileName = ui->filePathLineEdit->text();
|
||||||
|
QString text = ui->fileTextEdit->toPlainText();
|
||||||
|
bool remote = (ui->fileType->currentIndex() == REMOTE_FILE_IDX);
|
||||||
|
bool useRegex = ui->fileContentRegExCheckBox->isChecked();
|
||||||
|
bool useTime = ui->fileContentTimeCheckBox->isChecked();
|
||||||
|
|
||||||
|
if (sceneName.isEmpty() || transitionName.isEmpty() ||
|
||||||
|
fileName.isEmpty() || text.isEmpty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
OBSWeakSource source = GetWeakSourceByQString(sceneName);
|
||||||
|
OBSWeakSource transition = GetWeakTransitionByQString(transitionName);
|
||||||
|
|
||||||
|
QString switchText = MakeFileSwitchName(
|
||||||
|
sceneName, transitionName, fileName, text, useRegex, useTime);
|
||||||
|
QVariant v = QVariant::fromValue(switchText);
|
||||||
|
|
||||||
|
QListWidgetItem *item =
|
||||||
|
new QListWidgetItem(switchText, ui->fileScenesList);
|
||||||
|
item->setData(Qt::UserRole, v);
|
||||||
|
|
||||||
|
std::lock_guard<std::mutex> lock(switcher->m);
|
||||||
|
switcher->fileSwitches.emplace_back(source, transition,
|
||||||
|
fileName.toUtf8().constData(),
|
||||||
|
text.toUtf8().constData(), remote,
|
||||||
|
useRegex, useTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SceneSwitcher::on_fileRemove_clicked()
|
||||||
|
{
|
||||||
|
QListWidgetItem *item = ui->fileScenesList->currentItem();
|
||||||
|
if (!item)
|
||||||
|
return;
|
||||||
|
|
||||||
|
int idx = ui->fileScenesList->currentRow();
|
||||||
|
if (idx == -1)
|
||||||
|
return;
|
||||||
|
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(switcher->m);
|
||||||
|
|
||||||
|
auto &switches = switcher->fileSwitches;
|
||||||
|
switches.erase(switches.begin() + idx);
|
||||||
|
}
|
||||||
|
qDeleteAll(ui->fileScenesList->selectedItems());
|
||||||
|
}
|
||||||
|
|
||||||
|
void SceneSwitcher::on_fileScenesList_currentRowChanged(int idx)
|
||||||
|
{
|
||||||
|
if (loading)
|
||||||
|
return;
|
||||||
|
if (idx == -1)
|
||||||
|
return;
|
||||||
|
|
||||||
|
std::lock_guard<std::mutex> lock(switcher->m);
|
||||||
|
|
||||||
|
if (switcher->fileSwitches.size() <= idx)
|
||||||
|
return;
|
||||||
|
FileSwitch s = switcher->fileSwitches[idx];
|
||||||
|
|
||||||
|
std::string sceneName = GetWeakSourceName(s.scene);
|
||||||
|
std::string transitionName = GetWeakSourceName(s.transition);
|
||||||
|
|
||||||
|
ui->fileScenes->setCurrentText(sceneName.c_str());
|
||||||
|
ui->fileTransitions->setCurrentText(transitionName.c_str());
|
||||||
|
ui->fileTextEdit->setPlainText(s.text.c_str());
|
||||||
|
ui->filePathLineEdit->setText(s.file.c_str());
|
||||||
|
if (s.remote)
|
||||||
|
ui->fileType->setCurrentIndex(REMOTE_FILE_IDX);
|
||||||
|
else
|
||||||
|
ui->fileType->setCurrentIndex(LOCAL_FILE_IDX);
|
||||||
|
ui->fileContentRegExCheckBox->setChecked(s.useRegex);
|
||||||
|
ui->fileContentTimeCheckBox->setChecked(s.useTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SceneSwitcher::on_fileUp_clicked()
|
||||||
|
{
|
||||||
|
int index = ui->fileScenesList->currentRow();
|
||||||
|
if (index != -1 && index != 0) {
|
||||||
|
ui->fileScenesList->insertItem(
|
||||||
|
index - 1, ui->fileScenesList->takeItem(index));
|
||||||
|
ui->fileScenesList->setCurrentRow(index - 1);
|
||||||
|
|
||||||
|
std::lock_guard<std::mutex> lock(switcher->m);
|
||||||
|
|
||||||
|
iter_swap(switcher->fileSwitches.begin() + index,
|
||||||
|
switcher->fileSwitches.begin() + index - 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SceneSwitcher::on_fileDown_clicked()
|
||||||
|
{
|
||||||
|
int index = ui->fileScenesList->currentRow();
|
||||||
|
if (index != -1 && index != ui->fileScenesList->count() - 1) {
|
||||||
|
ui->fileScenesList->insertItem(
|
||||||
|
index + 1, ui->fileScenesList->takeItem(index));
|
||||||
|
ui->fileScenesList->setCurrentRow(index + 1);
|
||||||
|
|
||||||
|
std::lock_guard<std::mutex> lock(switcher->m);
|
||||||
|
|
||||||
|
iter_swap(switcher->fileSwitches.begin() + index,
|
||||||
|
switcher->fileSwitches.begin() + index + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SwitcherData::saveFileSwitches(obs_data_t *obj)
|
||||||
|
{
|
||||||
|
obs_data_array_t *fileArray = obs_data_array_create();
|
||||||
|
for (FileSwitch &s : switcher->fileSwitches) {
|
||||||
|
obs_data_t *array_obj = obs_data_create();
|
||||||
|
|
||||||
|
obs_source_t *source = obs_weak_source_get_source(s.scene);
|
||||||
|
obs_source_t *transition =
|
||||||
|
obs_weak_source_get_source(s.transition);
|
||||||
|
|
||||||
|
if (source && transition) {
|
||||||
|
const char *sceneName = obs_source_get_name(source);
|
||||||
|
const char *transitionName =
|
||||||
|
obs_source_get_name(transition);
|
||||||
|
obs_data_set_string(array_obj, "scene", sceneName);
|
||||||
|
obs_data_set_string(array_obj, "transition",
|
||||||
|
transitionName);
|
||||||
|
obs_data_set_string(array_obj, "file", s.file.c_str());
|
||||||
|
obs_data_set_string(array_obj, "text", s.text.c_str());
|
||||||
|
obs_data_set_bool(array_obj, "remote", s.remote);
|
||||||
|
obs_data_set_bool(array_obj, "useRegex", s.useRegex);
|
||||||
|
obs_data_set_bool(array_obj, "useTime", s.useTime);
|
||||||
|
obs_data_array_push_back(fileArray, array_obj);
|
||||||
|
obs_source_release(source);
|
||||||
|
obs_source_release(transition);
|
||||||
|
}
|
||||||
|
|
||||||
|
obs_data_release(array_obj);
|
||||||
|
}
|
||||||
|
obs_data_set_array(obj, "fileSwitches", fileArray);
|
||||||
|
obs_data_array_release(fileArray);
|
||||||
|
|
||||||
|
obs_data_set_bool(obj, "readEnabled", switcher->fileIO.readEnabled);
|
||||||
|
obs_data_set_string(obj, "readPath", switcher->fileIO.readPath.c_str());
|
||||||
|
obs_data_set_bool(obj, "writeEnabled", switcher->fileIO.writeEnabled);
|
||||||
|
obs_data_set_string(obj, "writePath",
|
||||||
|
switcher->fileIO.writePath.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
void SwitcherData::loadFileSwitches(obs_data_t *obj)
|
||||||
|
{
|
||||||
|
switcher->fileSwitches.clear();
|
||||||
|
obs_data_array_t *fileArray = obs_data_get_array(obj, "fileSwitches");
|
||||||
|
size_t count = obs_data_array_count(fileArray);
|
||||||
|
|
||||||
|
for (size_t i = 0; i < count; i++) {
|
||||||
|
obs_data_t *array_obj = obs_data_array_item(fileArray, i);
|
||||||
|
|
||||||
|
const char *scene = obs_data_get_string(array_obj, "scene");
|
||||||
|
const char *transition =
|
||||||
|
obs_data_get_string(array_obj, "transition");
|
||||||
|
const char *file = obs_data_get_string(array_obj, "file");
|
||||||
|
const char *text = obs_data_get_string(array_obj, "text");
|
||||||
|
bool remote = obs_data_get_bool(array_obj, "remote");
|
||||||
|
bool useRegex = obs_data_get_bool(array_obj, "useRegex");
|
||||||
|
bool useTime = obs_data_get_bool(array_obj, "useTime");
|
||||||
|
|
||||||
|
switcher->fileSwitches.emplace_back(
|
||||||
|
GetWeakSourceByName(scene),
|
||||||
|
GetWeakTransitionByName(transition), file, text, remote,
|
||||||
|
useRegex, useTime);
|
||||||
|
|
||||||
|
obs_data_release(array_obj);
|
||||||
|
}
|
||||||
|
obs_data_array_release(fileArray);
|
||||||
|
|
||||||
|
obs_data_set_default_bool(obj, "readEnabled", false);
|
||||||
|
switcher->fileIO.readEnabled = obs_data_get_bool(obj, "readEnabled");
|
||||||
|
switcher->fileIO.readPath = obs_data_get_string(obj, "readPath");
|
||||||
|
obs_data_set_default_bool(obj, "writeEnabled", false);
|
||||||
|
switcher->fileIO.writeEnabled = obs_data_get_bool(obj, "writeEnabled");
|
||||||
|
switcher->fileIO.writePath = obs_data_get_string(obj, "writePath");
|
||||||
|
}
|
||||||
|
|
||||||
|
void SceneSwitcher::setupFileTab()
|
||||||
|
{
|
||||||
|
populateSceneSelection(ui->fileScenes, false);
|
||||||
|
populateTransitionSelection(ui->fileTransitions);
|
||||||
|
|
||||||
|
ui->fileType->addItem("local");
|
||||||
|
ui->fileType->addItem("remote");
|
||||||
|
ui->remoteFileWarningLabel->setText(
|
||||||
|
"Note that the scene switcher will try to access the remote location every " +
|
||||||
|
QString::number(switcher->interval) + "ms");
|
||||||
|
|
||||||
|
for (auto &s : switcher->fileSwitches) {
|
||||||
|
std::string sceneName = GetWeakSourceName(s.scene);
|
||||||
|
std::string transitionName = GetWeakSourceName(s.transition);
|
||||||
|
QString listText = MakeFileSwitchName(
|
||||||
|
sceneName.c_str(), transitionName.c_str(),
|
||||||
|
s.file.c_str(), s.text.c_str(), s.useRegex, s.useTime);
|
||||||
|
|
||||||
|
QListWidgetItem *item =
|
||||||
|
new QListWidgetItem(listText, ui->fileScenesList);
|
||||||
|
item->setData(Qt::UserRole, listText);
|
||||||
|
}
|
||||||
|
|
||||||
|
ui->readPathLineEdit->setText(
|
||||||
|
QString::fromStdString(switcher->fileIO.readPath.c_str()));
|
||||||
|
ui->readFileCheckBox->setChecked(switcher->fileIO.readEnabled);
|
||||||
|
ui->writePathLineEdit->setText(
|
||||||
|
QString::fromStdString(switcher->fileIO.writePath.c_str()));
|
||||||
|
|
||||||
|
if (ui->readFileCheckBox->checkState()) {
|
||||||
|
ui->browseButton_2->setDisabled(false);
|
||||||
|
ui->readPathLineEdit->setDisabled(false);
|
||||||
|
} else {
|
||||||
|
ui->browseButton_2->setDisabled(true);
|
||||||
|
ui->readPathLineEdit->setDisabled(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
579
src/general.cpp
Normal file
579
src/general.cpp
Normal file
@@ -0,0 +1,579 @@
|
|||||||
|
#include <obs-module.h>
|
||||||
|
#include <QFileDialog>
|
||||||
|
#include <QTextStream>
|
||||||
|
#include <QMessageBox>
|
||||||
|
|
||||||
|
#include "headers/advanced-scene-switcher.hpp"
|
||||||
|
|
||||||
|
void SceneSwitcher::on_close_clicked()
|
||||||
|
{
|
||||||
|
done(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SceneSwitcher::UpdateNonMatchingScene(const QString &name)
|
||||||
|
{
|
||||||
|
obs_source_t *scene = obs_get_source_by_name(name.toUtf8().constData());
|
||||||
|
obs_weak_source_t *ws = obs_source_get_weak_source(scene);
|
||||||
|
|
||||||
|
switcher->nonMatchingScene = ws;
|
||||||
|
|
||||||
|
obs_weak_source_release(ws);
|
||||||
|
obs_source_release(scene);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SceneSwitcher::on_noMatchDontSwitch_clicked()
|
||||||
|
{
|
||||||
|
if (loading)
|
||||||
|
return;
|
||||||
|
|
||||||
|
std::lock_guard<std::mutex> lock(switcher->m);
|
||||||
|
switcher->switchIfNotMatching = NO_SWITCH;
|
||||||
|
ui->noMatchSwitchScene->setEnabled(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SceneSwitcher::on_noMatchSwitch_clicked()
|
||||||
|
{
|
||||||
|
if (loading)
|
||||||
|
return;
|
||||||
|
|
||||||
|
std::lock_guard<std::mutex> lock(switcher->m);
|
||||||
|
switcher->switchIfNotMatching = SWITCH;
|
||||||
|
ui->noMatchSwitchScene->setEnabled(true);
|
||||||
|
UpdateNonMatchingScene(ui->noMatchSwitchScene->currentText());
|
||||||
|
}
|
||||||
|
|
||||||
|
void SceneSwitcher::on_noMatchRandomSwitch_clicked()
|
||||||
|
{
|
||||||
|
if (loading)
|
||||||
|
return;
|
||||||
|
|
||||||
|
std::lock_guard<std::mutex> lock(switcher->m);
|
||||||
|
switcher->switchIfNotMatching = RANDOM_SWITCH;
|
||||||
|
ui->noMatchSwitchScene->setEnabled(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SceneSwitcher::on_startupBehavior_currentIndexChanged(int index)
|
||||||
|
{
|
||||||
|
if (loading)
|
||||||
|
return;
|
||||||
|
|
||||||
|
std::lock_guard<std::mutex> lock(switcher->m);
|
||||||
|
switcher->startupBehavior = (StartupBehavior)index;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SceneSwitcher::on_noMatchSwitchScene_currentTextChanged(const QString &text)
|
||||||
|
{
|
||||||
|
if (loading)
|
||||||
|
return;
|
||||||
|
|
||||||
|
std::lock_guard<std::mutex> lock(switcher->m);
|
||||||
|
UpdateNonMatchingScene(text);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SceneSwitcher::on_checkInterval_valueChanged(int value)
|
||||||
|
{
|
||||||
|
if (loading)
|
||||||
|
return;
|
||||||
|
|
||||||
|
std::lock_guard<std::mutex> lock(switcher->m);
|
||||||
|
switcher->interval = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SceneSwitcher::SetStarted()
|
||||||
|
{
|
||||||
|
ui->toggleStartButton->setText("Stop");
|
||||||
|
ui->pluginRunningText->setText("Active");
|
||||||
|
}
|
||||||
|
|
||||||
|
void SceneSwitcher::SetStopped()
|
||||||
|
{
|
||||||
|
ui->toggleStartButton->setText("Start");
|
||||||
|
ui->pluginRunningText->setText("Inactive");
|
||||||
|
}
|
||||||
|
|
||||||
|
void SceneSwitcher::on_toggleStartButton_clicked()
|
||||||
|
{
|
||||||
|
if (switcher->th && switcher->th->isRunning()) {
|
||||||
|
switcher->Stop();
|
||||||
|
SetStopped();
|
||||||
|
} else {
|
||||||
|
switcher->Start();
|
||||||
|
SetStarted();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SceneSwitcher::closeEvent(QCloseEvent *)
|
||||||
|
{
|
||||||
|
obs_frontend_save();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SceneSwitcher::on_autoStopScenes_currentTextChanged(const QString &text)
|
||||||
|
{
|
||||||
|
if (loading)
|
||||||
|
return;
|
||||||
|
|
||||||
|
std::lock_guard<std::mutex> lock(switcher->m);
|
||||||
|
UpdateAutoStopScene(text);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SceneSwitcher::on_autoStopSceneCheckBox_stateChanged(int state)
|
||||||
|
{
|
||||||
|
if (loading)
|
||||||
|
return;
|
||||||
|
|
||||||
|
std::lock_guard<std::mutex> lock(switcher->m);
|
||||||
|
if (!state) {
|
||||||
|
ui->autoStopScenes->setDisabled(true);
|
||||||
|
switcher->autoStopEnable = false;
|
||||||
|
} else {
|
||||||
|
ui->autoStopScenes->setDisabled(false);
|
||||||
|
switcher->autoStopEnable = true;
|
||||||
|
if (!switcher->autoStopScene)
|
||||||
|
UpdateAutoStopScene(ui->autoStopScenes->currentText());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SceneSwitcher::UpdateAutoStopScene(const QString &name)
|
||||||
|
{
|
||||||
|
obs_source_t *scene = obs_get_source_by_name(name.toUtf8().constData());
|
||||||
|
obs_weak_source_t *ws = obs_source_get_weak_source(scene);
|
||||||
|
|
||||||
|
switcher->autoStopScene = ws;
|
||||||
|
|
||||||
|
obs_weak_source_release(ws);
|
||||||
|
obs_source_release(scene);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SwitcherData::autoStopStreamAndRecording()
|
||||||
|
{
|
||||||
|
obs_source_t *currentSource = obs_frontend_get_current_scene();
|
||||||
|
obs_weak_source_t *ws = obs_source_get_weak_source(currentSource);
|
||||||
|
|
||||||
|
if (ws && autoStopScene == ws) {
|
||||||
|
if (obs_frontend_streaming_active())
|
||||||
|
obs_frontend_streaming_stop();
|
||||||
|
if (obs_frontend_recording_active())
|
||||||
|
obs_frontend_recording_stop();
|
||||||
|
}
|
||||||
|
obs_source_release(currentSource);
|
||||||
|
obs_weak_source_release(ws);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SceneSwitcher::on_verboseLogging_stateChanged(int state)
|
||||||
|
{
|
||||||
|
if (loading)
|
||||||
|
return;
|
||||||
|
|
||||||
|
std::lock_guard<std::mutex> lock(switcher->m);
|
||||||
|
switcher->verbose = state;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SceneSwitcher::on_exportSettings_clicked()
|
||||||
|
{
|
||||||
|
QString directory = QFileDialog::getSaveFileName(
|
||||||
|
this, tr("Export Advanced Scene Switcher settings to file ..."),
|
||||||
|
QDir::currentPath(), tr("Text files (*.txt)"));
|
||||||
|
if (directory.isEmpty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
QFile file(directory);
|
||||||
|
if (!file.open(QIODevice::WriteOnly | QIODevice::Text))
|
||||||
|
return;
|
||||||
|
|
||||||
|
obs_data_t *obj = obs_data_create();
|
||||||
|
|
||||||
|
switcher->saveWindowTitleSwitches(obj);
|
||||||
|
switcher->saveScreenRegionSwitches(obj);
|
||||||
|
switcher->savePauseSwitches(obj);
|
||||||
|
switcher->saveSceneRoundTripSwitches(obj);
|
||||||
|
switcher->saveSceneTransitions(obj);
|
||||||
|
switcher->saveIdleSwitches(obj);
|
||||||
|
switcher->saveExecutableSwitches(obj);
|
||||||
|
switcher->saveRandomSwitches(obj);
|
||||||
|
switcher->saveFileSwitches(obj);
|
||||||
|
switcher->saveMediaSwitches(obj);
|
||||||
|
switcher->saveTimeSwitches(obj);
|
||||||
|
switcher->saveGeneralSettings(obj);
|
||||||
|
|
||||||
|
obs_data_save_json(obj, file.fileName().toUtf8().constData());
|
||||||
|
|
||||||
|
obs_data_release(obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SceneSwitcher::on_importSettings_clicked()
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(switcher->m);
|
||||||
|
|
||||||
|
QString directory = QFileDialog::getOpenFileName(
|
||||||
|
this,
|
||||||
|
tr("Import Advanced Scene Switcher settings from file ..."),
|
||||||
|
QDir::currentPath(), tr("Text files (*.txt)"));
|
||||||
|
if (directory.isEmpty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
QFile file(directory);
|
||||||
|
if (!file.open(QIODevice::ReadOnly | QIODevice::Text))
|
||||||
|
return;
|
||||||
|
|
||||||
|
obs_data_t *obj = obs_data_create_from_json_file(
|
||||||
|
file.fileName().toUtf8().constData());
|
||||||
|
|
||||||
|
if (!obj) {
|
||||||
|
QMessageBox Msgbox;
|
||||||
|
Msgbox.setText(
|
||||||
|
"Advanced Scene Switcher failed to import settings");
|
||||||
|
Msgbox.exec();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
switcher->loadWindowTitleSwitches(obj);
|
||||||
|
switcher->loadScreenRegionSwitches(obj);
|
||||||
|
switcher->loadPauseSwitches(obj);
|
||||||
|
switcher->loadSceneRoundTripSwitches(obj);
|
||||||
|
switcher->loadSceneTransitions(obj);
|
||||||
|
switcher->loadIdleSwitches(obj);
|
||||||
|
switcher->loadExecutableSwitches(obj);
|
||||||
|
switcher->loadRandomSwitches(obj);
|
||||||
|
switcher->loadFileSwitches(obj);
|
||||||
|
switcher->loadMediaSwitches(obj);
|
||||||
|
switcher->loadTimeSwitches(obj);
|
||||||
|
switcher->loadGeneralSettings(obj);
|
||||||
|
|
||||||
|
obs_data_release(obj);
|
||||||
|
|
||||||
|
QMessageBox Msgbox;
|
||||||
|
Msgbox.setText(
|
||||||
|
"Advanced Scene Switcher settings imported successfully");
|
||||||
|
Msgbox.exec();
|
||||||
|
close();
|
||||||
|
}
|
||||||
|
|
||||||
|
int findTabIndex(QTabBar *bar, int pos)
|
||||||
|
{
|
||||||
|
int at = -1;
|
||||||
|
|
||||||
|
QString tabName = "";
|
||||||
|
switch (pos) {
|
||||||
|
case 0:
|
||||||
|
tabName = "General";
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
tabName = "Transition";
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
tabName = "Pause";
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
tabName = "Title";
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
tabName = "Executable";
|
||||||
|
break;
|
||||||
|
case 5:
|
||||||
|
tabName = "Region";
|
||||||
|
break;
|
||||||
|
case 6:
|
||||||
|
tabName = "Media";
|
||||||
|
break;
|
||||||
|
case 7:
|
||||||
|
tabName = "File";
|
||||||
|
break;
|
||||||
|
case 8:
|
||||||
|
tabName = "Random";
|
||||||
|
break;
|
||||||
|
case 9:
|
||||||
|
tabName = "Time";
|
||||||
|
break;
|
||||||
|
case 10:
|
||||||
|
tabName = "Idle";
|
||||||
|
break;
|
||||||
|
case 11:
|
||||||
|
tabName = "Sequence";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < bar->count(); ++i) {
|
||||||
|
if (bar->tabText(i).compare(tabName) == 0) {
|
||||||
|
at = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (at == -1)
|
||||||
|
blog(LOG_INFO, "Advanced Scene Switcher failed to find tab %s",
|
||||||
|
tabName.toUtf8().constData());
|
||||||
|
|
||||||
|
return at;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SceneSwitcher::setTabOrder()
|
||||||
|
{
|
||||||
|
QTabBar *bar = ui->tabWidget->tabBar();
|
||||||
|
for (int i = 0; i < bar->count(); ++i) {
|
||||||
|
int curPos = findTabIndex(bar, switcher->tabOrder[i]);
|
||||||
|
|
||||||
|
if (i != curPos && curPos != -1)
|
||||||
|
bar->moveTab(curPos, i);
|
||||||
|
}
|
||||||
|
|
||||||
|
connect(bar, &QTabBar::tabMoved, this, &SceneSwitcher::on_tabMoved);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SceneSwitcher::on_tabMoved(int from, int to)
|
||||||
|
{
|
||||||
|
if (loading)
|
||||||
|
return;
|
||||||
|
std::swap(switcher->tabOrder[from], switcher->tabOrder[to]);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SwitcherData::saveGeneralSettings(obs_data_t *obj)
|
||||||
|
{
|
||||||
|
obs_data_set_int(obj, "interval", switcher->interval);
|
||||||
|
|
||||||
|
std::string nonMatchingSceneName =
|
||||||
|
GetWeakSourceName(switcher->nonMatchingScene);
|
||||||
|
obs_data_set_string(obj, "non_matching_scene",
|
||||||
|
nonMatchingSceneName.c_str());
|
||||||
|
obs_data_set_int(obj, "switch_if_not_matching",
|
||||||
|
switcher->switchIfNotMatching);
|
||||||
|
|
||||||
|
obs_data_set_bool(obj, "active", !switcher->stop);
|
||||||
|
obs_data_set_int(obj, "startup_behavior", switcher->startupBehavior);
|
||||||
|
|
||||||
|
std::string autoStopSceneName =
|
||||||
|
GetWeakSourceName(switcher->autoStopScene);
|
||||||
|
obs_data_set_bool(obj, "autoStopEnable", switcher->autoStopEnable);
|
||||||
|
obs_data_set_string(obj, "autoStopSceneName",
|
||||||
|
autoStopSceneName.c_str());
|
||||||
|
|
||||||
|
obs_data_set_bool(obj, "verbose", switcher->verbose);
|
||||||
|
|
||||||
|
obs_data_set_int(obj, "priority0",
|
||||||
|
switcher->functionNamesByPriority[0]);
|
||||||
|
obs_data_set_int(obj, "priority1",
|
||||||
|
switcher->functionNamesByPriority[1]);
|
||||||
|
obs_data_set_int(obj, "priority2",
|
||||||
|
switcher->functionNamesByPriority[2]);
|
||||||
|
obs_data_set_int(obj, "priority3",
|
||||||
|
switcher->functionNamesByPriority[3]);
|
||||||
|
obs_data_set_int(obj, "priority4",
|
||||||
|
switcher->functionNamesByPriority[4]);
|
||||||
|
obs_data_set_int(obj, "priority5",
|
||||||
|
switcher->functionNamesByPriority[5]);
|
||||||
|
obs_data_set_int(obj, "priority6",
|
||||||
|
switcher->functionNamesByPriority[6]);
|
||||||
|
obs_data_set_int(obj, "priority7",
|
||||||
|
switcher->functionNamesByPriority[7]);
|
||||||
|
|
||||||
|
obs_data_set_int(obj, "threadPriority", switcher->threadPriority);
|
||||||
|
|
||||||
|
obs_data_set_int(obj, "generalTabPos", switcher->tabOrder[0]);
|
||||||
|
obs_data_set_int(obj, "transitionTabPos", switcher->tabOrder[1]);
|
||||||
|
obs_data_set_int(obj, "pauseTabPos", switcher->tabOrder[2]);
|
||||||
|
obs_data_set_int(obj, "titleTabPos", switcher->tabOrder[3]);
|
||||||
|
obs_data_set_int(obj, "exeTabPos", switcher->tabOrder[4]);
|
||||||
|
obs_data_set_int(obj, "regionTabPos", switcher->tabOrder[5]);
|
||||||
|
obs_data_set_int(obj, "mediaTabPos", switcher->tabOrder[6]);
|
||||||
|
obs_data_set_int(obj, "fileTabPos", switcher->tabOrder[7]);
|
||||||
|
obs_data_set_int(obj, "randomTabPos", switcher->tabOrder[8]);
|
||||||
|
obs_data_set_int(obj, "timeTabPos", switcher->tabOrder[9]);
|
||||||
|
obs_data_set_int(obj, "idleTabPos", switcher->tabOrder[10]);
|
||||||
|
obs_data_set_int(obj, "sequenceTabPos", switcher->tabOrder[11]);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SwitcherData::loadGeneralSettings(obs_data_t *obj)
|
||||||
|
{
|
||||||
|
obs_data_set_default_int(obj, "interval", DEFAULT_INTERVAL);
|
||||||
|
switcher->interval = obs_data_get_int(obj, "interval");
|
||||||
|
|
||||||
|
obs_data_set_default_int(obj, "switch_if_not_matching", NO_SWITCH);
|
||||||
|
switcher->switchIfNotMatching =
|
||||||
|
(NoMatch)obs_data_get_int(obj, "switch_if_not_matching");
|
||||||
|
std::string nonMatchingScene =
|
||||||
|
obs_data_get_string(obj, "non_matching_scene");
|
||||||
|
switcher->nonMatchingScene =
|
||||||
|
GetWeakSourceByName(nonMatchingScene.c_str());
|
||||||
|
|
||||||
|
switcher->stop = !obs_data_get_bool(obj, "active");
|
||||||
|
switcher->startupBehavior =
|
||||||
|
(StartupBehavior)obs_data_get_int(obj, "startup_behavior");
|
||||||
|
if (switcher->startupBehavior == START)
|
||||||
|
switcher->stop = false;
|
||||||
|
if (switcher->startupBehavior == STOP)
|
||||||
|
switcher->stop = true;
|
||||||
|
|
||||||
|
std::string autoStopScene =
|
||||||
|
obs_data_get_string(obj, "autoStopSceneName");
|
||||||
|
switcher->autoStopEnable = obs_data_get_bool(obj, "autoStopEnable");
|
||||||
|
switcher->autoStopScene = GetWeakSourceByName(autoStopScene.c_str());
|
||||||
|
|
||||||
|
switcher->verbose = obs_data_get_bool(obj, "verbose");
|
||||||
|
|
||||||
|
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, "priority2", DEFAULT_PRIORITY_2);
|
||||||
|
obs_data_set_default_int(obj, "priority3", DEFAULT_PRIORITY_3);
|
||||||
|
obs_data_set_default_int(obj, "priority4", DEFAULT_PRIORITY_4);
|
||||||
|
obs_data_set_default_int(obj, "priority5", DEFAULT_PRIORITY_5);
|
||||||
|
obs_data_set_default_int(obj, "priority6", DEFAULT_PRIORITY_6);
|
||||||
|
obs_data_set_default_int(obj, "priority7", DEFAULT_PRIORITY_7);
|
||||||
|
|
||||||
|
switcher->functionNamesByPriority[0] =
|
||||||
|
(obs_data_get_int(obj, "priority0"));
|
||||||
|
switcher->functionNamesByPriority[1] =
|
||||||
|
(obs_data_get_int(obj, "priority1"));
|
||||||
|
switcher->functionNamesByPriority[2] =
|
||||||
|
(obs_data_get_int(obj, "priority2"));
|
||||||
|
switcher->functionNamesByPriority[3] =
|
||||||
|
(obs_data_get_int(obj, "priority3"));
|
||||||
|
switcher->functionNamesByPriority[4] =
|
||||||
|
(obs_data_get_int(obj, "priority4"));
|
||||||
|
switcher->functionNamesByPriority[5] =
|
||||||
|
(obs_data_get_int(obj, "priority5"));
|
||||||
|
switcher->functionNamesByPriority[6] =
|
||||||
|
(obs_data_get_int(obj, "priority6"));
|
||||||
|
switcher->functionNamesByPriority[7] =
|
||||||
|
(obs_data_get_int(obj, "priority7"));
|
||||||
|
if (!switcher->prioFuncsValid()) {
|
||||||
|
switcher->functionNamesByPriority[0] = (DEFAULT_PRIORITY_0);
|
||||||
|
switcher->functionNamesByPriority[1] = (DEFAULT_PRIORITY_1);
|
||||||
|
switcher->functionNamesByPriority[2] = (DEFAULT_PRIORITY_2);
|
||||||
|
switcher->functionNamesByPriority[3] = (DEFAULT_PRIORITY_3);
|
||||||
|
switcher->functionNamesByPriority[4] = (DEFAULT_PRIORITY_4);
|
||||||
|
switcher->functionNamesByPriority[5] = (DEFAULT_PRIORITY_5);
|
||||||
|
switcher->functionNamesByPriority[6] = (DEFAULT_PRIORITY_6);
|
||||||
|
switcher->functionNamesByPriority[7] = (DEFAULT_PRIORITY_7);
|
||||||
|
}
|
||||||
|
|
||||||
|
obs_data_set_default_int(obj, "threadPriority",
|
||||||
|
QThread::NormalPriority);
|
||||||
|
switcher->threadPriority = obs_data_get_int(obj, "threadPriority");
|
||||||
|
|
||||||
|
obs_data_set_default_int(obj, "generalTabPos", 0);
|
||||||
|
obs_data_set_default_int(obj, "transitionTabPos", 1);
|
||||||
|
obs_data_set_default_int(obj, "pauseTabPos", 2);
|
||||||
|
obs_data_set_default_int(obj, "titleTabPos", 3);
|
||||||
|
obs_data_set_default_int(obj, "exeTabPos", 4);
|
||||||
|
obs_data_set_default_int(obj, "regionTabPos", 5);
|
||||||
|
obs_data_set_default_int(obj, "mediaTabPos", 6);
|
||||||
|
obs_data_set_default_int(obj, "fileTabPos", 7);
|
||||||
|
obs_data_set_default_int(obj, "randomTabPos", 8);
|
||||||
|
obs_data_set_default_int(obj, "timeTabPos", 9);
|
||||||
|
obs_data_set_default_int(obj, "idleTabPos", 10);
|
||||||
|
obs_data_set_default_int(obj, "sequenceTabPos", 11);
|
||||||
|
|
||||||
|
switcher->tabOrder.emplace_back(
|
||||||
|
(int)(obs_data_get_int(obj, "generalTabPos")));
|
||||||
|
switcher->tabOrder.emplace_back(
|
||||||
|
(int)(obs_data_get_int(obj, "transitionTabPos")));
|
||||||
|
switcher->tabOrder.emplace_back(
|
||||||
|
(int)(obs_data_get_int(obj, "pauseTabPos")));
|
||||||
|
switcher->tabOrder.emplace_back(
|
||||||
|
(int)(obs_data_get_int(obj, "titleTabPos")));
|
||||||
|
switcher->tabOrder.emplace_back(
|
||||||
|
(int)(obs_data_get_int(obj, "exeTabPos")));
|
||||||
|
switcher->tabOrder.emplace_back(
|
||||||
|
(int)(obs_data_get_int(obj, "regionTabPos")));
|
||||||
|
switcher->tabOrder.emplace_back(
|
||||||
|
(int)(obs_data_get_int(obj, "mediaTabPos")));
|
||||||
|
switcher->tabOrder.emplace_back(
|
||||||
|
(int)(obs_data_get_int(obj, "fileTabPos")));
|
||||||
|
switcher->tabOrder.emplace_back(
|
||||||
|
(int)(obs_data_get_int(obj, "randomTabPos")));
|
||||||
|
switcher->tabOrder.emplace_back(
|
||||||
|
(int)(obs_data_get_int(obj, "timeTabPos")));
|
||||||
|
switcher->tabOrder.emplace_back(
|
||||||
|
(int)(obs_data_get_int(obj, "idleTabPos")));
|
||||||
|
switcher->tabOrder.emplace_back(
|
||||||
|
(int)(obs_data_get_int(obj, "sequenceTabPos")));
|
||||||
|
}
|
||||||
|
|
||||||
|
void SceneSwitcher::setupGeneralTab()
|
||||||
|
{
|
||||||
|
populateSceneSelection(ui->noMatchSwitchScene, false);
|
||||||
|
populateSceneSelection(ui->autoStopScenes, false);
|
||||||
|
|
||||||
|
if (switcher->switchIfNotMatching == SWITCH) {
|
||||||
|
ui->noMatchSwitch->setChecked(true);
|
||||||
|
ui->noMatchSwitchScene->setEnabled(true);
|
||||||
|
} else if (switcher->switchIfNotMatching == NO_SWITCH) {
|
||||||
|
ui->noMatchDontSwitch->setChecked(true);
|
||||||
|
ui->noMatchSwitchScene->setEnabled(false);
|
||||||
|
} else {
|
||||||
|
ui->noMatchRandomSwitch->setChecked(true);
|
||||||
|
ui->noMatchSwitchScene->setEnabled(false);
|
||||||
|
}
|
||||||
|
ui->noMatchSwitchScene->setCurrentText(
|
||||||
|
GetWeakSourceName(switcher->nonMatchingScene).c_str());
|
||||||
|
ui->checkInterval->setValue(switcher->interval);
|
||||||
|
|
||||||
|
ui->autoStopSceneCheckBox->setChecked(switcher->autoStopEnable);
|
||||||
|
ui->autoStopScenes->setCurrentText(
|
||||||
|
GetWeakSourceName(switcher->autoStopScene).c_str());
|
||||||
|
|
||||||
|
if (ui->autoStopSceneCheckBox->checkState()) {
|
||||||
|
ui->autoStopScenes->setDisabled(false);
|
||||||
|
} else {
|
||||||
|
ui->autoStopScenes->setDisabled(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
ui->verboseLogging->setChecked(switcher->verbose);
|
||||||
|
|
||||||
|
for (int p : switcher->functionNamesByPriority) {
|
||||||
|
std::string s = "";
|
||||||
|
switch (p) {
|
||||||
|
case READ_FILE_FUNC:
|
||||||
|
s = "File Content";
|
||||||
|
break;
|
||||||
|
case ROUND_TRIP_FUNC:
|
||||||
|
s = "Scene Sequence";
|
||||||
|
break;
|
||||||
|
case IDLE_FUNC:
|
||||||
|
s = "Idle Detection";
|
||||||
|
break;
|
||||||
|
case EXE_FUNC:
|
||||||
|
s = "Executable";
|
||||||
|
break;
|
||||||
|
case SCREEN_REGION_FUNC:
|
||||||
|
s = "Screen Region";
|
||||||
|
break;
|
||||||
|
case WINDOW_TITLE_FUNC:
|
||||||
|
s = "Window Title";
|
||||||
|
break;
|
||||||
|
case MEDIA_FUNC:
|
||||||
|
s = "Media";
|
||||||
|
break;
|
||||||
|
case TIME_FUNC:
|
||||||
|
s = "Time";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
QString text(s.c_str());
|
||||||
|
QListWidgetItem *item =
|
||||||
|
new QListWidgetItem(text, ui->priorityList);
|
||||||
|
item->setData(Qt::UserRole, text);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < switcher->threadPriorities.size(); ++i) {
|
||||||
|
ui->threadPriority->addItem(
|
||||||
|
switcher->threadPriorities[i].name.c_str());
|
||||||
|
ui->threadPriority->setItemData(
|
||||||
|
i, switcher->threadPriorities[i].description.c_str(),
|
||||||
|
Qt::ToolTipRole);
|
||||||
|
if (switcher->threadPriority ==
|
||||||
|
switcher->threadPriorities[i].value) {
|
||||||
|
ui->threadPriority->setCurrentText(
|
||||||
|
switcher->threadPriorities[i].name.c_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ui->startupBehavior->addItem(
|
||||||
|
"Start the scene switcher if it was running");
|
||||||
|
ui->startupBehavior->addItem("Always start the scene switcher");
|
||||||
|
ui->startupBehavior->addItem("Do not start the scene switcher");
|
||||||
|
|
||||||
|
ui->startupBehavior->setCurrentIndex(switcher->startupBehavior);
|
||||||
|
|
||||||
|
if (switcher->th && switcher->th->isRunning())
|
||||||
|
SetStarted();
|
||||||
|
else
|
||||||
|
SetStopped();
|
||||||
|
}
|
||||||
246
src/headers/advanced-scene-switcher.hpp
Normal file
246
src/headers/advanced-scene-switcher.hpp
Normal file
@@ -0,0 +1,246 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QDialog>
|
||||||
|
#include <memory>
|
||||||
|
#include <vector>
|
||||||
|
#include <string>
|
||||||
|
#ifdef BUILD_OUT_OF_TREE
|
||||||
|
#include "../../forms/ui_advanced-scene-switcher.h"
|
||||||
|
#else
|
||||||
|
#include "ui_advanced-scene-switcher.h"
|
||||||
|
#endif
|
||||||
|
#include "switcher-data-structs.hpp"
|
||||||
|
|
||||||
|
class QCloseEvent;
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
* Advanced Scene Switcher window
|
||||||
|
*******************************************************************************/
|
||||||
|
class SceneSwitcher : public QDialog {
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
std::unique_ptr<Ui_SceneSwitcher> ui;
|
||||||
|
bool loading = true;
|
||||||
|
|
||||||
|
SceneSwitcher(QWidget *parent);
|
||||||
|
|
||||||
|
void closeEvent(QCloseEvent *event) override;
|
||||||
|
|
||||||
|
void SetStarted();
|
||||||
|
void SetStopped();
|
||||||
|
|
||||||
|
int FindByData(const QString &window);
|
||||||
|
int ScreenRegionFindByData(const QString ®ion);
|
||||||
|
int PauseScenesFindByData(const QString &scene);
|
||||||
|
int PauseWindowsFindByData(const QString &window);
|
||||||
|
int IgnoreWindowsFindByData(const QString &window);
|
||||||
|
int SceneRoundTripFindByData(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 randomFindByData(const QString &scene);
|
||||||
|
int timeFindByData(const QString &timeStr);
|
||||||
|
|
||||||
|
void UpdateNonMatchingScene(const QString &name);
|
||||||
|
void UpdateAutoStopScene(const QString &name);
|
||||||
|
void UpdateIdleDataTransition(const QString &name);
|
||||||
|
void UpdateIdleDataScene(const QString &name);
|
||||||
|
|
||||||
|
void loadUI();
|
||||||
|
void populateSceneSelection(QComboBox *sel, bool addPrevious);
|
||||||
|
void populateTransitionSelection(QComboBox *sel);
|
||||||
|
void populateWindowSelection(QComboBox *sel);
|
||||||
|
void setupGeneralTab();
|
||||||
|
void setupTitleTab();
|
||||||
|
void setupExecutableTab();
|
||||||
|
void setupRegionTab();
|
||||||
|
void setupPauseTab();
|
||||||
|
void setupSequenceTab();
|
||||||
|
void setupTransitionsTab();
|
||||||
|
void setupIdleTab();
|
||||||
|
void setupRandomTab();
|
||||||
|
void setupMediaTab();
|
||||||
|
void setupFileTab();
|
||||||
|
void setupTimeTab();
|
||||||
|
void setTabOrder();
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void on_switches_currentRowChanged(int idx);
|
||||||
|
void on_up_clicked();
|
||||||
|
void on_down_clicked();
|
||||||
|
void on_add_clicked();
|
||||||
|
void on_remove_clicked();
|
||||||
|
void on_noMatchDontSwitch_clicked();
|
||||||
|
void on_noMatchSwitch_clicked();
|
||||||
|
void on_noMatchRandomSwitch_clicked();
|
||||||
|
void on_startupBehavior_currentIndexChanged(int index);
|
||||||
|
void on_noMatchSwitchScene_currentTextChanged(const QString &text);
|
||||||
|
void on_checkInterval_valueChanged(int value);
|
||||||
|
void on_toggleStartButton_clicked();
|
||||||
|
void on_tabMoved(int from, int to);
|
||||||
|
|
||||||
|
void on_screenRegions_currentRowChanged(int idx);
|
||||||
|
void on_screenRegionAdd_clicked();
|
||||||
|
void on_screenRegionRemove_clicked();
|
||||||
|
void on_screenRegionUp_clicked();
|
||||||
|
void on_screenRegionDown_clicked();
|
||||||
|
|
||||||
|
void on_pauseScenes_currentRowChanged(int idx);
|
||||||
|
void on_pauseScenesAdd_clicked();
|
||||||
|
void on_pauseScenesRemove_clicked();
|
||||||
|
|
||||||
|
void on_pauseWindows_currentRowChanged(int idx);
|
||||||
|
void on_pauseWindowsAdd_clicked();
|
||||||
|
void on_pauseWindowsRemove_clicked();
|
||||||
|
|
||||||
|
void on_ignoreWindows_currentRowChanged(int idx);
|
||||||
|
void on_ignoreWindowsAdd_clicked();
|
||||||
|
void on_ignoreWindowsRemove_clicked();
|
||||||
|
|
||||||
|
void on_sceneRoundTrips_currentRowChanged(int idx);
|
||||||
|
void on_sceneRoundTripAdd_clicked();
|
||||||
|
void on_sceneRoundTripRemove_clicked();
|
||||||
|
void on_sceneRoundTripSave_clicked();
|
||||||
|
void on_sceneRoundTripLoad_clicked();
|
||||||
|
void on_sceneRoundTripUp_clicked();
|
||||||
|
void on_sceneRoundTripDown_clicked();
|
||||||
|
void on_sceneRoundTripDelayUnits_currentIndexChanged(int index);
|
||||||
|
|
||||||
|
void on_autoStopSceneCheckBox_stateChanged(int state);
|
||||||
|
void on_autoStopScenes_currentTextChanged(const QString &text);
|
||||||
|
|
||||||
|
void on_verboseLogging_stateChanged(int state);
|
||||||
|
|
||||||
|
void on_exportSettings_clicked();
|
||||||
|
void on_importSettings_clicked();
|
||||||
|
|
||||||
|
void on_sceneTransitions_currentRowChanged(int idx);
|
||||||
|
void on_transitionsAdd_clicked();
|
||||||
|
void on_transitionsRemove_clicked();
|
||||||
|
void on_defaultTransitions_currentRowChanged(int idx);
|
||||||
|
void on_defaultTransitionsAdd_clicked();
|
||||||
|
void on_defaultTransitionsRemove_clicked();
|
||||||
|
void on_transitionOverridecheckBox_stateChanged(int state);
|
||||||
|
|
||||||
|
void on_browseButton_clicked();
|
||||||
|
void on_readFileCheckBox_stateChanged(int state);
|
||||||
|
void on_fileType_currentIndexChanged(int idx);
|
||||||
|
void on_readPathLineEdit_textChanged(const QString &text);
|
||||||
|
void on_writePathLineEdit_textChanged(const QString &text);
|
||||||
|
void on_browseButton_2_clicked();
|
||||||
|
|
||||||
|
void on_executableUp_clicked();
|
||||||
|
void on_executableDown_clicked();
|
||||||
|
void on_executableAdd_clicked();
|
||||||
|
void on_executableRemove_clicked();
|
||||||
|
void on_executables_currentRowChanged(int idx);
|
||||||
|
|
||||||
|
void on_idleCheckBox_stateChanged(int state);
|
||||||
|
void on_idleTransitions_currentTextChanged(const QString &text);
|
||||||
|
void on_idleScenes_currentTextChanged(const QString &text);
|
||||||
|
void on_idleSpinBox_valueChanged(int i);
|
||||||
|
void on_ignoreIdleWindows_currentRowChanged(int idx);
|
||||||
|
void on_ignoreIdleAdd_clicked();
|
||||||
|
void on_ignoreIdleRemove_clicked();
|
||||||
|
|
||||||
|
void on_randomAdd_clicked();
|
||||||
|
void on_randomRemove_clicked();
|
||||||
|
void on_randomScenesList_currentRowChanged(int idx);
|
||||||
|
|
||||||
|
void on_fileAdd_clicked();
|
||||||
|
void on_fileRemove_clicked();
|
||||||
|
void on_fileScenesList_currentRowChanged(int idx);
|
||||||
|
void on_browseButton_3_clicked();
|
||||||
|
void on_fileUp_clicked();
|
||||||
|
void on_fileDown_clicked();
|
||||||
|
|
||||||
|
void on_mediaSwitches_currentRowChanged(int idx);
|
||||||
|
void on_mediaAdd_clicked();
|
||||||
|
void on_mediaRemove_clicked();
|
||||||
|
void on_mediaUp_clicked();
|
||||||
|
void on_mediaDown_clicked();
|
||||||
|
void on_mediaTimeRestrictions_currentIndexChanged(int idx);
|
||||||
|
|
||||||
|
void on_timeSwitches_currentRowChanged(int idx);
|
||||||
|
void on_timeAdd_clicked();
|
||||||
|
void on_timeRemove_clicked();
|
||||||
|
|
||||||
|
void on_priorityUp_clicked();
|
||||||
|
void on_priorityDown_clicked();
|
||||||
|
void on_threadPriority_currentTextChanged(const QString &text);
|
||||||
|
|
||||||
|
void updateScreenRegionCursorPos();
|
||||||
|
|
||||||
|
void on_close_clicked();
|
||||||
|
};
|
||||||
|
|
||||||
|
/********************************************************************************
|
||||||
|
* Windowtitle helper
|
||||||
|
********************************************************************************/
|
||||||
|
void GetWindowList(std::vector<std::string> &windows);
|
||||||
|
void GetWindowList(QStringList &windows); // Overloaded
|
||||||
|
void GetCurrentWindowTitle(std::string &title);
|
||||||
|
bool isFullscreen(std::string &title);
|
||||||
|
|
||||||
|
/********************************************************************************
|
||||||
|
* Screenregion helper
|
||||||
|
********************************************************************************/
|
||||||
|
std::pair<int, int> getCursorPos();
|
||||||
|
|
||||||
|
/********************************************************************************
|
||||||
|
* Idle detection helper
|
||||||
|
********************************************************************************/
|
||||||
|
int secondsSinceLastInput();
|
||||||
|
|
||||||
|
/********************************************************************************
|
||||||
|
* Executable helper
|
||||||
|
********************************************************************************/
|
||||||
|
void GetProcessList(QStringList &processes);
|
||||||
|
bool isInFocus(const QString &executable);
|
||||||
|
|
||||||
|
/********************************************************************************
|
||||||
|
* Sceneswitch helper
|
||||||
|
********************************************************************************/
|
||||||
|
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,
|
||||||
|
OBSWeakSource &transition,
|
||||||
|
bool &transitionOverrideOverride, transitionData &td);
|
||||||
|
void overwriteTransitionOverride(obs_weak_source_t *sceneWs,
|
||||||
|
obs_source_t *transition, transitionData &td);
|
||||||
|
void restoreTransitionOverride(obs_source_t *scene, transitionData td);
|
||||||
|
|
||||||
|
void switchScene(OBSWeakSource &scene, OBSWeakSource &transition,
|
||||||
|
bool &transitionOverrideOverride,
|
||||||
|
std::unique_lock<std::mutex> &lock);
|
||||||
|
|
||||||
|
/********************************************************************************
|
||||||
|
* Hotkey helper
|
||||||
|
********************************************************************************/
|
||||||
|
|
||||||
|
#define TOGGLE_HOTKEY_PATH "hotkey_toggle.txt"
|
||||||
|
#define STOP_HOTKEY_PATH "hotkey_stop.txt"
|
||||||
|
#define START_HOTKEY_PATH "hotkey_start.txt"
|
||||||
|
|
||||||
|
void stopHotkeyFunc(void *data, obs_hotkey_id id, obs_hotkey_t *hotkey,
|
||||||
|
bool pressed);
|
||||||
|
void startHotkeyFunc(void *data, obs_hotkey_id id, obs_hotkey_t *hotkey,
|
||||||
|
bool pressed);
|
||||||
|
void startStopToggleHotkeyFunc(void *data, obs_hotkey_id id,
|
||||||
|
obs_hotkey_t *hotkey, bool pressed);
|
||||||
|
void loadKeybinding(obs_hotkey_id hotkeyId, std::string path);
|
||||||
|
|
||||||
|
/********************************************************************************
|
||||||
|
* Main SwitcherData
|
||||||
|
********************************************************************************/
|
||||||
|
struct SwitcherData;
|
||||||
|
extern SwitcherData *switcher;
|
||||||
527
src/headers/switcher-data-structs.hpp
Normal file
527
src/headers/switcher-data-structs.hpp
Normal file
@@ -0,0 +1,527 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <condition_variable>
|
||||||
|
#include <chrono>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
#include <regex>
|
||||||
|
#include <mutex>
|
||||||
|
#include <fstream>
|
||||||
|
#include <QDateTime>
|
||||||
|
#include <QThread>
|
||||||
|
#include "utility.hpp"
|
||||||
|
|
||||||
|
#define DEFAULT_INTERVAL 300
|
||||||
|
|
||||||
|
#define DEFAULT_IDLE_TIME 60
|
||||||
|
|
||||||
|
#define PREVIOUS_SCENE_NAME "Previous Scene"
|
||||||
|
|
||||||
|
#define READ_FILE_FUNC 0
|
||||||
|
#define ROUND_TRIP_FUNC 1
|
||||||
|
#define IDLE_FUNC 2
|
||||||
|
#define EXE_FUNC 3
|
||||||
|
#define SCREEN_REGION_FUNC 4
|
||||||
|
#define WINDOW_TITLE_FUNC 5
|
||||||
|
#define MEDIA_FUNC 6
|
||||||
|
#define TIME_FUNC 7
|
||||||
|
|
||||||
|
#define DEFAULT_PRIORITY_0 READ_FILE_FUNC
|
||||||
|
#define DEFAULT_PRIORITY_1 ROUND_TRIP_FUNC
|
||||||
|
#define DEFAULT_PRIORITY_2 IDLE_FUNC
|
||||||
|
#define DEFAULT_PRIORITY_3 EXE_FUNC
|
||||||
|
#define DEFAULT_PRIORITY_4 SCREEN_REGION_FUNC
|
||||||
|
#define DEFAULT_PRIORITY_5 WINDOW_TITLE_FUNC
|
||||||
|
#define DEFAULT_PRIORITY_6 MEDIA_FUNC
|
||||||
|
#define DEFAULT_PRIORITY_7 TIME_FUNC
|
||||||
|
|
||||||
|
/********************************************************************************
|
||||||
|
* Data structs for each scene switching method
|
||||||
|
********************************************************************************/
|
||||||
|
struct WindowSceneSwitch {
|
||||||
|
OBSWeakSource scene;
|
||||||
|
std::string window;
|
||||||
|
OBSWeakSource transition;
|
||||||
|
bool fullscreen;
|
||||||
|
bool focus;
|
||||||
|
|
||||||
|
inline WindowSceneSwitch(OBSWeakSource scene_, const char *window_,
|
||||||
|
OBSWeakSource transition_, bool fullscreen_,
|
||||||
|
bool focus_)
|
||||||
|
: scene(scene_),
|
||||||
|
window(window_),
|
||||||
|
transition(transition_),
|
||||||
|
fullscreen(fullscreen_),
|
||||||
|
focus(focus_)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ExecutableSceneSwitch {
|
||||||
|
OBSWeakSource mScene;
|
||||||
|
OBSWeakSource mTransition;
|
||||||
|
QString mExe;
|
||||||
|
bool mInFocus;
|
||||||
|
|
||||||
|
inline ExecutableSceneSwitch(OBSWeakSource scene,
|
||||||
|
OBSWeakSource transition,
|
||||||
|
const QString &exe, bool inFocus)
|
||||||
|
: mScene(scene),
|
||||||
|
mTransition(transition),
|
||||||
|
mExe(exe),
|
||||||
|
mInFocus(inFocus)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ScreenRegionSwitch {
|
||||||
|
OBSWeakSource scene;
|
||||||
|
OBSWeakSource transition;
|
||||||
|
int minX, minY, maxX, maxY;
|
||||||
|
std::string regionStr;
|
||||||
|
|
||||||
|
inline ScreenRegionSwitch(OBSWeakSource scene_,
|
||||||
|
OBSWeakSource transition_, int minX_,
|
||||||
|
int minY_, int maxX_, int maxY_,
|
||||||
|
std::string regionStr_)
|
||||||
|
: scene(scene_),
|
||||||
|
transition(transition_),
|
||||||
|
minX(minX_),
|
||||||
|
minY(minY_),
|
||||||
|
maxX(maxX_),
|
||||||
|
maxY(maxY_),
|
||||||
|
regionStr(regionStr_)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct SceneRoundTripSwitch {
|
||||||
|
OBSWeakSource scene1;
|
||||||
|
OBSWeakSource scene2;
|
||||||
|
OBSWeakSource transition;
|
||||||
|
double delay;
|
||||||
|
bool usePreviousScene;
|
||||||
|
std::string sceneRoundTripStr;
|
||||||
|
|
||||||
|
inline SceneRoundTripSwitch(OBSWeakSource scene1_,
|
||||||
|
OBSWeakSource scene2_,
|
||||||
|
OBSWeakSource transition_, double delay_,
|
||||||
|
bool usePreviousScene_, std::string str)
|
||||||
|
: scene1(scene1_),
|
||||||
|
scene2(scene2_),
|
||||||
|
transition(transition_),
|
||||||
|
delay(delay_),
|
||||||
|
usePreviousScene(usePreviousScene_),
|
||||||
|
sceneRoundTripStr(str)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct RandomSwitch {
|
||||||
|
OBSWeakSource scene;
|
||||||
|
OBSWeakSource transition;
|
||||||
|
double delay;
|
||||||
|
std::string randomSwitchStr;
|
||||||
|
|
||||||
|
inline RandomSwitch(OBSWeakSource scene_, OBSWeakSource transition_,
|
||||||
|
double delay_, std::string str)
|
||||||
|
: scene(scene_),
|
||||||
|
transition(transition_),
|
||||||
|
delay(delay_),
|
||||||
|
randomSwitchStr(str)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct SceneTransition {
|
||||||
|
OBSWeakSource scene1;
|
||||||
|
OBSWeakSource scene2;
|
||||||
|
OBSWeakSource transition;
|
||||||
|
std::string sceneTransitionStr;
|
||||||
|
|
||||||
|
inline SceneTransition(OBSWeakSource scene1_, OBSWeakSource scene2_,
|
||||||
|
OBSWeakSource transition_,
|
||||||
|
std::string sceneTransitionStr_)
|
||||||
|
: scene1(scene1_),
|
||||||
|
scene2(scene2_),
|
||||||
|
transition(transition_),
|
||||||
|
sceneTransitionStr(sceneTransitionStr_)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct DefaultSceneTransition {
|
||||||
|
OBSWeakSource scene;
|
||||||
|
OBSWeakSource transition;
|
||||||
|
std::string sceneTransitionStr;
|
||||||
|
|
||||||
|
inline DefaultSceneTransition(OBSWeakSource scene_,
|
||||||
|
OBSWeakSource transition_,
|
||||||
|
std::string sceneTransitionStr_)
|
||||||
|
: scene(scene_),
|
||||||
|
transition(transition_),
|
||||||
|
sceneTransitionStr(sceneTransitionStr_)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct FileSwitch {
|
||||||
|
OBSWeakSource scene;
|
||||||
|
OBSWeakSource transition;
|
||||||
|
std::string file;
|
||||||
|
std::string text;
|
||||||
|
bool remote = false;
|
||||||
|
bool useRegex = false;
|
||||||
|
bool useTime = false;
|
||||||
|
QDateTime lastMod;
|
||||||
|
|
||||||
|
inline FileSwitch(OBSWeakSource scene_, OBSWeakSource transition_,
|
||||||
|
const char *file_, const char *text_, bool remote_,
|
||||||
|
bool useRegex_, bool useTime_)
|
||||||
|
: scene(scene_),
|
||||||
|
transition(transition_),
|
||||||
|
file(file_),
|
||||||
|
text(text_),
|
||||||
|
remote(remote_),
|
||||||
|
useRegex(useRegex_),
|
||||||
|
useTime(useTime_),
|
||||||
|
lastMod()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct FileIOData {
|
||||||
|
bool readEnabled = false;
|
||||||
|
std::string readPath;
|
||||||
|
bool writeEnabled = false;
|
||||||
|
std::string writePath;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct IdleData {
|
||||||
|
bool idleEnable = false;
|
||||||
|
int time = DEFAULT_IDLE_TIME;
|
||||||
|
OBSWeakSource scene;
|
||||||
|
OBSWeakSource transition;
|
||||||
|
bool usePreviousScene;
|
||||||
|
bool alreadySwitched = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct MediaSwitch {
|
||||||
|
OBSWeakSource scene;
|
||||||
|
OBSWeakSource source;
|
||||||
|
OBSWeakSource transition;
|
||||||
|
obs_media_state state;
|
||||||
|
int64_t time;
|
||||||
|
time_restriction restriction;
|
||||||
|
bool matched;
|
||||||
|
bool usePreviousScene;
|
||||||
|
std::string mediaSwitchStr;
|
||||||
|
|
||||||
|
inline MediaSwitch(OBSWeakSource scene_, OBSWeakSource source_,
|
||||||
|
OBSWeakSource transition_, obs_media_state state_,
|
||||||
|
time_restriction restriction_, uint64_t time_,
|
||||||
|
bool usePreviousScene_, std::string mediaSwitchStr_)
|
||||||
|
: scene(scene_),
|
||||||
|
source(source_),
|
||||||
|
transition(transition_),
|
||||||
|
state(state_),
|
||||||
|
restriction(restriction_),
|
||||||
|
time(time_),
|
||||||
|
usePreviousScene(usePreviousScene_),
|
||||||
|
mediaSwitchStr(mediaSwitchStr_)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct TimeSwitch {
|
||||||
|
OBSWeakSource scene;
|
||||||
|
OBSWeakSource transition;
|
||||||
|
QTime time;
|
||||||
|
bool matched;
|
||||||
|
bool usePreviousScene;
|
||||||
|
std::string timeSwitchStr;
|
||||||
|
|
||||||
|
inline TimeSwitch(OBSWeakSource scene_, OBSWeakSource transition_,
|
||||||
|
QTime time_, bool usePreviousScene_,
|
||||||
|
std::string timeSwitchStr_)
|
||||||
|
: scene(scene_),
|
||||||
|
transition(transition_),
|
||||||
|
time(time_),
|
||||||
|
usePreviousScene(usePreviousScene_),
|
||||||
|
timeSwitchStr(timeSwitchStr_)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef enum { NO_SWITCH = 0, SWITCH = 1, RANDOM_SWITCH = 2 } NoMatch;
|
||||||
|
typedef enum { PERSIST = 0, START = 1, STOP = 2 } StartupBehavior;
|
||||||
|
|
||||||
|
class SwitcherThread;
|
||||||
|
|
||||||
|
/********************************************************************************
|
||||||
|
* SwitcherData
|
||||||
|
********************************************************************************/
|
||||||
|
struct SwitcherData {
|
||||||
|
SwitcherThread *th = nullptr;
|
||||||
|
|
||||||
|
std::condition_variable cv;
|
||||||
|
std::mutex m;
|
||||||
|
bool transitionActive = false;
|
||||||
|
bool waitForTransition = false;
|
||||||
|
std::condition_variable transitionCv;
|
||||||
|
bool stop = false;
|
||||||
|
bool verbose = false;
|
||||||
|
bool tansitionOverrideOverride = false;
|
||||||
|
|
||||||
|
int interval = DEFAULT_INTERVAL;
|
||||||
|
|
||||||
|
obs_source_t *waitScene = NULL; //scene during which wait started
|
||||||
|
OBSWeakSource previousScene = NULL;
|
||||||
|
OBSWeakSource PreviousScene2 = NULL;
|
||||||
|
OBSWeakSource lastRandomScene;
|
||||||
|
OBSWeakSource nonMatchingScene;
|
||||||
|
NoMatch switchIfNotMatching = NO_SWITCH;
|
||||||
|
StartupBehavior startupBehavior = PERSIST;
|
||||||
|
|
||||||
|
std::vector<WindowSceneSwitch> windowSwitches;
|
||||||
|
std::vector<std::string> ignoreIdleWindows;
|
||||||
|
std::string lastTitle;
|
||||||
|
|
||||||
|
std::vector<ScreenRegionSwitch> screenRegionSwitches;
|
||||||
|
|
||||||
|
std::vector<OBSWeakSource> pauseScenesSwitches;
|
||||||
|
|
||||||
|
std::vector<std::string> pauseWindowsSwitches;
|
||||||
|
|
||||||
|
std::vector<std::string> ignoreWindowsSwitches;
|
||||||
|
|
||||||
|
std::vector<SceneRoundTripSwitch> sceneRoundTripSwitches;
|
||||||
|
int sceneRoundTripUnitMultiplier = 1;
|
||||||
|
|
||||||
|
std::vector<RandomSwitch> randomSwitches;
|
||||||
|
|
||||||
|
FileIOData fileIO;
|
||||||
|
IdleData idleData;
|
||||||
|
std::vector<FileSwitch> fileSwitches;
|
||||||
|
|
||||||
|
std::vector<ExecutableSceneSwitch> executableSwitches;
|
||||||
|
|
||||||
|
bool autoStopEnable = false;
|
||||||
|
OBSWeakSource autoStopScene;
|
||||||
|
|
||||||
|
std::vector<SceneTransition> sceneTransitions;
|
||||||
|
std::vector<DefaultSceneTransition> defaultSceneTransitions;
|
||||||
|
|
||||||
|
std::vector<MediaSwitch> mediaSwitches;
|
||||||
|
|
||||||
|
std::vector<TimeSwitch> timeSwitches;
|
||||||
|
|
||||||
|
std::vector<int> functionNamesByPriority = std::vector<int>{
|
||||||
|
DEFAULT_PRIORITY_0, DEFAULT_PRIORITY_1, DEFAULT_PRIORITY_2,
|
||||||
|
DEFAULT_PRIORITY_3, DEFAULT_PRIORITY_4, DEFAULT_PRIORITY_5,
|
||||||
|
DEFAULT_PRIORITY_6, DEFAULT_PRIORITY_7};
|
||||||
|
|
||||||
|
struct ThreadPrio {
|
||||||
|
std::string name;
|
||||||
|
std::string description;
|
||||||
|
uint32_t value;
|
||||||
|
};
|
||||||
|
|
||||||
|
std::vector<ThreadPrio> threadPriorities{
|
||||||
|
{"Idle",
|
||||||
|
"scheduled only when no other threads are running (lowest CPU load)",
|
||||||
|
QThread::IdlePriority},
|
||||||
|
{"Lowest", "scheduled less often than LowPriority",
|
||||||
|
QThread::LowestPriority},
|
||||||
|
{"Low", "scheduled less often than NormalPriority",
|
||||||
|
QThread::LowPriority},
|
||||||
|
{"Normal", "the default priority of the operating system",
|
||||||
|
QThread::NormalPriority},
|
||||||
|
{"High", "scheduled more often than NormalPriority",
|
||||||
|
QThread::HighPriority},
|
||||||
|
{"Highest", "scheduled more often than HighPriority",
|
||||||
|
QThread::HighestPriority},
|
||||||
|
{"Time critical",
|
||||||
|
"scheduled as often as possible (highest CPU load)",
|
||||||
|
QThread::TimeCriticalPriority},
|
||||||
|
};
|
||||||
|
|
||||||
|
uint32_t threadPriority = QThread::NormalPriority;
|
||||||
|
|
||||||
|
std::vector<int> tabOrder;
|
||||||
|
|
||||||
|
void Thread();
|
||||||
|
void Start();
|
||||||
|
void Stop();
|
||||||
|
|
||||||
|
bool sceneChangedDuringWait();
|
||||||
|
bool prioFuncsValid();
|
||||||
|
void writeSceneInfoToFile();
|
||||||
|
void setDefaultSceneTransitions();
|
||||||
|
void autoStopStreamAndRecording();
|
||||||
|
bool checkPause();
|
||||||
|
void checkSceneRoundTrip(bool &match, OBSWeakSource &scene,
|
||||||
|
OBSWeakSource &transition,
|
||||||
|
std::unique_lock<std::mutex> &lock);
|
||||||
|
void checkIdleSwitch(bool &match, OBSWeakSource &scene,
|
||||||
|
OBSWeakSource &transition);
|
||||||
|
void checkWindowTitleSwitch(bool &match, OBSWeakSource &scene,
|
||||||
|
OBSWeakSource &transition);
|
||||||
|
void checkExeSwitch(bool &match, OBSWeakSource &scene,
|
||||||
|
OBSWeakSource &transition);
|
||||||
|
void checkScreenRegionSwitch(bool &match, OBSWeakSource &scene,
|
||||||
|
OBSWeakSource &transition);
|
||||||
|
void checkSwitchInfoFromFile(bool &match, OBSWeakSource &scene,
|
||||||
|
OBSWeakSource &transition);
|
||||||
|
void checkFileContent(bool &match, OBSWeakSource &scene,
|
||||||
|
OBSWeakSource &transition);
|
||||||
|
void checkRandom(bool &match, OBSWeakSource &scene,
|
||||||
|
OBSWeakSource &transition, int &delay);
|
||||||
|
void checkMediaSwitch(bool &match, OBSWeakSource &scene,
|
||||||
|
OBSWeakSource &transition);
|
||||||
|
void checkTimeSwitch(bool &match, OBSWeakSource &scene,
|
||||||
|
OBSWeakSource &transition);
|
||||||
|
|
||||||
|
void saveWindowTitleSwitches(obs_data_t *obj);
|
||||||
|
void saveScreenRegionSwitches(obs_data_t *obj);
|
||||||
|
void savePauseSwitches(obs_data_t *obj);
|
||||||
|
void saveSceneRoundTripSwitches(obs_data_t *obj);
|
||||||
|
void saveSceneTransitions(obs_data_t *obj);
|
||||||
|
void saveIdleSwitches(obs_data_t *obj);
|
||||||
|
void saveExecutableSwitches(obs_data_t *obj);
|
||||||
|
void saveRandomSwitches(obs_data_t *obj);
|
||||||
|
void saveFileSwitches(obs_data_t *obj);
|
||||||
|
void saveMediaSwitches(obs_data_t *obj);
|
||||||
|
void saveTimeSwitches(obs_data_t *obj);
|
||||||
|
void saveGeneralSettings(obs_data_t *obj);
|
||||||
|
|
||||||
|
void loadWindowTitleSwitches(obs_data_t *obj);
|
||||||
|
void loadScreenRegionSwitches(obs_data_t *obj);
|
||||||
|
void loadPauseSwitches(obs_data_t *obj);
|
||||||
|
void loadSceneRoundTripSwitches(obs_data_t *obj);
|
||||||
|
void loadSceneTransitions(obs_data_t *obj);
|
||||||
|
void loadIdleSwitches(obs_data_t *obj);
|
||||||
|
void loadExecutableSwitches(obs_data_t *obj);
|
||||||
|
void loadRandomSwitches(obs_data_t *obj);
|
||||||
|
void loadFileSwitches(obs_data_t *obj);
|
||||||
|
void loadMediaSwitches(obs_data_t *obj);
|
||||||
|
void loadTimeSwitches(obs_data_t *obj);
|
||||||
|
void loadGeneralSettings(obs_data_t *obj);
|
||||||
|
|
||||||
|
void Prune()
|
||||||
|
{
|
||||||
|
for (size_t i = 0; i < windowSwitches.size(); i++) {
|
||||||
|
WindowSceneSwitch &s = windowSwitches[i];
|
||||||
|
if (!WeakSourceValid(s.scene) ||
|
||||||
|
!WeakSourceValid(s.transition))
|
||||||
|
windowSwitches.erase(windowSwitches.begin() +
|
||||||
|
i--);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nonMatchingScene && !WeakSourceValid(nonMatchingScene)) {
|
||||||
|
switchIfNotMatching = NO_SWITCH;
|
||||||
|
nonMatchingScene = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (size_t i = 0; i < randomSwitches.size(); i++) {
|
||||||
|
RandomSwitch &s = randomSwitches[i];
|
||||||
|
if (!WeakSourceValid(s.scene) ||
|
||||||
|
!WeakSourceValid(s.transition))
|
||||||
|
randomSwitches.erase(randomSwitches.begin() +
|
||||||
|
i--);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (size_t i = 0; i < screenRegionSwitches.size(); i++) {
|
||||||
|
ScreenRegionSwitch &s = screenRegionSwitches[i];
|
||||||
|
if (!WeakSourceValid(s.scene) ||
|
||||||
|
!WeakSourceValid(s.transition))
|
||||||
|
screenRegionSwitches.erase(
|
||||||
|
screenRegionSwitches.begin() + i--);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (size_t i = 0; i < pauseScenesSwitches.size(); i++) {
|
||||||
|
OBSWeakSource &scene = pauseScenesSwitches[i];
|
||||||
|
if (!WeakSourceValid(scene))
|
||||||
|
pauseScenesSwitches.erase(
|
||||||
|
pauseScenesSwitches.begin() + i--);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (size_t i = 0; i < sceneRoundTripSwitches.size(); i++) {
|
||||||
|
SceneRoundTripSwitch &s = sceneRoundTripSwitches[i];
|
||||||
|
if (!WeakSourceValid(s.scene1) ||
|
||||||
|
(!s.usePreviousScene &&
|
||||||
|
!WeakSourceValid(s.scene2)) ||
|
||||||
|
!WeakSourceValid(s.transition))
|
||||||
|
sceneRoundTripSwitches.erase(
|
||||||
|
sceneRoundTripSwitches.begin() + i--);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!WeakSourceValid(autoStopScene)) {
|
||||||
|
autoStopScene = nullptr;
|
||||||
|
autoStopEnable = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (size_t i = 0; i < sceneTransitions.size(); i++) {
|
||||||
|
SceneTransition &s = sceneTransitions[i];
|
||||||
|
if (!WeakSourceValid(s.scene1) ||
|
||||||
|
!WeakSourceValid(s.scene2) ||
|
||||||
|
!WeakSourceValid(s.transition))
|
||||||
|
sceneTransitions.erase(
|
||||||
|
sceneTransitions.begin() + i--);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (size_t i = 0; i < defaultSceneTransitions.size(); i++) {
|
||||||
|
DefaultSceneTransition &s = defaultSceneTransitions[i];
|
||||||
|
if (!WeakSourceValid(s.scene) ||
|
||||||
|
!WeakSourceValid(s.transition))
|
||||||
|
defaultSceneTransitions.erase(
|
||||||
|
defaultSceneTransitions.begin() + i--);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (size_t i = 0; i < executableSwitches.size(); i++) {
|
||||||
|
ExecutableSceneSwitch &s = executableSwitches[i];
|
||||||
|
if (!WeakSourceValid(s.mScene) ||
|
||||||
|
!WeakSourceValid(s.mTransition))
|
||||||
|
executableSwitches.erase(
|
||||||
|
executableSwitches.begin() + i--);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (size_t i = 0; i < fileSwitches.size(); i++) {
|
||||||
|
FileSwitch &s = fileSwitches[i];
|
||||||
|
if (!WeakSourceValid(s.scene) ||
|
||||||
|
!WeakSourceValid(s.transition))
|
||||||
|
fileSwitches.erase(fileSwitches.begin() + i--);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (size_t i = 0; i < timeSwitches.size(); i++) {
|
||||||
|
TimeSwitch &s = timeSwitches[i];
|
||||||
|
if ((!s.usePreviousScene &&
|
||||||
|
!WeakSourceValid(s.scene)) ||
|
||||||
|
!WeakSourceValid(s.transition))
|
||||||
|
timeSwitches.erase(timeSwitches.begin() + i--);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!idleData.usePreviousScene &&
|
||||||
|
!WeakSourceValid(idleData.scene) ||
|
||||||
|
!WeakSourceValid(idleData.transition)) {
|
||||||
|
idleData.idleEnable = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (size_t i = 0; i < mediaSwitches.size(); i++) {
|
||||||
|
MediaSwitch &s = mediaSwitches[i];
|
||||||
|
if ((!s.usePreviousScene &&
|
||||||
|
!WeakSourceValid(s.scene)) ||
|
||||||
|
!WeakSourceValid(s.source) ||
|
||||||
|
!WeakSourceValid(s.transition))
|
||||||
|
mediaSwitches.erase(mediaSwitches.begin() +
|
||||||
|
i--);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
inline ~SwitcherData() { Stop(); }
|
||||||
|
};
|
||||||
|
|
||||||
|
extern SwitcherData *switcher;
|
||||||
|
class SwitcherThread : public QThread {
|
||||||
|
public:
|
||||||
|
explicit SwitcherThread(){};
|
||||||
|
void run() { switcher->Thread(); };
|
||||||
|
};
|
||||||
252
src/headers/utility.hpp
Normal file
252
src/headers/utility.hpp
Normal file
@@ -0,0 +1,252 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <obs.hpp>
|
||||||
|
#include <QString>
|
||||||
|
#include <obs-frontend-api.h>
|
||||||
|
#include "switcher-data-structs.hpp"
|
||||||
|
|
||||||
|
static inline bool WeakSourceValid(obs_weak_source_t *ws)
|
||||||
|
{
|
||||||
|
obs_source_t *source = obs_weak_source_get_source(ws);
|
||||||
|
if (source)
|
||||||
|
obs_source_release(source);
|
||||||
|
return !!source;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline QString MakeSwitchName(const QString &scene, const QString &value,
|
||||||
|
const QString &transition, bool fullscreen,
|
||||||
|
bool focus)
|
||||||
|
{
|
||||||
|
QString name = QStringLiteral("[") + scene + QStringLiteral(", ") +
|
||||||
|
transition + QStringLiteral("]: ") + value;
|
||||||
|
|
||||||
|
if (fullscreen || focus) {
|
||||||
|
name += QStringLiteral(" (only if");
|
||||||
|
|
||||||
|
if (fullscreen)
|
||||||
|
name += QStringLiteral(" fullscreen");
|
||||||
|
if (fullscreen && focus)
|
||||||
|
name += QStringLiteral(" and");
|
||||||
|
if (focus)
|
||||||
|
name += QStringLiteral(" focused");
|
||||||
|
|
||||||
|
name += QStringLiteral(")");
|
||||||
|
}
|
||||||
|
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline QString MakeSwitchNameExecutable(const QString &scene,
|
||||||
|
const QString &value,
|
||||||
|
const QString &transition,
|
||||||
|
bool inFocus)
|
||||||
|
{
|
||||||
|
if (!inFocus)
|
||||||
|
return QStringLiteral("[") + scene + QStringLiteral(", ") +
|
||||||
|
transition + QStringLiteral("]: ") + value;
|
||||||
|
return QStringLiteral("[") + scene + QStringLiteral(", ") + transition +
|
||||||
|
QStringLiteral("]: ") + value +
|
||||||
|
QStringLiteral(" (only if focused)");
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline QString MakeScreenRegionSwitchName(const QString &scene,
|
||||||
|
const QString &transition,
|
||||||
|
int minX, int minY, int maxX,
|
||||||
|
int maxY)
|
||||||
|
{
|
||||||
|
return QStringLiteral("[") + scene + QStringLiteral(", ") + transition +
|
||||||
|
QStringLiteral("]: ") + QString::number(minX) +
|
||||||
|
QStringLiteral(", ") + QString::number(minY) +
|
||||||
|
QStringLiteral(" x ") + QString::number(maxX) +
|
||||||
|
QStringLiteral(", ") + QString::number(maxY);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline QString MakeSceneRoundTripSwitchName(const QString &scene1,
|
||||||
|
const QString &scene2,
|
||||||
|
const QString &transition,
|
||||||
|
double delay)
|
||||||
|
{
|
||||||
|
return scene1 + QStringLiteral(" -> wait for ") +
|
||||||
|
QString::number(delay) + QStringLiteral(" seconds -> ") +
|
||||||
|
scene2 + QStringLiteral(" (using ") + transition +
|
||||||
|
QStringLiteral(" transition)");
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline QString MakeSceneTransitionName(const QString &scene1,
|
||||||
|
const QString &scene2,
|
||||||
|
const QString &transition)
|
||||||
|
{
|
||||||
|
return scene1 + QStringLiteral(" --- ") + transition +
|
||||||
|
QStringLiteral(" --> ") + scene2;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline QString MakeDefaultSceneTransitionName(const QString &scene,
|
||||||
|
const QString &transition)
|
||||||
|
{
|
||||||
|
return scene + QStringLiteral(" --> ") + transition;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline QString MakeRandomSwitchName(const QString &scene,
|
||||||
|
const QString &transition,
|
||||||
|
double &delay)
|
||||||
|
{
|
||||||
|
return QStringLiteral("[") + scene + QStringLiteral(", ") + transition +
|
||||||
|
QStringLiteral("]: ") + QString::number(delay) +
|
||||||
|
QStringLiteral(" seconds");
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline QString MakeFileSwitchName(const QString &scene,
|
||||||
|
const QString &transition,
|
||||||
|
const QString &fileName,
|
||||||
|
const QString &text, bool useRegex,
|
||||||
|
bool useTime)
|
||||||
|
{
|
||||||
|
QString switchName = QStringLiteral("Switch to ") + scene +
|
||||||
|
QStringLiteral(" using ") + transition +
|
||||||
|
QStringLiteral(" if ") + fileName;
|
||||||
|
if (useTime)
|
||||||
|
switchName += QStringLiteral(" was modified and");
|
||||||
|
switchName += QStringLiteral(" contains");
|
||||||
|
if (useRegex)
|
||||||
|
switchName += QStringLiteral(" (RegEx): \n\"");
|
||||||
|
else
|
||||||
|
switchName += QStringLiteral(": \n\"");
|
||||||
|
if (text.length() > 30)
|
||||||
|
switchName += text.left(27) + QStringLiteral("...\"");
|
||||||
|
else
|
||||||
|
switchName += text + QStringLiteral("\"");
|
||||||
|
|
||||||
|
return switchName;
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
TIME_RESTRICTION_NONE,
|
||||||
|
TIME_RESTRICTION_SHORTER,
|
||||||
|
TIME_RESTRICTION_LONGER,
|
||||||
|
TIME_RESTRICTION_REMAINING_SHORTER,
|
||||||
|
TIME_RESTRICTION_REMAINING_LONGER,
|
||||||
|
} time_restriction;
|
||||||
|
|
||||||
|
static inline QString
|
||||||
|
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 +
|
||||||
|
QStringLiteral(" using ") + transition +
|
||||||
|
QStringLiteral(" if ") + source +
|
||||||
|
QStringLiteral(" state is ");
|
||||||
|
if (state == OBS_MEDIA_STATE_NONE) {
|
||||||
|
switchName += QStringLiteral("none");
|
||||||
|
} else if (state == OBS_MEDIA_STATE_PLAYING) {
|
||||||
|
switchName += QStringLiteral("playing");
|
||||||
|
} else if (state == OBS_MEDIA_STATE_OPENING) {
|
||||||
|
switchName += QStringLiteral("opening");
|
||||||
|
} else if (state == OBS_MEDIA_STATE_BUFFERING) {
|
||||||
|
switchName += QStringLiteral("buffering");
|
||||||
|
} else if (state == OBS_MEDIA_STATE_PAUSED) {
|
||||||
|
switchName += QStringLiteral("paused");
|
||||||
|
} else if (state == OBS_MEDIA_STATE_STOPPED) {
|
||||||
|
switchName += QStringLiteral("stopped");
|
||||||
|
} else if (state == OBS_MEDIA_STATE_ENDED) {
|
||||||
|
switchName += QStringLiteral("ended");
|
||||||
|
} else if (state == OBS_MEDIA_STATE_ERROR) {
|
||||||
|
switchName += QStringLiteral("error");
|
||||||
|
}
|
||||||
|
if (restriction == TIME_RESTRICTION_SHORTER) {
|
||||||
|
switchName += QStringLiteral(" and time shorter than ");
|
||||||
|
} else if (restriction == TIME_RESTRICTION_LONGER) {
|
||||||
|
switchName += QStringLiteral(" and time longer than ");
|
||||||
|
} else if (restriction == TIME_RESTRICTION_REMAINING_SHORTER) {
|
||||||
|
switchName +=
|
||||||
|
QStringLiteral(" and time remaining shorter than ");
|
||||||
|
} else if (restriction == TIME_RESTRICTION_REMAINING_LONGER) {
|
||||||
|
switchName +=
|
||||||
|
QStringLiteral(" and time remaining longer than ");
|
||||||
|
}
|
||||||
|
if (restriction != TIME_RESTRICTION_NONE) {
|
||||||
|
switchName += std::to_string(time).c_str();
|
||||||
|
switchName += QStringLiteral(" ms");
|
||||||
|
}
|
||||||
|
return switchName;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline QString MakeTimeSwitchName(const QString &scene,
|
||||||
|
const QString &transition, QTime &time)
|
||||||
|
{
|
||||||
|
QString switchName = QStringLiteral("At ") + time.toString() +
|
||||||
|
QStringLiteral(" switch to ") + scene +
|
||||||
|
QStringLiteral(" using ") + transition;
|
||||||
|
return switchName;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline std::string GetWeakSourceName(obs_weak_source_t *weak_source)
|
||||||
|
{
|
||||||
|
std::string name;
|
||||||
|
|
||||||
|
obs_source_t *source = obs_weak_source_get_source(weak_source);
|
||||||
|
if (source) {
|
||||||
|
name = obs_source_get_name(source);
|
||||||
|
obs_source_release(source);
|
||||||
|
}
|
||||||
|
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline OBSWeakSource GetWeakSourceByName(const char *name)
|
||||||
|
{
|
||||||
|
OBSWeakSource weak;
|
||||||
|
obs_source_t *source = obs_get_source_by_name(name);
|
||||||
|
if (source) {
|
||||||
|
weak = obs_source_get_weak_source(source);
|
||||||
|
obs_weak_source_release(weak);
|
||||||
|
obs_source_release(source);
|
||||||
|
}
|
||||||
|
|
||||||
|
return weak;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline OBSWeakSource GetWeakSourceByQString(const QString &name)
|
||||||
|
{
|
||||||
|
return GetWeakSourceByName(name.toUtf8().constData());
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline OBSWeakSource GetWeakTransitionByName(const char *transitionName)
|
||||||
|
{
|
||||||
|
OBSWeakSource weak;
|
||||||
|
obs_source_t *source = nullptr;
|
||||||
|
|
||||||
|
if (strcmp(transitionName, "Default") == 0) {
|
||||||
|
source = obs_frontend_get_current_transition();
|
||||||
|
weak = obs_source_get_weak_source(source);
|
||||||
|
obs_source_release(source);
|
||||||
|
obs_weak_source_release(weak);
|
||||||
|
return weak;
|
||||||
|
}
|
||||||
|
|
||||||
|
obs_frontend_source_list *transitions = new obs_frontend_source_list();
|
||||||
|
obs_frontend_get_transitions(transitions);
|
||||||
|
bool match = false;
|
||||||
|
|
||||||
|
for (size_t i = 0; i < transitions->sources.num; i++) {
|
||||||
|
const char *name =
|
||||||
|
obs_source_get_name(transitions->sources.array[i]);
|
||||||
|
if (strcmp(transitionName, name) == 0) {
|
||||||
|
match = true;
|
||||||
|
source = transitions->sources.array[i];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (match) {
|
||||||
|
weak = obs_source_get_weak_source(source);
|
||||||
|
obs_weak_source_release(weak);
|
||||||
|
}
|
||||||
|
obs_frontend_source_list_free(transitions);
|
||||||
|
|
||||||
|
return weak;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline OBSWeakSource GetWeakTransitionByQString(const QString &name)
|
||||||
|
{
|
||||||
|
return GetWeakTransitionByName(name.toUtf8().constData());
|
||||||
|
}
|
||||||
138
src/hotkey.cpp
Normal file
138
src/hotkey.cpp
Normal file
@@ -0,0 +1,138 @@
|
|||||||
|
#include <obs-module.h>
|
||||||
|
#include "headers/advanced-scene-switcher.hpp"
|
||||||
|
|
||||||
|
void startHotkeyFunc(void *data, obs_hotkey_id id, obs_hotkey_t *hotkey,
|
||||||
|
bool pressed)
|
||||||
|
{
|
||||||
|
UNUSED_PARAMETER(data);
|
||||||
|
UNUSED_PARAMETER(hotkey);
|
||||||
|
|
||||||
|
if (pressed) {
|
||||||
|
if (!(switcher->th && switcher->th->isRunning()))
|
||||||
|
switcher->Start();
|
||||||
|
}
|
||||||
|
|
||||||
|
obs_data_array *hotkeyData = obs_hotkey_save(id);
|
||||||
|
|
||||||
|
if (hotkeyData != NULL) {
|
||||||
|
char *path = obs_module_config_path("");
|
||||||
|
std::ofstream file;
|
||||||
|
file.open(std::string(path).append(START_HOTKEY_PATH),
|
||||||
|
std::ofstream::trunc);
|
||||||
|
if (file.is_open()) {
|
||||||
|
size_t num = obs_data_array_count(hotkeyData);
|
||||||
|
for (size_t i = 0; i < num; i++) {
|
||||||
|
obs_data_t *data =
|
||||||
|
obs_data_array_item(hotkeyData, i);
|
||||||
|
std::string temp = obs_data_get_json(data);
|
||||||
|
obs_data_release(data);
|
||||||
|
file << temp;
|
||||||
|
}
|
||||||
|
file.close();
|
||||||
|
}
|
||||||
|
bfree(path);
|
||||||
|
}
|
||||||
|
obs_data_array_release(hotkeyData);
|
||||||
|
}
|
||||||
|
|
||||||
|
void stopHotkeyFunc(void *data, obs_hotkey_id id, obs_hotkey_t *hotkey,
|
||||||
|
bool pressed)
|
||||||
|
{
|
||||||
|
UNUSED_PARAMETER(data);
|
||||||
|
UNUSED_PARAMETER(hotkey);
|
||||||
|
|
||||||
|
if (pressed) {
|
||||||
|
if (switcher->th && switcher->th->isRunning())
|
||||||
|
switcher->Stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
obs_data_array *hotkeyData = obs_hotkey_save(id);
|
||||||
|
|
||||||
|
if (hotkeyData != NULL) {
|
||||||
|
char *path = obs_module_config_path("");
|
||||||
|
std::ofstream file;
|
||||||
|
file.open(std::string(path).append(STOP_HOTKEY_PATH),
|
||||||
|
std::ofstream::trunc);
|
||||||
|
if (file.is_open()) {
|
||||||
|
size_t num = obs_data_array_count(hotkeyData);
|
||||||
|
for (size_t i = 0; i < num; i++) {
|
||||||
|
obs_data_t *data =
|
||||||
|
obs_data_array_item(hotkeyData, i);
|
||||||
|
std::string temp = obs_data_get_json(data);
|
||||||
|
obs_data_release(data);
|
||||||
|
file << temp;
|
||||||
|
}
|
||||||
|
file.close();
|
||||||
|
}
|
||||||
|
bfree(path);
|
||||||
|
}
|
||||||
|
obs_data_array_release(hotkeyData);
|
||||||
|
}
|
||||||
|
|
||||||
|
void startStopToggleHotkeyFunc(void *data, obs_hotkey_id id,
|
||||||
|
obs_hotkey_t *hotkey, bool pressed)
|
||||||
|
{
|
||||||
|
UNUSED_PARAMETER(data);
|
||||||
|
UNUSED_PARAMETER(hotkey);
|
||||||
|
|
||||||
|
if (pressed) {
|
||||||
|
if (switcher->th && switcher->th->isRunning())
|
||||||
|
switcher->Stop();
|
||||||
|
else
|
||||||
|
switcher->Start();
|
||||||
|
}
|
||||||
|
|
||||||
|
obs_data_array *hotkeyData = obs_hotkey_save(id);
|
||||||
|
|
||||||
|
if (hotkeyData != NULL) {
|
||||||
|
char *path = obs_module_config_path("");
|
||||||
|
std::ofstream file;
|
||||||
|
file.open(std::string(path).append(TOGGLE_HOTKEY_PATH),
|
||||||
|
std::ofstream::trunc);
|
||||||
|
if (file.is_open()) {
|
||||||
|
size_t num = obs_data_array_count(hotkeyData);
|
||||||
|
for (size_t i = 0; i < num; i++) {
|
||||||
|
obs_data_t *data =
|
||||||
|
obs_data_array_item(hotkeyData, i);
|
||||||
|
std::string temp = obs_data_get_json(data);
|
||||||
|
obs_data_release(data);
|
||||||
|
file << temp;
|
||||||
|
}
|
||||||
|
file.close();
|
||||||
|
}
|
||||||
|
bfree(path);
|
||||||
|
}
|
||||||
|
obs_data_array_release(hotkeyData);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string loadConfigFile(std::string filename)
|
||||||
|
{
|
||||||
|
std::ifstream settingsFile;
|
||||||
|
char *path = obs_module_config_path("");
|
||||||
|
std::string value;
|
||||||
|
|
||||||
|
settingsFile.open(std::string(path).append(filename));
|
||||||
|
if (settingsFile.is_open()) {
|
||||||
|
settingsFile.seekg(0, std::ios::end);
|
||||||
|
value.reserve(settingsFile.tellg());
|
||||||
|
settingsFile.seekg(0, std::ios::beg);
|
||||||
|
value.assign((std::istreambuf_iterator<char>(settingsFile)),
|
||||||
|
std::istreambuf_iterator<char>());
|
||||||
|
settingsFile.close();
|
||||||
|
}
|
||||||
|
bfree(path);
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
void loadKeybinding(obs_hotkey_id hotkeyId, std::string path)
|
||||||
|
{
|
||||||
|
std::string bindings = loadConfigFile(path);
|
||||||
|
if (!bindings.empty()) {
|
||||||
|
obs_data_array_t *hotkeyData = obs_data_array_create();
|
||||||
|
obs_data_t *data = obs_data_create_from_json(bindings.c_str());
|
||||||
|
obs_data_array_insert(hotkeyData, 0, data);
|
||||||
|
obs_data_release(data);
|
||||||
|
obs_hotkey_load(hotkeyId, hotkeyData);
|
||||||
|
obs_data_array_release(hotkeyData);
|
||||||
|
}
|
||||||
|
}
|
||||||
297
src/idle-switch.cpp
Normal file
297
src/idle-switch.cpp
Normal file
@@ -0,0 +1,297 @@
|
|||||||
|
#include "headers/advanced-scene-switcher.hpp"
|
||||||
|
|
||||||
|
void SwitcherData::checkIdleSwitch(bool &match, OBSWeakSource &scene,
|
||||||
|
OBSWeakSource &transition)
|
||||||
|
{
|
||||||
|
if (!idleData.idleEnable)
|
||||||
|
return;
|
||||||
|
|
||||||
|
std::string title;
|
||||||
|
bool ignoreIdle = false;
|
||||||
|
//lock.unlock();
|
||||||
|
GetCurrentWindowTitle(title);
|
||||||
|
//lock.lock();
|
||||||
|
|
||||||
|
for (std::string &window : ignoreIdleWindows) {
|
||||||
|
if (window == title) {
|
||||||
|
ignoreIdle = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ignoreIdle) {
|
||||||
|
for (std::string &window : ignoreIdleWindows) {
|
||||||
|
try {
|
||||||
|
bool matches = std::regex_match(
|
||||||
|
title, std::regex(window));
|
||||||
|
if (matches) {
|
||||||
|
ignoreIdle = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} catch (const std::regex_error &) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ignoreIdle && secondsSinceLastInput() > idleData.time) {
|
||||||
|
if (idleData.alreadySwitched)
|
||||||
|
return;
|
||||||
|
scene = (idleData.usePreviousScene) ? previousScene
|
||||||
|
: idleData.scene;
|
||||||
|
transition = idleData.transition;
|
||||||
|
match = true;
|
||||||
|
idleData.alreadySwitched = true;
|
||||||
|
|
||||||
|
if (verbose)
|
||||||
|
blog(LOG_INFO, "Advanced Scene Switcher idle match");
|
||||||
|
} else
|
||||||
|
idleData.alreadySwitched = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SceneSwitcher::on_idleCheckBox_stateChanged(int state)
|
||||||
|
{
|
||||||
|
if (loading)
|
||||||
|
return;
|
||||||
|
|
||||||
|
std::lock_guard<std::mutex> lock(switcher->m);
|
||||||
|
if (!state) {
|
||||||
|
ui->idleScenes->setDisabled(true);
|
||||||
|
ui->idleSpinBox->setDisabled(true);
|
||||||
|
ui->idleTransitions->setDisabled(true);
|
||||||
|
|
||||||
|
switcher->idleData.idleEnable = false;
|
||||||
|
} else {
|
||||||
|
ui->idleScenes->setDisabled(false);
|
||||||
|
ui->idleSpinBox->setDisabled(false);
|
||||||
|
ui->idleTransitions->setDisabled(false);
|
||||||
|
|
||||||
|
switcher->idleData.idleEnable = true;
|
||||||
|
|
||||||
|
UpdateIdleDataTransition(ui->idleTransitions->currentText());
|
||||||
|
UpdateIdleDataScene(ui->idleScenes->currentText());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SceneSwitcher::UpdateIdleDataTransition(const QString &name)
|
||||||
|
{
|
||||||
|
obs_weak_source_t *transition = GetWeakTransitionByQString(name);
|
||||||
|
switcher->idleData.transition = transition;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SceneSwitcher::UpdateIdleDataScene(const QString &name)
|
||||||
|
{
|
||||||
|
switcher->idleData.usePreviousScene = (name == PREVIOUS_SCENE_NAME);
|
||||||
|
obs_source_t *scene = obs_get_source_by_name(name.toUtf8().constData());
|
||||||
|
obs_weak_source_t *ws = obs_source_get_weak_source(scene);
|
||||||
|
|
||||||
|
switcher->idleData.scene = ws;
|
||||||
|
|
||||||
|
obs_weak_source_release(ws);
|
||||||
|
obs_source_release(scene);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SceneSwitcher::on_idleTransitions_currentTextChanged(const QString &text)
|
||||||
|
{
|
||||||
|
if (loading)
|
||||||
|
return;
|
||||||
|
|
||||||
|
std::lock_guard<std::mutex> lock(switcher->m);
|
||||||
|
UpdateIdleDataTransition(text);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SceneSwitcher::on_idleScenes_currentTextChanged(const QString &text)
|
||||||
|
{
|
||||||
|
if (loading)
|
||||||
|
return;
|
||||||
|
|
||||||
|
std::lock_guard<std::mutex> lock(switcher->m);
|
||||||
|
UpdateIdleDataScene(text);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SceneSwitcher::on_idleSpinBox_valueChanged(int i)
|
||||||
|
{
|
||||||
|
if (loading)
|
||||||
|
return;
|
||||||
|
std::lock_guard<std::mutex> lock(switcher->m);
|
||||||
|
switcher->idleData.time = i;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SceneSwitcher::on_ignoreIdleWindows_currentRowChanged(int idx)
|
||||||
|
{
|
||||||
|
if (loading)
|
||||||
|
return;
|
||||||
|
if (idx == -1)
|
||||||
|
return;
|
||||||
|
|
||||||
|
QListWidgetItem *item = ui->ignoreIdleWindows->item(idx);
|
||||||
|
|
||||||
|
QString window = item->data(Qt::UserRole).toString();
|
||||||
|
|
||||||
|
std::lock_guard<std::mutex> lock(switcher->m);
|
||||||
|
for (auto &w : switcher->ignoreIdleWindows) {
|
||||||
|
if (window.compare(w.c_str()) == 0) {
|
||||||
|
ui->ignoreIdleWindowsWindows->setCurrentText(w.c_str());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SceneSwitcher::on_ignoreIdleAdd_clicked()
|
||||||
|
{
|
||||||
|
QString windowName = ui->ignoreIdleWindowsWindows->currentText();
|
||||||
|
|
||||||
|
if (windowName.isEmpty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
QVariant v = QVariant::fromValue(windowName);
|
||||||
|
|
||||||
|
QList<QListWidgetItem *> items =
|
||||||
|
ui->ignoreIdleWindows->findItems(windowName, Qt::MatchExactly);
|
||||||
|
|
||||||
|
if (items.size() == 0) {
|
||||||
|
QListWidgetItem *item =
|
||||||
|
new QListWidgetItem(windowName, ui->ignoreIdleWindows);
|
||||||
|
item->setData(Qt::UserRole, v);
|
||||||
|
|
||||||
|
std::lock_guard<std::mutex> lock(switcher->m);
|
||||||
|
switcher->ignoreIdleWindows.emplace_back(
|
||||||
|
windowName.toUtf8().constData());
|
||||||
|
ui->ignoreIdleWindows->sortItems();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SceneSwitcher::on_ignoreIdleRemove_clicked()
|
||||||
|
{
|
||||||
|
QListWidgetItem *item = ui->ignoreIdleWindows->currentItem();
|
||||||
|
if (!item)
|
||||||
|
return;
|
||||||
|
|
||||||
|
QString windowName = item->data(Qt::UserRole).toString();
|
||||||
|
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(switcher->m);
|
||||||
|
auto &windows = switcher->ignoreIdleWindows;
|
||||||
|
|
||||||
|
for (auto it = windows.begin(); it != windows.end(); ++it) {
|
||||||
|
auto &s = *it;
|
||||||
|
|
||||||
|
if (s == windowName.toUtf8().constData()) {
|
||||||
|
windows.erase(it);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
delete item;
|
||||||
|
}
|
||||||
|
|
||||||
|
int SceneSwitcher::IgnoreIdleWindowsFindByData(const QString &window)
|
||||||
|
{
|
||||||
|
int count = ui->ignoreIdleWindows->count();
|
||||||
|
int idx = -1;
|
||||||
|
|
||||||
|
for (int i = 0; i < count; i++) {
|
||||||
|
QListWidgetItem *item = ui->ignoreIdleWindows->item(i);
|
||||||
|
QString itemRegion = item->data(Qt::UserRole).toString();
|
||||||
|
|
||||||
|
if (itemRegion == window) {
|
||||||
|
idx = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return idx;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SwitcherData::saveIdleSwitches(obs_data_t *obj)
|
||||||
|
{
|
||||||
|
obs_data_array_t *ignoreIdleWindowsArray = obs_data_array_create();
|
||||||
|
for (std::string &window : switcher->ignoreIdleWindows) {
|
||||||
|
obs_data_t *array_obj = obs_data_create();
|
||||||
|
obs_data_set_string(array_obj, "window", window.c_str());
|
||||||
|
obs_data_array_push_back(ignoreIdleWindowsArray, array_obj);
|
||||||
|
obs_data_release(array_obj);
|
||||||
|
}
|
||||||
|
obs_data_set_array(obj, "ignoreIdleWindows", ignoreIdleWindowsArray);
|
||||||
|
obs_data_array_release(ignoreIdleWindowsArray);
|
||||||
|
|
||||||
|
std::string idleSceneName = GetWeakSourceName(switcher->idleData.scene);
|
||||||
|
std::string idleTransitionName =
|
||||||
|
GetWeakSourceName(switcher->idleData.transition);
|
||||||
|
obs_data_set_bool(obj, "idleEnable", switcher->idleData.idleEnable);
|
||||||
|
obs_data_set_string(obj, "idleSceneName",
|
||||||
|
switcher->idleData.usePreviousScene
|
||||||
|
? PREVIOUS_SCENE_NAME
|
||||||
|
: idleSceneName.c_str());
|
||||||
|
obs_data_set_string(obj, "idleTransitionName",
|
||||||
|
idleTransitionName.c_str());
|
||||||
|
obs_data_set_int(obj, "idleTime", switcher->idleData.time);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SwitcherData::loadIdleSwitches(obs_data_t *obj)
|
||||||
|
{
|
||||||
|
switcher->ignoreIdleWindows.clear();
|
||||||
|
|
||||||
|
obs_data_array_t *ignoreIdleWindowsArray =
|
||||||
|
obs_data_get_array(obj, "ignoreIdleWindows");
|
||||||
|
size_t count = obs_data_array_count(ignoreIdleWindowsArray);
|
||||||
|
|
||||||
|
for (size_t i = 0; i < count; i++) {
|
||||||
|
obs_data_t *array_obj =
|
||||||
|
obs_data_array_item(ignoreIdleWindowsArray, i);
|
||||||
|
|
||||||
|
const char *window = obs_data_get_string(array_obj, "window");
|
||||||
|
|
||||||
|
switcher->ignoreIdleWindows.emplace_back(window);
|
||||||
|
|
||||||
|
obs_data_release(array_obj);
|
||||||
|
}
|
||||||
|
obs_data_array_release(ignoreIdleWindowsArray);
|
||||||
|
|
||||||
|
std::string idleSceneName = obs_data_get_string(obj, "idleSceneName");
|
||||||
|
std::string idleTransitionName =
|
||||||
|
obs_data_get_string(obj, "idleTransitionName");
|
||||||
|
switcher->idleData.scene = GetWeakSourceByName(idleSceneName.c_str());
|
||||||
|
switcher->idleData.transition =
|
||||||
|
GetWeakTransitionByName(idleTransitionName.c_str());
|
||||||
|
obs_data_set_default_bool(obj, "idleEnable", false);
|
||||||
|
switcher->idleData.idleEnable = obs_data_get_bool(obj, "idleEnable");
|
||||||
|
obs_data_set_default_int(obj, "idleTime", DEFAULT_IDLE_TIME);
|
||||||
|
switcher->idleData.time = obs_data_get_int(obj, "idleTime");
|
||||||
|
switcher->idleData.usePreviousScene =
|
||||||
|
(idleSceneName == PREVIOUS_SCENE_NAME);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SceneSwitcher::setupIdleTab()
|
||||||
|
{
|
||||||
|
populateSceneSelection(ui->idleScenes, true);
|
||||||
|
populateTransitionSelection(ui->idleTransitions);
|
||||||
|
populateWindowSelection(ui->ignoreIdleWindowsWindows);
|
||||||
|
|
||||||
|
for (auto &window : switcher->ignoreIdleWindows) {
|
||||||
|
QString text = QString::fromStdString(window);
|
||||||
|
|
||||||
|
QListWidgetItem *item =
|
||||||
|
new QListWidgetItem(text, ui->ignoreIdleWindows);
|
||||||
|
item->setData(Qt::UserRole, text);
|
||||||
|
}
|
||||||
|
|
||||||
|
ui->idleCheckBox->setChecked(switcher->idleData.idleEnable);
|
||||||
|
ui->idleScenes->setCurrentText(
|
||||||
|
switcher->idleData.usePreviousScene
|
||||||
|
? PREVIOUS_SCENE_NAME
|
||||||
|
: GetWeakSourceName(switcher->idleData.scene).c_str());
|
||||||
|
ui->idleTransitions->setCurrentText(
|
||||||
|
GetWeakSourceName(switcher->idleData.transition).c_str());
|
||||||
|
ui->idleSpinBox->setValue(switcher->idleData.time);
|
||||||
|
|
||||||
|
if (ui->idleCheckBox->checkState()) {
|
||||||
|
ui->idleScenes->setDisabled(false);
|
||||||
|
ui->idleSpinBox->setDisabled(false);
|
||||||
|
ui->idleTransitions->setDisabled(false);
|
||||||
|
} else {
|
||||||
|
ui->idleScenes->setDisabled(true);
|
||||||
|
ui->idleSpinBox->setDisabled(true);
|
||||||
|
ui->idleTransitions->setDisabled(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
346
src/linux/advanced-scene-switcher-nix.cpp
Normal file
346
src/linux/advanced-scene-switcher-nix.cpp
Normal file
@@ -0,0 +1,346 @@
|
|||||||
|
#include <X11/Xlib.h>
|
||||||
|
#include <X11/Xatom.h>
|
||||||
|
#include <X11/Xutil.h>
|
||||||
|
#include <X11/extensions/scrnsaver.h>
|
||||||
|
#undef Bool
|
||||||
|
#undef CursorShape
|
||||||
|
#undef Expose
|
||||||
|
#undef KeyPress
|
||||||
|
#undef KeyRelease
|
||||||
|
#undef FocusIn
|
||||||
|
#undef FocusOut
|
||||||
|
#undef FontChange
|
||||||
|
#undef None
|
||||||
|
#undef Status
|
||||||
|
#undef Unsorted
|
||||||
|
#include <util/platform.h>
|
||||||
|
#include "../headers/advanced-scene-switcher.hpp"
|
||||||
|
|
||||||
|
static Display *xdisplay = 0;
|
||||||
|
|
||||||
|
Display *disp()
|
||||||
|
{
|
||||||
|
if (!xdisplay)
|
||||||
|
xdisplay = XOpenDisplay(NULL);
|
||||||
|
|
||||||
|
return xdisplay;
|
||||||
|
}
|
||||||
|
|
||||||
|
void cleanupDisplay()
|
||||||
|
{
|
||||||
|
if (!xdisplay)
|
||||||
|
return;
|
||||||
|
|
||||||
|
XCloseDisplay(xdisplay);
|
||||||
|
xdisplay = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool ewmhIsSupported()
|
||||||
|
{
|
||||||
|
Display *display = disp();
|
||||||
|
Atom netSupportingWmCheck =
|
||||||
|
XInternAtom(display, "_NET_SUPPORTING_WM_CHECK", true);
|
||||||
|
Atom actualType;
|
||||||
|
int format = 0;
|
||||||
|
unsigned long num = 0, bytes = 0;
|
||||||
|
unsigned char *data = NULL;
|
||||||
|
Window ewmh_window = 0;
|
||||||
|
|
||||||
|
int status = XGetWindowProperty(display, DefaultRootWindow(display),
|
||||||
|
netSupportingWmCheck, 0L, 1L, false,
|
||||||
|
XA_WINDOW, &actualType, &format, &num,
|
||||||
|
&bytes, &data);
|
||||||
|
|
||||||
|
if (status == Success) {
|
||||||
|
if (num > 0) {
|
||||||
|
ewmh_window = ((Window *)data)[0];
|
||||||
|
}
|
||||||
|
if (data) {
|
||||||
|
XFree(data);
|
||||||
|
data = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ewmh_window) {
|
||||||
|
status = XGetWindowProperty(display, ewmh_window,
|
||||||
|
netSupportingWmCheck, 0L, 1L, false,
|
||||||
|
XA_WINDOW, &actualType, &format,
|
||||||
|
&num, &bytes, &data);
|
||||||
|
if (status != Success || num == 0 ||
|
||||||
|
ewmh_window != ((Window *)data)[0]) {
|
||||||
|
ewmh_window = 0;
|
||||||
|
}
|
||||||
|
if (status == Success && data) {
|
||||||
|
XFree(data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ewmh_window != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static QStringList getStates(Window window)
|
||||||
|
{
|
||||||
|
QStringList states;
|
||||||
|
|
||||||
|
if (!ewmhIsSupported())
|
||||||
|
return states;
|
||||||
|
|
||||||
|
Atom wmState = XInternAtom(disp(), "_NET_WM_STATE", true), type;
|
||||||
|
int format;
|
||||||
|
unsigned long num, bytes;
|
||||||
|
unsigned char *data;
|
||||||
|
|
||||||
|
int status = XGetWindowProperty(disp(), window, wmState, 0, ~0L, false,
|
||||||
|
AnyPropertyType, &type, &format, &num,
|
||||||
|
&bytes, &data);
|
||||||
|
|
||||||
|
if (status == Success)
|
||||||
|
for (unsigned long i = 0; i < num; i++)
|
||||||
|
states.append(QString(
|
||||||
|
XGetAtomName(disp(), ((Atom *)data)[i])));
|
||||||
|
|
||||||
|
return states;
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::vector<Window> getTopLevelWindows()
|
||||||
|
{
|
||||||
|
std::vector<Window> res;
|
||||||
|
|
||||||
|
res.resize(0);
|
||||||
|
|
||||||
|
if (!ewmhIsSupported()) {
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
Atom netClList = XInternAtom(disp(), "_NET_CLIENT_LIST", true);
|
||||||
|
Atom actualType;
|
||||||
|
int format;
|
||||||
|
unsigned long num, bytes;
|
||||||
|
Window *data = 0;
|
||||||
|
|
||||||
|
for (int i = 0; i < ScreenCount(disp()); ++i) {
|
||||||
|
Window rootWin = RootWindow(disp(), i);
|
||||||
|
|
||||||
|
int status = XGetWindowProperty(disp(), rootWin, netClList, 0L,
|
||||||
|
~0L, false, AnyPropertyType,
|
||||||
|
&actualType, &format, &num,
|
||||||
|
&bytes, (uint8_t **)&data);
|
||||||
|
|
||||||
|
if (status != Success) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (unsigned long i = 0; i < num; ++i)
|
||||||
|
res.emplace_back(data[i]);
|
||||||
|
|
||||||
|
XFree(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::string GetWindowTitle(size_t i)
|
||||||
|
{
|
||||||
|
Window w = getTopLevelWindows().at(i);
|
||||||
|
std::string windowTitle;
|
||||||
|
char *name;
|
||||||
|
|
||||||
|
XTextProperty text;
|
||||||
|
int status = XGetTextProperty(
|
||||||
|
disp(), w, &text, XInternAtom(disp(), "_NET_WM_NAME", true));
|
||||||
|
if (status == 0)
|
||||||
|
status = XGetTextProperty(disp(), w, &text,
|
||||||
|
XInternAtom(disp(), "WM_NAME", true));
|
||||||
|
name = reinterpret_cast<char *>(text.value);
|
||||||
|
|
||||||
|
if (status != 0 && name != nullptr) {
|
||||||
|
std::string str(name);
|
||||||
|
windowTitle = str;
|
||||||
|
XFree(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
return windowTitle;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GetWindowList(std::vector<std::string> &windows)
|
||||||
|
{
|
||||||
|
windows.resize(0);
|
||||||
|
|
||||||
|
for (size_t i = 0; i < getTopLevelWindows().size(); ++i) {
|
||||||
|
if (GetWindowTitle(i) != "")
|
||||||
|
windows.emplace_back(GetWindowTitle(i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Overloaded
|
||||||
|
void GetWindowList(QStringList &windows)
|
||||||
|
{
|
||||||
|
windows.clear();
|
||||||
|
|
||||||
|
for (size_t i = 0; i < getTopLevelWindows().size(); ++i) {
|
||||||
|
if (GetWindowTitle(i) != "")
|
||||||
|
windows << QString::fromStdString(GetWindowTitle(i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void GetCurrentWindowTitle(std::string &title)
|
||||||
|
{
|
||||||
|
if (!ewmhIsSupported()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Atom active = XInternAtom(disp(), "_NET_ACTIVE_WINDOW", true);
|
||||||
|
Atom actualType;
|
||||||
|
int format;
|
||||||
|
unsigned long num, bytes;
|
||||||
|
Window *data = 0;
|
||||||
|
char *name;
|
||||||
|
|
||||||
|
Window rootWin = RootWindow(disp(), 0);
|
||||||
|
|
||||||
|
int xstatus = XGetWindowProperty(disp(), rootWin, active, 0L, ~0L,
|
||||||
|
false, AnyPropertyType, &actualType,
|
||||||
|
&format, &num, &bytes,
|
||||||
|
(uint8_t **)&data);
|
||||||
|
|
||||||
|
int status = 0;
|
||||||
|
XTextProperty text;
|
||||||
|
if (xstatus == Success) {
|
||||||
|
status = XGetTextProperty(disp(), data[0], &text,
|
||||||
|
XInternAtom(disp(), "_NET_WM_NAME",
|
||||||
|
true));
|
||||||
|
if (status == 0)
|
||||||
|
status = XGetTextProperty(disp(), data[0], &text,
|
||||||
|
XInternAtom(disp(), "WM_NAME",
|
||||||
|
true));
|
||||||
|
}
|
||||||
|
name = reinterpret_cast<char *>(text.value);
|
||||||
|
|
||||||
|
if (status != 0 && name != nullptr) {
|
||||||
|
std::string str(name);
|
||||||
|
title = str;
|
||||||
|
XFree(name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::pair<int, int> getCursorPos()
|
||||||
|
{
|
||||||
|
std::pair<int, int> pos(0, 0);
|
||||||
|
Display *dpy;
|
||||||
|
Window root;
|
||||||
|
Window ret_root;
|
||||||
|
Window ret_child;
|
||||||
|
int root_x;
|
||||||
|
int root_y;
|
||||||
|
int win_x;
|
||||||
|
int win_y;
|
||||||
|
unsigned int mask;
|
||||||
|
|
||||||
|
dpy = XOpenDisplay(NULL);
|
||||||
|
root = XDefaultRootWindow(dpy);
|
||||||
|
|
||||||
|
if (XQueryPointer(dpy, root, &ret_root, &ret_child, &root_x, &root_y,
|
||||||
|
&win_x, &win_y, &mask)) {
|
||||||
|
pos = std::pair<int, int>(root_x, root_y);
|
||||||
|
}
|
||||||
|
XCloseDisplay(dpy);
|
||||||
|
return pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isFullscreen(std::string &title)
|
||||||
|
{
|
||||||
|
if (!ewmhIsSupported())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Find switch in top level windows
|
||||||
|
std::vector<Window> windows = getTopLevelWindows();
|
||||||
|
for (auto &window : windows) {
|
||||||
|
XTextProperty text;
|
||||||
|
int status = XGetTextProperty(
|
||||||
|
disp(), window, &text,
|
||||||
|
XInternAtom(disp(), "_NET_WM_NAME", true));
|
||||||
|
if (status == 0)
|
||||||
|
status = XGetTextProperty(disp(), window, &text,
|
||||||
|
XInternAtom(disp(), "WM_NAME",
|
||||||
|
true));
|
||||||
|
char *name = reinterpret_cast<char *>(text.value);
|
||||||
|
|
||||||
|
if (status == 0 || name == nullptr)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// True if switch equals window
|
||||||
|
bool equals = (title == name);
|
||||||
|
// True if switch matches window
|
||||||
|
bool matches = QString::fromStdString(name).contains(
|
||||||
|
QRegularExpression(QString::fromStdString(title)));
|
||||||
|
|
||||||
|
// If found, check if switch is fullscreen
|
||||||
|
if (equals || matches) {
|
||||||
|
QStringList states = getStates(window);
|
||||||
|
|
||||||
|
if (!states.isEmpty()) {
|
||||||
|
// True if window is fullscreen
|
||||||
|
bool fullscreen = states.contains(
|
||||||
|
"_NET_WM_STATE_FULLSCREEN");
|
||||||
|
// True if window is maximized vertically
|
||||||
|
bool vertical = states.contains(
|
||||||
|
"_NET_WM_STATE_MAXIMIZED_VERT");
|
||||||
|
// True if window is maximized horizontally
|
||||||
|
bool horizontal = states.contains(
|
||||||
|
"_NET_WM_STATE_MAXIMIZED_HORZ");
|
||||||
|
|
||||||
|
return (fullscreen || (vertical && horizontal));
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
//exe switch is not quite what is expected but it works for now
|
||||||
|
void GetProcessList(QStringList &processes)
|
||||||
|
{
|
||||||
|
processes.clear();
|
||||||
|
for (size_t i = 0; i < getTopLevelWindows().size(); ++i) {
|
||||||
|
std::string s = GetWindowTitle(i);
|
||||||
|
if (s != "")
|
||||||
|
processes << QString::fromStdString(s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isInFocus(const QString &executable)
|
||||||
|
{
|
||||||
|
std::string current;
|
||||||
|
GetCurrentWindowTitle(current);
|
||||||
|
|
||||||
|
// True if executable switch equals current window
|
||||||
|
bool equals = (executable.toStdString() == current);
|
||||||
|
// True if executable switch matches current window
|
||||||
|
bool matches = QString::fromStdString(current).contains(
|
||||||
|
QRegularExpression(executable));
|
||||||
|
|
||||||
|
return (equals || matches);
|
||||||
|
}
|
||||||
|
|
||||||
|
int secondsSinceLastInput()
|
||||||
|
{
|
||||||
|
time_t idle_time;
|
||||||
|
static XScreenSaverInfo *mit_info;
|
||||||
|
Display *display;
|
||||||
|
int screen;
|
||||||
|
|
||||||
|
mit_info = XScreenSaverAllocInfo();
|
||||||
|
|
||||||
|
if ((display = XOpenDisplay(NULL)) == NULL) {
|
||||||
|
return (-1);
|
||||||
|
}
|
||||||
|
screen = DefaultScreen(display);
|
||||||
|
XScreenSaverQueryInfo(display, RootWindow(display, screen), mit_info);
|
||||||
|
idle_time = (mit_info->idle) / 1000;
|
||||||
|
XFree(mit_info);
|
||||||
|
XCloseDisplay(display);
|
||||||
|
|
||||||
|
return idle_time;
|
||||||
|
}
|
||||||
301
src/media-switch.cpp
Normal file
301
src/media-switch.cpp
Normal file
@@ -0,0 +1,301 @@
|
|||||||
|
#include <obs-module.h>
|
||||||
|
#include "headers/advanced-scene-switcher.hpp"
|
||||||
|
|
||||||
|
void SceneSwitcher::on_mediaSwitches_currentRowChanged(int idx)
|
||||||
|
{
|
||||||
|
if (loading)
|
||||||
|
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);
|
||||||
|
for (auto &s : switcher->mediaSwitches) {
|
||||||
|
if (mediaSceneStr.compare(s.mediaSwitchStr.c_str()) == 0) {
|
||||||
|
QString sceneName = GetWeakSourceName(s.scene).c_str();
|
||||||
|
QString sourceName =
|
||||||
|
GetWeakSourceName(s.source).c_str();
|
||||||
|
QString transitionName =
|
||||||
|
GetWeakSourceName(s.transition).c_str();
|
||||||
|
ui->mediaScenes->setCurrentText(sceneName);
|
||||||
|
ui->mediaSources->setCurrentText(sourceName);
|
||||||
|
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()
|
||||||
|
{
|
||||||
|
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();
|
||||||
|
if (!item)
|
||||||
|
return;
|
||||||
|
|
||||||
|
std::string mediaStr =
|
||||||
|
item->data(Qt::UserRole).toString().toUtf8().constData();
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(switcher->m);
|
||||||
|
auto &switches = switcher->mediaSwitches;
|
||||||
|
|
||||||
|
for (auto it = switches.begin(); it != switches.end(); ++it) {
|
||||||
|
auto &s = *it;
|
||||||
|
|
||||||
|
if (s.mediaSwitchStr == mediaStr) {
|
||||||
|
switches.erase(it);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
delete item;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SceneSwitcher::on_mediaUp_clicked()
|
||||||
|
{
|
||||||
|
int index = ui->mediaSwitches->currentRow();
|
||||||
|
if (index != -1 && index != 0) {
|
||||||
|
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;
|
||||||
|
|
||||||
|
if ((time_restriction)ui->mediaTimeRestrictions->currentIndex() ==
|
||||||
|
TIME_RESTRICTION_NONE) {
|
||||||
|
ui->mediaTime->setDisabled(true);
|
||||||
|
} else {
|
||||||
|
ui->mediaTime->setDisabled(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SwitcherData::checkMediaSwitch(bool &match, OBSWeakSource &scene,
|
||||||
|
OBSWeakSource &transition)
|
||||||
|
{
|
||||||
|
for (MediaSwitch &mediaSwitch : mediaSwitches) {
|
||||||
|
obs_source_t *source =
|
||||||
|
obs_weak_source_get_source(mediaSwitch.source);
|
||||||
|
auto duration = obs_source_media_get_duration(source);
|
||||||
|
auto time = obs_source_media_get_time(source);
|
||||||
|
obs_media_state state = obs_source_media_get_state(source);
|
||||||
|
bool matched =
|
||||||
|
state == mediaSwitch.state &&
|
||||||
|
(mediaSwitch.restriction == TIME_RESTRICTION_NONE ||
|
||||||
|
(mediaSwitch.restriction == TIME_RESTRICTION_LONGER &&
|
||||||
|
time > mediaSwitch.time) ||
|
||||||
|
(mediaSwitch.restriction == TIME_RESTRICTION_SHORTER &&
|
||||||
|
time < mediaSwitch.time) ||
|
||||||
|
(mediaSwitch.restriction ==
|
||||||
|
TIME_RESTRICTION_REMAINING_SHORTER &&
|
||||||
|
duration > time &&
|
||||||
|
duration - time < mediaSwitch.time) ||
|
||||||
|
(mediaSwitch.restriction ==
|
||||||
|
TIME_RESTRICTION_REMAINING_LONGER &&
|
||||||
|
duration > time &&
|
||||||
|
duration - time > mediaSwitch.time));
|
||||||
|
if (matched && !mediaSwitch.matched) {
|
||||||
|
match = true;
|
||||||
|
scene = (mediaSwitch.usePreviousScene)
|
||||||
|
? previousScene
|
||||||
|
: mediaSwitch.scene;
|
||||||
|
transition = mediaSwitch.transition;
|
||||||
|
|
||||||
|
if (verbose)
|
||||||
|
blog(LOG_INFO,
|
||||||
|
"Advanced Scene Switcher meida match");
|
||||||
|
}
|
||||||
|
mediaSwitch.matched = matched;
|
||||||
|
obs_source_release(source);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SwitcherData::saveMediaSwitches(obs_data_t *obj)
|
||||||
|
{
|
||||||
|
obs_data_array_t *mediaArray = obs_data_array_create();
|
||||||
|
for (MediaSwitch &s : switcher->mediaSwitches) {
|
||||||
|
obs_data_t *array_obj = obs_data_create();
|
||||||
|
|
||||||
|
obs_source_t *source = obs_weak_source_get_source(s.source);
|
||||||
|
obs_source_t *sceneSource = obs_weak_source_get_source(s.scene);
|
||||||
|
obs_source_t *transition =
|
||||||
|
obs_weak_source_get_source(s.transition);
|
||||||
|
if ((s.usePreviousScene || sceneSource) && source &&
|
||||||
|
transition) {
|
||||||
|
const char *sourceName = obs_source_get_name(source);
|
||||||
|
const char *sceneName =
|
||||||
|
obs_source_get_name(sceneSource);
|
||||||
|
const char *transitionName =
|
||||||
|
obs_source_get_name(transition);
|
||||||
|
obs_data_set_string(array_obj, "source", sourceName);
|
||||||
|
obs_data_set_string(array_obj, "scene",
|
||||||
|
s.usePreviousScene
|
||||||
|
? PREVIOUS_SCENE_NAME
|
||||||
|
: sceneName);
|
||||||
|
obs_data_set_string(array_obj, "transition",
|
||||||
|
transitionName);
|
||||||
|
obs_data_set_int(array_obj, "state", s.state);
|
||||||
|
obs_data_set_int(array_obj, "restriction",
|
||||||
|
s.restriction);
|
||||||
|
obs_data_set_int(array_obj, "time", s.time);
|
||||||
|
obs_data_array_push_back(mediaArray, array_obj);
|
||||||
|
}
|
||||||
|
obs_source_release(source);
|
||||||
|
obs_source_release(sceneSource);
|
||||||
|
obs_source_release(transition);
|
||||||
|
|
||||||
|
obs_data_release(array_obj);
|
||||||
|
}
|
||||||
|
obs_data_set_array(obj, "mediaSwitches", mediaArray);
|
||||||
|
obs_data_array_release(mediaArray);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SwitcherData::loadMediaSwitches(obs_data_t *obj)
|
||||||
|
{
|
||||||
|
obs_data_array_t *mediaArray = obs_data_get_array(obj, "mediaSwitches");
|
||||||
|
switcher->mediaSwitches.clear();
|
||||||
|
size_t count = obs_data_array_count(mediaArray);
|
||||||
|
|
||||||
|
for (size_t i = 0; i < count; i++) {
|
||||||
|
obs_data_t *array_obj = obs_data_array_item(mediaArray, i);
|
||||||
|
|
||||||
|
const char *source = obs_data_get_string(array_obj, "source");
|
||||||
|
const char *scene = obs_data_get_string(array_obj, "scene");
|
||||||
|
const char *transition =
|
||||||
|
obs_data_get_string(array_obj, "transition");
|
||||||
|
obs_media_state state =
|
||||||
|
(obs_media_state)obs_data_get_int(array_obj, "state");
|
||||||
|
time_restriction restriction =
|
||||||
|
(time_restriction)obs_data_get_int(array_obj,
|
||||||
|
"restriction");
|
||||||
|
uint64_t time = obs_data_get_int(array_obj, "time");
|
||||||
|
|
||||||
|
std::string mediaStr = MakeMediaSwitchName(source, scene,
|
||||||
|
transition, state,
|
||||||
|
restriction, time)
|
||||||
|
.toUtf8()
|
||||||
|
.constData();
|
||||||
|
|
||||||
|
switcher->mediaSwitches.emplace_back(
|
||||||
|
GetWeakSourceByName(scene), GetWeakSourceByName(source),
|
||||||
|
GetWeakTransitionByName(transition), state, restriction,
|
||||||
|
time, (strcmp(scene, PREVIOUS_SCENE_NAME) == 0),
|
||||||
|
mediaStr);
|
||||||
|
|
||||||
|
obs_data_release(array_obj);
|
||||||
|
}
|
||||||
|
obs_data_array_release(mediaArray);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SceneSwitcher::setupMediaTab()
|
||||||
|
{
|
||||||
|
populateSceneSelection(ui->mediaScenes, true);
|
||||||
|
populateTransitionSelection(ui->mediaTransitions);
|
||||||
|
|
||||||
|
auto sourceEnum = [](void *data, obs_source_t *source) -> bool /* -- */
|
||||||
|
{
|
||||||
|
QComboBox *combo = reinterpret_cast<QComboBox *>(data);
|
||||||
|
if (strcmp(obs_source_get_id(source), "ffmpeg_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) {
|
||||||
|
std::string sourceName = GetWeakSourceName(s.source);
|
||||||
|
std::string sceneName = (s.usePreviousScene)
|
||||||
|
? PREVIOUS_SCENE_NAME
|
||||||
|
: GetWeakSourceName(s.scene);
|
||||||
|
std::string transitionName = GetWeakSourceName(s.transition);
|
||||||
|
QString listText = MakeMediaSwitchName(
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
237
src/osx/advanced-scene-switcher-osx.mm
Normal file
237
src/osx/advanced-scene-switcher-osx.mm
Normal file
@@ -0,0 +1,237 @@
|
|||||||
|
#import <AppKit/AppKit.h>
|
||||||
|
#import <CoreFoundation/CoreFoundation.h>
|
||||||
|
#import <CoreGraphics/CGEvent.h>
|
||||||
|
#import <Carbon/Carbon.h>
|
||||||
|
#include <Carbon/Carbon.h>
|
||||||
|
#include <util/platform.h>
|
||||||
|
#include "../headers/advanced-scene-switcher.hpp"
|
||||||
|
|
||||||
|
void GetWindowList(std::vector<std::string> &windows)
|
||||||
|
{
|
||||||
|
windows.resize(0);
|
||||||
|
|
||||||
|
@autoreleasepool {
|
||||||
|
NSMutableArray *apps =
|
||||||
|
(__bridge NSMutableArray *)CGWindowListCopyWindowInfo(
|
||||||
|
kCGWindowListOptionAll, kCGNullWindowID);
|
||||||
|
for (NSDictionary *app in apps) {
|
||||||
|
// Construct string from NSString accounting for nil
|
||||||
|
std::string name([[app objectForKey:@"kCGWindowName"]
|
||||||
|
UTF8String],
|
||||||
|
[[app objectForKey:@"kCGWindowName"]
|
||||||
|
lengthOfBytesUsingEncoding:
|
||||||
|
NSUTF8StringEncoding]);
|
||||||
|
std::string owner([[app objectForKey:@"kCGWindowOwnerName"]
|
||||||
|
UTF8String],
|
||||||
|
[[app objectForKey:@"kCGWindowOwnerName"]
|
||||||
|
lengthOfBytesUsingEncoding:
|
||||||
|
NSUTF8StringEncoding]);
|
||||||
|
|
||||||
|
// Check if name exists
|
||||||
|
if (!name.empty() &&
|
||||||
|
find(windows.begin(), windows.end(), name) ==
|
||||||
|
windows.end())
|
||||||
|
windows.emplace_back(name);
|
||||||
|
// Check if owner exists
|
||||||
|
else if (!owner.empty() &&
|
||||||
|
find(windows.begin(), windows.end(), owner) ==
|
||||||
|
windows.end())
|
||||||
|
windows.emplace_back(owner);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Overloaded
|
||||||
|
void GetWindowList(QStringList &windows)
|
||||||
|
{
|
||||||
|
windows.clear();
|
||||||
|
|
||||||
|
@autoreleasepool {
|
||||||
|
NSMutableArray *apps =
|
||||||
|
(__bridge NSMutableArray *)CGWindowListCopyWindowInfo(
|
||||||
|
kCGWindowListOptionAll, kCGNullWindowID);
|
||||||
|
for (NSDictionary *app in apps) {
|
||||||
|
// Construct string from NSString accounting for nil
|
||||||
|
std::string name([[app objectForKey:@"kCGWindowName"]
|
||||||
|
UTF8String],
|
||||||
|
[[app objectForKey:@"kCGWindowName"]
|
||||||
|
lengthOfBytesUsingEncoding:
|
||||||
|
NSUTF8StringEncoding]);
|
||||||
|
std::string owner([[app objectForKey:@"kCGWindowOwnerName"]
|
||||||
|
UTF8String],
|
||||||
|
[[app objectForKey:@"kCGWindowOwnerName"]
|
||||||
|
lengthOfBytesUsingEncoding:
|
||||||
|
NSUTF8StringEncoding]);
|
||||||
|
|
||||||
|
// Check if name exists
|
||||||
|
if (!name.empty() &&
|
||||||
|
!windows.contains(QString::fromStdString(name)))
|
||||||
|
windows << QString::fromStdString(name);
|
||||||
|
// Check if owner exists
|
||||||
|
else if (!owner.empty() &&
|
||||||
|
!windows.contains(
|
||||||
|
QString::fromStdString(name)))
|
||||||
|
windows << QString::fromStdString(owner);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void GetCurrentWindowTitle(std::string &title)
|
||||||
|
{
|
||||||
|
title.resize(0);
|
||||||
|
|
||||||
|
@autoreleasepool {
|
||||||
|
NSMutableArray *apps =
|
||||||
|
(__bridge NSMutableArray *)CGWindowListCopyWindowInfo(
|
||||||
|
kCGWindowListOptionOnScreenOnly,
|
||||||
|
kCGNullWindowID);
|
||||||
|
for (NSDictionary *app in apps) {
|
||||||
|
int layer =
|
||||||
|
[[app objectForKey:@"kCGWindowLayer"] intValue];
|
||||||
|
// True if window is frontmost
|
||||||
|
if (layer == 0) {
|
||||||
|
// Construct string from NSString accounting for nil
|
||||||
|
std::string name(
|
||||||
|
[[app objectForKey:@"kCGWindowName"]
|
||||||
|
UTF8String],
|
||||||
|
[[app objectForKey:@"kCGWindowName"]
|
||||||
|
lengthOfBytesUsingEncoding:
|
||||||
|
NSUTF8StringEncoding]);
|
||||||
|
std::string owner(
|
||||||
|
[[app objectForKey:@"kCGWindowOwnerName"]
|
||||||
|
UTF8String],
|
||||||
|
[[app objectForKey:@"kCGWindowOwnerName"]
|
||||||
|
lengthOfBytesUsingEncoding:
|
||||||
|
NSUTF8StringEncoding]);
|
||||||
|
|
||||||
|
if (!name.empty())
|
||||||
|
title = name;
|
||||||
|
else if (!owner.empty())
|
||||||
|
title = owner;
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::pair<int, int> getCursorPos()
|
||||||
|
{
|
||||||
|
std::pair<int, int> pos(0, 0);
|
||||||
|
CGEventRef event = CGEventCreate(NULL);
|
||||||
|
CGPoint cursorPos = CGEventGetLocation(event);
|
||||||
|
CFRelease(event);
|
||||||
|
pos.first = cursorPos.x;
|
||||||
|
pos.second = cursorPos.y;
|
||||||
|
return pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isFullscreen(std::string &title)
|
||||||
|
{
|
||||||
|
// Check for match
|
||||||
|
@autoreleasepool {
|
||||||
|
NSArray *screens = [NSScreen screens];
|
||||||
|
NSMutableArray *apps =
|
||||||
|
(__bridge NSMutableArray *)CGWindowListCopyWindowInfo(
|
||||||
|
kCGWindowListOptionAll, kCGNullWindowID);
|
||||||
|
for (NSDictionary *app in apps) {
|
||||||
|
// Construct string from NSString accounting for nil
|
||||||
|
std::string name([[app objectForKey:@"kCGWindowName"]
|
||||||
|
UTF8String],
|
||||||
|
[[app objectForKey:@"kCGWindowName"]
|
||||||
|
lengthOfBytesUsingEncoding:
|
||||||
|
NSUTF8StringEncoding]);
|
||||||
|
std::string owner([[app objectForKey:@"kCGWindowOwnerName"]
|
||||||
|
UTF8String],
|
||||||
|
[[app objectForKey:@"kCGWindowOwnerName"]
|
||||||
|
lengthOfBytesUsingEncoding:
|
||||||
|
NSUTF8StringEncoding]);
|
||||||
|
|
||||||
|
// True if switch equals app
|
||||||
|
bool equals = (title == name || title == owner);
|
||||||
|
// True if switch matches app
|
||||||
|
bool matches = (QString::fromStdString(name).contains(
|
||||||
|
QRegularExpression(
|
||||||
|
QString::fromStdString(
|
||||||
|
title))) ||
|
||||||
|
QString::fromStdString(owner).contains(
|
||||||
|
QRegularExpression(
|
||||||
|
QString::fromStdString(
|
||||||
|
title))));
|
||||||
|
|
||||||
|
// If found, check if fullscreen
|
||||||
|
if (equals || matches) {
|
||||||
|
// Get window bounds
|
||||||
|
NSRect bounds;
|
||||||
|
CGRectMakeWithDictionaryRepresentation(
|
||||||
|
(CFDictionaryRef)[app
|
||||||
|
objectForKey:@"kCGWindowBounds"],
|
||||||
|
&bounds);
|
||||||
|
|
||||||
|
// Compare to screen bounds
|
||||||
|
for (NSScreen *screen in screens) {
|
||||||
|
NSRect frame = [screen visibleFrame];
|
||||||
|
|
||||||
|
// True if flipped window origin equals screen origin
|
||||||
|
bool origin =
|
||||||
|
(bounds.origin.x ==
|
||||||
|
frame.origin.x &&
|
||||||
|
([screens[0] visibleFrame]
|
||||||
|
.size.height -
|
||||||
|
frame.size.height -
|
||||||
|
bounds.origin.y ==
|
||||||
|
frame.origin.y));
|
||||||
|
// True if window size equals screen size
|
||||||
|
bool size = NSEqualSizes(bounds.size,
|
||||||
|
frame.size);
|
||||||
|
|
||||||
|
if (origin && size)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
int secondsSinceLastInput()
|
||||||
|
{
|
||||||
|
double time = CGEventSourceSecondsSinceLastEventType(
|
||||||
|
kCGEventSourceStateCombinedSessionState,
|
||||||
|
kCGAnyInputEventType) +
|
||||||
|
0.5;
|
||||||
|
return (int)time;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GetProcessList(QStringList &list)
|
||||||
|
{
|
||||||
|
list.clear();
|
||||||
|
@autoreleasepool {
|
||||||
|
NSWorkspace *ws = [NSWorkspace sharedWorkspace];
|
||||||
|
NSArray *array = [ws runningApplications];
|
||||||
|
for (NSRunningApplication *app in array) {
|
||||||
|
NSString *name = app.localizedName;
|
||||||
|
if (!name)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
const char *str = name.UTF8String;
|
||||||
|
if (str && *str)
|
||||||
|
list << (str);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isInFocus(const QString &executable)
|
||||||
|
{
|
||||||
|
std::string current;
|
||||||
|
GetCurrentWindowTitle(current);
|
||||||
|
|
||||||
|
// True if executable switch equals current window
|
||||||
|
bool equals = (executable.toStdString() == current);
|
||||||
|
// True if executable switch matches current window
|
||||||
|
bool matches = QString::fromStdString(current).contains(
|
||||||
|
QRegularExpression(executable));
|
||||||
|
|
||||||
|
return (equals || matches);
|
||||||
|
}
|
||||||
322
src/pause-switch.cpp
Normal file
322
src/pause-switch.cpp
Normal file
@@ -0,0 +1,322 @@
|
|||||||
|
#include "headers/advanced-scene-switcher.hpp"
|
||||||
|
|
||||||
|
void SceneSwitcher::on_pauseScenesAdd_clicked()
|
||||||
|
{
|
||||||
|
QString sceneName = ui->pauseScenesScenes->currentText();
|
||||||
|
|
||||||
|
if (sceneName.isEmpty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
OBSWeakSource source = GetWeakSourceByQString(sceneName);
|
||||||
|
QVariant v = QVariant::fromValue(sceneName);
|
||||||
|
|
||||||
|
QList<QListWidgetItem *> items =
|
||||||
|
ui->pauseScenes->findItems(sceneName, Qt::MatchExactly);
|
||||||
|
|
||||||
|
if (items.size() == 0) {
|
||||||
|
QListWidgetItem *item =
|
||||||
|
new QListWidgetItem(sceneName, ui->pauseScenes);
|
||||||
|
item->setData(Qt::UserRole, v);
|
||||||
|
|
||||||
|
std::lock_guard<std::mutex> lock(switcher->m);
|
||||||
|
switcher->pauseScenesSwitches.emplace_back(source);
|
||||||
|
ui->pauseScenes->sortItems();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SceneSwitcher::on_pauseScenesRemove_clicked()
|
||||||
|
{
|
||||||
|
QListWidgetItem *item = ui->pauseScenes->currentItem();
|
||||||
|
if (!item)
|
||||||
|
return;
|
||||||
|
|
||||||
|
QString pauseScene = item->data(Qt::UserRole).toString();
|
||||||
|
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(switcher->m);
|
||||||
|
auto &switches = switcher->pauseScenesSwitches;
|
||||||
|
|
||||||
|
for (auto it = switches.begin(); it != switches.end(); ++it) {
|
||||||
|
auto &s = *it;
|
||||||
|
|
||||||
|
if (s == GetWeakSourceByQString(pauseScene)) {
|
||||||
|
switches.erase(it);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
delete item;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SceneSwitcher::on_pauseWindowsAdd_clicked()
|
||||||
|
{
|
||||||
|
QString windowName = ui->pauseWindowsWindows->currentText();
|
||||||
|
|
||||||
|
if (windowName.isEmpty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
QVariant v = QVariant::fromValue(windowName);
|
||||||
|
|
||||||
|
QList<QListWidgetItem *> items =
|
||||||
|
ui->pauseWindows->findItems(windowName, Qt::MatchExactly);
|
||||||
|
|
||||||
|
if (items.size() == 0) {
|
||||||
|
QListWidgetItem *item =
|
||||||
|
new QListWidgetItem(windowName, ui->pauseWindows);
|
||||||
|
item->setData(Qt::UserRole, v);
|
||||||
|
|
||||||
|
std::lock_guard<std::mutex> lock(switcher->m);
|
||||||
|
switcher->pauseWindowsSwitches.emplace_back(
|
||||||
|
windowName.toUtf8().constData());
|
||||||
|
ui->pauseWindows->sortItems();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SceneSwitcher::on_pauseWindowsRemove_clicked()
|
||||||
|
{
|
||||||
|
QListWidgetItem *item = ui->pauseWindows->currentItem();
|
||||||
|
if (!item)
|
||||||
|
return;
|
||||||
|
|
||||||
|
QString windowName = item->data(Qt::UserRole).toString();
|
||||||
|
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(switcher->m);
|
||||||
|
auto &switches = switcher->pauseWindowsSwitches;
|
||||||
|
|
||||||
|
for (auto it = switches.begin(); it != switches.end(); ++it) {
|
||||||
|
auto &s = *it;
|
||||||
|
|
||||||
|
if (s == windowName.toUtf8().constData()) {
|
||||||
|
switches.erase(it);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
delete item;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SceneSwitcher::on_pauseScenes_currentRowChanged(int idx)
|
||||||
|
{
|
||||||
|
if (loading)
|
||||||
|
return;
|
||||||
|
if (idx == -1)
|
||||||
|
return;
|
||||||
|
|
||||||
|
QListWidgetItem *item = ui->pauseScenes->item(idx);
|
||||||
|
|
||||||
|
QString scene = item->data(Qt::UserRole).toString();
|
||||||
|
|
||||||
|
std::lock_guard<std::mutex> lock(switcher->m);
|
||||||
|
for (auto &s : switcher->pauseScenesSwitches) {
|
||||||
|
std::string name = GetWeakSourceName(s);
|
||||||
|
if (scene.compare(name.c_str()) == 0) {
|
||||||
|
ui->pauseScenesScenes->setCurrentText(name.c_str());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SceneSwitcher::on_pauseWindows_currentRowChanged(int idx)
|
||||||
|
{
|
||||||
|
if (loading)
|
||||||
|
return;
|
||||||
|
if (idx == -1)
|
||||||
|
return;
|
||||||
|
|
||||||
|
QListWidgetItem *item = ui->pauseWindows->item(idx);
|
||||||
|
|
||||||
|
QString window = item->data(Qt::UserRole).toString();
|
||||||
|
|
||||||
|
std::lock_guard<std::mutex> lock(switcher->m);
|
||||||
|
for (auto &s : switcher->pauseWindowsSwitches) {
|
||||||
|
if (window.compare(s.c_str()) == 0) {
|
||||||
|
ui->pauseWindowsWindows->setCurrentText(s.c_str());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int SceneSwitcher::PauseScenesFindByData(const QString &scene)
|
||||||
|
{
|
||||||
|
int count = ui->pauseScenes->count();
|
||||||
|
int idx = -1;
|
||||||
|
|
||||||
|
for (int i = 0; i < count; i++) {
|
||||||
|
QListWidgetItem *item = ui->pauseScenes->item(i);
|
||||||
|
QString itemRegion = item->data(Qt::UserRole).toString();
|
||||||
|
|
||||||
|
if (itemRegion == scene) {
|
||||||
|
idx = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return idx;
|
||||||
|
}
|
||||||
|
|
||||||
|
int SceneSwitcher::PauseWindowsFindByData(const QString &window)
|
||||||
|
{
|
||||||
|
int count = ui->pauseWindows->count();
|
||||||
|
int idx = -1;
|
||||||
|
|
||||||
|
for (int i = 0; i < count; i++) {
|
||||||
|
QListWidgetItem *item = ui->pauseWindows->item(i);
|
||||||
|
QString itemRegion = item->data(Qt::UserRole).toString();
|
||||||
|
|
||||||
|
if (itemRegion == window) {
|
||||||
|
idx = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return idx;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SwitcherData::checkPause()
|
||||||
|
{
|
||||||
|
bool pause = false;
|
||||||
|
obs_source_t *currentSource = obs_frontend_get_current_scene();
|
||||||
|
obs_weak_source_t *ws = obs_source_get_weak_source(currentSource);
|
||||||
|
|
||||||
|
for (OBSWeakSource &s : pauseScenesSwitches) {
|
||||||
|
if (s == ws) {
|
||||||
|
pause = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
obs_source_release(currentSource);
|
||||||
|
obs_weak_source_release(ws);
|
||||||
|
|
||||||
|
std::string title;
|
||||||
|
if (!pause) {
|
||||||
|
//lock.unlock();
|
||||||
|
GetCurrentWindowTitle(title);
|
||||||
|
//lock.lock();
|
||||||
|
for (std::string &window : pauseWindowsSwitches) {
|
||||||
|
if (window == title) {
|
||||||
|
pause = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!pause) {
|
||||||
|
//lock.unlock();
|
||||||
|
GetCurrentWindowTitle(title);
|
||||||
|
//lock.lock();
|
||||||
|
for (std::string &window : pauseWindowsSwitches) {
|
||||||
|
try {
|
||||||
|
bool matches = std::regex_match(
|
||||||
|
title, std::regex(window));
|
||||||
|
if (matches) {
|
||||||
|
pause = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} catch (const std::regex_error &) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (verbose && pause)
|
||||||
|
blog(LOG_INFO, "Advanced Scene Switcher pause match");
|
||||||
|
|
||||||
|
return pause;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SwitcherData::savePauseSwitches(obs_data_t *obj)
|
||||||
|
{
|
||||||
|
obs_data_array_t *pauseScenesArray = obs_data_array_create();
|
||||||
|
for (OBSWeakSource &scene : switcher->pauseScenesSwitches) {
|
||||||
|
obs_data_t *array_obj = obs_data_create();
|
||||||
|
|
||||||
|
obs_source_t *source = obs_weak_source_get_source(scene);
|
||||||
|
if (source) {
|
||||||
|
const char *n = obs_source_get_name(source);
|
||||||
|
obs_data_set_string(array_obj, "pauseScene", n);
|
||||||
|
obs_data_array_push_back(pauseScenesArray, array_obj);
|
||||||
|
obs_source_release(source);
|
||||||
|
}
|
||||||
|
|
||||||
|
obs_data_release(array_obj);
|
||||||
|
}
|
||||||
|
obs_data_set_array(obj, "pauseScenes", pauseScenesArray);
|
||||||
|
obs_data_array_release(pauseScenesArray);
|
||||||
|
|
||||||
|
obs_data_array_t *pauseWindowsArray = obs_data_array_create();
|
||||||
|
for (std::string &window : switcher->pauseWindowsSwitches) {
|
||||||
|
obs_data_t *array_obj = obs_data_create();
|
||||||
|
obs_data_set_string(array_obj, "pauseWindow", window.c_str());
|
||||||
|
obs_data_array_push_back(pauseWindowsArray, array_obj);
|
||||||
|
obs_data_release(array_obj);
|
||||||
|
}
|
||||||
|
obs_data_set_array(obj, "pauseWindows", pauseWindowsArray);
|
||||||
|
obs_data_array_release(pauseWindowsArray);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SwitcherData::loadPauseSwitches(obs_data_t *obj)
|
||||||
|
{
|
||||||
|
switcher->pauseScenesSwitches.clear();
|
||||||
|
|
||||||
|
obs_data_array_t *pauseScenesArray =
|
||||||
|
obs_data_get_array(obj, "pauseScenes");
|
||||||
|
size_t count = obs_data_array_count(pauseScenesArray);
|
||||||
|
|
||||||
|
for (size_t i = 0; i < count; i++) {
|
||||||
|
obs_data_t *array_obj =
|
||||||
|
obs_data_array_item(pauseScenesArray, i);
|
||||||
|
|
||||||
|
const char *scene =
|
||||||
|
obs_data_get_string(array_obj, "pauseScene");
|
||||||
|
|
||||||
|
switcher->pauseScenesSwitches.emplace_back(
|
||||||
|
GetWeakSourceByName(scene));
|
||||||
|
|
||||||
|
obs_data_release(array_obj);
|
||||||
|
}
|
||||||
|
obs_data_array_release(pauseScenesArray);
|
||||||
|
|
||||||
|
switcher->pauseWindowsSwitches.clear();
|
||||||
|
|
||||||
|
obs_data_array_t *pauseWindowsArray =
|
||||||
|
obs_data_get_array(obj, "pauseWindows");
|
||||||
|
count = obs_data_array_count(pauseWindowsArray);
|
||||||
|
|
||||||
|
for (size_t i = 0; i < count; i++) {
|
||||||
|
obs_data_t *array_obj =
|
||||||
|
obs_data_array_item(pauseWindowsArray, i);
|
||||||
|
|
||||||
|
const char *window =
|
||||||
|
obs_data_get_string(array_obj, "pauseWindow");
|
||||||
|
|
||||||
|
switcher->pauseWindowsSwitches.emplace_back(window);
|
||||||
|
|
||||||
|
obs_data_release(array_obj);
|
||||||
|
}
|
||||||
|
obs_data_array_release(pauseWindowsArray);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SceneSwitcher::setupPauseTab()
|
||||||
|
{
|
||||||
|
populateSceneSelection(ui->pauseScenesScenes, false);
|
||||||
|
populateWindowSelection(ui->pauseWindowsWindows);
|
||||||
|
|
||||||
|
for (auto &scene : switcher->pauseScenesSwitches) {
|
||||||
|
std::string sceneName = GetWeakSourceName(scene);
|
||||||
|
QString text = QString::fromStdString(sceneName);
|
||||||
|
|
||||||
|
QListWidgetItem *item =
|
||||||
|
new QListWidgetItem(text, ui->pauseScenes);
|
||||||
|
item->setData(Qt::UserRole, text);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto &window : switcher->pauseWindowsSwitches) {
|
||||||
|
QString text = QString::fromStdString(window);
|
||||||
|
|
||||||
|
QListWidgetItem *item =
|
||||||
|
new QListWidgetItem(text, ui->pauseWindows);
|
||||||
|
item->setData(Qt::UserRole, text);
|
||||||
|
}
|
||||||
|
}
|
||||||
68
src/priority.cpp
Normal file
68
src/priority.cpp
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
#include <algorithm>
|
||||||
|
#include "headers/advanced-scene-switcher.hpp"
|
||||||
|
|
||||||
|
void SceneSwitcher::on_threadPriority_currentTextChanged(const QString &text)
|
||||||
|
{
|
||||||
|
if (loading ||
|
||||||
|
ui->threadPriority->count() != switcher->threadPriorities.size())
|
||||||
|
return;
|
||||||
|
|
||||||
|
std::lock_guard<std::mutex> lock(switcher->m);
|
||||||
|
|
||||||
|
for (auto p : switcher->threadPriorities) {
|
||||||
|
if (p.name == text.toUtf8().constData()) {
|
||||||
|
switcher->threadPriority = p.value;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SceneSwitcher::on_priorityUp_clicked()
|
||||||
|
{
|
||||||
|
int currentIndex = ui->priorityList->currentRow();
|
||||||
|
if (currentIndex != -1 && currentIndex != 0) {
|
||||||
|
ui->priorityList->insertItem(
|
||||||
|
currentIndex - 1,
|
||||||
|
ui->priorityList->takeItem(currentIndex));
|
||||||
|
ui->priorityList->setCurrentRow(currentIndex - 1);
|
||||||
|
std::lock_guard<std::mutex> lock(switcher->m);
|
||||||
|
|
||||||
|
iter_swap(switcher->functionNamesByPriority.begin() +
|
||||||
|
currentIndex,
|
||||||
|
switcher->functionNamesByPriority.begin() +
|
||||||
|
currentIndex - 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SceneSwitcher::on_priorityDown_clicked()
|
||||||
|
{
|
||||||
|
int currentIndex = ui->priorityList->currentRow();
|
||||||
|
if (currentIndex != -1 &&
|
||||||
|
currentIndex != ui->priorityList->count() - 1) {
|
||||||
|
ui->priorityList->insertItem(
|
||||||
|
currentIndex + 1,
|
||||||
|
ui->priorityList->takeItem(currentIndex));
|
||||||
|
ui->priorityList->setCurrentRow(currentIndex + 1);
|
||||||
|
std::lock_guard<std::mutex> lock(switcher->m);
|
||||||
|
|
||||||
|
iter_swap(switcher->functionNamesByPriority.begin() +
|
||||||
|
currentIndex,
|
||||||
|
switcher->functionNamesByPriority.begin() +
|
||||||
|
currentIndex + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SwitcherData::prioFuncsValid()
|
||||||
|
{
|
||||||
|
auto it = std::unique(functionNamesByPriority.begin(),
|
||||||
|
functionNamesByPriority.end());
|
||||||
|
bool wasUnique = (it == functionNamesByPriority.end());
|
||||||
|
if (!wasUnique)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
for (int p : functionNamesByPriority) {
|
||||||
|
if (p < 0 || p > 7)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
215
src/random.cpp
Normal file
215
src/random.cpp
Normal file
@@ -0,0 +1,215 @@
|
|||||||
|
#include "headers/advanced-scene-switcher.hpp"
|
||||||
|
#include <random>
|
||||||
|
|
||||||
|
void SceneSwitcher::on_randomScenesList_currentRowChanged(int idx)
|
||||||
|
{
|
||||||
|
if (loading)
|
||||||
|
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);
|
||||||
|
for (auto &s : switcher->randomSwitches) {
|
||||||
|
if (randomSceneStr.compare(s.randomSwitchStr.c_str()) == 0) {
|
||||||
|
QString sceneName = GetWeakSourceName(s.scene).c_str();
|
||||||
|
QString transitionName =
|
||||||
|
GetWeakSourceName(s.transition).c_str();
|
||||||
|
ui->randomScenes->setCurrentText(sceneName);
|
||||||
|
ui->randomSpinBox->setValue(s.delay);
|
||||||
|
ui->randomTransitions->setCurrentText(transitionName);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int SceneSwitcher::randomFindByData(const QString &randomStr)
|
||||||
|
{
|
||||||
|
int count = ui->randomScenesList->count();
|
||||||
|
|
||||||
|
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)
|
||||||
|
return;
|
||||||
|
|
||||||
|
std::string text =
|
||||||
|
item->data(Qt::UserRole).toString().toUtf8().constData();
|
||||||
|
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(switcher->m);
|
||||||
|
auto &switches = switcher->randomSwitches;
|
||||||
|
|
||||||
|
for (auto it = switches.begin(); it != switches.end(); ++it) {
|
||||||
|
auto &s = *it;
|
||||||
|
|
||||||
|
if (s.randomSwitchStr == text) {
|
||||||
|
switches.erase(it);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
delete item;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SwitcherData::checkRandom(bool &match, OBSWeakSource &scene,
|
||||||
|
OBSWeakSource &transition, int &delay)
|
||||||
|
{
|
||||||
|
if (randomSwitches.size() == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
std::vector<RandomSwitch> rs(randomSwitches);
|
||||||
|
std::random_device rng;
|
||||||
|
std::mt19937 urng(rng());
|
||||||
|
std::shuffle(rs.begin(), rs.end(), urng);
|
||||||
|
for (RandomSwitch &r : rs) {
|
||||||
|
if (r.scene == lastRandomScene)
|
||||||
|
continue;
|
||||||
|
scene = r.scene;
|
||||||
|
transition = r.transition;
|
||||||
|
delay = (int)r.delay * 1000;
|
||||||
|
match = true;
|
||||||
|
lastRandomScene = r.scene;
|
||||||
|
|
||||||
|
if (verbose)
|
||||||
|
blog(LOG_INFO, "Advanced Scene Switcher random match");
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SwitcherData::saveRandomSwitches(obs_data_t *obj)
|
||||||
|
{
|
||||||
|
obs_data_array_t *randomArray = obs_data_array_create();
|
||||||
|
for (RandomSwitch &s : switcher->randomSwitches) {
|
||||||
|
obs_data_t *array_obj = obs_data_create();
|
||||||
|
|
||||||
|
obs_source_t *source = obs_weak_source_get_source(s.scene);
|
||||||
|
obs_source_t *transition =
|
||||||
|
obs_weak_source_get_source(s.transition);
|
||||||
|
|
||||||
|
if (source && transition) {
|
||||||
|
const char *sceneName = obs_source_get_name(source);
|
||||||
|
const char *transitionName =
|
||||||
|
obs_source_get_name(transition);
|
||||||
|
obs_data_set_string(array_obj, "scene", sceneName);
|
||||||
|
obs_data_set_string(array_obj, "transition",
|
||||||
|
transitionName);
|
||||||
|
obs_data_set_double(array_obj, "delay", s.delay);
|
||||||
|
obs_data_set_string(array_obj, "str",
|
||||||
|
s.randomSwitchStr.c_str());
|
||||||
|
obs_data_array_push_back(randomArray, array_obj);
|
||||||
|
obs_source_release(source);
|
||||||
|
obs_source_release(transition);
|
||||||
|
}
|
||||||
|
|
||||||
|
obs_data_release(array_obj);
|
||||||
|
}
|
||||||
|
obs_data_set_array(obj, "randomSwitches", randomArray);
|
||||||
|
obs_data_array_release(randomArray);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SwitcherData::loadRandomSwitches(obs_data_t *obj)
|
||||||
|
{
|
||||||
|
switcher->randomSwitches.clear();
|
||||||
|
|
||||||
|
obs_data_array_t *randomArray =
|
||||||
|
obs_data_get_array(obj, "randomSwitches");
|
||||||
|
size_t count = obs_data_array_count(randomArray);
|
||||||
|
|
||||||
|
for (size_t i = 0; i < count; i++) {
|
||||||
|
obs_data_t *array_obj = obs_data_array_item(randomArray, i);
|
||||||
|
|
||||||
|
const char *scene = obs_data_get_string(array_obj, "scene");
|
||||||
|
const char *transition =
|
||||||
|
obs_data_get_string(array_obj, "transition");
|
||||||
|
double delay = obs_data_get_double(array_obj, "delay");
|
||||||
|
const char *str = obs_data_get_string(array_obj, "str");
|
||||||
|
|
||||||
|
switcher->randomSwitches.emplace_back(
|
||||||
|
GetWeakSourceByName(scene),
|
||||||
|
GetWeakTransitionByName(transition), delay, str);
|
||||||
|
|
||||||
|
obs_data_release(array_obj);
|
||||||
|
}
|
||||||
|
obs_data_array_release(randomArray);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SceneSwitcher::setupRandomTab()
|
||||||
|
{
|
||||||
|
populateSceneSelection(ui->randomScenes, false);
|
||||||
|
populateTransitionSelection(ui->randomTransitions);
|
||||||
|
|
||||||
|
for (auto &s : switcher->randomSwitches) {
|
||||||
|
std::string sceneName = GetWeakSourceName(s.scene);
|
||||||
|
std::string transitionName = GetWeakSourceName(s.transition);
|
||||||
|
QString text = MakeRandomSwitchName(
|
||||||
|
sceneName.c_str(), transitionName.c_str(), s.delay);
|
||||||
|
|
||||||
|
QListWidgetItem *item =
|
||||||
|
new QListWidgetItem(text, ui->randomScenesList);
|
||||||
|
item->setData(Qt::UserRole, text);
|
||||||
|
}
|
||||||
|
}
|
||||||
500
src/scene-round-trip.cpp
Normal file
500
src/scene-round-trip.cpp
Normal file
@@ -0,0 +1,500 @@
|
|||||||
|
#include <QFileDialog>
|
||||||
|
#include <QTextStream>
|
||||||
|
#include <QMessageBox>
|
||||||
|
#include <obs.hpp>
|
||||||
|
|
||||||
|
#include "headers/advanced-scene-switcher.hpp"
|
||||||
|
|
||||||
|
#define SECONDS_INDEX 0
|
||||||
|
#define MINUTES_INDEX 1
|
||||||
|
#define HOURS_INDEX 2
|
||||||
|
|
||||||
|
void SceneSwitcher::on_sceneRoundTripDelayUnits_currentIndexChanged(int index)
|
||||||
|
{
|
||||||
|
if (loading)
|
||||||
|
return;
|
||||||
|
|
||||||
|
std::lock_guard<std::mutex> lock(switcher->m);
|
||||||
|
|
||||||
|
double val = ui->sceneRoundTripSpinBox->value();
|
||||||
|
|
||||||
|
switch (switcher->sceneRoundTripUnitMultiplier) {
|
||||||
|
case 1:
|
||||||
|
val /= pow(60, index);
|
||||||
|
break;
|
||||||
|
case 60:
|
||||||
|
val /= pow(60, index - 1);
|
||||||
|
break;
|
||||||
|
case 3600:
|
||||||
|
val /= pow(60, index - 2);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
ui->sceneRoundTripSpinBox->setValue(val);
|
||||||
|
|
||||||
|
switch (index) {
|
||||||
|
case SECONDS_INDEX:
|
||||||
|
switcher->sceneRoundTripUnitMultiplier = 1;
|
||||||
|
break;
|
||||||
|
case MINUTES_INDEX:
|
||||||
|
switcher->sceneRoundTripUnitMultiplier = 60;
|
||||||
|
break;
|
||||||
|
case HOURS_INDEX:
|
||||||
|
switcher->sceneRoundTripUnitMultiplier = 3600;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SceneSwitcher::on_sceneRoundTripAdd_clicked()
|
||||||
|
{
|
||||||
|
QString scene1Name = ui->sceneRoundTripScenes1->currentText();
|
||||||
|
QString scene2Name = ui->sceneRoundTripScenes2->currentText();
|
||||||
|
QString transitionName = ui->sceneRoundTripTransitions->currentText();
|
||||||
|
|
||||||
|
if (scene1Name.isEmpty() || scene2Name.isEmpty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
double delay = ui->sceneRoundTripSpinBox->value() *
|
||||||
|
switcher->sceneRoundTripUnitMultiplier;
|
||||||
|
|
||||||
|
if (scene1Name == scene2Name)
|
||||||
|
return;
|
||||||
|
|
||||||
|
OBSWeakSource source1 = GetWeakSourceByQString(scene1Name);
|
||||||
|
OBSWeakSource source2 = GetWeakSourceByQString(scene2Name);
|
||||||
|
OBSWeakSource transition = GetWeakTransitionByQString(transitionName);
|
||||||
|
|
||||||
|
QString text = MakeSceneRoundTripSwitchName(scene1Name, scene2Name,
|
||||||
|
transitionName, delay);
|
||||||
|
QVariant v = QVariant::fromValue(text);
|
||||||
|
|
||||||
|
int idx = SceneRoundTripFindByData(scene1Name);
|
||||||
|
|
||||||
|
if (idx == -1) {
|
||||||
|
QListWidgetItem *item =
|
||||||
|
new QListWidgetItem(text, ui->sceneRoundTrips);
|
||||||
|
item->setData(Qt::UserRole, v);
|
||||||
|
|
||||||
|
std::lock_guard<std::mutex> lock(switcher->m);
|
||||||
|
switcher->sceneRoundTripSwitches.emplace_back(
|
||||||
|
source1, source2, transition, delay,
|
||||||
|
(scene2Name == QString(PREVIOUS_SCENE_NAME)),
|
||||||
|
text.toUtf8().constData());
|
||||||
|
} else {
|
||||||
|
QListWidgetItem *item = ui->sceneRoundTrips->item(idx);
|
||||||
|
item->setText(text);
|
||||||
|
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(switcher->m);
|
||||||
|
for (auto &s : switcher->sceneRoundTripSwitches) {
|
||||||
|
if (s.scene1 == source1) {
|
||||||
|
s.scene2 = source2;
|
||||||
|
s.delay = delay;
|
||||||
|
s.transition = transition;
|
||||||
|
s.usePreviousScene =
|
||||||
|
(scene2Name ==
|
||||||
|
QString(PREVIOUS_SCENE_NAME));
|
||||||
|
s.sceneRoundTripStr =
|
||||||
|
text.toUtf8().constData();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SceneSwitcher::on_sceneRoundTripRemove_clicked()
|
||||||
|
{
|
||||||
|
QListWidgetItem *item = ui->sceneRoundTrips->currentItem();
|
||||||
|
if (!item)
|
||||||
|
return;
|
||||||
|
|
||||||
|
std::string text =
|
||||||
|
item->data(Qt::UserRole).toString().toUtf8().constData();
|
||||||
|
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(switcher->m);
|
||||||
|
auto &switches = switcher->sceneRoundTripSwitches;
|
||||||
|
|
||||||
|
for (auto it = switches.begin(); it != switches.end(); ++it) {
|
||||||
|
auto &s = *it;
|
||||||
|
|
||||||
|
if (s.sceneRoundTripStr == text) {
|
||||||
|
switches.erase(it);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
delete item;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SceneSwitcher::on_sceneRoundTripSave_clicked()
|
||||||
|
{
|
||||||
|
QString directory = QFileDialog::getSaveFileName(
|
||||||
|
this, tr("Save Scene Round Trip to file ..."),
|
||||||
|
QDir::currentPath(), tr("Text files (*.txt)"));
|
||||||
|
if (directory.isEmpty())
|
||||||
|
return;
|
||||||
|
QFile file(directory);
|
||||||
|
if (!file.open(QIODevice::WriteOnly | QIODevice::Text))
|
||||||
|
return;
|
||||||
|
|
||||||
|
obs_data_t *obj = obs_data_create();
|
||||||
|
switcher->saveSceneRoundTripSwitches(obj);
|
||||||
|
obs_data_save_json(obj, file.fileName().toUtf8().constData());
|
||||||
|
obs_data_release(obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
// to be removed once support for old file format is dropped
|
||||||
|
bool oldSceneRoundTripLoad(QFile *file)
|
||||||
|
{
|
||||||
|
QTextStream in(file);
|
||||||
|
std::vector<QString> lines;
|
||||||
|
|
||||||
|
std::vector<SceneRoundTripSwitch> 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(
|
||||||
|
SceneRoundTripSwitch(
|
||||||
|
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->sceneRoundTripSwitches.clear();
|
||||||
|
switcher->sceneRoundTripSwitches = newSceneRoundTripSwitch;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SceneSwitcher::on_sceneRoundTripLoad_clicked()
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(switcher->m);
|
||||||
|
|
||||||
|
QString directory = QFileDialog::getOpenFileName(
|
||||||
|
this, tr("Select a file to read Scene Round Trip from ..."),
|
||||||
|
QDir::currentPath(), tr("Text files (*.txt)"));
|
||||||
|
if (directory.isEmpty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
QFile file(directory);
|
||||||
|
if (!file.open(QIODevice::ReadOnly | QIODevice::Text))
|
||||||
|
return;
|
||||||
|
|
||||||
|
obs_data_t *obj = obs_data_create_from_json_file(
|
||||||
|
file.fileName().toUtf8().constData());
|
||||||
|
|
||||||
|
if (!obj) {
|
||||||
|
QMessageBox Msgbox;
|
||||||
|
QString txt =
|
||||||
|
"Advanced Scene Switcher failed to import settings!\n";
|
||||||
|
txt += "Falling back to old format.";
|
||||||
|
Msgbox.setText(txt);
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
switcher->loadSceneRoundTripSwitches(obj);
|
||||||
|
obs_data_release(obj);
|
||||||
|
|
||||||
|
QMessageBox Msgbox;
|
||||||
|
Msgbox.setText(
|
||||||
|
"Advanced Scene Switcher settings imported successfully!");
|
||||||
|
Msgbox.exec();
|
||||||
|
close();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SwitcherData::checkSceneRoundTrip(bool &match, OBSWeakSource &scene,
|
||||||
|
OBSWeakSource &transition,
|
||||||
|
std::unique_lock<std::mutex> &lock)
|
||||||
|
{
|
||||||
|
bool sceneRoundTripActive = false;
|
||||||
|
obs_source_t *currentSource = obs_frontend_get_current_scene();
|
||||||
|
obs_weak_source_t *ws = obs_source_get_weak_source(currentSource);
|
||||||
|
|
||||||
|
for (SceneRoundTripSwitch &s : sceneRoundTripSwitches) {
|
||||||
|
if (s.scene1 == ws) {
|
||||||
|
sceneRoundTripActive = true;
|
||||||
|
int dur = s.delay * 1000 - interval;
|
||||||
|
if (dur > 0) {
|
||||||
|
waitScene = currentSource;
|
||||||
|
|
||||||
|
if (verbose)
|
||||||
|
blog(LOG_INFO,
|
||||||
|
"Advanced Scene Switcher sequence sleep %d",
|
||||||
|
dur);
|
||||||
|
|
||||||
|
cv.wait_for(lock,
|
||||||
|
std::chrono::milliseconds(dur));
|
||||||
|
waitScene = nullptr;
|
||||||
|
}
|
||||||
|
obs_source_t *currentSource2 =
|
||||||
|
obs_frontend_get_current_scene();
|
||||||
|
|
||||||
|
// only switch if user hasn't changed scene manually
|
||||||
|
if (currentSource == currentSource2) {
|
||||||
|
match = true;
|
||||||
|
scene = (s.usePreviousScene) ? previousScene
|
||||||
|
: s.scene2;
|
||||||
|
transition = s.transition;
|
||||||
|
}
|
||||||
|
obs_source_release(currentSource2);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
obs_source_release(currentSource);
|
||||||
|
obs_weak_source_release(ws);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SceneSwitcher::on_sceneRoundTrips_currentRowChanged(int idx)
|
||||||
|
{
|
||||||
|
if (loading)
|
||||||
|
return;
|
||||||
|
if (idx == -1)
|
||||||
|
return;
|
||||||
|
|
||||||
|
QListWidgetItem *item = ui->sceneRoundTrips->item(idx);
|
||||||
|
|
||||||
|
QString sceneRoundTrip = item->text();
|
||||||
|
|
||||||
|
std::lock_guard<std::mutex> lock(switcher->m);
|
||||||
|
for (auto &s : switcher->sceneRoundTripSwitches) {
|
||||||
|
if (sceneRoundTrip.compare(s.sceneRoundTripStr.c_str()) == 0) {
|
||||||
|
std::string scene1 = GetWeakSourceName(s.scene1);
|
||||||
|
std::string scene2 = GetWeakSourceName(s.scene2);
|
||||||
|
std::string transitionName =
|
||||||
|
GetWeakSourceName(s.transition);
|
||||||
|
double delay = s.delay /
|
||||||
|
switcher->sceneRoundTripUnitMultiplier;
|
||||||
|
ui->sceneRoundTripScenes1->setCurrentText(
|
||||||
|
scene1.c_str());
|
||||||
|
ui->sceneRoundTripScenes2->setCurrentText(
|
||||||
|
scene2.c_str());
|
||||||
|
ui->sceneRoundTripTransitions->setCurrentText(
|
||||||
|
transitionName.c_str());
|
||||||
|
ui->sceneRoundTripSpinBox->setValue(delay);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int SceneSwitcher::SceneRoundTripFindByData(const QString &scene1)
|
||||||
|
{
|
||||||
|
QRegExp rx(scene1 + " ->.*");
|
||||||
|
int count = ui->sceneRoundTrips->count();
|
||||||
|
int idx = -1;
|
||||||
|
|
||||||
|
for (int i = 0; i < count; i++) {
|
||||||
|
QListWidgetItem *item = ui->sceneRoundTrips->item(i);
|
||||||
|
QString itemString = item->data(Qt::UserRole).toString();
|
||||||
|
|
||||||
|
if (rx.exactMatch(itemString)) {
|
||||||
|
idx = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return idx;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SceneSwitcher::on_sceneRoundTripUp_clicked()
|
||||||
|
{
|
||||||
|
int index = ui->sceneRoundTrips->currentRow();
|
||||||
|
if (index != -1 && index != 0) {
|
||||||
|
ui->sceneRoundTrips->insertItem(
|
||||||
|
index - 1, ui->sceneRoundTrips->takeItem(index));
|
||||||
|
ui->sceneRoundTrips->setCurrentRow(index - 1);
|
||||||
|
|
||||||
|
std::lock_guard<std::mutex> lock(switcher->m);
|
||||||
|
|
||||||
|
iter_swap(switcher->sceneRoundTripSwitches.begin() + index,
|
||||||
|
switcher->sceneRoundTripSwitches.begin() + index - 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SceneSwitcher::on_sceneRoundTripDown_clicked()
|
||||||
|
{
|
||||||
|
int index = ui->sceneRoundTrips->currentRow();
|
||||||
|
if (index != -1 && index != ui->sceneRoundTrips->count() - 1) {
|
||||||
|
ui->sceneRoundTrips->insertItem(
|
||||||
|
index + 1, ui->sceneRoundTrips->takeItem(index));
|
||||||
|
ui->sceneRoundTrips->setCurrentRow(index + 1);
|
||||||
|
|
||||||
|
std::lock_guard<std::mutex> lock(switcher->m);
|
||||||
|
|
||||||
|
iter_swap(switcher->sceneRoundTripSwitches.begin() + index,
|
||||||
|
switcher->sceneRoundTripSwitches.begin() + index + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SwitcherData::saveSceneRoundTripSwitches(obs_data_t *obj)
|
||||||
|
{
|
||||||
|
obs_data_array_t *sceneRoundTripArray = obs_data_array_create();
|
||||||
|
for (SceneRoundTripSwitch &s : switcher->sceneRoundTripSwitches) {
|
||||||
|
obs_data_t *array_obj = obs_data_create();
|
||||||
|
|
||||||
|
obs_source_t *source1 = obs_weak_source_get_source(s.scene1);
|
||||||
|
obs_source_t *source2 = obs_weak_source_get_source(s.scene2);
|
||||||
|
obs_source_t *transition =
|
||||||
|
obs_weak_source_get_source(s.transition);
|
||||||
|
if (source1 && (s.usePreviousScene || source2) && transition) {
|
||||||
|
const char *sceneName1 = obs_source_get_name(source1);
|
||||||
|
const char *sceneName2 = obs_source_get_name(source2);
|
||||||
|
const char *transitionName =
|
||||||
|
obs_source_get_name(transition);
|
||||||
|
obs_data_set_string(array_obj, "sceneRoundTripScene1",
|
||||||
|
sceneName1);
|
||||||
|
obs_data_set_string(array_obj, "sceneRoundTripScene2",
|
||||||
|
s.usePreviousScene
|
||||||
|
? PREVIOUS_SCENE_NAME
|
||||||
|
: sceneName2);
|
||||||
|
obs_data_set_string(array_obj, "transition",
|
||||||
|
transitionName);
|
||||||
|
obs_data_set_double(array_obj, "delay", s.delay);
|
||||||
|
obs_data_set_string(array_obj, "sceneRoundTripStr",
|
||||||
|
s.sceneRoundTripStr.c_str());
|
||||||
|
obs_data_array_push_back(sceneRoundTripArray,
|
||||||
|
array_obj);
|
||||||
|
obs_source_release(source1);
|
||||||
|
obs_source_release(source2);
|
||||||
|
obs_source_release(transition);
|
||||||
|
}
|
||||||
|
|
||||||
|
obs_data_release(array_obj);
|
||||||
|
}
|
||||||
|
obs_data_set_array(obj, "sceneRoundTrip", sceneRoundTripArray);
|
||||||
|
obs_data_array_release(sceneRoundTripArray);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SwitcherData::loadSceneRoundTripSwitches(obs_data_t *obj)
|
||||||
|
{
|
||||||
|
switcher->sceneRoundTripSwitches.clear();
|
||||||
|
|
||||||
|
obs_data_array_t *sceneRoundTripArray =
|
||||||
|
obs_data_get_array(obj, "sceneRoundTrip");
|
||||||
|
size_t count = obs_data_array_count(sceneRoundTripArray);
|
||||||
|
|
||||||
|
for (size_t i = 0; i < count; i++) {
|
||||||
|
obs_data_t *array_obj =
|
||||||
|
obs_data_array_item(sceneRoundTripArray, i);
|
||||||
|
|
||||||
|
const char *scene1 =
|
||||||
|
obs_data_get_string(array_obj, "sceneRoundTripScene1");
|
||||||
|
const char *scene2 =
|
||||||
|
obs_data_get_string(array_obj, "sceneRoundTripScene2");
|
||||||
|
const char *transition =
|
||||||
|
obs_data_get_string(array_obj, "transition");
|
||||||
|
|
||||||
|
double delay = 0;
|
||||||
|
|
||||||
|
// To be removed in future version
|
||||||
|
// to be compatible with older versions
|
||||||
|
if (!obs_data_has_user_value(array_obj, "delay")) {
|
||||||
|
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 = MakeSceneRoundTripSwitchName(
|
||||||
|
scene1, scene2, transition, delay)
|
||||||
|
.toUtf8()
|
||||||
|
.constData();
|
||||||
|
const char *sceneRoundTripStr = str.c_str();
|
||||||
|
|
||||||
|
switcher->sceneRoundTripSwitches.emplace_back(
|
||||||
|
GetWeakSourceByName(scene1),
|
||||||
|
GetWeakSourceByName(scene2),
|
||||||
|
GetWeakTransitionByName(transition), delay,
|
||||||
|
(strcmp(scene2, PREVIOUS_SCENE_NAME) == 0),
|
||||||
|
sceneRoundTripStr);
|
||||||
|
|
||||||
|
obs_data_release(array_obj);
|
||||||
|
}
|
||||||
|
obs_data_array_release(sceneRoundTripArray);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SceneSwitcher::setupSequenceTab()
|
||||||
|
{
|
||||||
|
populateSceneSelection(ui->sceneRoundTripScenes1, false);
|
||||||
|
populateSceneSelection(ui->sceneRoundTripScenes2, true);
|
||||||
|
populateTransitionSelection(ui->sceneRoundTripTransitions);
|
||||||
|
|
||||||
|
double smallestDelay = double(switcher->interval) / 1000;
|
||||||
|
for (auto &s : switcher->sceneRoundTripSwitches) {
|
||||||
|
std::string sceneName1 = GetWeakSourceName(s.scene1);
|
||||||
|
std::string sceneName2 = (s.usePreviousScene)
|
||||||
|
? PREVIOUS_SCENE_NAME
|
||||||
|
: GetWeakSourceName(s.scene2);
|
||||||
|
std::string transitionName = GetWeakSourceName(s.transition);
|
||||||
|
QString text = MakeSceneRoundTripSwitchName(
|
||||||
|
sceneName1.c_str(), sceneName2.c_str(),
|
||||||
|
transitionName.c_str(), s.delay);
|
||||||
|
|
||||||
|
QListWidgetItem *item =
|
||||||
|
new QListWidgetItem(text, ui->sceneRoundTrips);
|
||||||
|
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->sceneRoundTripDelayUnits->addItem("seconds");
|
||||||
|
ui->sceneRoundTripDelayUnits->addItem("minutes");
|
||||||
|
ui->sceneRoundTripDelayUnits->addItem("hours");
|
||||||
|
|
||||||
|
switch (switcher->sceneRoundTripUnitMultiplier) {
|
||||||
|
case 1:
|
||||||
|
ui->sceneRoundTripDelayUnits->setCurrentIndex(0);
|
||||||
|
break;
|
||||||
|
case 60:
|
||||||
|
ui->sceneRoundTripDelayUnits->setCurrentIndex(1);
|
||||||
|
break;
|
||||||
|
case 3600:
|
||||||
|
ui->sceneRoundTripDelayUnits->setCurrentIndex(2);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
515
src/scene-transitions.cpp
Normal file
515
src/scene-transitions.cpp
Normal file
@@ -0,0 +1,515 @@
|
|||||||
|
#include "headers/advanced-scene-switcher.hpp"
|
||||||
|
|
||||||
|
void SceneSwitcher::on_transitionsAdd_clicked()
|
||||||
|
{
|
||||||
|
QString scene1Name = ui->transitionsScene1->currentText();
|
||||||
|
QString scene2Name = ui->transitionsScene2->currentText();
|
||||||
|
QString transitionName = ui->transitionsTransitions->currentText();
|
||||||
|
|
||||||
|
if (scene1Name.isEmpty() || scene2Name.isEmpty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (scene1Name == scene2Name)
|
||||||
|
return;
|
||||||
|
|
||||||
|
OBSWeakSource source1 = GetWeakSourceByQString(scene1Name);
|
||||||
|
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.scene1 == source1 &&
|
||||||
|
s.scene2 == source2) {
|
||||||
|
s.transition = transition;
|
||||||
|
s.sceneTransitionStr =
|
||||||
|
text.toUtf8().constData();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ui->sceneTransitions->sortItems();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SceneSwitcher::on_transitionsRemove_clicked()
|
||||||
|
{
|
||||||
|
QListWidgetItem *item = ui->sceneTransitions->currentItem();
|
||||||
|
if (!item)
|
||||||
|
return;
|
||||||
|
|
||||||
|
std::string text =
|
||||||
|
item->data(Qt::UserRole).toString().toUtf8().constData();
|
||||||
|
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(switcher->m);
|
||||||
|
auto &switches = switcher->sceneTransitions;
|
||||||
|
|
||||||
|
for (auto it = switches.begin(); it != switches.end(); ++it) {
|
||||||
|
auto &s = *it;
|
||||||
|
|
||||||
|
if (s.sceneTransitionStr == text) {
|
||||||
|
switches.erase(it);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
delete item;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SceneSwitcher::on_defaultTransitionsAdd_clicked()
|
||||||
|
{
|
||||||
|
QString sceneName = ui->defaultTransitionsScene->currentText();
|
||||||
|
QString transitionName =
|
||||||
|
ui->defaultTransitionsTransitions->currentText();
|
||||||
|
|
||||||
|
if (sceneName.isEmpty() || transitionName.isEmpty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
OBSWeakSource source = GetWeakSourceByQString(sceneName);
|
||||||
|
OBSWeakSource transition = GetWeakTransitionByQString(transitionName);
|
||||||
|
|
||||||
|
QString text =
|
||||||
|
MakeDefaultSceneTransitionName(sceneName, transitionName);
|
||||||
|
QVariant v = QVariant::fromValue(text);
|
||||||
|
|
||||||
|
int idx = DefaultTransitionsFindByData(sceneName);
|
||||||
|
|
||||||
|
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()
|
||||||
|
{
|
||||||
|
QListWidgetItem *item = ui->defaultTransitions->currentItem();
|
||||||
|
if (!item)
|
||||||
|
return;
|
||||||
|
|
||||||
|
std::string text =
|
||||||
|
item->data(Qt::UserRole).toString().toUtf8().constData();
|
||||||
|
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(switcher->m);
|
||||||
|
auto &switches = switcher->defaultSceneTransitions;
|
||||||
|
|
||||||
|
for (auto it = switches.begin(); it != switches.end(); ++it) {
|
||||||
|
auto &s = *it;
|
||||||
|
|
||||||
|
if (s.sceneTransitionStr == text) {
|
||||||
|
switches.erase(it);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
delete item;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SwitcherData::setDefaultSceneTransitions()
|
||||||
|
{
|
||||||
|
obs_source_t *currentSource = obs_frontend_get_current_scene();
|
||||||
|
obs_weak_source_t *ws = obs_source_get_weak_source(currentSource);
|
||||||
|
|
||||||
|
for (DefaultSceneTransition &s : defaultSceneTransitions) {
|
||||||
|
if (s.scene == ws) {
|
||||||
|
obs_source_t *transition =
|
||||||
|
obs_weak_source_get_source(s.transition);
|
||||||
|
//This might cancel the current transition
|
||||||
|
//There is no way to be sure when the previous transition finished
|
||||||
|
obs_frontend_set_current_transition(transition);
|
||||||
|
|
||||||
|
if (verbose)
|
||||||
|
blog(LOG_INFO,
|
||||||
|
"Advanced Scene Switcher default transition set");
|
||||||
|
|
||||||
|
obs_source_release(transition);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
obs_source_release(currentSource);
|
||||||
|
obs_weak_source_release(ws);
|
||||||
|
}
|
||||||
|
|
||||||
|
int SceneSwitcher::SceneTransitionsFindByData(const QString &scene1,
|
||||||
|
const QString &scene2)
|
||||||
|
{
|
||||||
|
QRegExp rx(scene1 + " --- .* --> " + scene2);
|
||||||
|
int count = ui->sceneTransitions->count();
|
||||||
|
int idx = -1;
|
||||||
|
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
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.scene1);
|
||||||
|
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)
|
||||||
|
return;
|
||||||
|
|
||||||
|
std::lock_guard<std::mutex> lock(switcher->m);
|
||||||
|
if (!state) {
|
||||||
|
switcher->tansitionOverrideOverride = false;
|
||||||
|
} else {
|
||||||
|
switcher->tansitionOverrideOverride = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
obs_weak_source_t *getNextTransition(obs_weak_source_t *scene1,
|
||||||
|
obs_weak_source_t *scene2)
|
||||||
|
{
|
||||||
|
obs_weak_source_t *ws = nullptr;
|
||||||
|
if (scene1 && scene2) {
|
||||||
|
for (SceneTransition &t : switcher->sceneTransitions) {
|
||||||
|
if (t.scene1 == scene1 && t.scene2 == scene2)
|
||||||
|
ws = t.transition;
|
||||||
|
}
|
||||||
|
obs_weak_source_addref(ws);
|
||||||
|
}
|
||||||
|
return ws;
|
||||||
|
}
|
||||||
|
|
||||||
|
void overwriteTransitionOverride(obs_weak_source_t *sceneWs,
|
||||||
|
obs_source_t *transition, transitionData &td)
|
||||||
|
{
|
||||||
|
obs_source_t *scene = obs_weak_source_get_source(sceneWs);
|
||||||
|
obs_data_t *data = obs_source_get_private_settings(scene);
|
||||||
|
|
||||||
|
td.name = obs_data_get_string(data, "transition");
|
||||||
|
td.duration = obs_data_get_int(data, "duration");
|
||||||
|
|
||||||
|
const char *name = obs_source_get_name(transition);
|
||||||
|
int duration = obs_frontend_get_transition_duration();
|
||||||
|
|
||||||
|
obs_data_set_string(data, "transition", name);
|
||||||
|
obs_data_set_int(data, "transition_duration", duration);
|
||||||
|
|
||||||
|
obs_data_release(data);
|
||||||
|
obs_source_release(scene);
|
||||||
|
}
|
||||||
|
|
||||||
|
void restoreTransitionOverride(obs_source_t *scene, transitionData td)
|
||||||
|
{
|
||||||
|
obs_data_t *data = obs_source_get_private_settings(scene);
|
||||||
|
|
||||||
|
obs_data_set_string(data, "transition", td.name.c_str());
|
||||||
|
obs_data_set_int(data, "transition_duration", td.duration);
|
||||||
|
|
||||||
|
obs_data_release(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
void setNextTransition(OBSWeakSource &targetScene, obs_source_t *currentSource,
|
||||||
|
OBSWeakSource &transition,
|
||||||
|
bool &transitionOverrideOverride, transitionData &td)
|
||||||
|
{
|
||||||
|
obs_weak_source_t *currentScene =
|
||||||
|
obs_source_get_weak_source(currentSource);
|
||||||
|
obs_weak_source_t *nextTransitionWs =
|
||||||
|
getNextTransition(currentScene, targetScene);
|
||||||
|
obs_weak_source_release(currentScene);
|
||||||
|
|
||||||
|
obs_source_t *nextTransition = nullptr;
|
||||||
|
if (nextTransitionWs) {
|
||||||
|
nextTransition = obs_weak_source_get_source(nextTransitionWs);
|
||||||
|
} else if (transition) {
|
||||||
|
nextTransition = obs_weak_source_get_source(transition);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nextTransition) {
|
||||||
|
obs_frontend_set_current_transition(nextTransition);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (transitionOverrideOverride)
|
||||||
|
overwriteTransitionOverride(targetScene, nextTransition, td);
|
||||||
|
|
||||||
|
obs_weak_source_release(nextTransitionWs);
|
||||||
|
obs_source_release(nextTransition);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SwitcherData::saveSceneTransitions(obs_data_t *obj)
|
||||||
|
{
|
||||||
|
obs_data_array_t *sceneTransitionsArray = obs_data_array_create();
|
||||||
|
for (SceneTransition &s : switcher->sceneTransitions) {
|
||||||
|
obs_data_t *array_obj = obs_data_create();
|
||||||
|
|
||||||
|
obs_source_t *source1 = obs_weak_source_get_source(s.scene1);
|
||||||
|
obs_source_t *source2 = obs_weak_source_get_source(s.scene2);
|
||||||
|
obs_source_t *transition =
|
||||||
|
obs_weak_source_get_source(s.transition);
|
||||||
|
if (source1 && source2 && transition) {
|
||||||
|
const char *sceneName1 = obs_source_get_name(source1);
|
||||||
|
const char *sceneName2 = obs_source_get_name(source2);
|
||||||
|
const char *transitionName =
|
||||||
|
obs_source_get_name(transition);
|
||||||
|
obs_data_set_string(array_obj, "Scene1", sceneName1);
|
||||||
|
obs_data_set_string(array_obj, "Scene2", sceneName2);
|
||||||
|
obs_data_set_string(array_obj, "transition",
|
||||||
|
transitionName);
|
||||||
|
obs_data_set_string(array_obj, "Str",
|
||||||
|
s.sceneTransitionStr.c_str());
|
||||||
|
obs_data_array_push_back(sceneTransitionsArray,
|
||||||
|
array_obj);
|
||||||
|
obs_source_release(source1);
|
||||||
|
obs_source_release(source2);
|
||||||
|
obs_source_release(transition);
|
||||||
|
}
|
||||||
|
|
||||||
|
obs_data_release(array_obj);
|
||||||
|
}
|
||||||
|
obs_data_set_array(obj, "sceneTransitions", sceneTransitionsArray);
|
||||||
|
obs_data_array_release(sceneTransitionsArray);
|
||||||
|
|
||||||
|
obs_data_array_t *defaultTransitionsArray = obs_data_array_create();
|
||||||
|
for (DefaultSceneTransition &s : switcher->defaultSceneTransitions) {
|
||||||
|
obs_data_t *array_obj = obs_data_create();
|
||||||
|
|
||||||
|
obs_source_t *source = obs_weak_source_get_source(s.scene);
|
||||||
|
obs_source_t *transition =
|
||||||
|
obs_weak_source_get_source(s.transition);
|
||||||
|
if (source && transition) {
|
||||||
|
const char *sceneName = obs_source_get_name(source);
|
||||||
|
const char *transitionName =
|
||||||
|
obs_source_get_name(transition);
|
||||||
|
obs_data_set_string(array_obj, "Scene", sceneName);
|
||||||
|
obs_data_set_string(array_obj, "transition",
|
||||||
|
transitionName);
|
||||||
|
obs_data_set_string(array_obj, "Str",
|
||||||
|
s.sceneTransitionStr.c_str());
|
||||||
|
obs_data_array_push_back(defaultTransitionsArray,
|
||||||
|
array_obj);
|
||||||
|
obs_source_release(source);
|
||||||
|
obs_source_release(transition);
|
||||||
|
}
|
||||||
|
|
||||||
|
obs_data_release(array_obj);
|
||||||
|
}
|
||||||
|
obs_data_set_array(obj, "defaultTransitions", defaultTransitionsArray);
|
||||||
|
obs_data_array_release(defaultTransitionsArray);
|
||||||
|
|
||||||
|
obs_data_set_bool(obj, "tansitionOverrideOverride",
|
||||||
|
switcher->tansitionOverrideOverride);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SwitcherData::loadSceneTransitions(obs_data_t *obj)
|
||||||
|
{
|
||||||
|
switcher->sceneTransitions.clear();
|
||||||
|
|
||||||
|
obs_data_array_t *sceneTransitionsArray =
|
||||||
|
obs_data_get_array(obj, "sceneTransitions");
|
||||||
|
size_t count = obs_data_array_count(sceneTransitionsArray);
|
||||||
|
|
||||||
|
for (size_t i = 0; i < count; i++) {
|
||||||
|
obs_data_t *array_obj =
|
||||||
|
obs_data_array_item(sceneTransitionsArray, i);
|
||||||
|
|
||||||
|
const char *scene1 = obs_data_get_string(array_obj, "Scene1");
|
||||||
|
const char *scene2 = obs_data_get_string(array_obj, "Scene2");
|
||||||
|
const char *transition =
|
||||||
|
obs_data_get_string(array_obj, "transition");
|
||||||
|
const char *sceneTransitionsStr =
|
||||||
|
obs_data_get_string(array_obj, "Str");
|
||||||
|
|
||||||
|
switcher->sceneTransitions.emplace_back(
|
||||||
|
GetWeakSourceByName(scene1),
|
||||||
|
GetWeakSourceByName(scene2),
|
||||||
|
GetWeakTransitionByName(transition),
|
||||||
|
sceneTransitionsStr);
|
||||||
|
|
||||||
|
obs_data_release(array_obj);
|
||||||
|
}
|
||||||
|
obs_data_array_release(sceneTransitionsArray);
|
||||||
|
|
||||||
|
switcher->defaultSceneTransitions.clear();
|
||||||
|
|
||||||
|
obs_data_array_t *defaultTransitionsArray =
|
||||||
|
obs_data_get_array(obj, "defaultTransitions");
|
||||||
|
count = obs_data_array_count(defaultTransitionsArray);
|
||||||
|
|
||||||
|
for (size_t i = 0; i < count; i++) {
|
||||||
|
obs_data_t *array_obj =
|
||||||
|
obs_data_array_item(defaultTransitionsArray, i);
|
||||||
|
|
||||||
|
const char *scene = obs_data_get_string(array_obj, "Scene");
|
||||||
|
const char *transition =
|
||||||
|
obs_data_get_string(array_obj, "transition");
|
||||||
|
const char *sceneTransitionsStr =
|
||||||
|
obs_data_get_string(array_obj, "Str");
|
||||||
|
|
||||||
|
switcher->defaultSceneTransitions.emplace_back(
|
||||||
|
GetWeakSourceByName(scene),
|
||||||
|
GetWeakTransitionByName(transition),
|
||||||
|
sceneTransitionsStr);
|
||||||
|
|
||||||
|
obs_data_release(array_obj);
|
||||||
|
}
|
||||||
|
obs_data_array_release(defaultTransitionsArray);
|
||||||
|
|
||||||
|
switcher->tansitionOverrideOverride =
|
||||||
|
obs_data_get_bool(obj, "tansitionOverrideOverride");
|
||||||
|
}
|
||||||
|
|
||||||
|
void SceneSwitcher::setupTransitionsTab()
|
||||||
|
{
|
||||||
|
populateSceneSelection(ui->transitionsScene1, false);
|
||||||
|
populateSceneSelection(ui->transitionsScene2, false);
|
||||||
|
populateSceneSelection(ui->defaultTransitionsScene, false);
|
||||||
|
populateTransitionSelection(ui->transitionsTransitions);
|
||||||
|
populateTransitionSelection(ui->defaultTransitionsTransitions);
|
||||||
|
|
||||||
|
for (auto &s : switcher->sceneTransitions) {
|
||||||
|
std::string sceneName1 = GetWeakSourceName(s.scene1);
|
||||||
|
std::string sceneName2 = GetWeakSourceName(s.scene2);
|
||||||
|
std::string transitionName = GetWeakSourceName(s.transition);
|
||||||
|
QString text = MakeSceneTransitionName(sceneName1.c_str(),
|
||||||
|
sceneName2.c_str(),
|
||||||
|
transitionName.c_str());
|
||||||
|
|
||||||
|
QListWidgetItem *item =
|
||||||
|
new QListWidgetItem(text, ui->sceneTransitions);
|
||||||
|
item->setData(Qt::UserRole, text);
|
||||||
|
}
|
||||||
|
//(transitionDurationLongerThanInterval(switcher->interval))
|
||||||
|
// ? ui->transitionWarning->setVisible(true)
|
||||||
|
// : ui->transitionWarning->setVisible(false);
|
||||||
|
|
||||||
|
for (auto &s : switcher->defaultSceneTransitions) {
|
||||||
|
std::string sceneName = GetWeakSourceName(s.scene);
|
||||||
|
std::string transitionName = GetWeakSourceName(s.transition);
|
||||||
|
QString text = MakeDefaultSceneTransitionName(
|
||||||
|
sceneName.c_str(), transitionName.c_str());
|
||||||
|
|
||||||
|
QListWidgetItem *item =
|
||||||
|
new QListWidgetItem(text, ui->defaultTransitions);
|
||||||
|
item->setData(Qt::UserRole, text);
|
||||||
|
}
|
||||||
|
|
||||||
|
ui->transitionOverridecheckBox->setChecked(
|
||||||
|
switcher->tansitionOverrideOverride);
|
||||||
|
}
|
||||||
284
src/screen-region-switch.cpp
Normal file
284
src/screen-region-switch.cpp
Normal file
@@ -0,0 +1,284 @@
|
|||||||
|
#include <QTimer>
|
||||||
|
|
||||||
|
#include "headers/advanced-scene-switcher.hpp"
|
||||||
|
|
||||||
|
void SwitcherData::checkScreenRegionSwitch(bool &match, OBSWeakSource &scene,
|
||||||
|
OBSWeakSource &transition)
|
||||||
|
{
|
||||||
|
std::pair<int, int> cursorPos = getCursorPos();
|
||||||
|
int minRegionSize = 99999;
|
||||||
|
|
||||||
|
for (auto &s : screenRegionSwitches) {
|
||||||
|
if (cursorPos.first >= s.minX && cursorPos.second >= s.minY &&
|
||||||
|
cursorPos.first <= s.maxX && cursorPos.second <= s.maxY) {
|
||||||
|
int regionSize = (s.maxX - s.minX) + (s.maxY - s.minY);
|
||||||
|
if (regionSize < minRegionSize) {
|
||||||
|
match = true;
|
||||||
|
scene = s.scene;
|
||||||
|
transition = s.transition;
|
||||||
|
minRegionSize = regionSize;
|
||||||
|
|
||||||
|
if (verbose)
|
||||||
|
blog(LOG_INFO,
|
||||||
|
"Advanced Scene Switcher region match");
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SceneSwitcher::updateScreenRegionCursorPos()
|
||||||
|
{
|
||||||
|
std::pair<int, int> position = getCursorPos();
|
||||||
|
ui->cursorXPosition->setText(QString::number(position.first));
|
||||||
|
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 ®ion)
|
||||||
|
{
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
obs_data_array_t *screenRegionArray = obs_data_array_create();
|
||||||
|
for (ScreenRegionSwitch &s : switcher->screenRegionSwitches) {
|
||||||
|
obs_data_t *array_obj = obs_data_create();
|
||||||
|
|
||||||
|
obs_source_t *source = obs_weak_source_get_source(s.scene);
|
||||||
|
obs_source_t *transition =
|
||||||
|
obs_weak_source_get_source(s.transition);
|
||||||
|
if (source && transition) {
|
||||||
|
const char *sceneName = obs_source_get_name(source);
|
||||||
|
const char *transitionName =
|
||||||
|
obs_source_get_name(transition);
|
||||||
|
obs_data_set_string(array_obj, "screenRegionScene",
|
||||||
|
sceneName);
|
||||||
|
obs_data_set_string(array_obj, "transition",
|
||||||
|
transitionName);
|
||||||
|
obs_data_set_int(array_obj, "minX", s.minX);
|
||||||
|
obs_data_set_int(array_obj, "minY", s.minY);
|
||||||
|
obs_data_set_int(array_obj, "maxX", s.maxX);
|
||||||
|
obs_data_set_int(array_obj, "maxY", s.maxY);
|
||||||
|
obs_data_set_string(array_obj, "screenRegionStr",
|
||||||
|
s.regionStr.c_str());
|
||||||
|
obs_data_array_push_back(screenRegionArray, array_obj);
|
||||||
|
obs_source_release(source);
|
||||||
|
obs_source_release(transition);
|
||||||
|
}
|
||||||
|
|
||||||
|
obs_data_release(array_obj);
|
||||||
|
}
|
||||||
|
obs_data_set_array(obj, "screenRegion", screenRegionArray);
|
||||||
|
obs_data_array_release(screenRegionArray);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SwitcherData::loadScreenRegionSwitches(obs_data_t *obj)
|
||||||
|
{
|
||||||
|
switcher->screenRegionSwitches.clear();
|
||||||
|
|
||||||
|
obs_data_array_t *screenRegionArray =
|
||||||
|
obs_data_get_array(obj, "screenRegion");
|
||||||
|
size_t count = obs_data_array_count(screenRegionArray);
|
||||||
|
|
||||||
|
for (size_t i = 0; i < count; i++) {
|
||||||
|
obs_data_t *array_obj =
|
||||||
|
obs_data_array_item(screenRegionArray, i);
|
||||||
|
|
||||||
|
const char *scene =
|
||||||
|
obs_data_get_string(array_obj, "screenRegionScene");
|
||||||
|
const char *transition =
|
||||||
|
obs_data_get_string(array_obj, "transition");
|
||||||
|
int minX = obs_data_get_int(array_obj, "minX");
|
||||||
|
int minY = obs_data_get_int(array_obj, "minY");
|
||||||
|
int maxX = obs_data_get_int(array_obj, "maxX");
|
||||||
|
int maxY = obs_data_get_int(array_obj, "maxY");
|
||||||
|
std::string regionStr =
|
||||||
|
obs_data_get_string(array_obj, "screenRegionStr");
|
||||||
|
|
||||||
|
switcher->screenRegionSwitches.emplace_back(
|
||||||
|
GetWeakSourceByName(scene),
|
||||||
|
GetWeakTransitionByName(transition), minX, minY, maxX,
|
||||||
|
maxY, regionStr);
|
||||||
|
|
||||||
|
obs_data_release(array_obj);
|
||||||
|
}
|
||||||
|
obs_data_array_release(screenRegionArray);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SceneSwitcher::setupRegionTab()
|
||||||
|
{
|
||||||
|
populateSceneSelection(ui->screenRegionScenes, false);
|
||||||
|
populateTransitionSelection(ui->screenRegionsTransitions);
|
||||||
|
|
||||||
|
for (auto &s : switcher->screenRegionSwitches) {
|
||||||
|
std::string sceneName = GetWeakSourceName(s.scene);
|
||||||
|
std::string transitionName = GetWeakSourceName(s.transition);
|
||||||
|
QString text = MakeScreenRegionSwitchName(
|
||||||
|
sceneName.c_str(), transitionName.c_str(), s.minX,
|
||||||
|
s.minY, s.maxX, s.maxY);
|
||||||
|
|
||||||
|
QListWidgetItem *item =
|
||||||
|
new QListWidgetItem(text, ui->screenRegions);
|
||||||
|
item->setData(Qt::UserRole, s.regionStr.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
// screen region cursor position
|
||||||
|
QTimer *screenRegionTimer = new QTimer(this);
|
||||||
|
connect(screenRegionTimer, SIGNAL(timeout()), this,
|
||||||
|
SLOT(updateScreenRegionCursorPos()));
|
||||||
|
screenRegionTimer->start(1000);
|
||||||
|
}
|
||||||
238
src/time-switch.cpp
Normal file
238
src/time-switch.cpp
Normal file
@@ -0,0 +1,238 @@
|
|||||||
|
#include "headers/advanced-scene-switcher.hpp"
|
||||||
|
|
||||||
|
void SceneSwitcher::on_timeSwitches_currentRowChanged(int idx)
|
||||||
|
{
|
||||||
|
if (loading)
|
||||||
|
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);
|
||||||
|
for (auto &s : switcher->timeSwitches) {
|
||||||
|
if (timeScenestr.compare(s.timeSwitchStr.c_str()) == 0) {
|
||||||
|
QString sceneName = GetWeakSourceName(s.scene).c_str();
|
||||||
|
QString transitionName =
|
||||||
|
GetWeakSourceName(s.transition).c_str();
|
||||||
|
ui->timeScenes->setCurrentText(sceneName);
|
||||||
|
ui->timeEdit->setTime(s.time);
|
||||||
|
ui->timeTransitions->setCurrentText(transitionName);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int SceneSwitcher::timeFindByData(const QString &timeStr)
|
||||||
|
{
|
||||||
|
QRegExp rx("At " + timeStr + " switch to .*");
|
||||||
|
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);
|
||||||
|
|
||||||
|
QString text = MakeTimeSwitchName(sceneName, transitionName, time);
|
||||||
|
QVariant v = QVariant::fromValue(text);
|
||||||
|
|
||||||
|
int idx = timeFindByData(time.toString());
|
||||||
|
|
||||||
|
if (idx == -1) {
|
||||||
|
std::lock_guard<std::mutex> lock(switcher->m);
|
||||||
|
switcher->timeSwitches.emplace_back(
|
||||||
|
source, transition, 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.time == time) {
|
||||||
|
s.scene = source;
|
||||||
|
s.transition = transition;
|
||||||
|
s.usePreviousScene =
|
||||||
|
(sceneName ==
|
||||||
|
QString(PREVIOUS_SCENE_NAME));
|
||||||
|
s.timeSwitchStr =
|
||||||
|
text.toUtf8().constData();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ui->timeSwitches->sortItems();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SceneSwitcher::on_timeRemove_clicked()
|
||||||
|
{
|
||||||
|
QListWidgetItem *item = ui->timeSwitches->currentItem();
|
||||||
|
if (!item)
|
||||||
|
return;
|
||||||
|
|
||||||
|
std::string text =
|
||||||
|
item->data(Qt::UserRole).toString().toUtf8().constData();
|
||||||
|
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(switcher->m);
|
||||||
|
auto &switches = switcher->timeSwitches;
|
||||||
|
|
||||||
|
for (auto it = switches.begin(); it != switches.end(); ++it) {
|
||||||
|
auto &s = *it;
|
||||||
|
|
||||||
|
if (s.timeSwitchStr == text) {
|
||||||
|
switches.erase(it);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
delete item;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SwitcherData::checkTimeSwitch(bool &match, OBSWeakSource &scene,
|
||||||
|
OBSWeakSource &transition)
|
||||||
|
{
|
||||||
|
if (timeSwitches.size() == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
QTime now = QTime::currentTime();
|
||||||
|
|
||||||
|
for (TimeSwitch &s : timeSwitches) {
|
||||||
|
|
||||||
|
QTime validSwitchTimeWindow = s.time.addMSecs(interval);
|
||||||
|
|
||||||
|
match = s.time <= now && now <= validSwitchTimeWindow;
|
||||||
|
if (!match &&
|
||||||
|
validSwitchTimeWindow.msecsSinceStartOfDay() < interval) {
|
||||||
|
// check for overflow
|
||||||
|
match = now >= s.time || now <= validSwitchTimeWindow;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (match) {
|
||||||
|
scene = (s.usePreviousScene) ? previousScene : s.scene;
|
||||||
|
transition = s.transition;
|
||||||
|
match = true;
|
||||||
|
|
||||||
|
if (verbose)
|
||||||
|
blog(LOG_INFO,
|
||||||
|
"Advanced Scene Switcher time match");
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SwitcherData::saveTimeSwitches(obs_data_t *obj)
|
||||||
|
{
|
||||||
|
obs_data_array_t *timeArray = obs_data_array_create();
|
||||||
|
for (TimeSwitch &s : switcher->timeSwitches) {
|
||||||
|
obs_data_t *array_obj = obs_data_create();
|
||||||
|
|
||||||
|
obs_source_t *sceneSource = obs_weak_source_get_source(s.scene);
|
||||||
|
obs_source_t *transition =
|
||||||
|
obs_weak_source_get_source(s.transition);
|
||||||
|
if ((s.usePreviousScene || sceneSource) && transition) {
|
||||||
|
const char *sceneName =
|
||||||
|
obs_source_get_name(sceneSource);
|
||||||
|
const char *transitionName =
|
||||||
|
obs_source_get_name(transition);
|
||||||
|
obs_data_set_string(array_obj, "scene",
|
||||||
|
s.usePreviousScene
|
||||||
|
? PREVIOUS_SCENE_NAME
|
||||||
|
: sceneName);
|
||||||
|
obs_data_set_string(array_obj, "transition",
|
||||||
|
transitionName);
|
||||||
|
obs_data_set_string(
|
||||||
|
array_obj, "time",
|
||||||
|
s.time.toString().toStdString().c_str());
|
||||||
|
obs_data_array_push_back(timeArray, array_obj);
|
||||||
|
}
|
||||||
|
obs_source_release(sceneSource);
|
||||||
|
obs_source_release(transition);
|
||||||
|
|
||||||
|
obs_data_release(array_obj);
|
||||||
|
}
|
||||||
|
obs_data_set_array(obj, "timeSwitches", timeArray);
|
||||||
|
obs_data_array_release(timeArray);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SwitcherData::loadTimeSwitches(obs_data_t *obj)
|
||||||
|
{
|
||||||
|
switcher->timeSwitches.clear();
|
||||||
|
|
||||||
|
obs_data_array_t *timeArray = obs_data_get_array(obj, "timeSwitches");
|
||||||
|
size_t count = obs_data_array_count(timeArray);
|
||||||
|
|
||||||
|
for (size_t i = 0; i < count; i++) {
|
||||||
|
obs_data_t *array_obj = obs_data_array_item(timeArray, i);
|
||||||
|
|
||||||
|
const char *scene = obs_data_get_string(array_obj, "scene");
|
||||||
|
const char *transition =
|
||||||
|
obs_data_get_string(array_obj, "transition");
|
||||||
|
QTime time = QTime::fromString(
|
||||||
|
obs_data_get_string(array_obj, "time"));
|
||||||
|
|
||||||
|
std::string timeSwitchStr =
|
||||||
|
MakeTimeSwitchName(scene, transition, time)
|
||||||
|
.toUtf8()
|
||||||
|
.constData();
|
||||||
|
|
||||||
|
switcher->timeSwitches.emplace_back(
|
||||||
|
GetWeakSourceByName(scene),
|
||||||
|
GetWeakTransitionByName(transition), time,
|
||||||
|
(strcmp(scene, PREVIOUS_SCENE_NAME) == 0),
|
||||||
|
timeSwitchStr);
|
||||||
|
|
||||||
|
obs_data_release(array_obj);
|
||||||
|
}
|
||||||
|
obs_data_array_release(timeArray);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SceneSwitcher::setupTimeTab()
|
||||||
|
{
|
||||||
|
populateSceneSelection(ui->timeScenes, true);
|
||||||
|
populateTransitionSelection(ui->timeTransitions);
|
||||||
|
|
||||||
|
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.time);
|
||||||
|
|
||||||
|
QListWidgetItem *item =
|
||||||
|
new QListWidgetItem(listText, ui->timeSwitches);
|
||||||
|
item->setData(Qt::UserRole, listText);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,12 +1,12 @@
|
|||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
#include <util/platform.h>
|
#include <util/platform.h>
|
||||||
#include "advanced-scene-switcher.hpp"
|
#include "../headers/advanced-scene-switcher.hpp"
|
||||||
#include <TlHelp32.h>
|
#include <TlHelp32.h>
|
||||||
#include <Psapi.h>
|
#include <Psapi.h>
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
static bool GetWindowTitle(HWND window, string& title)
|
static bool GetWindowTitle(HWND window, string &title)
|
||||||
{
|
{
|
||||||
size_t len = (size_t)GetWindowTextLengthW(window);
|
size_t len = (size_t)GetWindowTextLengthW(window);
|
||||||
wstring wtitle;
|
wstring wtitle;
|
||||||
@@ -45,12 +45,13 @@ static bool WindowValid(HWND window)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void GetWindowList(vector<string>& windows)
|
void GetWindowList(vector<string> &windows)
|
||||||
{
|
{
|
||||||
|
windows.resize(0);
|
||||||
|
|
||||||
HWND window = GetWindow(GetDesktopWindow(), GW_CHILD);
|
HWND window = GetWindow(GetDesktopWindow(), GW_CHILD);
|
||||||
|
|
||||||
while (window)
|
while (window) {
|
||||||
{
|
|
||||||
string title;
|
string title;
|
||||||
if (WindowValid(window) && GetWindowTitle(window, title))
|
if (WindowValid(window) && GetWindowTitle(window, title))
|
||||||
windows.emplace_back(title);
|
windows.emplace_back(title);
|
||||||
@@ -58,59 +59,95 @@ void GetWindowList(vector<string>& windows)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void GetCurrentWindowTitle(string& title)
|
// Overloaded
|
||||||
|
void GetWindowList(QStringList &windows)
|
||||||
|
{
|
||||||
|
windows.clear();
|
||||||
|
|
||||||
|
HWND window = GetWindow(GetDesktopWindow(), GW_CHILD);
|
||||||
|
|
||||||
|
while (window) {
|
||||||
|
string title;
|
||||||
|
if (WindowValid(window) && GetWindowTitle(window, title))
|
||||||
|
windows << QString::fromStdString(title);
|
||||||
|
window = GetNextWindow(window, GW_HWNDNEXT);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void GetCurrentWindowTitle(string &title)
|
||||||
{
|
{
|
||||||
HWND window = GetForegroundWindow();
|
HWND window = GetForegroundWindow();
|
||||||
DWORD id;
|
DWORD pid;
|
||||||
GetWindowThreadProcessId(window, &id);
|
DWORD thid;
|
||||||
|
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.Instead,
|
||||||
it waits for the thread that created the control to process a WM_GETTEXT message.
|
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 (id == GetCurrentProcessId()) {
|
if (GetCurrentProcessId() == pid) {
|
||||||
title = "";
|
title = "OBS";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
GetWindowTitle(window, title);
|
GetWindowTitle(window, title);
|
||||||
}
|
}
|
||||||
|
|
||||||
pair<int, int> getCursorPos()
|
std::pair<int, int> getCursorPos()
|
||||||
{
|
{
|
||||||
pair<int, int> pos(0, 0);
|
std::pair<int, int> pos(0, 0);
|
||||||
POINT cursorPos;
|
POINT cursorPos;
|
||||||
if (GetPhysicalCursorPos(&cursorPos))
|
if (GetPhysicalCursorPos(&cursorPos)) {
|
||||||
{
|
|
||||||
pos.first = cursorPos.x;
|
pos.first = cursorPos.x;
|
||||||
pos.second = cursorPos.y;
|
pos.second = cursorPos.y;
|
||||||
}
|
}
|
||||||
return pos;
|
return pos;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isFullscreen()
|
bool isFullscreen(std::string &title)
|
||||||
{
|
{
|
||||||
RECT appBounds;
|
RECT appBounds;
|
||||||
RECT rc;
|
MONITORINFO monitorInfo = {0};
|
||||||
GetWindowRect(GetDesktopWindow(), &rc);
|
|
||||||
HWND hwnd = GetForegroundWindow();
|
HWND hwnd = GetWindow(GetDesktopWindow(), GW_CHILD);
|
||||||
if (hwnd != GetDesktopWindow() && hwnd != GetShellWindow())
|
while (hwnd) {
|
||||||
{
|
string wtitle;
|
||||||
|
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);
|
||||||
|
GetMonitorInfo(MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST),
|
||||||
|
&monitorInfo);
|
||||||
|
|
||||||
|
if (hwnd && hwnd != GetDesktopWindow() && hwnd != GetShellWindow()) {
|
||||||
|
if (IsZoomed(hwnd)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
GetWindowRect(hwnd, &appBounds);
|
GetWindowRect(hwnd, &appBounds);
|
||||||
if (rc.bottom == appBounds.bottom && rc.top == appBounds.top && rc.left == appBounds.left
|
if (monitorInfo.rcMonitor.bottom == appBounds.bottom &&
|
||||||
&& rc.right == appBounds.right)
|
monitorInfo.rcMonitor.top == appBounds.top &&
|
||||||
{
|
monitorInfo.rcMonitor.left == appBounds.left &&
|
||||||
|
monitorInfo.rcMonitor.right == appBounds.right) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void GetProcessList(QStringList &processes) {
|
void GetProcessList(QStringList &processes)
|
||||||
|
{
|
||||||
|
|
||||||
HANDLE procSnapshot;
|
HANDLE procSnapshot;
|
||||||
PROCESSENTRY32 procEntry;
|
PROCESSENTRY32 procEntry;
|
||||||
|
|
||||||
procSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
|
procSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
|
||||||
if (procSnapshot == INVALID_HANDLE_VALUE) return;
|
if (procSnapshot == INVALID_HANDLE_VALUE)
|
||||||
|
return;
|
||||||
|
|
||||||
procEntry.dwSize = sizeof(PROCESSENTRY32);
|
procEntry.dwSize = sizeof(PROCESSENTRY32);
|
||||||
|
|
||||||
@@ -121,30 +158,45 @@ void GetProcessList(QStringList &processes) {
|
|||||||
|
|
||||||
do {
|
do {
|
||||||
QString tempexe = QString::fromWCharArray(procEntry.szExeFile);
|
QString tempexe = QString::fromWCharArray(procEntry.szExeFile);
|
||||||
if (tempexe == "System") continue;
|
if (tempexe == "System")
|
||||||
if (tempexe == "[System Process]") continue;
|
continue;
|
||||||
if (processes.contains(tempexe)) continue;
|
if (tempexe == "[System Process]")
|
||||||
|
continue;
|
||||||
|
if (processes.contains(tempexe))
|
||||||
|
continue;
|
||||||
processes.append(tempexe);
|
processes.append(tempexe);
|
||||||
} while (Process32Next(procSnapshot, &procEntry));
|
} while (Process32Next(procSnapshot, &procEntry));
|
||||||
|
|
||||||
CloseHandle(procSnapshot);
|
CloseHandle(procSnapshot);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isInFocus(const QString &exeToCheck) {
|
bool isInFocus(const QString &executable)
|
||||||
|
{
|
||||||
// only checks if the current foreground window is from the same executable,
|
// only checks if the current foreground window is from the same executable,
|
||||||
// may return true for incorrent not meant windows from a program
|
// may return true for any window from a program
|
||||||
HWND foregroundWindow = GetForegroundWindow();
|
HWND foregroundWindow = GetForegroundWindow();
|
||||||
DWORD processId = 0;
|
DWORD processId = 0;
|
||||||
GetWindowThreadProcessId(foregroundWindow, &processId);
|
GetWindowThreadProcessId(foregroundWindow, &processId);
|
||||||
|
|
||||||
HANDLE process = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, processId);
|
HANDLE process = OpenProcess(
|
||||||
if (process == NULL) return false;
|
PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, processId);
|
||||||
|
if (process == NULL)
|
||||||
|
return false;
|
||||||
|
|
||||||
WCHAR executablePath[600];
|
WCHAR executablePath[600];
|
||||||
GetModuleFileNameEx(process, 0, executablePath, 600);
|
GetModuleFileNameEx(process, 0, executablePath, 600);
|
||||||
CloseHandle(process);
|
CloseHandle(process);
|
||||||
|
|
||||||
return exeToCheck == QString::fromWCharArray(executablePath).split(QRegExp("(/|\\\\)")).back();
|
QString file = QString::fromWCharArray(executablePath)
|
||||||
|
.split(QRegularExpression("(/|\\\\)"))
|
||||||
|
.back();
|
||||||
|
|
||||||
|
// True if executable switch equals current window
|
||||||
|
bool equals = (executable == file);
|
||||||
|
// True if executable switch matches current window
|
||||||
|
bool matches = file.contains(QRegularExpression(executable));
|
||||||
|
|
||||||
|
return (equals || matches);
|
||||||
}
|
}
|
||||||
|
|
||||||
int getLastInputTime()
|
int getLastInputTime()
|
||||||
432
src/window-title-switch.cpp
Normal file
432
src/window-title-switch.cpp
Normal file
@@ -0,0 +1,432 @@
|
|||||||
|
#include <obs-module.h>
|
||||||
|
#include "headers/advanced-scene-switcher.hpp"
|
||||||
|
|
||||||
|
bool isRunning(std::string &title)
|
||||||
|
{
|
||||||
|
QStringList windows;
|
||||||
|
|
||||||
|
GetWindowList(windows);
|
||||||
|
// True if switch is running (direct)
|
||||||
|
bool equals = windows.contains(QString::fromStdString(title));
|
||||||
|
// True if switch is running (regex)
|
||||||
|
bool matches = (windows.indexOf(QRegularExpression(
|
||||||
|
QString::fromStdString(title))) != -1);
|
||||||
|
|
||||||
|
return (equals || matches);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isFocused(std::string &title)
|
||||||
|
{
|
||||||
|
std::string current;
|
||||||
|
|
||||||
|
GetCurrentWindowTitle(current);
|
||||||
|
// True if switch equals current window
|
||||||
|
bool equals = (title == current);
|
||||||
|
// True if switch matches current window
|
||||||
|
bool matches = QString::fromStdString(current).contains(
|
||||||
|
QRegularExpression(QString::fromStdString(title)));
|
||||||
|
|
||||||
|
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 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 = MakeSwitchName(sceneName, windowName, transitionName,
|
||||||
|
fullscreen, 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, 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.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->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,
|
||||||
|
OBSWeakSource &transition)
|
||||||
|
{
|
||||||
|
std::string title;
|
||||||
|
bool ignored = false;
|
||||||
|
|
||||||
|
// Check if current window is ignored
|
||||||
|
GetCurrentWindowTitle(title);
|
||||||
|
for (auto &window : ignoreWindowsSwitches) {
|
||||||
|
// True if ignored switch equals current window
|
||||||
|
bool equals = (title == window);
|
||||||
|
// True if ignored switch matches current window
|
||||||
|
bool matches = QString::fromStdString(title).contains(
|
||||||
|
QRegularExpression(QString::fromStdString(window)));
|
||||||
|
|
||||||
|
if (equals || matches) {
|
||||||
|
ignored = true;
|
||||||
|
title = lastTitle;
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
lastTitle = title;
|
||||||
|
|
||||||
|
// Check for match
|
||||||
|
for (WindowSceneSwitch &s : windowSwitches) {
|
||||||
|
// True if fullscreen is disabled OR current window is fullscreen
|
||||||
|
bool fullscreen = (!s.fullscreen || isFullscreen(s.window));
|
||||||
|
// True if focus is disabled OR switch is focused
|
||||||
|
bool focus = (!s.focus || isFocused(s.window));
|
||||||
|
// True if current window is ignored AND switch equals OR matches last window
|
||||||
|
bool ignore =
|
||||||
|
(ignored &&
|
||||||
|
(title == s.window ||
|
||||||
|
QString::fromStdString(title).contains(
|
||||||
|
QRegularExpression(
|
||||||
|
QString::fromStdString(s.window)))));
|
||||||
|
|
||||||
|
if (isRunning(s.window) && (fullscreen && (focus || ignore))) {
|
||||||
|
match = true;
|
||||||
|
scene = s.scene;
|
||||||
|
transition = s.transition;
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SwitcherData::saveWindowTitleSwitches(obs_data_t *obj)
|
||||||
|
{
|
||||||
|
obs_data_array_t *windowTitleArray = obs_data_array_create();
|
||||||
|
for (WindowSceneSwitch &s : switcher->windowSwitches) {
|
||||||
|
obs_data_t *array_obj = obs_data_create();
|
||||||
|
|
||||||
|
obs_source_t *source = obs_weak_source_get_source(s.scene);
|
||||||
|
obs_source_t *transition =
|
||||||
|
obs_weak_source_get_source(s.transition);
|
||||||
|
if (source && transition) {
|
||||||
|
const char *sceneName = obs_source_get_name(source);
|
||||||
|
const char *transitionName =
|
||||||
|
obs_source_get_name(transition);
|
||||||
|
obs_data_set_string(array_obj, "scene", sceneName);
|
||||||
|
obs_data_set_string(array_obj, "transition",
|
||||||
|
transitionName);
|
||||||
|
obs_data_set_string(array_obj, "window_title",
|
||||||
|
s.window.c_str());
|
||||||
|
obs_data_set_bool(array_obj, "fullscreen",
|
||||||
|
s.fullscreen);
|
||||||
|
obs_data_set_bool(array_obj, "focus", s.focus);
|
||||||
|
obs_data_array_push_back(windowTitleArray, array_obj);
|
||||||
|
obs_source_release(source);
|
||||||
|
obs_source_release(transition);
|
||||||
|
}
|
||||||
|
obs_data_release(array_obj);
|
||||||
|
}
|
||||||
|
obs_data_set_array(obj, "switches", windowTitleArray);
|
||||||
|
obs_data_array_release(windowTitleArray);
|
||||||
|
|
||||||
|
obs_data_array_t *ignoreWindowsArray = obs_data_array_create();
|
||||||
|
for (std::string &window : switcher->ignoreWindowsSwitches) {
|
||||||
|
obs_data_t *array_obj = obs_data_create();
|
||||||
|
obs_data_set_string(array_obj, "ignoreWindow", window.c_str());
|
||||||
|
obs_data_array_push_back(ignoreWindowsArray, array_obj);
|
||||||
|
obs_data_release(array_obj);
|
||||||
|
}
|
||||||
|
obs_data_set_array(obj, "ignoreWindows", ignoreWindowsArray);
|
||||||
|
obs_data_array_release(ignoreWindowsArray);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SwitcherData::loadWindowTitleSwitches(obs_data_t *obj)
|
||||||
|
{
|
||||||
|
switcher->windowSwitches.clear();
|
||||||
|
|
||||||
|
obs_data_array_t *windowTitleArray =
|
||||||
|
obs_data_get_array(obj, "switches");
|
||||||
|
size_t count = obs_data_array_count(windowTitleArray);
|
||||||
|
|
||||||
|
for (size_t i = 0; i < count; i++) {
|
||||||
|
obs_data_t *array_obj =
|
||||||
|
obs_data_array_item(windowTitleArray, i);
|
||||||
|
|
||||||
|
const char *scene = obs_data_get_string(array_obj, "scene");
|
||||||
|
const char *transition =
|
||||||
|
obs_data_get_string(array_obj, "transition");
|
||||||
|
const char *window =
|
||||||
|
obs_data_get_string(array_obj, "window_title");
|
||||||
|
bool fullscreen = obs_data_get_bool(array_obj, "fullscreen");
|
||||||
|
bool focus = obs_data_get_bool(array_obj, "focus") ||
|
||||||
|
!obs_data_has_user_value(array_obj, "focus");
|
||||||
|
|
||||||
|
switcher->windowSwitches.emplace_back(
|
||||||
|
GetWeakSourceByName(scene), window,
|
||||||
|
GetWeakTransitionByName(transition), fullscreen, focus);
|
||||||
|
|
||||||
|
obs_data_release(array_obj);
|
||||||
|
}
|
||||||
|
obs_data_array_release(windowTitleArray);
|
||||||
|
|
||||||
|
switcher->ignoreWindowsSwitches.clear();
|
||||||
|
|
||||||
|
obs_data_array_t *ignoreWindowsArray =
|
||||||
|
obs_data_get_array(obj, "ignoreWindows");
|
||||||
|
count = obs_data_array_count(ignoreWindowsArray);
|
||||||
|
|
||||||
|
for (size_t i = 0; i < count; i++) {
|
||||||
|
obs_data_t *array_obj =
|
||||||
|
obs_data_array_item(ignoreWindowsArray, i);
|
||||||
|
|
||||||
|
const char *window =
|
||||||
|
obs_data_get_string(array_obj, "ignoreWindow");
|
||||||
|
|
||||||
|
switcher->ignoreWindowsSwitches.emplace_back(window);
|
||||||
|
|
||||||
|
obs_data_release(array_obj);
|
||||||
|
}
|
||||||
|
obs_data_array_release(ignoreWindowsArray);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SceneSwitcher::setupTitleTab()
|
||||||
|
{
|
||||||
|
populateSceneSelection(ui->scenes, false);
|
||||||
|
populateTransitionSelection(ui->transitions);
|
||||||
|
populateWindowSelection(ui->windows);
|
||||||
|
populateWindowSelection(ui->ignoreWindowsWindows);
|
||||||
|
|
||||||
|
for (auto &s : switcher->windowSwitches) {
|
||||||
|
std::string sceneName = GetWeakSourceName(s.scene);
|
||||||
|
std::string transitionName = GetWeakSourceName(s.transition);
|
||||||
|
QString text = MakeSwitchName(sceneName.c_str(),
|
||||||
|
s.window.c_str(),
|
||||||
|
transitionName.c_str(),
|
||||||
|
s.fullscreen, s.focus);
|
||||||
|
|
||||||
|
QListWidgetItem *item = new QListWidgetItem(text, ui->switches);
|
||||||
|
item->setData(Qt::UserRole, s.window.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto &window : switcher->ignoreWindowsSwitches) {
|
||||||
|
QString text = QString::fromStdString(window);
|
||||||
|
|
||||||
|
QListWidgetItem *item =
|
||||||
|
new QListWidgetItem(text, ui->ignoreWindows);
|
||||||
|
item->setData(Qt::UserRole, text);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,288 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
#include <condition_variable>
|
|
||||||
#include <chrono>
|
|
||||||
#include <string>
|
|
||||||
#include <vector>
|
|
||||||
#include <thread>
|
|
||||||
#include <regex>
|
|
||||||
#include <mutex>
|
|
||||||
#include <fstream>
|
|
||||||
#include "utility.hpp"
|
|
||||||
|
|
||||||
#define DEFAULT_INTERVAL 300
|
|
||||||
|
|
||||||
#define READ_FILE_FUNC "File based"
|
|
||||||
#define ROUND_TRIP_FUNC "Scene Sequence"
|
|
||||||
#define IDLE_FUNC "Idle detection"
|
|
||||||
#define EXE_FUNC "Executable"
|
|
||||||
#define SCREEN_REGION_FUNC "Screen region"
|
|
||||||
#define WINDOW_TITLE_FUNC "Window title"
|
|
||||||
|
|
||||||
#define DEFAULT_PRIORITY_0 READ_FILE_FUNC
|
|
||||||
#define DEFAULT_PRIORITY_1 ROUND_TRIP_FUNC
|
|
||||||
#define DEFAULT_PRIORITY_2 IDLE_FUNC
|
|
||||||
#define DEFAULT_PRIORITY_3 EXE_FUNC
|
|
||||||
#define DEFAULT_PRIORITY_4 SCREEN_REGION_FUNC
|
|
||||||
#define DEFAULT_PRIORITY_5 WINDOW_TITLE_FUNC
|
|
||||||
|
|
||||||
using namespace std;
|
|
||||||
|
|
||||||
struct SceneSwitch
|
|
||||||
{
|
|
||||||
OBSWeakSource scene;
|
|
||||||
string window;
|
|
||||||
OBSWeakSource transition;
|
|
||||||
bool fullscreen;
|
|
||||||
|
|
||||||
inline SceneSwitch(
|
|
||||||
OBSWeakSource scene_, const char* window_, OBSWeakSource transition_, bool fullscreen_)
|
|
||||||
: scene(scene_)
|
|
||||||
, window(window_)
|
|
||||||
, transition(transition_)
|
|
||||||
, fullscreen(fullscreen_)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct ExecutableSceneSwitch
|
|
||||||
{
|
|
||||||
OBSWeakSource mScene;
|
|
||||||
OBSWeakSource mTransition;
|
|
||||||
QString mExe;
|
|
||||||
bool mInFocus;
|
|
||||||
|
|
||||||
inline ExecutableSceneSwitch(
|
|
||||||
OBSWeakSource scene, OBSWeakSource transition, const QString& exe, bool inFocus)
|
|
||||||
: mScene(scene)
|
|
||||||
, mTransition(transition)
|
|
||||||
, mExe(exe)
|
|
||||||
, mInFocus(inFocus)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct ScreenRegionSwitch
|
|
||||||
{
|
|
||||||
OBSWeakSource scene;
|
|
||||||
OBSWeakSource transition;
|
|
||||||
int minX, minY, maxX, maxY;
|
|
||||||
string regionStr;
|
|
||||||
|
|
||||||
inline ScreenRegionSwitch(OBSWeakSource scene_, OBSWeakSource transition_, int minX_, int minY_,
|
|
||||||
int maxX_, int maxY_, string regionStr_)
|
|
||||||
: scene(scene_)
|
|
||||||
, transition(transition_)
|
|
||||||
, minX(minX_)
|
|
||||||
, minY(minY_)
|
|
||||||
, maxX(maxX_)
|
|
||||||
, maxY(maxY_)
|
|
||||||
, regionStr(regionStr_)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct SceneRoundTripSwitch
|
|
||||||
{
|
|
||||||
OBSWeakSource scene1;
|
|
||||||
OBSWeakSource scene2;
|
|
||||||
OBSWeakSource transition;
|
|
||||||
int delay;
|
|
||||||
string sceneRoundTripStr;
|
|
||||||
|
|
||||||
inline SceneRoundTripSwitch(OBSWeakSource scene1_, OBSWeakSource scene2_,
|
|
||||||
OBSWeakSource transition_, int delay_, string str)
|
|
||||||
: scene1(scene1_)
|
|
||||||
, scene2(scene2_)
|
|
||||||
, transition(transition_)
|
|
||||||
, delay(delay_)
|
|
||||||
, sceneRoundTripStr(str)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct SceneTransition
|
|
||||||
{
|
|
||||||
OBSWeakSource scene1;
|
|
||||||
OBSWeakSource scene2;
|
|
||||||
OBSWeakSource transition;
|
|
||||||
string sceneTransitionStr;
|
|
||||||
|
|
||||||
inline SceneTransition(OBSWeakSource scene1_, OBSWeakSource scene2_, OBSWeakSource transition_,
|
|
||||||
string sceneTransitionStr_)
|
|
||||||
: scene1(scene1_)
|
|
||||||
, scene2(scene2_)
|
|
||||||
, transition(transition_)
|
|
||||||
, sceneTransitionStr(sceneTransitionStr_)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct DefaultSceneTransition
|
|
||||||
{
|
|
||||||
OBSWeakSource scene;
|
|
||||||
OBSWeakSource transition;
|
|
||||||
string sceneTransitionStr;
|
|
||||||
|
|
||||||
inline DefaultSceneTransition(OBSWeakSource scene_, OBSWeakSource transition_,
|
|
||||||
string sceneTransitionStr_)
|
|
||||||
: scene(scene_)
|
|
||||||
, transition(transition_)
|
|
||||||
, sceneTransitionStr(sceneTransitionStr_)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct FileIOData
|
|
||||||
{
|
|
||||||
bool readEnabled;
|
|
||||||
string readPath;
|
|
||||||
bool writeEnabled;
|
|
||||||
string writePath;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct IdleData
|
|
||||||
{
|
|
||||||
bool idleEnable;
|
|
||||||
int time;
|
|
||||||
OBSWeakSource scene;
|
|
||||||
OBSWeakSource transition;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
struct SwitcherData
|
|
||||||
{
|
|
||||||
thread th;
|
|
||||||
condition_variable cv;
|
|
||||||
mutex m;
|
|
||||||
mutex transitionMutex;
|
|
||||||
bool transitionActive = false;
|
|
||||||
bool waitForTransition = false;
|
|
||||||
condition_variable transitionCv;
|
|
||||||
bool stop = false;
|
|
||||||
|
|
||||||
vector<SceneSwitch> switches;
|
|
||||||
string lastTitle;
|
|
||||||
OBSWeakSource nonMatchingScene;
|
|
||||||
int interval = DEFAULT_INTERVAL;
|
|
||||||
bool switchIfNotMatching = false;
|
|
||||||
bool startAtLaunch = false;
|
|
||||||
vector<ScreenRegionSwitch> screenRegionSwitches;
|
|
||||||
vector<OBSWeakSource> pauseScenesSwitches;
|
|
||||||
vector<string> pauseWindowsSwitches;
|
|
||||||
vector<string> ignoreWindowsSwitches;
|
|
||||||
vector<SceneRoundTripSwitch> sceneRoundTripSwitches;
|
|
||||||
bool autoStopEnable = false;
|
|
||||||
OBSWeakSource autoStopScene;
|
|
||||||
string waitSceneName; //indicates which scene was active when we startet waiting on something
|
|
||||||
vector<SceneTransition> sceneTransitions;
|
|
||||||
vector<DefaultSceneTransition> defaultSceneTransitions;
|
|
||||||
vector<ExecutableSceneSwitch> executableSwitches;
|
|
||||||
FileIOData fileIO;
|
|
||||||
IdleData idleData;
|
|
||||||
vector<string> ignoreIdleWindows;
|
|
||||||
vector<string> functionNamesByPriority = vector<string>{
|
|
||||||
string(DEFAULT_PRIORITY_0),
|
|
||||||
string(DEFAULT_PRIORITY_1),
|
|
||||||
string(DEFAULT_PRIORITY_2),
|
|
||||||
string(DEFAULT_PRIORITY_3),
|
|
||||||
string(DEFAULT_PRIORITY_4),
|
|
||||||
string(DEFAULT_PRIORITY_5),
|
|
||||||
};
|
|
||||||
|
|
||||||
void Thread();
|
|
||||||
void Start();
|
|
||||||
void Stop();
|
|
||||||
|
|
||||||
bool sceneChangedDuringWait();
|
|
||||||
bool prioFuncsValid();
|
|
||||||
void writeSceneInfoToFile();
|
|
||||||
void setDefaultSceneTransitions(unique_lock<mutex>& lock);
|
|
||||||
void autoStopStreamAndRecording();
|
|
||||||
bool checkPause();
|
|
||||||
void checkSceneRoundTrip(bool& match, OBSWeakSource& scene, OBSWeakSource& transition, unique_lock<mutex>& lock);
|
|
||||||
void checkIdleSwitch(bool& match, OBSWeakSource& scene, OBSWeakSource& transition);
|
|
||||||
void checkWindowTitleSwitch(bool& match, OBSWeakSource& scene, OBSWeakSource& transition);
|
|
||||||
void checkExeSwitch(bool& match, OBSWeakSource& scene, OBSWeakSource& transition);
|
|
||||||
void checkScreenRegionSwitch(bool& match, OBSWeakSource& scene, OBSWeakSource& transition);
|
|
||||||
void checkSwitchInfoFromFile(bool& match, OBSWeakSource& scene, OBSWeakSource& transition);
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void Prune()
|
|
||||||
{
|
|
||||||
for (size_t i = 0; i < switches.size(); i++)
|
|
||||||
{
|
|
||||||
SceneSwitch& s = switches[i];
|
|
||||||
if (!WeakSourceValid(s.scene) || !WeakSourceValid(s.transition))
|
|
||||||
switches.erase(switches.begin() + i--);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (nonMatchingScene && !WeakSourceValid(nonMatchingScene))
|
|
||||||
{
|
|
||||||
switchIfNotMatching = false;
|
|
||||||
nonMatchingScene = nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (size_t i = 0; i < screenRegionSwitches.size(); i++)
|
|
||||||
{
|
|
||||||
ScreenRegionSwitch& s = screenRegionSwitches[i];
|
|
||||||
if (!WeakSourceValid(s.scene) || !WeakSourceValid(s.transition))
|
|
||||||
screenRegionSwitches.erase(screenRegionSwitches.begin() + i--);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (size_t i = 0; i < pauseScenesSwitches.size(); i++)
|
|
||||||
{
|
|
||||||
OBSWeakSource& scene = pauseScenesSwitches[i];
|
|
||||||
if (!WeakSourceValid(scene))
|
|
||||||
pauseScenesSwitches.erase(pauseScenesSwitches.begin() + i--);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (size_t i = 0; i < sceneRoundTripSwitches.size(); i++)
|
|
||||||
{
|
|
||||||
SceneRoundTripSwitch& s = sceneRoundTripSwitches[i];
|
|
||||||
if (!WeakSourceValid(s.scene1) || !WeakSourceValid(s.scene2)
|
|
||||||
|| !WeakSourceValid(s.transition))
|
|
||||||
sceneRoundTripSwitches.erase(sceneRoundTripSwitches.begin() + i--);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!WeakSourceValid(autoStopScene))
|
|
||||||
{
|
|
||||||
autoStopScene = nullptr;
|
|
||||||
autoStopEnable = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (size_t i = 0; i < sceneTransitions.size(); i++)
|
|
||||||
{
|
|
||||||
SceneTransition& s = sceneTransitions[i];
|
|
||||||
if (!WeakSourceValid(s.scene1) || !WeakSourceValid(s.scene2)
|
|
||||||
|| !WeakSourceValid(s.transition))
|
|
||||||
sceneTransitions.erase(sceneTransitions.begin() + i--);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (size_t i = 0; i < defaultSceneTransitions.size(); i++)
|
|
||||||
{
|
|
||||||
DefaultSceneTransition& s = defaultSceneTransitions[i];
|
|
||||||
if (!WeakSourceValid(s.scene) || !WeakSourceValid(s.transition))
|
|
||||||
defaultSceneTransitions.erase(defaultSceneTransitions.begin() + i--);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (size_t i = 0; i < executableSwitches.size(); i++)
|
|
||||||
{
|
|
||||||
ExecutableSceneSwitch& s = executableSwitches[i];
|
|
||||||
if (!WeakSourceValid(s.mScene) || !WeakSourceValid(s.mTransition))
|
|
||||||
executableSwitches.erase(executableSwitches.begin() + i--);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!WeakSourceValid(idleData.scene) || !WeakSourceValid(idleData.transition))
|
|
||||||
{
|
|
||||||
idleData.idleEnable = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
inline ~SwitcherData()
|
|
||||||
{
|
|
||||||
Stop();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
140
utility.hpp
140
utility.hpp
@@ -1,140 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
#include <obs.hpp>
|
|
||||||
#include <QString>
|
|
||||||
#include <obs-frontend-api.h>
|
|
||||||
#include "switcher-data-structs.hpp"
|
|
||||||
using namespace std;
|
|
||||||
|
|
||||||
static inline bool WeakSourceValid(obs_weak_source_t* ws)
|
|
||||||
{
|
|
||||||
obs_source_t* source = obs_weak_source_get_source(ws);
|
|
||||||
if (source)
|
|
||||||
obs_source_release(source);
|
|
||||||
return !!source;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline QString MakeSwitchName(
|
|
||||||
const QString& scene, const QString& value, const QString& transition, bool fullscreen)
|
|
||||||
{
|
|
||||||
if (!fullscreen)
|
|
||||||
return QStringLiteral("[") + scene + QStringLiteral(", ") + transition
|
|
||||||
+ QStringLiteral("]: ") + value;
|
|
||||||
return QStringLiteral("[") + scene + QStringLiteral(", ") + transition + QStringLiteral("]: ")
|
|
||||||
+ value + QStringLiteral(" (only if window is fullscreen)");
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline QString MakeSwitchNameExecutable(
|
|
||||||
const QString& scene, const QString& value, const QString& transition, bool inFocus)
|
|
||||||
{
|
|
||||||
if (!inFocus)
|
|
||||||
return QStringLiteral("[") + scene + QStringLiteral(", ") + transition
|
|
||||||
+ QStringLiteral("]: ") + value;
|
|
||||||
return QStringLiteral("[") + scene + QStringLiteral(", ") + transition + QStringLiteral("]: ")
|
|
||||||
+ value + QStringLiteral(" (only if window is focused)");
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline QString MakeScreenRegionSwitchName(
|
|
||||||
const QString& scene, const QString& transition, int minX, int minY, int maxX, int maxY)
|
|
||||||
{
|
|
||||||
return QStringLiteral("[") + scene + QStringLiteral(", ") + transition + QStringLiteral("]: ")
|
|
||||||
+ QString::number(minX) + QStringLiteral(", ") + QString::number(minY)
|
|
||||||
+ QStringLiteral(" x ") + QString::number(maxX) + QStringLiteral(", ")
|
|
||||||
+ QString::number(maxY);
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline QString MakeSceneRoundTripSwitchName(
|
|
||||||
const QString& scene1, const QString& scene2, const QString& transition, int delay)
|
|
||||||
{
|
|
||||||
return scene1 + QStringLiteral(" -> wait for ") + QString::number(delay)
|
|
||||||
+ QStringLiteral(" seconds -> ") + scene2 + QStringLiteral(" (using ") + transition
|
|
||||||
+ QStringLiteral(" transition)");
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline QString MakeSceneTransitionName(
|
|
||||||
const QString& scene1, const QString& scene2, const QString& transition)
|
|
||||||
{
|
|
||||||
return scene1 + QStringLiteral(" --- ") + transition + QStringLiteral(" --> ") + scene2;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline QString MakeDefaultSceneTransitionName(
|
|
||||||
const QString& scene, const QString& transition)
|
|
||||||
{
|
|
||||||
return scene + QStringLiteral(" --> ") + transition;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline string GetWeakSourceName(obs_weak_source_t* weak_source)
|
|
||||||
{
|
|
||||||
string name;
|
|
||||||
|
|
||||||
obs_source_t* source = obs_weak_source_get_source(weak_source);
|
|
||||||
if (source)
|
|
||||||
{
|
|
||||||
name = obs_source_get_name(source);
|
|
||||||
obs_source_release(source);
|
|
||||||
}
|
|
||||||
|
|
||||||
return name;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline OBSWeakSource GetWeakSourceByName(const char* name)
|
|
||||||
{
|
|
||||||
OBSWeakSource weak;
|
|
||||||
obs_source_t* source = obs_get_source_by_name(name);
|
|
||||||
if (source)
|
|
||||||
{
|
|
||||||
weak = obs_source_get_weak_source(source);
|
|
||||||
obs_weak_source_release(weak);
|
|
||||||
obs_source_release(source);
|
|
||||||
}
|
|
||||||
|
|
||||||
return weak;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline OBSWeakSource GetWeakSourceByQString(const QString& name)
|
|
||||||
{
|
|
||||||
return GetWeakSourceByName(name.toUtf8().constData());
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline OBSWeakSource GetWeakTransitionByName(const char* transitionName)
|
|
||||||
{
|
|
||||||
OBSWeakSource weak;
|
|
||||||
obs_source_t* source = nullptr;
|
|
||||||
|
|
||||||
if (strcmp(transitionName, "Default") == 0)
|
|
||||||
{
|
|
||||||
source = obs_frontend_get_current_transition();
|
|
||||||
weak = obs_source_get_weak_source(source);
|
|
||||||
obs_source_release(source);
|
|
||||||
obs_weak_source_release(weak);
|
|
||||||
return weak;
|
|
||||||
}
|
|
||||||
|
|
||||||
obs_frontend_source_list* transitions = new obs_frontend_source_list();
|
|
||||||
obs_frontend_get_transitions(transitions);
|
|
||||||
bool match = false;
|
|
||||||
|
|
||||||
for (size_t i = 0; i < transitions->sources.num; i++)
|
|
||||||
{
|
|
||||||
const char* name = obs_source_get_name(transitions->sources.array[i]);
|
|
||||||
if (strcmp(transitionName, name) == 0)
|
|
||||||
{
|
|
||||||
match = true;
|
|
||||||
source = transitions->sources.array[i];
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (match){
|
|
||||||
weak = obs_source_get_weak_source(source);
|
|
||||||
obs_weak_source_release(weak);
|
|
||||||
}
|
|
||||||
obs_frontend_source_list_free(transitions);
|
|
||||||
|
|
||||||
return weak;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline OBSWeakSource GetWeakTransitionByQString(const QString& name)
|
|
||||||
{
|
|
||||||
return GetWeakTransitionByName(name.toUtf8().constData());
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -1,276 +0,0 @@
|
|||||||
#include <obs-module.h>
|
|
||||||
#include "advanced-scene-switcher.hpp"
|
|
||||||
|
|
||||||
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();
|
|
||||||
|
|
||||||
if (windowName.isEmpty() || sceneName.isEmpty())
|
|
||||||
return;
|
|
||||||
|
|
||||||
OBSWeakSource source = GetWeakSourceByQString(sceneName);
|
|
||||||
OBSWeakSource transition = GetWeakTransitionByQString(transitionName);
|
|
||||||
QVariant v = QVariant::fromValue(windowName);
|
|
||||||
|
|
||||||
QString text = MakeSwitchName(sceneName, windowName, transitionName, fullscreen);
|
|
||||||
|
|
||||||
int idx = FindByData(windowName);
|
|
||||||
|
|
||||||
if (idx == -1)
|
|
||||||
{
|
|
||||||
lock_guard<mutex> lock(switcher->m);
|
|
||||||
switcher->switches.emplace_back(
|
|
||||||
source, windowName.toUtf8().constData(), transition, fullscreen);
|
|
||||||
|
|
||||||
QListWidgetItem* item = new QListWidgetItem(text, ui->switches);
|
|
||||||
item->setData(Qt::UserRole, v);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
QListWidgetItem* item = ui->switches->item(idx);
|
|
||||||
item->setText(text);
|
|
||||||
|
|
||||||
string window = windowName.toUtf8().constData();
|
|
||||||
|
|
||||||
{
|
|
||||||
lock_guard<mutex> lock(switcher->m);
|
|
||||||
for (auto& s : switcher->switches)
|
|
||||||
{
|
|
||||||
if (s.window == window)
|
|
||||||
{
|
|
||||||
s.scene = source;
|
|
||||||
s.transition = transition;
|
|
||||||
s.fullscreen = fullscreen;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ui->switches->sortItems();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void SceneSwitcher::on_remove_clicked()
|
|
||||||
{
|
|
||||||
QListWidgetItem* item = ui->switches->currentItem();
|
|
||||||
if (!item)
|
|
||||||
return;
|
|
||||||
|
|
||||||
string window = item->data(Qt::UserRole).toString().toUtf8().constData();
|
|
||||||
|
|
||||||
{
|
|
||||||
lock_guard<mutex> lock(switcher->m);
|
|
||||||
auto& switches = switcher->switches;
|
|
||||||
|
|
||||||
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);
|
|
||||||
|
|
||||||
lock_guard<mutex> lock(switcher->m);
|
|
||||||
switcher->ignoreWindowsSwitches.emplace_back(windowName.toUtf8().constData());
|
|
||||||
ui->ignoreWindows->sortItems();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void SceneSwitcher::on_ignoreWindowsRemove_clicked()
|
|
||||||
{
|
|
||||||
QListWidgetItem* item = ui->ignoreWindows->currentItem();
|
|
||||||
if (!item)
|
|
||||||
return;
|
|
||||||
|
|
||||||
QString windowName = item->data(Qt::UserRole).toString();
|
|
||||||
|
|
||||||
{
|
|
||||||
lock_guard<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();
|
|
||||||
|
|
||||||
lock_guard<mutex> lock(switcher->m);
|
|
||||||
for (auto& s : switcher->switches)
|
|
||||||
{
|
|
||||||
if (window.compare(s.window.c_str()) == 0)
|
|
||||||
{
|
|
||||||
string name = GetWeakSourceName(s.scene);
|
|
||||||
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);
|
|
||||||
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();
|
|
||||||
|
|
||||||
lock_guard<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, OBSWeakSource& transition)
|
|
||||||
{
|
|
||||||
//check if title should be ignored
|
|
||||||
string title;
|
|
||||||
GetCurrentWindowTitle(title);
|
|
||||||
for (auto& window : ignoreWindowsSwitches)
|
|
||||||
{
|
|
||||||
if (window == title)
|
|
||||||
{
|
|
||||||
title = lastTitle;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
lastTitle = title;
|
|
||||||
|
|
||||||
//direct match
|
|
||||||
for (SceneSwitch& s : switches)
|
|
||||||
{
|
|
||||||
if (s.window == title)
|
|
||||||
{
|
|
||||||
match = !s.fullscreen || (s.fullscreen && isFullscreen());
|
|
||||||
scene = s.scene;
|
|
||||||
transition = s.transition;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//regex match
|
|
||||||
for (SceneSwitch& s : switches)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
bool matches = regex_match(title, regex(s.window));
|
|
||||||
if (matches)
|
|
||||||
{
|
|
||||||
match = !s.fullscreen || (s.fullscreen && isFullscreen());
|
|
||||||
scene = s.scene;
|
|
||||||
transition = s.transition;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (const regex_error&)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
Reference in New Issue
Block a user