Compare commits

...

121 Commits

Author SHA1 Message Date
WarmUpTill
438c54599e Reduce time it takes to close preview dialog 2023-01-20 12:30:47 -08:00
WarmUpTill
da42183177 Fix VariableResolvingString returning empty string if no variables exist 2023-01-20 12:30:47 -08:00
WarmUpTill
295fa3f122 Fix crash on import 2023-01-20 12:30:47 -08:00
WarmUpTill
cb87f1cc36 Fix file condition always returning false 2023-01-18 07:49:21 -08:00
WarmUpTill
19eb5074f2 Enable getting variable values from websocket condition 2023-01-17 12:12:44 -08:00
WarmUpTill
828a69bbf7 Save space by reducing size of "current value" text box 2023-01-17 12:12:44 -08:00
WarmUpTill
c01d471b5f Save space by moving settings button to the same layout as stop button 2023-01-15 09:33:46 -08:00
WarmUpTill
8751c0d296 Enable getting variable values from http action 2023-01-15 09:33:46 -08:00
WarmUpTill
334d173dc9 Add PostLoad()
Used to reslove values that only can be resolved after all macros /
macro segments have been set up initally.
2023-01-15 09:33:46 -08:00
WarmUpTill
1c3ff927a5 Enable getting variable values from window condition 2023-01-15 09:33:46 -08:00
WarmUpTill
b12c25fdf4 Enable getting variable values from studio mode condition 2023-01-15 09:33:46 -08:00
WarmUpTill
e9f4ac7ca7 Enable getting variable values from source condition 2023-01-15 09:33:46 -08:00
WarmUpTill
f1cdab80a9 Enable getting variable values from process condition 2023-01-15 09:33:46 -08:00
WarmUpTill
ae6f0bc470 Enable getting variable values from idle condition 2023-01-15 09:33:46 -08:00
WarmUpTill
3d4b69848c Enable getting variable values from filter condition 2023-01-15 09:33:46 -08:00
WarmUpTill
c4c4a27dcb Enable getting variable values from file condition 2023-01-15 09:33:46 -08:00
WarmUpTill
87ed11d3bf Enable getting variable values from date condition 2023-01-15 09:33:46 -08:00
WarmUpTill
23e2fb8d5a Enable getting variable values from cursor condition 2023-01-15 09:33:46 -08:00
WarmUpTill
af5950748d Switch to using RegexConfig for variable comparison check 2023-01-15 09:33:46 -08:00
WarmUpTill
df9dc1e002 Enable getting variable values from scene condition 2023-01-15 09:33:46 -08:00
WarmUpTill
5c7e0f292f Enable getting variable values from audio condition 2023-01-15 09:33:46 -08:00
WarmUpTill
a5f610b7b7 Add options to compare variable values 2023-01-15 09:33:46 -08:00
WarmUpTill
df8e84dd09 Add option to set variable value based on condition or action 2023-01-15 09:33:46 -08:00
WarmUpTill
ee5038106a Add MacroSegmentOrderChanged() signal 2023-01-15 09:33:46 -08:00
WarmUpTill
6617c08b89 Allow choosing highlight color 2023-01-15 09:33:46 -08:00
WarmUpTill
42773790e4 Enable getting varaible values from conditions and actions 2023-01-15 09:33:46 -08:00
WarmUpTill
1fd6f75ee1 Add variable support to video condition 2023-01-15 09:33:46 -08:00
WarmUpTill
8a2ccef8ec Only return source from variable if source is a scene 2023-01-15 09:33:46 -08:00
WarmUpTill
d30f389db7 Add variable support to source condition 2023-01-15 09:33:46 -08:00
WarmUpTill
a2906a534b Add variable support to media condition 2023-01-15 09:33:46 -08:00
WarmUpTill
30294842f2 Add variable support to filter condition 2023-01-15 09:33:46 -08:00
WarmUpTill
eb478d2948 Add variable support to audio condition 2023-01-15 09:33:46 -08:00
WarmUpTill
e4ee55cd3d Add variable support to source action 2023-01-15 09:33:46 -08:00
WarmUpTill
9c4fe38c97 Add variable support for screenshot action 2023-01-15 09:33:46 -08:00
WarmUpTill
81e64382c9 Add variable support to media action 2023-01-15 09:33:46 -08:00
WarmUpTill
b55db1a79a Add variable support to filter action 2023-01-15 09:33:46 -08:00
WarmUpTill
dcdcd23361 Add variable support to audio action 2023-01-15 09:33:46 -08:00
WarmUpTill
3ccc057547 Clean up utilty 2023-01-15 09:33:46 -08:00
WarmUpTill
d04884fe8b Resolve variables in log messages 2023-01-15 09:33:46 -08:00
WarmUpTill
7ae490ca25 Add option to resolve variable values in ToString() 2023-01-15 09:33:46 -08:00
WarmUpTill
cd74534e4c Add source selection helpers 2023-01-15 09:33:46 -08:00
WarmUpTill
e4f9ccec3f Add support for variables in text edit widgets of actions and conditions 2023-01-15 09:33:46 -08:00
WarmUpTill
f106329892 Enable use of multi-line values for variables 2023-01-15 09:33:46 -08:00
WarmUpTill
14f0194372 Add helper classes to support strings containing variables
* VariableResolvingString will accept strings potentially containing
   variables as inputs and will automatically resolve any variables when
   converting to std::string
 * VariableTextEdit is a simple wrapper around ResizingPlainTextEdit to
   enable working with VariableResolvingString
2023-01-15 09:33:46 -08:00
WarmUpTill
632ad5085f Remove duplicate "is" 2023-01-15 09:33:21 -08:00
WarmUpTill
fafaafd578 Fix crash when showing frame for cursor condition
Extreme values for setGeometry() could cause a crash.
Limit frame size to screen union to avoid these crashes.
2023-01-14 05:09:42 -08:00
WarmUpTill
e01f28f2bf Create archives for manual install on MacOS 2023-01-13 15:05:21 -08:00
WarmUpTill
fd4476c0b3 Fix warnings 2023-01-07 11:49:53 -08:00
WarmUpTill
c96c81cf1d Update macro properties dialog
* Visually group settings
 * Hide hotkey settings if no macro or a group is selected
2023-01-07 11:49:53 -08:00
WarmUpTill
3e4293f9b0 Set background of macro action / condition splitter transparent 2023-01-07 11:49:53 -08:00
WarmUpTill
a80af327d7 Enable grouping of macros
Based on OBS's source-tree model/view implementation
2023-01-07 11:49:53 -08:00
WarmUpTill
2116bd7a78 Adjust function signature for macro rename signal 2023-01-07 11:49:53 -08:00
WarmUpTill
11571751b9 Fix crash related to highlights
A crash could occur when macro segments would be deleted while
highlighting of a segment was triggered.
2023-01-07 11:49:53 -08:00
WarmUpTill
f9de63bc88 Rename "count" to "runCount" 2023-01-07 11:49:53 -08:00
WarmUpTill
378bf4dfd6 Add button to open settings window to dock
Also apply current OBS theme to status dock by using "OBSDock" wrapper
class
2023-01-07 11:49:53 -08:00
WarmUpTill
5444d6fd9e Adjust locale 2023-01-02 08:38:25 -08:00
WarmUpTill
15d7e2f972 Add option to check audio balance 2022-12-27 04:02:08 -08:00
WarmUpTill
357d9447f2 Add option to set audio balance 2022-12-27 04:02:08 -08:00
WarmUpTill
6c9d6095c9 Move slider spinbox to core lib 2022-12-27 04:02:08 -08:00
WarmUpTill
bba029e901 Only show audio monitoring options if monitoring is supported by OBS 2022-12-27 04:02:08 -08:00
WarmUpTill
4eafbb8f18 Improve plugin state action
Add options to check for ...
 * Initial plugin start
 * Plugin restart
 * Scene collection change
2022-12-27 03:55:40 -08:00
WarmUpTill
e147402250 Improve random action
* Allow duplicates to enable weighting macro occurance
 * Add option to allow consecutive execution of the same macro
2022-12-23 15:50:17 -08:00
WarmUpTill
e0602f8de2 Rename function and class 2022-12-23 14:10:37 -08:00
WarmUpTill
11f769cf09 Add option to check for change in visibilty 2022-12-23 14:10:37 -08:00
WarmUpTill
4ab88ea218 Add option to not reset seletion on any / all index type change 2022-12-23 14:10:37 -08:00
WarmUpTill
20cbe636df Update locale 2022-12-23 14:09:26 -08:00
WarmUpTill
601b035285 Fall back to OBS-only key press if global key press can't be simulated 2022-12-23 13:18:45 -08:00
WarmUpTill
0027ae9bb8 Reuse last import path 2022-12-23 13:17:58 -08:00
WarmUpTill
ffccc435ca Use unicode as chracter set for lib build 2022-12-23 13:17:58 -08:00
WarmUpTill
8d6b9e37d5 Prioritize manually specified variables 2022-12-23 13:17:58 -08:00
WarmUpTill
70c9a3bd83 Improve video condition and preview dialog
* Fix crashes in preview dialog
 * Pass copy of parameters instead of working directly with condition
   data
 * Set sensible default paths in file selection dialogs
 * Add maximize button to preview dialog
 * Derive preview dialog size from scene switcher settings window
 * Use parameter helper classes
2022-12-23 13:16:22 -08:00
WarmUpTill
b7c6deddde Add helper classes to more easily handle video condition parameters 2022-12-23 13:16:22 -08:00
WarmUpTill
feab4fe744 Add ValidPathOrDesktop() helper function 2022-12-23 13:16:22 -08:00
WarmUpTill
b5ec9e7586 Add const qualifier to various functions 2022-12-23 13:16:22 -08:00
WarmUpTill
a901f59f0f Add option to interact with source settings buttons 2022-12-21 11:52:48 -08:00
WarmUpTill
ea826f1be0 Add option to check for mouse button press
Implemented only for Windows for now.
2022-12-21 11:52:36 -08:00
WarmUpTill
f7afb9c446 Cleanup
* Add include
* Fix typo
2022-12-21 11:52:36 -08:00
WarmUpTill
42198a8773 Update locale 2022-12-18 11:02:09 -08:00
WarmUpTill
368034ad5a Combine hotkeys with the same description into a single hotkey 2022-12-15 11:46:38 -08:00
WarmUpTill
0d1a3e5646 Add missing virtual destructors 2022-12-15 11:46:38 -08:00
WarmUpTill
76be75474b Set default path for import to desktop folder 2022-12-15 11:46:38 -08:00
WarmUpTill
cca32e59c2 Do not allow running paused macros
The old behaviour was not consistent with the other action types.
(E.g. "Sequence" or "Random" ignore paused macros)
It would also only ever execute the first action of the given macro as
afterwards the plugin would realise the macro is paused and abort the
execution.
2022-12-10 13:06:55 -08:00
WarmUpTill
85bb0e62dc Clean up locale 2022-12-10 13:06:19 -08:00
WarmUpTill
0ce9c515af Add option to check / set audio monitoring of sources 2022-12-10 13:06:19 -08:00
WarmUpTill
03854376ef Remove outdated translations for cooldown option 2022-12-10 00:55:28 -08:00
WarmUpTill
88b7603ed1 CI: No longer manually install obs-frontend-api.h for debian build 2022-12-07 11:28:46 -08:00
WarmUpTill
c07ef3c699 Enable detection of user holding down hotkey
Previously only the key down event would trigger the condition
2022-12-05 07:23:13 -08:00
WarmUpTill
f3e5fb59f3 Reset duration modifier timers when stopping the plugin 2022-12-05 07:22:57 -08:00
WarmUpTill
ef0f6839e5 Remove unnecessary duration reset 2022-12-05 07:22:57 -08:00
WarmUpTill
6a488316d8 Update locale (provided by qwe1154323937) 2022-12-04 10:26:03 -08:00
WarmUpTill
8dc434f45e Add option to match scene name to pattern 2022-12-04 09:26:38 -08:00
WarmUpTill
7b65286181 Add error handling for non X11 windowing systems
This will only avoid crashes.
Functionality relying on X11 might not be available in such situations.
(E.g. window title, idle detection, key press simulation)
2022-12-02 20:14:13 -08:00
WarmUpTill
05fce566f4 Use existing Qt functionality to get cursor position
Might resolve issues with cursor position detection on non-X windows on
Linux
2022-12-02 20:14:13 -08:00
WarmUpTill
cc3ce3f0e0 Resolve libXss.so symbols at runtime
This enables running the advanced scene switcher in environments in
which this library is not available with only minimal loss of
functionality (idle detection)
2022-11-28 10:18:18 -08:00
WarmUpTill
20ba02be8d Fix MultiStateCondition not being saved 2022-11-26 08:46:31 -08:00
WarmUpTill
e3d9bf11a4 Fix windows build
The default cmake version used on the Windows runner was unable to
configure OpenCV
2022-11-26 08:46:31 -08:00
WarmUpTill
31511851c9 Add option to match date pattern 2022-11-26 08:46:31 -08:00
WarmUpTill
4f71c55034 Show simple date settings by default 2022-11-26 08:46:31 -08:00
WarmUpTill
46fb63e960 Add option to set working directory for new process 2022-11-26 05:47:24 -08:00
WarmUpTill
034a5255a5 Add option to select folder 2022-11-26 05:47:24 -08:00
WarmUpTill
1be2764d7b Allow setting / checking the audio sync offset of sources 2022-11-26 05:47:13 -08:00
WarmUpTill
34355379ca Clean up Linux platform code
* Fix foreground process check not working for large pids.
* Clean up memory leaks.
* Reduce code duplication.
* Style changes.
2022-11-21 11:15:07 -08:00
WarmUpTill
fd8e3c1e5c Add support for libproc2 (Linux)
Needed for Debian build. (#590)
Also cleaned up platform dependent sections of CMakeLists.txt.
2022-11-21 11:15:07 -08:00
WarmUpTill
7407bc7d82 Add option to toggle visibility 2022-11-20 10:09:56 -08:00
WarmUpTill
d636022576 Add support for additional source groups
Previous implementation missed source types like "scene" and "group"
2022-11-20 10:09:56 -08:00
WarmUpTill
ae9ef2bf3b Fix potential crash for sources of unkown type
This can happen if a source was created but the corresponding
functionality was later removed (e.g. when loading a scene collection
containing a browser source on an OBS build without browser source
support)
2022-11-20 10:09:56 -08:00
WarmUpTill
624ff9f5c6 Fix freeze when changing scene collection 2022-11-16 08:44:15 -08:00
WarmUpTill
9bcced524b Move transition behaviour related settings to General tab 2022-11-09 09:57:55 -08:00
WarmUpTill
3bb2ddfaac Update GitHub Actions for set-output deprecation
GitHub Actions has deprecated set-output. Replace usages of set-output
in stdout with the new syntax to save the output to the new environment
variable.

See:
https://github.blog/changelog/2022-10-11-github-actions-deprecating-save-state-and-set-output-commands/
2022-11-06 09:53:25 -08:00
WarmUpTill
50e26aba72 Improve focus handling
* Display current focus window / process
 * Match against app name instead of window name on MacOS when using
   Process condition
 * Clean up
2022-11-06 09:53:25 -08:00
WarmUpTill
267ab6a7c1 Set Windows installer version to git tag 2022-11-05 12:24:50 -07:00
WarmUpTill
65ea7987c7 Add special handling for the "between" case when ignoring the date
In this case the left time value will be treated as the start of the
time range and the right one as the end.

This now enables specifying ranges that span over the 24h boundary.
E.g. 23:00:00 to 01:00:00.
This would have previously been treated as 01:00:00 to 23:00:00 instead.
2022-11-02 07:57:01 -07:00
WarmUpTill
a6839666ce Fix scene item selection not saving index 2022-11-02 07:56:37 -07:00
WarmUpTill
e5843de4fc Add option to check for average brightness 2022-10-21 13:15:11 -07:00
WarmUpTill
a330378c16 Improve option to check multiple media sources
* Added the option to select current, previous, variables, ...
 * Refresh the source list on scene change
 * General clean up of media condition
2022-10-21 13:15:00 -07:00
WarmUpTill
ddb7888e61 Enable word wrap for "on change" and "parallel" options on macro tab 2022-10-21 13:14:35 -07:00
WarmUpTill
73aef4f0b2 Workaround for macro drag / drop not working as expected
See:
 * QTBUG-106395
 * https://github.com/obsproject/obs-studio/issues/7321
2022-10-13 10:29:39 -07:00
WarmUpTill
67c3b73e10 Fix date condition showing incorrect date
This is only affecting the displayed value when reopening a macro.
The entered values were stored / used correctly.
2022-10-08 15:59:15 -07:00
WarmUpTill
9a3d381cf0 Improve macro examples 2022-10-08 15:59:07 -07:00
WarmUpTill
8b8829f7df Fix scene item selection not returning expected number of items 2022-10-04 23:35:58 -07:00
WarmUpTill
6fff3b54e6 Remove version number from dep package name 2022-10-04 12:01:47 -07:00
208 changed files with 7711 additions and 3169 deletions

View File

@@ -31,6 +31,10 @@ inputs:
runs:
using: 'composite'
steps:
- name: Setup cmake
uses: jwlawson/actions-setup-cmake@v1.13
with:
cmake-version: '3.24.x'
- name: Run macOS Build
if: ${{ runner.os == 'macOS' }}
shell: zsh {0}

View File

@@ -65,6 +65,8 @@ runs:
if [[ '${{ inputs.notarize }}' == 'true' ]] package_args+=(-n)
if (( ${+CI} && ${+RUNNER_DEBUG} )) build_args+=(--debug)
package_args+=(-z)
${{ inputs.workingDirectory }}/.github/scripts/package-macos.zsh ${package_args}
- name: Run Linux packaging

View File

@@ -180,6 +180,12 @@ Usage: %B${functrace[1]%:*}%b <option> [<options>]
--keychain-profile "OBS-Codesign-Password" --wait
xcrun stapler staple "${project_root}/release/${output_name}"
}
if (( ${+ZIP} )) {
local output_name="${product_name}-${host_os}-${target##*-}.zip"
pushd ${project_root}/release
zip -r "${output_name}" *~*.pkg
popd
}
popd
} elif [[ ${host_os} == 'linux' ]] {
if (( ${+ZIP} )) {

View File

@@ -27,23 +27,10 @@ jobs:
- name: check_libobs_revision
run: |
sudo apt update
sudo apt install libobs-dev
sudo apt install devscripts libobs-dev
mkdir source
cd source
dpkg -l libobs-dev | tr -s " "| grep libobs | cut -d" " -f3 > libobs.rev
- name: install_frontend_header
run: |
[ -e /usr/include/obs/obs-frontend-api.h ] && { echo "ERROR: obs-frontend-api.h already in system. Maybe the package libobs-dev is installing it."; exit 1; }
cd source
LIBOBSREV=$(cat libobs.rev)
sudo apt update
sudo apt install devscripts
dget -u http://archive.ubuntu.com/ubuntu/pool/universe/o/obs-studio/obs-studio_$LIBOBSREV.dsc
cd ..
sudo find -name obs-frontend-api.h -exec cp {} /usr/include/obs/ \;
egrep '#include <obs.h>' /usr/include/obs/obs-frontend-api.h || { echo "ERROR: check if the sed commands are needed from now."; exit 1; }
sudo sed -i 's/#include <obs.h>/#include <obs\/obs.h>/' /usr/include/obs/obs-frontend-api.h
sudo sed -i 's/#include <util\/darray.h>/#include <obs\/util\/darray.h>/' /usr/include/obs/obs-frontend-api.h
- name: create_tarball
run: |
cd ..

View File

@@ -92,21 +92,21 @@ jobs:
if [[ '${{ secrets.MACOS_SIGNING_APPLICATION_IDENTITY }}' != '' && \
'${{ secrets.MACOS_SIGNING_INSTALLER_IDENTITY }}' != '' && \
'${{ secrets.MACOS_SIGNING_CERT }}' != '' ]] {
print '::set-output name=haveCodesignIdent::true'
print 'haveCodesignIdent=true' >> $GITHUB_OUTPUT
} else {
print '::set-output name=haveCodesignIdent::false'
print 'haveCodesignIdent=false' >> $GITHUB_OUTPUT
}
if [[ '${{ secrets.MACOS_NOTARIZATION_USERNAME }}' != '' && \
'${{ secrets.MACOS_NOTARIZATION_PASSWORD }}' != '' ]] {
print '::set-output name=haveNotarizationUser::true'
print 'haveNotarizationUser=true' >> $GITHUB_OUTPUT
} else {
print '::set-output name=haveNotarizationUser::false'
print 'haveNotarizationUser=false' >> $GITHUB_OUTPUT
}
print '::endgroup::'
print "::set-output name=ccacheDate::$(date +"%Y-%m-%d")"
print "::set-output name=commitHash::${"$(git rev-parse HEAD)"[0,9]}"
print "ccacheDate=$(date +"%Y-%m-%d")" >> $GITHUB_OUTPUT
print "commitHash=${"$(git rev-parse HEAD)"[0,9]}" >> $GITHUB_OUTPUT
echo "$PWD/.github/scripts" >> $GITHUB_PATH
- name: Restore Compilation Cache
@@ -123,9 +123,9 @@ jobs:
if: ${{ github.event_name == 'pull_request' }}
run: |
if [[ -n "$(curl -H "Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}" -s "${{ github.event.pull_request.url }}" | jq -e '.labels[] | select(.name == "Seeking Testers")')" ]] {
print '::set-output name=found::true'
print 'found=true' >> $GITHUB_OUTPUT
} else {
print '::set-output name=found::false'
print 'found=false' >> $GITHUB_OUTPUT
}
- name: Install Apple Developer Certificate
@@ -164,12 +164,19 @@ jobs:
codesignUser: ${{ secrets.MACOS_NOTARIZATION_USERNAME }}
codesignPass: ${{ secrets.MACOS_NOTARIZATION_PASSWORD }}
- name: Upload Installer Artifact
if: ${{ success() }}
uses: actions/upload-artifact@v3
with:
name: ${{ env.PLUGIN_NAME }}-macos-${{ matrix.arch }}-${{ steps.setup.outputs.commitHash }}-installer
path: ${{ github.workspace }}/plugin/release/${{ env.LIB_NAME }}*-macos-${{ matrix.arch }}.pkg
- name: Upload Build Artifact
if: ${{ success() }}
uses: actions/upload-artifact@v3
with:
name: ${{ env.PLUGIN_NAME }}-macos-${{ matrix.arch }}-${{ steps.setup.outputs.commitHash }}
path: ${{ github.workspace }}/plugin/release/${{ env.LIB_NAME }}*-macos-${{ matrix.arch }}.pkg
path: ${{ github.workspace }}/plugin/release/${{ env.LIB_NAME }}-*.zip
linux_build:
name: 02 - Linux
@@ -206,8 +213,8 @@ jobs:
id: setup
run: |
## SETUP ENVIRONMENT SCRIPT
echo "::set-output name=ccacheDate::$(date +"%Y-%m-%d")"
echo "::set-output name=commitHash::$(git rev-parse HEAD | cut -c1-9)"
echo "ccacheDate=$(date +"%Y-%m-%d")" >> $GITHUB_OUTPUT
echo "commitHash=$(git rev-parse HEAD | cut -c1-9)" >> $GITHUB_OUTPUT
echo "$PWD/.github/scripts" >> $GITHUB_PATH
- name: Restore Compilation Cache
@@ -225,9 +232,9 @@ jobs:
run: |
## GITHUB LABEL SCRIPT
if [[ -n "$(curl -H "Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}" -s "${{ github.event.pull_request.url }}" | jq -e '.labels[] | select(.name == "Seeking Testers")')" ]]; then
echo '::set-output name=found::true'
echo 'found=true' >> $GITHUB_OUTPUT
else
echo '::set-output name=found::false'
echo 'found=false' >> $GITHUB_OUTPUT
fi
- name: Build Plugin
@@ -295,7 +302,7 @@ jobs:
run: |
## SETUP ENVIRONMENT SCRIPT
$CommitHash = (git rev-parse HEAD)[0..8] -join ''
Write-Output "::set-output name=commitHash::${CommitHash}"
"commitHash=${CommitHash}" >> $env:GITHUB_OUTPUT
- name: Check for GitHub Labels
id: seekingTesters
@@ -316,7 +323,7 @@ jobs:
$false
}
Write-Output "::set-output name=found::$(([string]${LabelFound}).ToLower())"
"found=$(([string]${LabelFound}).ToLower())" >> $env:GITHUB_OUTPUT
- name: Build Plugin
uses: ./plugin/.github/actions/build-plugin
@@ -367,7 +374,7 @@ jobs:
id: metadata
run: |
## METADATA SCRIPT
echo "::set-output name=version::${GITHUB_REF/refs\/tags\//}"
echo "version=${GITHUB_REF/refs\/tags\//}" >> $GITHUB_OUTPUT
- name: Download build artifacts
uses: actions/download-artifact@v3

View File

@@ -15,7 +15,7 @@ cd obs-studio/UI/frontend-plugins/
git clone --recursive https://github.com/WarmUpTill/SceneSwitcher.git
```
Then modify the obs-studio/UI/frontend-plugins/CMakeLists.txt file and add an entry for the scene switcher:
Then modify the obs-studio/UI/frontend-plugins/CMakeLists.txt Example and add an entry for the scene switcher:
```
add_subdirectory(SceneSwitcher)
```
@@ -66,11 +66,123 @@ Finally, start the plugin build using your provided generator. (E.g. Ninja on Li
Contributions to the plugin are always welcome and if you need any assistance do not hesitate to reach out.
If you would like to expand upon the macro system by adding a new condition or action type have a loot at the examples in `src/macro-core`.
In general changes in the `src/legacy` folder should be avoided.
If you would like to expand upon the macro system by adding a new condition or action type have a loot at the examples in `src/macro-core`.
The key functions to add conditions or are the Register() functions.
## Macro condition
Macro conditions should inherit from the `MacroCondition` class and must implement the following functions:
```
class MacroConditionExample : public MacroCondition {
public:
MacroConditionExample(Macro *m) : MacroCondition(m) {}
// This function should perfrom the condition check
bool CheckCondition();
// This function should store the required condition data to "obj"
// For example called on OBS shutdown
bool Save(obs_data_t *obj);
// This function should load the condition data from "obj"
// For example called on OBS startup
bool Load(obs_data_t *obj);
// This function should return a unique id for this condition type
// The _id is defined below
std::string GetId() { return _id; };
// Helper function called when new conditions are created
// Will be used later when regeistering the new condition type
static std::shared_ptr<MacroCondition> Create(Macro *m)
{
return std::make_shared<MacroConditionExample>(m);
}
private:
// Used to register new condition type
static bool _registered;
// Unique id identifying this condition type
static const std::string _id;
};
```
When defining the widget used to control the settings of the condition type, it is important to add a static `Create()` method.
It will be called whenever a new condition MacroConditionExample is created. (See `MacroConditionFactory::Register()`)
```
class MacroConditionExampleEdit : public QWidget {
Q_OBJECT
public:
// ...
MacroConditionExampleEdit(
QWidget *parent,
std::shared_ptr<MacroConditionExample> cond = nullptr);
// Function will be used to create the widget for editing the settings of the condition
static QWidget *Create(QWidget *parent,
std::shared_ptr<MacroCondition> cond)
{
return new MacroConditionExampleEdit(
parent,
std::dynamic_pointer_cast<MacroConditionExample>(cond));
}
// ...
};
```
To finally register the new condition type you will have to add the following call to `MacroConditionFactory::Register()`.
```
bool MacroConditionExample::_registered = MacroConditionFactory::Register(
MacroConditionExample::id, // Unique string identifying this condition type
{
MacroConditionExample::Create, // Function called to create the object performing the condition
MacroConditionExampleEdit::Create, // Function called to create the widget configure the condition
"AdvSceneSwitcher.condition.example", // User facing name of the condition type (will be translated)
true // Condition type supports duration modifiers (default true)
}
);
```
## Macro action
The process of adding a new action type is very similar to adding a new condition.
The differences are highlighted in the comments below.
```
class MacroActionExample : public MacroAction {
public:
MacroActionExample(Macro *m) : MacroAction(m) {}
// This function should perfrom the action
// If false is returned the macro will aborted
bool PerformAction();
bool Save(obs_data_t *obj);
bool Load(obs_data_t *obj);
std::string GetId() { return _id; };
static std::shared_ptr<MacroAction> Create(Macro *m)
{
return std::make_shared<MacroActionExample>(m);
}
private:
static bool _registered;
static const std::string _id;
};
```
```
class MacroActionExampleEdit : public QWidget {
Q_OBJECT
public:
// ...
MacroActionExampleEdit(
QWidget *parent,
std::shared_ptr<MacroActionExample> entryData = nullptr);
static QWidget *Create(QWidget *parent,
std::shared_ptr<MacroAction> action)
{
return new MacroActionExampleEdit(
parent,
std::dynamic_pointer_cast<MacroActionExample>(action));
}
// ...
};
```
```
MacroActionFactory::Register(
MacroActionExample::id, // Unique string identifying this action type
@@ -81,6 +193,6 @@ MacroActionFactory::Register(
}
);
```
## External dependencies
If your intention is to add macro functionality which depends on external libraries, which is likely not to exist on all user setups, try to follow the examples under `src/macro-external`.
These are basically plugins themselves that get attempted to be loaded on startup of the advanced scene switcher.

View File

@@ -217,8 +217,6 @@ target_sources(
src/macro-core/macro-condition-window.hpp
src/macro-core/macro-condition.cpp
src/macro-core/macro-condition.hpp
src/macro-core/macro-list-entry-widget.cpp
src/macro-core/macro-list-entry-widget.hpp
src/macro-core/macro-properties.cpp
src/macro-core/macro-properties.hpp
src/macro-core/macro-ref.cpp
@@ -230,6 +228,8 @@ target_sources(
src/macro-core/macro-selection.cpp
src/macro-core/macro-selection.hpp
src/macro-core/macro-tab.cpp
src/macro-core/macro-tree.cpp
src/macro-core/macro-tree.hpp
src/macro-core/macro.cpp
src/macro-core/macro.hpp)
@@ -262,12 +262,18 @@ target_sources(
src/utils/screenshot-helper.hpp
src/utils/section.cpp
src/utils/section.hpp
src/utils/slider-spinbox.cpp
src/utils/slider-spinbox.hpp
src/utils/source-selection.cpp
src/utils/source-selection.hpp
src/utils/transition-selection.cpp
src/utils/transition-selection.hpp
src/utils/utility.cpp
src/utils/utility.hpp
src/utils/variable.cpp
src/utils/variable.hpp
src/utils/variable-text-edit.cpp
src/utils/variable-text-edit.hpp
src/utils/volume-control.cpp
src/utils/volume-control.hpp
src/utils/websocket-helpers.cpp
@@ -321,6 +327,7 @@ if(OS_WINDOWS)
configure_file(cmake/bundle/windows/installer-Windows.iss.in
${CMAKE_CURRENT_BINARY_DIR}/installer-Windows.generated.iss)
target_compile_definitions(${LIB_NAME} PRIVATE UNICODE _UNICODE)
if(MSVC)
target_compile_options(${LIB_NAME} PUBLIC /MP /d2FH4- /wd4267 /wd4267)
endif()
@@ -346,10 +353,8 @@ elseif(OS_MACOS)
find_library(COCOA Cocoa)
target_include_directories(${LIB_NAME} PRIVATE ${COCOA})
target_sources(${LIB_NAME} PRIVATE src/osx/advanced-scene-switcher-osx.mm)
set_source_files_properties(advanced-scene-switcher-osx.mm
PROPERTIES COMPILE_FLAGS "-fobjc-arc")
set("${PROJECT_NAME}_PLATFORM_LIBS" ${COCOA})
target_link_libraries(${LIB_NAME} PRIVATE ${COCOA})
find_package(CURL)
find_package(Libcurl)
if(CURL_FOUND)
@@ -359,6 +364,10 @@ elseif(OS_MACOS)
else()
message(FATAL_ERROR "Couldn't find CURL or Libcurl - abort")
endif()
target_sources(${LIB_NAME} PRIVATE src/osx/advanced-scene-switcher-osx.mm)
set_source_files_properties(advanced-scene-switcher-osx.mm
PROPERTIES COMPILE_FLAGS "-fobjc-arc")
# --- End of section ---
# --- Linux-specific build settings and tasks ---
@@ -369,23 +378,36 @@ else()
set_target_properties(${LIB_NAME} PROPERTIES SOVERSION 1)
find_package(X11 REQUIRED COMPONENTS Xtst Xss)
find_path(PROCPS_INCLUDE_DIR NAMES proc/procps.h)
if(NOT PROCPS_INCLUDE_DIR)
message(
FATAL_ERROR "procps include dir not found - please set PROCPS_INCLUDE_DIR"
)
endif()
find_library(PROCPS_LIBRARY NAMES procps)
if(NOT PROCPS_LIBRARY)
message(FATAL_ERROR "procps lib not found - please set PROCPS_LIBRARY")
endif()
target_link_libraries(${LIB_NAME} PRIVATE ${X11_LIBRARIES}
${procps_LIBRARIES})
target_include_directories(
${LIB_NAME} PRIVATE "${X11_INCLUDE_DIR}" "${X11_Xtst_INCLUDE_PATH}"
"${X11_Xss_INCLUDE_PATH}" "${PROCPS_INCLUDE_DIR}")
target_sources(${LIB_NAME} PRIVATE src/linux/advanced-scene-switcher-nix.cpp)
set("${PROJECT_NAME}_PLATFORM_LIBS" Xss ${PROCPS_LIBRARY})
"${X11_Xss_INCLUDE_PATH}")
target_link_libraries(${LIB_NAME} PRIVATE ${X11_LIBRARIES})
find_path(PROCPS_INCLUDE_DIR NAMES proc/procps.h)
find_path(PROCPS2_INCLUDE_DIR NAMES libproc2/pids.h)
find_library(PROCPS_LIBRARY NAMES procps)
find_library(PROCPS2_LIBRARY NAMES proc2)
if(PROCPS_INCLUDE_DIR AND PROCPS_LIBRARY)
message(STATUS "${PROJECT_NAME} using procps")
set(PROC_INCLUDE_DIR "${PROCPS_INCLUDE_DIR}")
set(PROC_LIBRARY "${PROCPS_LIBRARY}")
target_compile_definitions(${LIB_NAME} PRIVATE USE_PROCPS)
elseif(PROCPS2_INCLUDE_DIR AND PROCPS2_LIBRARY)
message(STATUS "${PROJECT_NAME} using libproc2")
set(PROC_INCLUDE_DIR "${PROCPS2_INCLUDE_DIR}")
set(PROC_LIBRARY "${PROCPS2_LIBRARY}")
else()
message(
FATAL_ERROR
"found neither procps nor libproc2!
please either set ...
PROCPS_INCLUDE_DIR and PROCPS_LIBRARY
... or ...
PROCPS2_INCLUDE_DIR and PROCPS2_LIBRARY")
endif()
target_link_libraries(${LIB_NAME} PRIVATE "${PROC_LIBRARY}")
target_include_directories(${LIB_NAME} PRIVATE "${PROC_INCLUDE_DIR}")
find_package(CURL)
find_package(Libcurl)
if(CURL_FOUND)
@@ -395,11 +417,11 @@ else()
else()
message(FATAL_ERROR "Couldn't find CURL or Libcurl - abort")
endif()
target_sources(${LIB_NAME} PRIVATE src/linux/advanced-scene-switcher-nix.cpp)
endif()
# --- End of section ---
target_link_libraries(${LIB_NAME} PUBLIC ${${PROJECT_NAME}_PLATFORM_LIBS})
if(DEB_INSTALL)
file(GLOB ASS_TRANSLATION_FILES "data/locale/*.ini")
if(NOT LIB_OUT_DIR)

View File

@@ -214,7 +214,7 @@ endif()
function(setup_obs_lib_dependency target)
if(BUILD_OUT_OF_TREE)
find_package(libobs)
if(libobs_FOUND)
if(libobs_FOUND AND NOT LIBOBS_LIB)
target_link_libraries(${target} PUBLIC OBS::libobs)
else()
if(NOT LIBOBS_LIB)
@@ -229,7 +229,7 @@ function(setup_obs_lib_dependency target)
target_include_directories(${target} PRIVATE ${LIBOBS_INCLUDE_DIR})
endif()
find_package(obs-frontend-api)
if(obs-frontend-api_FOUND)
if(obs-frontend-api_FOUND AND NOT LIBOBS_FRONTEND_API_LIB)
target_link_libraries(${target} PUBLIC OBS::obs-frontend-api)
else()
if(NOT LIBOBS_FRONTEND_API_LIB)

View File

@@ -346,8 +346,7 @@ else()
set(CPACK_PACKAGE_NAME "${CMAKE_PROJECT_NAME}")
set(CPACK_DEBIAN_PACKAGE_MAINTAINER "${LINUX_MAINTAINER_EMAIL}")
set(CPACK_PACKAGE_VERSION "${CMAKE_PROJECT_VERSION}")
set(CPACK_PACKAGE_FILE_NAME
"${CPACK_PACKAGE_NAME}-${CPACK_PACKAGE_VERSION}-linux-x86_64")
set(CPACK_PACKAGE_FILE_NAME "${CPACK_PACKAGE_NAME}-linux-x86_64")
set(CPACK_GENERATOR "DEB")
if(${QT_VERSION} STREQUAL "6")

View File

@@ -1,5 +1,5 @@
#define MyAppName "@CMAKE_PROJECT_NAME@"
#define MyAppVersion "@CMAKE_PROJECT_VERSION@"
#define MyAppVersion "@GIT_TAG@"
#define MyAppPublisher "@PLUGIN_AUTHOR@"
#define MyAppURL "http://www.mywebsite.com"

View File

@@ -24,7 +24,7 @@ AdvSceneSwitcher.generalTab.generalBehavior.onNoMetDelayTooltip="Kann nur so gen
AdvSceneSwitcher.generalTab.generalBehavior.onNoMet.dontSwitch="Nicht wechseln"
AdvSceneSwitcher.generalTab.generalBehavior.onNoMet.switchToRandom="Wechsle zu einer Szene auf dem Zufall-Tab"
AdvSceneSwitcher.generalTab.generalBehavior.onNoMet.switchTo="Wechsle zu:"
AdvSceneSwitcher.generalTab.generalBehavior.cooldown="Nach einem automatisierten Szenenwechsel wechsle nicht mehr für"
AdvSceneSwitcher.generalTab.generalBehavior.cooldown="Nach Ausführen von Aktionen überspringe das Ausführen von Aktionen für"
AdvSceneSwitcher.generalTab.generalBehavior.cooldownHint="In diesem Zeitraum werden potentielle erfüllte Bedingungen ignoriert!"
AdvSceneSwitcher.generalTab.generalBehavior.verboseLogging="Ausführliches Logging"
AdvSceneSwitcher.generalTab.generalBehavior.saveWindowGeo="Fensterposition und -größe speichern"
@@ -51,13 +51,13 @@ AdvSceneSwitcher.generalTab.priority.media="Medien"
AdvSceneSwitcher.generalTab.priority.time="Zeit"
AdvSceneSwitcher.generalTab.priority.audio="Audio"
AdvSceneSwitcher.generalTab.priority.video="Video"
AdvSceneSwitcher.generalTab.setTransitionBy="Beim Ändern des Szenenübergangs:"
AdvSceneSwitcher.generalTab.transitionOverride="Verwende Übergangsüberschreibungen"
AdvSceneSwitcher.generalTab.adjustActiveTransitionType="Wechsle den aktiven Szenenübergangtyp"
AdvSceneSwitcher.generalTab.transitionBehaviorSelectionError="Mindestens eine Option muss aktiv sein:\n\n - Verwende Übergangsüberschreibungen\n\n - Wechsle den aktiven Szenenübergangtyp"
; Transition Tab
AdvSceneSwitcher.transitionTab.title="Szenenübergänge"
AdvSceneSwitcher.transitionTab.setTransitionBy="Beim Ändern des Szenenübergangs:"
AdvSceneSwitcher.transitionTab.transitionOverride="Verwende Übergangsüberschreibungen"
AdvSceneSwitcher.transitionTab.adjustActiveTransitionType="Wechsle den aktiven Szenenübergangtyp"
AdvSceneSwitcher.transitionTab.transitionBehaviorSelectionError="Mindestens eine Option muss aktiv sein:\n\n - Verwende Übergangsüberschreibungen\n\n - Wechsle den aktiven Szenenübergangtyp"
AdvSceneSwitcher.transitionTab.transitionForAToB="Szenenübergänge für automatisierte Szenenwechsel von Szene A zu Szene B"
AdvSceneSwitcher.transitionTab.transitionsHelp="<html><head/><body><p>Diese Einstellungen beeinflussen <span style=\"font-style:italic;\">nur</span> vom Szenenwechsler ausgelöste Szenenübergänge - Siehe <a href=\"https://obsproject.com/forum/resources/transition-table.1174/\"><span style=\" text-decoration: underline; color:#268bd2;\">Transition Table</span></a> um auch manuelle Szenenübergänge zu konfigurieren.<br/>Einstellungen auf diesem Tab haben Vorrang, vor denen welche auf den übrigen Tabs konfiguriert wurden.<br/><br/>Klicke auf das Plus Symbol, um einen neuen Eintrag hinzuzufügen.</p></body></html>"
AdvSceneSwitcher.transitionTab.defaultTransition="Ändere den Szenenübergang wenn eine Szene aktiv ist"

View File

@@ -24,7 +24,7 @@ AdvSceneSwitcher.generalTab.generalBehavior.onNoMetDelayTooltip="Will only ever
AdvSceneSwitcher.generalTab.generalBehavior.onNoMet.dontSwitch="Don't switch"
AdvSceneSwitcher.generalTab.generalBehavior.onNoMet.switchToRandom="Switch to any scene in Random tab"
AdvSceneSwitcher.generalTab.generalBehavior.onNoMet.switchTo="Switch to:"
AdvSceneSwitcher.generalTab.generalBehavior.cooldown="After a match don't perform actions for"
AdvSceneSwitcher.generalTab.generalBehavior.cooldown="After performing actions skip performing actions for"
AdvSceneSwitcher.generalTab.generalBehavior.cooldownHint="During this time potential matches will be ignored!"
AdvSceneSwitcher.generalTab.generalBehavior.verboseLogging="Enable verbose logging"
AdvSceneSwitcher.generalTab.generalBehavior.saveWindowGeo="Save window position and size"
@@ -55,6 +55,11 @@ AdvSceneSwitcher.generalTab.priority.time="Time"
AdvSceneSwitcher.generalTab.priority.audio="Audio"
AdvSceneSwitcher.generalTab.priority.video="Video"
AdvSceneSwitcher.generalTab.priority.macro="Macro"
AdvSceneSwitcher.generalTab.transition="Transitions"
AdvSceneSwitcher.generalTab.setTransitionBy="When changing transitions:"
AdvSceneSwitcher.generalTab.transitionOverride="Set transition overrides"
AdvSceneSwitcher.generalTab.adjustActiveTransitionType="Change active transition type"
AdvSceneSwitcher.generalTab.transitionBehaviorSelectionError="At least one option must be enabled:\n\n - Use transition overrides\n\n - Change active transition type"
; Macro Tab
AdvSceneSwitcher.macroTab.title="Macro"
@@ -74,17 +79,23 @@ AdvSceneSwitcher.macroTab.runFail="Running \"%1\" failed!\nEither one of the act
AdvSceneSwitcher.macroTab.runInParallel="Run macro in parallel to other macros"
AdvSceneSwitcher.macroTab.onChange="Perform actions only on condition change"
AdvSceneSwitcher.macroTab.defaultname="Macro %1"
AdvSceneSwitcher.macroTab.defaultGroupName="Group %1"
AdvSceneSwitcher.macroTab.exists="Macro name exists already"
AdvSceneSwitcher.macroTab.copy="Create copy"
AdvSceneSwitcher.macroTab.groupDeleteConfirm="Are you sure you want to delete \"%1\" and all its elements?"
AdvSceneSwitcher.macroTab.copy="Create copy of current macro"
AdvSceneSwitcher.macroTab.group="Group selected items"
AdvSceneSwitcher.macroTab.ungroup="Ungroup selected groups"
AdvSceneSwitcher.macroTab.expandAll="Expand all"
AdvSceneSwitcher.macroTab.collapseAll="Collapse all"
AdvSceneSwitcher.macroTab.maximize="Maximize"
AdvSceneSwitcher.macroTab.minimize="Minimize"
AdvSceneSwitcher.macroTab.highlightSettings="Visual settings"
AdvSceneSwitcher.macroTab.hotkeySettings="Hotkey settings"
AdvSceneSwitcher.macroTab.highlightExecutedMacros="Highlight recently executed macros"
AdvSceneSwitcher.macroTab.highlightTrueConditions="Highlight conditions of currently selected macro that evaluated to true recently"
AdvSceneSwitcher.macroTab.highlightPerformedActions="Highlight recently performed actions of currently selected macro"
AdvSceneSwitcher.macroTab.newMacroRegisterHotkey="Register hotkeys to control pause state of new macros"
AdvSceneSwitcher.macroTab.currentDisableHotkeys="Register hotkeys to control pause state of selected macro"
AdvSceneSwitcher.macroTab.newMacroRegisterHotkey="Register hotkeys to control the pause state of new macros"
AdvSceneSwitcher.macroTab.currentDisableHotkeys="Register hotkeys to control the pause state of selected macro"
; Macro List
AdvSceneSwitcher.macroList.deleted="deleted"
@@ -108,39 +119,52 @@ AdvSceneSwitcher.condition.audio.state.mute="muted"
AdvSceneSwitcher.condition.audio.state.unmute="unmuted"
AdvSceneSwitcher.condition.audio.type.output="Output volume"
AdvSceneSwitcher.condition.audio.type.volume="Configured volume level"
AdvSceneSwitcher.condition.audio.entry="{{checkType}} of {{audioSources}} is {{condition}} {{volume}}"
AdvSceneSwitcher.condition.audio.type.syncOffset="Sync offset"
AdvSceneSwitcher.condition.audio.type.monitor="Audio monitoring"
AdvSceneSwitcher.condition.audio.type.balance="Audio balance"
AdvSceneSwitcher.condition.audio.entry="{{checkType}} of {{audioSources}} is {{condition}}{{volume}}{{syncOffset}}{{monitorTypes}}"
AdvSceneSwitcher.condition.cursor="Cursor"
AdvSceneSwitcher.condition.cursor.type.region="is in region"
AdvSceneSwitcher.condition.cursor.type.moving="is moving"
AdvSceneSwitcher.condition.cursor.type.click="clicked"
AdvSceneSwitcher.condition.cursor.button.left="Left mouse button"
AdvSceneSwitcher.condition.cursor.button.middle="Middle mouse button"
AdvSceneSwitcher.condition.cursor.button.right="Right mouse button"
AdvSceneSwitcher.condition.cursor.showFrame="Show frame"
AdvSceneSwitcher.condition.cursor.hideFrame="Hide frame"
AdvSceneSwitcher.condition.cursor.entry.line1="Cursor is {{conditions}} {{minX}} {{minY}} {{maxX}} {{maxY}} - {{toggleFrameButton}}"
AdvSceneSwitcher.condition.cursor.entry.line1="Cursor {{conditions}}{{buttons}}{{minX}}{{minY}}{{maxX}}{{maxY}}{{toggleFrameButton}}"
AdvSceneSwitcher.condition.cursor.entry.line2="Cursor is currently at {{xPos}} x {{yPos}}"
AdvSceneSwitcher.condition.scene="Scene"
AdvSceneSwitcher.condition.scene.type.current="Current scene is"
AdvSceneSwitcher.condition.scene.type.previous="Previous scene is"
AdvSceneSwitcher.condition.scene.type.changed="Scene changed"
AdvSceneSwitcher.condition.scene.type.notChanged="Scene has not changed"
AdvSceneSwitcher.condition.scene.type.currentPattern="Current scene matches"
AdvSceneSwitcher.condition.scene.type.previousPattern="Previous scene matches"
AdvSceneSwitcher.condition.scene.currentSceneTransitionBehaviour="During transition check for transition target scene"
AdvSceneSwitcher.condition.scene.previousSceneTransitionBehaviour="During transition check for transition source scene"
AdvSceneSwitcher.condition.scene.entry.line1="{{sceneType}} {{scenes}}"
AdvSceneSwitcher.condition.scene.entry.line1="{{sceneType}}{{scenes}}{{pattern}}"
AdvSceneSwitcher.condition.scene.entry.line2="{{useTransitionTargetScene}}"
AdvSceneSwitcher.condition.window="Window"
AdvSceneSwitcher.condition.window.entry.line1="{{windows}} exist and ..."
AdvSceneSwitcher.condition.window.entry.line2="... is {{fullscreen}} fullscreen {{maximized}} maximized {{focused}} focused {{windowFocusChanged}} foreground window changed"
AdvSceneSwitcher.condition.window.entry.line3="Current foreground window: {{focusWindow}}"
AdvSceneSwitcher.condition.file="File"
AdvSceneSwitcher.condition.file.type.match="matches"
AdvSceneSwitcher.condition.file.type.contentChange="content changed"
AdvSceneSwitcher.condition.file.type.dateChange="modification date changed"
AdvSceneSwitcher.condition.file.entry.line1="Content of{{fileType}}{{filePath}}{{conditions}}"
AdvSceneSwitcher.condition.file.remote="Remote file"
AdvSceneSwitcher.condition.file.local="Local file"
AdvSceneSwitcher.condition.file.entry.line1="{{fileType}}{{filePath}}{{conditions}}"
AdvSceneSwitcher.condition.file.entry.line2="{{matchText}}"
AdvSceneSwitcher.condition.file.entry.line3="{{useRegex}} {{checkModificationDate}} {{checkFileContent}}"
AdvSceneSwitcher.condition.media="Media"
AdvSceneSwitcher.condition.media.source="Source"
AdvSceneSwitcher.condition.media.anyOnScene="Any media source on"
AdvSceneSwitcher.condition.media.allOnScene="All media sources on"
AdvSceneSwitcher.condition.media.matchOnChange="Only match on change (Note: This option will be removed in a future version - please use duration modifiers instead)"
AdvSceneSwitcher.condition.media.inconsistencyInfo="Unfortunately not all media source types behave the same (e.g. Media Source vs. VLC Video Source \"Stopped\" state).\nSo please experiment what works for your setup!"
AdvSceneSwitcher.condition.media.entry="{{mediaSources}}{{scenes}} state is {{states}} and {{timeRestrictions}} {{time}}"
AdvSceneSwitcher.condition.media.entry="{{sourceTypes}}{{mediaSources}}{{scenes}}state is{{states}}and{{timeRestrictions}}{{time}}"
AdvSceneSwitcher.condition.video="Video"
AdvSceneSwitcher.condition.video.condition.match="exactly matches"
AdvSceneSwitcher.condition.video.condition.differ="does not match"
@@ -149,6 +173,7 @@ AdvSceneSwitcher.condition.video.condition.hasNotChanged="has not changed"
AdvSceneSwitcher.condition.video.condition.noImage="has no output"
AdvSceneSwitcher.condition.video.condition.pattern="matches pattern"
AdvSceneSwitcher.condition.video.condition.object="contains object"
AdvSceneSwitcher.condition.video.condition.brightness="brightness"
AdvSceneSwitcher.condition.video.askFileAction="Do you want to use an existing file or create a screenshot of the currently selected source?"
AdvSceneSwitcher.condition.video.askFileAction.file="Use existing file"
AdvSceneSwitcher.condition.video.askFileAction.screenshot="Create screenshot"
@@ -159,6 +184,9 @@ AdvSceneSwitcher.condition.video.usePatternForChangedCheck.tooltip="This will al
AdvSceneSwitcher.condition.video.patternThreshold="Threshold: "
AdvSceneSwitcher.condition.video.patternThresholdDescription="A higher threshold value means that the pattern needs to match the video source more closely."
AdvSceneSwitcher.condition.video.patternThresholdUseAlphaAsMask="Use alpha channel as mask for pattern."
AdvSceneSwitcher.condition.video.brightnessThreshold="Average brightness is above:"
AdvSceneSwitcher.condition.video.brightnessThresholdDescription="A high value is indicating a bright image and a low one a darker one."
AdvSceneSwitcher.condition.video.currentBrightness="Current average brightness: %1"
AdvSceneSwitcher.condition.video.objectScaleThreshold="Scale factor: "
AdvSceneSwitcher.condition.video.objectScaleThresholdDescription="A lower scale factor will lead to more matches but higher CPU load."
AdvSceneSwitcher.condition.video.minNeighborDescription="A higher minimum neighbors value will result in fewer but higher quality matches."
@@ -171,7 +199,10 @@ AdvSceneSwitcher.condition.video.patternMatchSuccess="Pattern is highlighted in
AdvSceneSwitcher.condition.video.objectMatchFail="Object was not found!"
AdvSceneSwitcher.condition.video.objectMatchSuccess="Object is highlighted in red"
AdvSceneSwitcher.condition.video.modelLoadFail="Model data could not be loaded!"
AdvSceneSwitcher.condition.video.entry="{{videoSources}} {{condition}} {{imagePath}}"
AdvSceneSwitcher.condition.video.type.main="OBS's main output"
AdvSceneSwitcher.condition.video.type.source="Source"
AdvSceneSwitcher.condition.video.type.scene="Scene"
AdvSceneSwitcher.condition.video.entry="{{videoInputTypes}}{{sources}}{{scenes}}{{condition}}{{imagePath}}"
AdvSceneSwitcher.condition.video.entry.modelPath="Model data (haar cascade classifier): {{modelDataPath}}"
AdvSceneSwitcher.condition.video.entry.minNeighbor="Minimum neighbors: {{minNeighbors}}"
AdvSceneSwitcher.condition.video.entry.throttle="{{throttleEnable}}Reduce CPU load by performing check only every {{throttleCount}} milliseconds"
@@ -196,12 +227,16 @@ AdvSceneSwitcher.condition.record.state.stop="Recording stopped"
AdvSceneSwitcher.condition.record.entry="{{recordState}}"
AdvSceneSwitcher.condition.process="Process"
AdvSceneSwitcher.condition.process.entry="{{processes}} is running {{focused}} and is focused"
AdvSceneSwitcher.condition.process.entry.focus="Current foreground process: {{focusProcess}}"
AdvSceneSwitcher.condition.idle="Idle"
AdvSceneSwitcher.condition.idle.entry="No keyboard or mouse inputs for {{duration}}"
AdvSceneSwitcher.condition.pluginState="Plugin state"
AdvSceneSwitcher.condition.pluginState.state.sceneSwitched="Automated scene change was triggered in this interval"
AdvSceneSwitcher.condition.pluginState.state.running="Advanced scene switcher is running"
AdvSceneSwitcher.condition.pluginState.state.start="Plugin started"
AdvSceneSwitcher.condition.pluginState.state.restart="Plugin restarted"
AdvSceneSwitcher.condition.pluginState.state.running="Plugin is running"
AdvSceneSwitcher.condition.pluginState.state.shutdown="OBS is shutting down"
AdvSceneSwitcher.condition.pluginState.state.sceneCollection="Scene collection loaded"
AdvSceneSwitcher.condition.pluginState.state.sceneSwitched="Scene changed by previous macro"
AdvSceneSwitcher.condition.pluginState.entry="{{condition}}"
AdvSceneSwitcher.condition.timer="Timer"
AdvSceneSwitcher.condition.timer.type.fixed="Fixed"
@@ -279,6 +314,7 @@ AdvSceneSwitcher.condition.date.state.at="At"
AdvSceneSwitcher.condition.date.state.after="After"
AdvSceneSwitcher.condition.date.state.before="Before"
AdvSceneSwitcher.condition.date.state.between="Between"
AdvSceneSwitcher.condition.date.state.pattern="Pattern"
AdvSceneSwitcher.condition.date.separator="and"
AdvSceneSwitcher.condition.date.ignoreDate="If unchecked the date component will be ignored"
AdvSceneSwitcher.condition.date.ignoreTime="If unchecked the time component will be ignored"
@@ -287,6 +323,7 @@ AdvSceneSwitcher.condition.date.showSimpleSettings="Show simple settings"
AdvSceneSwitcher.condition.date.entry.simple="On {{dayOfWeek}} {{weekCondition}} {{ignoreWeekTime}}{{weekTime}}"
AdvSceneSwitcher.condition.date.entry.advanced="{{condition}} {{ignoreDate}}{{date}} {{ignoreTime}}{{time}} {{separator}} {{date2}} {{time2}}"
AdvSceneSwitcher.condition.date.entry.repeat="{{repeat}} Repeat every {{duration}} on date match"
AdvSceneSwitcher.condition.date.entry.pattern="Current date \"{{currentDate}}\" matches pattern {{pattern}}"
AdvSceneSwitcher.condition.date.entry.nextMatchDate="Next match at: %1"
AdvSceneSwitcher.condition.date.entry.updateOnRepeat="{{updateOnRepeat}} On repeat update selected date to repeat date"
AdvSceneSwitcher.condition.sceneTransform="Scene item transform"
@@ -304,9 +341,10 @@ AdvSceneSwitcher.condition.transition.type.transitionTarget="Transitioning to"
AdvSceneSwitcher.condition.transition.durationSuffix="seconds"
AdvSceneSwitcher.condition.transition.entry="{{conditions}}{{transitions}}{{scenes}}{{duration}}{{durationSuffix}}"
AdvSceneSwitcher.condition.sceneVisibility="Scene item visibility"
AdvSceneSwitcher.condition.sceneVisibility.type.shown="Shown"
AdvSceneSwitcher.condition.sceneVisibility.type.hidden="Hidden"
AdvSceneSwitcher.condition.sceneVisibility.entry="On{{scenes}}{{sources}}is{{conditions}}"
AdvSceneSwitcher.condition.sceneVisibility.type.shown="is visible"
AdvSceneSwitcher.condition.sceneVisibility.type.hidden="is hidden"
AdvSceneSwitcher.condition.sceneVisibility.type.changed="visibility changed"
AdvSceneSwitcher.condition.sceneVisibility.entry="On{{scenes}}{{sources}}{{conditions}}"
AdvSceneSwitcher.condition.studioMode="Studio mode"
AdvSceneSwitcher.condition.studioMode.state.active="Studio mode is active"
AdvSceneSwitcher.condition.studioMode.state.notActive="Studio mode is not active"
@@ -351,8 +389,10 @@ AdvSceneSwitcher.condition.variable.type.number="is a number"
AdvSceneSwitcher.condition.variable.type.lessThan="is less than"
AdvSceneSwitcher.condition.variable.type.greaterThan="is greater than"
AdvSceneSwitcher.condition.variable.type.valueChanged="value changed"
AdvSceneSwitcher.condition.variable.regex="Use regular expressions"
AdvSceneSwitcher.condition.variable.entry="{{variables}}{{conditions}}{{strValue}}{{numValue}}"
AdvSceneSwitcher.condition.variable.type.equalsVariable="equals variable"
AdvSceneSwitcher.condition.variable.type.lessThanVariable="is less than variable"
AdvSceneSwitcher.condition.variable.type.greaterThanVariable="is greater than variable"
AdvSceneSwitcher.condition.variable.entry="{{variables}}{{conditions}}{{strValue}}{{numValue}}{{variables2}}"
; Macro Actions
AdvSceneSwitcher.action.switchScene="Switch scene"
@@ -369,13 +409,17 @@ AdvSceneSwitcher.action.audio.type.mute="Mute"
AdvSceneSwitcher.action.audio.type.unmute="Unmute"
AdvSceneSwitcher.action.audio.type.sourceVolume="Set source volume"
AdvSceneSwitcher.action.audio.type.masterVolume="Set master volume"
AdvSceneSwitcher.action.audio.type.syncOffset="Set sync offset"
AdvSceneSwitcher.action.audio.type.monitor="Set audio monitoring"
AdvSceneSwitcher.action.audio.type.balance="Set balance"
AdvSceneSwitcher.action.audio.balance.description="Move the slider in the direction of the audio channel you want to focus on."
AdvSceneSwitcher.action.audio.fade.type.duration="over a duration of"
AdvSceneSwitcher.action.audio.fade.type.rate="at a rate of"
AdvSceneSwitcher.action.audio.fade.duration="{{fade}}Fade {{fadeTypes}} {{duration}} seconds."
AdvSceneSwitcher.action.audio.fade.rate="{{fade}}Fade {{fadeTypes}} {{rate}}per second."
AdvSceneSwitcher.action.audio.fade.wait="Wait for fade to complete."
AdvSceneSwitcher.action.audio.fade.abort="Abort already active fade."
AdvSceneSwitcher.action.audio.entry="{{actions}} {{audioSources}} {{volume}}"
AdvSceneSwitcher.action.audio.entry="{{actions}}{{audioSources}}{{volume}}{{syncOffset}}{{monitorTypes}}"
AdvSceneSwitcher.action.recording="Recording"
AdvSceneSwitcher.action.recording.type.stop="Stop recording"
AdvSceneSwitcher.action.recording.type.start="Start recording"
@@ -400,9 +444,11 @@ AdvSceneSwitcher.action.run.arguments="Arguments:"
AdvSceneSwitcher.action.run.addArgument="Add argument"
AdvSceneSwitcher.action.run.addArgumentDescription="Add new argument:"
AdvSceneSwitcher.action.run.entry="Run {{filePath}}"
AdvSceneSwitcher.action.run.entry.workingDirectory="Working directory:{{workingDirectory}}"
AdvSceneSwitcher.action.sceneVisibility="Scene item visibility"
AdvSceneSwitcher.action.sceneVisibility.type.show="Show"
AdvSceneSwitcher.action.sceneVisibility.type.hide="Hide"
AdvSceneSwitcher.action.sceneVisibility.type.toggle="Toggle"
AdvSceneSwitcher.action.sceneVisibility.type.source="Source"
AdvSceneSwitcher.action.sceneVisibility.type.sourceGroup="Any"
AdvSceneSwitcher.action.sceneVisibility.entry="On{{scenes}}{{actions}}{{sourceTypes}}{{sources}}{{sourceGroups}}"
@@ -417,8 +463,10 @@ AdvSceneSwitcher.action.source.type.enable="Enable"
AdvSceneSwitcher.action.source.type.disable="Disable"
AdvSceneSwitcher.action.source.type.settings="Set settings"
AdvSceneSwitcher.action.source.type.refreshSettings="Refresh source settings"
AdvSceneSwitcher.action.source.type.pressSettingsButton="Press settings button"
AdvSceneSwitcher.action.source.type.refreshSettings.tooltip="Can be used to refresh browser, media, etc. sources"
AdvSceneSwitcher.action.source.entry="{{actions}} {{sources}}"
AdvSceneSwitcher.action.source.noSettingsButtons="No buttons found!"
AdvSceneSwitcher.action.source.entry="{{actions}}{{sources}}{{settingsButtons}}"
AdvSceneSwitcher.action.source.warning="Warning: Enabling and disabling sources globally cannot be controlled by the OBS UI"
AdvSceneSwitcher.action.source.getSettings="Get current settings"
AdvSceneSwitcher.action.media="Media"
@@ -434,8 +482,8 @@ AdvSceneSwitcher.action.macro="Macro"
AdvSceneSwitcher.action.macro.type.pause="Pause"
AdvSceneSwitcher.action.macro.type.unpause="Unpause"
AdvSceneSwitcher.action.macro.type.resetCounter="Reset counter"
AdvSceneSwitcher.action.macro.type.run="Run"
AdvSceneSwitcher.action.macro.type.stop="Stop"
AdvSceneSwitcher.action.macro.type.run="Run actions"
AdvSceneSwitcher.action.macro.type.stop="Stop actions"
AdvSceneSwitcher.action.macro.entry="{{actions}} {{macros}}"
AdvSceneSwitcher.action.pluginState="Plugin state"
AdvSceneSwitcher.action.pluginState.type.stop="Stop the Advanced Scene Switcher plugin"
@@ -457,7 +505,7 @@ AdvSceneSwitcher.action.hotkey.rightAlt="Right Alt"
AdvSceneSwitcher.action.hotkey.leftMeta="Left Meta"
AdvSceneSwitcher.action.hotkey.rightMeta="Right Meta"
AdvSceneSwitcher.action.hotkey.onlyOBS="Send keypress only to OBS"
AdvSceneSwitcher.action.hotkey.disabled="Cannot simulate key presses - functionality disabled!"
AdvSceneSwitcher.action.hotkey.disabled="Cannot simulate global key presses - functionality limited to sending key press to OBS!"
AdvSceneSwitcher.action.hotkey.entry="Press {{keys}} for {{duration}} milliseconds"
AdvSceneSwitcher.action.sceneOrder="Scene item order"
AdvSceneSwitcher.action.sceneOrder.type.moveUp="Move up"
@@ -494,6 +542,7 @@ AdvSceneSwitcher.action.timer.type.reset="Reset"
AdvSceneSwitcher.action.timer.type.setTimeRemaining="Set time remaining of"
AdvSceneSwitcher.action.timer.entry="{{timerAction}} timers on {{macros}} {{duration}}"
AdvSceneSwitcher.action.random="Random"
AdvSceneSwitcher.action.random.allowRepeat="Allow consecutive execution of the same macro"
AdvSceneSwitcher.action.random.entry="Randomly run any of the following macros (paused macros are ignored)"
AdvSceneSwitcher.action.systray="System tray notification"
AdvSceneSwitcher.action.systray.entry="Show notification: {{message}}"
@@ -527,20 +576,22 @@ AdvSceneSwitcher.action.http.type.post="POST"
AdvSceneSwitcher.action.http.entry.line1="Send {{method}} to {{url}}"
AdvSceneSwitcher.action.http.entry.line2="Timeout: {{timeout}} seconds"
AdvSceneSwitcher.action.variable="Variable"
AdvSceneSwitcher.action.variable.type.set="Set"
AdvSceneSwitcher.action.variable.type.set="Set to fixed value"
AdvSceneSwitcher.action.variable.type.append="Append"
AdvSceneSwitcher.action.variable.type.appendVar="Append variable"
AdvSceneSwitcher.action.variable.type.increment="Increment"
AdvSceneSwitcher.action.variable.type.decrement="Decrement"
AdvSceneSwitcher.action.variable.entry="{{actions}}{{variables}}{{variables2}}{{strValue}}{{numValue}}"
AdvSceneSwitcher.action.variable.type.setActionValue="Set to action value"
AdvSceneSwitcher.action.variable.type.setConditionValue="Set to condition value"
AdvSceneSwitcher.action.variable.invalidSelection="Invalid selection!"
AdvSceneSwitcher.action.variable.actionNoVariableSupport="Getting variable values from %1 actions is not supported!"
AdvSceneSwitcher.action.variable.conditionNoVariableSupport="Getting variable values from %1 conditions is not supported!"
AdvSceneSwitcher.action.variable.currentSegmentValue="Current value:"
AdvSceneSwitcher.action.variable.entry="{{actions}}{{variables}}{{variables2}}{{strValue}}{{numValue}}{{segmentIndex}}"
; Transition Tab
AdvSceneSwitcher.transitionTab.title="Transition"
AdvSceneSwitcher.transitionTab.setTransitionBy="When changing transitions:"
AdvSceneSwitcher.transitionTab.transitionOverride="Set transition overrides"
AdvSceneSwitcher.transitionTab.adjustActiveTransitionType="Change active transition type"
AdvSceneSwitcher.transitionTab.transitionBehaviorSelectionError="At least one option must be enabled:\n\n - Use transition overrides\n\n - Change active transition type"
AdvSceneSwitcher.transitionTab.transitionForAToB="Use transition for automated scene switch from scene A to scene B"
AdvSceneSwitcher.transitionTab.transitionsHelp="<html><head/><body><p>These settings <span style=\"font-style:italic;\">only</span> affect transitions caused by the scene switcher - Check out <a href=\"https://obsproject.com/forum/resources/transition-table.1174/\"><span style=\" text-decoration: underline; color:#268bd2;\">Transition Table</span></a> if you want to configure this for manual scene changes.<br/>Settings defined here take priority over transition settings configured elsewhere in the scene switcher.<br/><br/>Click the plus symbol below to add a new entry.</p></body></html>"
AdvSceneSwitcher.transitionTab.defaultTransition="Change transition if scene is active"
@@ -876,3 +927,7 @@ AdvSceneSwitcher.duration.condition.more="For at least"
AdvSceneSwitcher.duration.condition.equal="For exactly"
AdvSceneSwitcher.duration.condition.less="For at most"
AdvSceneSwitcher.duration.condition.within="Within the last"
AdvSceneSwitcher.audio.monitor.none="Monitor Off"
AdvSceneSwitcher.audio.monitor.monitorOnly="Monitor Only (mute output)"
AdvSceneSwitcher.audio.monitor.both="Monitor and Output"

View File

@@ -24,7 +24,6 @@ AdvSceneSwitcher.generalTab.generalBehavior.onNoMetDelayTooltip="Solo será tan
AdvSceneSwitcher.generalTab.generalBehavior.onNoMet.dontSwitch="No cambiar"
AdvSceneSwitcher.generalTab.generalBehavior.onNoMet.switchToRandom="Cambiar a una escena aleatoria"
AdvSceneSwitcher.generalTab.generalBehavior.onNoMet.switchTo="Cambiar a:"
AdvSceneSwitcher.generalTab.generalBehavior.cooldown="Después de una coincidencia, no cambiar de escena durante"
AdvSceneSwitcher.generalTab.generalBehavior.cooldownHint="¡Durante este tiempo, se ignorarán las posibles coincidencias!"
AdvSceneSwitcher.generalTab.generalBehavior.verboseLogging="Habilitar el registro detallado"
AdvSceneSwitcher.generalTab.generalBehavior.saveWindowGeo="Guardar la posición y el tamaño de la ventana"
@@ -54,6 +53,10 @@ AdvSceneSwitcher.generalTab.priority.time="Tiempo"
AdvSceneSwitcher.generalTab.priority.audio="Audio"
AdvSceneSwitcher.generalTab.priority.video="Video"
AdvSceneSwitcher.generalTab.priority.macro="Macro"
AdvSceneSwitcher.generalTab.setTransitionBy="Al cambiar las transiciones:"
AdvSceneSwitcher.generalTab.transitionOverride="Establecer anulaciones de transición"
AdvSceneSwitcher.generalTab.adjustActiveTransitionType="Cambiar tipo de transición activa"
AdvSceneSwitcher.generalTab.transitionBehaviorSelectionError="Al menos una opción debe estar habilitada:\n\n - Usar anulaciones de transición\n\n - Cambiar el tipo de transición activo"
; Macro Tab
AdvSceneSwitcher.macroTab.title="Macro"
@@ -82,7 +85,6 @@ AdvSceneSwitcher.macroTab.minimize="Minimizar"
AdvSceneSwitcher.macroTab.highlightExecutedMacros="Resaltar macros ejecutadas recientemente"
AdvSceneSwitcher.macroTab.highlightTrueConditions="Resaltar condiciones de la macro seleccionada, que se evaluaron como verdaderas recientemente"
AdvSceneSwitcher.macroTab.highlightPerformedActions="Resaltar acciones realizadas recientemente de la macro seleccionada actualmente"
AdvSceneSwitcher.macroTab.disableHotkeys="Registre teclas de acceso rápido para controlar el estado de pausa de la macro seleccionada"
; Lógica de macros
AdvSceneSwitcher.logic.none="Omitir entrada"
@@ -102,13 +104,13 @@ AdvSceneSwitcher.condition.audio.state.mute="silenciado"
AdvSceneSwitcher.condition.audio.state.unmute="no silenciado"
AdvSceneSwitcher.condition.audio.type.output="Volumen de salida"
AdvSceneSwitcher.condition.audio.type.volume="Nivel de volumen configurado"
AdvSceneSwitcher.condition.audio.entry="{{checkType}} de {{audioSources}} es {{condition}} {{volume}}"
AdvSceneSwitcher.condition.audio.entry="{{checkType}} de {{audioSources}} es {{condition}}{{volume}}{{syncOffset}}{{monitorTypes}}"
AdvSceneSwitcher.condition.cursor="Cursor"
AdvSceneSwitcher.condition.cursor.type.region="está en la región"
AdvSceneSwitcher.condition.cursor.type.moving="se está moviendo"
AdvSceneSwitcher.condition.cursor.showFrame="Mostrar cuadro"
AdvSceneSwitcher.condition.cursor.hideFrame="Ocultar cuadro"
AdvSceneSwitcher.condition.cursor.entry.line1="El cursor es {{conditions}} {{minX}} {{minY}} {{maxX}} {{maxY}} - {{toggleFrameButton}}"
AdvSceneSwitcher.condition.cursor.entry.line1="El cursor es {{conditions}}{{buttons}}{{minX}}{{minY}}{{maxX}}{{maxY}}{{toggleFrameButton}}"
AdvSceneSwitcher.condition.cursor.entry.line2="El cursor se encuentra actualmente en {{xPos}} x {{yPos}}"
AdvSceneSwitcher.condition.scene="Escena"
AdvSceneSwitcher.condition.scene.type.current="La escena actual es"
@@ -117,7 +119,7 @@ AdvSceneSwitcher.condition.scene.type.changed="Escena cambiada"
AdvSceneSwitcher.condition.scene.type.notChanged="La escena no ha cambiado"
AdvSceneSwitcher.condition.scene.currentSceneTransitionBehaviour="Durante la transición, verifique la escena de destino de la transición"
AdvSceneSwitcher.condition.scene.previousSceneTransitionBehaviour="Durante la transición, verifique la escena de origen de la transición"
AdvSceneSwitcher.condition.scene.entry.line1="{{sceneType}} {{scenes}}"
AdvSceneSwitcher.condition.scene.entry.line1="{{sceneType}}{{scenes}}{{pattern}}"
AdvSceneSwitcher.condition.scene.entry.line2="{{useTransitionTargetScene}}"
AdvSceneSwitcher.condition.window="Ventana"
AdvSceneSwitcher.condition.window.entry.line1="{{windows}} existen y ..."
@@ -131,7 +133,7 @@ AdvSceneSwitcher.condition.media.anyOnScene="Cualquier fuente multimedia activad
AdvSceneSwitcher.condition.media.allOnScene="Todas las fuentes de medios activadas"
AdvSceneSwitcher.condition.media.matchOnChange="Solo coincidir con el cambio (Nota: esta opción se eliminará en una versión futura; use modificadores de duración en su lugar)"
AdvSceneSwitcher.condition.media.inconsistencyInfo="Desafortunadamente, no todos los tipos de fuentes de medios se comportan de la misma manera (p. ej., fuente de medios frente a estado \"Detenido\" de fuente de video VLC).\n¡Así que experimente lo que funciona para su configuración!"
AdvSceneSwitcher.condition.media.entry="El estado de {{mediaSources}}{{scenes}} es {{states}} y {{timeRestrictions}} {{time}}"
AdvSceneSwitcher.condition.media.entry="El estado de{{sourceTypes}}{{mediaSources}}{{scenes}}es{{states}}y{{timeRestrictions}}{{time}}"
AdvSceneSwitcher.condition.video="Video"
AdvSceneSwitcher.condition.video.condition.match="coincide exactamente"
AdvSceneSwitcher.condition.video.condition.differ="no coincide"
@@ -160,7 +162,7 @@ AdvSceneSwitcher.condition.video.patternMatchSuccess="El patrón está resaltado
AdvSceneSwitcher.condition.video.objectMatchFail="¡No se encontró el objeto!"
AdvSceneSwitcher.condition.video.objectMatchSuccess="El objeto está resaltado en rojo"
AdvSceneSwitcher.condition.video.modelLoadFail="¡No se pudieron cargar los datos del modelo!"
AdvSceneSwitcher.condition.video.entry="{{videoSources}} {{condition}} {{imagePath}}"
AdvSceneSwitcher.condition.video.entry="{{videoInputTypes}}{{sources}}{{scenes}}{{condition}}{{imagePath}}"
AdvSceneSwitcher.condition.video.entry.modelPath="Datos del modelo (haar cascade classifier): {{modelDataPath}}"
AdvSceneSwitcher.condition.video.entry.minNeighbor="Mínimo de vecinos: {{minNeighbors}}"
AdvSceneSwitcher.condition.video.entry.throttle="{{throttleEnable}}Reduzca la carga de la CPU realizando una comprobación solo cada {{throttleCount}} milisegundos"
@@ -218,7 +220,6 @@ AdvSceneSwitcher.condition.source="Fuente"
AdvSceneSwitcher.condition.source.type.active="Está activo"
AdvSceneSwitcher.condition.source.type.showing="Se muestra"
AdvSceneSwitcher.condition.source.type.settings="Coincidencia de configuración"
AdvSceneSwitcher.condition.source.regex="Usar expresiones regulares"
AdvSceneSwitcher.condition.source.getSettings="Obtener la configuración actual"
AdvSceneSwitcher.condition.source.entry.line1="{{sources}} {{conditions}}"
AdvSceneSwitcher.condition.source.entry.line2="{{settings}}"
@@ -231,7 +232,6 @@ AdvSceneSwitcher.condition.filter="Filtro"
AdvSceneSwitcher.condition.filter.type.active="Está habilitado"
AdvSceneSwitcher.condition.filter.type.showing="Está deshabilitado"
AdvSceneSwitcher.condition.filter.type.settings="Coincidencia de configuración"
AdvSceneSwitcher.condition.filter.regex="Usar expresiones regulares"
AdvSceneSwitcher.condition.filter.getSettings="Obtener la configuración actual"
AdvSceneSwitcher.condition.filter.entry.line1="En {{sources}} {{filters}} {{conditions}}"
AdvSceneSwitcher.condition.filter.entry.line2="{{settings}}"
@@ -277,7 +277,6 @@ AdvSceneSwitcher.condition.date.entry.nextMatchDate="Próxima coincidencia en: %
AdvSceneSwitcher.condition.date.entry.updateOnRepeat="{{updateOnRepeat}} Al repetir actualizar la fecha seleccionada para repetir la fecha"
AdvSceneSwitcher.condition.sceneTransform="Transformar elemento de escena"
AdvSceneSwitcher.condition.sceneTransform.getTransform="Obtener transformación"
AdvSceneSwitcher.condition.sceneTransform.regex="Usar expresiones regulares"
AdvSceneSwitcher.condition.sceneTransform.entry.line1="En{{scenes}}{{sources}}coincide con la transformación"
AdvSceneSwitcher.condition.sceneTransform.entry.line2="{{settings}}"
AdvSceneSwitcher.condition.sceneTransform.entry.line3="{{regex}} {{getSettings}}"
@@ -300,7 +299,7 @@ AdvSceneSwitcher.condition.studioMode.state.notActive="El modo de estudio no est
AdvSceneSwitcher.condition.studioMode.state.previewScene="La vista previa de la escena es"
AdvSceneSwitcher.condition.studioMode.entry="{{conditions}}{{scenes}}"
AdvSceneSwitcher.condition.openvr="OpenVR"
AdvSceneSwitcher.condition.errorStatus="Error de OpenVR: "
AdvSceneSwitcher.condition.openvr.errorStatus="Error de OpenVR: "
AdvSceneSwitcher.condition.openvr.entry.line1="HMD está en..."
AdvSceneSwitcher.condition.openvr.entry.line2="{{controls}}"
AdvSceneSwitcher.condition.openvr.entry.line3="HMD se encuentra actualmente en {{xPos}} x {{yPos}} x {{zPos}}"
@@ -344,7 +343,7 @@ AdvSceneSwitcher.action.audio.fade.duration="{{fade}}Fade {{fadeTypes}} {{durati
AdvSceneSwitcher.action.audio.fade.rate="{{fade}}Fade {{fadeTypes}} {{rate}}por segundo."
AdvSceneSwitcher.action.audio.fade.wait="Espere a que se complete el desvanecimiento".
AdvSceneSwitcher.action.audio.fade.abort="Cancelar atenuación ya activa."
AdvSceneSwitcher.action.audio.entry="{{actions}} {{audioSources}} {{volume}}"
AdvSceneSwitcher.action.audio.entry="{{actions}}{{audioSources}}{{volume}}{{syncOffset}}{{monitorTypes}}"
AdvSceneSwitcher.action.recording="Grabando"
AdvSceneSwitcher.action.recording.type.stop="Detener grabación"
AdvSceneSwitcher.action.recording.type.start="Iniciar grabación"
@@ -383,7 +382,7 @@ AdvSceneSwitcher.action.source="Fuente"
AdvSceneSwitcher.action.source.type.enable="Habilitar"
AdvSceneSwitcher.action.source.type.disable="Deshabilitar"
AdvSceneSwitcher.action.source.type.settings="Establecer configuración"
AdvSceneSwitcher.action.source.entry="{{actions}} {{sources}}"
AdvSceneSwitcher.action.source.entry="{{actions}}{{sources}}{{settingsButtons}}"
AdvSceneSwitcher.action.source.warning="Advertencia: la IU de OBS no puede controlar la habilitación y deshabilitación global de fuentes"
AdvSceneSwitcher.action.source.getSettings="Obtener la configuración actual"
AdvSceneSwitcher.action.media="Medios"
@@ -478,10 +477,6 @@ AdvSceneSwitcher.action.sequence.continueFrom="Continuar con el elemento selecci
; Transition Tab
AdvSceneSwitcher.transitionTab.title="Transición"
AdvSceneSwitcher.transitionTab.setTransitionBy="Al cambiar las transiciones:"
AdvSceneSwitcher.transitionTab.transitionOverride="Establecer anulaciones de transición"
AdvSceneSwitcher.transitionTab.adjustActiveTransitionType="Cambiar tipo de transición activa"
AdvSceneSwitcher.transitionTab.transitionBehaviorSelectionError="Al menos una opción debe estar habilitada:\n\n - Usar anulaciones de transición\n\n - Cambiar el tipo de transición activo"
AdvSceneSwitcher.transitionTab.transitionForAToB="Utiliza la transición para el cambio de escena automatizado de la escena A a la escena B"
AdvSceneSwitcher.transitionTab.transitionsHelp="<html><head/><body> <p> Estos ajustes <span style =\"font-style: italic; \"> solo </span> surgió a las transiciones causadas por el selector de escenas - Echa un vistazo a <a href =\"https://obsproject.com/forum/resources/transition-table.1174/\"> <span style=\" texto-decoración: subrayado; color: # 268bd2; \"> Tabla de transición </span> </a> si desea configurar esto para cambios de escena manuales. <br/> Los definiciones definidos aquí tienen prioridad sobre los ajustes de transición configurados en cualquier otro lugar del selector de escenas. < br/> <br/> Haz clic en el símbolo más a continuación para agregar una nueva entrada. </p></body></html>"
AdvSceneSwitcher.transitionTab.defaultTransition="Cambiar transición si la escena está activa"

View File

@@ -24,7 +24,6 @@ AdvSceneSwitcher.generalTab.generalBehavior.onNoMetDelayTooltip="Будет то
AdvSceneSwitcher.generalTab.generalBehavior.onNoMet.dontSwitch="Не переключаться"
AdvSceneSwitcher.generalTab.generalBehavior.onNoMet.switchToRandom="Переключиться на любую сцену на вкладке Random"
AdvSceneSwitcher.generalTab.generalBehavior.onNoMet.switchTo="Переключиться на:"
AdvSceneSwitcher.generalTab.generalBehavior.cooldown="После совпадения не переключаться между сценами в течение"
AdvSceneSwitcher.generalTab.generalBehavior.cooldownHint="В течение этого времени потенциальные совпадения будут игнорироваться!"
AdvSceneSwitcher.generalTab.generalBehavior.verboseLogging="Включить ведение подробного журнала"
AdvSceneSwitcher.generalTab.generalBehavior.saveWindowGeo="Сохранять положение и размер окна"
@@ -52,6 +51,10 @@ AdvSceneSwitcher.generalTab.priority.time="Время"
AdvSceneSwitcher.generalTab.priority.audio="Аудио"
AdvSceneSwitcher.generalTab.priority.video="Видео"
AdvSceneSwitcher.generalTab.priority.macro="Макрос"
AdvSceneSwitcher.generalTab.setTransitionBy="При изменении переходов:"
AdvSceneSwitcher.generalTab.transitionOverride="Установить переопределение переходов"
AdvSceneSwitcher.generalTab.adjustActiveTransitionType="Изменить тип активного перехода"
AdvSceneSwitcher.generalTab.transitionBehaviorSelectionError="Должна быть включена хотя бы одна опция:\n\n - Использовать переопределения переходов\n\n - Изменить тип активного перехода"
; Macro Tab
AdvSceneSwitcher.macroTab.title="Макрос"
@@ -125,7 +128,7 @@ AdvSceneSwitcher.action.audio.type.mute="Замьютить"
AdvSceneSwitcher.action.audio.type.unmute="Размьютить"
AdvSceneSwitcher.action.audio.type.sourceVolume="Установить громкость источника"
AdvSceneSwitcher.action.audio.type.masterVolume="Установить главную громкость"
AdvSceneSwitcher.action.audio.entry="{{actions}} {{audioSources}} {{volume}}"
AdvSceneSwitcher.action.audio.entry="{{actions}}{{audioSources}}{{volume}}{{syncOffset}}{{monitorTypes}}"
AdvSceneSwitcher.action.recording="Запись"
AdvSceneSwitcher.action.recording.type.stop="Остановить запись"
AdvSceneSwitcher.action.recording.type.start="Начать запись"
@@ -147,10 +150,6 @@ AdvSceneSwitcher.action.run="Запустить"
; Transition Tab
AdvSceneSwitcher.transitionTab.title="Переход"
AdvSceneSwitcher.transitionTab.setTransitionBy="При изменении переходов:"
AdvSceneSwitcher.transitionTab.transitionOverride="Установить переопределение переходов"
AdvSceneSwitcher.transitionTab.adjustActiveTransitionType="Изменить тип активного перехода"
AdvSceneSwitcher.transitionTab.transitionBehaviorSelectionError="Должна быть включена хотя бы одна опция:\n\n - Использовать переопределения переходов\n\n - Изменить тип активного перехода"
AdvSceneSwitcher.transitionTab.transitionForAToB="Использовать переход для автоматического переключения сцены со сцены A на сцену B"
AdvSceneSwitcher.transitionTab.transitionsHelp="<html><head/><body><p>Эти настройки <span style=\"font-style:italic;\">только</span> влияют на переходы, вызванные переключателем сцен - Проверьте <a href=\"https://obsproject.com/forum/resources/transition-table.1174/\"><span style=\" text-decoration: underline; color:#268bd2;\">Transition Table</span></a> если вы хотите настроить его для ручного изменения сцены.<br/>Настройки, определенные здесь, имеют приоритет над настройками перехода, сконфигурированными в других местах переключателя сцен.<br/><br/>Нажмите на символ плюса ниже, чтобы добавить новую запись.</p></body></html>"
AdvSceneSwitcher.transitionTab.defaultTransition="Изменить переход, если сцена активна"

View File

@@ -53,6 +53,10 @@ AdvSceneSwitcher.generalTab.priority.time="Zaman"
AdvSceneSwitcher.generalTab.priority.audio="Ses"
AdvSceneSwitcher.generalTab.priority.video="Video"
AdvSceneSwitcher.generalTab.priority.macro="Makro"
AdvSceneSwitcher.generalTab.setTransitionBy="Geçişleri değiştirirken:"
AdvSceneSwitcher.generalTab.transitionOverride="Geçiş geçersiz kılmaları ayarla"
AdvSceneSwitcher.generalTab.adjustActiveTransitionType="Etkin geçiş türünü değiştir"
AdvSceneSwitcher.generalTab.transitionBehaviorSelectionError="En az bir seçenek etkinleştirilmelidir: \n\n - Geçiş geçersiz kılmalarını kullan \n\n - Etkin geçiş türünü değiştir"
; Macro Tab
AdvSceneSwitcher.macroTab.title="Makro"
@@ -97,7 +101,7 @@ AdvSceneSwitcher.condition.cursor.type.region="alanında"
AdvSceneSwitcher.condition.cursor.type.moving="hareketlidir"
AdvSceneSwitcher.condition.cursor.showFrame="Çerçeveyi Göster"
AdvSceneSwitcher.condition.cursor.hideFrame="Çerçeveyi Gizle"
AdvSceneSwitcher.condition.cursor.entry.line1="İmleç {{conditions}} {{minX}} {{minY}} {{maxX}} {{maxY}} - {{toggleFrameButton}}"
AdvSceneSwitcher.condition.cursor.entry.line1="İmleç {{conditions}}{{buttons}}{{minX}}{{minY}}{{maxX}}{{maxY}}{{toggleFrameButton}}"
AdvSceneSwitcher.condition.cursor.entry.line2="İmleç şuanda {{xPos}} x {{yPos}}"
AdvSceneSwitcher.condition.scene="Sahne"
AdvSceneSwitcher.condition.scene.type.current="Mevcut Sahne"
@@ -105,7 +109,7 @@ AdvSceneSwitcher.condition.scene.type.previous="Önceki Sahne"
AdvSceneSwitcher.condition.scene.type.changed="Sahne değişti"
AdvSceneSwitcher.condition.scene.type.notChanged="Sahne değişmedi"
AdvSceneSwitcher.condition.scene.currentSceneTransitionBehaviour="Geçiş hedefi sahnesi için geçiş kontrolü sırasında"
AdvSceneSwitcher.condition.scene.entry.line1="{{sceneType}} {{scenes}}"
AdvSceneSwitcher.condition.scene.entry.line1="{{sceneType}}{{scenes}}{{pattern}}"
AdvSceneSwitcher.condition.scene.entry.line2="{{useTransitionTargetScene}}"
AdvSceneSwitcher.condition.window="Pencere"
AdvSceneSwitcher.condition.window.entry.line1="{{windows}} Varolan ve ..."
@@ -118,7 +122,7 @@ AdvSceneSwitcher.condition.media="Medya"
AdvSceneSwitcher.condition.media.anyOnScene="Herhangi bir medya kaynağı"
AdvSceneSwitcher.condition.media.allOnScene="Tüm medya kaynakları "
AdvSceneSwitcher.condition.media.matchOnChange="Yalnızca değişiklikle eşleştirin (Not: Bu seçenek gelecekteki bir sürümde kaldırılacaktır - lütfen bunun yerine zaman kısıtlamalarını kullanın)"
AdvSceneSwitcher.condition.media.entry="{{mediaSources}}{{scenes}} durumu {{states}} ve {{timeRestrictions}} {{time}}"
AdvSceneSwitcher.condition.media.entry="{{sourceTypes}}{{mediaSources}}{{scenes}}durumu{{states}}ve{{timeRestrictions}}{{time}}"
AdvSceneSwitcher.condition.video="Video"
AdvSceneSwitcher.condition.video.condition.match="Tam olarak eşleşir"
AdvSceneSwitcher.condition.video.condition.differ="Eşleştirme"
@@ -143,7 +147,7 @@ AdvSceneSwitcher.condition.video.screenshotFail="Kaynağın ekran görüntüsü
AdvSceneSwitcher.condition.video.patternMatchFail="Desen bulunamadı!"
AdvSceneSwitcher.condition.video.objectMatchFail="Nesne bulunamadı!"
AdvSceneSwitcher.condition.video.modelLoadFail="Model verileri yüklenemedi!"
AdvSceneSwitcher.condition.video.entry="{{videoSources}} {{condition}} {{imagePath}}"
AdvSceneSwitcher.condition.video.entry="{{videoInputTypes}}{{sources}}{{scenes}}{{condition}}{{imagePath}}"
AdvSceneSwitcher.condition.video.entry.modelPath="Model verileri (haar kademeli sınıflandırıcı):{{modelDataPath}}"
AdvSceneSwitcher.condition.video.entry.minNeighbor="Minimum komşular: {{minNeighbors}}"
AdvSceneSwitcher.condition.video.entry.throttle="{{throttleEnable}} Yalnızca her seferinde kontrol gerçekleştirerek CPU yükünü azaltın {{throttleCount}} millisaniyeler"
@@ -192,7 +196,6 @@ AdvSceneSwitcher.condition.source="Kaynak"
AdvSceneSwitcher.condition.source.type.active="Aktiftir"
AdvSceneSwitcher.condition.source.type.showing="gösteriyor"
AdvSceneSwitcher.condition.source.type.settings="Ayarlar eşleştirildi"
AdvSceneSwitcher.condition.source.regex="Normal ifadeler kullanın"
AdvSceneSwitcher.condition.source.getSettings="Mevcut ayarları al"
AdvSceneSwitcher.condition.source.entry.line1="{{sources}} {{conditions}}"
AdvSceneSwitcher.condition.source.entry.line2="{{settings}}"
@@ -205,7 +208,6 @@ AdvSceneSwitcher.condition.filter="Filtrele"
AdvSceneSwitcher.condition.filter.type.active="Etkinleştirildi"
AdvSceneSwitcher.condition.filter.type.showing="Etkisizleştirildi"
AdvSceneSwitcher.condition.filter.type.settings="Ayarlar eşleşti"
AdvSceneSwitcher.condition.filter.regex="Normal ifadeler kullanın"
AdvSceneSwitcher.condition.filter.getSettings="Mevcut ayarları al"
AdvSceneSwitcher.condition.filter.entry.line1="Açık {{sources}} {{filters}} {{conditions}}"
AdvSceneSwitcher.condition.filter.entry.line2="{{settings}}"
@@ -231,7 +233,6 @@ AdvSceneSwitcher.condition.date.state.before="Önce"
AdvSceneSwitcher.condition.date.state.between="Arasında"
AdvSceneSwitcher.condition.sceneTransform="Sahne öğesi dönüşümü"
AdvSceneSwitcher.condition.sceneTransform.getTransform="Dönüşümü al"
AdvSceneSwitcher.condition.sceneTransform.regex="Normal ifadeler kullanın"
AdvSceneSwitcher.condition.sceneTransform.entry.line1="Açık{{scenes}}{{sources}} dönüşümle eşleşir"
AdvSceneSwitcher.condition.sceneTransform.entry.line2="{{settings}}"
AdvSceneSwitcher.condition.sceneTransform.entry.line3="{{regex}} {{getSettings}}"
@@ -254,7 +255,7 @@ AdvSceneSwitcher.condition.studioMode.state.notActive="Studyo modu aktif değil"
AdvSceneSwitcher.condition.studioMode.state.previewScene="Önizleme sahnesi"
AdvSceneSwitcher.condition.studioMode.entry="{{conditions}}{{scenes}}"
AdvSceneSwitcher.condition.openvr="Aç VR"
AdvSceneSwitcher.condition.errorStatus="Aç VR hatası: "
AdvSceneSwitcher.condition.openvr.errorStatus="Aç VR hatası: "
AdvSceneSwitcher.condition.openvr.entry.line1="HMD içinde ..."
AdvSceneSwitcher.condition.openvr.entry.line2="{{controls}}"
AdvSceneSwitcher.condition.openvr.entry.line3="HMD mevcut {{xPos}} x {{yPos}} x {{zPos}}"
@@ -273,7 +274,7 @@ AdvSceneSwitcher.action.audio.type.mute="Sessiz"
AdvSceneSwitcher.action.audio.type.unmute="Ses açmak"
AdvSceneSwitcher.action.audio.type.sourceVolume="Kaynak ses seviyesini ayarla"
AdvSceneSwitcher.action.audio.type.masterVolume="Ana ses seviyesini ayarla"
AdvSceneSwitcher.action.audio.entry="{{actions}} {{audioSources}} {{volume}}"
AdvSceneSwitcher.action.audio.entry="{{actions}}{{audioSources}}{{volume}}{{syncOffset}}{{monitorTypes}}"
AdvSceneSwitcher.action.recording="Kayıt"
AdvSceneSwitcher.action.recording.type.stop="Kayıt Durdur"
AdvSceneSwitcher.action.recording.type.start="Kayıt Başlat"
@@ -311,7 +312,7 @@ AdvSceneSwitcher.action.source="Kaynak"
AdvSceneSwitcher.action.source.type.enable="Etkin"
AdvSceneSwitcher.action.source.type.disable="Etkisiz"
AdvSceneSwitcher.action.source.type.settings="Ayarları yap"
AdvSceneSwitcher.action.source.entry="{{actions}} {{sources}}"
AdvSceneSwitcher.action.source.entry="{{actions}}{{sources}}{{settingsButtons}}"
AdvSceneSwitcher.action.source.warning="Uyarı: Kaynakların global olarak etkinleştirilmesi ve devre dışı bırakılması, OBS UI tarafından kontrol edilemez"
AdvSceneSwitcher.action.source.getSettings="Mevcut Ayarları yap"
AdvSceneSwitcher.action.media="Medya"
@@ -364,10 +365,6 @@ AdvSceneSwitcher.action.file="Dosya"
AdvSceneSwitcher.action.file.type.write="Yaz"
AdvSceneSwitcher.action.file.type.append="Ekle"
AdvSceneSwitcher.action.file.entry="{{actions}} {{filePath}}:"
AdvSceneSwitcher.action.previewScene="Önizleme sahnesini değiştir"
AdvSceneSwitcher.action.previewScene.entry="Önizleme sahnesini şu şekilde değiştir: {{scenes}}"
AdvSceneSwitcher.action.SceneSwap="Sahneyi değiştir (Studyo modu)"
AdvSceneSwitcher.action.SceneSwap.entry="Stüdyo modunda önizleme ve program sahnesini değiştir"
AdvSceneSwitcher.action.transition="Geçiş"
AdvSceneSwitcher.action.transition.entry.line2="{{setTransition}}Geçiş türünü ayarla {{transitions}}"
AdvSceneSwitcher.action.transition.entry.line3="{{setDuration}}Geçiş süresini şuna ayarla: {{duration}}saniyeler"
@@ -391,10 +388,6 @@ AdvSceneSwitcher.action.sceneCollection.warning="Not: Değişen sahne koleksiyon
; Transition Tab
AdvSceneSwitcher.transitionTab.title="Geçiş"
AdvSceneSwitcher.transitionTab.setTransitionBy="Geçişleri değiştirirken:"
AdvSceneSwitcher.transitionTab.transitionOverride="Geçiş geçersiz kılmaları ayarla"
AdvSceneSwitcher.transitionTab.adjustActiveTransitionType="Etkin geçiş türünü değiştir"
AdvSceneSwitcher.transitionTab.transitionBehaviorSelectionError="En az bir seçenek etkinleştirilmelidir: \n\n - Geçiş geçersiz kılmalarını kullan \n\n - Etkin geçiş türünü değiştir"
AdvSceneSwitcher.transitionTab.transitionForAToB="Sahne A'dan sahne B'ye otomatik sahne geçişi için geçişi kullanın"
AdvSceneSwitcher.transitionTab.transitionsHelp="<html><head/><body><p>Bu ayarlar <span style=\"font-style:italic;\">yalnızca</span>, sahne değiştiricinin neden olduğu geçişleri etkiler - <a href=\ Göz atın "https://obsproject.com/forum/resources/transition-table.1174/\"><span style=\" text-decoration: underline; color:#268bd2;\">Geçiş Tablosu</span></ a> bunu manuel sahne değişiklikleri için yapılandırmak istiyorsanız.<br/>Burada tanımlanan ayarlar, sahne değiştiricide başka bir yerde yapılandırılmış geçiş ayarlarına göre önceliklidir.<br/><br/>Yeni bir giriş eklemek için aşağıdaki artı simgesini tıklayın. .</p></body></html>"
AdvSceneSwitcher.transitionTab.defaultTransition="Sahne aktifse geçişi değiştir"

View File

@@ -24,13 +24,13 @@ AdvSceneSwitcher.generalTab.generalBehavior.onNoMetDelayTooltip="时间精度最
AdvSceneSwitcher.generalTab.generalBehavior.onNoMet.dontSwitch="不切换"
AdvSceneSwitcher.generalTab.generalBehavior.onNoMet.switchToRandom="切换到随机场景列表中任意随机场景"
AdvSceneSwitcher.generalTab.generalBehavior.onNoMet.switchTo="切换到"
AdvSceneSwitcher.generalTab.generalBehavior.cooldown="比赛结束多少秒后切换场景"
AdvSceneSwitcher.generalTab.generalBehavior.cooldownHint="在此期间,可能的匹配项将被忽略!"
AdvSceneSwitcher.generalTab.generalBehavior.verboseLogging="详细日志输出"
AdvSceneSwitcher.generalTab.generalBehavior.saveWindowGeo="保存窗口位置和大小"
AdvSceneSwitcher.generalTab.generalBehavior.showTrayNotifications="显示系统托盘通知"
AdvSceneSwitcher.generalTab.generalBehavior.disableUIHints="禁用UI提示"
AdvSceneSwitcher.generalTab.generalBehavior.hideLegacyTabs="隐藏可通过宏操作的分组栏"
AdvSceneSwitcher.generalTab.matchBehavior="场景匹配设置"
AdvSceneSwitcher.generalTab.priority="优先级"
AdvSceneSwitcher.generalTab.priority.description="切换场景优先级 (最上方的项优先级最高)"
AdvSceneSwitcher.generalTab.priority.threadPriority="使用线程优先级"
@@ -54,6 +54,11 @@ AdvSceneSwitcher.generalTab.priority.time="时间"
AdvSceneSwitcher.generalTab.priority.audio="音频"
AdvSceneSwitcher.generalTab.priority.video="视频"
AdvSceneSwitcher.generalTab.priority.macro="宏"
AdvSceneSwitcher.generalTab.transition="转场特效"
AdvSceneSwitcher.generalTab.setTransitionBy="更改转场特效时:"
AdvSceneSwitcher.generalTab.transitionOverride="在场景切换器中设定的转场特效优先级高于场景设定的转场特效"
AdvSceneSwitcher.generalTab.adjustActiveTransitionType="更改激活转场特效类型"
AdvSceneSwitcher.generalTab.transitionBehaviorSelectionError="必须至少启用一个选项:\n\n - 使用转场特效替代\n\n - 改变激活的转场特效类型"
; Macro Tab
AdvSceneSwitcher.macroTab.title="宏"
@@ -70,7 +75,7 @@ AdvSceneSwitcher.macroTab.add="添加新宏"
AdvSceneSwitcher.macroTab.name="名称:"
AdvSceneSwitcher.macroTab.run="运行宏"
AdvSceneSwitcher.macroTab.runFail="运行 \"%1\" 失败!\n其中一个操作失败或者宏已在运行."
AdvSceneSwitcher.macroTab.runInParallel="与其他宏并行运行宏"
AdvSceneSwitcher.macroTab.runInParallel="与其他宏同时运行宏"
AdvSceneSwitcher.macroTab.onChange="仅在条件更改时执行操作"
AdvSceneSwitcher.macroTab.defaultname="宏 %1"
AdvSceneSwitcher.macroTab.exists="宏名称已存在"
@@ -79,6 +84,15 @@ AdvSceneSwitcher.macroTab.expandAll="全部展开"
AdvSceneSwitcher.macroTab.collapseAll="全部收回"
AdvSceneSwitcher.macroTab.maximize="最大化"
AdvSceneSwitcher.macroTab.minimize="最小化"
AdvSceneSwitcher.macroTab.highlightExecutedMacros="突出显示最近执行的宏"
AdvSceneSwitcher.macroTab.highlightTrueConditions="突出显示最近运算为 真 的当前选定宏的条件"
AdvSceneSwitcher.macroTab.highlightPerformedActions="突出显示当前选定宏的最近执行的操作"
AdvSceneSwitcher.macroTab.newMacroRegisterHotkey="使用快捷键控制新增宏的暂停状态"
AdvSceneSwitcher.macroTab.currentDisableHotkeys="使用快捷键控制所选宏的暂停状态"
; Macro List
AdvSceneSwitcher.macroList.deleted="删除"
AdvSceneSwitcher.macroList.duplicate="\"%1\" 已选择!"
; Macro Logic
AdvSceneSwitcher.logic.none="忽略条目"
@@ -98,30 +112,40 @@ AdvSceneSwitcher.condition.audio.state.mute="静音"
AdvSceneSwitcher.condition.audio.state.unmute="取消静音"
AdvSceneSwitcher.condition.audio.type.output="输出音频"
AdvSceneSwitcher.condition.audio.type.volume="配置音量级别"
AdvSceneSwitcher.condition.audio.entry="{{checkType}} 的 {{audioSources}} 是 {{condition}}{{volume}}{{syncOffset}}{{monitorTypes}}"
AdvSceneSwitcher.condition.cursor="屏幕区域"
AdvSceneSwitcher.condition.cursor.type.region="当前位置"
AdvSceneSwitcher.condition.cursor.type.moving="正在移动"
AdvSceneSwitcher.condition.cursor.showFrame="显示帧率"
AdvSceneSwitcher.condition.cursor.hideFrame="隐藏帧率"
AdvSceneSwitcher.condition.cursor.entry.line1="鼠标 处于{{conditions}} {{minX}} {{minY}} {{maxX}} {{maxY}} - {{toggleFrameButton}}"
AdvSceneSwitcher.condition.cursor.entry.line1="鼠标 处于{{conditions}}{{buttons}}{{minX}}{{minY}}{{maxX}}{{maxY}}{{toggleFrameButton}}"
AdvSceneSwitcher.condition.cursor.entry.line2="鼠标当前位于 {{xPos}} x {{yPos}}"
AdvSceneSwitcher.condition.scene="场景"
AdvSceneSwitcher.condition.scene.type.current="当前场景是"
AdvSceneSwitcher.condition.scene.type.previous="上一个场景是"
AdvSceneSwitcher.condition.scene.type.changed="场景改变了"
AdvSceneSwitcher.condition.scene.type.notChanged="场景没有改变"
AdvSceneSwitcher.condition.scene.entry.line1="{{sceneType}} {{scenes}}"
AdvSceneSwitcher.condition.scene.currentSceneTransitionBehaviour="在转场过程中检查转场源场景"
AdvSceneSwitcher.condition.scene.previousSceneTransitionBehaviour="在转场过程中检查转场源场景"
AdvSceneSwitcher.condition.scene.entry.line1="{{sceneType}}{{scenes}}{{pattern}}"
AdvSceneSwitcher.condition.scene.entry.line2="{{useTransitionTargetScene}}"
AdvSceneSwitcher.condition.window="窗口"
AdvSceneSwitcher.condition.window.entry.line1="{{windows}} 存在..."
AdvSceneSwitcher.condition.window.entry.line2="... 并且是 {{fullscreen}} 全屏 {{maximized}} 最大化 {{focused}} 获得焦点 {{windowFocusChanged}} 焦点窗口已更改"
AdvSceneSwitcher.condition.window.entry.line3="当前焦点窗口: {{focusWindow}}"
AdvSceneSwitcher.condition.file="文件"
AdvSceneSwitcher.condition.file.type.match="内容完全匹配"
AdvSceneSwitcher.condition.file.type.contentChange="内容已更改"
AdvSceneSwitcher.condition.file.type.dateChange="修改日期已更改"
AdvSceneSwitcher.condition.file.entry.line1="文件内容来自{{fileType}}{{filePath}}{{conditions}}"
AdvSceneSwitcher.condition.file.entry.line2="{{matchText}}"
AdvSceneSwitcher.condition.file.entry.line3="{{useRegex}} {{checkModificationDate}} {{checkFileContent}}"
AdvSceneSwitcher.condition.media="媒体"
AdvSceneSwitcher.condition.media.anyOnScene="任何媒体来源"
AdvSceneSwitcher.condition.media.allOnScene="所有媒体来源"
AdvSceneSwitcher.condition.media.matchOnChange="仅在更改时匹配(注意:此选项将在未来版本中删除-请改用时间限制)"
AdvSceneSwitcher.condition.media.inconsistencyInfo="不幸的是并非所有媒体源类型的行为都相同例如媒体源与VLC视频源“停止”状态。\n所以请尝试适合您的设置"
AdvSceneSwitcher.condition.media.entry="{{sourceTypes}}{{mediaSources}}{{scenes}}的状态是{{states}}和{{timeRestrictions}}{{time}}"
AdvSceneSwitcher.condition.video="视频"
AdvSceneSwitcher.condition.video.condition.match="完全匹配"
AdvSceneSwitcher.condition.video.condition.differ="不匹配"
@@ -130,14 +154,20 @@ AdvSceneSwitcher.condition.video.condition.hasNotChanged="没有改变"
AdvSceneSwitcher.condition.video.condition.noImage="没有输出"
AdvSceneSwitcher.condition.video.condition.pattern="匹配模式"
AdvSceneSwitcher.condition.video.condition.object="包含对象"
AdvSceneSwitcher.condition.video.condition.brightness="亮度"
AdvSceneSwitcher.condition.video.askFileAction="是否要使用现有文件或创建当前选定源的屏幕截图?"
AdvSceneSwitcher.condition.video.askFileAction.file="使用现有文件"
AdvSceneSwitcher.condition.video.askFileAction.screenshot="创建屏幕截图"
AdvSceneSwitcher.condition.video.reduceLatency="减少匹配延迟"
AdvSceneSwitcher.condition.video.reduceLatency.tooltip="启用将减少匹配延迟,但总体上会减慢所有宏的总体条件检查."
AdvSceneSwitcher.condition.video.usePatternForChangedCheck="使用模式匹配"
AdvSceneSwitcher.condition.video.usePatternForChangedCheck.tooltip="这将允许您控制图像必须更改多少才能使条件为真."
AdvSceneSwitcher.condition.video.patternThreshold="阈值: "
AdvSceneSwitcher.condition.video.patternThresholdDescription="较高的阈值意味着模式需要更紧密地匹配视频源."
AdvSceneSwitcher.condition.video.patternThresholdUseAlphaAsMask="使用alpha通道作为图案的遮罩."
AdvSceneSwitcher.condition.video.brightnessThreshold="平均亮度:"
AdvSceneSwitcher.condition.video.brightnessThresholdDescription="较高的值表示图像较亮,较低的值表示图像较暗。"
AdvSceneSwitcher.condition.video.currentBrightness="当前平均亮度: %1"
AdvSceneSwitcher.condition.video.objectScaleThreshold="模型比例: "
AdvSceneSwitcher.condition.video.objectScaleThresholdDescription="较低的模比例将导致更多的匹配但会导致更高的CPU负载."
AdvSceneSwitcher.condition.video.minNeighborDescription="较高的“最小区域”值将导致较少但质量较高的匹配."
@@ -150,10 +180,18 @@ AdvSceneSwitcher.condition.video.patternMatchSuccess="Pattern is highlighted in
AdvSceneSwitcher.condition.video.objectMatchFail="找不到对象!"
AdvSceneSwitcher.condition.video.objectMatchSuccess="Object is highlighted in red"
AdvSceneSwitcher.condition.video.modelLoadFail="无法加载模型数据!"
AdvSceneSwitcher.condition.video.entry="{{videoSources}} {{condition}} {{imagePath}}"
AdvSceneSwitcher.condition.video.entry="{{videoInputTypes}}{{sources}}{{scenes}}{{condition}}{{imagePath}}"
AdvSceneSwitcher.condition.video.entry.modelPath="模型数据 (haar级联分类器): {{modelDataPath}}"
AdvSceneSwitcher.condition.video.entry.minNeighbor="最小区域: {{minNeighbors}}"
AdvSceneSwitcher.condition.video.entry.throttle="{{throttleEnable}}通过只执行每一次检查来减少CPU负载 {{throttleCount}} 毫秒"
AdvSceneSwitcher.condition.video.entry.checkAreaEnable="仅在区域内执行检查"
AdvSceneSwitcher.condition.video.entry.checkArea="{{checkAreaEnable}}{{checkArea}}{{selectArea}}"
AdvSceneSwitcher.condition.video.minSize="最小尺寸:"
AdvSceneSwitcher.condition.video.maxSize="最大尺寸:"
AdvSceneSwitcher.condition.video.selectArea="选择区域"
AdvSceneSwitcher.condition.video.selectArea.status="仅检查突出显示的区域"
AdvSceneSwitcher.condition.video.width="宽度"
AdvSceneSwitcher.condition.video.height="长度"
AdvSceneSwitcher.condition.stream="推流"
AdvSceneSwitcher.condition.stream.state.start="推流启动"
AdvSceneSwitcher.condition.stream.state.stop="推流停止"
@@ -166,7 +204,8 @@ AdvSceneSwitcher.condition.record.state.pause="录制暂停"
AdvSceneSwitcher.condition.record.state.stop="录制停止"
AdvSceneSwitcher.condition.record.entry="{{recordState}}"
AdvSceneSwitcher.condition.process="进程"
AdvSceneSwitcher.condition.process.entry="{{processes}} 正在运行 {{focused}} 而且是获得焦点"
AdvSceneSwitcher.condition.process.entry="{{processes}} 正在运行 {{focused}} 而且是获得焦点"
AdvSceneSwitcher.condition.process.entry.focus="当前焦点进程: {{focusProcess}}"
AdvSceneSwitcher.condition.idle="闲置检测"
AdvSceneSwitcher.condition.idle.entry="没有键盘或鼠标输入{{duration}}"
AdvSceneSwitcher.condition.pluginState="插件状态"
@@ -179,26 +218,32 @@ AdvSceneSwitcher.condition.timer.type.fixed="固定"
AdvSceneSwitcher.condition.timer.type.random="随机"
AdvSceneSwitcher.condition.timer.pause="暂停"
AdvSceneSwitcher.condition.timer.continue="继续"
AdvSceneSwitcher.condition.timer.entry.line1.fixed="{{type}} 持续时间 {{duration}} 后结束"
AdvSceneSwitcher.condition.timer.entry.line1.random="{{type}} 持续时间从 {{duration}} 到 {{duration2}} 后结束"
AdvSceneSwitcher.condition.timer.entry.line2="剩余时间: {{remaining}} 秒"
AdvSceneSwitcher.condition.timer.entry.line3="{{pauseContinue}} {{reset}} {{saveRemaining}} 保存剩余时间 {{autoReset}} 达到时间后自动重置计时器"
AdvSceneSwitcher.condition.timer.reset="重置"
AdvSceneSwitcher.condition.macro="宏"
AdvSceneSwitcher.condition.macro.type.count="计数"
AdvSceneSwitcher.condition.macro.type.state="状态"
AdvSceneSwitcher.condition.macro.type.multiState="多线宏状态"
AdvSceneSwitcher.condition.macro.type.selection="条件类型: {{types}}"
AdvSceneSwitcher.condition.macro.count.type.below="少于"
AdvSceneSwitcher.condition.macro.count.type.above="大于"
AdvSceneSwitcher.condition.macro.count.type.equal="等于"
AdvSceneSwitcher.condition.macro.state.type.below="少于"
AdvSceneSwitcher.condition.macro.state.type.above="大于"
AdvSceneSwitcher.condition.macro.state.type.equal="等于"
AdvSceneSwitcher.condition.macro.count.reset="重置"
AdvSceneSwitcher.condition.macro.pausedWarning="所选宏当前已暂停!"
AdvSceneSwitcher.condition.macro.state.entry="条件 {{macros}} 是真的"
AdvSceneSwitcher.condition.macro.state.entry="条件 {{macros}} 运行结果为真"
AdvSceneSwitcher.condition.macro.multistate.entry="条件 {{multiStateConditions}}{{multiStateCount}} 以下宏运行结果为真:"
AdvSceneSwitcher.condition.macro.count.entry.line1="{{macros}} 运行 {{conditions}} {{count}} 次(相对于下面计数匹配)"
AdvSceneSwitcher.condition.macro.count.entry.line2="当前计数: {{currentCount}} {{resetCount}}"
AdvSceneSwitcher.condition.source="源"
AdvSceneSwitcher.condition.source="源"
AdvSceneSwitcher.condition.source.type.active="是激活状态"
AdvSceneSwitcher.condition.source.type.showing="是显示状态"
AdvSceneSwitcher.condition.source.type.settings="设置完全匹配"
AdvSceneSwitcher.condition.source.regex="使用正则表达式"
AdvSceneSwitcher.condition.source.getSettings="获取当前设置"
AdvSceneSwitcher.condition.source.entry.line1="{{sources}} {{conditions}}"
AdvSceneSwitcher.condition.source.entry.line2="{{settings}}"
@@ -211,7 +256,6 @@ AdvSceneSwitcher.condition.filter="滤镜"
AdvSceneSwitcher.condition.filter.type.active="已启用"
AdvSceneSwitcher.condition.filter.type.showing="已关闭"
AdvSceneSwitcher.condition.filter.type.settings="设置完全匹配"
AdvSceneSwitcher.condition.filter.regex="使用正则表达式"
AdvSceneSwitcher.condition.filter.getSettings="获取当前设置"
AdvSceneSwitcher.condition.filter.entry.line1="在 {{sources}} {{filters}} {{conditions}}"
AdvSceneSwitcher.condition.filter.entry.line2="{{settings}}"
@@ -250,11 +294,13 @@ AdvSceneSwitcher.condition.date.ignoreDate="如果未选中,日期组件将被
AdvSceneSwitcher.condition.date.ignoreTime="如果未选中,时间组件将被忽略"
AdvSceneSwitcher.condition.date.showAdvancedSettings="显示高级设置"
AdvSceneSwitcher.condition.date.showSimpleSettings="显示简单设置"
AdvSceneSwitcher.condition.date.entry.simple="在 {{dayOfWeek}} {{weekCondition}} {{ignoreWeekTime}}{{weekTime}}"
AdvSceneSwitcher.condition.date.entry.advanced="{{condition}} {{ignoreDate}}{{date}} {{ignoreTime}}{{time}} {{separator}} {{date2}} {{time2}}"
AdvSceneSwitcher.condition.date.entry.repeat="{{repeat}} 在匹配到日期时间后,每隔 {{duration}} 重复一次"
AdvSceneSwitcher.condition.date.entry.nextMatchDate="Next match at: %1"
AdvSceneSwitcher.condition.date.entry.updateOnRepeat="{{updateOnRepeat}} On repeat update selected date to repeat date"
AdvSceneSwitcher.condition.sceneTransform="场景项目被改变"
AdvSceneSwitcher.condition.sceneTransform.getTransform="内容或者设置被改变了"
AdvSceneSwitcher.condition.sceneTransform.regex="使用正则表达式"
AdvSceneSwitcher.condition.sceneTransform.entry.line1="在 {{scenes}} {{sources}} 匹配变换"
AdvSceneSwitcher.condition.sceneTransform.entry.line2="{{settings}}"
AdvSceneSwitcher.condition.sceneTransform.entry.line3="{{regex}} {{getSettings}}"
@@ -277,7 +323,7 @@ AdvSceneSwitcher.condition.studioMode.state.notActive="工作室模式未处于
AdvSceneSwitcher.condition.studioMode.state.previewScene="预览场景是"
AdvSceneSwitcher.condition.studioMode.entry="{{conditions}}{{scenes}}"
AdvSceneSwitcher.condition.openvr="OpenVR"
AdvSceneSwitcher.condition.errorStatus="OpenVR 错误: "
AdvSceneSwitcher.condition.openvr.errorStatus="OpenVR 错误: "
AdvSceneSwitcher.condition.openvr.entry.line1="HMD在 ..."
AdvSceneSwitcher.condition.openvr.entry.line2="{{controls}}"
AdvSceneSwitcher.condition.openvr.entry.line3="HMD目前处于 {{xPos}} x {{yPos}} x {{zPos}}"
@@ -300,10 +346,27 @@ AdvSceneSwitcher.condition.stats.condition.equals="等于"
AdvSceneSwitcher.condition.stats.condition.below="少于"
AdvSceneSwitcher.condition.stats.dockHint="你可以打开“统计”窗口查看当前状态"
AdvSceneSwitcher.condition.stats.entry="{{stats}} is {{condition}} {{value}}"
AdvSceneSwitcher.condition.profile="配置文件"
AdvSceneSwitcher.condition.profile.entry="当前活动的配置文件为 {{profiles}}"
AdvSceneSwitcher.condition.websocket="websocket"
AdvSceneSwitcher.condition.websocket.type.request="request"
AdvSceneSwitcher.condition.websocket.type.event="event"
AdvSceneSwitcher.condition.websocket.useRegex="使用正则表达式"
AdvSceneSwitcher.condition.websocket.entry.request="{{type}} 收到内容:"
AdvSceneSwitcher.condition.websocket.entry.event="{{type}} 收到自 {{connection}}:"
AdvSceneSwitcher.condition.variable="变量"
AdvSceneSwitcher.condition.variable.type.compare="等于"
AdvSceneSwitcher.condition.variable.type.empty="为空"
AdvSceneSwitcher.condition.variable.type.number="是一个数字"
AdvSceneSwitcher.condition.variable.type.lessThan="小于"
AdvSceneSwitcher.condition.variable.type.greaterThan="大于"
AdvSceneSwitcher.condition.variable.type.valueChanged="值已更改"
AdvSceneSwitcher.condition.variable.entry="{{variables}}{{conditions}}{{strValue}}{{numValue}}{{variables2}}"
; Macro Actions
AdvSceneSwitcher.action.switchScene="切换场景"
AdvSceneSwitcher.action.scene.entry="切换场景 {{scenes}} 使用 {{transitions}} 时长 {{duration}} 秒"
AdvSceneSwitcher.action.scene.entry.noDuration="切换到场景{{scenes}}使用{{transitions}}"
AdvSceneSwitcher.action.scene.blockUntilTransitionDone="等待目标场景的过渡完成"
AdvSceneSwitcher.action.wait="等待"
AdvSceneSwitcher.action.wait.type.fixed="固定数值"
@@ -315,15 +378,24 @@ AdvSceneSwitcher.action.audio.type.mute="静音"
AdvSceneSwitcher.action.audio.type.unmute="取消静音"
AdvSceneSwitcher.action.audio.type.sourceVolume="来源音量"
AdvSceneSwitcher.action.audio.type.masterVolume="主音量"
AdvSceneSwitcher.action.audio.entry="{{actions}} {{audioSources}} {{volume}}"
AdvSceneSwitcher.action.audio.fade.type.duration="持续时间"
AdvSceneSwitcher.action.audio.fade.type.rate="百分比"
AdvSceneSwitcher.action.audio.fade.duration="{{fade}}淡出 {{fadeTypes}} {{duration}} 秒."
AdvSceneSwitcher.action.audio.fade.rate="{{fade}}淡出 {{fadeTypes}} {{rate}}每秒."
AdvSceneSwitcher.action.audio.fade.wait="等待淡入淡出完成."
AdvSceneSwitcher.action.audio.fade.abort="中止已处于活动状态的淡入淡出."
AdvSceneSwitcher.action.audio.entry="{{actions}}{{audioSources}}{{volume}}{{syncOffset}}{{monitorTypes}}"
AdvSceneSwitcher.action.recording="录制"
AdvSceneSwitcher.action.recording.type.stop="停止录制"
AdvSceneSwitcher.action.recording.type.start="开始录制"
AdvSceneSwitcher.action.recording.type.pause="暂停录制"
AdvSceneSwitcher.action.recording.type.unpause="取消录制暂停"
AdvSceneSwitcher.action.recording.type.split="分割录制文件"
AdvSceneSwitcher.action.recording.pause.hint="请注意,根据您的录制设置,您可能无法暂停录制"
AdvSceneSwitcher.action.recording.split.hint="确保先在OBS设置中启用自动分割文件!"
AdvSceneSwitcher.action.recording.entry="{{actions}}{{pauseHint}}{{splitHint}}"
AdvSceneSwitcher.action.replay="回放缓冲区"
AdvSceneSwitcher.action.replay.saveWarn="警告:保存过于频繁可能会导致重播缓冲区实际上未保存!"
AdvSceneSwitcher.action.replay.type.stop="停止回放缓冲区"
AdvSceneSwitcher.action.replay.type.start="启动回放缓冲区"
AdvSceneSwitcher.action.replay.type.save="保存回放缓冲区"
@@ -342,6 +414,7 @@ AdvSceneSwitcher.action.sceneVisibility.type.show="显示"
AdvSceneSwitcher.action.sceneVisibility.type.hide="隐藏"
AdvSceneSwitcher.action.sceneVisibility.type.source="来源"
AdvSceneSwitcher.action.sceneVisibility.type.sourceGroup="全部"
AdvSceneSwitcher.action.sceneVisibility.entry="设置{{scenes}}{{actions}}{{sourceTypes}}{{sources}}{{sourceGroups}}"
AdvSceneSwitcher.action.filter="滤镜"
AdvSceneSwitcher.action.filter.type.enable="开启"
AdvSceneSwitcher.action.filter.type.disable="关闭"
@@ -352,7 +425,9 @@ AdvSceneSwitcher.action.source="来源"
AdvSceneSwitcher.action.source.type.enable="启用"
AdvSceneSwitcher.action.source.type.disable="停用"
AdvSceneSwitcher.action.source.type.settings="配置设置"
AdvSceneSwitcher.action.source.entry="{{actions}} {{sources}}"
AdvSceneSwitcher.action.source.type.refreshSettings="刷新源设置"
AdvSceneSwitcher.action.source.type.refreshSettings.tooltip="可用于刷新浏览器、媒体等源"
AdvSceneSwitcher.action.source.entry="{{actions}}{{sources}}{{settingsButtons}}"
AdvSceneSwitcher.action.source.warning="警告OBS UI无法控制全局启用和禁用源注意通过这个设置禁用我测试你必须通过这个在启用才行不然点击小眼睛你无法显示"
AdvSceneSwitcher.action.source.getSettings="获取当前设置"
AdvSceneSwitcher.action.media="媒体"
@@ -362,6 +437,7 @@ AdvSceneSwitcher.action.media.type.stop="停止"
AdvSceneSwitcher.action.media.type.restart="重新启动"
AdvSceneSwitcher.action.media.type.next="下一个"
AdvSceneSwitcher.action.media.type.previous="上一个"
AdvSceneSwitcher.action.media.type.seek="时间轴定位"
AdvSceneSwitcher.action.media.entry="{{actions}}{{duration}}{{mediaSources}}"
AdvSceneSwitcher.action.macro="宏"
AdvSceneSwitcher.action.macro.type.pause="暂停"
@@ -375,6 +451,7 @@ AdvSceneSwitcher.action.pluginState.type.stop="停止高级场景切换插件"
AdvSceneSwitcher.action.pluginState.type.noMatch="没有匹配项:"
AdvSceneSwitcher.action.pluginState.type.import="从设置导入"
AdvSceneSwitcher.action.pluginState.importWarning="注意:当“设置”窗口打开时,操作将被忽略。"
AdvSceneSwitcher.action.pluginState.entry="{{actions}}{{values}}{{scenes}}{{settings}}{{settingsWarning}}"
AdvSceneSwitcher.action.virtualCamera="虚拟摄像机"
AdvSceneSwitcher.action.virtualCamera.type.stop="停止虚拟摄像机"
AdvSceneSwitcher.action.virtualCamera.type.start="启动虚拟摄像机"
@@ -405,13 +482,20 @@ AdvSceneSwitcher.action.file="文件"
AdvSceneSwitcher.action.file.type.write="写入"
AdvSceneSwitcher.action.file.type.append="追加写入"
AdvSceneSwitcher.action.file.entry="{{actions}} 到 {{filePath}}:"
AdvSceneSwitcher.action.previewScene="切换预览场景"
AdvSceneSwitcher.action.previewScene.entry="将预览场景切换到 {{scenes}}"
AdvSceneSwitcher.action.SceneSwap="交换场景 (Studio mode)"
AdvSceneSwitcher.action.SceneSwap.entry="在studio模式下交换预览和编程场景"
AdvSceneSwitcher.action.transition="过场特效"
AdvSceneSwitcher.action.studioMode="工作室模式"
AdvSceneSwitcher.action.studioMode.type.swap="切换预览和编程场景"
AdvSceneSwitcher.action.studioMode.type.setScene="将预览场景设置为"
AdvSceneSwitcher.action.studioMode.type.enable="启用工作室模式"
AdvSceneSwitcher.action.studioMode.type.disable="停用工作室模式"
AdvSceneSwitcher.action.studioMode.entry="{{actions}}{{scenes}}"
AdvSceneSwitcher.action.transition="转场特效"
AdvSceneSwitcher.action.transition.type.scene="场景过渡"
AdvSceneSwitcher.action.transition.type.sceneOverride="场景过渡覆盖"
AdvSceneSwitcher.action.transition.type.sourceShow="源显示过渡"
AdvSceneSwitcher.action.transition.type.sourceHide="源隐藏过渡"
AdvSceneSwitcher.action.transition.entry.line1="修改 {{type}}{{scenes}}{{sources}}"
AdvSceneSwitcher.action.transition.entry.line2="{{setTransition}}将过场特效类型设置为 {{transitions}}"
AdvSceneSwitcher.action.transition.entry.line3="{{setDuration}}将过场特效持续时间设置为 {{duration}}seconds"
AdvSceneSwitcher.action.transition.entry.line3="{{setDuration}}将过场特效持续时间设置为 {{duration}}"
AdvSceneSwitcher.action.timer="计时器"
AdvSceneSwitcher.action.timer.type.pause="暂停"
AdvSceneSwitcher.action.timer.type.continue="继续"
@@ -419,10 +503,17 @@ AdvSceneSwitcher.action.timer.type.reset="重置"
AdvSceneSwitcher.action.timer.type.setTimeRemaining="设置剩余时间"
AdvSceneSwitcher.action.timer.entry="{{timerAction}} 定时在 {{macros}} {{duration}}"
AdvSceneSwitcher.action.random="随机"
AdvSceneSwitcher.action.random.entry="随机运行以下任何宏(忽略暂停的宏)"
AdvSceneSwitcher.action.systray="系统托盘通知"
AdvSceneSwitcher.action.systray.entry="显示通知: {{message}}"
AdvSceneSwitcher.action.screenshot="截图"
AdvSceneSwitcher.action.screenshot.save.default="默认"
AdvSceneSwitcher.action.screenshot.save.custom="其他"
AdvSceneSwitcher.action.screenshot.type.source="来源"
AdvSceneSwitcher.action.screenshot.type.scene="场景"
AdvSceneSwitcher.action.screenshot.mainOutput="OBS 预览画面"
AdvSceneSwitcher.action.screenshot.blackscreenNote="源和场景并不总是呈现,可能会导致屏幕截图的某些部分保持空白。"
AdvSceneSwitcher.action.screenshot.entry="截图{{targetType}}{{sources}}{{scenes}}保存在{{saveType}}位置"
AdvSceneSwitcher.action.profile="配置文件"
AdvSceneSwitcher.action.profile.entry="将活动配置文件切换到 {{profiles}}"
AdvSceneSwitcher.action.sceneCollection="场景集合"
@@ -434,15 +525,31 @@ AdvSceneSwitcher.action.sequence.status="上次执行的宏:%1 - 要执行的
AdvSceneSwitcher.action.sequence.status.none="无"
AdvSceneSwitcher.action.sequence.restart="到达列表末尾后,从开头重新启动"
AdvSceneSwitcher.action.sequence.continueFrom="继续所选项目"
AdvSceneSwitcher.action.websocket="websocket"
AdvSceneSwitcher.action.websocket.type.request="request"
AdvSceneSwitcher.action.websocket.type.event="event"
AdvSceneSwitcher.action.websocket.entry.request="发送场景切换器 {{type}}{{connection}}"
AdvSceneSwitcher.action.websocket.entry.event="发送场景切换器 {{type}} 到连接的客户端"
AdvSceneSwitcher.action.http="Http"
AdvSceneSwitcher.action.http.type.get="GET"
AdvSceneSwitcher.action.http.type.post="POST"
AdvSceneSwitcher.action.http.entry.line1="发送 {{method}} 到 {{url}}"
AdvSceneSwitcher.action.http.entry.line2="超时: {{timeout}} 秒"
AdvSceneSwitcher.action.variable="变量"
AdvSceneSwitcher.action.variable.type.set="设置"
AdvSceneSwitcher.action.variable.type.append="追加"
AdvSceneSwitcher.action.variable.type.appendVar="追加变量"
AdvSceneSwitcher.action.variable.type.increment="增加"
AdvSceneSwitcher.action.variable.type.decrement="减少"
AdvSceneSwitcher.action.variable.entry="{{actions}}{{variables}}{{variables2}}{{strValue}}{{numValue}}{{segmentIndex}}"
; Transition Tab
AdvSceneSwitcher.transitionTab.title="转场特效"
AdvSceneSwitcher.transitionTab.setTransitionBy="更改转场特效时:"
AdvSceneSwitcher.transitionTab.transitionOverride="在场景切换器中设定的转场特效优先级高于场景设定的转场特效"
AdvSceneSwitcher.transitionTab.adjustActiveTransitionType="更改激活转场特效类型"
AdvSceneSwitcher.transitionTab.transitionForAToB="当自动从场景A切换到场景B时使用的转场特效"
AdvSceneSwitcher.transitionTab.transitionsHelp="<html><head/><body><p>这里的设定<span style=\"font-style:bold;\">只影响由场景切换器引起的转场有效</span>,不影响你手动的引起的转场。<br/>在这里设定的转场特效优先级高于场景切换器其他地方设置的<br/><br/>单击加号添加项目.</p></body></html>"
AdvSceneSwitcher.transitionTab.defaultTransition="当切换到这个场景时修改默认转场特效"
AdvSceneSwitcher.transitionTab.entry="切换自 {{scenes}} 到 {{scenes2}} 使用 {{transitions}} 持续时间为 {{duration}}"
AdvSceneSwitcher.transitionTab.defaultTransitionEntry="当场景 {{scenes}} 被激活时更改默认转场特效为 {{transitions}}"
AdvSceneSwitcher.transitionTab.defaultTransitionsHelp="单击加号添加项目."
AdvSceneSwitcher.transitionTab.defaultTransition.delay="场景更改 {{defTransitionDelay}} 后切换转换特效."
@@ -498,6 +605,7 @@ AdvSceneSwitcher.mediaTab.states.Paused="暂停"
AdvSceneSwitcher.mediaTab.states.stopped="停止"
AdvSceneSwitcher.mediaTab.states.ended="结束"
AdvSceneSwitcher.mediaTab.states.error="错误"
AdvSceneSwitcher.mediaTab.states.playlistEnd="已结束(播放列表)"
AdvSceneSwitcher.mediaTab.states.any="任意"
AdvSceneSwitcher.mediaTab.timeRestriction.none="无"
AdvSceneSwitcher.mediaTab.timeRestriction.shorter="播放时间小于"
@@ -582,6 +690,7 @@ AdvSceneSwitcher.audioTab.title="音频"
AdvSceneSwitcher.audioTab.condition.above="高于"
AdvSceneSwitcher.audioTab.condition.below="低于"
AdvSceneSwitcher.audioTab.ignoreInactiveSource="除非源处于非活动状态"
AdvSceneSwitcher.audioTab.entry="当音量 {{audioSources}} 在 {{condition}} {{volumeWidget}} 时 {{duration}} 秒后 切换至 {{scenes}} 使用 {{transitions}} {{ignoreInactiveSource}}"
AdvSceneSwitcher.audioTab.multiMatchfallbackCondition="如果多个条目匹配 ..."
AdvSceneSwitcher.audioTab.multiMatchfallback="... {{duration}} 秒切换到 {{scenes}} 使用 {{transitions}}"
AdvSceneSwitcher.audioTab.help="此选项卡允许您根据源音量切换场景。\n例如如果麦克风音量达到某个阈值您可以自动切换到其他场景。\n\n单击高亮显示的加号继续."
@@ -675,6 +784,8 @@ AdvSceneSwitcher.hotkey.startStopToggleSwitcherHotkey="切换(启动/停止)高
AdvSceneSwitcher.hotkey.macro.pause="暂停宏 %1"
AdvSceneSwitcher.hotkey.macro.unpause="取消暂停宏 %1"
AdvSceneSwitcher.hotkey.macro.togglePause="切换暂停宏 %1"
AdvSceneSwitcher.hotkey.upMacroSegmentHotkey="上移选择宏"
AdvSceneSwitcher.hotkey.downMacroSegmentHotkey="下移选择宏"
AdvSceneSwitcher.hotkey.removeMacroSegmentHotkey="删除选定的宏"
AdvSceneSwitcher.askBackup="检测到新版本的高级场景切换程序。\n是否要创建旧设置的备份?"
@@ -683,17 +794,60 @@ AdvSceneSwitcher.askForMacro="选择宏 {{macroSelection}}"
AdvSceneSwitcher.close="关闭"
AdvSceneSwitcher.browse="浏览文件"
AdvSceneSwitcher.item.select="--选择项目--"
AdvSceneSwitcher.item.add="添加新项目"
AdvSceneSwitcher.item.rename="重命名"
AdvSceneSwitcher.item.remove="删除"
AdvSceneSwitcher.item.properties="属性"
AdvSceneSwitcher.item.newName="新名称:"
AdvSceneSwitcher.item.emptyName="不允许使用空名称!"
AdvSceneSwitcher.item.nameNotAvailable="名称不可用!"
AdvSceneSwitcher.item.nameReserved="保留名称!"
AdvSceneSwitcher.variable.select="--选择变量--"
AdvSceneSwitcher.variable.add="增加新变量"
AdvSceneSwitcher.variable.name="名称:"
AdvSceneSwitcher.variable.value="当前值:"
AdvSceneSwitcher.variable.save="保存 / 载入 行为"
AdvSceneSwitcher.variable.save.dontSave="不保存变量值"
AdvSceneSwitcher.variable.save.save="保存变量值"
AdvSceneSwitcher.variable.save.default="设置变量值"
AdvSceneSwitcher.connection.select="--选择连接--"
AdvSceneSwitcher.connection.add="增加新连接"
AdvSceneSwitcher.connection.name="名称:"
AdvSceneSwitcher.connection.address="地址:"
AdvSceneSwitcher.connection.port="端口:"
AdvSceneSwitcher.connection.password="密码:"
AdvSceneSwitcher.connection.reconnect="自动重新连接:"
AdvSceneSwitcher.connection.reconnectDelay="自动重新连接延迟:"
AdvSceneSwitcher.connection.connectOnStart="启动时自动连接:"
AdvSceneSwitcher.connection.test="测试连接"
AdvSceneSwitcher.connection.status.disconnected="断开"
AdvSceneSwitcher.connection.status.connecting="连接"
AdvSceneSwitcher.connection.status.connected="已连接,但未经过身份验证"
AdvSceneSwitcher.connection.status.authenticated="已连接,并经过身份验证"
AdvSceneSwitcher.regex.enable="启用正则表达式"
AdvSceneSwitcher.regex.partialMatch="允许部分匹配"
AdvSceneSwitcher.regex.caseInsensitive="匹配不区分大小写"
AdvSceneSwitcher.regex.dotMatchNewline=". 匹配换行符"
AdvSceneSwitcher.regex.multiLine="^ 和 $ 是匹配行的开始/结束"
AdvSceneSwitcher.regex.extendedPattern="启用Qt的扩展模式语法"
AdvSceneSwitcher.selectScene="--选择场景--"
AdvSceneSwitcher.selectPreviousScene="上个场景"
AdvSceneSwitcher.selectCurrentScene="当前场景"
AdvSceneSwitcher.selectPreviewScene="预览场景"
AdvSceneSwitcher.selectAnyScene="任何场景"
AdvSceneSwitcher.currentTransition="当前转场特效"
AdvSceneSwitcher.anyTransition="任何转场特效"
AdvSceneSwitcher.selectTransition="--选择转场特效--"
AdvSceneSwitcher.selectWindow="--选择窗口--"
AdvSceneSwitcher.selectSource="--选择源--"
AdvSceneSwitcher.selectSource="--选择源--"
AdvSceneSwitcher.selectAudioSource="--选择音频源--"
AdvSceneSwitcher.selectVideoSource="--选择视频源--"
AdvSceneSwitcher.OBSVideoOutput="OBS的主要输出"
AdvSceneSwitcher.selectMediaSource="--选择媒体源--"
AdvSceneSwitcher.selectProcess="--选择进程--"
AdvSceneSwitcher.selectFilter="--选择滤镜--"
@@ -703,6 +857,7 @@ AdvSceneSwitcher.selectProfile="--选择配置文件--"
AdvSceneSwitcher.selectSceneCollection="--选择场景集合--"
AdvSceneSwitcher.enterPath="--输入路径--"
AdvSceneSwitcher.enterText="--输入文本--"
AdvSceneSwitcher.enterURL="--输入网址--"
AdvSceneSwitcher.invaildEntriesWillNotBeSaved="无效项将不会被保存"
AdvSceneSwitcher.selectWindowTip="使用 \"OBS\" 来指定OBS窗口\n使用 \"任务切换\" 来指定 Alt + Tab"
AdvSceneSwitcher.sceneItemSelection.all="全部"
@@ -725,3 +880,4 @@ AdvSceneSwitcher.duration.condition.none="没有时间限制"
AdvSceneSwitcher.duration.condition.more="时间少于"
AdvSceneSwitcher.duration.condition.equal="时间等于"
AdvSceneSwitcher.duration.condition.less="时间大于"
AdvSceneSwitcher.duration.condition.within="保留结果后余存"

View File

@@ -68,7 +68,7 @@
<x>0</x>
<y>0</y>
<width>957</width>
<height>817</height>
<height>905</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout_19">
@@ -419,6 +419,49 @@
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="transitionBox">
<property name="title">
<string>AdvSceneSwitcher.generalTab.transition</string>
</property>
<layout class="QGridLayout" name="transitionLayout">
<item row="1" column="1">
<widget class="QCheckBox" name="adjustActiveTransitionType">
<property name="text">
<string>AdvSceneSwitcher.generalTab.adjustActiveTransitionType</string>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label_12">
<property name="text">
<string>AdvSceneSwitcher.generalTab.setTransitionBy</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QCheckBox" name="transitionOverridecheckBox">
<property name="text">
<string>AdvSceneSwitcher.generalTab.transitionOverride</string>
</property>
</widget>
</item>
<item row="0" column="2">
<spacer name="horizontalSpacer_9">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="priorityBox">
<property name="title">
@@ -591,49 +634,26 @@
</widget>
</item>
<item>
<layout class="QGridLayout" name="gridLayout_26">
<item row="0" column="0">
<widget class="QListWidget" name="macros">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="dragEnabled">
<bool>true</bool>
</property>
<property name="dragDropMode">
<enum>QAbstractItemView::InternalMove</enum>
</property>
<property name="defaultDropAction">
<enum>Qt::MoveAction</enum>
</property>
<property name="spacing">
<number>1</number>
</property>
<property name="sortingEnabled">
<bool>false</bool>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="macroHelp">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>AdvSceneSwitcher.macroTab.help</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
<widget class="MacroTree" name="macros">
<property name="showDropIndicator" stdset="0">
<bool>true</bool>
</property>
<property name="dragEnabled">
<bool>true</bool>
</property>
<property name="dragDropMode">
<enum>QAbstractItemView::InternalMove</enum>
</property>
<property name="defaultDropAction">
<enum>Qt::TargetMoveAction</enum>
</property>
<property name="selectionMode">
<enum>QAbstractItemView::ExtendedSelection</enum>
</property>
<property name="spacing">
<number>0</number>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_61">
@@ -763,7 +783,7 @@
</property>
<layout class="QVBoxLayout" name="verticalLayout_33">
<item>
<layout class="QHBoxLayout" name="horizontalLayout_18" stretch="0,0,0,0,0,0,0">
<layout class="QHBoxLayout" name="horizontalLayout_18" stretch="0,0,0,0,0,0,0,0,0">
<item>
<widget class="QLabel" name="label_20">
<property name="text">
@@ -783,16 +803,36 @@
</item>
<item>
<widget class="QCheckBox" name="runMacroInParallel">
<property name="text">
<string/>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_5">
<property name="text">
<string>AdvSceneSwitcher.macroTab.runInParallel</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="runMacroOnChange">
<property name="text">
<string/>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_18">
<property name="text">
<string>AdvSceneSwitcher.macroTab.onChange</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item>
@@ -1710,44 +1750,6 @@
<string>AdvSceneSwitcher.transitionTab.title</string>
</attribute>
<layout class="QVBoxLayout" name="verticalLayout_15">
<item>
<layout class="QGridLayout" name="gridLayout_5">
<item row="1" column="1">
<widget class="QCheckBox" name="adjustActiveTransitionType">
<property name="text">
<string>AdvSceneSwitcher.transitionTab.adjustActiveTransitionType</string>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label_12">
<property name="text">
<string>AdvSceneSwitcher.transitionTab.setTransitionBy</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QCheckBox" name="transitionOverridecheckBox">
<property name="text">
<string>AdvSceneSwitcher.transitionTab.transitionOverride</string>
</property>
</widget>
</item>
<item row="0" column="2">
<spacer name="horizontalSpacer_9">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<widget class="QGroupBox" name="transitionsGroup">
<property name="title">
@@ -4535,6 +4537,13 @@
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>MacroTree</class>
<extends>QListView</extends>
<header>macro-tree.hpp</header>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>

View File

@@ -151,6 +151,7 @@ void SwitcherData::Thread()
std::chrono::milliseconds duration;
auto startTime = std::chrono::high_resolution_clock::now();
auto endTime = std::chrono::high_resolution_clock::now();
switcher->firstIntervalAfterStop = true;
while (true) {
std::unique_lock<std::mutex> lock(m);
@@ -251,6 +252,8 @@ void SwitcherData::Thread()
}
writeSceneInfoToFile();
switcher->firstInterval = false;
switcher->firstIntervalAfterStop = false;
}
blog(LOG_INFO, "stopped");
@@ -280,6 +283,9 @@ void SwitcherData::setPreconditions()
}
currentTitle = title;
// Process name
GetForegroundProcessName(currentForegroundProcess);
// Cursor
std::pair<int, int> cursorPos = getCursorPos();
cursorPosChanged = cursorPos.first != switcher->lastCursorPos.first ||
@@ -420,10 +426,11 @@ void SwitcherData::Start()
}
}
void ResetMacroCounters()
void ResetMacros()
{
for (auto &m : switcher->macros) {
m->ResetCount();
m->ResetRunCount();
m->ResetTimers();
}
}
@@ -440,7 +447,7 @@ void SwitcherData::Stop()
th = nullptr;
writeToStatusFile("Advanced Scene Switcher stopped");
ResetMacroCounters();
ResetMacros();
}
server.stop();
@@ -598,6 +605,17 @@ void handleExit()
FreeSceneSwitcher();
}
void handleSceneCollectionChanging()
{
if (switcher->settingsWindowOpened) {
AdvSceneSwitcher::window->close();
}
if (!switcher->stop) {
switcher->sceneColletionStop = true;
switcher->Stop();
}
}
// Note to future self:
// be careful using switcher->m here as there is potential for deadlocks when using
// frontend functions such as obs_frontend_set_current_scene()
@@ -643,6 +661,11 @@ static void OBSEvent(enum obs_frontend_event event, void *switcher)
case OBS_FRONTEND_EVENT_STREAMING_STOPPING:
setStreamStopping();
break;
#if LIBOBS_API_VER >= MAKE_SEMANTIC_VERSION(27, 2, 0)
case OBS_FRONTEND_EVENT_SCENE_COLLECTION_CHANGING:
handleSceneCollectionChanging();
break;
#endif
default:
break;
}
@@ -676,6 +699,21 @@ void LoadPlugins()
}
}
void OpenSettingsWindow()
{
if (switcher->settingsWindowOpened) {
AdvSceneSwitcher::window->show();
AdvSceneSwitcher::window->raise();
AdvSceneSwitcher::window->activateWindow();
} else {
AdvSceneSwitcher::window =
new AdvSceneSwitcher(static_cast<QMainWindow *>(
obs_frontend_get_main_window()));
AdvSceneSwitcher::window->setAttribute(Qt::WA_DeleteOnClose);
AdvSceneSwitcher::window->show();
}
}
extern "C" void InitSceneSwitcher(obs_module_t *m, translateFunc t)
{
blog(LOG_INFO, "version: %s", g_GIT_TAG);
@@ -689,25 +727,10 @@ extern "C" void InitSceneSwitcher(obs_module_t *m, translateFunc t)
LoadPlugins();
SetupDock();
auto cb = []() {
if (switcher->settingsWindowOpened) {
AdvSceneSwitcher::window->show();
AdvSceneSwitcher::window->raise();
AdvSceneSwitcher::window->activateWindow();
} else {
AdvSceneSwitcher::window =
new AdvSceneSwitcher(static_cast<QMainWindow *>(
obs_frontend_get_main_window()));
AdvSceneSwitcher::window->setAttribute(
Qt::WA_DeleteOnClose);
AdvSceneSwitcher::window->show();
}
};
obs_frontend_add_save_callback(SaveSceneSwitcher, nullptr);
obs_frontend_add_event_callback(OBSEvent, switcher);
QAction *action = (QAction *)obs_frontend_add_tools_menu_qaction(
obs_module_text("AdvSceneSwitcher.pluginName"));
action->connect(action, &QAction::triggered, cb);
action->connect(action, &QAction::triggered, OpenSettingsWindow);
}

View File

@@ -44,12 +44,13 @@ public:
void OpenSequenceExtendEdit(SequenceWidget *sw);
void SetEditSceneGroup(SceneGroup &sg);
bool addNewMacro(std::string &name, std::string format = "");
Macro *getSelectedMacro();
bool addNewMacro(std::shared_ptr<Macro> &res, std::string &name,
std::string format = "");
std::shared_ptr<Macro> getSelectedMacro();
void SetEditMacro(Macro &m);
void SetMacroEditAreaDisabled(bool);
void HighlightAction(int idx);
void HighlightCondition(int idx);
void HighlightAction(int idx, QColor color = QColor(Qt::green));
void HighlightCondition(int idx, QColor color = QColor(Qt::green));
void PopulateMacroActions(Macro &m, uint32_t afterIdx = 0);
void PopulateMacroConditions(Macro &m, uint32_t afterIdx = 0);
void SetActionData(Macro &m);
@@ -85,7 +86,8 @@ public:
signals:
void MacroAdded(const QString &name);
void MacroRemoved(const QString &name);
void MacroRenamed(const QString &oldName, const QString newName);
void MacroRenamed(const QString &oldName, const QString &newName);
void MacroSegmentOrderChanged();
void HighlightMacrosChanged(bool value);
void HighlightActionsChanged(bool value);
void HighlightConditionsChanged(bool value);
@@ -126,7 +128,6 @@ public slots:
void on_runMacro_clicked();
void on_runMacroInParallel_stateChanged(int value);
void on_runMacroOnChange_stateChanged(int value);
void on_macros_currentRowChanged(int idx);
void on_conditionAdd_clicked();
void on_conditionRemove_clicked();
void on_conditionUp_clicked();
@@ -135,6 +136,8 @@ public slots:
void on_actionRemove_clicked();
void on_actionUp_clicked();
void on_actionDown_clicked();
void MacroSelectionChanged(const QItemSelection &,
const QItemSelection &);
void UpMacroSegementHotkey();
void DownMacroSegementHotkey();
void DeleteMacroSegementHotkey();
@@ -165,7 +168,6 @@ public slots:
void ResetOpacityActionControls();
void ResetOpacityConditionControls();
void HighlightControls();
void MacroDragDropReorder(QModelIndex, int, int, QModelIndex, int);
void HighlightOnChange();
void on_macroProperties_clicked();
@@ -314,10 +316,11 @@ private:
int currentActionIdx = -1;
};
void OpenSettingsWindow();
/******************************************************************************
* Sceneswitch helper
******************************************************************************/
void setNextTransition(const sceneSwitchInfo &ssi, obs_source_t *currentSource,
transitionData &td);
void overwriteTransitionOverride(const sceneSwitchInfo &ssi,

View File

@@ -1,5 +1,6 @@
#include "advanced-scene-switcher.hpp"
#include "status-control.hpp"
#include "file-selection.hpp"
#include "utility.hpp"
#include "version.h"
@@ -151,6 +152,7 @@ void AdvSceneSwitcher::closeEvent(QCloseEvent *)
ui->macroActionConditionSplitter->sizes();
switcher->macroListMacroEditSplitterPosition =
ui->macroListMacroEditSplitter->sizes();
obs_frontend_save();
}
@@ -308,11 +310,8 @@ void AdvSceneSwitcher::on_exportSettings_clicked()
}
obs_data_t *obj = obs_data_create();
switcher->saveSettings(obj);
obs_data_save_json(obj, file.fileName().toUtf8().constData());
obs_data_release(obj);
}
@@ -323,20 +322,20 @@ void AdvSceneSwitcher::on_importSettings_clicked()
bool start = !switcher->stop;
switcher->Stop();
std::lock_guard<std::mutex> lock(switcher->m);
QString directory = QFileDialog::getOpenFileName(
auto basePath = FileSelection::ValidPathOrDesktop(
QString::fromStdString(switcher->lastImportPath));
QString path = QFileDialog::getOpenFileName(
this,
tr(obs_module_text(
"AdvSceneSwitcher.generalTab.saveOrLoadsettings.importWindowTitle")),
QDir::currentPath(),
basePath,
tr(obs_module_text(
"AdvSceneSwitcher.generalTab.saveOrLoadsettings.textType")));
if (directory.isEmpty()) {
if (path.isEmpty()) {
return;
}
QFile file(directory);
QFile file(path);
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
return;
}
@@ -350,12 +349,15 @@ void AdvSceneSwitcher::on_importSettings_clicked()
return;
}
std::lock_guard<std::mutex> lock(switcher->m);
switcher->loadSettings(obj);
obs_data_release(obj);
switcher->lastImportPath = path.toStdString();
(void)DisplayMessage(obs_module_text(
"AdvSceneSwitcher.generalTab.saveOrLoadsettings.loadSuccess"));
// Just close the UI and let the user reopen it to not have to
// implement updating each setting
close();
// Restart scene switcher if it was active
@@ -496,6 +498,39 @@ void AdvSceneSwitcher::on_tabWidget_currentChanged(int)
SetShowFrames();
}
void AdvSceneSwitcher::on_transitionOverridecheckBox_stateChanged(int state)
{
if (loading) {
return;
}
if (!state && !switcher->adjustActiveTransitionType) {
DisplayMessage(obs_module_text(
"AdvSceneSwitcher.generalTab.transitionBehaviorSelectionError"));
ui->adjustActiveTransitionType->setChecked(true);
}
std::lock_guard<std::mutex> lock(switcher->m);
switcher->transitionOverrideOverride = state;
}
void AdvSceneSwitcher::on_adjustActiveTransitionType_stateChanged(int state)
{
if (loading) {
return;
}
// This option only makes sense if we are allowed to use transition overrides
if (!state && !switcher->transitionOverrideOverride) {
DisplayMessage(obs_module_text(
"AdvSceneSwitcher.generalTab.transitionBehaviorSelectionError"));
ui->transitionOverridecheckBox->setChecked(true);
}
std::lock_guard<std::mutex> lock(switcher->m);
switcher->adjustActiveTransitionType = state;
}
void SwitcherData::loadSettings(obs_data_t *obj)
{
if (!obj) {
@@ -574,7 +609,8 @@ void SwitcherData::saveGeneralSettings(obs_data_t *obj)
cooldown.Save(obj, "cooldown", "cooldownUnit");
obs_data_set_bool(obj, "active", !stop);
obs_data_set_bool(obj, "active", sceneColletionStop ? true : !stop);
sceneColletionStop = false;
obs_data_set_int(obj, "startup_behavior", startupBehavior);
obs_data_set_int(obj, "autoStartEvent",
@@ -599,6 +635,14 @@ void SwitcherData::saveGeneralSettings(obs_data_t *obj)
obs_data_set_int(obj, "priority10", functionNamesByPriority[10]);
obs_data_set_int(obj, "threadPriority", threadPriority);
obs_data_set_bool(obj, "tansitionOverrideOverride",
transitionOverrideOverride);
obs_data_set_default_bool(obj, "adjustActiveTransitionType",
adjustActiveTransitionType);
obs_data_set_bool(obj, "adjustActiveTransitionType",
adjustActiveTransitionType);
obs_data_set_string(obj, "lastImportPath", lastImportPath.c_str());
}
void SwitcherData::loadGeneralSettings(obs_data_t *obj)
@@ -674,6 +718,13 @@ void SwitcherData::loadGeneralSettings(obs_data_t *obj)
obs_data_set_default_int(obj, "threadPriority",
QThread::NormalPriority);
threadPriority = obs_data_get_int(obj, "threadPriority");
transitionOverrideOverride =
obs_data_get_bool(obj, "tansitionOverrideOverride");
adjustActiveTransitionType =
obs_data_get_bool(obj, "adjustActiveTransitionType");
lastImportPath = obs_data_get_string(obj, "lastImportPath");
}
void saveSplitterPos(QList<int> &sizes, obs_data_t *obj, const std::string name)

View File

@@ -4,6 +4,119 @@
#include <fstream>
#include <regex>
std::vector<std::weak_ptr<Hotkey>> Hotkey::_registeredHotkeys = {};
uint32_t Hotkey::_hotkeyCounter = 1;
std::shared_ptr<Hotkey> Hotkey::GetHotkey(const std::string &description,
bool ignoreExistingHotkeys)
{
// Clean up expired hotkeys
auto it = _registeredHotkeys.begin();
while (it != _registeredHotkeys.end()) {
if (it->expired()) {
it = _registeredHotkeys.erase(it);
} else {
it++;
}
}
// Check for existing hotkey with same description
for (const auto &h : _registeredHotkeys) {
auto hotkey = h.lock();
if (!hotkey) {
continue;
}
if (hotkey->_description == description) {
hotkey->_ignoreExistingHotkeys = ignoreExistingHotkeys;
return hotkey;
}
}
// Create new hotkey
auto hotkey = std::make_shared<Hotkey>(description);
_registeredHotkeys.emplace_back(hotkey);
hotkey->_ignoreExistingHotkeys = ignoreExistingHotkeys;
return hotkey;
}
Hotkey::Hotkey(const std::string &description) : _description(description)
{
std::string name =
"macro_condition_hotkey_" + std::to_string(_hotkeyCounter);
_hotkeyID = obs_hotkey_register_frontend(
name.c_str(), _description.c_str(), Callback, this);
_hotkeyCounter++;
}
bool Hotkey::Save(obs_data_t *obj) const
{
obs_data_set_string(obj, "desc", _description.c_str());
obs_data_array_t *hotkeyData = obs_hotkey_save(_hotkeyID);
obs_data_set_array(obj, "keyBind", hotkeyData);
obs_data_array_release(hotkeyData);
return true;
}
bool Hotkey::Load(obs_data_t *obj)
{
auto description = obs_data_get_string(obj, "desc");
if (!DescriptionAvailable(description)) {
return false;
}
_description = description;
obs_data_array_t *hotkeyData = obs_data_get_array(obj, "keyBind");
obs_hotkey_load(_hotkeyID, hotkeyData);
obs_data_array_release(hotkeyData);
obs_hotkey_set_description(_hotkeyID, _description.c_str());
_ignoreExistingHotkeys = false;
return true;
}
Hotkey::~Hotkey()
{
obs_hotkey_unregister(_hotkeyID);
}
bool Hotkey::UpdateDescription(const std::string &descritpion)
{
if (!DescriptionAvailable(descritpion)) {
return false;
}
_description = descritpion;
obs_hotkey_set_description(_hotkeyID, descritpion.c_str());
return true;
}
bool Hotkey::DescriptionAvailable(const std::string &descritpion)
{
for (const auto &hotkey : _registeredHotkeys) {
auto h = hotkey.lock();
if (!h) {
continue;
}
if (!h->_ignoreExistingHotkeys &&
h->_description == descritpion) {
return false;
}
}
return true;
}
void Hotkey::Callback(void *data, obs_hotkey_id, obs_hotkey_t *, bool pressed)
{
auto hotkey = static_cast<Hotkey *>(data);
if (pressed) {
hotkey->_lastPressed =
std::chrono::high_resolution_clock::now();
}
hotkey->_pressed = pressed;
}
void Hotkey::ClearAllHotkeys()
{
_registeredHotkeys.clear();
}
void startHotkeyFunc(void *, obs_hotkey_id, obs_hotkey_t *, bool pressed)
{
if (pressed) {

View File

@@ -1,7 +1,46 @@
#pragma once
#include <obs.hpp>
#include <string>
#include <memory>
#include <vector>
#include <chrono>
extern bool canSimulateKeyPresses;
class Hotkey {
public:
Hotkey(const std::string &description);
~Hotkey();
static std::shared_ptr<Hotkey>
GetHotkey(const std::string &description,
bool ignoreExistingHotkeys = false);
static void ClearAllHotkeys();
bool Save(obs_data_t *obj) const;
bool Load(obs_data_t *obj);
bool GetPressed() const { return _pressed; }
auto GetLastPressed() const { return _lastPressed; }
std::string GetDescription() const { return _description; }
bool UpdateDescription(const std::string &);
private:
static bool DescriptionAvailable(const std::string &);
static void Callback(void *data, obs_hotkey_id, obs_hotkey_t *,
bool pressed);
static std::vector<std::weak_ptr<Hotkey>> _registeredHotkeys;
static uint32_t _hotkeyCounter;
std::string _description;
obs_hotkey_id _hotkeyID = OBS_INVALID_HOTKEY_ID;
bool _pressed = false;
std::chrono::high_resolution_clock::time_point _lastPressed{};
// When set will not attempt to share settings with existing hotkey
bool _ignoreExistingHotkeys = false;
};
enum class HotkeyType {
Key_NoKey = 0,

View File

@@ -169,39 +169,6 @@ void SwitcherData::checkDefaultSceneTransitions()
}
}
void AdvSceneSwitcher::on_transitionOverridecheckBox_stateChanged(int state)
{
if (loading) {
return;
}
if (!state && !switcher->adjustActiveTransitionType) {
DisplayMessage(obs_module_text(
"AdvSceneSwitcher.transitionTab.transitionBehaviorSelectionError"));
ui->adjustActiveTransitionType->setChecked(true);
}
std::lock_guard<std::mutex> lock(switcher->m);
switcher->transitionOverrideOverride = state;
}
void AdvSceneSwitcher::on_adjustActiveTransitionType_stateChanged(int state)
{
if (loading) {
return;
}
// This option only makes sense if we are allowed to use transition overrides
if (!state && !switcher->transitionOverrideOverride) {
DisplayMessage(obs_module_text(
"AdvSceneSwitcher.transitionTab.transitionBehaviorSelectionError"));
ui->transitionOverridecheckBox->setChecked(true);
}
std::lock_guard<std::mutex> lock(switcher->m);
switcher->adjustActiveTransitionType = state;
}
void AdvSceneSwitcher::defTransitionDelayValueChanged(int value)
{
if (loading) {
@@ -335,13 +302,6 @@ void SwitcherData::saveSceneTransitions(obs_data_t *obj)
}
obs_data_set_array(obj, "defaultTransitions", defaultTransitionsArray);
obs_data_array_release(defaultTransitionsArray);
obs_data_set_bool(obj, "tansitionOverrideOverride",
transitionOverrideOverride);
obs_data_set_default_bool(obj, "adjustActiveTransitionType",
adjustActiveTransitionType);
obs_data_set_bool(obj, "adjustActiveTransitionType",
adjustActiveTransitionType);
obs_data_set_default_int(obj, "defTransitionDelay",
default_def_transition_dealy);
obs_data_set_int(obj, "defTransitionDelay",
@@ -380,11 +340,6 @@ void SwitcherData::loadSceneTransitions(obs_data_t *obj)
}
obs_data_array_release(defaultTransitionsArray);
transitionOverrideOverride =
obs_data_get_bool(obj, "tansitionOverrideOverride");
adjustActiveTransitionType =
obs_data_get_bool(obj, "adjustActiveTransitionType");
// Check for invalid config
if (!transitionOverrideOverride && !adjustActiveTransitionType) {
adjustActiveTransitionType = true;

View File

@@ -1,4 +1,5 @@
#include "platform-funcs.hpp"
#include "hotkey.hpp"
#include <X11/Xlib.h>
#include <X11/Xatom.h>
@@ -24,7 +25,11 @@
#include <QStringList>
#include <QRegularExpression>
#include <QLibrary>
#ifdef USE_PROCPS
#include <proc/readproc.h>
#else
#include <libproc2/pids.h>
#endif
#include <fstream>
#include <sstream>
@@ -35,18 +40,31 @@ typedef int (*keyPressFunc)(Display *, unsigned int, bool, unsigned long);
static keyPressFunc pressFunc = nullptr;
bool canSimulateKeyPresses = false;
static QLibrary *libXssHandle = nullptr;
typedef XScreenSaverInfo *(*XScreenSaverAllocInfoFunc)();
typedef int (*XScreenSaverQueryInfoFunc)(Display *, Window, XScreenSaverInfo *);
static XScreenSaverAllocInfoFunc allocSSFunc = nullptr;
static XScreenSaverQueryInfoFunc querySSFunc = nullptr;
bool canGetIdleTime = false;
std::chrono::high_resolution_clock::time_point lastMouseLeftClickTime{};
std::chrono::high_resolution_clock::time_point lastMouseMiddleClickTime{};
std::chrono::high_resolution_clock::time_point lastMouseRightClickTime{};
Display *disp()
{
if (!xdisplay)
if (!xdisplay) {
xdisplay = XOpenDisplay(NULL);
}
return xdisplay;
}
void cleanupDisplay()
{
if (!xdisplay)
if (!xdisplay) {
return;
}
XCloseDisplay(xdisplay);
xdisplay = 0;
@@ -55,22 +73,29 @@ void cleanupDisplay()
static bool ewmhIsSupported()
{
Display *display = disp();
if (!display) {
return false;
}
auto window = DefaultRootWindow(display);
if (!window) {
return false;
}
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;
Window ewmhWindow = 0;
int status = XGetWindowProperty(display, DefaultRootWindow(display),
netSupportingWmCheck, 0L, 1L, false,
XA_WINDOW, &actualType, &format, &num,
&bytes, &data);
int status = XGetWindowProperty(display, window, netSupportingWmCheck,
0L, 1L, false, XA_WINDOW, &actualType,
&format, &num, &bytes, &data);
if (status == Success) {
if (num > 0) {
ewmh_window = ((Window *)data)[0];
ewmhWindow = ((Window *)data)[0];
}
if (data) {
XFree(data);
@@ -78,29 +103,28 @@ static bool ewmhIsSupported()
}
}
if (ewmh_window) {
status = XGetWindowProperty(display, ewmh_window,
if (ewmhWindow) {
status = XGetWindowProperty(display, ewmhWindow,
netSupportingWmCheck, 0L, 1L, false,
XA_WINDOW, &actualType, &format,
&num, &bytes, &data);
if (status != Success || num == 0 ||
ewmh_window != ((Window *)data)[0]) {
ewmh_window = 0;
ewmhWindow != ((Window *)data)[0]) {
ewmhWindow = 0;
}
if (status == Success && data) {
XFree(data);
}
}
return ewmh_window != 0;
return ewmhWindow != 0;
}
static QStringList getStates(Window window)
{
QStringList states;
if (!ewmhIsSupported())
if (!window || !ewmhIsSupported()) {
return states;
}
Atom wmState = XInternAtom(disp(), "_NET_WM_STATE", true), type;
int format;
@@ -111,10 +135,13 @@ static QStringList getStates(Window window)
AnyPropertyType, &type, &format, &num,
&bytes, &data);
if (status == Success)
for (unsigned long i = 0; i < num; i++)
if (status == Success) {
for (unsigned long i = 0; i < num; i++) {
states.append(QString(
XGetAtomName(disp(), ((Atom *)data)[i])));
}
XFree(data);
}
return states;
}
@@ -122,9 +149,6 @@ static QStringList getStates(Window window)
static std::vector<Window> getTopLevelWindows()
{
std::vector<Window> res;
res.resize(0);
if (!ewmhIsSupported()) {
return res;
}
@@ -136,10 +160,13 @@ static std::vector<Window> getTopLevelWindows()
Window *data = 0;
for (int i = 0; i < ScreenCount(disp()); ++i) {
Window rootWin = RootWindow(disp(), i);
Window rootWindow = RootWindow(disp(), i);
if (!rootWindow) {
continue;
}
int status = XGetWindowProperty(disp(), rootWin, netClList, 0L,
~0L, false, AnyPropertyType,
int status = XGetWindowProperty(disp(), rootWindow, netClList,
0L, ~0L, false, AnyPropertyType,
&actualType, &format, &num,
&bytes, (uint8_t **)&data);
@@ -147,8 +174,9 @@ static std::vector<Window> getTopLevelWindows()
continue;
}
for (unsigned long i = 0; i < num; ++i)
for (unsigned long i = 0; i < num; ++i) {
res.emplace_back(data[i]);
}
XFree(data);
}
@@ -156,24 +184,27 @@ static std::vector<Window> getTopLevelWindows()
return res;
}
static std::string GetWindowTitle(size_t i)
std::string getWindowName(Window window)
{
Window w = getTopLevelWindows().at(i);
auto display = disp();
if (!display || !window) {
return "";
}
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) {
int status = XFetchName(display, window, &name);
if (status >= Success && name != nullptr) {
std::string str(name);
windowTitle = str;
XFree(name);
} else {
XTextProperty xtp_new_name;
if (XGetWMName(display, window, &xtp_new_name) != 0 &&
xtp_new_name.value != nullptr) {
std::string str((const char *)xtp_new_name.value);
windowTitle = str;
XFree(xtp_new_name.value);
}
}
return windowTitle;
@@ -182,187 +213,124 @@ static std::string GetWindowTitle(size_t i)
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));
for (auto window : getTopLevelWindows()) {
auto name = getWindowName(window);
if (name.empty()) {
continue;
}
windows.emplace_back(name);
}
}
// Overloaded
void GetWindowList(QStringList &windows)
{
windows.clear();
for (size_t i = 0; i < getTopLevelWindows().size(); ++i) {
if (GetWindowTitle(i) != "")
windows << QString::fromStdString(GetWindowTitle(i));
for (auto window : getTopLevelWindows()) {
auto name = getWindowName(window);
if (name.empty()) {
continue;
}
windows << QString::fromStdString(name);
}
}
void GetCurrentWindowTitle(std::string &title)
int getActiveWindow(Window *&window)
{
if (!ewmhIsSupported()) {
return;
return -1;
}
Atom active = XInternAtom(disp(), "_NET_ACTIVE_WINDOW", true);
Atom actualType;
int format;
unsigned long num, bytes;
auto rootWindow = DefaultRootWindow(disp());
if (!rootWindow) {
return -2;
}
return XGetWindowProperty(disp(), rootWindow, active, 0L, ~0L, false,
AnyPropertyType, &actualType, &format, &num,
&bytes, (uint8_t **)&window);
}
void GetCurrentWindowTitle(std::string &title)
{
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);
if (data == nullptr) {
if (getActiveWindow(data) != Success || !data) {
return;
}
if (!data[0]) {
XFree(data);
return;
}
int status = 0;
XTextProperty text;
if (xstatus == Success && data[0]) {
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);
auto name = getWindowName(data[0]);
XFree(data);
if (name.empty()) {
return;
}
title = name;
}
std::pair<int, int> getCursorPos()
bool windowStatesAreSet(const std::string &windowTitle,
std::vector<QString> &expectedStates)
{
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 = disp();
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);
if (!ewmhIsSupported()) {
return false;
}
return pos;
std::vector<Window> windows = getTopLevelWindows();
for (auto &window : windows) {
auto name = getWindowName(window);
if (name.empty()) {
continue;
}
bool equals = windowTitle == name;
bool matches = QString::fromStdString(name).contains(
QRegularExpression(
QString::fromStdString(windowTitle)));
if (!(equals || matches)) {
continue;
}
QStringList states = getStates(window);
if (states.isEmpty()) {
if (expectedStates.empty()) {
return true;
}
return false;
}
for (const auto &state : expectedStates) {
if (!states.contains(state)) {
return false;
}
}
return true;
}
return false;
}
bool isMaximized(const 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 maximized
if (equals || matches) {
QStringList states = getStates(window);
if (!states.isEmpty()) {
// 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 (vertical && horizontal);
}
break;
}
}
return false;
std::vector<QString> states;
states.emplace_back("_NET_WM_STATE_MAXIMIZED_VERT");
states.emplace_back("_NET_WM_STATE_MAXIMIZED_HORZ");
return windowStatesAreSet(title, states);
}
bool isFullscreen(const 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");
return (fullscreen);
}
break;
}
}
return false;
std::vector<QString> states;
states.emplace_back("_NET_WM_STATE_FULLSCREEN");
return windowStatesAreSet(title, states);
}
//exe switch is not quite what is expected but it works for now
#ifdef USE_PROCPS
void GetProcessList(QStringList &processes)
{
processes.clear();
@@ -371,48 +339,56 @@ void GetProcessList(QStringList &processes)
memset(&proc_info, 0, sizeof(proc_info));
while (readproc(proc, &proc_info) != NULL) {
QString procName(proc_info.cmd);
if (!procName.isEmpty() && !processes.contains(proc_info.cmd)) {
processes << QString(proc_info.cmd);
if (!procName.isEmpty() && !processes.contains(procName)) {
processes << procName;
}
}
closeproc(proc);
}
int getForegroundProcessPid()
#else
void GetProcessList(QStringList &processes)
{
if (!ewmhIsSupported()) {
return -1;
processes.clear();
struct pids_info *info = NULL;
struct pids_stack *stack;
enum pids_item Items[] = {
PIDS_CMD,
};
if (procps_pids_new(&info, Items, sizeof(Items) / sizeof(Items[0])) <
0) {
return;
}
auto dpy = disp();
Atom active = XInternAtom(dpy, "_NET_ACTIVE_WINDOW", true);
Atom actualType;
int format;
unsigned long num, bytes;
Window *window = 0;
int pid = -1;
while ((stack = procps_pids_get(info, PIDS_FETCH_TASKS_ONLY))) {
auto cmd = PIDS_VAL(0, str, stack, info);
QString procName(cmd);
if (!procName.isEmpty() && !processes.contains(procName)) {
processes << procName;
}
}
procps_pids_unref(&info);
}
#endif
Window rootWin = RootWindow(dpy, 0);
int xstatus = XGetWindowProperty(dpy, rootWin, active, 0L, ~0L, false,
AnyPropertyType, &actualType, &format,
&num, &bytes, (uint8_t **)&window);
if (xstatus == 0 && window == nullptr) {
long getForegroundProcessPid()
{
Window *window;
if (getActiveWindow(window) != Success || !window || !*window) {
return -1;
}
Atom atom, actual_type;
int actual_format;
unsigned long nitems;
unsigned long bytes_after;
unsigned char *prop;
atom = XInternAtom(dpy, "_NET_WM_PID", True);
auto status = XGetWindowProperty(dpy, *window, atom, 0, 1024, False,
AnyPropertyType, &actual_type,
long long pid;
atom = XInternAtom(disp(), "_NET_WM_PID", True);
auto status = XGetWindowProperty(disp(), *window, atom, 0, 1024, False,
XA_CARDINAL, &actual_type,
&actual_format, &nitems, &bytes_after,
&prop);
XFree(window);
if (status != 0) {
return -2;
}
@@ -420,24 +396,42 @@ int getForegroundProcessPid()
return -3;
}
pid = prop[1] * 256;
pid += prop[0];
pid = *((long *)prop);
XFree(prop);
return pid;
}
std::string getProcNameFromPid(int pid)
std::string getProcNameFromPid(long pid)
{
std::string path = "/proc/" + std::to_string(pid) + "/comm";
std::ifstream t(path);
std::stringstream buffer;
buffer << t.rdbuf();
return buffer.str();
std::string name = buffer.str();
if (!name.empty() && name[name.length() - 1] == '\n') {
name.erase(name.length() - 1);
}
return name;
}
void GetForegroundProcessName(QString &proc)
{
std::string temp;
GetForegroundProcessName(temp);
proc = QString::fromStdString(temp);
}
void GetForegroundProcessName(std::string &proc)
{
proc.resize(0);
auto pid = getForegroundProcessPid();
proc = getProcNameFromPid(pid);
}
bool isInFocus(const QString &executable)
{
auto pid = getForegroundProcessPid();
std::string current = getProcNameFromPid(pid);
std::string current;
GetForegroundProcessName(current);
// True if executable switch equals current window
bool equals = (executable.toStdString() == current);
@@ -450,22 +444,28 @@ bool isInFocus(const QString &executable)
int secondsSinceLastInput()
{
time_t idle_time;
static XScreenSaverInfo *mit_info;
Display *display;
int screen;
mit_info = XScreenSaverAllocInfo();
if ((display = disp()) == NULL) {
return -1;
if (!canGetIdleTime) {
return 0;
}
screen = DefaultScreen(display);
XScreenSaverQueryInfo(display, RootWindow(display, screen), mit_info);
auto display = disp();
if (!display) {
return 0;
}
auto window = DefaultRootWindow(display);
if (!window) {
return 0;
}
time_t idle_time;
auto mit_info = allocSSFunc();
auto status = querySSFunc(display, window, mit_info);
idle_time = (mit_info->idle) / 1000;
XFree(mit_info);
return idle_time;
return status != 0 ? idle_time : 0;
}
static std::unordered_map<HotkeyType, long> keyTable = {
@@ -595,8 +595,9 @@ void PressKeys(const std::vector<HotkeyType> keys, int duration)
}
Display *display = disp();
if (display == NULL)
if (!display) {
return;
}
// Press keys
for (auto &key : keys) {
@@ -626,17 +627,35 @@ void PressKeys(const std::vector<HotkeyType> keys, int duration)
void PlatformInit()
{
libXtstHandle = new QLibrary("libXtst.so", nullptr);
auto display = disp();
if (!display) {
return;
}
libXtstHandle = new QLibrary("libXtst", nullptr);
pressFunc = (keyPressFunc)libXtstHandle->resolve("XTestFakeKeyEvent");
int _;
canSimulateKeyPresses = pressFunc &&
!XQueryExtension(disp(), "XTEST", &_, &_, &_);
XQueryExtension(disp(), "XTEST", &_, &_, &_);
libXssHandle = new QLibrary("libXss", nullptr);
allocSSFunc = (XScreenSaverAllocInfoFunc)libXssHandle->resolve(
"XScreenSaverAllocInfo");
querySSFunc = (XScreenSaverQueryInfoFunc)libXssHandle->resolve(
"XScreenSaverQueryInfo");
canGetIdleTime = allocSSFunc && querySSFunc &&
XQueryExtension(disp(), ScreenSaverName, &_, &_, &_);
}
void PlatformCleanup()
{
delete libXtstHandle;
libXtstHandle = nullptr;
if (libXtstHandle) {
delete libXtstHandle;
libXtstHandle = nullptr;
}
if (libXssHandle) {
delete libXssHandle;
libXssHandle = nullptr;
}
cleanupDisplay();
}

View File

@@ -2,6 +2,7 @@
#include "advanced-scene-switcher.hpp"
#include "utility.hpp"
constexpr int64_t nsPerMs = 1000000;
const std::string MacroActionAudio::id = "audio";
bool MacroActionAudio::_registered = MacroActionFactory::Register(
@@ -9,19 +10,28 @@ bool MacroActionAudio::_registered = MacroActionFactory::Register(
{MacroActionAudio::Create, MacroActionAudioEdit::Create,
"AdvSceneSwitcher.action.audio"});
const static std::map<AudioAction, std::string> actionTypes = {
{AudioAction::MUTE, "AdvSceneSwitcher.action.audio.type.mute"},
{AudioAction::UNMUTE, "AdvSceneSwitcher.action.audio.type.unmute"},
{AudioAction::SOURCE_VOLUME,
const static std::map<MacroActionAudio::Action, std::string> actionTypes = {
{MacroActionAudio::Action::MUTE,
"AdvSceneSwitcher.action.audio.type.mute"},
{MacroActionAudio::Action::UNMUTE,
"AdvSceneSwitcher.action.audio.type.unmute"},
{MacroActionAudio::Action::SOURCE_VOLUME,
"AdvSceneSwitcher.action.audio.type.sourceVolume"},
{AudioAction::MASTER_VOLUME,
{MacroActionAudio::Action::MASTER_VOLUME,
"AdvSceneSwitcher.action.audio.type.masterVolume"},
{MacroActionAudio::Action::SYNC_OFFSET,
"AdvSceneSwitcher.action.audio.type.syncOffset"},
{MacroActionAudio::Action::MONITOR,
"AdvSceneSwitcher.action.audio.type.monitor"},
{MacroActionAudio::Action::BALANCE,
"AdvSceneSwitcher.action.audio.type.balance"},
};
const static std::map<FadeType, std::string> fadeTypes = {
{FadeType::DURATION,
const static std::map<MacroActionAudio::FadeType, std::string> fadeTypes = {
{MacroActionAudio::FadeType::DURATION,
"AdvSceneSwitcher.action.audio.fade.type.duration"},
{FadeType::RATE, "AdvSceneSwitcher.action.audio.fade.type.rate"},
{MacroActionAudio::FadeType::RATE,
"AdvSceneSwitcher.action.audio.fade.type.rate"},
};
constexpr auto fadeInterval = std::chrono::milliseconds(100);
@@ -29,9 +39,9 @@ constexpr float minFade = 0.000001f;
void MacroActionAudio::SetFadeActive(bool value)
{
if (_action == AudioAction::SOURCE_VOLUME) {
switcher->activeAudioFades[GetWeakSourceName(_audioSource)]
.active = value;
if (_action == Action::SOURCE_VOLUME) {
switcher->activeAudioFades[_audioSource.ToString()].active =
value;
} else {
switcher->masterAudioFade.active = value;
}
@@ -40,9 +50,9 @@ void MacroActionAudio::SetFadeActive(bool value)
bool MacroActionAudio::FadeActive()
{
bool active = true;
if (_action == AudioAction::SOURCE_VOLUME) {
if (_action == Action::SOURCE_VOLUME) {
auto it = switcher->activeAudioFades.find(
GetWeakSourceName(_audioSource));
_audioSource.ToString());
if (it == switcher->activeAudioFades.end()) {
return false;
}
@@ -57,9 +67,9 @@ bool MacroActionAudio::FadeActive()
std::atomic_int *MacroActionAudio::GetFadeIdPtr()
{
if (_action == AudioAction::SOURCE_VOLUME) {
if (_action == Action::SOURCE_VOLUME) {
auto it = switcher->activeAudioFades.find(
GetWeakSourceName(_audioSource));
_audioSource.ToString());
if (it == switcher->activeAudioFades.end()) {
return nullptr;
}
@@ -70,8 +80,8 @@ std::atomic_int *MacroActionAudio::GetFadeIdPtr()
void MacroActionAudio::SetVolume(float vol)
{
if (_action == AudioAction::SOURCE_VOLUME) {
auto s = obs_weak_source_get_source(_audioSource);
if (_action == Action::SOURCE_VOLUME) {
auto s = obs_weak_source_get_source(_audioSource.GetSource());
obs_source_set_volume(s, vol);
obs_source_release(s);
} else {
@@ -82,8 +92,8 @@ void MacroActionAudio::SetVolume(float vol)
float MacroActionAudio::GetVolume()
{
float curVol;
if (_action == AudioAction::SOURCE_VOLUME) {
auto s = obs_weak_source_get_source(_audioSource);
if (_action == Action::SOURCE_VOLUME) {
auto s = obs_weak_source_get_source(_audioSource.GetSource());
if (!s) {
return 0.;
}
@@ -139,15 +149,15 @@ void MacroActionAudio::FadeVolume()
void MacroActionAudio::StartFade()
{
if (_action == AudioAction::SOURCE_VOLUME && !_audioSource) {
if (_action == Action::SOURCE_VOLUME && !_audioSource.GetSource()) {
return;
}
if (FadeActive() && !_abortActiveFade) {
blog(LOG_WARNING,
"Audio fade for volume of %s already active! New fade request will be ignored!",
(_action == AudioAction::SOURCE_VOLUME)
? GetWeakSourceName(_audioSource).c_str()
(_action == Action::SOURCE_VOLUME)
? _audioSource.ToString(true).c_str()
: "master volume");
return;
}
@@ -163,22 +173,31 @@ void MacroActionAudio::StartFade()
bool MacroActionAudio::PerformAction()
{
auto s = obs_weak_source_get_source(_audioSource);
auto s = obs_weak_source_get_source(_audioSource.GetSource());
switch (_action) {
case AudioAction::MUTE:
case Action::MUTE:
obs_source_set_muted(s, true);
break;
case AudioAction::UNMUTE:
case Action::UNMUTE:
obs_source_set_muted(s, false);
break;
case AudioAction::SOURCE_VOLUME:
case AudioAction::MASTER_VOLUME:
case Action::SOURCE_VOLUME:
case Action::MASTER_VOLUME:
if (_fade) {
StartFade();
} else {
SetVolume((float)_volume / 100.0f);
}
break;
case Action::SYNC_OFFSET:
obs_source_set_sync_offset(s, _syncOffset * nsPerMs);
break;
case Action::MONITOR:
obs_source_set_monitoring_type(s, _monitorType);
break;
case Action::BALANCE:
obs_source_set_balance_value(s, _balance);
break;
default:
break;
}
@@ -186,28 +205,29 @@ bool MacroActionAudio::PerformAction()
return true;
}
void MacroActionAudio::LogAction()
void MacroActionAudio::LogAction() const
{
auto it = actionTypes.find(_action);
if (it != actionTypes.end()) {
vblog(LOG_INFO,
"performed action \"%s\" for source \"%s\" with volume %d with fade %d %f",
it->second.c_str(),
GetWeakSourceName(_audioSource).c_str(), _volume, _fade,
_duration.seconds);
it->second.c_str(), _audioSource.ToString(true).c_str(),
_volume, _fade, _duration.seconds);
} else {
blog(LOG_WARNING, "ignored unknown audio action %d",
static_cast<int>(_action));
}
}
bool MacroActionAudio::Save(obs_data_t *obj)
bool MacroActionAudio::Save(obs_data_t *obj) const
{
MacroAction::Save(obj);
_duration.Save(obj);
obs_data_set_string(obj, "audioSource",
GetWeakSourceName(_audioSource).c_str());
_audioSource.Save(obj, "audioSource");
obs_data_set_int(obj, "action", static_cast<int>(_action));
obs_data_set_int(obj, "syncOffset", _syncOffset);
obs_data_set_int(obj, "monitor", _monitorType);
obs_data_set_double(obj, "balance", _balance);
obs_data_set_int(obj, "volume", _volume);
obs_data_set_double(obj, "rate", _rate);
obs_data_set_bool(obj, "fade", _fade);
@@ -221,9 +241,12 @@ bool MacroActionAudio::Load(obs_data_t *obj)
{
MacroAction::Load(obj);
_duration.Load(obj);
const char *audioSourceName = obs_data_get_string(obj, "audioSource");
_audioSource = GetWeakSourceByName(audioSourceName);
_action = static_cast<AudioAction>(obs_data_get_int(obj, "action"));
_audioSource.Load(obj, "audioSource");
_action = static_cast<Action>(obs_data_get_int(obj, "action"));
_syncOffset = obs_data_get_int(obj, "syncOffset");
_monitorType = static_cast<obs_monitoring_type>(
obs_data_get_int(obj, "monitor"));
_balance = obs_data_get_double(obj, "balance");
_volume = obs_data_get_int(obj, "volume");
_rate = obs_data_get_double(obj, "rate");
_fade = obs_data_get_bool(obj, "fade");
@@ -246,18 +269,23 @@ bool MacroActionAudio::Load(obs_data_t *obj)
return true;
}
std::string MacroActionAudio::GetShortDesc()
std::string MacroActionAudio::GetShortDesc() const
{
if (_audioSource) {
return GetWeakSourceName(_audioSource);
}
return "";
return _audioSource.ToString();
}
static inline void populateActionSelection(QComboBox *list)
{
for (auto entry : actionTypes) {
list->addItem(obs_module_text(entry.second.c_str()));
for (const auto &[action, name] : actionTypes) {
if (action == MacroActionAudio::Action::MONITOR) {
if (obs_audio_monitoring_available()) {
list->addItem(obs_module_text(name.c_str()),
static_cast<int>(action));
}
} else {
list->addItem(obs_module_text(name.c_str()),
static_cast<int>(action));
}
}
}
@@ -271,9 +299,15 @@ static inline void populateFadeTypeSelection(QComboBox *list)
MacroActionAudioEdit::MacroActionAudioEdit(
QWidget *parent, std::shared_ptr<MacroActionAudio> entryData)
: QWidget(parent),
_audioSources(new QComboBox),
_sources(new SourceSelectionWidget(this, QStringList(), true)),
_actions(new QComboBox),
_fadeTypes(new QComboBox),
_syncOffset(new QSpinBox),
_monitorTypes(new QComboBox),
_balance(new SliderSpinBox(
0., 1., "",
obs_module_text(
"AdvSceneSwitcher.action.audio.balance.description"))),
_volumePercent(new QSpinBox),
_fade(new QCheckBox),
_duration(new DurationSelection(parent, false)),
@@ -285,6 +319,10 @@ MacroActionAudioEdit::MacroActionAudioEdit(
_fadeTypeLayout(new QHBoxLayout),
_fadeOptionsLayout(new QVBoxLayout)
{
_syncOffset->setMinimum(-950);
_syncOffset->setMaximum(20000);
_syncOffset->setSuffix("ms");
_volumePercent->setMinimum(0);
_volumePercent->setMaximum(2000);
_volumePercent->setSuffix("%");
@@ -294,14 +332,23 @@ MacroActionAudioEdit::MacroActionAudioEdit(
_rate->setSuffix("%");
populateActionSelection(_actions);
populateAudioSelection(_audioSources);
auto sources = GetAudioSourceNames();
sources.sort();
_sources->SetSourceNameList(sources);
populateFadeTypeSelection(_fadeTypes);
populateMonitorTypeSelection(_monitorTypes);
QWidget::connect(_actions, SIGNAL(currentIndexChanged(int)), this,
SLOT(ActionChanged(int)));
QWidget::connect(_audioSources,
SIGNAL(currentTextChanged(const QString &)), this,
SLOT(SourceChanged(const QString &)));
QWidget::connect(_sources,
SIGNAL(SourceChanged(const SourceSelection &)), this,
SLOT(SourceChanged(const SourceSelection &)));
QWidget::connect(_syncOffset, SIGNAL(valueChanged(int)), this,
SLOT(SyncOffsetChanged(int)));
QWidget::connect(_balance, SIGNAL(DoubleValueChanged(double)), this,
SLOT(BalanceChanged(double)));
QWidget::connect(_monitorTypes, SIGNAL(currentIndexChanged(int)), this,
SLOT(MonitorTypeChanged(int)));
QWidget::connect(_volumePercent, SIGNAL(valueChanged(int)), this,
SLOT(VolumeChanged(int)));
QWidget::connect(_fade, SIGNAL(stateChanged(int)), this,
@@ -318,8 +365,11 @@ MacroActionAudioEdit::MacroActionAudioEdit(
SLOT(FadeTypeChanged(int)));
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
{"{{audioSources}}", _audioSources},
{"{{audioSources}}", _sources},
{"{{actions}}", _actions},
{"{{syncOffset}}", _syncOffset},
{"{{monitorTypes}}", _monitorTypes},
{"{{balance}}", _balance},
{"{{volume}}", _volumePercent},
{"{{fade}}", _fade},
{"{{duration}}", _duration},
@@ -342,6 +392,7 @@ MacroActionAudioEdit::MacroActionAudioEdit(
QVBoxLayout *mainLayout = new QVBoxLayout;
mainLayout->addLayout(entryLayout);
mainLayout->addWidget(_balance);
mainLayout->addLayout(_fadeOptionsLayout);
setLayout(mainLayout);
@@ -350,21 +401,23 @@ MacroActionAudioEdit::MacroActionAudioEdit(
_loading = false;
}
bool hasVolumeControl(AudioAction action)
bool hasVolumeControl(MacroActionAudio::Action action)
{
return action == AudioAction::SOURCE_VOLUME ||
action == AudioAction::MASTER_VOLUME;
}
bool hasSourceControl(AudioAction action)
{
return action != AudioAction::MASTER_VOLUME;
return action == MacroActionAudio::Action::SOURCE_VOLUME ||
action == MacroActionAudio::Action::MASTER_VOLUME;
}
void MacroActionAudioEdit::SetWidgetVisibility()
{
_volumePercent->setVisible(hasVolumeControl(_entryData->_action));
_audioSources->setVisible(hasSourceControl(_entryData->_action));
_sources->setVisible(_entryData->_action !=
MacroActionAudio::Action::MASTER_VOLUME);
_syncOffset->setVisible(_entryData->_action ==
MacroActionAudio::Action::SYNC_OFFSET);
_monitorTypes->setVisible(_entryData->_action ==
MacroActionAudio::Action::MONITOR);
_balance->setVisible(_entryData->_action ==
MacroActionAudio::Action::BALANCE);
_fadeTypes->setDisabled(!_entryData->_fade);
_wait->setDisabled(!_entryData->_fade);
@@ -383,7 +436,7 @@ void MacroActionAudioEdit::SetWidgetVisibility()
{"{{rate}}", _rate},
{"{{fadeTypes}}", _fadeTypes},
};
if (_entryData->_fadeType == FadeType::DURATION) {
if (_entryData->_fadeType == MacroActionAudio::FadeType::DURATION) {
placeWidgets(
obs_module_text(
"AdvSceneSwitcher.action.audio.fade.duration"),
@@ -394,8 +447,10 @@ void MacroActionAudioEdit::SetWidgetVisibility()
_fadeTypeLayout, widgetPlaceholders);
}
_duration->setVisible(_entryData->_fadeType == FadeType::DURATION);
_rate->setVisible(_entryData->_fadeType == FadeType::RATE);
_duration->setVisible(_entryData->_fadeType ==
MacroActionAudio::FadeType::DURATION);
_rate->setVisible(_entryData->_fadeType ==
MacroActionAudio::FadeType::RATE);
setLayoutVisible(_fadeTypeLayout,
hasVolumeControl(_entryData->_action));
@@ -410,9 +465,12 @@ void MacroActionAudioEdit::UpdateEntryData()
return;
}
_audioSources->setCurrentText(
GetWeakSourceName(_entryData->_audioSource).c_str());
_actions->setCurrentIndex(static_cast<int>(_entryData->_action));
_sources->SetSource(_entryData->_audioSource);
_actions->setCurrentIndex(
_actions->findData(static_cast<int>(_entryData->_action)));
_syncOffset->setValue(_entryData->_syncOffset);
_monitorTypes->setCurrentIndex(_entryData->_monitorType);
_balance->SetDoubleValue(_entryData->_balance);
_volumePercent->setValue(_entryData->_volume);
_fade->setChecked(_entryData->_fade);
_duration->SetDuration(_entryData->_duration);
@@ -423,29 +481,60 @@ void MacroActionAudioEdit::UpdateEntryData()
SetWidgetVisibility();
}
void MacroActionAudioEdit::SourceChanged(const QString &text)
void MacroActionAudioEdit::SourceChanged(const SourceSelection &source)
{
if (_loading || !_entryData) {
return;
}
std::lock_guard<std::mutex> lock(switcher->m);
_entryData->_audioSource = GetWeakSourceByQString(text);
_entryData->_audioSource = source;
emit HeaderInfoChanged(
QString::fromStdString(_entryData->GetShortDesc()));
}
void MacroActionAudioEdit::ActionChanged(int value)
void MacroActionAudioEdit::ActionChanged(int idx)
{
if (_loading || !_entryData) {
return;
}
std::lock_guard<std::mutex> lock(switcher->m);
_entryData->_action = static_cast<AudioAction>(value);
_entryData->_action = static_cast<MacroActionAudio::Action>(
_actions->itemData(idx).toInt());
SetWidgetVisibility();
}
void MacroActionAudioEdit::SyncOffsetChanged(int value)
{
if (_loading || !_entryData) {
return;
}
std::lock_guard<std::mutex> lock(switcher->m);
_entryData->_syncOffset = value;
}
void MacroActionAudioEdit::MonitorTypeChanged(int value)
{
if (_loading || !_entryData) {
return;
}
std::lock_guard<std::mutex> lock(switcher->m);
_entryData->_monitorType = static_cast<obs_monitoring_type>(value);
}
void MacroActionAudioEdit::BalanceChanged(double value)
{
if (_loading || !_entryData) {
return;
}
std::lock_guard<std::mutex> lock(switcher->m);
_entryData->_balance = value;
}
void MacroActionAudioEdit::VolumeChanged(int value)
{
if (_loading || !_entryData) {
@@ -514,6 +603,6 @@ void MacroActionAudioEdit::FadeTypeChanged(int value)
}
std::lock_guard<std::mutex> lock(switcher->m);
_entryData->_fadeType = static_cast<FadeType>(value);
_entryData->_fadeType = static_cast<MacroActionAudio::FadeType>(value);
SetWidgetVisibility();
}

View File

@@ -1,41 +1,50 @@
#pragma once
#include "macro-action-edit.hpp"
#include "duration-control.hpp"
#include "slider-spinbox.hpp"
#include "source-selection.hpp"
#include <QSpinBox>
#include <QDoubleSpinBox>
#include <QCheckBox>
#include <QHBoxLayout>
enum class AudioAction {
MUTE,
UNMUTE,
SOURCE_VOLUME,
MASTER_VOLUME,
};
enum class FadeType {
DURATION,
RATE,
};
class MacroActionAudio : public MacroAction {
public:
MacroActionAudio(Macro *m) : MacroAction(m) {}
bool PerformAction();
void LogAction();
bool Save(obs_data_t *obj);
void LogAction() const;
bool Save(obs_data_t *obj) const;
bool Load(obs_data_t *obj);
std::string GetShortDesc();
std::string GetId() { return id; };
std::string GetShortDesc() const;
std::string GetId() const { return id; };
static std::shared_ptr<MacroAction> Create(Macro *m)
{
return std::make_shared<MacroActionAudio>(m);
}
OBSWeakSource _audioSource;
AudioAction _action = AudioAction::MUTE;
SourceSelection _audioSource;
enum class Action {
MUTE,
UNMUTE,
SOURCE_VOLUME,
MASTER_VOLUME,
SYNC_OFFSET,
MONITOR,
BALANCE,
};
enum class FadeType {
DURATION,
RATE,
};
Action _action = Action::MUTE;
FadeType _fadeType = FadeType::DURATION;
int64_t _syncOffset = 0;
obs_monitoring_type _monitorType = OBS_MONITORING_TYPE_NONE;
double _balance = 0.5;
int _volume = 0;
bool _fade = false;
Duration _duration;
@@ -73,8 +82,11 @@ public:
}
private slots:
void SourceChanged(const QString &text);
void SourceChanged(const SourceSelection &);
void ActionChanged(int value);
void SyncOffsetChanged(int value);
void MonitorTypeChanged(int value);
void BalanceChanged(double value);
void VolumeChanged(int value);
void FadeChanged(int value);
void DurationChanged(double seconds);
@@ -86,9 +98,12 @@ signals:
void HeaderInfoChanged(const QString &);
protected:
QComboBox *_audioSources;
SourceSelectionWidget *_sources;
QComboBox *_actions;
QComboBox *_fadeTypes;
QSpinBox *_syncOffset;
QComboBox *_monitorTypes;
SliderSpinBox *_balance;
QSpinBox *_volumePercent;
QCheckBox *_fade;
DurationSelection *_duration;

View File

@@ -147,9 +147,9 @@ void MacroActionEdit::SetEntryData(std::shared_ptr<MacroAction> *data)
_entryData = data;
}
MacroSegment *MacroActionEdit::Data()
std::shared_ptr<MacroSegment> MacroActionEdit::Data()
{
return _entryData->get();
return *_entryData;
}
void AdvSceneSwitcher::AddMacroAction(int idx)
@@ -172,8 +172,9 @@ void AdvSceneSwitcher::AddMacroAction(int idx)
}
{
std::lock_guard<std::mutex> lock(switcher->m);
macro->Actions().emplace(macro->Actions().begin() + idx,
MacroActionFactory::Create(id, macro));
macro->Actions().emplace(
macro->Actions().begin() + idx,
MacroActionFactory::Create(id, macro.get()));
if (idx - 1 >= 0) {
auto data = obs_data_create();
macro->Actions().at(idx - 1)->Save(data);
@@ -187,6 +188,7 @@ void AdvSceneSwitcher::AddMacroAction(int idx)
SetActionData(*macro);
}
HighlightAction(idx);
emit(MacroSegmentOrderChanged());
}
void AdvSceneSwitcher::on_actionAdd_clicked()
@@ -197,10 +199,6 @@ void AdvSceneSwitcher::on_actionAdd_clicked()
}
if (currentActionIdx == -1) {
auto macro = getSelectedMacro();
if (!macro) {
return;
}
AddMacroAction((int)macro->Actions().size());
} else {
AddMacroAction(currentActionIdx + 1);
@@ -233,6 +231,7 @@ void AdvSceneSwitcher::RemoveMacroAction(int idx)
}
MacroActionSelectionChanged(-1);
lastInteracted = MacroSection::ACTIONS;
emit(MacroSegmentOrderChanged());
}
void AdvSceneSwitcher::on_actionRemove_clicked()
@@ -285,6 +284,7 @@ void AdvSceneSwitcher::SwapActions(Macro *m, int pos1, int pos2)
actionsList->Insert(pos1, widget2);
actionsList->Insert(pos2, widget1);
SetActionData(*m);
emit(MacroSegmentOrderChanged());
}
void AdvSceneSwitcher::MoveMacroActionUp(int idx)
@@ -298,7 +298,7 @@ void AdvSceneSwitcher::MoveMacroActionUp(int idx)
return;
}
SwapActions(macro, idx, idx - 1);
SwapActions(macro.get(), idx, idx - 1);
HighlightAction(idx - 1);
}
@@ -313,7 +313,7 @@ void AdvSceneSwitcher::MoveMacroActionDown(int idx)
return;
}
SwapActions(macro, idx, idx + 1);
SwapActions(macro.get(), idx, idx + 1);
HighlightAction(idx + 1);
}
@@ -359,4 +359,5 @@ void AdvSceneSwitcher::MacroActionReorder(int to, int from)
SetActionData(*macro);
}
HighlightAction(to);
emit(MacroSegmentOrderChanged());
}

View File

@@ -44,7 +44,7 @@ private slots:
void ActionSelectionChanged(const QString &text);
private:
MacroSegment *Data();
std::shared_ptr<MacroSegment> Data();
QComboBox *_actionSelection;

View File

@@ -40,7 +40,7 @@ bool MacroActionFile::PerformAction()
return true;
}
void MacroActionFile::LogAction()
void MacroActionFile::LogAction() const
{
auto it = actionTypes.find(_action);
if (it != actionTypes.end()) {
@@ -52,11 +52,11 @@ void MacroActionFile::LogAction()
}
}
bool MacroActionFile::Save(obs_data_t *obj)
bool MacroActionFile::Save(obs_data_t *obj) const
{
MacroAction::Save(obj);
obs_data_set_string(obj, "file", _file.c_str());
obs_data_set_string(obj, "text", _text.c_str());
_text.Save(obj, "text");
obs_data_set_int(obj, "action", static_cast<int>(_action));
return true;
}
@@ -65,12 +65,12 @@ bool MacroActionFile::Load(obs_data_t *obj)
{
MacroAction::Load(obj);
_file = obs_data_get_string(obj, "file");
_text = obs_data_get_string(obj, "text");
_text.Load(obj, "text");
_action = static_cast<FileAction>(obs_data_get_int(obj, "action"));
return true;
}
std::string MacroActionFile::GetShortDesc()
std::string MacroActionFile::GetShortDesc() const
{
return _file;
}
@@ -87,7 +87,7 @@ MacroActionFileEdit::MacroActionFileEdit(
: QWidget(parent)
{
_filePath = new FileSelection(FileSelection::Type::WRITE);
_text = new ResizingPlainTextEdit(this);
_text = new VariableTextEdit(this);
_actions = new QComboBox();
populateActionSelection(_actions);
@@ -127,7 +127,7 @@ void MacroActionFileEdit::UpdateEntryData()
_actions->setCurrentIndex(static_cast<int>(_entryData->_action));
_filePath->SetPath(QString::fromStdString(_entryData->_file));
_text->setPlainText(QString::fromStdString(_entryData->_text));
_text->setPlainText(_entryData->_text);
adjustSize();
updateGeometry();

View File

@@ -1,7 +1,7 @@
#pragma once
#include "macro-action-edit.hpp"
#include "file-selection.hpp"
#include "resizing-text-edit.hpp"
#include "variable-text-edit.hpp"
#include <QSpinBox>
@@ -14,18 +14,19 @@ class MacroActionFile : public MacroAction {
public:
MacroActionFile(Macro *m) : MacroAction(m) {}
bool PerformAction();
void LogAction();
bool Save(obs_data_t *obj);
void LogAction() const;
bool Save(obs_data_t *obj) const;
bool Load(obs_data_t *obj);
std::string GetShortDesc();
std::string GetId() { return id; };
std::string GetShortDesc() const;
std::string GetId() const { return id; };
static std::shared_ptr<MacroAction> Create(Macro *m)
{
return std::make_shared<MacroActionFile>(m);
}
std::string _file = obs_module_text("AdvSceneSwitcher.enterPath");
std::string _text = obs_module_text("AdvSceneSwitcher.enterText");
VariableResolvingString _text =
obs_module_text("AdvSceneSwitcher.enterText");
FileAction _action = FileAction::WRITE;
private:
@@ -58,7 +59,7 @@ signals:
protected:
FileSelection *_filePath;
ResizingPlainTextEdit *_text;
VariableTextEdit *_text;
QComboBox *_actions;
std::shared_ptr<MacroActionFile> _entryData;

View File

@@ -9,24 +9,41 @@ bool MacroActionFilter::_registered = MacroActionFactory::Register(
{MacroActionFilter::Create, MacroActionFilterEdit::Create,
"AdvSceneSwitcher.action.filter"});
const static std::map<FilterAction, std::string> actionTypes = {
{FilterAction::ENABLE, "AdvSceneSwitcher.action.filter.type.enable"},
{FilterAction::DISABLE, "AdvSceneSwitcher.action.filter.type.disable"},
{FilterAction::SETTINGS,
const static std::map<MacroActionFilter::Action, std::string> actionTypes = {
{MacroActionFilter::Action::ENABLE,
"AdvSceneSwitcher.action.filter.type.enable"},
{MacroActionFilter::Action::DISABLE,
"AdvSceneSwitcher.action.filter.type.disable"},
{MacroActionFilter::Action::SETTINGS,
"AdvSceneSwitcher.action.filter.type.settings"},
};
void MacroActionFilter::ResolveVariables()
{
if (_source.GetType() == SourceSelection::Type::SOURCE) {
return;
}
std::string name = GetWeakSourceName(_filter);
if (!name.empty()) {
_filterName = name;
}
_filter = GetWeakFilterByName(_source.GetSource(), _filterName.c_str());
}
bool MacroActionFilter::PerformAction()
{
ResolveVariables();
auto s = obs_weak_source_get_source(_filter);
switch (_action) {
case FilterAction::ENABLE:
case Action::ENABLE:
obs_source_set_enabled(s, true);
break;
case FilterAction::DISABLE:
case Action::DISABLE:
obs_source_set_enabled(s, false);
break;
case FilterAction::SETTINGS:
case Action::SETTINGS:
setSourceSettings(s, _settings);
break;
default:
@@ -36,47 +53,46 @@ bool MacroActionFilter::PerformAction()
return true;
}
void MacroActionFilter::LogAction()
void MacroActionFilter::LogAction() const
{
auto it = actionTypes.find(_action);
if (it != actionTypes.end()) {
vblog(LOG_INFO,
"performed action \"%s\" for filter \"%s\" on source \"%s\"",
it->second.c_str(), GetWeakSourceName(_filter).c_str(),
GetWeakSourceName(_source).c_str());
_source.ToString(true).c_str());
} else {
blog(LOG_WARNING, "ignored unknown filter action %d",
static_cast<int>(_action));
}
}
bool MacroActionFilter::Save(obs_data_t *obj)
bool MacroActionFilter::Save(obs_data_t *obj) const
{
MacroAction::Save(obj);
obs_data_set_string(obj, "source", GetWeakSourceName(_source).c_str());
obs_data_set_string(obj, "filter", GetWeakSourceName(_filter).c_str());
_source.Save(obj);
obs_data_set_string(obj, "filter", _filterName.c_str());
obs_data_set_int(obj, "action", static_cast<int>(_action));
obs_data_set_string(obj, "settings", _settings.c_str());
_settings.Save(obj, "settings");
return true;
}
bool MacroActionFilter::Load(obs_data_t *obj)
{
MacroAction::Load(obj);
const char *sourceName = obs_data_get_string(obj, "source");
_source = GetWeakSourceByName(sourceName);
const char *filterName = obs_data_get_string(obj, "filter");
_filter = GetWeakFilterByQString(_source, filterName);
_action = static_cast<FilterAction>(obs_data_get_int(obj, "action"));
_settings = obs_data_get_string(obj, "settings");
_source.Load(obj);
_filterName = obs_data_get_string(obj, "filter");
_filter = GetWeakFilterByQString(_source.GetSource(),
_filterName.c_str());
_action = static_cast<Action>(obs_data_get_int(obj, "action"));
_settings.Load(obj, "settings");
return true;
}
std::string MacroActionFilter::GetShortDesc()
std::string MacroActionFilter::GetShortDesc() const
{
if (_filter && _source) {
return GetWeakSourceName(_source) + " - " +
GetWeakSourceName(_filter);
if (_filter && !_source.ToString().empty()) {
return _source.ToString() + " - " + GetWeakSourceName(_filter);
}
return "";
}
@@ -90,23 +106,26 @@ static inline void populateActionSelection(QComboBox *list)
MacroActionFilterEdit::MacroActionFilterEdit(
QWidget *parent, std::shared_ptr<MacroActionFilter> entryData)
: QWidget(parent)
: QWidget(parent),
_sources(new SourceSelectionWidget(this, QStringList(), true)),
_filters(new QComboBox()),
_actions(new QComboBox()),
_getSettings(new QPushButton(obs_module_text(
"AdvSceneSwitcher.action.filter.getSettings"))),
_settings(new VariableTextEdit(this))
{
_sources = new QComboBox();
_filters = new QComboBox();
_filters->setSizeAdjustPolicy(QComboBox::AdjustToContents);
_actions = new QComboBox();
_getSettings = new QPushButton(
obs_module_text("AdvSceneSwitcher.action.filter.getSettings"));
_settings = new ResizingPlainTextEdit(this);
populateActionSelection(_actions);
populateSourcesWithFilterSelection(_sources);
auto sources = GetSourcesWithFilterNames();
sources.sort();
_sources->SetSourceNameList(sources);
QWidget::connect(_actions, SIGNAL(currentIndexChanged(int)), this,
SLOT(ActionChanged(int)));
QWidget::connect(_sources, SIGNAL(currentTextChanged(const QString &)),
this, SLOT(SourceChanged(const QString &)));
QWidget::connect(_sources,
SIGNAL(SourceChanged(const SourceSelection &)), this,
SLOT(SourceChanged(const SourceSelection &)));
QWidget::connect(_filters, SIGNAL(currentTextChanged(const QString &)),
this, SLOT(FilterChanged(const QString &)));
QWidget::connect(_getSettings, SIGNAL(clicked()), this,
@@ -146,29 +165,29 @@ void MacroActionFilterEdit::UpdateEntryData()
}
_actions->setCurrentIndex(static_cast<int>(_entryData->_action));
_sources->setCurrentText(
GetWeakSourceName(_entryData->_source).c_str());
populateFilterSelection(_filters, _entryData->_source);
_sources->SetSource(_entryData->_source);
populateFilterSelection(_filters, _entryData->_source.GetSource());
_filters->setCurrentText(
GetWeakSourceName(_entryData->_filter).c_str());
_settings->setPlainText(QString::fromStdString(_entryData->_settings));
SetWidgetVisibility(_entryData->_action == FilterAction::SETTINGS);
_settings->setPlainText(_entryData->_settings);
SetWidgetVisibility(_entryData->_action ==
MacroActionFilter::Action::SETTINGS);
adjustSize();
updateGeometry();
}
void MacroActionFilterEdit::SourceChanged(const QString &text)
void MacroActionFilterEdit::SourceChanged(const SourceSelection &source)
{
if (_loading || !_entryData) {
return;
}
{
std::lock_guard<std::mutex> lock(switcher->m);
_entryData->_source = GetWeakSourceByQString(text);
_entryData->_source = source;
}
_filters->clear();
populateFilterSelection(_filters, _entryData->_source);
populateFilterSelection(_filters, _entryData->_source.GetSource());
_filters->adjustSize();
}
@@ -179,7 +198,9 @@ void MacroActionFilterEdit::FilterChanged(const QString &text)
}
std::lock_guard<std::mutex> lock(switcher->m);
_entryData->_filter = GetWeakFilterByQString(_entryData->_source, text);
_entryData->_filterName = text.toStdString();
_entryData->_filter =
GetWeakFilterByQString(_entryData->_source.GetSource(), text);
emit HeaderInfoChanged(
QString::fromStdString(_entryData->GetShortDesc()));
}
@@ -191,13 +212,14 @@ void MacroActionFilterEdit::ActionChanged(int value)
}
std::lock_guard<std::mutex> lock(switcher->m);
_entryData->_action = static_cast<FilterAction>(value);
SetWidgetVisibility(_entryData->_action == FilterAction::SETTINGS);
_entryData->_action = static_cast<MacroActionFilter::Action>(value);
SetWidgetVisibility(_entryData->_action ==
MacroActionFilter::Action::SETTINGS);
}
void MacroActionFilterEdit::GetSettingsClicked()
{
if (_loading || !_entryData || !_entryData->_source ||
if (_loading || !_entryData || !_entryData->_source.GetSource() ||
!_entryData->_filter) {
return;
}

View File

@@ -1,39 +1,45 @@
#pragma once
#include "macro-action-edit.hpp"
#include "resizing-text-edit.hpp"
#include "variable-text-edit.hpp"
#include "source-selection.hpp"
#include <QComboBox>
#include <QSpinBox>
#include <QPushButton>
enum class FilterAction {
ENABLE,
DISABLE,
SETTINGS,
};
class MacroActionFilter : public MacroAction {
public:
MacroActionFilter(Macro *m) : MacroAction(m) {}
bool PerformAction();
void LogAction();
bool Save(obs_data_t *obj);
void LogAction() const;
bool Save(obs_data_t *obj) const;
bool Load(obs_data_t *obj);
std::string GetShortDesc();
std::string GetId() { return id; };
std::string GetShortDesc() const;
std::string GetId() const { return id; };
static std::shared_ptr<MacroAction> Create(Macro *m)
{
return std::make_shared<MacroActionFilter>(m);
}
OBSWeakSource _source;
enum class Action {
ENABLE,
DISABLE,
SETTINGS,
};
SourceSelection _source;
OBSWeakSource _filter;
FilterAction _action = FilterAction::ENABLE;
std::string _settings = "";
Action _action = Action::ENABLE;
VariableResolvingString _settings = "";
private:
void ResolveVariables();
std::string _filterName = "";
static bool _registered;
static const std::string id;
friend class MacroActionFilterEdit;
};
class MacroActionFilterEdit : public QWidget {
@@ -53,8 +59,8 @@ public:
}
private slots:
void SourceChanged(const QString &text);
void FilterChanged(const QString &text);
void SourceChanged(const SourceSelection &);
void FilterChanged(const QString &);
void ActionChanged(int value);
void GetSettingsClicked();
void SettingsChanged();
@@ -62,11 +68,11 @@ signals:
void HeaderInfoChanged(const QString &);
protected:
QComboBox *_sources;
SourceSelectionWidget *_sources;
QComboBox *_filters;
QComboBox *_actions;
QPushButton *_getSettings;
ResizingPlainTextEdit *_settings;
VariableTextEdit *_settings;
std::shared_ptr<MacroActionFilter> _entryData;
private:

View File

@@ -225,7 +225,7 @@ bool MacroActionHotkey::PerformAction()
if (!keys.empty()) {
int dur = _duration;
if (_onlySendToObs) {
if (_onlySendToObs || !canSimulateKeyPresses) {
std::thread t([keys, dur]() { InjectKeys(keys, dur); });
t.detach();
} else {
@@ -237,12 +237,12 @@ bool MacroActionHotkey::PerformAction()
return true;
}
void MacroActionHotkey::LogAction()
void MacroActionHotkey::LogAction() const
{
vblog(LOG_INFO, "sent hotkey");
}
bool MacroActionHotkey::Save(obs_data_t *obj)
bool MacroActionHotkey::Save(obs_data_t *obj) const
{
MacroAction::Save(obj);
obs_data_set_int(obj, "key", static_cast<int>(_key));
@@ -463,9 +463,10 @@ MacroActionHotkeyEdit::MacroActionHotkeyEdit(
mainLayout->addLayout(line2Layout);
mainLayout->addWidget(_onlySendToOBS);
mainLayout->addWidget(_noKeyPressSimulationWarning);
setLayout(mainLayout);
_onlySendToOBS->setEnabled(canSimulateKeyPresses);
_entryData = entryData;
UpdateEntryData();
_loading = false;
@@ -493,7 +494,8 @@ void MacroActionHotkeyEdit::UpdateEntryData()
_leftMeta->setChecked(_entryData->_leftMeta);
_rightMeta->setChecked(_entryData->_rightMeta);
_duration->setValue(_entryData->_duration);
_onlySendToOBS->setChecked(_entryData->_onlySendToObs);
_onlySendToOBS->setChecked(_entryData->_onlySendToObs ||
!canSimulateKeyPresses);
SetWarningVisibility();
}

View File

@@ -11,10 +11,10 @@ class MacroActionHotkey : public MacroAction {
public:
MacroActionHotkey(Macro *m) : MacroAction(m) {}
bool PerformAction();
void LogAction();
bool Save(obs_data_t *obj);
void LogAction() const;
bool Save(obs_data_t *obj) const;
bool Load(obs_data_t *obj);
std::string GetId() { return id; };
std::string GetId() const { return id; };
static std::shared_ptr<MacroAction> Create(Macro *m)
{
return std::make_shared<MacroActionHotkey>(m);

View File

@@ -16,9 +16,14 @@ const static std::map<MacroActionHttp::Method, std::string> methods = {
"AdvSceneSwitcher.action.http.type.post"},
};
size_t WriteCB(void *, size_t size, size_t nmemb, std::string *)
size_t DropCB(void *, size_t size, size_t nmemb, std::string *)
{
// Just drop the data
return size * nmemb;
}
size_t WriteCB(void *ptr, size_t size, size_t nmemb, std::string *buffer)
{
buffer->append((char *)ptr, nmemb);
return size * nmemb;
}
@@ -29,10 +34,15 @@ void MacroActionHttp::Get()
switcher->curl.SetOpt(CURLOPT_TIMEOUT_MS, _timeout.seconds * 1000);
std::string response;
switcher->curl.SetOpt(CURLOPT_WRITEFUNCTION, WriteCB);
if (IsReferencedInVars()) {
switcher->curl.SetOpt(CURLOPT_WRITEFUNCTION, WriteCB);
} else {
switcher->curl.SetOpt(CURLOPT_WRITEFUNCTION, DropCB);
}
switcher->curl.SetOpt(CURLOPT_WRITEDATA, &response);
switcher->curl.Perform();
SetVariableValue(response);
}
void MacroActionHttp::Post()
@@ -64,7 +74,7 @@ bool MacroActionHttp::PerformAction()
return true;
}
void MacroActionHttp::LogAction()
void MacroActionHttp::LogAction() const
{
auto it = methods.find(_method);
if (it != methods.end()) {
@@ -77,11 +87,11 @@ void MacroActionHttp::LogAction()
}
}
bool MacroActionHttp::Save(obs_data_t *obj)
bool MacroActionHttp::Save(obs_data_t *obj) const
{
MacroAction::Save(obj);
obs_data_set_string(obj, "url", _url.c_str());
obs_data_set_string(obj, "data", _data.c_str());
_data.Save(obj, "data");
obs_data_set_int(obj, "method", static_cast<int>(_method));
_timeout.Save(obj);
return true;
@@ -91,13 +101,13 @@ bool MacroActionHttp::Load(obs_data_t *obj)
{
MacroAction::Load(obj);
_url = obs_data_get_string(obj, "url");
_data = obs_data_get_string(obj, "data");
_data.Load(obj, "data");
_method = static_cast<Method>(obs_data_get_int(obj, "method"));
_timeout.Load(obj);
return true;
}
std::string MacroActionHttp::GetShortDesc()
std::string MacroActionHttp::GetShortDesc() const
{
return _url;
}
@@ -114,7 +124,7 @@ MacroActionHttpEdit::MacroActionHttpEdit(
: QWidget(parent),
_url(new QLineEdit()),
_methods(new QComboBox()),
_data(new ResizingPlainTextEdit(this)),
_data(new VariableTextEdit(this)),
_timeout(new DurationSelection(this, false))
{
populateMethodSelection(_methods);
@@ -161,7 +171,7 @@ void MacroActionHttpEdit::UpdateEntryData()
}
_url->setText(QString::fromStdString(_entryData->_url));
_data->setPlainText(QString::fromStdString(_entryData->_data));
_data->setPlainText(_entryData->_data);
_methods->setCurrentIndex(static_cast<int>(_entryData->_method));
_timeout->SetDuration(_entryData->_timeout);
SetWidgetVisibility();

View File

@@ -1,6 +1,6 @@
#pragma once
#include "macro-action-edit.hpp"
#include "resizing-text-edit.hpp"
#include "variable-text-edit.hpp"
#include "duration-control.hpp"
#include <QLineEdit>
@@ -8,13 +8,13 @@
class MacroActionHttp : public MacroAction {
public:
MacroActionHttp(Macro *m) : MacroAction(m) {}
MacroActionHttp(Macro *m) : MacroAction(m, true) {}
bool PerformAction();
void LogAction();
bool Save(obs_data_t *obj);
void LogAction() const;
bool Save(obs_data_t *obj) const;
bool Load(obs_data_t *obj);
std::string GetShortDesc();
std::string GetId() { return id; };
std::string GetShortDesc() const;
std::string GetId() const { return id; };
static std::shared_ptr<MacroAction> Create(Macro *m)
{
return std::make_shared<MacroActionHttp>(m);
@@ -26,7 +26,8 @@ public:
};
std::string _url = obs_module_text("AdvSceneSwitcher.enterURL");
std::string _data = obs_module_text("AdvSceneSwitcher.enterText");
VariableResolvingString _data =
obs_module_text("AdvSceneSwitcher.enterText");
Method _method = Method::GET;
Duration _timeout;
@@ -70,7 +71,7 @@ private:
QLineEdit *_url;
QComboBox *_methods;
ResizingPlainTextEdit *_data;
VariableTextEdit *_data;
DurationSelection *_timeout;
bool _loading = true;
};

View File

@@ -33,10 +33,12 @@ bool MacroActionMacro::PerformAction()
_macro->SetPaused(false);
break;
case PerformMacroAction::RESET_COUNTER:
_macro->ResetCount();
_macro->ResetRunCount();
break;
case PerformMacroAction::RUN:
_macro->PerformActions();
if (!_macro->Paused()) {
_macro->PerformActions();
}
break;
case PerformMacroAction::STOP:
_macro->Stop();
@@ -47,7 +49,7 @@ bool MacroActionMacro::PerformAction()
return true;
}
void MacroActionMacro::LogAction()
void MacroActionMacro::LogAction() const
{
if (!_macro.get()) {
return;
@@ -75,7 +77,7 @@ void MacroActionMacro::LogAction()
}
}
bool MacroActionMacro::Save(obs_data_t *obj)
bool MacroActionMacro::Save(obs_data_t *obj) const
{
MacroAction::Save(obj);
_macro.Save(obj);
@@ -92,7 +94,7 @@ bool MacroActionMacro::Load(obs_data_t *obj)
return true;
}
std::string MacroActionMacro::GetShortDesc()
std::string MacroActionMacro::GetShortDesc() const
{
if (_macro.get()) {
return _macro->Name();

View File

@@ -16,11 +16,11 @@ class MacroActionMacro : public MacroRefAction {
public:
MacroActionMacro(Macro *m) : MacroAction(m), MacroRefAction(m) {}
bool PerformAction();
void LogAction();
bool Save(obs_data_t *obj);
void LogAction() const;
bool Save(obs_data_t *obj) const;
bool Load(obs_data_t *obj);
std::string GetShortDesc();
std::string GetId() { return id; };
std::string GetShortDesc() const;
std::string GetId() const { return id; };
static std::shared_ptr<MacroAction> Create(Macro *m)
{
return std::make_shared<MacroActionMacro>(m);

View File

@@ -21,7 +21,7 @@ const static std::map<MediaAction, std::string> actionTypes = {
bool MacroActionMedia::PerformAction()
{
auto source = obs_weak_source_get_source(_mediaSource);
auto source = obs_weak_source_get_source(_mediaSource.GetSource());
obs_media_state state = obs_source_media_get_state(source);
switch (_action) {
case MediaAction::PLAY:
@@ -57,24 +57,22 @@ bool MacroActionMedia::PerformAction()
return true;
}
void MacroActionMedia::LogAction()
void MacroActionMedia::LogAction() const
{
auto it = actionTypes.find(_action);
if (it != actionTypes.end()) {
vblog(LOG_INFO, "performed action \"%s\" for source \"%s\"",
it->second.c_str(),
GetWeakSourceName(_mediaSource).c_str());
it->second.c_str(), _mediaSource.ToString(true).c_str());
} else {
blog(LOG_WARNING, "ignored unknown media action %d",
static_cast<int>(_action));
}
}
bool MacroActionMedia::Save(obs_data_t *obj)
bool MacroActionMedia::Save(obs_data_t *obj) const
{
MacroAction::Save(obj);
obs_data_set_string(obj, "mediaSource",
GetWeakSourceName(_mediaSource).c_str());
_mediaSource.Save(obj, "mediaSource");
obs_data_set_int(obj, "action", static_cast<int>(_action));
_seek.Save(obj);
return true;
@@ -83,19 +81,15 @@ bool MacroActionMedia::Save(obs_data_t *obj)
bool MacroActionMedia::Load(obs_data_t *obj)
{
MacroAction::Load(obj);
const char *MediaSourceName = obs_data_get_string(obj, "mediaSource");
_mediaSource = GetWeakSourceByName(MediaSourceName);
_mediaSource.Load(obj, "mediaSource");
_action = static_cast<MediaAction>(obs_data_get_int(obj, "action"));
_seek.Load(obj);
return true;
}
std::string MacroActionMedia::GetShortDesc()
std::string MacroActionMedia::GetShortDesc() const
{
if (_mediaSource) {
return GetWeakSourceName(_mediaSource);
}
return "";
return _mediaSource.ToString();
}
static inline void populateActionSelection(QComboBox *list)
@@ -108,18 +102,20 @@ static inline void populateActionSelection(QComboBox *list)
MacroActionMediaEdit::MacroActionMediaEdit(
QWidget *parent, std::shared_ptr<MacroActionMedia> entryData)
: QWidget(parent),
_mediaSources(new QComboBox()),
_sources(new SourceSelectionWidget(this, QStringList(), true)),
_actions(new QComboBox()),
_seek(new DurationSelection())
{
populateActionSelection(_actions);
populateMediaSelection(_mediaSources);
auto sources = GetMediaSourceNames();
sources.sort();
_sources->SetSourceNameList(sources);
QWidget::connect(_actions, SIGNAL(currentIndexChanged(int)), this,
SLOT(ActionChanged(int)));
QWidget::connect(_mediaSources,
SIGNAL(currentTextChanged(const QString &)), this,
SLOT(SourceChanged(const QString &)));
QWidget::connect(_sources,
SIGNAL(SourceChanged(const SourceSelection &)), this,
SLOT(SourceChanged(const SourceSelection &)));
QWidget::connect(_seek, SIGNAL(DurationChanged(double)), this,
SLOT(DurationChanged(double)));
QWidget::connect(_seek, SIGNAL(UnitChanged(DurationUnit)), this,
@@ -127,7 +123,7 @@ MacroActionMediaEdit::MacroActionMediaEdit(
QHBoxLayout *mainLayout = new QHBoxLayout;
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
{"{{mediaSources}}", _mediaSources},
{"{{mediaSources}}", _sources},
{"{{actions}}", _actions},
{"{{duration}}", _seek},
};
@@ -146,21 +142,20 @@ void MacroActionMediaEdit::UpdateEntryData()
return;
}
_mediaSources->setCurrentText(
GetWeakSourceName(_entryData->_mediaSource).c_str());
_sources->SetSource(_entryData->_mediaSource);
_actions->setCurrentIndex(static_cast<int>(_entryData->_action));
_seek->SetDuration(_entryData->_seek);
SetWidgetVisibility();
}
void MacroActionMediaEdit::SourceChanged(const QString &text)
void MacroActionMediaEdit::SourceChanged(const SourceSelection &source)
{
if (_loading || !_entryData) {
return;
}
std::lock_guard<std::mutex> lock(switcher->m);
_entryData->_mediaSource = GetWeakSourceByQString(text);
_entryData->_mediaSource = source;
emit HeaderInfoChanged(
QString::fromStdString(_entryData->GetShortDesc()));
}

View File

@@ -1,6 +1,7 @@
#pragma once
#include "macro-action-edit.hpp"
#include "duration-control.hpp"
#include "source-selection.hpp"
#include <QSpinBox>
#include <QHBoxLayout>
@@ -19,17 +20,17 @@ class MacroActionMedia : public MacroAction {
public:
MacroActionMedia(Macro *m) : MacroAction(m) {}
bool PerformAction();
void LogAction();
bool Save(obs_data_t *obj);
void LogAction() const;
bool Save(obs_data_t *obj) const;
bool Load(obs_data_t *obj);
std::string GetShortDesc();
std::string GetId() { return id; };
std::string GetShortDesc() const;
std::string GetId() const { return id; };
static std::shared_ptr<MacroAction> Create(Macro *m)
{
return std::make_shared<MacroActionMedia>(m);
}
OBSWeakSource _mediaSource;
SourceSelection _mediaSource;
MediaAction _action = MediaAction::PLAY;
Duration _seek;
@@ -55,7 +56,7 @@ public:
}
private slots:
void SourceChanged(const QString &text);
void SourceChanged(const SourceSelection &);
void ActionChanged(int value);
void DurationChanged(double value);
void DurationUnitChanged(DurationUnit unit);
@@ -63,7 +64,7 @@ signals:
void HeaderInfoChanged(const QString &);
protected:
QComboBox *_mediaSources;
SourceSelectionWidget *_sources;
QComboBox *_actions;
DurationSelection *_seek;
std::shared_ptr<MacroActionMedia> _entryData;

View File

@@ -76,7 +76,7 @@ bool MacroActionPluginState::PerformAction()
return true;
}
void MacroActionPluginState::LogAction()
void MacroActionPluginState::LogAction() const
{
switch (_action) {
case PluginStateAction::STOP:
@@ -96,7 +96,7 @@ void MacroActionPluginState::LogAction()
}
}
bool MacroActionPluginState::Save(obs_data_t *obj)
bool MacroActionPluginState::Save(obs_data_t *obj) const
{
MacroAction::Save(obj);
obs_data_set_int(obj, "action", static_cast<int>(_action));

View File

@@ -15,10 +15,10 @@ class MacroActionPluginState : public MacroAction {
public:
MacroActionPluginState(Macro *m) : MacroAction(m) {}
bool PerformAction();
void LogAction();
bool Save(obs_data_t *obj);
void LogAction() const;
bool Save(obs_data_t *obj) const;
bool Load(obs_data_t *obj);
std::string GetId() { return id; };
std::string GetId() const { return id; };
static std::shared_ptr<MacroAction> Create(Macro *m)
{
return std::make_shared<MacroActionPluginState>(m);

View File

@@ -15,12 +15,12 @@ bool MacroActionProfile::PerformAction()
return true;
}
void MacroActionProfile::LogAction()
void MacroActionProfile::LogAction() const
{
vblog(LOG_INFO, "set profile type to \"%s\"", _profile.c_str());
}
bool MacroActionProfile::Save(obs_data_t *obj)
bool MacroActionProfile::Save(obs_data_t *obj) const
{
MacroAction::Save(obj);
obs_data_set_string(obj, "profile", _profile.c_str());
@@ -34,7 +34,7 @@ bool MacroActionProfile::Load(obs_data_t *obj)
return true;
}
std::string MacroActionProfile::GetShortDesc()
std::string MacroActionProfile::GetShortDesc() const
{
return _profile;
}

View File

@@ -7,11 +7,11 @@ class MacroActionProfile : public MacroAction {
public:
MacroActionProfile(Macro *m) : MacroAction(m) {}
bool PerformAction();
void LogAction();
bool Save(obs_data_t *obj);
void LogAction() const;
bool Save(obs_data_t *obj) const;
bool Load(obs_data_t *obj);
std::string GetShortDesc();
std::string GetId() { return id; };
std::string GetShortDesc() const;
std::string GetId() const { return id; };
static std::shared_ptr<MacroAction> Create(Macro *m)
{
return std::make_shared<MacroActionProfile>(m);

View File

@@ -11,8 +11,8 @@ bool MacroActionRandom::_registered = MacroActionFactory::Register(
{MacroActionRandom::Create, MacroActionRandomEdit::Create,
"AdvSceneSwitcher.action.random"});
std::vector<MacroRef> getNextMacro(std::vector<MacroRef> &macros,
MacroRef &lastRandomMacro)
std::vector<MacroRef> getNextMacros(std::vector<MacroRef> &macros,
MacroRef &lastRandomMacro, bool allowRepeat)
{
std::vector<MacroRef> res;
if (macros.size() == 1) {
@@ -25,7 +25,7 @@ std::vector<MacroRef> getNextMacro(std::vector<MacroRef> &macros,
for (auto &m : macros) {
if (m.get() && !m->Paused() &&
!(lastRandomMacro.get() == m.get())) {
(allowRepeat || (lastRandomMacro.get() != m.get()))) {
res.push_back(m);
}
}
@@ -38,7 +38,7 @@ bool MacroActionRandom::PerformAction()
return true;
}
auto macros = getNextMacro(_macros, lastRandomMacro);
auto macros = getNextMacros(_macros, lastRandomMacro, _allowRepeat);
if (macros.size() == 0) {
return true;
}
@@ -52,15 +52,16 @@ bool MacroActionRandom::PerformAction()
return macros[idx]->PerformActions();
}
void MacroActionRandom::LogAction()
void MacroActionRandom::LogAction() const
{
vblog(LOG_INFO, "running random macro");
}
bool MacroActionRandom::Save(obs_data_t *obj)
bool MacroActionRandom::Save(obs_data_t *obj) const
{
MacroAction::Save(obj);
SaveMacroList(obj, _macros);
obs_data_set_bool(obj, "allowRepeat", _allowRepeat);
return true;
}
@@ -68,12 +69,16 @@ bool MacroActionRandom::Load(obs_data_t *obj)
{
MacroAction::Load(obj);
LoadMacroList(obj, _macros);
_allowRepeat = obs_data_get_bool(obj, "allowRepeat");
return true;
}
MacroActionRandomEdit::MacroActionRandomEdit(
QWidget *parent, std::shared_ptr<MacroActionRandom> entryData)
: QWidget(parent), _list(new MacroList(this, false, false))
: QWidget(parent),
_list(new MacroList(this, true, false)),
_allowRepeat(new QCheckBox(obs_module_text(
"AdvSceneSwitcher.action.random.allowRepeat")))
{
QWidget::connect(_list, SIGNAL(Added(const std::string &)), this,
SLOT(Add(const std::string &)));
@@ -82,6 +87,8 @@ MacroActionRandomEdit::MacroActionRandomEdit(
this, SLOT(Replace(int, const std::string &)));
QWidget::connect(window(), SIGNAL(MacroRemoved(const QString &)), this,
SLOT(MacroRemove(const QString &)));
QWidget::connect(_allowRepeat, SIGNAL(stateChanged(int)), this,
SLOT(AllowRepeatChanged(int)));
auto *entryLayout = new QHBoxLayout;
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {};
@@ -91,6 +98,7 @@ MacroActionRandomEdit::MacroActionRandomEdit(
auto *mainLayout = new QVBoxLayout;
mainLayout->addLayout(entryLayout);
mainLayout->addWidget(_list);
mainLayout->addWidget(_allowRepeat);
setLayout(mainLayout);
_entryData = entryData;
@@ -105,6 +113,8 @@ void MacroActionRandomEdit::UpdateEntryData()
}
_list->SetContent(_entryData->_macros);
_allowRepeat->setChecked(_entryData->_allowRepeat);
_allowRepeat->setVisible(ShouldShowAllowRepeat());
adjustSize();
}
@@ -135,6 +145,7 @@ void MacroActionRandomEdit::Add(const std::string &name)
std::lock_guard<std::mutex> lock(switcher->m);
MacroRef macro(name);
_entryData->_macros.push_back(macro);
_allowRepeat->setVisible(ShouldShowAllowRepeat());
adjustSize();
}
@@ -146,6 +157,7 @@ void MacroActionRandomEdit::Remove(int idx)
std::lock_guard<std::mutex> lock(switcher->m);
_entryData->_macros.erase(std::next(_entryData->_macros.begin(), idx));
_allowRepeat->setVisible(ShouldShowAllowRepeat());
adjustSize();
}
@@ -160,3 +172,27 @@ void MacroActionRandomEdit::Replace(int idx, const std::string &name)
_entryData->_macros[idx] = macro;
adjustSize();
}
void MacroActionRandomEdit::AllowRepeatChanged(int value)
{
if (_loading || !_entryData) {
return;
}
std::lock_guard<std::mutex> lock(switcher->m);
_entryData->_allowRepeat = value;
}
bool MacroActionRandomEdit::ShouldShowAllowRepeat()
{
if (_entryData->_macros.size() <= 1) {
return false;
}
const auto macro = _entryData->_macros[0];
for (const auto &m : _entryData->_macros) {
if (macro.get() != m.get()) {
return true;
}
}
return false;
}

View File

@@ -4,23 +4,22 @@
#include <QPushButton>
#include <QListWidget>
#include <unordered_map>
#include <QCheckBox>
class MacroActionRandom : public MultiMacroRefAction {
public:
MacroActionRandom(Macro *m) : MacroAction(m), MultiMacroRefAction(m) {}
bool PerformAction();
void LogAction();
bool Save(obs_data_t *obj);
void LogAction() const;
bool Save(obs_data_t *obj) const;
bool Load(obs_data_t *obj);
std::string GetId() { return id; };
std::string GetId() const { return id; };
static std::shared_ptr<MacroAction> Create(Macro *m)
{
return std::make_shared<MacroActionRandom>(m);
}
// TODO: add weights to each macro ...
// std::unordered_map<MacroRef, int> _weights;
bool _allowRepeat = false;
private:
MacroRef lastRandomMacro;
@@ -49,11 +48,15 @@ private slots:
void Add(const std::string &);
void Remove(int);
void Replace(int, const std::string &);
void AllowRepeatChanged(int);
protected:
std::shared_ptr<MacroActionRandom> _entryData;
private:
bool ShouldShowAllowRepeat();
MacroList *_list;
QCheckBox *_allowRepeat;
bool _loading = true;
};

View File

@@ -54,7 +54,7 @@ bool MacroActionRecord::PerformAction()
return true;
}
void MacroActionRecord::LogAction()
void MacroActionRecord::LogAction() const
{
auto it = actionTypes.find(_action);
if (it != actionTypes.end()) {
@@ -65,7 +65,7 @@ void MacroActionRecord::LogAction()
}
}
bool MacroActionRecord::Save(obs_data_t *obj)
bool MacroActionRecord::Save(obs_data_t *obj) const
{
MacroAction::Save(obj);
obs_data_set_int(obj, "action", static_cast<int>(_action));

View File

@@ -16,10 +16,10 @@ class MacroActionRecord : public MacroAction {
public:
MacroActionRecord(Macro *m) : MacroAction(m) {}
bool PerformAction();
void LogAction();
bool Save(obs_data_t *obj);
void LogAction() const;
bool Save(obs_data_t *obj) const;
bool Load(obs_data_t *obj);
std::string GetId() { return id; };
std::string GetId() const { return id; };
static std::shared_ptr<MacroAction> Create(Macro *m)
{
return std::make_shared<MacroActionRecord>(m);

View File

@@ -40,7 +40,7 @@ bool MacroActionReplayBuffer::PerformAction()
return true;
}
void MacroActionReplayBuffer::LogAction()
void MacroActionReplayBuffer::LogAction() const
{
auto it = actionTypes.find(_action);
if (it != actionTypes.end()) {
@@ -51,7 +51,7 @@ void MacroActionReplayBuffer::LogAction()
}
}
bool MacroActionReplayBuffer::Save(obs_data_t *obj)
bool MacroActionReplayBuffer::Save(obs_data_t *obj) const
{
MacroAction::Save(obj);
obs_data_set_int(obj, "action", static_cast<int>(_action));

View File

@@ -14,10 +14,10 @@ class MacroActionReplayBuffer : public MacroAction {
public:
MacroActionReplayBuffer(Macro *m) : MacroAction(m) {}
bool PerformAction();
void LogAction();
bool Save(obs_data_t *obj);
void LogAction() const;
bool Save(obs_data_t *obj) const;
bool Load(obs_data_t *obj);
std::string GetId() { return id; };
std::string GetId() const { return id; };
static std::shared_ptr<MacroAction> Create(Macro *m)
{
return std::make_shared<MacroActionReplayBuffer>(m);

View File

@@ -15,7 +15,9 @@ bool MacroActionRun::_registered = MacroActionFactory::Register(
bool MacroActionRun::PerformAction()
{
if (!QProcess::startDetached(QString::fromStdString(_path), _args) &&
if (!QProcess::startDetached(
QString::fromStdString(_path), _args,
QString::fromStdString(_workingDirectory)) &&
_args.empty()) {
vblog(LOG_INFO, "run \"%s\" using QDesktopServices",
_path.c_str());
@@ -25,15 +27,16 @@ bool MacroActionRun::PerformAction()
return true;
}
void MacroActionRun::LogAction()
void MacroActionRun::LogAction() const
{
vblog(LOG_INFO, "run \"%s\"", _path.c_str());
}
bool MacroActionRun::Save(obs_data_t *obj)
bool MacroActionRun::Save(obs_data_t *obj) const
{
MacroAction::Save(obj);
obs_data_set_string(obj, "path", _path.c_str());
obs_data_set_string(obj, "workingDirectory", _workingDirectory.c_str());
obs_data_array_t *args = obs_data_array_create();
for (auto &arg : _args) {
obs_data_t *array_obj = obs_data_create();
@@ -51,6 +54,7 @@ bool MacroActionRun::Load(obs_data_t *obj)
{
MacroAction::Load(obj);
_path = obs_data_get_string(obj, "path");
_workingDirectory = obs_data_get_string(obj, "workingDirectory");
obs_data_array_t *args = obs_data_get_array(obj, "args");
size_t count = obs_data_array_count(args);
for (size_t i = 0; i < count; i++) {
@@ -63,33 +67,34 @@ bool MacroActionRun::Load(obs_data_t *obj)
return true;
}
std::string MacroActionRun::GetShortDesc()
std::string MacroActionRun::GetShortDesc() const
{
return _path;
}
MacroActionRunEdit::MacroActionRunEdit(
QWidget *parent, std::shared_ptr<MacroActionRun> entryData)
: QWidget(parent)
: QWidget(parent),
_filePath(new FileSelection()),
_argList(new QListWidget()),
_addArg(new QPushButton()),
_removeArg(new QPushButton()),
_argUp(new QPushButton()),
_argDown(new QPushButton()),
_workingDirectory(new FileSelection(FileSelection::Type::FOLDER))
{
_filePath = new FileSelection();
_argList = new QListWidget();
_addArg = new QPushButton();
_addArg->setMaximumWidth(22);
_addArg->setProperty("themeID",
QVariant(QString::fromUtf8("addIconSmall")));
_addArg->setFlat(true);
_removeArg = new QPushButton();
_removeArg->setMaximumWidth(22);
_removeArg->setProperty("themeID",
QVariant(QString::fromUtf8("removeIconSmall")));
_removeArg->setFlat(true);
_argUp = new QPushButton();
_argUp->setMaximumWidth(22);
_argUp->setProperty("themeID",
QVariant(QString::fromUtf8("upArrowIconSmall")));
_argUp->setFlat(true);
_argDown = new QPushButton();
_argDown->setMaximumWidth(22);
_argDown->setProperty(
"themeID", QVariant(QString::fromUtf8("downArrowIconSmall")));
@@ -102,17 +107,21 @@ MacroActionRunEdit::MacroActionRunEdit(
SLOT(RemoveArg()));
QWidget::connect(_argUp, SIGNAL(clicked()), this, SLOT(ArgUp()));
QWidget::connect(_argDown, SIGNAL(clicked()), this, SLOT(ArgDown()));
connect(_argList, SIGNAL(itemDoubleClicked(QListWidgetItem *)), this,
SLOT(ArgItemClicked(QListWidgetItem *)));
QWidget::connect(_argList, SIGNAL(itemDoubleClicked(QListWidgetItem *)),
this, SLOT(ArgItemClicked(QListWidgetItem *)));
QWidget::connect(_workingDirectory,
SIGNAL(PathChanged(const QString &)), this,
SLOT(WorkingDirectoryChanged(const QString &)));
auto *entryLayout = new QHBoxLayout;
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
{"{{filePath}}", _filePath},
{"{{workingDirectory}}", _workingDirectory},
};
placeWidgets(obs_module_text("AdvSceneSwitcher.action.run.entry"),
entryLayout, widgetPlaceholders);
entryLayout, widgetPlaceholders, false);
auto *argButtonLayout = new QHBoxLayout;
auto argButtonLayout = new QHBoxLayout;
argButtonLayout->addWidget(_addArg);
argButtonLayout->addWidget(_removeArg);
QFrame *line = new QFrame();
@@ -123,12 +132,19 @@ MacroActionRunEdit::MacroActionRunEdit(
argButtonLayout->addWidget(_argDown);
argButtonLayout->addStretch();
auto workingDirectoryLayout = new QHBoxLayout;
placeWidgets(
obs_module_text(
"AdvSceneSwitcher.action.run.entry.workingDirectory"),
workingDirectoryLayout, widgetPlaceholders, false);
auto *mainLayout = new QVBoxLayout;
mainLayout->addLayout(entryLayout);
mainLayout->addWidget(new QLabel(
obs_module_text("AdvSceneSwitcher.action.run.arguments")));
mainLayout->addWidget(_argList);
mainLayout->addLayout(argButtonLayout);
mainLayout->addLayout(workingDirectoryLayout);
setLayout(mainLayout);
_entryData = entryData;
@@ -146,6 +162,8 @@ void MacroActionRunEdit::UpdateEntryData()
QListWidgetItem *item = new QListWidgetItem(arg, _argList);
item->setData(Qt::UserRole, arg);
}
_workingDirectory->SetPath(
QString::fromStdString(_entryData->_workingDirectory));
SetArgListSize();
}
@@ -264,6 +282,15 @@ void MacroActionRunEdit::ArgItemClicked(QListWidgetItem *item)
_entryData->_args[idx] = arg;
}
void MacroActionRunEdit::WorkingDirectoryChanged(const QString &path)
{
if (_loading || !_entryData) {
return;
}
_entryData->_workingDirectory = path.toStdString();
}
void MacroActionRunEdit::SetArgListSize()
{
setHeightToContentHeight(_argList);

View File

@@ -11,17 +11,18 @@ class MacroActionRun : public MacroAction {
public:
MacroActionRun(Macro *m) : MacroAction(m) {}
bool PerformAction();
void LogAction();
bool Save(obs_data_t *obj);
void LogAction() const;
bool Save(obs_data_t *obj) const;
bool Load(obs_data_t *obj);
std::string GetShortDesc();
std::string GetId() { return id; };
std::string GetShortDesc() const;
std::string GetId() const { return id; };
static std::shared_ptr<MacroAction> Create(Macro *m)
{
return std::make_shared<MacroActionRun>(m);
}
std::string _path = obs_module_text("AdvSceneSwitcher.enterPath");
std::string _workingDirectory = "";
QStringList _args;
private:
@@ -51,6 +52,7 @@ private slots:
void ArgUp();
void ArgDown();
void ArgItemClicked(QListWidgetItem *);
void WorkingDirectoryChanged(const QString &text);
signals:
void HeaderInfoChanged(const QString &);
@@ -66,5 +68,6 @@ private:
QPushButton *_removeArg;
QPushButton *_argUp;
QPushButton *_argDown;
FileSelection *_workingDirectory;
bool _loading = true;
};

View File

@@ -24,13 +24,13 @@ bool MacroActionSceneCollection::PerformAction()
return false;
}
void MacroActionSceneCollection::LogAction()
void MacroActionSceneCollection::LogAction() const
{
vblog(LOG_INFO, "set scene collection type to \"%s\"",
_sceneCollection.c_str());
}
bool MacroActionSceneCollection::Save(obs_data_t *obj)
bool MacroActionSceneCollection::Save(obs_data_t *obj) const
{
MacroAction::Save(obj);
obs_data_set_string(obj, "sceneCollection", _sceneCollection.c_str());
@@ -44,7 +44,7 @@ bool MacroActionSceneCollection::Load(obs_data_t *obj)
return true;
}
std::string MacroActionSceneCollection::GetShortDesc()
std::string MacroActionSceneCollection::GetShortDesc() const
{
return _sceneCollection;
}

View File

@@ -7,11 +7,11 @@ class MacroActionSceneCollection : public MacroAction {
public:
MacroActionSceneCollection(Macro *m) : MacroAction(m) {}
bool PerformAction();
void LogAction();
bool Save(obs_data_t *obj);
void LogAction() const;
bool Save(obs_data_t *obj) const;
bool Load(obs_data_t *obj);
std::string GetShortDesc();
std::string GetId() { return id; };
std::string GetShortDesc() const;
std::string GetId() const { return id; };
static std::shared_ptr<MacroAction> Create(Macro *m)
{
return std::make_shared<MacroActionSceneCollection>(m);

View File

@@ -96,21 +96,21 @@ bool MacroActionSceneOrder::PerformAction()
return true;
}
void MacroActionSceneOrder::LogAction()
void MacroActionSceneOrder::LogAction() const
{
auto it = actionTypes.find(_action);
if (it != actionTypes.end()) {
vblog(LOG_INFO,
"performed order action \"%s\" for source \"%s\" on scene \"%s\"",
it->second.c_str(), _source.ToString().c_str(),
_scene.ToString().c_str());
it->second.c_str(), _source.ToString(true).c_str(),
_scene.ToString(true).c_str());
} else {
blog(LOG_WARNING, "ignored unknown scene order action %d",
static_cast<int>(_action));
}
}
bool MacroActionSceneOrder::Save(obs_data_t *obj)
bool MacroActionSceneOrder::Save(obs_data_t *obj) const
{
MacroAction::Save(obj);
_scene.Save(obj);
@@ -138,7 +138,7 @@ bool MacroActionSceneOrder::Load(obs_data_t *obj)
return true;
}
std::string MacroActionSceneOrder::GetShortDesc()
std::string MacroActionSceneOrder::GetShortDesc() const
{
if (_source.ToString().empty()) {
return "";

View File

@@ -17,11 +17,11 @@ class MacroActionSceneOrder : public MacroAction {
public:
MacroActionSceneOrder(Macro *m) : MacroAction(m) {}
bool PerformAction();
void LogAction();
bool Save(obs_data_t *obj);
void LogAction() const;
bool Save(obs_data_t *obj) const;
bool Load(obs_data_t *obj);
std::string GetShortDesc();
std::string GetId() { return id; };
std::string GetShortDesc() const;
std::string GetId() const { return id; };
static std::shared_ptr<MacroAction> Create(Macro *m)
{
return std::make_shared<MacroActionSceneOrder>(m);

View File

@@ -118,18 +118,18 @@ bool MacroActionSwitchScene::PerformAction()
return true;
}
void MacroActionSwitchScene::LogAction()
void MacroActionSwitchScene::LogAction() const
{
auto t = _scene.GetType();
auto sceneName = GetWeakSourceName(_scene.GetScene(false));
switch (t) {
case SceneSelection::Type::SCENE:
vblog(LOG_INFO, "switch to scene '%s'",
_scene.ToString().c_str());
_scene.ToString(true).c_str());
break;
case SceneSelection::Type::GROUP:
vblog(LOG_INFO, "switch to scene '%s' (scene group '%s')",
sceneName.c_str(), _scene.ToString().c_str());
sceneName.c_str(), _scene.ToString(true).c_str());
break;
case SceneSelection::Type::PREVIOUS:
vblog(LOG_INFO, "switch to previous scene '%s'",
@@ -140,7 +140,7 @@ void MacroActionSwitchScene::LogAction()
}
}
bool MacroActionSwitchScene::Save(obs_data_t *obj)
bool MacroActionSwitchScene::Save(obs_data_t *obj) const
{
MacroAction::Save(obj);
_scene.Save(obj);
@@ -162,7 +162,7 @@ bool MacroActionSwitchScene::Load(obs_data_t *obj)
return true;
}
std::string MacroActionSwitchScene::GetShortDesc()
std::string MacroActionSwitchScene::GetShortDesc() const
{
return _scene.ToString();
}

View File

@@ -10,11 +10,11 @@ class MacroActionSwitchScene : public MacroAction {
public:
MacroActionSwitchScene(Macro *m) : MacroAction(m) {}
bool PerformAction();
void LogAction();
bool Save(obs_data_t *obj);
void LogAction() const;
bool Save(obs_data_t *obj) const;
bool Load(obs_data_t *obj);
std::string GetShortDesc();
std::string GetId() { return id; };
std::string GetShortDesc() const;
std::string GetId() const { return id; };
static std::shared_ptr<MacroAction> Create(Macro *m)
{

View File

@@ -12,6 +12,8 @@ bool MacroActionSceneTransform::_registered = MacroActionFactory::Register(
bool MacroActionSceneTransform::PerformAction()
{
ApplySettings(_settings); // Resolves variables
auto items = _source.GetSceneItems(_scene);
for (auto &item : items) {
obs_sceneitem_defer_update_begin(item);
@@ -24,19 +26,19 @@ bool MacroActionSceneTransform::PerformAction()
return true;
}
void MacroActionSceneTransform::LogAction()
void MacroActionSceneTransform::LogAction() const
{
vblog(LOG_INFO,
"performed transform action for source \"%s\" on scene \"%s\"",
_source.ToString().c_str(), _scene.ToString().c_str());
_source.ToString(true).c_str(), _scene.ToString(true).c_str());
}
bool MacroActionSceneTransform::Save(obs_data_t *obj)
bool MacroActionSceneTransform::Save(obs_data_t *obj) const
{
MacroAction::Save(obj);
_scene.Save(obj);
_source.Save(obj);
saveTransformState(obj, _info, _crop);
_settings.Save(obj, "settings");
return true;
}
@@ -52,11 +54,18 @@ bool MacroActionSceneTransform::Load(obs_data_t *obj)
MacroAction::Load(obj);
_scene.Load(obj);
_source.Load(obj);
loadTransformState(obj, _info, _crop);
_settings.Load(obj, "settings");
// Convert old data format
// TODO: Remove in future version
if (!obs_data_has_user_value(obj, "settings")) {
loadTransformState(obj, _info, _crop);
_settings = ConvertSettings();
}
return true;
}
std::string MacroActionSceneTransform::GetShortDesc()
std::string MacroActionSceneTransform::GetShortDesc() const
{
if (_source.ToString().empty()) {
return "";
@@ -64,7 +73,7 @@ std::string MacroActionSceneTransform::GetShortDesc()
return _scene.ToString() + " - " + _source.ToString();
}
std::string MacroActionSceneTransform::GetSettings()
std::string MacroActionSceneTransform::ConvertSettings()
{
auto data = obs_data_create();
saveTransformState(data, _info, _crop);
@@ -73,7 +82,7 @@ std::string MacroActionSceneTransform::GetSettings()
return json;
}
void MacroActionSceneTransform::SetSettings(std::string &settings)
void MacroActionSceneTransform::ApplySettings(const std::string &settings)
{
auto data = obs_data_create_from_json(settings.c_str());
if (!data) {
@@ -111,7 +120,7 @@ MacroActionSceneTransformEdit::MacroActionSceneTransformEdit(
_sources = new SceneItemSelectionWidget(parent);
_getSettings = new QPushButton(obs_module_text(
"AdvSceneSwitcher.action.sceneTransform.getTransform"));
_settings = new ResizingPlainTextEdit(this);
_settings = new VariableTextEdit(this);
QWidget::connect(_scenes, SIGNAL(SceneChanged(const SceneSelection &)),
this, SLOT(SceneChanged(const SceneSelection &)));
@@ -160,7 +169,7 @@ void MacroActionSceneTransformEdit::UpdateEntryData()
_scenes->SetScene(_entryData->_scene);
_sources->SetSceneItem(_entryData->_source);
_settings->setPlainText(formatJsonString(_entryData->GetSettings()));
_settings->setPlainText(_entryData->_settings);
adjustSize();
updateGeometry();
@@ -213,8 +222,7 @@ void MacroActionSceneTransformEdit::SettingsChanged()
}
std::lock_guard<std::mutex> lock(switcher->m);
auto json = _settings->toPlainText().toStdString();
_entryData->SetSettings(json);
_entryData->_settings = _settings->toPlainText().toStdString();
adjustSize();
updateGeometry();

View File

@@ -2,7 +2,7 @@
#include "macro-action-edit.hpp"
#include "scene-selection.hpp"
#include "scene-item-selection.hpp"
#include "resizing-text-edit.hpp"
#include "variable-text-edit.hpp"
#include <QSpinBox>
@@ -10,24 +10,27 @@ class MacroActionSceneTransform : public MacroAction {
public:
MacroActionSceneTransform(Macro *m) : MacroAction(m) {}
bool PerformAction();
void LogAction();
bool Save(obs_data_t *obj);
void LogAction() const;
bool Save(obs_data_t *obj) const;
bool Load(obs_data_t *obj);
std::string GetShortDesc();
std::string GetId() { return id; };
std::string GetShortDesc() const;
std::string GetId() const { return id; };
static std::shared_ptr<MacroAction> Create(Macro *m)
{
return std::make_shared<MacroActionSceneTransform>(m);
}
std::string GetSettings();
void SetSettings(std::string &);
SceneSelection _scene;
SceneItemSelection _source;
VariableResolvingString _settings = "";
private:
void ApplySettings(const std::string &);
std::string ConvertSettings();
struct obs_transform_info _info = {};
struct obs_sceneitem_crop _crop = {};
private:
static bool _registered;
static const std::string id;
};
@@ -61,7 +64,7 @@ protected:
SceneSelectionWidget *_scenes;
SceneItemSelectionWidget *_sources;
QPushButton *_getSettings;
ResizingPlainTextEdit *_settings;
VariableTextEdit *_settings;
std::shared_ptr<MacroActionSceneTransform> _entryData;
private:

View File

@@ -15,6 +15,8 @@ const static std::map<SceneVisibilityAction, std::string> actionTypes = {
"AdvSceneSwitcher.action.sceneVisibility.type.show"},
{SceneVisibilityAction::HIDE,
"AdvSceneSwitcher.action.sceneVisibility.type.hide"},
{SceneVisibilityAction::TOGGLE,
"AdvSceneSwitcher.action.sceneVisibility.type.toggle"},
};
const static std::map<SceneItemSourceType, std::string> sourceItemSourceTypes = {
@@ -26,9 +28,25 @@ const static std::map<SceneItemSourceType, std::string> sourceItemSourceTypes =
struct VisibilityData {
std::string name;
bool visible;
SceneVisibilityAction action;
};
static void setSceneItemVisibility(obs_sceneitem_t *item,
SceneVisibilityAction action)
{
switch (action) {
case SceneVisibilityAction::SHOW:
obs_sceneitem_set_visible(item, true);
break;
case SceneVisibilityAction::HIDE:
obs_sceneitem_set_visible(item, false);
break;
case SceneVisibilityAction::TOGGLE:
obs_sceneitem_set_visible(item, !obs_sceneitem_visible(item));
break;
}
}
static bool visibilitySourceTypeEnum(obs_scene_t *, obs_sceneitem_t *item,
void *ptr)
{
@@ -36,8 +54,8 @@ static bool visibilitySourceTypeEnum(obs_scene_t *, obs_sceneitem_t *item,
auto sourceTypeName = obs_source_get_display_name(
obs_source_get_id(obs_sceneitem_get_source(item)));
if (vInfo->name == sourceTypeName) {
obs_sceneitem_set_visible(item, vInfo->visible);
if (sourceTypeName && vInfo->name == sourceTypeName) {
setSceneItemVisibility(item, vInfo->action);
}
if (obs_sceneitem_is_group(item)) {
@@ -54,8 +72,7 @@ bool MacroActionSceneVisibility::PerformAction()
case SceneItemSourceType::SOURCE: {
auto items = _source.GetSceneItems(_scene);
for (auto item : items) {
obs_sceneitem_set_visible(
item, _action == SceneVisibilityAction::SHOW);
setSceneItemVisibility(item, _action);
obs_sceneitem_release(item);
}
break;
@@ -63,8 +80,7 @@ bool MacroActionSceneVisibility::PerformAction()
case SceneItemSourceType::SOURCE_GROUP: {
auto s = obs_weak_source_get_source(_scene.GetScene());
auto scene = obs_scene_from_source(s);
VisibilityData vInfo = {_sourceGroup,
_action == SceneVisibilityAction::SHOW};
VisibilityData vInfo = {_sourceGroup, _action};
obs_scene_enum_items(scene, visibilitySourceTypeEnum, &vInfo);
obs_source_release(s);
break;
@@ -76,20 +92,21 @@ bool MacroActionSceneVisibility::PerformAction()
return true;
}
void MacroActionSceneVisibility::LogAction()
void MacroActionSceneVisibility::LogAction() const
{
auto it = actionTypes.find(_action);
if (it != actionTypes.end()) {
if (_sourceType == SceneItemSourceType::SOURCE) {
vblog(LOG_INFO,
"performed visibility action \"%s\" for source \"%s\" on scene \"%s\"",
it->second.c_str(), _source.ToString().c_str(),
_scene.ToString().c_str());
it->second.c_str(),
_source.ToString(true).c_str(),
_scene.ToString(true).c_str());
} else {
vblog(LOG_INFO,
"performed visibility action \"%s\" for any source type \"%s\" on scene \"%s\"",
it->second.c_str(), _sourceGroup.c_str(),
_scene.ToString().c_str());
_scene.ToString(true).c_str());
}
} else {
blog(LOG_WARNING, "ignored unknown SceneVisibility action %d",
@@ -97,7 +114,7 @@ void MacroActionSceneVisibility::LogAction()
}
}
bool MacroActionSceneVisibility::Save(obs_data_t *obj)
bool MacroActionSceneVisibility::Save(obs_data_t *obj) const
{
MacroAction::Save(obj);
_scene.Save(obj);
@@ -132,7 +149,7 @@ bool MacroActionSceneVisibility::Load(obs_data_t *obj)
return true;
}
std::string MacroActionSceneVisibility::GetShortDesc()
std::string MacroActionSceneVisibility::GetShortDesc() const
{
if (_sourceType == SceneItemSourceType::SOURCE &&
!_source.ToString().empty()) {

View File

@@ -8,6 +8,7 @@
enum class SceneVisibilityAction {
SHOW,
HIDE,
TOGGLE,
};
enum class SceneItemSourceType {
@@ -19,11 +20,11 @@ class MacroActionSceneVisibility : public MacroAction {
public:
MacroActionSceneVisibility(Macro *m) : MacroAction(m) {}
bool PerformAction();
void LogAction();
bool Save(obs_data_t *obj);
void LogAction() const;
bool Save(obs_data_t *obj) const;
bool Load(obs_data_t *obj);
std::string GetShortDesc();
std::string GetId() { return id; };
std::string GetShortDesc() const;
std::string GetId() const { return id; };
static std::shared_ptr<MacroAction> Create(Macro *m)
{
return std::make_shared<MacroActionSceneVisibility>(m);

View File

@@ -2,6 +2,8 @@
#include "advanced-scene-switcher.hpp"
#include "utility.hpp"
const uint32_t MacroActionScreenshot::_version = 1;
const std::string MacroActionScreenshot::id = "screenshot";
bool MacroActionScreenshot::_registered = MacroActionFactory::Register(
@@ -38,7 +40,7 @@ bool MacroActionScreenshot::PerformAction()
OBSWeakSource source = nullptr;
switch (_targetType) {
case MacroActionScreenshot::TargetType::SOURCE:
source = _source;
source = _source.GetSource();
break;
case MacroActionScreenshot::TargetType::SCENE:
source = _scene.GetScene(false);
@@ -59,22 +61,33 @@ bool MacroActionScreenshot::PerformAction()
return true;
}
void MacroActionScreenshot::LogAction()
void MacroActionScreenshot::LogAction() const
{
vblog(LOG_INFO, "trigger screenshot for \"%s\"",
_targetType == TargetType::SOURCE
? GetWeakSourceName(_source).c_str()
: _scene.ToString().c_str());
switch (_targetType) {
case MacroActionScreenshot::TargetType::SOURCE:
vblog(LOG_INFO, "trigger screenshot of \"%s\"",
_source.ToString(true).c_str());
break;
case MacroActionScreenshot::TargetType::SCENE:
vblog(LOG_INFO, "trigger screenshot of \"%s\"",
_scene.ToString(true).c_str());
break;
case MacroActionScreenshot::TargetType::MAIN_OUTPUT:
vblog(LOG_INFO, "trigger screenshot of main output",
_scene.ToString(true).c_str());
break;
}
}
bool MacroActionScreenshot::Save(obs_data_t *obj)
bool MacroActionScreenshot::Save(obs_data_t *obj) const
{
MacroAction::Save(obj);
_scene.Save(obj);
obs_data_set_string(obj, "source", GetWeakSourceName(_source).c_str());
_source.Save(obj);
obs_data_set_int(obj, "saveType", static_cast<int>(_saveType));
obs_data_set_int(obj, "targetType", static_cast<int>(_targetType));
obs_data_set_string(obj, "savePath", _path.c_str());
obs_data_set_int(obj, "version", _version);
return true;
}
@@ -82,21 +95,27 @@ bool MacroActionScreenshot::Load(obs_data_t *obj)
{
MacroAction::Load(obj);
_scene.Load(obj);
const char *sourceName = obs_data_get_string(obj, "source");
_source = GetWeakSourceByName(sourceName);
_source.Load(obj);
_saveType = static_cast<SaveType>(obs_data_get_int(obj, "saveType"));
_targetType =
static_cast<TargetType>(obs_data_get_int(obj, "targetType"));
_path = obs_data_get_string(obj, "savePath");
// TODO: Remove fallback for older versions
if (!obs_data_has_user_value(obj, "version")) {
if (!_source.GetSource() && !_scene.GetScene(false)) {
_targetType = TargetType::MAIN_OUTPUT;
}
}
return true;
}
std::string MacroActionScreenshot::GetShortDesc()
std::string MacroActionScreenshot::GetShortDesc() const
{
if (_targetType == TargetType::SCENE) {
return _scene.ToString();
} else {
return GetWeakSourceName(_source);
return _source.ToString();
}
return "";
}
@@ -115,6 +134,7 @@ static void populateTargetTypeSelection(QComboBox *list)
"AdvSceneSwitcher.action.screenshot.type.source"));
list->addItem(obs_module_text(
"AdvSceneSwitcher.action.screenshot.type.scene"));
list->addItem(obs_module_text("AdvSceneSwitcher.OBSVideoOutput"));
}
MacroActionScreenshotEdit::MacroActionScreenshotEdit(
@@ -122,21 +142,25 @@ MacroActionScreenshotEdit::MacroActionScreenshotEdit(
: QWidget(parent),
_scenes(new SceneSelectionWidget(this, true, false, true, true,
true)),
_sources(new QComboBox()),
_sources(new SourceSelectionWidget(this, QStringList(), true)),
_saveType(new QComboBox()),
_targetType(new QComboBox()),
_savePath(new FileSelection(FileSelection::Type::WRITE, this))
{
setToolTip(obs_module_text(
"AdvSceneSwitcher.action.screenshot.blackscreenNote"));
populateVideoSelection(_sources, true);
auto sources = GetVideoSourceNames();
sources.sort();
_sources->SetSourceNameList(sources);
populateSaveTypeSelection(_saveType);
populateTargetTypeSelection(_targetType);
QWidget::connect(_scenes, SIGNAL(SceneChanged(const SceneSelection &)),
this, SLOT(SceneChanged(const SceneSelection &)));
QWidget::connect(_sources, SIGNAL(currentTextChanged(const QString &)),
this, SLOT(SourceChanged(const QString &)));
QWidget::connect(_sources,
SIGNAL(SourceChanged(const SourceSelection &)), this,
SLOT(SourceChanged(const SourceSelection &)));
QWidget::connect(_saveType, SIGNAL(currentIndexChanged(int)), this,
SLOT(SaveTypeChanged(int)));
QWidget::connect(_targetType, SIGNAL(currentIndexChanged(int)), this,
@@ -171,12 +195,7 @@ void MacroActionScreenshotEdit::UpdateEntryData()
return;
}
if (_entryData->_source) {
_sources->setCurrentText(
GetWeakSourceName(_entryData->_source).c_str());
} else {
_sources->setCurrentIndex(1);
}
_sources->SetSource(_entryData->_source);
_scenes->SetScene(_entryData->_scene);
_saveType->setCurrentIndex(static_cast<int>(_entryData->_saveType));
_targetType->setCurrentIndex(static_cast<int>(_entryData->_targetType));
@@ -226,14 +245,14 @@ void MacroActionScreenshotEdit::PathChanged(const QString &text)
_entryData->_path = text.toUtf8().constData();
}
void MacroActionScreenshotEdit::SourceChanged(const QString &text)
void MacroActionScreenshotEdit::SourceChanged(const SourceSelection &source)
{
if (_loading || !_entryData) {
return;
}
std::lock_guard<std::mutex> lock(switcher->m);
_entryData->_source = GetWeakSourceByQString(text);
_entryData->_source = source;
emit HeaderInfoChanged(
QString::fromStdString(_entryData->GetShortDesc()));
}

View File

@@ -3,6 +3,7 @@
#include "file-selection.hpp"
#include "scene-selection.hpp"
#include "screenshot-helper.hpp"
#include "source-selection.hpp"
#include <QComboBox>
@@ -10,11 +11,11 @@ class MacroActionScreenshot : public MacroAction {
public:
MacroActionScreenshot(Macro *m) : MacroAction(m) {}
bool PerformAction();
void LogAction();
bool Save(obs_data_t *obj);
void LogAction() const;
bool Save(obs_data_t *obj) const;
bool Load(obs_data_t *obj);
std::string GetShortDesc();
std::string GetId() { return id; };
std::string GetShortDesc() const;
std::string GetId() const { return id; };
static std::shared_ptr<MacroAction> Create(Macro *m)
{
return std::make_shared<MacroActionScreenshot>(m);
@@ -27,10 +28,11 @@ public:
enum class TargetType {
SOURCE,
SCENE,
MAIN_OUTPUT,
};
TargetType _targetType = TargetType::SOURCE;
SceneSelection _scene;
OBSWeakSource _source;
SourceSelection _source;
std::string _path = obs_module_text("AdvSceneSwitcher.enterPath");
private:
@@ -38,6 +40,8 @@ private:
void CustomScreenshot(OBSWeakSource &);
ScreenshotHelper _screenshot;
const static uint32_t _version;
static bool _registered;
static const std::string id;
};
@@ -60,7 +64,7 @@ public:
}
private slots:
void SceneChanged(const SceneSelection &);
void SourceChanged(const QString &text);
void SourceChanged(const SourceSelection &);
void SaveTypeChanged(int index);
void TargetTypeChanged(int index);
void PathChanged(const QString &text);
@@ -69,7 +73,7 @@ signals:
protected:
SceneSelectionWidget *_scenes;
QComboBox *_sources;
SourceSelectionWidget *_sources;
QComboBox *_saveType;
QComboBox *_targetType;
FileSelection *_savePath;

View File

@@ -66,12 +66,12 @@ bool MacroActionSequence::PerformAction()
return macro->PerformActions();
}
void MacroActionSequence::LogAction()
void MacroActionSequence::LogAction() const
{
vblog(LOG_INFO, "running macro sequence");
}
bool MacroActionSequence::Save(obs_data_t *obj)
bool MacroActionSequence::Save(obs_data_t *obj) const
{
MacroAction::Save(obj);
SaveMacroList(obj, _macros);

View File

@@ -13,10 +13,10 @@ public:
{
}
bool PerformAction();
void LogAction();
bool Save(obs_data_t *obj);
void LogAction() const;
bool Save(obs_data_t *obj) const;
bool Load(obs_data_t *obj);
std::string GetId() { return id; };
std::string GetId() const { return id; };
MacroRef GetNextMacro(bool advance = true);
static std::shared_ptr<MacroAction> Create(Macro *m)
{

View File

@@ -2,6 +2,8 @@
#include "advanced-scene-switcher.hpp"
#include "utility.hpp"
Q_DECLARE_METATYPE(SourceSettingButton);
const std::string MacroActionSource::id = "source";
bool MacroActionSource::_registered = MacroActionFactory::Register(
@@ -16,9 +18,42 @@ const static std::map<SourceAction, std::string> actionTypes = {
"AdvSceneSwitcher.action.source.type.settings"},
{SourceAction::REFRESH_SETTINGS,
"AdvSceneSwitcher.action.source.type.refreshSettings"},
{SourceAction::SETTINGS_BUTTON,
"AdvSceneSwitcher.action.source.type.pressSettingsButton"},
};
void refreshSourceSettings(obs_source_t *s)
static std::vector<SourceSettingButton> getSourceButtons(OBSWeakSource source)
{
auto s = obs_weak_source_get_source(source);
std::vector<SourceSettingButton> buttons;
obs_properties_t *sourceProperties = obs_source_properties(s);
auto it = obs_properties_first(sourceProperties);
do {
if (!it || obs_property_get_type(it) != OBS_PROPERTY_BUTTON) {
continue;
}
SourceSettingButton button = {obs_property_name(it),
obs_property_description(it)};
buttons.emplace_back(button);
} while (obs_property_next(&it));
obs_source_release(s);
return buttons;
}
static void pressSourceButton(const SourceSettingButton &button,
obs_source_t *source)
{
obs_properties_t *sourceProperties = obs_source_properties(source);
obs_property_t *property =
obs_properties_get(sourceProperties, button.id.c_str());
if (!obs_property_button_clicked(property, source)) {
blog(LOG_WARNING, "Failed to press settings button '%s' for %s",
button.id.c_str(), obs_source_get_name(source));
}
obs_properties_destroy(sourceProperties);
}
static void refreshSourceSettings(obs_source_t *s)
{
if (!s) {
return;
@@ -41,7 +76,7 @@ void refreshSourceSettings(obs_source_t *s)
bool MacroActionSource::PerformAction()
{
auto s = obs_weak_source_get_source(_source);
auto s = obs_weak_source_get_source(_source.GetSource());
switch (_action) {
case SourceAction::ENABLE:
obs_source_set_enabled(s, true);
@@ -55,6 +90,9 @@ bool MacroActionSource::PerformAction()
case SourceAction::REFRESH_SETTINGS:
refreshSourceSettings(s);
break;
case SourceAction::SETTINGS_BUTTON:
pressSourceButton(_button, s);
break;
default:
break;
}
@@ -62,42 +100,41 @@ bool MacroActionSource::PerformAction()
return true;
}
void MacroActionSource::LogAction()
void MacroActionSource::LogAction() const
{
auto it = actionTypes.find(_action);
if (it != actionTypes.end()) {
vblog(LOG_INFO, "performed action \"%s\" for Source \"%s\"",
it->second.c_str(), GetWeakSourceName(_source).c_str());
it->second.c_str(), _source.ToString(true).c_str());
} else {
blog(LOG_WARNING, "ignored unknown source action %d",
static_cast<int>(_action));
}
}
bool MacroActionSource::Save(obs_data_t *obj)
bool MacroActionSource::Save(obs_data_t *obj) const
{
MacroAction::Save(obj);
obs_data_set_string(obj, "source", GetWeakSourceName(_source).c_str());
_source.Save(obj);
obs_data_set_int(obj, "action", static_cast<int>(_action));
obs_data_set_string(obj, "settings", _settings.c_str());
_button.Save(obj);
_settings.Save(obj, "settings");
return true;
}
bool MacroActionSource::Load(obs_data_t *obj)
{
MacroAction::Load(obj);
const char *sourceName = obs_data_get_string(obj, "source");
_source = GetWeakSourceByName(sourceName);
_source.Load(obj);
_action = static_cast<SourceAction>(obs_data_get_int(obj, "action"));
_settings = obs_data_get_string(obj, "settings");
_button.Load(obj);
_settings.Load(obj, "settings");
return true;
}
std::string MacroActionSource::GetShortDesc()
std::string MacroActionSource::GetShortDesc() const
{
if (_source) {
return GetWeakSourceName(_source);
}
_source.ToString();
return "";
}
@@ -115,25 +152,48 @@ static inline void populateActionSelection(QComboBox *list)
}
}
static inline void populateSourceButtonSelection(QComboBox *list,
OBSWeakSource source)
{
list->clear();
auto buttons = getSourceButtons(source);
if (buttons.empty()) {
list->addItem(obs_module_text(
"AdvSceneSwitcher.action.source.noSettingsButtons"));
}
for (const auto &button : buttons) {
QVariant value;
value.setValue(button);
list->addItem(QString::fromStdString(button.ToString()), value);
}
}
MacroActionSourceEdit::MacroActionSourceEdit(
QWidget *parent, std::shared_ptr<MacroActionSource> entryData)
: QWidget(parent)
{
_sources = new QComboBox();
_sources = new SourceSelectionWidget(this, QStringList(), true);
_actions = new QComboBox();
_settingsButtons = new QComboBox();
_getSettings = new QPushButton(
obs_module_text("AdvSceneSwitcher.action.source.getSettings"));
_settings = new ResizingPlainTextEdit(this);
_settings = new VariableTextEdit(this);
_warning = new QLabel(
obs_module_text("AdvSceneSwitcher.action.source.warning"));
populateActionSelection(_actions);
populateSourceSelection(_sources);
auto sources = GetSourceNames();
sources.sort();
_sources->SetSourceNameList(sources);
QWidget::connect(_actions, SIGNAL(currentIndexChanged(int)), this,
SLOT(ActionChanged(int)));
QWidget::connect(_sources, SIGNAL(currentTextChanged(const QString &)),
this, SLOT(SourceChanged(const QString &)));
QWidget::connect(_settingsButtons, SIGNAL(currentIndexChanged(int)),
this, SLOT(ButtonChanged(int)));
QWidget::connect(_sources,
SIGNAL(SourceChanged(const SourceSelection &)), this,
SLOT(SourceChanged(const SourceSelection &)));
QWidget::connect(_getSettings, SIGNAL(clicked()), this,
SLOT(GetSettingsClicked()));
QWidget::connect(_settings, SIGNAL(textChanged()), this,
@@ -147,6 +207,7 @@ MacroActionSourceEdit::MacroActionSourceEdit(
{"{{actions}}", _actions},
{"{{settings}}", _settings},
{"{{getSettings}}", _getSettings},
{"{{settingsButtons}}", _settingsButtons},
};
placeWidgets(obs_module_text("AdvSceneSwitcher.action.source.entry"),
entryLayout, widgetPlaceholders);
@@ -169,24 +230,31 @@ void MacroActionSourceEdit::UpdateEntryData()
return;
}
populateSourceButtonSelection(_settingsButtons,
_entryData->_source.GetSource());
_actions->setCurrentIndex(static_cast<int>(_entryData->_action));
_sources->setCurrentText(
GetWeakSourceName(_entryData->_source).c_str());
_settings->setPlainText(QString::fromStdString(_entryData->_settings));
_sources->SetSource(_entryData->_source);
_settingsButtons->setCurrentText(
QString::fromStdString(_entryData->_button.ToString()));
_settings->setPlainText(_entryData->_settings);
SetWidgetVisibility();
adjustSize();
updateGeometry();
}
void MacroActionSourceEdit::SourceChanged(const QString &text)
void MacroActionSourceEdit::SourceChanged(const SourceSelection &source)
{
if (_loading || !_entryData) {
return;
}
std::lock_guard<std::mutex> lock(switcher->m);
_entryData->_source = GetWeakSourceByQString(text);
{
std::lock_guard<std::mutex> lock(switcher->m);
_entryData->_source = source;
}
populateSourceButtonSelection(_settingsButtons,
_entryData->_source.GetSource());
emit HeaderInfoChanged(
QString::fromStdString(_entryData->GetShortDesc()));
}
@@ -202,14 +270,25 @@ void MacroActionSourceEdit::ActionChanged(int value)
SetWidgetVisibility();
}
void MacroActionSourceEdit::GetSettingsClicked()
void MacroActionSourceEdit::ButtonChanged(int idx)
{
if (_loading || !_entryData || !_entryData->_source) {
if (_loading || !_entryData) {
return;
}
_settings->setPlainText(
formatJsonString(getSourceSettings(_entryData->_source)));
std::lock_guard<std::mutex> lock(switcher->m);
_entryData->_button = qvariant_cast<SourceSettingButton>(
_settingsButtons->itemData(idx));
}
void MacroActionSourceEdit::GetSettingsClicked()
{
if (_loading || !_entryData || !_entryData->_source.GetSource()) {
return;
}
_settings->setPlainText(formatJsonString(
getSourceSettings(_entryData->_source.GetSource())));
}
void MacroActionSourceEdit::SettingsChanged()
@@ -233,5 +312,34 @@ void MacroActionSourceEdit::SetWidgetVisibility()
_settings->setVisible(showSettings);
_getSettings->setVisible(showSettings);
_warning->setVisible(showWarning);
_settingsButtons->setVisible(_entryData->_action ==
SourceAction::SETTINGS_BUTTON);
adjustSize();
}
bool SourceSettingButton::Save(obs_data_t *obj) const
{
auto data = obs_data_create();
obs_data_set_string(data, "id", id.c_str());
obs_data_set_string(data, "description", description.c_str());
obs_data_set_obj(obj, "sourceSettingButton", data);
obs_data_release(data);
return true;
}
bool SourceSettingButton::Load(obs_data_t *obj)
{
auto data = obs_data_get_obj(obj, "sourceSettingButton");
id = obs_data_get_string(data, "id");
description = obs_data_get_string(data, "description");
obs_data_release(data);
return true;
}
std::string SourceSettingButton::ToString() const
{
if (id.empty()) {
return "";
}
return "[" + id + "] " + description;
}

View File

@@ -1,6 +1,7 @@
#pragma once
#include "macro-action-edit.hpp"
#include "resizing-text-edit.hpp"
#include "variable-text-edit.hpp"
#include "source-selection.hpp"
#include <QSpinBox>
#include <QLabel>
@@ -12,24 +13,35 @@ enum class SourceAction {
DISABLE,
SETTINGS,
REFRESH_SETTINGS,
SETTINGS_BUTTON,
};
struct SourceSettingButton {
bool Save(obs_data_t *obj) const;
bool Load(obs_data_t *obj);
std::string ToString() const;
std::string id = "";
std::string description = "";
};
class MacroActionSource : public MacroAction {
public:
MacroActionSource(Macro *m) : MacroAction(m) {}
bool PerformAction();
void LogAction();
bool Save(obs_data_t *obj);
void LogAction() const;
bool Save(obs_data_t *obj) const;
bool Load(obs_data_t *obj);
std::string GetShortDesc();
std::string GetId() { return id; };
std::string GetShortDesc() const;
std::string GetId() const { return id; };
static std::shared_ptr<MacroAction> Create(Macro *m)
{
return std::make_shared<MacroActionSource>(m);
}
OBSWeakSource _source;
std::string _settings = "";
SourceSelection _source;
SourceSettingButton _button;
VariableResolvingString _settings = "";
SourceAction _action = SourceAction::ENABLE;
private:
@@ -54,18 +66,20 @@ public:
}
private slots:
void SourceChanged(const QString &text);
void SourceChanged(const SourceSelection &);
void ActionChanged(int value);
void ButtonChanged(int idx);
void GetSettingsClicked();
void SettingsChanged();
signals:
void HeaderInfoChanged(const QString &);
protected:
QComboBox *_sources;
SourceSelectionWidget *_sources;
QComboBox *_actions;
QComboBox *_settingsButtons;
QPushButton *_getSettings;
ResizingPlainTextEdit *_settings;
VariableTextEdit *_settings;
QLabel *_warning;
std::shared_ptr<MacroActionSource> _entryData;

View File

@@ -47,7 +47,7 @@ bool MacroActionStream::PerformAction()
return true;
}
void MacroActionStream::LogAction()
void MacroActionStream::LogAction() const
{
auto it = actionTypes.find(_action);
if (it != actionTypes.end()) {
@@ -58,7 +58,7 @@ void MacroActionStream::LogAction()
}
}
bool MacroActionStream::Save(obs_data_t *obj)
bool MacroActionStream::Save(obs_data_t *obj) const
{
MacroAction::Save(obj);
obs_data_set_int(obj, "action", static_cast<int>(_action));

View File

@@ -15,10 +15,10 @@ class MacroActionStream : public MacroAction {
public:
MacroActionStream(Macro *m) : MacroAction(m) {}
bool PerformAction();
void LogAction();
bool Save(obs_data_t *obj);
void LogAction() const;
bool Save(obs_data_t *obj) const;
bool Load(obs_data_t *obj);
std::string GetId() { return id; };
std::string GetId() const { return id; };
static std::shared_ptr<MacroAction> Create(Macro *m)
{
return std::make_shared<MacroActionStream>(m);

View File

@@ -72,19 +72,19 @@ bool MacroActionSudioMode::PerformAction()
return true;
}
void MacroActionSudioMode::LogAction()
void MacroActionSudioMode::LogAction() const
{
auto it = actionTypes.find(_action);
if (it != actionTypes.end()) {
vblog(LOG_INFO, "performed action \"%s\" with scene \"%s\"",
it->second.c_str(), _scene.ToString().c_str());
it->second.c_str(), _scene.ToString(true).c_str());
} else {
blog(LOG_WARNING, "ignored unknown studio mode action %d",
static_cast<int>(_action));
}
}
bool MacroActionSudioMode::Save(obs_data_t *obj)
bool MacroActionSudioMode::Save(obs_data_t *obj) const
{
MacroAction::Save(obj);
obs_data_set_int(obj, "action", static_cast<int>(_action));
@@ -105,7 +105,7 @@ bool MacroActionSudioMode::Load(obs_data_t *obj)
return true;
}
std::string MacroActionSudioMode::GetShortDesc()
std::string MacroActionSudioMode::GetShortDesc() const
{
if (_action == StudioModeAction::SET_SCENE) {
return _scene.ToString();

View File

@@ -13,11 +13,11 @@ class MacroActionSudioMode : public MacroAction {
public:
MacroActionSudioMode(Macro *m) : MacroAction(m) {}
bool PerformAction();
void LogAction();
bool Save(obs_data_t *obj);
void LogAction() const;
bool Save(obs_data_t *obj) const;
bool Load(obs_data_t *obj);
std::string GetShortDesc();
std::string GetId() { return id; };
std::string GetShortDesc() const;
std::string GetId() const { return id; };
static std::shared_ptr<MacroAction> Create(Macro *m)
{
return std::make_shared<MacroActionSudioMode>(m);

View File

@@ -19,12 +19,12 @@ bool MacroActionSystray::PerformAction()
return true;
}
void MacroActionSystray::LogAction()
void MacroActionSystray::LogAction() const
{
vblog(LOG_INFO, "display systray message \"%s\"", _msg.c_str());
}
bool MacroActionSystray::Save(obs_data_t *obj)
bool MacroActionSystray::Save(obs_data_t *obj) const
{
MacroAction::Save(obj);
obs_data_set_string(obj, "message", _msg.c_str());

View File

@@ -7,10 +7,10 @@ class MacroActionSystray : public MacroAction {
public:
MacroActionSystray(Macro *m) : MacroAction(m) {}
bool PerformAction();
void LogAction();
bool Save(obs_data_t *obj);
void LogAction() const;
bool Save(obs_data_t *obj) const;
bool Load(obs_data_t *obj);
std::string GetId() { return id; };
std::string GetId() const { return id; };
static std::shared_ptr<MacroAction> Create(Macro *m)
{
return std::make_shared<MacroActionSystray>(m);

View File

@@ -56,7 +56,7 @@ bool MacroActionTimer::PerformAction()
return true;
}
void MacroActionTimer::LogAction()
void MacroActionTimer::LogAction() const
{
if (!_macro.get()) {
return;
@@ -84,7 +84,7 @@ void MacroActionTimer::LogAction()
}
}
bool MacroActionTimer::Save(obs_data_t *obj)
bool MacroActionTimer::Save(obs_data_t *obj) const
{
MacroAction::Save(obj);
_macro.Save(obj);
@@ -103,7 +103,7 @@ bool MacroActionTimer::Load(obs_data_t *obj)
return true;
}
std::string MacroActionTimer::GetShortDesc()
std::string MacroActionTimer::GetShortDesc() const
{
if (_macro.get()) {
return _macro->Name();

View File

@@ -17,11 +17,11 @@ class MacroActionTimer : public MacroRefAction {
public:
MacroActionTimer(Macro *m) : MacroAction(m), MacroRefAction(m) {}
bool PerformAction();
void LogAction();
bool Save(obs_data_t *obj);
void LogAction() const;
bool Save(obs_data_t *obj) const;
bool Load(obs_data_t *obj);
std::string GetShortDesc();
std::string GetId() { return id; };
std::string GetShortDesc() const;
std::string GetId() const { return id; };
static std::shared_ptr<MacroAction> Create(Macro *m)
{
return std::make_shared<MacroActionTimer>(m);

View File

@@ -119,7 +119,7 @@ bool MacroActionTransition::PerformAction()
return true;
}
void MacroActionTransition::LogAction()
void MacroActionTransition::LogAction() const
{
std::string msgBegin;
switch (_type) {
@@ -127,18 +127,18 @@ void MacroActionTransition::LogAction()
msgBegin += "set scene transition";
break;
case Type::SCENE_OVERRIDE:
msgBegin +=
"set scene override transition of " + _scene.ToString();
msgBegin += "set scene override transition of " +
_scene.ToString(true);
break;
case Type::SOURCE_SHOW:
msgBegin += "set source show transition of " +
_source.ToString() + " on scene " +
_scene.ToString();
_source.ToString(true) + " on scene " +
_scene.ToString(true);
break;
case Type::SOURCE_HIDE:
msgBegin += "set source hide transition of " +
_source.ToString() + " on scene " +
_scene.ToString();
_source.ToString(true) + " on scene " +
_scene.ToString(true);
break;
}
if (_setDuration) {
@@ -151,7 +151,7 @@ void MacroActionTransition::LogAction()
}
}
bool MacroActionTransition::Save(obs_data_t *obj)
bool MacroActionTransition::Save(obs_data_t *obj) const
{
MacroAction::Save(obj);
obs_data_set_int(obj, "actionType", static_cast<int>(_type));
@@ -177,7 +177,7 @@ bool MacroActionTransition::Load(obs_data_t *obj)
return true;
}
std::string MacroActionTransition::GetShortDesc()
std::string MacroActionTransition::GetShortDesc() const
{
std::string msgBegin;
switch (_type) {

View File

@@ -12,11 +12,11 @@ class MacroActionTransition : public MacroAction {
public:
MacroActionTransition(Macro *m) : MacroAction(m) {}
bool PerformAction();
void LogAction();
bool Save(obs_data_t *obj);
void LogAction() const;
bool Save(obs_data_t *obj) const;
bool Load(obs_data_t *obj);
std::string GetShortDesc();
std::string GetId() { return id; };
std::string GetShortDesc() const;
std::string GetId() const { return id; };
static std::shared_ptr<MacroAction> Create(Macro *m)
{
return std::make_shared<MacroActionTransition>(m);

View File

@@ -1,5 +1,6 @@
#include "macro-action-variable.hpp"
#include "advanced-scene-switcher.hpp"
#include "macro-condition-edit.hpp"
#include "utility.hpp"
const std::string MacroActionVariable::id = "variable";
@@ -9,8 +10,8 @@ bool MacroActionVariable::_registered = MacroActionFactory::Register(
{MacroActionVariable::Create, MacroActionVariableEdit::Create,
"AdvSceneSwitcher.action.variable"});
static std::map<MacroActionVariable::Type, std::string> waitTypes = {
{MacroActionVariable::Type::SET,
static std::map<MacroActionVariable::Type, std::string> actionTypes = {
{MacroActionVariable::Type::SET_FIXED_VALUE,
"AdvSceneSwitcher.action.variable.type.set"},
{MacroActionVariable::Type::APPEND,
"AdvSceneSwitcher.action.variable.type.append"},
@@ -20,6 +21,10 @@ static std::map<MacroActionVariable::Type, std::string> waitTypes = {
"AdvSceneSwitcher.action.variable.type.increment"},
{MacroActionVariable::Type::DECREMENT,
"AdvSceneSwitcher.action.variable.type.decrement"},
{MacroActionVariable::Type::SET_CONDITION_VALUE,
"AdvSceneSwitcher.action.variable.type.setConditionValue"},
{MacroActionVariable::Type::SET_ACTION_VALUE,
"AdvSceneSwitcher.action.variable.type.setActionValue"},
};
static void apppend(Variable &var, const std::string &value)
@@ -41,6 +46,11 @@ static void modifyNumValue(Variable &var, double val, const bool increment)
}
}
MacroActionVariable::~MacroActionVariable()
{
DecrementCurrentSegmentVariableRef();
}
bool MacroActionVariable::PerformAction()
{
auto var = GetVariableByName(_variableName);
@@ -49,13 +59,13 @@ bool MacroActionVariable::PerformAction()
}
switch (_type) {
case MacroActionVariable::Type::SET:
case Type::SET_FIXED_VALUE:
var->SetValue(_strValue);
break;
case MacroActionVariable::Type::APPEND:
case Type::APPEND:
apppend(*var, _strValue);
break;
case MacroActionVariable::Type::APPEND_VAR: {
case Type::APPEND_VAR: {
auto var2 = GetVariableByName(_variable2Name);
if (!var2) {
return true;
@@ -63,18 +73,34 @@ bool MacroActionVariable::PerformAction()
apppend(*var, var2->Value());
break;
}
case MacroActionVariable::Type::INCREMENT:
case Type::INCREMENT:
modifyNumValue(*var, _numValue, true);
break;
case MacroActionVariable::Type::DECREMENT:
case Type::DECREMENT:
modifyNumValue(*var, _numValue, false);
break;
case Type::SET_CONDITION_VALUE:
case Type::SET_ACTION_VALUE: {
auto m = GetMacro();
if (!m) {
return true;
}
if (GetSegmentIndexValue() == -1) {
return true;
}
auto segment = _macroSegment.lock();
if (!segment) {
return true;
}
var->SetValue(segment->GetVariableValue());
break;
}
}
return true;
}
bool MacroActionVariable::Save(obs_data_t *obj)
bool MacroActionVariable::Save(obs_data_t *obj) const
{
MacroAction::Save(obj);
obs_data_set_string(obj, "variableName", _variableName.c_str());
@@ -82,6 +108,7 @@ bool MacroActionVariable::Save(obs_data_t *obj)
obs_data_set_string(obj, "strValue", _strValue.c_str());
obs_data_set_double(obj, "numValue", _numValue);
obs_data_set_int(obj, "condition", static_cast<int>(_type));
obs_data_set_int(obj, "segmentIdx", GetSegmentIndexValue());
return true;
}
@@ -93,17 +120,97 @@ bool MacroActionVariable::Load(obs_data_t *obj)
_strValue = obs_data_get_string(obj, "strValue");
_numValue = obs_data_get_double(obj, "numValue");
_type = static_cast<Type>(obs_data_get_int(obj, "condition"));
_segmentIdxLoadValue = obs_data_get_int(obj, "segmentIdx");
return true;
}
std::string MacroActionVariable::GetShortDesc()
bool MacroActionVariable::PostLoad()
{
SetSegmentIndexValue(_segmentIdxLoadValue);
return true;
}
std::string MacroActionVariable::GetShortDesc() const
{
return _variableName;
}
void MacroActionVariable::SetSegmentIndexValue(int value)
{
DecrementCurrentSegmentVariableRef();
auto m = GetMacro();
if (!m) {
_macroSegment.reset();
return;
}
if (value < 0) {
_macroSegment.reset();
return;
}
std::shared_ptr<MacroSegment> segment;
if (_type == Type::SET_CONDITION_VALUE) {
if (value < m->Conditions().size()) {
segment = m->Conditions().at(value);
}
} else if (_type == Type::SET_ACTION_VALUE) {
if (value < m->Actions().size()) {
segment = m->Actions().at(value);
}
}
_macroSegment = segment;
if (segment) {
segment->IncrementVariableRef();
}
}
int MacroActionVariable::GetSegmentIndexValue() const
{
auto m = GetMacro();
if (!m) {
return -1;
}
auto segment = _macroSegment.lock();
if (!segment) {
return -1;
}
if (_type == Type::SET_CONDITION_VALUE) {
auto it = std::find(m->Conditions().begin(),
m->Conditions().end(), segment);
if (it != m->Conditions().end()) {
return std::distance(m->Conditions().begin(), it);
}
return -1;
} else if (_type == Type::SET_ACTION_VALUE) {
auto it = std::find(m->Actions().begin(), m->Actions().end(),
segment);
if (it != m->Actions().end()) {
return std::distance(m->Actions().begin(), it);
}
return -1;
}
return -1;
}
void MacroActionVariable::DecrementCurrentSegmentVariableRef()
{
auto segment = _macroSegment.lock();
if (!segment) {
return;
}
segment->DecrementVariableRef();
}
static inline void populateTypeSelection(QComboBox *list)
{
for (auto entry : waitTypes) {
for (auto entry : actionTypes) {
list->addItem(obs_module_text(entry.second.c_str()));
}
}
@@ -114,11 +221,18 @@ MacroActionVariableEdit::MacroActionVariableEdit(
_variables(new VariableSelection(this)),
_variables2(new VariableSelection(this)),
_actions(new QComboBox()),
_strValue(new QLineEdit()),
_numValue(new QDoubleSpinBox())
_strValue(new ResizingPlainTextEdit(this, 5, 1, 1)),
_numValue(new QDoubleSpinBox()),
_segmentIdx(new QSpinBox()),
_segmentValueStatus(new QLabel()),
_segmentValue(new ResizingPlainTextEdit(this, 10, 1, 1))
{
_numValue->setMinimum(-9999999999);
_numValue->setMaximum(9999999999);
_segmentIdx->setMinimum(0);
_segmentIdx->setMaximum(99);
_segmentIdx->setSpecialValueText("-");
_segmentValue->setReadOnly(true);
populateTypeSelection(_actions);
QWidget::connect(_variables, SIGNAL(SelectionChanged(const QString &)),
@@ -127,25 +241,41 @@ MacroActionVariableEdit::MacroActionVariableEdit(
this, SLOT(Variable2Changed(const QString &)));
QWidget::connect(_actions, SIGNAL(currentIndexChanged(int)), this,
SLOT(ActionChanged(int)));
QWidget::connect(_strValue, SIGNAL(editingFinished()), this,
QWidget::connect(_strValue, SIGNAL(textChanged()), this,
SLOT(StrValueChanged()));
QWidget::connect(_numValue, SIGNAL(valueChanged(double)), this,
SLOT(NumValueChanged(double)));
QWidget::connect(_segmentIdx, SIGNAL(valueChanged(int)), this,
SLOT(SegmentIndexChanged(int)));
QWidget::connect(window(), SIGNAL(MacroSegmentOrderChanged()), this,
SLOT(MacroSegmentOrderChanged()));
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
{"{{variables}}", _variables}, {"{{variables2}}", _variables2},
{"{{actions}}", _actions}, {"{{strValue}}", _strValue},
{"{{variables}}", _variables},
{"{{variables2}}", _variables2},
{"{{actions}}", _actions},
{"{{strValue}}", _strValue},
{"{{numValue}}", _numValue},
{"{{segmentIndex}}", _segmentIdx},
};
auto mainLayout = new QHBoxLayout;
auto entryLayout = new QHBoxLayout;
placeWidgets(obs_module_text("AdvSceneSwitcher.action.variable.entry"),
mainLayout, widgetPlaceholders);
setLayout(mainLayout);
entryLayout, widgetPlaceholders);
auto layout = new QVBoxLayout;
layout->addLayout(entryLayout);
layout->addWidget(_segmentValueStatus);
layout->addWidget(_segmentValue);
setLayout(layout);
_entryData = entryData;
UpdateEntryData();
_loading = false;
UpdateSegmentVariableValue();
connect(&_timer, SIGNAL(timeout()), this,
SLOT(UpdateSegmentVariableValue()));
_timer.start(1500);
}
void MacroActionVariableEdit::UpdateEntryData()
@@ -157,8 +287,9 @@ void MacroActionVariableEdit::UpdateEntryData()
_variables->SetVariable(_entryData->_variableName);
_variables2->SetVariable(_entryData->_variable2Name);
_actions->setCurrentIndex(static_cast<int>(_entryData->_type));
_strValue->setText(QString::fromStdString(_entryData->_strValue));
_strValue->setPlainText(QString::fromStdString(_entryData->_strValue));
_numValue->setValue(_entryData->_numValue);
_segmentIdx->setValue(_entryData->GetSegmentIndexValue() + 1);
SetWidgetVisibility();
}
@@ -191,6 +322,12 @@ void MacroActionVariableEdit::ActionChanged(int value)
std::lock_guard<std::mutex> lock(switcher->m);
_entryData->_type = static_cast<MacroActionVariable::Type>(value);
SetWidgetVisibility();
if (_entryData->_type == MacroActionVariable::Type::SET_ACTION_VALUE ||
_entryData->_type ==
MacroActionVariable::Type::SET_CONDITION_VALUE) {
MarkSelectedSegment();
}
}
void MacroActionVariableEdit::StrValueChanged()
@@ -200,7 +337,8 @@ void MacroActionVariableEdit::StrValueChanged()
}
std::lock_guard<std::mutex> lock(switcher->m);
_entryData->_strValue = _strValue->text().toStdString();
_entryData->_strValue = _strValue->toPlainText().toStdString();
adjustSize();
}
void MacroActionVariableEdit::NumValueChanged(double val)
@@ -213,40 +351,175 @@ void MacroActionVariableEdit::NumValueChanged(double val)
_entryData->_numValue = val;
}
void MacroActionVariableEdit::SegmentIndexChanged(int val)
{
if (_loading || !_entryData) {
return;
}
std::lock_guard<std::mutex> lock(switcher->m);
_entryData->SetSegmentIndexValue(val - 1);
MarkSelectedSegment();
}
void MacroActionVariableEdit::SetSegmentValueError(const QString &text)
{
_segmentValueStatus->setText(text);
_segmentValue->setPlainText("");
_segmentValue->hide();
adjustSize();
updateGeometry();
}
void MacroActionVariableEdit::UpdateSegmentVariableValue()
{
if (!_entryData ||
!(_entryData->_type ==
MacroActionVariable::Type::SET_CONDITION_VALUE ||
_entryData->_type ==
MacroActionVariable::Type::SET_ACTION_VALUE)) {
return;
}
auto m = _entryData->GetMacro();
if (!m) {
return;
}
int index = _entryData->GetSegmentIndexValue();
if (index < 0) {
SetSegmentValueError(obs_module_text(
"AdvSceneSwitcher.action.variable.invalidSelection"));
const QSignalBlocker b(_segmentIdx);
_segmentIdx->setValue(index);
return;
}
std::shared_ptr<MacroSegment> segment;
if (_entryData->_type == MacroActionVariable::Type::SET_ACTION_VALUE) {
const auto &actions = m->Actions();
if (index < actions.size()) {
segment = actions.at(index);
}
} else if (_entryData->_type ==
MacroActionVariable::Type::SET_CONDITION_VALUE) {
const auto &conditions = m->Conditions();
if (index < conditions.size()) {
segment = conditions.at(index);
}
}
if (!segment) {
SetSegmentValueError(obs_module_text(
"AdvSceneSwitcher.action.variable.invalidSelection"));
return;
}
if (!segment->SupportsVariableValue()) {
std::string type;
QString fmt;
if (_entryData->_type ==
MacroActionVariable::Type::SET_ACTION_VALUE) {
type = MacroActionFactory::GetActionName(
segment->GetId());
fmt = QString(obs_module_text(
"AdvSceneSwitcher.action.variable.actionNoVariableSupport"));
} else if (_entryData->_type ==
MacroActionVariable::Type::SET_CONDITION_VALUE) {
type = MacroConditionFactory::GetConditionName(
segment->GetId());
fmt = QString(obs_module_text(
"AdvSceneSwitcher.action.variable.conditionNoVariableSupport"));
}
SetSegmentValueError(
fmt.arg(QString(obs_module_text(type.c_str()))));
return;
}
_segmentValueStatus->setText(obs_module_text(
"AdvSceneSwitcher.action.variable.currentSegmentValue"));
_segmentValue->show();
_segmentValue->setPlainText(
QString::fromStdString(segment->GetVariableValue()));
adjustSize();
updateGeometry();
}
void MacroActionVariableEdit::MacroSegmentOrderChanged()
{
const QSignalBlocker b(_segmentIdx);
_segmentIdx->setValue(_entryData->GetSegmentIndexValue() + 1);
}
void MacroActionVariableEdit::MarkSelectedSegment()
{
if (switcher->disableHints) {
return;
}
auto m = _entryData->GetMacro();
if (!m) {
return;
}
int index = _entryData->GetSegmentIndexValue();
if (index < 0) {
return;
}
if (_entryData->_type == MacroActionVariable::Type::SET_ACTION_VALUE) {
const auto &actions = m->Actions();
if (index >= actions.size()) {
return;
}
AdvSceneSwitcher::window->HighlightAction(
index, QColor(Qt::lightGray));
} else {
const auto &conditions = m->Conditions();
if (index >= conditions.size()) {
return;
}
AdvSceneSwitcher::window->HighlightCondition(
index, QColor(Qt::lightGray));
}
PulseWidget(_segmentIdx, QColor(Qt::lightGray), QColor(0, 0, 0, 0),
true);
}
void MacroActionVariableEdit::SetWidgetVisibility()
{
if (!_entryData) {
return;
}
auto type = _entryData->_type;
switch (type) {
case MacroActionVariable::Type::SET:
_variables2->hide();
_strValue->show();
_numValue->hide();
break;
case MacroActionVariable::Type::APPEND:
_variables2->hide();
_strValue->show();
_numValue->hide();
break;
case MacroActionVariable::Type::APPEND_VAR:
_variables2->show();
_strValue->hide();
_numValue->hide();
break;
case MacroActionVariable::Type::INCREMENT:
_variables2->hide();
_strValue->hide();
_numValue->show();
break;
case MacroActionVariable::Type::DECREMENT:
_variables2->hide();
_strValue->hide();
_numValue->show();
break;
}
_variables2->setVisible(_entryData->_type ==
MacroActionVariable::Type::APPEND_VAR);
_strValue->setVisible(
_entryData->_type ==
MacroActionVariable::Type::SET_FIXED_VALUE ||
_entryData->_type == MacroActionVariable::Type::APPEND);
_numValue->setVisible(
_entryData->_type == MacroActionVariable::Type::INCREMENT ||
_entryData->_type == MacroActionVariable::Type::DECREMENT);
_segmentValueStatus->setVisible(
_entryData->_type ==
MacroActionVariable::Type::SET_ACTION_VALUE ||
_entryData->_type ==
MacroActionVariable::Type::SET_CONDITION_VALUE);
_segmentValue->setVisible(
_entryData->_type ==
MacroActionVariable::Type::SET_ACTION_VALUE ||
_entryData->_type ==
MacroActionVariable::Type::SET_CONDITION_VALUE);
_segmentIdx->setVisible(
_entryData->_type ==
MacroActionVariable::Type::SET_ACTION_VALUE ||
_entryData->_type ==
MacroActionVariable::Type::SET_CONDITION_VALUE);
adjustSize();
updateGeometry();

View File

@@ -1,36 +1,46 @@
#pragma once
#include "macro-action-edit.hpp"
#include "resizing-text-edit.hpp"
#include "variable.hpp"
class MacroActionVariable : public MacroAction {
public:
MacroActionVariable(Macro *m) : MacroAction(m) {}
~MacroActionVariable();
bool PerformAction();
bool Save(obs_data_t *obj);
bool Save(obs_data_t *obj) const;
bool Load(obs_data_t *obj);
std::string GetShortDesc();
std::string GetId() { return id; };
bool PostLoad() override;
std::string GetShortDesc() const;
std::string GetId() const { return id; };
static std::shared_ptr<MacroAction> Create(Macro *m)
{
return std::make_shared<MacroActionVariable>(m);
}
void SetSegmentIndexValue(int);
int GetSegmentIndexValue() const;
enum class Type {
SET,
SET_FIXED_VALUE,
APPEND,
APPEND_VAR,
INCREMENT,
DECREMENT,
//...
SET_CONDITION_VALUE,
SET_ACTION_VALUE,
};
Type _type = Type::SET;
Type _type = Type::SET_FIXED_VALUE;
std::string _variableName = "";
std::string _variable2Name = "";
std::string _strValue = "";
double _numValue = 0;
private:
void DecrementCurrentSegmentVariableRef();
std::weak_ptr<MacroSegment> _macroSegment;
int _segmentIdxLoadValue = -1;
static bool _registered;
static const std::string id;
};
@@ -57,6 +67,9 @@ private slots:
void ActionChanged(int);
void StrValueChanged();
void NumValueChanged(double);
void SegmentIndexChanged(int val);
void UpdateSegmentVariableValue();
void MacroSegmentOrderChanged();
signals:
void HeaderInfoChanged(const QString &);
@@ -65,12 +78,18 @@ protected:
VariableSelection *_variables;
VariableSelection *_variables2;
QComboBox *_actions;
QLineEdit *_strValue;
ResizingPlainTextEdit *_strValue;
QSpinBox *_segmentIdx;
QDoubleSpinBox *_numValue;
QLabel *_segmentValueStatus;
ResizingPlainTextEdit *_segmentValue;
std::shared_ptr<MacroActionVariable> _entryData;
private:
void MarkSelectedSegment();
void SetWidgetVisibility();
void SetSegmentValueError(const QString &);
QTimer _timer;
bool _loading = true;
};

View File

@@ -35,7 +35,7 @@ bool MacroActionVCam::PerformAction()
return true;
}
void MacroActionVCam::LogAction()
void MacroActionVCam::LogAction() const
{
auto it = actionTypes.find(_action);
if (it != actionTypes.end()) {
@@ -46,7 +46,7 @@ void MacroActionVCam::LogAction()
}
}
bool MacroActionVCam::Save(obs_data_t *obj)
bool MacroActionVCam::Save(obs_data_t *obj) const
{
MacroAction::Save(obj);
obs_data_set_int(obj, "action", static_cast<int>(_action));

View File

@@ -13,10 +13,10 @@ class MacroActionVCam : public MacroAction {
public:
MacroActionVCam(Macro *m) : MacroAction(m) {}
bool PerformAction();
void LogAction();
bool Save(obs_data_t *obj);
void LogAction() const;
bool Save(obs_data_t *obj) const;
bool Load(obs_data_t *obj);
std::string GetId() { return id; };
std::string GetId() const { return id; };
static std::shared_ptr<MacroAction> Create(Macro *m)
{
return std::make_shared<MacroActionVCam>(m);

View File

@@ -52,7 +52,7 @@ bool MacroActionWait::PerformAction()
return !switcher->abortMacroWait;
}
bool MacroActionWait::Save(obs_data_t *obj)
bool MacroActionWait::Save(obs_data_t *obj) const
{
MacroAction::Save(obj);
_duration.Save(obj);

View File

@@ -14,9 +14,9 @@ class MacroActionWait : public MacroAction {
public:
MacroActionWait(Macro *m) : MacroAction(m) {}
bool PerformAction();
bool Save(obs_data_t *obj);
bool Save(obs_data_t *obj) const;
bool Load(obs_data_t *obj);
std::string GetId() { return id; };
std::string GetId() const { return id; };
static std::shared_ptr<MacroAction> Create(Macro *m)
{
return std::make_shared<MacroActionWait>(m);

View File

@@ -41,7 +41,7 @@ bool MacroActionWebsocket::PerformAction()
return true;
}
void MacroActionWebsocket::LogAction()
void MacroActionWebsocket::LogAction() const
{
switch (_type) {
case MacroActionWebsocket::Type::REQUEST:
@@ -57,11 +57,11 @@ void MacroActionWebsocket::LogAction()
}
}
bool MacroActionWebsocket::Save(obs_data_t *obj)
bool MacroActionWebsocket::Save(obs_data_t *obj) const
{
MacroAction::Save(obj);
obs_data_set_int(obj, "type", static_cast<int>(_type));
obs_data_set_string(obj, "message", _message.c_str());
_message.Save(obj, "message");
obs_data_set_string(obj, "connection", _connection.c_str());
return true;
}
@@ -70,12 +70,12 @@ bool MacroActionWebsocket::Load(obs_data_t *obj)
{
MacroAction::Load(obj);
_type = static_cast<Type>(obs_data_get_int(obj, "type"));
_message = obs_data_get_string(obj, "message");
_message.Load(obj, "message");
_connection = obs_data_get_string(obj, "connection");
return true;
}
std::string MacroActionWebsocket::GetShortDesc()
std::string MacroActionWebsocket::GetShortDesc() const
{
if (_type == Type::REQUEST) {
return _connection;
@@ -94,7 +94,7 @@ MacroActionWebsocketEdit::MacroActionWebsocketEdit(
QWidget *parent, std::shared_ptr<MacroActionWebsocket> entryData)
: QWidget(parent),
_actions(new QComboBox(this)),
_message(new ResizingPlainTextEdit(this)),
_message(new VariableTextEdit(this)),
_connection(new ConnectionSelection(this)),
_editLayout(new QHBoxLayout())
{
@@ -155,7 +155,7 @@ void MacroActionWebsocketEdit::UpdateEntryData()
}
_actions->setCurrentIndex(static_cast<int>(_entryData->_type));
_message->setPlainText(QString::fromStdString(_entryData->_message));
_message->setPlainText(_entryData->_message);
_connection->SetConnection(_entryData->_connection);
if (_entryData->_type == MacroActionWebsocket::Type::REQUEST) {

View File

@@ -1,7 +1,7 @@
#pragma once
#include "macro-action-edit.hpp"
#include "connection-manager.hpp"
#include "resizing-text-edit.hpp"
#include "variable-text-edit.hpp"
#include <QLineEdit>
#include <QPushButton>
@@ -12,11 +12,11 @@ class MacroActionWebsocket : public MacroAction {
public:
MacroActionWebsocket(Macro *m) : MacroAction(m) {}
bool PerformAction();
void LogAction();
bool Save(obs_data_t *obj);
void LogAction() const;
bool Save(obs_data_t *obj) const;
bool Load(obs_data_t *obj);
std::string GetShortDesc();
std::string GetId() { return id; };
std::string GetShortDesc() const;
std::string GetId() const { return id; };
static std::shared_ptr<MacroAction> Create(Macro *m)
{
return std::make_shared<MacroActionWebsocket>(m);
@@ -28,7 +28,8 @@ public:
};
Type _type = Type::REQUEST;
std::string _message = obs_module_text("AdvSceneSwitcher.enterText");
VariableResolvingString _message =
obs_module_text("AdvSceneSwitcher.enterText");
std::string _connection;
private:
@@ -69,7 +70,7 @@ private:
void SetupEventEdit();
QComboBox *_actions;
ResizingPlainTextEdit *_message;
VariableTextEdit *_message;
ConnectionSelection *_connection;
QHBoxLayout *_editLayout;
bool _loading = true;

View File

@@ -1,7 +1,12 @@
#include "macro-action.hpp"
#include "advanced-scene-switcher.hpp"
bool MacroAction::Save(obs_data_t *obj)
MacroAction::MacroAction(Macro *m, bool supportsVariableValue)
: MacroSegment(m, supportsVariableValue)
{
}
bool MacroAction::Save(obs_data_t *obj) const
{
MacroSegment::Save(obj);
obs_data_set_string(obj, "id", GetId().c_str());
@@ -14,16 +19,26 @@ bool MacroAction::Load(obs_data_t *obj)
return true;
}
void MacroAction::LogAction()
void MacroAction::LogAction() const
{
vblog(LOG_INFO, "performed action %s", GetId().c_str());
}
MacroRefAction::MacroRefAction(Macro *m, bool supportsVariableValue)
: MacroAction(m, supportsVariableValue)
{
}
void MacroRefAction::ResolveMacroRef()
{
_macro.UpdateRef();
}
MultiMacroRefAction::MultiMacroRefAction(Macro *m, bool supportsVariableValue)
: MacroAction(m, supportsVariableValue)
{
}
void MultiMacroRefAction::ResolveMacroRef()
{
for (auto &m : _macros) {

View File

@@ -4,23 +4,24 @@
class MacroAction : public MacroSegment {
public:
MacroAction(Macro *m) : MacroSegment(m) {}
MacroAction(Macro *m, bool supportsVariableValue = false);
virtual ~MacroAction() = default;
virtual bool PerformAction() = 0;
virtual bool Save(obs_data_t *obj) = 0;
virtual bool Save(obs_data_t *obj) const = 0;
virtual bool Load(obs_data_t *obj) = 0;
virtual void LogAction();
virtual void LogAction() const;
};
class MacroRefAction : virtual public MacroAction {
public:
MacroRefAction(Macro *m) : MacroAction(m) {}
MacroRefAction(Macro *m, bool supportsVariableValue = false);
void ResolveMacroRef();
MacroRef _macro;
};
class MultiMacroRefAction : virtual public MacroAction {
public:
MultiMacroRefAction(Macro *m) : MacroAction(m) {}
MultiMacroRefAction(Macro *m, bool supportsVariableValue = false);
void ResolveMacroRef();
std::vector<MacroRef> _macros;
};

View File

@@ -3,6 +3,7 @@
#include "utility.hpp"
#include "advanced-scene-switcher.hpp"
constexpr int64_t nsPerMs = 1000000;
const std::string MacroConditionAudio::id = "audio";
bool MacroConditionAudio::_registered = MacroConditionFactory::Register(
@@ -10,24 +11,39 @@ bool MacroConditionAudio::_registered = MacroConditionFactory::Register(
{MacroConditionAudio::Create, MacroConditionAudioEdit::Create,
"AdvSceneSwitcher.condition.audio"});
static std::map<AudioOutputCondition, std::string> audioOutputConditionTypes = {
{AudioOutputCondition::ABOVE,
"AdvSceneSwitcher.condition.audio.state.above"},
{AudioOutputCondition::BELOW,
"AdvSceneSwitcher.condition.audio.state.below"},
static std::map<MacroConditionAudio::Type, std::string> checkTypes = {
{MacroConditionAudio::Type::OUTPUT_VOLUME,
"AdvSceneSwitcher.condition.audio.type.output"},
{MacroConditionAudio::Type::CONFIGURED_VOLUME,
"AdvSceneSwitcher.condition.audio.type.volume"},
{MacroConditionAudio::Type::SYNC_OFFSET,
"AdvSceneSwitcher.condition.audio.type.syncOffset"},
{MacroConditionAudio::Type::MONITOR,
"AdvSceneSwitcher.condition.audio.type.monitor"},
{MacroConditionAudio::Type::BALANCE,
"AdvSceneSwitcher.condition.audio.type.balance"},
};
static std::map<AudioVolumeCondition, std::string> audioVolumeConditionTypes = {
{AudioVolumeCondition::ABOVE,
"AdvSceneSwitcher.condition.audio.state.above"},
{AudioVolumeCondition::EXACT,
"AdvSceneSwitcher.condition.audio.state.exact"},
{AudioVolumeCondition::BELOW,
"AdvSceneSwitcher.condition.audio.state.below"},
{AudioVolumeCondition::MUTE,
"AdvSceneSwitcher.condition.audio.state.mute"},
{AudioVolumeCondition::UNMUTE,
"AdvSceneSwitcher.condition.audio.state.unmute"},
static std::map<MacroConditionAudio::OutputCondition, std::string>
audioOutputConditionTypes = {
{MacroConditionAudio::OutputCondition::ABOVE,
"AdvSceneSwitcher.condition.audio.state.above"},
{MacroConditionAudio::OutputCondition::BELOW,
"AdvSceneSwitcher.condition.audio.state.below"},
};
static std::map<MacroConditionAudio::VolumeCondition, std::string>
audioVolumeConditionTypes = {
{MacroConditionAudio::VolumeCondition::ABOVE,
"AdvSceneSwitcher.condition.audio.state.above"},
{MacroConditionAudio::VolumeCondition::EXACT,
"AdvSceneSwitcher.condition.audio.state.exact"},
{MacroConditionAudio::VolumeCondition::BELOW,
"AdvSceneSwitcher.condition.audio.state.below"},
{MacroConditionAudio::VolumeCondition::MUTE,
"AdvSceneSwitcher.condition.audio.state.mute"},
{MacroConditionAudio::VolumeCondition::UNMUTE,
"AdvSceneSwitcher.condition.audio.state.unmute"},
};
MacroConditionAudio::~MacroConditionAudio()
@@ -39,49 +55,63 @@ MacroConditionAudio::~MacroConditionAudio()
bool MacroConditionAudio::CheckOutputCondition()
{
bool ret = false;
auto s = obs_weak_source_get_source(_audioSource);
auto s = obs_weak_source_get_source(_audioSource.GetSource());
double curVolume = ((double)_peak + 60) * 1.7;
switch (_outputCondition) {
case AudioOutputCondition::ABOVE:
case OutputCondition::ABOVE:
// peak will have a value from -60 db to 0 db
ret = ((double)_peak + 60) * 1.7 > _volume;
ret = curVolume > _volume;
break;
case AudioOutputCondition::BELOW:
case OutputCondition::BELOW:
// peak will have a value from -60 db to 0 db
ret = ((double)_peak + 60) * 1.7 < _volume;
ret = curVolume < _volume;
break;
default:
break;
}
SetVariableValue(std::to_string(curVolume));
// Reset for next check
_peak = -std::numeric_limits<float>::infinity();
obs_source_release(s);
if (_audioSource.GetType() == SourceSelection::Type::VARIABLE) {
ResetVolmeter();
}
return ret;
}
bool MacroConditionAudio::CheckVolumeCondition()
{
bool ret = false;
auto s = obs_weak_source_get_source(_audioSource);
auto s = obs_weak_source_get_source(_audioSource.GetSource());
float curVolume = obs_source_get_volume(s);
bool muted = obs_source_muted(s);
switch (_volumeCondition) {
case AudioVolumeCondition::ABOVE:
ret = obs_source_get_volume(s) > _volume / 100.f;
case VolumeCondition::ABOVE:
ret = curVolume > _volume / 100.f;
SetVariableValue(std::to_string(curVolume));
break;
case AudioVolumeCondition::EXACT:
ret = obs_source_get_volume(s) == _volume / 100.f;
case VolumeCondition::EXACT:
ret = curVolume == _volume / 100.f;
SetVariableValue(std::to_string(curVolume));
break;
case AudioVolumeCondition::BELOW:
ret = obs_source_get_volume(s) < _volume / 100.f;
case VolumeCondition::BELOW:
ret = curVolume < _volume / 100.f;
SetVariableValue(std::to_string(curVolume));
break;
case AudioVolumeCondition::MUTE:
ret = obs_source_muted(s);
case VolumeCondition::MUTE:
ret = muted;
SetVariableValue("");
break;
case AudioVolumeCondition::UNMUTE:
ret = !obs_source_muted(s);
case VolumeCondition::UNMUTE:
ret = !muted;
SetVariableValue("");
break;
default:
break;
@@ -91,25 +121,94 @@ bool MacroConditionAudio::CheckVolumeCondition()
return ret;
}
bool MacroConditionAudio::CheckCondition()
bool MacroConditionAudio::CheckSyncOffset()
{
switch (_checkType) {
case AudioConditionCheckType::OUTPUT_VOLUME:
return CheckOutputCondition();
case AudioConditionCheckType::CONFIGURED_VOLUME:
return CheckVolumeCondition();
default:
break;
if (!_audioSource.GetSource()) {
return false;
}
return false;
bool ret = false;
auto s = obs_weak_source_get_source(_audioSource.GetSource());
auto curOffset = obs_source_get_sync_offset(s) / nsPerMs;
if (_outputCondition == OutputCondition::ABOVE) {
ret = curOffset > _syncOffset;
} else {
ret = curOffset < _syncOffset;
}
SetVariableValue(std::to_string(curOffset));
obs_source_release(s);
return ret;
}
bool MacroConditionAudio::Save(obs_data_t *obj)
bool MacroConditionAudio::CheckMonitor()
{
if (!_audioSource.GetSource()) {
return false;
}
bool ret = false;
auto s = obs_weak_source_get_source(_audioSource.GetSource());
ret = obs_source_get_monitoring_type(s) == _monitorType;
SetVariableValue("");
obs_source_release(s);
return ret;
}
bool MacroConditionAudio::CheckBalance()
{
if (!_audioSource.GetSource()) {
return false;
}
bool ret = false;
auto s = obs_weak_source_get_source(_audioSource.GetSource());
auto curBalance = obs_source_get_balance_value(s);
if (_outputCondition == OutputCondition::ABOVE) {
ret = curBalance > _balance;
} else {
ret = curBalance < _balance;
}
SetVariableValue(std::to_string(curBalance));
obs_source_release(s);
return ret;
}
bool MacroConditionAudio::CheckCondition()
{
bool ret = false;
switch (_checkType) {
case Type::OUTPUT_VOLUME:
ret = CheckOutputCondition();
break;
case Type::CONFIGURED_VOLUME:
ret = CheckVolumeCondition();
break;
case Type::SYNC_OFFSET:
ret = CheckSyncOffset();
break;
case Type::MONITOR:
ret = CheckMonitor();
break;
case Type::BALANCE:
ret = CheckBalance();
break;
}
if (GetVariableValue().empty()) {
SetVariableValue(ret ? "true" : "false");
}
return ret;
}
bool MacroConditionAudio::Save(obs_data_t *obj) const
{
MacroCondition::Save(obj);
obs_data_set_string(obj, "audioSource",
GetWeakSourceName(_audioSource).c_str());
_audioSource.Save(obj, "audioSource");
obs_data_set_int(obj, "volume", _volume);
obs_data_set_int(obj, "syncOffset", _syncOffset);
obs_data_set_int(obj, "monitor", _monitorType);
obs_data_set_double(obj, "balance", _balance);
obs_data_set_int(obj, "checkType", static_cast<int>(_checkType));
obs_data_set_int(obj, "outputCondition",
static_cast<int>(_outputCondition));
@@ -135,76 +234,27 @@ obs_volmeter_t *AddVolmeterToSource(MacroConditionAudio *entry,
return volmeter;
}
// TODO: Remove in future version
static void convertOldSettingsFormat(obs_data_t *obj)
{
auto oldCond = static_cast<AudioOutputCondition>(
obs_data_get_int(obj, "condition"));
switch (oldCond) {
case AudioOutputCondition::ABOVE:
obs_data_set_int(
obj, "checkType",
static_cast<int>(
AudioConditionCheckType::OUTPUT_VOLUME));
obs_data_set_int(obj, "outputCondition",
static_cast<int>(AudioOutputCondition::ABOVE));
break;
case AudioOutputCondition::BELOW:
obs_data_set_int(
obj, "checkType",
static_cast<int>(
AudioConditionCheckType::OUTPUT_VOLUME));
obs_data_set_int(obj, "outputCondition",
static_cast<int>(AudioOutputCondition::BELOW));
break;
case AudioOutputCondition::MUTE:
obs_data_set_int(
obj, "checkType",
static_cast<int>(
AudioConditionCheckType::CONFIGURED_VOLUME));
obs_data_set_int(obj, "volumeCondition",
static_cast<int>(AudioVolumeCondition::MUTE));
break;
case AudioOutputCondition::UNMUTE:
obs_data_set_int(
obj, "checkType",
static_cast<int>(
AudioConditionCheckType::CONFIGURED_VOLUME));
obs_data_set_int(
obj, "volumeCondition",
static_cast<int>(AudioVolumeCondition::UNMUTE));
break;
default:
break;
}
}
bool MacroConditionAudio::Load(obs_data_t *obj)
{
if (!obs_data_has_user_value(obj, "checkType")) {
convertOldSettingsFormat(obj);
}
MacroCondition::Load(obj);
const char *audioSourceName = obs_data_get_string(obj, "audioSource");
_audioSource = GetWeakSourceByName(audioSourceName);
_audioSource.Load(obj, "audioSource");
_volume = obs_data_get_int(obj, "volume");
_checkType = static_cast<AudioConditionCheckType>(
obs_data_get_int(obj, "checkType"));
_outputCondition = static_cast<AudioOutputCondition>(
_syncOffset = obs_data_get_int(obj, "syncOffset");
_monitorType = static_cast<obs_monitoring_type>(
obs_data_get_int(obj, "monitor"));
_balance = obs_data_get_double(obj, "balance");
_checkType = static_cast<Type>(obs_data_get_int(obj, "checkType"));
_outputCondition = static_cast<OutputCondition>(
obs_data_get_int(obj, "outputCondition"));
_volumeCondition = static_cast<AudioVolumeCondition>(
_volumeCondition = static_cast<VolumeCondition>(
obs_data_get_int(obj, "volumeCondition"));
_volmeter = AddVolmeterToSource(this, _audioSource);
_volmeter = AddVolmeterToSource(this, _audioSource.GetSource());
return true;
}
std::string MacroConditionAudio::GetShortDesc()
std::string MacroConditionAudio::GetShortDesc() const
{
if (_audioSource) {
return GetWeakSourceName(_audioSource);
}
return "";
return _audioSource.ToString();
}
void MacroConditionAudio::SetVolumeLevel(void *data, const float *,
@@ -229,15 +279,23 @@ void MacroConditionAudio::ResetVolmeter()
obs_volmeter_remove_callback(_volmeter, SetVolumeLevel, this);
obs_volmeter_destroy(_volmeter);
_volmeter = AddVolmeterToSource(this, _audioSource);
_volmeter = AddVolmeterToSource(this, _audioSource.GetSource());
}
static inline void populateCheckTypes(QComboBox *list)
{
list->addItem(obs_module_text(
"AdvSceneSwitcher.condition.audio.type.output"));
list->addItem(obs_module_text(
"AdvSceneSwitcher.condition.audio.type.volume"));
list->clear();
for (const auto &[type, name] : checkTypes) {
if (type == MacroConditionAudio::Type::MONITOR) {
if (obs_audio_monitoring_available()) {
list->addItem(obs_module_text(name.c_str()),
static_cast<int>(type));
}
} else {
list->addItem(obs_module_text(name.c_str()),
static_cast<int>(type));
}
}
}
static inline void populateOutputConditionSelection(QComboBox *list)
@@ -258,44 +316,62 @@ static inline void populateVolumeConditionSelection(QComboBox *list)
MacroConditionAudioEdit::MacroConditionAudioEdit(
QWidget *parent, std::shared_ptr<MacroConditionAudio> entryData)
: QWidget(parent)
: QWidget(parent),
_checkTypes(new QComboBox()),
_sources(new SourceSelectionWidget(this, QStringList(), true)),
_condition(new QComboBox()),
_volume(new QSpinBox()),
_syncOffset(new QSpinBox()),
_monitorTypes(new QComboBox),
_balance(new SliderSpinBox(0., 1., ""))
{
_checkTypes = new QComboBox();
_audioSources = new QComboBox();
_condition = new QComboBox();
_volume = new QSpinBox();
_volume->setSuffix("%");
_volume->setMaximum(100);
_volume->setMinimum(0);
_syncOffset->setMinimum(-950);
_syncOffset->setMaximum(20000);
_syncOffset->setSuffix("ms");
auto sources = GetAudioSourceNames();
sources.sort();
_sources->SetSourceNameList(sources);
QWidget::connect(_checkTypes, SIGNAL(currentIndexChanged(int)), this,
SLOT(CheckTypeChanged(int)));
QWidget::connect(_volume, SIGNAL(valueChanged(int)), this,
SLOT(VolumeThresholdChanged(int)));
QWidget::connect(_syncOffset, SIGNAL(valueChanged(int)), this,
SLOT(SyncOffsetChanged(int)));
QWidget::connect(_monitorTypes, SIGNAL(currentIndexChanged(int)), this,
SLOT(MonitorTypeChanged(int)));
QWidget::connect(_balance, SIGNAL(DoubleValueChanged(double)), this,
SLOT(BalanceChanged(double)));
QWidget::connect(_condition, SIGNAL(currentIndexChanged(int)), this,
SLOT(ConditionChanged(int)));
QWidget::connect(_audioSources,
SIGNAL(currentTextChanged(const QString &)), this,
SLOT(SourceChanged(const QString &)));
QWidget::connect(_sources,
SIGNAL(SourceChanged(const SourceSelection &)), this,
SLOT(SourceChanged(const SourceSelection &)));
populateCheckTypes(_checkTypes);
populateAudioSelection(_audioSources);
populateMonitorTypeSelection(_monitorTypes);
QHBoxLayout *switchLayout = new QHBoxLayout;
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
{"{{checkType}}", _checkTypes},
{"{{audioSources}}", _audioSources},
{"{{audioSources}}", _sources},
{"{{volume}}", _volume},
{"{{syncOffset}}", _syncOffset},
{"{{monitorTypes}}", _monitorTypes},
{"{{balance}}", _balance},
{"{{condition}}", _condition},
};
placeWidgets(obs_module_text("AdvSceneSwitcher.condition.audio.entry"),
switchLayout, widgetPlaceholders);
QVBoxLayout *mainLayout = new QVBoxLayout;
mainLayout->addLayout(switchLayout);
mainLayout->addWidget(_balance);
setLayout(mainLayout);
_entryData = entryData;
@@ -306,8 +382,8 @@ MacroConditionAudioEdit::MacroConditionAudioEdit(
void MacroConditionAudioEdit::UpdateVolmeterSource()
{
delete _volMeter;
obs_source_t *soruce =
obs_weak_source_get_source(_entryData->_audioSource);
obs_source_t *soruce = obs_weak_source_get_source(
_entryData->_audioSource.GetSource());
_volMeter = new VolControl(soruce);
obs_source_release(soruce);
@@ -323,14 +399,14 @@ void MacroConditionAudioEdit::UpdateVolmeterSource()
_volMeter->GetSlider()->setValue(_entryData->_volume);
}
void MacroConditionAudioEdit::SourceChanged(const QString &text)
void MacroConditionAudioEdit::SourceChanged(const SourceSelection &source)
{
if (_loading || !_entryData) {
return;
}
std::lock_guard<std::mutex> lock(switcher->m);
_entryData->_audioSource = GetWeakSourceByQString(text);
_entryData->_audioSource = source;
_entryData->ResetVolmeter();
UpdateVolmeterSource();
SetWidgetVisibility();
@@ -348,6 +424,36 @@ void MacroConditionAudioEdit::VolumeThresholdChanged(int vol)
_entryData->_volume = vol;
}
void MacroConditionAudioEdit::SyncOffsetChanged(int value)
{
if (_loading || !_entryData) {
return;
}
std::lock_guard<std::mutex> lock(switcher->m);
_entryData->_syncOffset = value;
}
void MacroConditionAudioEdit::MonitorTypeChanged(int value)
{
if (_loading || !_entryData) {
return;
}
std::lock_guard<std::mutex> lock(switcher->m);
_entryData->_monitorType = static_cast<obs_monitoring_type>(value);
}
void MacroConditionAudioEdit::BalanceChanged(double value)
{
if (_loading || !_entryData) {
return;
}
std::lock_guard<std::mutex> lock(switcher->m);
_entryData->_balance = value;
}
void MacroConditionAudioEdit::ConditionChanged(int cond)
{
if (_loading || !_entryData) {
@@ -355,29 +461,37 @@ void MacroConditionAudioEdit::ConditionChanged(int cond)
}
std::lock_guard<std::mutex> lock(switcher->m);
if (_entryData->_checkType == AudioConditionCheckType::OUTPUT_VOLUME) {
if (_entryData->_checkType ==
MacroConditionAudio::Type::OUTPUT_VOLUME ||
_entryData->_checkType == MacroConditionAudio::Type::BALANCE ||
_entryData->_checkType == MacroConditionAudio::Type::SYNC_OFFSET) {
_entryData->_outputCondition =
static_cast<AudioOutputCondition>(cond);
static_cast<MacroConditionAudio::OutputCondition>(cond);
} else {
_entryData->_volumeCondition =
static_cast<AudioVolumeCondition>(cond);
static_cast<MacroConditionAudio::VolumeCondition>(cond);
}
SetWidgetVisibility();
}
void MacroConditionAudioEdit::CheckTypeChanged(int cond)
void MacroConditionAudioEdit::CheckTypeChanged(int idx)
{
if (_loading || !_entryData) {
return;
}
std::lock_guard<std::mutex> lock(switcher->m);
_entryData->_checkType = static_cast<AudioConditionCheckType>(cond);
_entryData->_checkType = static_cast<MacroConditionAudio::Type>(
_checkTypes->itemData(idx).toInt());
const QSignalBlocker b(_condition);
if (_entryData->_checkType == AudioConditionCheckType::OUTPUT_VOLUME) {
if (_entryData->_checkType ==
MacroConditionAudio::Type::OUTPUT_VOLUME ||
_entryData->_checkType == MacroConditionAudio::Type::BALANCE ||
_entryData->_checkType == MacroConditionAudio::Type::SYNC_OFFSET) {
populateOutputConditionSelection(_condition);
} else {
} else if (_entryData->_checkType ==
MacroConditionAudio::Type::CONFIGURED_VOLUME) {
populateVolumeConditionSelection(_condition);
}
SetWidgetVisibility();
@@ -389,19 +503,28 @@ void MacroConditionAudioEdit::UpdateEntryData()
return;
}
_audioSources->setCurrentText(
GetWeakSourceName(_entryData->_audioSource).c_str());
_sources->SetSource(_entryData->_audioSource);
_volume->setValue(_entryData->_volume);
_checkTypes->setCurrentIndex(static_cast<int>(_entryData->_checkType));
if (_entryData->_checkType == AudioConditionCheckType::OUTPUT_VOLUME) {
_syncOffset->setValue(_entryData->_syncOffset);
_monitorTypes->setCurrentIndex(_entryData->_monitorType);
_balance->SetDoubleValue(_entryData->_balance);
_checkTypes->setCurrentIndex(_checkTypes->findData(
static_cast<int>(_entryData->_checkType)));
if (_entryData->_checkType ==
MacroConditionAudio::Type::OUTPUT_VOLUME ||
_entryData->_checkType == MacroConditionAudio::Type::BALANCE ||
_entryData->_checkType == MacroConditionAudio::Type::SYNC_OFFSET) {
populateOutputConditionSelection(_condition);
_condition->setCurrentIndex(
static_cast<int>(_entryData->_outputCondition));
} else {
} else if (_entryData->_checkType ==
MacroConditionAudio::Type::CONFIGURED_VOLUME) {
populateVolumeConditionSelection(_condition);
_condition->setCurrentIndex(
static_cast<int>(_entryData->_volumeCondition));
}
UpdateVolmeterSource();
SetWidgetVisibility();
}
@@ -414,13 +537,30 @@ void MacroConditionAudioEdit::SetWidgetVisibility()
_volume->setVisible(
_entryData->_checkType ==
AudioConditionCheckType::OUTPUT_VOLUME ||
MacroConditionAudio::Type::OUTPUT_VOLUME ||
(_entryData->_checkType ==
AudioConditionCheckType::CONFIGURED_VOLUME &&
(_entryData->_volumeCondition == AudioVolumeCondition::ABOVE ||
_entryData->_volumeCondition == AudioVolumeCondition::EXACT ||
_entryData->_volumeCondition == AudioVolumeCondition::BELOW)));
MacroConditionAudio::Type::CONFIGURED_VOLUME &&
(_entryData->_volumeCondition ==
MacroConditionAudio::VolumeCondition::ABOVE ||
_entryData->_volumeCondition ==
MacroConditionAudio::VolumeCondition::EXACT ||
_entryData->_volumeCondition ==
MacroConditionAudio::VolumeCondition::BELOW)));
_condition->setVisible(
_entryData->_checkType ==
MacroConditionAudio::Type::OUTPUT_VOLUME ||
_entryData->_checkType ==
MacroConditionAudio::Type::CONFIGURED_VOLUME ||
_entryData->_checkType == MacroConditionAudio::Type::BALANCE ||
_entryData->_checkType ==
MacroConditionAudio::Type::SYNC_OFFSET);
_syncOffset->setVisible(_entryData->_checkType ==
MacroConditionAudio::Type::SYNC_OFFSET);
_monitorTypes->setVisible(_entryData->_checkType ==
MacroConditionAudio::Type::MONITOR);
_balance->setVisible(_entryData->_checkType ==
MacroConditionAudio::Type::BALANCE);
_volMeter->setVisible(_entryData->_checkType ==
AudioConditionCheckType::OUTPUT_VOLUME);
MacroConditionAudio::Type::OUTPUT_VOLUME);
adjustSize();
}

View File

@@ -1,41 +1,23 @@
#pragma once
#include "macro.hpp"
#include "volume-control.hpp"
#include "slider-spinbox.hpp"
#include "source-selection.hpp"
#include <limits>
#include <QWidget>
#include <QComboBox>
#include <chrono>
enum class AudioConditionCheckType {
OUTPUT_VOLUME,
CONFIGURED_VOLUME,
};
enum class AudioOutputCondition {
ABOVE,
BELOW,
// TODO: To be removed
MUTE,
UNMUTE,
};
enum class AudioVolumeCondition {
ABOVE,
EXACT,
BELOW,
MUTE,
UNMUTE,
};
class MacroConditionAudio : public MacroCondition {
public:
MacroConditionAudio(Macro *m) : MacroCondition(m) {}
MacroConditionAudio(Macro *m) : MacroCondition(m, true) {}
~MacroConditionAudio();
bool CheckCondition();
bool Save(obs_data_t *obj);
bool Save(obs_data_t *obj) const;
bool Load(obs_data_t *obj);
std::string GetShortDesc();
std::string GetId() { return id; };
std::string GetShortDesc() const;
std::string GetId() const { return id; };
static std::shared_ptr<MacroCondition> Create(Macro *m)
{
return std::make_shared<MacroConditionAudio>(m);
@@ -46,17 +28,43 @@ public:
const float inputPeak[MAX_AUDIO_CHANNELS]);
void ResetVolmeter();
OBSWeakSource _audioSource;
enum class Type {
OUTPUT_VOLUME,
CONFIGURED_VOLUME,
SYNC_OFFSET,
MONITOR,
BALANCE,
};
enum class OutputCondition {
ABOVE,
BELOW,
};
enum class VolumeCondition {
ABOVE,
EXACT,
BELOW,
MUTE,
UNMUTE,
};
SourceSelection _audioSource;
int _volume = 0;
AudioConditionCheckType _checkType =
AudioConditionCheckType::OUTPUT_VOLUME;
AudioOutputCondition _outputCondition = AudioOutputCondition::ABOVE;
AudioVolumeCondition _volumeCondition = AudioVolumeCondition::ABOVE;
int64_t _syncOffset = 0;
obs_monitoring_type _monitorType = OBS_MONITORING_TYPE_NONE;
double _balance = 0.5;
Type _checkType = Type::OUTPUT_VOLUME;
OutputCondition _outputCondition = OutputCondition::ABOVE;
VolumeCondition _volumeCondition = VolumeCondition::ABOVE;
obs_volmeter_t *_volmeter = nullptr;
private:
bool CheckOutputCondition();
bool CheckVolumeCondition();
bool CheckSyncOffset();
bool CheckMonitor();
bool CheckBalance();
float _peak = -std::numeric_limits<float>::infinity();
static bool _registered;
@@ -81,19 +89,25 @@ public:
void UpdateVolmeterSource();
private slots:
void SourceChanged(const QString &text);
void SourceChanged(const SourceSelection &);
void VolumeThresholdChanged(int vol);
void ConditionChanged(int cond);
void CheckTypeChanged(int cond);
void SyncOffsetChanged(int value);
void MonitorTypeChanged(int value);
void BalanceChanged(double value);
signals:
void HeaderInfoChanged(const QString &);
protected:
QComboBox *_checkTypes;
QComboBox *_audioSources;
SourceSelectionWidget *_sources;
QComboBox *_condition;
QSpinBox *_volume;
QSpinBox *_syncOffset;
QComboBox *_monitorTypes;
SliderSpinBox *_balance;
VolControl *_volMeter = nullptr;
std::shared_ptr<MacroConditionAudio> _entryData;

View File

@@ -3,6 +3,8 @@
#include "utility.hpp"
#include "advanced-scene-switcher.hpp"
#include <QScreen>
const std::string MacroConditionCursor::id = "cursor";
bool MacroConditionCursor::_registered = MacroConditionFactory::Register(
@@ -10,32 +12,71 @@ bool MacroConditionCursor::_registered = MacroConditionFactory::Register(
{MacroConditionCursor::Create, MacroConditionCursorEdit::Create,
"AdvSceneSwitcher.condition.cursor"});
static std::map<CursorCondition, std::string> cursorConditionTypes = {
{CursorCondition::REGION,
"AdvSceneSwitcher.condition.cursor.type.region"},
{CursorCondition::MOVING,
"AdvSceneSwitcher.condition.cursor.type.moving"},
static std::map<MacroConditionCursor::Condition, std::string>
cursorConditionTypes = {
{MacroConditionCursor::Condition::REGION,
"AdvSceneSwitcher.condition.cursor.type.region"},
{MacroConditionCursor::Condition::MOVING,
"AdvSceneSwitcher.condition.cursor.type.moving"},
#ifdef _WIN32 // Not implemented for MacOS and Linux
{MacroConditionCursor::Condition::CLICK,
"AdvSceneSwitcher.condition.cursor.type.click"},
#endif
};
bool MacroConditionCursor::CheckCondition()
static std::map<MacroConditionCursor::Button, std::string> buttonTypes = {
{MacroConditionCursor::Button::LEFT,
"AdvSceneSwitcher.condition.cursor.button.left"},
{MacroConditionCursor::Button::MIDDLE,
"AdvSceneSwitcher.condition.cursor.button.middle"},
{MacroConditionCursor::Button::RIGHT,
"AdvSceneSwitcher.condition.cursor.button.right"},
};
bool MacroConditionCursor::CheckClick()
{
std::pair<int, int> cursorPos = getCursorPos();
switch (_condition) {
case CursorCondition::REGION:
return cursorPos.first >= _minX && cursorPos.second >= _minY &&
cursorPos.first <= _maxX && cursorPos.second <= _maxY;
case CursorCondition::MOVING:
return switcher->cursorPosChanged;
default:
break;
switch (_button) {
case MacroConditionCursor::Button::LEFT:
return _lastCheckTime < lastMouseLeftClickTime;
case MacroConditionCursor::Button::MIDDLE:
return _lastCheckTime < lastMouseMiddleClickTime;
case MacroConditionCursor::Button::RIGHT:
return _lastCheckTime < lastMouseRightClickTime;
}
return false;
}
bool MacroConditionCursor::Save(obs_data_t *obj)
bool MacroConditionCursor::CheckCondition()
{
bool ret = false;
std::pair<int, int> cursorPos = getCursorPos();
switch (_condition) {
case Condition::REGION:
ret = cursorPos.first >= _minX && cursorPos.second >= _minY &&
cursorPos.first <= _maxX && cursorPos.second <= _maxY;
SetVariableValue(std::to_string(cursorPos.first) + " " +
std::to_string(cursorPos.second));
break;
case Condition::MOVING:
ret = switcher->cursorPosChanged;
break;
case Condition::CLICK:
ret = CheckClick();
break;
}
_lastCheckTime = std::chrono::high_resolution_clock::now();
if (GetVariableValue().empty()) {
SetVariableValue(ret ? "true" : "false");
}
return ret;
}
bool MacroConditionCursor::Save(obs_data_t *obj) const
{
MacroCondition::Save(obj);
obs_data_set_int(obj, "condition", static_cast<int>(_condition));
obs_data_set_int(obj, "button", static_cast<int>(_button));
obs_data_set_int(obj, "minX", _minX);
obs_data_set_int(obj, "minY", _minY);
obs_data_set_int(obj, "maxX", _maxX);
@@ -46,8 +87,8 @@ bool MacroConditionCursor::Save(obs_data_t *obj)
bool MacroConditionCursor::Load(obs_data_t *obj)
{
MacroCondition::Load(obj);
_condition = static_cast<CursorCondition>(
obs_data_get_int(obj, "condition"));
_condition = static_cast<Condition>(obs_data_get_int(obj, "condition"));
_button = static_cast<Button>(obs_data_get_int(obj, "button"));
_minX = obs_data_get_int(obj, "minX");
_minY = obs_data_get_int(obj, "minY");
_maxX = obs_data_get_int(obj, "maxX");
@@ -57,8 +98,17 @@ bool MacroConditionCursor::Load(obs_data_t *obj)
static inline void populateConditionSelection(QComboBox *list)
{
for (auto entry : cursorConditionTypes) {
list->addItem(obs_module_text(entry.second.c_str()));
for (const auto &[condition, name] : cursorConditionTypes) {
list->addItem(obs_module_text(name.c_str()),
static_cast<int>(condition));
}
}
static inline void populateButtonSelection(QComboBox *list)
{
for (const auto &[button, name] : buttonTypes) {
list->addItem(obs_module_text(name.c_str()),
static_cast<int>(button));
}
}
@@ -70,14 +120,17 @@ MacroConditionCursorEdit::MacroConditionCursorEdit(
_maxX(new QSpinBox()),
_maxY(new QSpinBox()),
_conditions(new QComboBox()),
_buttons(new QComboBox()),
_frameToggle(new QPushButton(obs_module_text(
"AdvSceneSwitcher.condition.cursor.showFrame"))),
_xPos(new QLabel("-")),
_yPos(new QLabel("-"))
_yPos(new QLabel("-")),
_curentPosLayout(new QHBoxLayout())
{
_frame.setAttribute(Qt::WA_TransparentForMouseEvents);
populateConditionSelection(_conditions);
populateButtonSelection(_buttons);
_minX->setPrefix("Min X: ");
_minY->setPrefix("Min Y: ");
@@ -96,6 +149,8 @@ MacroConditionCursorEdit::MacroConditionCursorEdit(
QWidget::connect(_conditions, SIGNAL(currentIndexChanged(int)), this,
SLOT(ConditionChanged(int)));
QWidget::connect(_buttons, SIGNAL(currentIndexChanged(int)), this,
SLOT(ButtonChanged(int)));
QWidget::connect(_minX, SIGNAL(valueChanged(int)), this,
SLOT(MinXChanged(int)));
QWidget::connect(_minY, SIGNAL(valueChanged(int)), this,
@@ -109,6 +164,7 @@ MacroConditionCursorEdit::MacroConditionCursorEdit(
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
{"{{conditions}}", _conditions},
{"{{buttons}}", _buttons},
{"{{minX}}", _minX},
{"{{minY}}", _minY},
{"{{maxX}}", _maxX},
@@ -121,13 +177,12 @@ MacroConditionCursorEdit::MacroConditionCursorEdit(
placeWidgets(obs_module_text(
"AdvSceneSwitcher.condition.cursor.entry.line1"),
line1, widgetPlaceholders);
QHBoxLayout *line2 = new QHBoxLayout;
placeWidgets(obs_module_text(
"AdvSceneSwitcher.condition.cursor.entry.line2"),
line2, widgetPlaceholders);
_curentPosLayout, widgetPlaceholders);
QVBoxLayout *mainLayout = new QVBoxLayout;
mainLayout->addLayout(line1);
mainLayout->addLayout(line2);
mainLayout->addLayout(_curentPosLayout);
setLayout(mainLayout);
connect(&_timer, &QTimer::timeout, this,
@@ -146,9 +201,20 @@ void MacroConditionCursorEdit::ConditionChanged(int index)
}
std::lock_guard<std::mutex> lock(switcher->m);
_entryData->_condition = static_cast<CursorCondition>(index);
SetRegionSelectionVisible(_entryData->_condition ==
CursorCondition::REGION);
_entryData->_condition = static_cast<MacroConditionCursor::Condition>(
_conditions->itemData(index).toInt());
SetWidgetVisibility();
}
void MacroConditionCursorEdit::ButtonChanged(int index)
{
if (_loading || !_entryData) {
return;
}
std::lock_guard<std::mutex> lock(switcher->m);
_entryData->_button = static_cast<MacroConditionCursor::Button>(
_buttons->itemData(index).toInt());
}
void MacroConditionCursorEdit::MinXChanged(int pos)
@@ -216,20 +282,49 @@ void MacroConditionCursorEdit::ToggleFrame()
}
}
void MacroConditionCursorEdit::SetRegionSelectionVisible(bool visible)
void MacroConditionCursorEdit::SetWidgetVisibility()
{
_minX->setVisible(visible);
_minY->setVisible(visible);
_maxX->setVisible(visible);
_maxY->setVisible(visible);
_frameToggle->setVisible(visible);
const bool isRegionCondition = _entryData->_condition ==
MacroConditionCursor::Condition::REGION;
_minX->setVisible(isRegionCondition);
_minY->setVisible(isRegionCondition);
_maxX->setVisible(isRegionCondition);
_maxY->setVisible(isRegionCondition);
_frameToggle->setVisible(isRegionCondition);
setLayoutVisible(_curentPosLayout, isRegionCondition);
if (_frame.isVisible()) {
ToggleFrame();
}
_buttons->setVisible(_entryData->_condition ==
MacroConditionCursor::Condition::CLICK);
adjustSize();
}
static QRect getScreenUnionRect()
{
QPoint screenTopLeft(0, 0);
QPoint screenBottomRight(0, 0);
auto screens = QGuiApplication::screens();
for (const auto &screen : screens) {
const auto geo = screen->geometry();
if (geo.topLeft().x() < screenTopLeft.x()) {
screenTopLeft.setX(geo.topLeft().x());
}
if (geo.topLeft().y() < screenTopLeft.y()) {
screenTopLeft.setY(geo.topLeft().y());
}
if (geo.bottomRight().x() > screenBottomRight.x()) {
screenBottomRight.setX(geo.bottomRight().x());
}
if (geo.bottomRight().y() > screenBottomRight.y()) {
screenBottomRight.setY(geo.bottomRight().y());
}
}
return QRect(screenTopLeft, screenBottomRight);
}
void MacroConditionCursorEdit::SetupFrame()
{
_frame.setFrameStyle(QFrame::Box | QFrame::Plain);
@@ -240,9 +335,16 @@ void MacroConditionCursorEdit::SetupFrame()
_frame.setAttribute(Qt::WA_TranslucentBackground, true);
if (_entryData) {
_frame.setGeometry(_entryData->_minX, _entryData->_minY,
_entryData->_maxX - _entryData->_minX,
_entryData->_maxY - _entryData->_minY);
QRect rect(_entryData->_minX, _entryData->_minY,
_entryData->_maxX - _entryData->_minX,
_entryData->_maxY - _entryData->_minY);
if (rect.size().height() == 0 || rect.size().width() == 0) {
_frame.setGeometry(0, 0, 1, 1);
return;
}
_frame.setGeometry(getScreenUnionRect().intersected(rect));
}
}
@@ -253,10 +355,10 @@ void MacroConditionCursorEdit::UpdateEntryData()
}
_conditions->setCurrentIndex(static_cast<int>(_entryData->_condition));
_buttons->setCurrentIndex(static_cast<int>(_entryData->_button));
_minX->setValue(_entryData->_minX);
_minY->setValue(_entryData->_minY);
_maxX->setValue(_entryData->_maxX);
_maxY->setValue(_entryData->_maxY);
SetRegionSelectionVisible(_entryData->_condition ==
CursorCondition::REGION);
SetWidgetVisibility();
}

View File

@@ -7,27 +7,38 @@
#include <QTimer>
#include <QFrame>
enum class CursorCondition {
REGION,
MOVING,
};
class MacroConditionCursor : public MacroCondition {
public:
MacroConditionCursor(Macro *m) : MacroCondition(m) {}
MacroConditionCursor(Macro *m) : MacroCondition(m, true) {}
bool CheckCondition();
bool Save(obs_data_t *obj);
bool Save(obs_data_t *obj) const;
bool Load(obs_data_t *obj);
std::string GetId() { return id; };
std::string GetId() const { return id; };
static std::shared_ptr<MacroCondition> Create(Macro *m)
{
return std::make_shared<MacroConditionCursor>(m);
}
CursorCondition _condition = CursorCondition::REGION;
enum class Condition {
REGION,
MOVING,
CLICK,
};
enum class Button {
LEFT,
MIDDLE,
RIGHT,
};
Condition _condition = Condition::REGION;
Button _button = Button::LEFT;
int _minX = 0, _minY = 0, _maxX = 0, _maxY = 0;
private:
bool CheckClick();
std::chrono::high_resolution_clock::time_point _lastCheckTime{};
static bool _registered;
static const std::string id;
};
@@ -49,7 +60,8 @@ public:
}
private slots:
void ConditionChanged(int cond);
void ConditionChanged(int idx);
void ButtonChanged(int idx);
void MinXChanged(int pos);
void MinYChanged(int pos);
void MaxXChanged(int pos);
@@ -63,13 +75,15 @@ protected:
QSpinBox *_maxX;
QSpinBox *_maxY;
QComboBox *_conditions;
QComboBox *_buttons;
QPushButton *_frameToggle;
QLabel *_xPos;
QLabel *_yPos;
QHBoxLayout *_curentPosLayout;
std::shared_ptr<MacroConditionCursor> _entryData;
private:
void SetRegionSelectionVisible(bool);
void SetWidgetVisibility();
void SetupFrame();
QTimer _timer;
QFrame _frame;

View File

@@ -6,45 +6,61 @@
#include <QCalendarWidget>
const std::string MacroConditionDate::id = "date";
constexpr const char dateFormat[] = "yyyy MM dd hh mm ss";
bool MacroConditionDate::_registered = MacroConditionFactory::Register(
MacroConditionDate::id,
{MacroConditionDate::Create, MacroConditionDateEdit::Create,
"AdvSceneSwitcher.condition.date", false});
static std::map<DateCondition, std::string> dateConditionTypes = {
{DateCondition::AT, "AdvSceneSwitcher.condition.date.state.at"},
{DateCondition::AFTER, "AdvSceneSwitcher.condition.date.state.after"},
{DateCondition::BEFORE, "AdvSceneSwitcher.condition.date.state.before"},
{DateCondition::BETWEEN,
"AdvSceneSwitcher.condition.date.state.between"},
static std::map<MacroConditionDate::Condition, std::string> dateConditionTypes =
{
{MacroConditionDate::Condition::AT,
"AdvSceneSwitcher.condition.date.state.at"},
{MacroConditionDate::Condition::AFTER,
"AdvSceneSwitcher.condition.date.state.after"},
{MacroConditionDate::Condition::BEFORE,
"AdvSceneSwitcher.condition.date.state.before"},
{MacroConditionDate::Condition::BETWEEN,
"AdvSceneSwitcher.condition.date.state.between"},
{MacroConditionDate::Condition::PATTERN,
"AdvSceneSwitcher.condition.date.state.pattern"},
};
static std::map<DateCondition, std::string> weekDateConditionTypes = {
{DateCondition::AT, "AdvSceneSwitcher.condition.date.state.at"},
{DateCondition::AFTER, "AdvSceneSwitcher.condition.date.state.after"},
{DateCondition::BEFORE, "AdvSceneSwitcher.condition.date.state.before"},
static std::map<MacroConditionDate::Condition, std::string> weekConditionTypes =
{
{MacroConditionDate::Condition::AT,
"AdvSceneSwitcher.condition.date.state.at"},
{MacroConditionDate::Condition::AFTER,
"AdvSceneSwitcher.condition.date.state.after"},
{MacroConditionDate::Condition::BEFORE,
"AdvSceneSwitcher.condition.date.state.before"},
};
static std::map<DayOfWeekSelection, std::string> dayOfWeekNames = {
{DayOfWeekSelection::ANY, "AdvSceneSwitcher.condition.date.anyDay"},
{DayOfWeekSelection::MONDAY, "AdvSceneSwitcher.condition.date.monday"},
{DayOfWeekSelection::TUESDAY,
static std::map<MacroConditionDate::Day, std::string> dayOfWeekNames = {
{MacroConditionDate::Day::ANY,
"AdvSceneSwitcher.condition.date.anyDay"},
{MacroConditionDate::Day::MONDAY,
"AdvSceneSwitcher.condition.date.monday"},
{MacroConditionDate::Day::TUESDAY,
"AdvSceneSwitcher.condition.date.tuesday"},
{DayOfWeekSelection::WEDNESDAY,
{MacroConditionDate::Day::WEDNESDAY,
"AdvSceneSwitcher.condition.date.wednesday"},
{DayOfWeekSelection::THURSDAY,
{MacroConditionDate::Day::THURSDAY,
"AdvSceneSwitcher.condition.date.thursday"},
{DayOfWeekSelection::FRIDAY, "AdvSceneSwitcher.condition.date.friday"},
{DayOfWeekSelection::SATURDAY,
{MacroConditionDate::Day::FRIDAY,
"AdvSceneSwitcher.condition.date.friday"},
{MacroConditionDate::Day::SATURDAY,
"AdvSceneSwitcher.condition.date.saturday"},
{DayOfWeekSelection::SUNDAY, "AdvSceneSwitcher.condition.date.sunday"},
{MacroConditionDate::Day::SUNDAY,
"AdvSceneSwitcher.condition.date.sunday"},
};
bool MacroConditionDate::CheckDayOfWeek(int64_t msSinceLastCheck)
{
QDateTime cur = QDateTime::currentDateTime();
if (_dayOfWeek != DayOfWeekSelection::ANY &&
SetVariableValue(cur.toString().toStdString());
if (_dayOfWeek != Day::ANY &&
cur.date().dayOfWeek() != static_cast<int>(_dayOfWeek)) {
return false;
}
@@ -54,12 +70,12 @@ bool MacroConditionDate::CheckDayOfWeek(int64_t msSinceLastCheck)
_dateTime.setDate(cur.date());
switch (_condition) {
case DateCondition::AT:
case MacroConditionDate::Condition::AT:
return _dateTime <= cur &&
_dateTime >= cur.addMSecs(-msSinceLastCheck);
case DateCondition::AFTER:
case MacroConditionDate::Condition::AFTER:
return cur >= _dateTime;
case DateCondition::BEFORE:
case MacroConditionDate::Condition::BEFORE:
return cur <= _dateTime;
default:
break;
@@ -67,10 +83,57 @@ bool MacroConditionDate::CheckDayOfWeek(int64_t msSinceLastCheck)
return false;
}
bool MacroConditionDate::CheckBetween(const QDateTime &now)
{
// In the case of ignoring the date component treat the "left" value as
// the start of the time range and the "right" value as the end
if (_ignoreDate) {
if (_dateTime2 >= _dateTime) {
return now >= _dateTime && now <= _dateTime2;
}
// Assume that the end / start value should be shifted by 24h
// as otherwise the condition would never be true
return (now >= _dateTime && now <= _dateTime2.addDays(1)) ||
(now >= _dateTime.addDays(-1) && now <= _dateTime2);
}
if (_dateTime2 >= _dateTime) {
return now >= _dateTime && now <= _dateTime2;
}
return now >= _dateTime2 && now <= _dateTime;
}
bool MacroConditionDate::CheckPattern(QDateTime now,
int64_t secondsSinceLastCheck)
{
auto regex = QRegularExpression(QRegularExpression::anchoredPattern(
QString::fromStdString(_pattern)));
if (!regex.isValid()) {
return false;
}
// Silently ignore cases in which the scene switcher was not able to
// run for more than 60s
if (secondsSinceLastCheck > 60) {
secondsSinceLastCheck = 0;
}
for (int i = 0; i <= secondsSinceLastCheck; ++i) {
auto match = regex.match(now.toString(dateFormat));
if (match.hasMatch()) {
return true;
}
now = now.addSecs(-1);
}
return false;
}
bool MacroConditionDate::CheckRegularDate(int64_t msSinceLastCheck)
{
bool match = false;
QDateTime cur = QDateTime::currentDateTime();
SetVariableValue(cur.toString().toStdString());
if (_ignoreDate) {
_dateTime.setDate(cur.date());
_dateTime2.setDate(cur.date());
@@ -81,22 +144,21 @@ bool MacroConditionDate::CheckRegularDate(int64_t msSinceLastCheck)
}
switch (_condition) {
case DateCondition::AT:
case MacroConditionDate::Condition::AT:
match = _dateTime <= cur &&
_dateTime >= cur.addMSecs(-msSinceLastCheck);
break;
case DateCondition::AFTER:
case MacroConditionDate::Condition::AFTER:
match = cur >= _dateTime;
break;
case DateCondition::BEFORE:
case MacroConditionDate::Condition::BEFORE:
match = cur <= _dateTime;
break;
case DateCondition::BETWEEN:
if (_dateTime2 > _dateTime) {
match = cur >= _dateTime && cur <= _dateTime2;
} else {
match = cur >= _dateTime2 && cur <= _dateTime;
}
case MacroConditionDate::Condition::BETWEEN:
match = CheckBetween(cur);
break;
case MacroConditionDate::Condition::PATTERN:
match = CheckPattern(cur, msSinceLastCheck / 1000);
break;
default:
break;
@@ -123,7 +185,7 @@ bool MacroConditionDate::CheckCondition()
return CheckRegularDate(msSinceLastCheck);
}
bool MacroConditionDate::Save(obs_data_t *obj)
bool MacroConditionDate::Save(obs_data_t *obj) const
{
MacroCondition::Save(obj);
obs_data_set_int(obj, "dayOfWeek", static_cast<int>(_dayOfWeek));
@@ -140,16 +202,16 @@ bool MacroConditionDate::Save(obs_data_t *obj)
obs_data_set_bool(obj, "updateOnRepeat", _updateOnRepeat);
_duration.Save(obj);
obs_data_set_bool(obj, "dayOfWeekCheck", _dayOfWeekCheck);
obs_data_set_string(obj, "pattern", _pattern.c_str());
return true;
}
bool MacroConditionDate::Load(obs_data_t *obj)
{
MacroCondition::Load(obj);
_dayOfWeek = static_cast<DayOfWeekSelection>(
obs_data_get_int(obj, "dayOfWeek"));
_condition =
static_cast<DateCondition>(obs_data_get_int(obj, "condition"));
_dayOfWeek = static_cast<Day>(obs_data_get_int(obj, "dayOfWeek"));
_condition = static_cast<MacroConditionDate::Condition>(
obs_data_get_int(obj, "condition"));
_dateTime = QDateTime::fromString(
QString::fromStdString(obs_data_get_string(obj, "dateTime")));
_origDateTime = _dateTime;
@@ -159,29 +221,21 @@ bool MacroConditionDate::Load(obs_data_t *obj)
_ignoreDate = obs_data_get_bool(obj, "ignoreDate");
_ignoreTime = obs_data_get_bool(obj, "ignoreTime");
_repeat = obs_data_get_bool(obj, "repeat");
// TODO: Remove this fallback in future version
if (!obs_data_has_user_value(obj, "updateOnRepeat")) {
_updateOnRepeat = true;
} else {
_updateOnRepeat = obs_data_get_bool(obj, "updateOnRepeat");
}
_updateOnRepeat = obs_data_get_bool(obj, "updateOnRepeat");
_duration.Load(obj);
_dayOfWeekCheck = obs_data_get_bool(obj, "dayOfWeekCheck");
_pattern = obs_data_get_string(obj, "pattern");
// TODO: Remove this fallback in future version
if (!obs_data_has_user_value(obj, "dayOfWeekCheck")) {
_dayOfWeekCheck = false;
} else {
_dayOfWeekCheck = obs_data_get_bool(obj, "dayOfWeekCheck");
}
// The following code is used to avoid issues with old save files in
// which the simple date check did not support setting _condition
if (_dayOfWeekCheck && _condition == DateCondition::BETWEEN) {
_condition = DateCondition::AT;
if (_dayOfWeekCheck &&
_condition == MacroConditionDate::Condition::BETWEEN) {
_condition = MacroConditionDate::Condition::AT;
}
return true;
}
std::string MacroConditionDate::GetShortDesc()
std::string MacroConditionDate::GetShortDesc() const
{
if (_dayOfWeekCheck) {
auto it = dayOfWeekNames.find(_dayOfWeek);
@@ -195,6 +249,10 @@ std::string MacroConditionDate::GetShortDesc()
return dayName + " " +
_dateTime.time().toString().toStdString();
} else {
if (_condition == Condition::PATTERN) {
return "";
}
if (!_ignoreDate && !_ignoreTime) {
return GetDateTime1().toString().toStdString();
} else if (_ignoreDate && !_ignoreTime) {
@@ -230,17 +288,17 @@ void MacroConditionDate::SetTime2(const QTime &time)
_origDateTime2.setTime(time);
}
QDateTime MacroConditionDate::GetDateTime1()
QDateTime MacroConditionDate::GetDateTime1() const
{
return _updateOnRepeat ? _dateTime : _origDateTime;
return _repeat && _updateOnRepeat ? _dateTime : _origDateTime;
}
QDateTime MacroConditionDate::GetDateTime2()
QDateTime MacroConditionDate::GetDateTime2() const
{
return _updateOnRepeat ? _dateTime2 : _origDateTime2;
return _repeat && _updateOnRepeat ? _dateTime2 : _origDateTime2;
}
QDateTime MacroConditionDate::GetNextMatchDateTime()
QDateTime MacroConditionDate::GetNextMatchDateTime() const
{
return _dateTime;
}
@@ -261,7 +319,7 @@ static inline void populateConditionSelection(QComboBox *list)
static inline void populateWeekConditionSelection(QComboBox *list)
{
for (auto entry : weekDateConditionTypes) {
for (auto entry : weekConditionTypes) {
list->addItem(obs_module_text(entry.second.c_str()));
}
}
@@ -286,12 +344,15 @@ MacroConditionDateEdit::MacroConditionDateEdit(
_nextMatchDate(new QLabel()),
_updateOnRepeat(new QCheckBox()),
_duration(new DurationSelection()),
_pattern(new QLineEdit()),
_currentDate(new QLabel()),
_advancedSettingsTooggle(new QPushButton(obs_module_text(
"AdvSceneSwitcher.condition.date.showAdvancedSettings"))),
_simpleLayout(new QHBoxLayout()),
_advancedLayout(new QHBoxLayout()),
_repeatLayout(new QVBoxLayout()),
_repeatUpdateLayout(new QHBoxLayout())
_repeatUpdateLayout(new QHBoxLayout()),
_patternLayout(new QHBoxLayout())
{
_ignoreWeekTime->setToolTip(
obs_module_text("AdvSceneSwitcher.condition.date.ignoreTime"));
@@ -343,6 +404,8 @@ MacroConditionDateEdit::MacroConditionDateEdit(
SLOT(DurationUnitChanged(DurationUnit)));
QWidget::connect(_advancedSettingsTooggle, SIGNAL(clicked()), this,
SLOT(AdvancedSettingsToggleClicked()));
QWidget::connect(_pattern, SIGNAL(editingFinished()), this,
SLOT(PatternChanged()));
populateDaySelection(_dayOfWeek);
populateConditionSelection(_condition);
@@ -364,6 +427,8 @@ MacroConditionDateEdit::MacroConditionDateEdit(
{"{{repeat}}", _repeat},
{"{{updateOnRepeat}}", _updateOnRepeat},
{"{{duration}}", _duration},
{"{{pattern}}", _pattern},
{"{{currentDate}}", _currentDate},
};
placeWidgets(
obs_module_text("AdvSceneSwitcher.condition.date.entry.simple"),
@@ -371,14 +436,17 @@ MacroConditionDateEdit::MacroConditionDateEdit(
placeWidgets(obs_module_text(
"AdvSceneSwitcher.condition.date.entry.advanced"),
_advancedLayout, widgetPlaceholders);
auto repeatLayout = new QHBoxLayout;
placeWidgets(
obs_module_text("AdvSceneSwitcher.condition.date.entry.repeat"),
repeatLayout, widgetPlaceholders);
placeWidgets(
obs_module_text(
"AdvSceneSwitcher.condition.date.entry.updateOnRepeat"),
_repeatUpdateLayout, widgetPlaceholders);
auto repeatLayout = new QHBoxLayout;
placeWidgets(
obs_module_text("AdvSceneSwitcher.condition.date.entry.repeat"),
repeatLayout, widgetPlaceholders);
placeWidgets(obs_module_text(
"AdvSceneSwitcher.condition.date.entry.pattern"),
_patternLayout, widgetPlaceholders);
_repeatLayout->addLayout(repeatLayout);
_repeatLayout->addWidget(_nextMatchDate);
_repeatLayout->addLayout(_repeatUpdateLayout);
@@ -386,6 +454,7 @@ MacroConditionDateEdit::MacroConditionDateEdit(
auto *mainLayout = new QVBoxLayout;
mainLayout->addLayout(_simpleLayout);
mainLayout->addLayout(_advancedLayout);
mainLayout->addLayout(_patternLayout);
mainLayout->addLayout(_repeatLayout);
auto *_advancedToggleLayout = new QHBoxLayout;
_advancedToggleLayout->addWidget(_advancedSettingsTooggle);
@@ -393,10 +462,11 @@ MacroConditionDateEdit::MacroConditionDateEdit(
mainLayout->addLayout(_advancedToggleLayout);
setLayout(mainLayout);
_timer.setInterval(3000);
QWidget::connect(&_timer, SIGNAL(timeout()), this,
SLOT(ShowNextMatch()));
_timer.start();
QWidget::connect(&_timer, SIGNAL(timeout()), this,
SLOT(UpdateCurrentTime()));
_timer.start(1000);
_entryData = entryData;
UpdateEntryData();
@@ -410,7 +480,7 @@ void MacroConditionDateEdit::DayOfWeekChanged(int day)
}
std::lock_guard<std::mutex> lock(switcher->m);
_entryData->_dayOfWeek = static_cast<DayOfWeekSelection>(day);
_entryData->_dayOfWeek = static_cast<MacroConditionDate::Day>(day);
emit HeaderInfoChanged(
QString::fromStdString(_entryData->GetShortDesc()));
}
@@ -422,9 +492,11 @@ void MacroConditionDateEdit::ConditionChanged(int cond)
}
std::lock_guard<std::mutex> lock(switcher->m);
_entryData->_condition = static_cast<DateCondition>(cond);
ShowSecondDateSelection(_entryData->_condition ==
DateCondition::BETWEEN);
_entryData->_condition =
static_cast<MacroConditionDate::Condition>(cond);
SetWidgetStatus();
emit HeaderInfoChanged(
QString::fromStdString(_entryData->GetShortDesc()));
}
void MacroConditionDateEdit::DateChanged(const QDate &date)
@@ -545,7 +617,7 @@ void MacroConditionDateEdit::AdvancedSettingsToggleClicked()
{
std::lock_guard<std::mutex> lock(switcher->m);
_entryData->_dayOfWeekCheck = !_entryData->_dayOfWeekCheck;
_entryData->_condition = DateCondition::AT;
_entryData->_condition = MacroConditionDate::Condition::AT;
}
_condition->setCurrentIndex(0);
_weekCondition->setCurrentIndex(0);
@@ -566,6 +638,20 @@ void MacroConditionDateEdit::ShowNextMatch()
format.arg(_entryData->GetNextMatchDateTime().toString()));
}
void MacroConditionDateEdit::PatternChanged()
{
if (_loading || !_entryData) {
return;
}
_entryData->_pattern = _pattern->text().toStdString();
}
void MacroConditionDateEdit::UpdateCurrentTime()
{
QDateTime dateTime = dateTime.currentDateTime();
_currentDate->setText(dateTime.toString(dateFormat));
}
void MacroConditionDateEdit::UpdateEntryData()
{
if (!_entryData) {
@@ -576,56 +662,93 @@ void MacroConditionDateEdit::UpdateEntryData()
_dayOfWeek->setCurrentIndex(
static_cast<int>(static_cast<int>(_entryData->_dayOfWeek)));
_ignoreWeekTime->setChecked(!_entryData->_ignoreTime);
_weekTime->setDateTime(_entryData->GetDateTime1());
_weekTime->setTime(_entryData->GetDateTime1().time());
_condition->setCurrentIndex(static_cast<int>(_entryData->_condition));
_date->setDateTime(_entryData->GetDateTime1());
_time->setDateTime(_entryData->GetDateTime1());
_date2->setDateTime(_entryData->GetDateTime2());
_time2->setDateTime(_entryData->GetDateTime2());
_date->setDate(_entryData->GetDateTime1().date());
_time->setTime(_entryData->GetDateTime1().time());
_date2->setDate(_entryData->GetDateTime2().date());
_time2->setTime(_entryData->GetDateTime2().time());
_ignoreDate->setChecked(!_entryData->_ignoreDate);
_ignoreTime->setChecked(!_entryData->_ignoreTime);
_repeat->setChecked(_entryData->_repeat);
_updateOnRepeat->setChecked(_entryData->_updateOnRepeat);
_duration->SetDuration(_entryData->_duration);
_duration->setDisabled(!_entryData->_repeat);
_pattern->setText(QString::fromStdString(_entryData->_pattern));
ShowNextMatch();
SetWidgetStatus();
}
void MacroConditionDateEdit::SetupSimpleView()
{
setLayoutVisible(_simpleLayout, true);
setLayoutVisible(_advancedLayout, false);
setLayoutVisible(_patternLayout, false);
setLayoutVisible(_repeatLayout, false);
setLayoutVisible(_repeatUpdateLayout, false);
_weekTime->setDisabled(_entryData->_ignoreTime);
_weekCondition->setDisabled(_entryData->_ignoreTime);
const QSignalBlocker b(_weekTime);
_weekTime->setTime(_entryData->GetDateTime1().time());
_advancedSettingsTooggle->setText(obs_module_text(
"AdvSceneSwitcher.condition.date.showAdvancedSettings"));
}
void MacroConditionDateEdit::SetupAdvancedView()
{
setLayoutVisible(_simpleLayout, false);
setLayoutVisible(_advancedLayout, true);
setLayoutVisible(_patternLayout, false);
setLayoutVisible(_repeatLayout, true);
setLayoutVisible(_repeatUpdateLayout, _entryData->_repeat);
_nextMatchDate->setVisible(_entryData->_repeat);
_date->setDisabled(_entryData->_ignoreDate);
_date2->setDisabled(_entryData->_ignoreDate);
_time->setDisabled(_entryData->_ignoreTime);
_time2->setDisabled(_entryData->_ignoreTime);
ShowFirstDateSelection(true);
ShowSecondDateSelection(_entryData->_condition ==
MacroConditionDate::Condition::BETWEEN);
_advancedSettingsTooggle->setText(obs_module_text(
"AdvSceneSwitcher.condition.date.showSimpleSettings"));
}
void MacroConditionDateEdit::SetupPatternView()
{
setLayoutVisible(_simpleLayout, false);
setLayoutVisible(_advancedLayout, true);
setLayoutVisible(_patternLayout, true);
setLayoutVisible(_repeatLayout, false);
setLayoutVisible(_repeatUpdateLayout, false);
ShowFirstDateSelection(false);
ShowSecondDateSelection(false);
}
void MacroConditionDateEdit::SetWidgetStatus()
{
if (!_entryData) {
return;
}
setLayoutVisible(_simpleLayout, _entryData->_dayOfWeekCheck);
setLayoutVisible(_advancedLayout, !_entryData->_dayOfWeekCheck);
setLayoutVisible(_repeatLayout, !_entryData->_dayOfWeekCheck);
_nextMatchDate->setVisible(!_entryData->_dayOfWeekCheck &&
_entryData->_repeat);
setLayoutVisible(_repeatUpdateLayout,
!_entryData->_dayOfWeekCheck && _entryData->_repeat);
if (_entryData->_dayOfWeekCheck) {
_advancedSettingsTooggle->setText(obs_module_text(
"AdvSceneSwitcher.condition.date.showAdvancedSettings"));
_weekTime->setDisabled(_entryData->_ignoreTime);
_weekCondition->setDisabled(_entryData->_ignoreTime);
SetupSimpleView();
} else if (_entryData->_condition ==
MacroConditionDate::Condition::PATTERN) {
SetupPatternView();
} else {
_advancedSettingsTooggle->setText(obs_module_text(
"AdvSceneSwitcher.condition.date.showSimpleSettings"));
_date->setDisabled(_entryData->_ignoreDate);
_date2->setDisabled(_entryData->_ignoreDate);
_time->setDisabled(_entryData->_ignoreTime);
_time2->setDisabled(_entryData->_ignoreTime);
ShowSecondDateSelection(_entryData->_condition ==
DateCondition::BETWEEN);
SetupAdvancedView();
}
const QSignalBlocker b(_weekTime);
_weekTime->setTime(_entryData->GetDateTime1().time());
adjustSize();
}
void MacroConditionDateEdit::ShowFirstDateSelection(bool visible)
{
_ignoreDate->setVisible(visible);
_date->setVisible(visible);
_ignoreTime->setVisible(visible);
_time->setVisible(visible);
}
void MacroConditionDateEdit::ShowSecondDateSelection(bool visible)
{
_separator->setVisible(visible);

View File

@@ -7,32 +7,14 @@
#include <QComboBox>
#include <QTimer>
enum class DateCondition {
AT,
AFTER,
BEFORE,
BETWEEN,
};
enum class DayOfWeekSelection {
ANY = 0,
MONDAY,
TUESDAY,
WEDNESDAY,
THURSDAY,
FRIDAY,
SATURDAY,
SUNDAY,
};
class MacroConditionDate : public MacroCondition {
public:
MacroConditionDate(Macro *m) : MacroCondition(m) {}
MacroConditionDate(Macro *m) : MacroCondition(m, true) {}
bool CheckCondition();
bool Save(obs_data_t *obj);
bool Save(obs_data_t *obj) const;
bool Load(obs_data_t *obj);
std::string GetShortDesc();
std::string GetId() { return id; };
std::string GetShortDesc() const;
std::string GetId() const { return id; };
static std::shared_ptr<MacroCondition> Create(Macro *m)
{
return std::make_shared<MacroConditionDate>(m);
@@ -42,22 +24,44 @@ public:
void SetDate2(const QDate &date);
void SetTime1(const QTime &time);
void SetTime2(const QTime &time);
QDateTime GetDateTime1();
QDateTime GetDateTime2();
QDateTime GetNextMatchDateTime();
QDateTime GetDateTime1() const;
QDateTime GetDateTime2() const;
QDateTime GetNextMatchDateTime() const;
DayOfWeekSelection _dayOfWeek = DayOfWeekSelection::ANY;
enum class Day {
ANY = 0,
MONDAY,
TUESDAY,
WEDNESDAY,
THURSDAY,
FRIDAY,
SATURDAY,
SUNDAY,
};
enum class Condition {
AT,
AFTER,
BEFORE,
BETWEEN,
PATTERN,
};
Day _dayOfWeek = Day::ANY;
bool _ignoreDate = false;
bool _ignoreTime = false;
bool _repeat = false;
bool _updateOnRepeat = true;
Duration _duration;
DateCondition _condition = DateCondition::AT;
bool _dayOfWeekCheck = false;
Condition _condition = Condition::AT;
bool _dayOfWeekCheck = true;
std::string _pattern = ".... .. .. .. .. ..";
private:
bool CheckDayOfWeek(int64_t);
bool CheckRegularDate(int64_t);
bool CheckBetween(const QDateTime &now);
bool CheckPattern(QDateTime now, int64_t secondsSinceLastCheck);
QDateTime _dateTime = QDateTime::currentDateTime();
QDateTime _dateTime2 = QDateTime::currentDateTime();
@@ -100,6 +104,8 @@ private slots:
void DurationUnitChanged(DurationUnit unit);
void AdvancedSettingsToggleClicked();
void ShowNextMatch();
void UpdateCurrentTime();
void PatternChanged();
signals:
void HeaderInfoChanged(const QString &);
@@ -121,17 +127,24 @@ protected:
QLabel *_nextMatchDate;
QCheckBox *_updateOnRepeat;
DurationSelection *_duration;
QLineEdit *_pattern;
QLabel *_currentDate;
QPushButton *_advancedSettingsTooggle;
QHBoxLayout *_simpleLayout;
QHBoxLayout *_advancedLayout;
QVBoxLayout *_repeatLayout;
QHBoxLayout *_repeatUpdateLayout;
QHBoxLayout *_patternLayout;
std::shared_ptr<MacroConditionDate> _entryData;
private:
void SetupSimpleView();
void SetupAdvancedView();
void SetupPatternView();
void SetWidgetStatus();
void ShowFirstDateSelection(bool visible);
void ShowSecondDateSelection(bool visible);
QTimer _timer;
bool _loading = true;

View File

@@ -354,9 +354,9 @@ void MacroConditionEdit::DurationUnitChanged(DurationUnit unit)
(*_entryData)->SetDurationUnit(unit);
}
MacroSegment *MacroConditionEdit::Data()
std::shared_ptr<MacroSegment> MacroConditionEdit::Data()
{
return _entryData->get();
return *_entryData;
}
void AdvSceneSwitcher::AddMacroCondition(int idx)
@@ -380,7 +380,7 @@ void AdvSceneSwitcher::AddMacroCondition(int idx)
logic = macro->Conditions().at(idx - 1)->GetLogicType();
}
} else {
MacroConditionScene temp(macro);
MacroConditionScene temp(macro.get());
id = temp.GetId();
logic = LogicType::ROOT_NONE;
}
@@ -388,7 +388,7 @@ void AdvSceneSwitcher::AddMacroCondition(int idx)
std::lock_guard<std::mutex> lock(switcher->m);
auto cond = macro->Conditions().emplace(
macro->Conditions().begin() + idx,
MacroConditionFactory::Create(id, macro));
MacroConditionFactory::Create(id, macro.get()));
if (idx - 1 >= 0) {
auto data = obs_data_create();
macro->Conditions().at(idx - 1)->Save(data);
@@ -404,6 +404,7 @@ void AdvSceneSwitcher::AddMacroCondition(int idx)
SetConditionData(*macro);
}
HighlightCondition(idx);
emit(MacroSegmentOrderChanged());
}
void AdvSceneSwitcher::on_conditionAdd_clicked()
@@ -414,10 +415,6 @@ void AdvSceneSwitcher::on_conditionAdd_clicked()
}
if (currentConditionIdx == -1) {
auto macro = getSelectedMacro();
if (!macro) {
return;
}
AddMacroCondition((int)macro->Conditions().size());
} else {
AddMacroCondition(currentConditionIdx + 1);
@@ -455,6 +452,7 @@ void AdvSceneSwitcher::RemoveMacroCondition(int idx)
}
MacroConditionSelectionChanged(-1);
lastInteracted = MacroSection::CONDITIONS;
emit(MacroSegmentOrderChanged());
}
void AdvSceneSwitcher::on_conditionRemove_clicked()
@@ -522,6 +520,7 @@ void AdvSceneSwitcher::SwapConditions(Macro *m, int pos1, int pos2)
SetConditionData(*m);
widget2->SetRootNode(root);
widget1->SetRootNode(false);
emit(MacroSegmentOrderChanged());
}
void AdvSceneSwitcher::MoveMacroConditionUp(int idx)
@@ -535,7 +534,7 @@ void AdvSceneSwitcher::MoveMacroConditionUp(int idx)
return;
}
SwapConditions(macro, idx, idx - 1);
SwapConditions(macro.get(), idx, idx - 1);
HighlightCondition(idx - 1);
}
@@ -550,7 +549,7 @@ void AdvSceneSwitcher::MoveMacroConditionDown(int idx)
return;
}
SwapConditions(macro, idx, idx + 1);
SwapConditions(macro.get(), idx, idx + 1);
HighlightCondition(idx + 1);
}
@@ -618,4 +617,5 @@ void AdvSceneSwitcher::MacroConditionReorder(int to, int from)
SetConditionData(*macro);
}
HighlightCondition(to);
emit(MacroSegmentOrderChanged());
}

View File

@@ -75,7 +75,7 @@ private slots:
private:
void SetLogicSelection();
MacroSegment *Data();
std::shared_ptr<MacroSegment> Data();
QComboBox *_logicSelection;
QComboBox *_conditionSelection;

View File

@@ -66,6 +66,7 @@ bool MacroConditionFile::MatchFileContent(QString &filedata)
bool MacroConditionFile::CheckRemoteFileContent()
{
std::string data = getRemoteData(_file);
SetVariableValue(data);
QString qdata = QString::fromStdString(data);
return MatchFileContent(qdata);
}
@@ -86,6 +87,7 @@ bool MacroConditionFile::CheckLocalFileContent()
}
QString filedata = QTextStream(&file).readAll();
SetVariableValue(filedata.toStdString());
bool match = MatchFileContent(filedata);
file.close();
@@ -126,6 +128,7 @@ bool MacroConditionFile::CheckChangeDate()
QFile file(QString::fromStdString(_file));
QDateTime newLastMod = QFileInfo(file).lastModified();
SetVariableValue(newLastMod.toString().toStdString());
const bool dateChanged = _lastMod != newLastMod;
_lastMod = newLastMod;
return dateChanged;
@@ -133,29 +136,38 @@ bool MacroConditionFile::CheckChangeDate()
bool MacroConditionFile::CheckCondition()
{
bool ret = false;
switch (_condition) {
case MacroConditionFile::ConditionType::MATCH:
if (_fileType == FileType::REMOTE) {
return CheckRemoteFileContent();
ret = CheckRemoteFileContent();
break;
}
return CheckLocalFileContent();
ret = CheckLocalFileContent();
break;
case MacroConditionFile::ConditionType::CONTENT_CHANGE:
return CheckChangeContent();
ret = CheckChangeContent();
break;
case MacroConditionFile::ConditionType::DATE_CHANGE:
return CheckChangeDate();
ret = CheckChangeDate();
break;
default:
break;
}
return false;
if (GetVariableValue().empty()) {
SetVariableValue(ret ? "true" : "false");
}
return ret;
}
bool MacroConditionFile::Save(obs_data_t *obj)
bool MacroConditionFile::Save(obs_data_t *obj) const
{
MacroCondition::Save(obj);
_regex.Save(obj);
obs_data_set_string(obj, "file", _file.c_str());
obs_data_set_string(obj, "text", _text.c_str());
_text.Save(obj, "text");
obs_data_set_int(obj, "fileType", static_cast<int>(_fileType));
obs_data_set_int(obj, "condition", static_cast<int>(_condition));
obs_data_set_bool(obj, "useTime", _useTime);
@@ -173,7 +185,7 @@ bool MacroConditionFile::Load(obs_data_t *obj)
obs_data_get_bool(obj, "useRegex"));
}
_file = obs_data_get_string(obj, "file");
_text = obs_data_get_string(obj, "text");
_text.Load(obj, "text");
_fileType = static_cast<FileType>(obs_data_get_int(obj, "fileType"));
_condition =
static_cast<ConditionType>(obs_data_get_int(obj, "condition"));
@@ -182,15 +194,16 @@ bool MacroConditionFile::Load(obs_data_t *obj)
return true;
}
std::string MacroConditionFile::GetShortDesc()
std::string MacroConditionFile::GetShortDesc() const
{
return _file;
}
static void populateFileTypes(QComboBox *list)
{
list->addItem(obs_module_text("AdvSceneSwitcher.fileTab.local"));
list->addItem(obs_module_text("AdvSceneSwitcher.fileTab.remote"));
list->addItem(obs_module_text("AdvSceneSwitcher.condition.file.local"));
list->addItem(
obs_module_text("AdvSceneSwitcher.condition.file.remote"));
}
static void populateConditions(QComboBox *list)
@@ -209,7 +222,7 @@ MacroConditionFileEdit::MacroConditionFileEdit(
_fileTypes(new QComboBox()),
_conditions(new QComboBox()),
_filePath(new FileSelection()),
_matchText(new ResizingPlainTextEdit(this)),
_matchText(new VariableTextEdit(this)),
_regex(new RegexConfigWidget(parent)),
_checkModificationDate(new QCheckBox(obs_module_text(
"AdvSceneSwitcher.fileTab.checkfileContentTime"))),
@@ -248,6 +261,9 @@ MacroConditionFileEdit::MacroConditionFileEdit(
QHBoxLayout *line1Layout = new QHBoxLayout;
QHBoxLayout *line2Layout = new QHBoxLayout;
QHBoxLayout *line3Layout = new QHBoxLayout;
line1Layout->setContentsMargins(0, 0, 0, 0);
line2Layout->setContentsMargins(0, 0, 0, 0);
line3Layout->setContentsMargins(0, 0, 0, 0);
placeWidgets(
obs_module_text("AdvSceneSwitcher.condition.file.entry.line1"),
line1Layout, widgetPlaceholders);
@@ -277,7 +293,7 @@ void MacroConditionFileEdit::UpdateEntryData()
_fileTypes->setCurrentIndex(static_cast<int>(_entryData->_fileType));
_conditions->setCurrentIndex(static_cast<int>(_entryData->_condition));
_filePath->SetPath(QString::fromStdString(_entryData->_file));
_matchText->setPlainText(QString::fromStdString(_entryData->_text));
_matchText->setPlainText(_entryData->_text);
_regex->SetRegexConfig(_entryData->_regex);
_checkModificationDate->setChecked(_entryData->_useTime);
_checkFileContent->setChecked(_entryData->_onlyMatchIfChanged);

View File

@@ -1,7 +1,7 @@
#pragma once
#include "macro.hpp"
#include "file-selection.hpp"
#include "resizing-text-edit.hpp"
#include "variable-text-edit.hpp"
#include "regex-config.hpp"
#include <QWidget>
@@ -13,12 +13,12 @@
class MacroConditionFile : public MacroCondition {
public:
MacroConditionFile(Macro *m) : MacroCondition(m) {}
MacroConditionFile(Macro *m) : MacroCondition(m, true) {}
bool CheckCondition();
bool Save(obs_data_t *obj);
bool Save(obs_data_t *obj) const;
bool Load(obs_data_t *obj);
std::string GetShortDesc();
std::string GetId() { return id; };
std::string GetShortDesc() const;
std::string GetId() const { return id; };
static std::shared_ptr<MacroCondition> Create(Macro *m)
{
return std::make_shared<MacroConditionFile>(m);
@@ -36,7 +36,8 @@ public:
};
std::string _file = obs_module_text("AdvSceneSwitcher.enterPath");
std::string _text = obs_module_text("AdvSceneSwitcher.enterText");
VariableResolvingString _text =
obs_module_text("AdvSceneSwitcher.enterText");
FileType _fileType = FileType::LOCAL;
ConditionType _condition = ConditionType::MATCH;
RegexConfig _regex;
@@ -89,7 +90,7 @@ protected:
QComboBox *_fileTypes;
QComboBox *_conditions;
FileSelection *_filePath;
ResizingPlainTextEdit *_matchText;
VariableTextEdit *_matchText;
RegexConfigWidget *_regex;
QCheckBox *_checkModificationDate;
QCheckBox *_checkFileContent;

View File

@@ -12,18 +12,33 @@ bool MacroConditionFilter::_registered = MacroConditionFactory::Register(
{MacroConditionFilter::Create, MacroConditionFilterEdit::Create,
"AdvSceneSwitcher.condition.filter"});
static std::map<FilterCondition, std::string> filterConditionTypes = {
{FilterCondition::ENABLED,
"AdvSceneSwitcher.condition.filter.type.active"},
{FilterCondition::DISABLED,
"AdvSceneSwitcher.condition.filter.type.showing"},
{FilterCondition::SETTINGS,
"AdvSceneSwitcher.condition.filter.type.settings"},
static std::map<MacroConditionFilter::Condition, std::string>
filterConditionTypes = {
{MacroConditionFilter::Condition::ENABLED,
"AdvSceneSwitcher.condition.filter.type.active"},
{MacroConditionFilter::Condition::DISABLED,
"AdvSceneSwitcher.condition.filter.type.showing"},
{MacroConditionFilter::Condition::SETTINGS,
"AdvSceneSwitcher.condition.filter.type.settings"},
};
void MacroConditionFilter::ResolveVariables()
{
if (_source.GetType() == SourceSelection::Type::SOURCE) {
return;
}
std::string name = GetWeakSourceName(_filter);
if (!name.empty()) {
_filterName = name;
}
_filter = GetWeakFilterByName(_source.GetSource(), _filterName.c_str());
}
bool MacroConditionFilter::CheckCondition()
{
if (!_source) {
ResolveVariables();
if (!_source.GetSource()) {
return false;
}
@@ -31,14 +46,17 @@ bool MacroConditionFilter::CheckCondition()
auto s = obs_weak_source_get_source(_filter);
switch (_condition) {
case FilterCondition::ENABLED:
case Condition::ENABLED:
ret = obs_source_enabled(s);
break;
case FilterCondition::DISABLED:
case Condition::DISABLED:
ret = !obs_source_enabled(s);
break;
case FilterCondition::SETTINGS:
case Condition::SETTINGS:
ret = compareSourceSettings(_filter, _settings, _regex);
if (IsReferencedInVars()) {
SetVariableValue(getSourceSettings(_filter));
}
break;
default:
break;
@@ -46,16 +64,20 @@ bool MacroConditionFilter::CheckCondition()
obs_source_release(s);
if (GetVariableValue().empty()) {
SetVariableValue(ret ? "true" : "false");
}
return ret;
}
bool MacroConditionFilter::Save(obs_data_t *obj)
bool MacroConditionFilter::Save(obs_data_t *obj) const
{
MacroCondition::Save(obj);
obs_data_set_string(obj, "source", GetWeakSourceName(_source).c_str());
obs_data_set_string(obj, "filter", GetWeakSourceName(_filter).c_str());
_source.Save(obj);
obs_data_set_string(obj, "filter", _filterName.c_str());
obs_data_set_int(obj, "condition", static_cast<int>(_condition));
obs_data_set_string(obj, "settings", _settings.c_str());
_settings.Save(obj, "settings");
_regex.Save(obj);
return true;
}
@@ -63,13 +85,12 @@ bool MacroConditionFilter::Save(obs_data_t *obj)
bool MacroConditionFilter::Load(obs_data_t *obj)
{
MacroCondition::Load(obj);
const char *sourceName = obs_data_get_string(obj, "source");
_source = GetWeakSourceByName(sourceName);
const char *filterName = obs_data_get_string(obj, "filter");
_filter = GetWeakFilterByQString(_source, filterName);
_condition = static_cast<FilterCondition>(
obs_data_get_int(obj, "condition"));
_settings = obs_data_get_string(obj, "settings");
_source.Load(obj);
_filterName = obs_data_get_string(obj, "filter");
_filter = GetWeakFilterByQString(_source.GetSource(),
_filterName.c_str());
_condition = static_cast<Condition>(obs_data_get_int(obj, "condition"));
_settings.Load(obj, "settings");
_regex.Load(obj);
// TOOD: remove in future version
if (obs_data_has_user_value(obj, "regex")) {
@@ -79,11 +100,10 @@ bool MacroConditionFilter::Load(obs_data_t *obj)
return true;
}
std::string MacroConditionFilter::GetShortDesc()
std::string MacroConditionFilter::GetShortDesc() const
{
if (_filter && _source) {
return GetWeakSourceName(_source) + " - " +
GetWeakSourceName(_filter);
if (_filter && !_source.ToString().empty()) {
return _source.ToString() + " - " + GetWeakSourceName(_filter);
}
return "";
}
@@ -98,20 +118,23 @@ static inline void populateConditionSelection(QComboBox *list)
MacroConditionFilterEdit::MacroConditionFilterEdit(
QWidget *parent, std::shared_ptr<MacroConditionFilter> entryData)
: QWidget(parent),
_sources(new QComboBox()),
_sources(new SourceSelectionWidget(this, QStringList(), true)),
_filters(new QComboBox()),
_conditions(new QComboBox()),
_getSettings(new QPushButton(obs_module_text(
"AdvSceneSwitcher.condition.filter.getSettings"))),
_settings(new ResizingPlainTextEdit(this)),
_settings(new VariableTextEdit(this)),
_regex(new RegexConfigWidget(parent))
{
_filters->setSizeAdjustPolicy(QComboBox::AdjustToContents);
populateConditionSelection(_conditions);
populateSourcesWithFilterSelection(_sources);
auto sources = GetSourcesWithFilterNames();
sources.sort();
_sources->SetSourceNameList(sources);
QWidget::connect(_sources, SIGNAL(currentTextChanged(const QString &)),
this, SLOT(SourceChanged(const QString &)));
QWidget::connect(_sources,
SIGNAL(SourceChanged(const SourceSelection &)), this,
SLOT(SourceChanged(const SourceSelection &)));
QWidget::connect(_filters, SIGNAL(currentTextChanged(const QString &)),
this, SLOT(FilterChanged(const QString &)));
QWidget::connect(_conditions, SIGNAL(currentIndexChanged(int)), this,
@@ -152,17 +175,17 @@ MacroConditionFilterEdit::MacroConditionFilterEdit(
_loading = false;
}
void MacroConditionFilterEdit::SourceChanged(const QString &text)
void MacroConditionFilterEdit::SourceChanged(const SourceSelection &source)
{
if (_loading || !_entryData) {
return;
}
{
std::lock_guard<std::mutex> lock(switcher->m);
_entryData->_source = GetWeakSourceByQString(text);
_entryData->_source = source;
}
_filters->clear();
populateFilterSelection(_filters, _entryData->_source);
populateFilterSelection(_filters, _entryData->_source.GetSource());
_filters->adjustSize();
}
@@ -173,7 +196,9 @@ void MacroConditionFilterEdit::FilterChanged(const QString &text)
}
std::lock_guard<std::mutex> lock(switcher->m);
_entryData->_filter = GetWeakFilterByQString(_entryData->_source, text);
_entryData->_filterName = text.toStdString();
_entryData->_filter =
GetWeakFilterByQString(_entryData->_source.GetSource(), text);
emit HeaderInfoChanged(
QString::fromStdString(_entryData->GetShortDesc()));
}
@@ -185,14 +210,15 @@ void MacroConditionFilterEdit::ConditionChanged(int index)
}
std::lock_guard<std::mutex> lock(switcher->m);
_entryData->_condition = static_cast<FilterCondition>(index);
_entryData->_condition =
static_cast<MacroConditionFilter::Condition>(index);
SetSettingsSelectionVisible(_entryData->_condition ==
FilterCondition::SETTINGS);
MacroConditionFilter::Condition::SETTINGS);
}
void MacroConditionFilterEdit::GetSettingsClicked()
{
if (_loading || !_entryData || !_entryData->_source) {
if (_loading || !_entryData || !_entryData->_source.GetSource()) {
return;
}
@@ -243,16 +269,15 @@ void MacroConditionFilterEdit::UpdateEntryData()
return;
}
_sources->setCurrentText(
GetWeakSourceName(_entryData->_source).c_str());
populateFilterSelection(_filters, _entryData->_source);
_sources->SetSource(_entryData->_source);
populateFilterSelection(_filters, _entryData->_source.GetSource());
_filters->setCurrentText(
GetWeakSourceName(_entryData->_filter).c_str());
_conditions->setCurrentIndex(static_cast<int>(_entryData->_condition));
_settings->setPlainText(QString::fromStdString(_entryData->_settings));
_settings->setPlainText(_entryData->_settings);
_regex->SetRegexConfig(_entryData->_regex);
SetSettingsSelectionVisible(_entryData->_condition ==
FilterCondition::SETTINGS);
MacroConditionFilter::Condition::SETTINGS);
adjustSize();
updateGeometry();

View File

@@ -1,40 +1,46 @@
#pragma once
#include "macro.hpp"
#include "resizing-text-edit.hpp"
#include "variable-text-edit.hpp"
#include "regex-config.hpp"
#include "source-selection.hpp"
#include <QComboBox>
#include <QPushButton>
#include <QCheckBox>
enum class FilterCondition {
ENABLED,
DISABLED,
SETTINGS,
};
class MacroConditionFilter : public MacroCondition {
public:
MacroConditionFilter(Macro *m) : MacroCondition(m) {}
MacroConditionFilter(Macro *m) : MacroCondition(m, true) {}
bool CheckCondition();
bool Save(obs_data_t *obj);
bool Save(obs_data_t *obj) const;
bool Load(obs_data_t *obj);
std::string GetShortDesc();
std::string GetId() { return id; };
std::string GetShortDesc() const;
std::string GetId() const { return id; };
static std::shared_ptr<MacroCondition> Create(Macro *m)
{
return std::make_shared<MacroConditionFilter>(m);
}
OBSWeakSource _source;
enum class Condition {
ENABLED,
DISABLED,
SETTINGS,
};
SourceSelection _source;
OBSWeakSource _filter;
FilterCondition _condition = FilterCondition::ENABLED;
std::string _settings = "";
Condition _condition = Condition::ENABLED;
VariableResolvingString _settings = "";
RegexConfig _regex;
private:
void ResolveVariables();
std::string _filterName = "";
static bool _registered;
static const std::string id;
friend class MacroConditionFilterEdit;
};
class MacroConditionFilterEdit : public QWidget {
@@ -54,7 +60,7 @@ public:
}
private slots:
void SourceChanged(const QString &text);
void SourceChanged(const SourceSelection &);
void FilterChanged(const QString &text);
void ConditionChanged(int cond);
void GetSettingsClicked();
@@ -64,11 +70,11 @@ signals:
void HeaderInfoChanged(const QString &);
protected:
QComboBox *_sources;
SourceSelectionWidget *_sources;
QComboBox *_filters;
QComboBox *_conditions;
QPushButton *_getSettings;
ResizingPlainTextEdit *_settings;
VariableTextEdit *_settings;
RegexConfigWidget *_regex;
std::shared_ptr<MacroConditionFilter> _entryData;

Some files were not shown because too many files have changed in this diff Show More