Compare commits

..

84 Commits

Author SHA1 Message Date
WarmUpTill
aa87911b71 Use cpp-httplib based HTTP action type
Some checks failed
debian-build / build (push) Has been cancelled
Check locale / ubuntu64 (push) Has been cancelled
Push to master / Check Formatting 🔍 (push) Has been cancelled
Push to master / Build Project 🧱 (push) Has been cancelled
Push to master / Create Release 🛫 (push) Has been cancelled
The goal is to remove the older, more limited version of the HTTP action
at some point in the future.
2025-03-30 14:06:19 +02:00
WarmUpTill
1b05019acc Add option to hide entries from action selection
Intended to be used to hide legacy version of actions types (e.g. HTTP)
2025-03-30 14:01:41 +02:00
WarmUpTill
78a5a2629d Hide the "remote" file check option
This option will be removed at some point in the future.
The http action should be used instead.
2025-03-30 14:01:41 +02:00
WarmUpTill
634270a978 Cleanup includes 2025-03-30 14:01:41 +02:00
WarmUpTill
78ba22e1e4 Hide "get settings" button when setting macro property value 2025-03-30 14:01:41 +02:00
WarmUpTill
aba5737a60 Update and clean up locale (qwe1154323937) 2025-03-30 14:01:41 +02:00
WarmUpTill
4315f7f621 Exclude unwanted files from sources archive 2025-03-30 14:01:41 +02:00
WarmUpTill
53c535962f Use tab key to switch to dialog controls and set default focus to input 2025-03-30 14:01:41 +02:00
WarmUpTill
1e718b78c7 Fall back to project version if git tag cannot be queried 2025-03-30 14:01:41 +02:00
WarmUpTill
b1d2156228 Update libremidi to v4.3.0 2025-03-30 14:01:41 +02:00
WarmUpTill
9c3c953c6b Ignore deprecation warnings for Qt 6.7 and above
Some checks failed
debian-build / build (push) Has been cancelled
Check locale / ubuntu64 (push) Has been cancelled
Push to master / Check Formatting 🔍 (push) Has been cancelled
Push to master / Build Project 🧱 (push) Has been cancelled
Push to master / Create Release 🛫 (push) Has been cancelled
2025-03-22 18:09:51 +01:00
WarmUpTill
ae74f68db7 Add option to close projector windows 2025-03-22 18:09:51 +01:00
WarmUpTill
213f1bba36 Add "Any" entry of the reward selection only for Twitch condition 2025-03-22 18:09:51 +01:00
WarmUpTill
5a2cb0bd68 Add more scripting signals / procedures 2025-03-22 18:09:51 +01:00
WarmUpTill
23b461828b Add start start / stop callbacks 2025-03-22 18:09:51 +01:00
WarmUpTill
9944a1b03b Move interval reset handling 2025-03-22 18:09:51 +01:00
WarmUpTill
eaad4d1bbd Fix Twitch helper caches misbehaving
API calls with different sets of arguments could map to the same key
value pair, which resulted in unexpected behavior for functions using
those caches
2025-03-22 18:09:51 +01:00
WarmUpTill
17d9b73b9a Fix thread flood when Twitch event sub connection is lost
Some checks failed
debian-build / build (push) Has been cancelled
Check locale / ubuntu64 (push) Has been cancelled
Push to master / Check Formatting 🔍 (push) Has been cancelled
Push to master / Build Project 🧱 (push) Has been cancelled
Push to master / Create Release 🛫 (push) Has been cancelled
2025-03-15 12:31:13 +01:00
WarmUpTill
d6185a6099 Add handling for Twitch API throttling 2025-03-15 12:31:13 +01:00
WarmUpTill
41c06b9edb Refactor Twitch condition event handling 2025-03-15 12:31:13 +01:00
WarmUpTill
d8807077da Add option to query reward information 2025-03-15 12:31:13 +01:00
WarmUpTill
34e125f82d Rework chache handling
* No longer provide multiple HTTP helper functions with the same name
* Default to cache not being used
* Explicitly use the cache for certain Twitch actions and conditions
* Clear caches on shutdown to avoid leaks being reported
2025-03-15 12:31:13 +01:00
WarmUpTill
d670b6d07e Move JSON temp var heper functions 2025-03-15 12:31:13 +01:00
WarmUpTill
7b0f985f18 Add macro properties for 'reward' object in redemption events 2025-03-15 12:31:13 +01:00
WarmUpTill
212be923f6 Add option to query user information 2025-03-15 12:31:13 +01:00
WarmUpTill
88fcb57e9f Cleanup 2025-03-15 12:31:13 +01:00
WarmUpTill
9b609c118f Fix disabling chat emote only mode attempting to send chat messages 2025-03-15 12:31:13 +01:00
WarmUpTill
37c398d37a Indicate that "Set to condition/action value" should be avoided 2025-03-15 12:31:13 +01:00
WarmUpTill
a51b7f6b13 Fix UI theming issues
Some checks failed
debian-build / build (push) Has been cancelled
Push to master / Check Formatting 🔍 (push) Has been cancelled
Push to master / Build Project 🧱 (push) Has been cancelled
Push to master / Create Release 🛫 (push) Has been cancelled
2025-03-11 22:12:05 +01:00
WarmUpTill
808fd84b83 Remove Qt5 support 2025-03-11 22:12:05 +01:00
WarmUpTill
57bcea15f5 cmake: Replace find_qt with find_package 2025-03-10 19:48:01 +01:00
WarmUpTill
4e51b56b9b Add game capture condition 2025-03-10 18:49:07 +01:00
WarmUpTill
ef5cf41d34 Add OBS 31 support 2025-03-10 18:49:07 +01:00
WarmUpTill
911e79ea6a Fix OCR controls showing in place of color controls
This happened when OCR support was disabled at build time
2025-03-10 18:33:13 +01:00
WarmUpTill
d586177de0 Add screenshot condition
Some checks are pending
debian-build / build (push) Waiting to run
Check locale / ubuntu64 (push) Waiting to run
Push to master / Check Formatting 🔍 (push) Waiting to run
Push to master / Build Project 🧱 (push) Waiting to run
Push to master / Create Release 🛫 (push) Blocked by required conditions
2025-03-09 20:57:51 +01:00
WarmUpTill
202c36646c Fix UI issues when opening ItemSettingsDialogs
Some widgets were displayed incorrectly if the parent of the dialog
window is not the main settings window
2025-03-09 20:05:07 +01:00
WarmUpTill
ab5102f5ca Fix typo 2025-03-09 20:05:07 +01:00
WarmUpTill
e612fb99f6 Cleanup
Some checks failed
debian-build / build (push) Has been cancelled
Check locale / ubuntu64 (push) Has been cancelled
Push to master / Check Formatting 🔍 (push) Has been cancelled
Push to master / Build Project 🧱 (push) Has been cancelled
Push to master / Create Release 🛫 (push) Has been cancelled
* Reorder action type selection
* Refactor
2025-03-06 21:30:03 +01:00
WarmUpTill
691e77a69a Add option to generate random number 2025-03-06 21:30:03 +01:00
Kaito Udagawa
930118c61f Call Setup() after Load()
Some checks are pending
debian-build / build (push) Waiting to run
Push to master / Check Formatting 🔍 (push) Waiting to run
Push to master / Build Project 🧱 (push) Waiting to run
Push to master / Create Release 🛫 (push) Blocked by required conditions
Tesseract API depends on the languageCode and this must be reinitialized after Load
2025-03-06 08:43:54 +01:00
Kaito Udagawa
1e05f2f2ce Fix languageCode
LanguageCode is not respected by OCRParameters and settings properly.
2025-03-06 08:43:54 +01:00
WarmUpTill
ae25b4d023 Improve setting selection handling
Some checks are pending
debian-build / build (push) Waiting to run
Push to master / Check Formatting 🔍 (push) Waiting to run
Push to master / Build Project 🧱 (push) Waiting to run
Push to master / Create Release 🛫 (push) Blocked by required conditions
Will now try to keep the current selection if possible.
This can be useful when switching between source of the same time.
2025-03-05 14:16:04 +01:00
WarmUpTill
af055a12a1 Cleanup 2025-03-05 14:16:04 +01:00
WarmUpTill
7de28eedd3 Improve settings button selection handling
Will now try to keep the current button selection if possible.
This can be useful when switching between source of the same time.
2025-03-05 14:16:04 +01:00
WarmUpTill
8dfe81f522 Display failure if Twitch token account name cannot be fetched
Some checks failed
debian-build / build (push) Has been cancelled
Push to master / Check Formatting 🔍 (push) Has been cancelled
Push to master / Build Project 🧱 (push) Has been cancelled
Push to master / Create Release 🛫 (push) Has been cancelled
2025-02-27 19:52:41 +01:00
WarmUpTill
a1d8ae291d Add option to hide empty name warning in ItemSettingsDialog 2025-02-27 19:52:41 +01:00
WarmUpTill
eb6989527d Add support for libproc2 version 4.0.5
Some checks failed
debian-build / build (push) Has been cancelled
Push to master / Check Formatting 🔍 (push) Has been cancelled
Push to master / Build Project 🧱 (push) Has been cancelled
Push to master / Create Release 🛫 (push) Has been cancelled
2025-02-10 18:12:08 +01:00
WarmUpTill
2986a5dd96 Fix crash when actions were copied into a action queue
Some checks failed
debian-build / build (push) Has been cancelled
Push to master / Check Formatting 🔍 (push) Has been cancelled
Push to master / Build Project 🧱 (push) Has been cancelled
Push to master / Create Release 🛫 (push) Has been cancelled
The steps added via AddPostLoadStep() from the copy were never executed.
Thus they might be invalid when RunPostLoadSteps() is next called.

This could result in a crash when a new action or condition is added
after e.g. a "Filter" action's TempVariableRef was copied, which adds a
PostLoadStep to resolve the macro segment reference.
2025-02-04 08:26:04 +01:00
WarmUpTill
816ee9b244 Fix crash when changing Twitch condition type 2025-02-04 08:26:04 +01:00
WarmUpTill
51b53bf948 Fix script actions / conditions not working after stopping macro
Some checks failed
debian-build / build (push) Has been cancelled
Push to master / Check Formatting 🔍 (push) Has been cancelled
Push to master / Build Project 🧱 (push) Has been cancelled
Push to master / Create Release 🛫 (push) Has been cancelled
2025-01-30 09:39:50 +01:00
WarmUpTill
2c5121ee94 Prevent crash when macro contains invalid action or condition 2025-01-30 09:39:50 +01:00
WarmUpTill
6a58684854 Fix import of incomplete settings not resetting duration to zero
Some checks failed
debian-build / build (push) Has been cancelled
Push to master / Check Formatting 🔍 (push) Has been cancelled
Push to master / Build Project 🧱 (push) Has been cancelled
Push to master / Create Release 🛫 (push) Has been cancelled
2025-01-23 19:00:51 +01:00
WarmUpTill
0b622fdbed Fix crash when importing settings 2025-01-23 19:00:51 +01:00
WarmUpTill
3bce8e075f CI: Switch to and adapt to Ubuntu 24 runner
Some checks failed
debian-build / build (push) Has been cancelled
Push to master / Check Formatting 🔍 (push) Has been cancelled
Push to master / Build Project 🧱 (push) Has been cancelled
Push to master / Create Release 🛫 (push) Has been cancelled
2025-01-15 21:36:54 +01:00
WarmUpTill
f598d84ea1 Fix freeze due to ResizingPlainTextEdit constantly resizing incorrectly 2025-01-15 21:36:35 +01:00
WarmUpTill
d7a244e80e Fix transition condition not considering transition overrides
Some checks failed
debian-build / build (push) Has been cancelled
Push to master / Check Formatting 🔍 (push) Has been cancelled
Push to master / Build Project 🧱 (push) Has been cancelled
Push to master / Create Release 🛫 (push) Has been cancelled
2024-12-31 12:47:20 +01:00
WarmUpTill
408002e96a Add support for OBS 31 in-tree builds
Some checks failed
debian-build / build (push) Has been cancelled
Push to master / Check Formatting 🔍 (push) Has been cancelled
Push to master / Build Project 🧱 (push) Has been cancelled
Push to master / Create Release 🛫 (push) Has been cancelled
2024-12-12 21:41:21 +01:00
WarmUpTill
50da3d3210 Fix "Media" condition showing the incorrect macro property selection
Some checks failed
debian-build / build (push) Has been cancelled
Push to master / Check Formatting 🔍 (push) Has been cancelled
Push to master / Build Project 🧱 (push) Has been cancelled
Push to master / Create Release 🛫 (push) Has been cancelled
2024-11-30 12:11:14 +01:00
WarmUpTill
9e13067769 Fix typo
Some checks failed
Check locale / ubuntu64 (push) Has been cancelled
Push to master / Check Formatting 🔍 (push) Has been cancelled
Push to master / Build Project 🧱 (push) Has been cancelled
Push to master / Create Release 🛫 (push) Has been cancelled
2024-11-23 17:11:05 +01:00
WarmUpTill
821e768e76 Reduce screenshot selection area to area to be checked
Some checks failed
Push to master / Check Formatting 🔍 (push) Waiting to run
Push to master / Build Project 🧱 (push) Waiting to run
Push to master / Create Release 🛫 (push) Blocked by required conditions
debian-build / build (push) Has been cancelled
2024-11-22 20:52:15 +01:00
WarmUpTill
4d2179448b Rename files 2024-11-22 20:52:15 +01:00
WarmUpTill
40d9002470 Update scripting examples
* Add more comments
* Add example showcasing how to register and set temp vars
2024-11-22 20:36:36 +01:00
WarmUpTill
7f1e310ca6 Add support to register, deregister, and set temp vars from scripts 2024-11-22 20:36:36 +01:00
WarmUpTill
66d02f4683 Fix typos 2024-11-22 20:36:36 +01:00
Arash Partow
0aae1b9978 Update ExprTk to 0.0.3
Some checks are pending
debian-build / build (push) Waiting to run
Push to master / Check Formatting 🔍 (push) Waiting to run
Push to master / Build Project 🧱 (push) Waiting to run
Push to master / Create Release 🛫 (push) Blocked by required conditions
2024-11-22 19:56:02 +01:00
WarmUpTill
db50822b05 Prepare to fix deprecation warning when using OBS 31
Some checks failed
debian-build / build (push) Has been cancelled
Push to master / Check Formatting 🔍 (push) Has been cancelled
Push to master / Build Project 🧱 (push) Has been cancelled
Push to master / Create Release 🛫 (push) Has been cancelled
The following log message will be present in OBS 31 when
CheckIfTrayIsDisabled() is called:

DEPRECATION: obs_frontend_get_global_config is deprecated. Read from
global or user configuration explicitly instead.

It will be resolved when the minimum OBS version is bumped to OBS 31.
2024-11-02 08:28:52 +01:00
WarmUpTill
63a545b293 Fix "Date" condition returning true unexpectedly
This could happen if the plugin was stopped on the General tab and
restarted or the first time the plugin was started.
2024-11-02 08:28:52 +01:00
WarmUpTill
bc0497d2c9 Add MacroWasCheckedSinceLastStart() 2024-11-02 08:28:52 +01:00
WarmUpTill
37226a3a4c Fix macro group icon not being displayed correctly in OBS 31
Some checks failed
debian-build / build (push) Has been cancelled
Push to master / Check Formatting 🔍 (push) Has been cancelled
Push to master / Build Project 🧱 (push) Has been cancelled
Push to master / Create Release 🛫 (push) Has been cancelled
2024-10-31 19:39:38 +01:00
WarmUpTill
9f15fbe47c Recursively search for source button in property groups
Some checks are pending
debian-build / build (push) Waiting to run
Push to master / Check Formatting 🔍 (push) Waiting to run
Push to master / Build Project 🧱 (push) Waiting to run
Push to master / Create Release 🛫 (push) Blocked by required conditions
2024-10-30 18:29:23 +01:00
WarmUpTill
0ee6feb529 Add option to check the current stream key
Some checks failed
debian-build / build (push) Has been cancelled
Check locale / ubuntu64 (push) Has been cancelled
Push to master / Check Formatting 🔍 (push) Has been cancelled
Push to master / Build Project 🧱 (push) Has been cancelled
Push to master / Create Release 🛫 (push) Has been cancelled
2024-10-26 23:42:59 +02:00
WarmUpTill
3b82b55090 Fix Queue condition not displaying the correct size selection 2024-10-26 23:42:59 +02:00
WarmUpTill
071c3309c2 Add option to check current streaming service
Some checks are pending
debian-build / build (push) Waiting to run
Check locale / ubuntu64 (push) Waiting to run
Push to master / Check Formatting 🔍 (push) Waiting to run
Push to master / Build Project 🧱 (push) Waiting to run
Push to master / Create Release 🛫 (push) Blocked by required conditions
2024-10-26 11:09:06 +02:00
WarmUpTill
ee7ee7a846 Move AutoUpdateTooltipLabel definition 2024-10-26 11:09:06 +02:00
WarmUpTill
d10300bd4b Fix typo 2024-10-26 11:09:06 +02:00
WarmUpTill
e262672876 Add "Last empty" row to Queue tab 2024-10-26 11:08:51 +02:00
WarmUpTill
392e775c7b Add temp var support for size and running state 2024-10-26 11:08:51 +02:00
WarmUpTill
f641c20564 Add SetTempVarValue() overload to support boolean values 2024-10-26 11:08:51 +02:00
WarmUpTill
39327be88e Add option to move scene item above / below specified scene item
Some checks failed
debian-build / build (push) Has been cancelled
Check locale / ubuntu64 (push) Has been cancelled
Push to master / Check Formatting 🔍 (push) Has been cancelled
Push to master / Build Project 🧱 (push) Has been cancelled
Push to master / Create Release 🛫 (push) Has been cancelled
2024-10-21 20:46:27 +02:00
WarmUpTill
ed0963799c Fix "Screenshot" action variable selection not being loaded
Some checks failed
debian-build / build (push) Has been cancelled
Push to master / Check Formatting 🔍 (push) Has been cancelled
Push to master / Build Project 🧱 (push) Has been cancelled
Push to master / Create Release 🛫 (push) Has been cancelled
2024-10-18 18:30:49 +02:00
WarmUpTill
f9e73a2b8f Fix crash when clearing Twitch chat buffer 2024-10-18 18:29:46 +02:00
Przemek Pawlas
dfaa704b71 Remove some unused includes 2024-10-12 17:09:33 +02:00
Przemek Pawlas
d54edddde4 Enable use of variables as input for "Variable" action 2024-10-12 17:09:33 +02:00
Przemek Pawlas
37cc289204 Rename function 2024-10-12 17:09:33 +02:00
166 changed files with 15884 additions and 6043 deletions

View File

@@ -6,15 +6,6 @@
"autosort": true
},
"additional_commands": {
"find_qt": {
"flags": [],
"kwargs": {
"COMPONENTS": "+",
"COMPONENTS_WIN": "+",
"COMPONENTS_MACOS": "+",
"COMPONENTS_LINUX": "+"
}
},
"set_target_properties_obs": {
"pargs": 1,
"flags": [],

View File

@@ -20,6 +20,7 @@ body:
- macOS 13
- macOS 12
- macOS 11
- Ubuntu 24.04
- Ubuntu 23.10
- Ubuntu 22.04
- Other

View File

@@ -9,5 +9,5 @@ package 'libxtst-dev'
package 'libxss-dev'
package 'libopencv-dev'
package 'libtesseract-dev'
package 'libprocps-dev'
package 'libproc2-dev'
package 'libusb-1.0-0-dev'

View File

@@ -480,12 +480,7 @@ Usage: %B${functrace[1]%:*}%b <option> [<options>]
popd
;;
linux)
# Hacky workaround to support libproc2 with Ubuntu 22 build environment
local lsb_version=$(lsb_release -r | cut -f 2 || true)
if [[ "${lsb_version}" == '22.04' ]] {
sudo apt install ${project_root}/build-aux/CI/linux/ubuntu22/libproc2-0_4.0.2-3_amd64.deb
sudo apt install ${project_root}/build-aux/CI/linux/ubuntu22/libproc2-dev_4.0.2-3_amd64.deb
}
# Nothing to do for now
;;
}
}

0
.github/test vendored
View File

View File

@@ -43,7 +43,7 @@ jobs:
# devscripts and libobs-dev are needed but they were already installed
# from check_libobs_revision and install_frontend_header sections.
sudo apt update
sudo apt install cmake debhelper libcurl4-openssl-dev libxss-dev libxtst-dev qtbase5-dev libopencv-dev libprocps-dev
sudo apt install build-essential cmake debhelper libcurl4-openssl-dev libxss-dev libxtst-dev qt6-base-dev libopencv-dev libproc2-dev
- name: build
run: |
debuild --no-lintian --no-sign

View File

@@ -10,7 +10,7 @@ env:
jobs:
check-event:
name: Check GitHub Event Data 🔎
runs-on: ubuntu-22.04
runs-on: ubuntu-latest
defaults:
run:
shell: bash
@@ -177,7 +177,7 @@ jobs:
ubuntu-build:
name: Build for Ubuntu 🐧
runs-on: ubuntu-22.04
runs-on: ubuntu-latest
needs: check-event
defaults:
run:
@@ -241,14 +241,14 @@ jobs:
- name: Upload Artifacts 📡
uses: actions/upload-artifact@v4
with:
name: ${{ steps.setup.outputs.pluginName }}-${{ steps.setup.outputs.pluginVersion }}-ubuntu-22.04-x86_64-${{ needs.check-event.outputs.commitHash }}
name: ${{ steps.setup.outputs.pluginName }}-${{ steps.setup.outputs.pluginVersion }}-ubuntu-24.04-x86_64-${{ needs.check-event.outputs.commitHash }}
path: ${{ github.workspace }}/release/${{ steps.setup.outputs.pluginName }}-*-x86_64*.*
- name: Upload debug symbol artifacts 🪲
uses: actions/upload-artifact@v4
if: ${{ fromJSON(needs.check-event.outputs.package) }}
with:
name: ${{ steps.setup.outputs.pluginName }}-${{ steps.setup.outputs.pluginVersion }}-ubuntu-22.04-x86_64-${{ needs.check-event.outputs.commitHash }}-dbgsym
name: ${{ steps.setup.outputs.pluginName }}-${{ steps.setup.outputs.pluginVersion }}-ubuntu-24.04-x86_64-${{ needs.check-event.outputs.commitHash }}-dbgsym
path: ${{ github.workspace }}/release/${{ steps.setup.outputs.pluginName }}-*-x86_64*-dbgsym.ddeb
windows-build:

View File

@@ -3,7 +3,7 @@ on:
workflow_call:
jobs:
clang-format:
runs-on: ubuntu-22.04
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
@@ -15,7 +15,7 @@ jobs:
failCondition: error
cmake-format:
runs-on: ubuntu-22.04
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:

View File

@@ -31,7 +31,7 @@ jobs:
create-release:
name: Create Release 🛫
if: github.ref_type == 'tag'
runs-on: ubuntu-22.04
runs-on: ubuntu-latest
needs: build-project
defaults:
run:
@@ -77,7 +77,7 @@ jobs:
variants=(
'windows-x64;zip|exe'
'macos-universal;tar.xz|pkg'
'ubuntu-22.04-x86_64;tar.xz|deb|ddeb'
'ubuntu-24.04-x86_64;tar.xz|deb|ddeb'
'sources;tar.xz'
)

View File

@@ -20,6 +20,12 @@ if(BUILD_OUT_OF_TREE)
include(helpers)
endif()
# OBS 31 no longer defines find_qt so check if we need to include it for in-tree
# builds
if(NOT COMMAND find_qt)
include("${CMAKE_CURRENT_SOURCE_DIR}/cmake/common/helpers_common.cmake")
endif()
set(LIB_NAME "${PROJECT_NAME}-lib")
add_library(${PROJECT_NAME} MODULE)
add_library(${LIB_NAME} SHARED)
@@ -27,6 +33,11 @@ add_library(${LIB_NAME} SHARED)
include(cmake/common/get_git_revision_description.cmake)
get_git_head_revision(GIT_REFSPEC GIT_SHA1)
git_describe(GIT_TAG)
if(${GIT_TAG} STREQUAL "GIT-NOTFOUND")
set(GIT_TAG ${PROJECT_VERSION})
endif()
message(STATUS "${PROJECT_NAME} version: ${GIT_TAG}")
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/cmake/common/version.cpp.in"
"${CMAKE_CURRENT_BINARY_DIR}/lib/version.cpp" @ONLY)
@@ -157,6 +168,8 @@ target_sources(
lib/queue/action-queue.hpp
lib/queue/action-queue-tab.cpp
lib/queue/action-queue-tab.hpp
lib/utils/auto-update-tooltip-label.cpp
lib/utils/auto-update-tooltip-label.hpp
lib/utils/backup.cpp
lib/utils/backup.hpp
lib/utils/condition-logic.cpp
@@ -287,9 +300,16 @@ include(cmake/common/advss_helpers.cmake)
setup_obs_lib_dependency(${LIB_NAME})
setup_obs_lib_dependency(${PROJECT_NAME})
find_qt(COMPONENTS Widgets Core)
target_link_libraries(${PROJECT_NAME} PRIVATE Qt::Core Qt::Widgets)
target_link_libraries(${LIB_NAME} PRIVATE Qt::Core Qt::Widgets)
find_package(Qt6 REQUIRED COMPONENTS Widgets Core)
target_link_libraries(${PROJECT_NAME} PRIVATE Qt6::Core Qt6::Widgets)
target_link_libraries(${LIB_NAME} PRIVATE Qt6::Core Qt6::Widgets)
# Ignore QCheckBox::stateChanged deprecation warning until minimum supported Qt
# version is at least Qt 6.7, which introduces QCheckBox::checkStateChanged
if(Qt6_VERSION VERSION_GREATER "6.0.0")
target_compile_definitions(${LIB_NAME} PRIVATE QT_NO_DEPRECATED_WARNINGS)
endif()
target_compile_options(
${PROJECT_NAME}
PRIVATE
@@ -405,7 +425,12 @@ else()
set(PROCESS_CONDITION_SUPPORTED 1)
endif()
if(PROCPS2_INCLUDE_DIR)
message(STATUS "${PROJECT_NAME} using libproc2")
find_package(PkgConfig REQUIRED)
pkg_check_modules(libproc2 IMPORTED_TARGET libproc2<4.0.5 QUIET)
if(libproc2_FOUND)
target_compile_definitions(${LIB_NAME} PRIVATE PROCPS2_USE_INFO)
endif()
pkg_check_modules(libproc2 REQUIRED IMPORTED_TARGET libproc2)
set(PROC_INCLUDE_DIR "${PROCPS2_INCLUDE_DIR}")
target_compile_definitions(${LIB_NAME} PRIVATE PROCPS2_AVAILABLE)
set(PROCESS_CONDITION_SUPPORTED 1)
@@ -418,6 +443,9 @@ else()
endif()
target_include_directories(${LIB_NAME} PRIVATE "${PROC_INCLUDE_DIR}")
target_sources(${LIB_NAME} PRIVATE lib/linux/advanced-scene-switcher-nix.cpp)
# Don't include irrelevant folders into sources archive
list(APPEND CPACK_SOURCE_IGNORE_FILES "\\.deps/.*")
endif()
if(NOT OS_WINDOWS)

View File

@@ -7,7 +7,7 @@ Build-Depends: cmake,
libcurl4-openssl-dev,
libobs-dev,
libxtst-dev,
qtbase5-dev,
qt6-base-dev,
libxss-dev,
libopencv-dev
Standards-Version: 4.6.0

View File

@@ -3,7 +3,7 @@
export DEB_BUILD_MAINT_OPTIONS = hardening=+all
export QT_SELECT = qt5
export QT_SELECT = qt6
%:
dh $@

View File

@@ -283,8 +283,14 @@ endfunction()
function(setup_advss_plugin target)
setup_obs_lib_dependency(${target})
find_qt(COMPONENTS Widgets Core)
target_link_libraries(${target} PRIVATE Qt::Core Qt::Widgets)
find_package(Qt6 REQUIRED COMPONENTS Widgets Core)
target_link_libraries(${target} PRIVATE Qt6::Core Qt6::Widgets)
# Ignore QCheckBox::stateChanged deprecation warning until minimum supported
# Qt version is at least Qt 6.7, which introduces QCheckBox::checkStateChanged
if(Qt6_VERSION VERSION_GREATER "6.7.0")
target_compile_definitions(${target} PRIVATE QT_NO_DEPRECATED_WARNINGS)
endif()
set_target_properties(
${target}

View File

@@ -17,85 +17,6 @@ if(NOT QT_VERSION)
set_property(CACHE QT_VERSION PROPERTY STRINGS AUTO 5 6)
endif()
# find_qt: Macro to find best possible Qt version for use with the project:
macro(find_qt)
set(multiValueArgs COMPONENTS COMPONENTS_WIN COMPONENTS_MAC COMPONENTS_LINUX)
cmake_parse_arguments(find_qt "" "${oneValueArgs}" "${multiValueArgs}"
${ARGN})
# Do not use versionless targets in the first step to avoid Qt::Core being
# clobbered by later opportunistic find_package runs
set(QT_NO_CREATE_VERSIONLESS_TARGETS TRUE)
message(DEBUG "Start Qt version discovery...")
# Loop until _QT_VERSION is set or FATAL_ERROR aborts script execution early
while(NOT _QT_VERSION)
message(DEBUG "QT_VERSION set to ${QT_VERSION}")
if(QT_VERSION STREQUAL AUTO AND NOT qt_test_version)
set(qt_test_version 6)
elseif(NOT QT_VERSION STREQUAL AUTO)
set(qt_test_version ${QT_VERSION})
endif()
message(DEBUG "Attempting to find Qt${qt_test_version}")
find_package(
Qt${qt_test_version}
COMPONENTS Core
QUIET)
if(TARGET Qt${qt_test_version}::Core)
set(_QT_VERSION
${qt_test_version}
CACHE INTERNAL "")
message(STATUS "Qt version found: ${_QT_VERSION}")
unset(qt_test_version)
break()
elseif(QT_VERSION STREQUAL AUTO)
if(qt_test_version EQUAL 6)
message(WARNING "Qt6 was not found, falling back to Qt5")
set(qt_test_version 5)
continue()
endif()
endif()
message(FATAL_ERROR "Neither Qt6 nor Qt5 found.")
endwhile()
# Enable versionless targets for the remaining Qt components
set(QT_NO_CREATE_VERSIONLESS_TARGETS FALSE)
set(qt_components ${find_qt_COMPONENTS})
if(OS_WINDOWS)
list(APPEND qt_components ${find_qt_COMPONENTS_WIN})
elseif(OS_MACOS)
list(APPEND qt_components ${find_qt_COMPONENTS_MAC})
else()
list(APPEND qt_components ${find_qt_COMPONENTS_LINUX})
endif()
message(DEBUG "Trying to find Qt components ${qt_components}...")
find_package(Qt${_QT_VERSION} REQUIRED ${qt_components})
list(APPEND qt_components Core)
if("Gui" IN_LIST find_qt_COMPONENTS_LINUX)
list(APPEND qt_components "GuiPrivate")
endif()
# Check for versionless targets of each requested component and create if
# necessary
foreach(component IN LISTS qt_components)
message(DEBUG "Checking for target Qt::${component}")
if(NOT TARGET Qt::${component} AND TARGET Qt${_QT_VERSION}::${component})
add_library(Qt::${component} INTERFACE IMPORTED)
set_target_properties(
Qt::${component} PROPERTIES INTERFACE_LINK_LIBRARIES
Qt${_QT_VERSION}::${component})
endif()
set_property(TARGET Qt::${component} PROPERTY INTERFACE_COMPILE_FEATURES "")
endforeach()
endmacro()
# check_uuid: Helper function to check for valid UUID
function(check_uuid uuid_string return_value)
set(valid_uuid TRUE)

View File

@@ -212,7 +212,6 @@ AdvSceneSwitcher.condition.stream.state.start="Stream läuft"
AdvSceneSwitcher.condition.stream.state.stop="Stream gestoppt"
AdvSceneSwitcher.condition.stream.state.starting="Stream wird gestartet"
AdvSceneSwitcher.condition.stream.state.stopping="Stream wird gestoppt"
AdvSceneSwitcher.condition.stream.entry="{{streamState}}{{keyFrameInterval}}"
AdvSceneSwitcher.condition.record="Aufnahme"
AdvSceneSwitcher.condition.record.state.start="Aufnahme läuft"
AdvSceneSwitcher.condition.record.state.pause="Aufnahme pausiert"
@@ -490,7 +489,7 @@ AdvSceneSwitcher.action.sceneOrder.type.moveDown="Nach unten verschieben"
AdvSceneSwitcher.action.sceneOrder.type.moveTop="An erste Stelle verschieben"
AdvSceneSwitcher.action.sceneOrder.type.moveBottom="An letzte Stelle verschieben"
AdvSceneSwitcher.action.sceneOrder.type.movePosition="An Position verschieben"
AdvSceneSwitcher.action.sceneOrder.entry="Auf{{scenes}}{{actions}}{{sources}}{{position}}"
AdvSceneSwitcher.action.sceneOrder.entry="Auf{{scenes}}{{actions}}{{sources}}{{sources2}}{{position}}"
AdvSceneSwitcher.action.sceneTransform="Szenenelement transformieren"
AdvSceneSwitcher.action.sceneTransform.getTransform="Transformation erhalten"
AdvSceneSwitcher.action.sceneTransform.entry="Auf{{scenes}}{{action}}{{rotation}}{{sources}}{{settingSelection}}{{singleSettingValue}}"
@@ -527,7 +526,6 @@ AdvSceneSwitcher.action.screenshot.save.default="Standard"
AdvSceneSwitcher.action.screenshot.save.custom="Benutzerdefiniert"
AdvSceneSwitcher.action.screenshot.type.source="Quelle"
AdvSceneSwitcher.action.screenshot.type.scene="Szene"
AdvSceneSwitcher.action.screenshot.mainOutput="OBS's Haupt-Ausgabe"
AdvSceneSwitcher.action.screenshot.blackscreenNote="Quellen oder Szenen, die nicht immer gerendert werden, können dazu führen, dass einige Teile der Screenshots leer bleiben."
AdvSceneSwitcher.action.screenshot.entry="Screenshot{{targetType}}{{sources}}{{scenes}}und speichere in{{saveType}}{{variables}}Pfad"
AdvSceneSwitcher.action.profile="Profil"

View File

@@ -97,6 +97,7 @@ AdvSceneSwitcher.actionQueueTab.queueAddButton.tooltip="Add new action queue"
AdvSceneSwitcher.actionQueueTab.queueRemoveButton.tooltip="Remove selected action queues"
AdvSceneSwitcher.actionQueueTab.name.header="Name"
AdvSceneSwitcher.actionQueueTab.size.header="Size"
AdvSceneSwitcher.actionQueueTab.lastEmpty.header="Last empty"
AdvSceneSwitcher.actionQueueTab.isRunning.header="Is running"
AdvSceneSwitcher.actionQueueTab.runOnStartup.header="Run on startup"
AdvSceneSwitcher.actionQueueTab.yes="Yes"
@@ -418,7 +419,10 @@ AdvSceneSwitcher.condition.stream.state.stop="Stream stopped"
AdvSceneSwitcher.condition.stream.state.starting="Stream starting"
AdvSceneSwitcher.condition.stream.state.stopping="Stream stopping"
AdvSceneSwitcher.condition.stream.state.keyFrameInterval="Keyframe interval equals"
AdvSceneSwitcher.condition.stream.entry="{{streamState}}{{keyFrameInterval}}"
AdvSceneSwitcher.condition.stream.state.streamKey="Stream key is"
AdvSceneSwitcher.condition.stream.state.service="Service name matches"
AdvSceneSwitcher.condition.stream.service.tooltip="Current service name: %1"
AdvSceneSwitcher.condition.stream.entry="{{streamState}}{{keyFrameInterval}}{{streamKey}}{{serviceName}}{{regex}}{{currentService}}"
AdvSceneSwitcher.condition.record="Recording"
AdvSceneSwitcher.condition.record.state.start="Recording running"
AdvSceneSwitcher.condition.record.state.pause="Recording paused"
@@ -765,6 +769,11 @@ AdvSceneSwitcher.condition.streamDeck.checkData="Check data field"
AdvSceneSwitcher.condition.streamDeck.startListen="Start listening"
AdvSceneSwitcher.condition.streamDeck.stopListen="Stop listening"
AdvSceneSwitcher.condition.streamDeck.pluginDownload="<html><head/><body><p>The Stream Deck plugin can be found <a href=\"https://github.com/WarmUpTill/advanced-scene-switcher-streamdeck-plugin/releases\"><span style=\" text-decoration: underline; color:#268bd2;\">here on GitHub</span></a>.</p></body></html>"
AdvSceneSwitcher.condition.gameCapture="Game capture"
AdvSceneSwitcher.condition.gameCapture.entry="{{sources}}hooked a game."
AdvSceneSwitcher.condition.screenshot="Screenshot"
AdvSceneSwitcher.condition.screenshot.entry="A screenshot was taken"
# Macro Actions
AdvSceneSwitcher.action.unknown="Unknown action"
@@ -956,7 +965,9 @@ AdvSceneSwitcher.action.sceneOrder.type.moveDown="Move down"
AdvSceneSwitcher.action.sceneOrder.type.moveTop="Move to top"
AdvSceneSwitcher.action.sceneOrder.type.moveBottom="Move to bottom"
AdvSceneSwitcher.action.sceneOrder.type.movePosition="Move to position"
AdvSceneSwitcher.action.sceneOrder.entry="On{{scenes}}{{actions}}{{sources}}{{position}}"
AdvSceneSwitcher.action.sceneOrder.type.above="Move above"
AdvSceneSwitcher.action.sceneOrder.type.below="Move below"
AdvSceneSwitcher.action.sceneOrder.entry="On{{scenes}}{{actions}}{{sources}}{{position}}{{sources2}}"
AdvSceneSwitcher.action.sceneTransform="Scene item transform"
AdvSceneSwitcher.action.sceneTransform.type.manual="Set transform string"
AdvSceneSwitcher.action.sceneTransform.type.setSingleSetting="Set transform setting"
@@ -1011,7 +1022,6 @@ AdvSceneSwitcher.action.screenshot.save.custom="Custom file path"
AdvSceneSwitcher.action.screenshot.save.variable="Variable (base64 encoded PNG)"
AdvSceneSwitcher.action.screenshot.type.source="Source"
AdvSceneSwitcher.action.screenshot.type.scene="Scene"
AdvSceneSwitcher.action.screenshot.mainOutput="OBS's main output"
AdvSceneSwitcher.action.screenshot.blackscreenNote="Sources or scenes, which are not always rendered, may result in some parts of screenshots to remain blank."
AdvSceneSwitcher.action.screenshot.entry="Screenshot{{targetType}}{{sources}}{{scenes}}and save to{{saveType}}{{variables}}"
AdvSceneSwitcher.action.profile="Profile"
@@ -1040,21 +1050,35 @@ AdvSceneSwitcher.action.websocket.entry.sceneSwitcher.request="Send{{api}}of typ
AdvSceneSwitcher.action.websocket.entry.sceneSwitcher.event="Send{{api}}of type{{type}}to connected clients"
AdvSceneSwitcher.action.websocket.entry.generic="Send{{api}}via{{connection}}"
AdvSceneSwitcher.action.http="HTTP"
AdvSceneSwitcher.action.http.legacy="HTTP (legacy)"
AdvSceneSwitcher.action.http.setHeaders="Set headers"
AdvSceneSwitcher.action.http.headers="Headers:"
AdvSceneSwitcher.action.http.addHeader="Add header"
AdvSceneSwitcher.action.http.addHeader.name="Header name"
AdvSceneSwitcher.action.http.addHeader.value="Header value"
AdvSceneSwitcher.action.http.setParams="Set parameters"
AdvSceneSwitcher.action.http.params="Parameters:"
AdvSceneSwitcher.action.http.addParam.name="Parameter name"
AdvSceneSwitcher.action.http.addParam.value="Parameter value"
AdvSceneSwitcher.action.http.body="Message body:"
AdvSceneSwitcher.action.http.type.get="GET"
AdvSceneSwitcher.action.http.type.post="POST"
AdvSceneSwitcher.action.http.type.put="PUT"
AdvSceneSwitcher.action.http.type.patch="PATCH"
AdvSceneSwitcher.action.http.type.delete="DELETE"
AdvSceneSwitcher.action.http.layout.method="Send{{method}}to URL{{url}}at path{{path}}"
AdvSceneSwitcher.action.http.layout.contentType="Content type:{{contentType}}"
AdvSceneSwitcher.action.http.layout.timeout="Timeout:{{timeout}}seconds"
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 to fixed value"
AdvSceneSwitcher.action.variable.type.set="Set to 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.type.setActionValue="Set to action value"
AdvSceneSwitcher.action.variable.type.setConditionValue="Set to condition value"
AdvSceneSwitcher.action.variable.type.setActionValue="Set to action value (legacy)"
AdvSceneSwitcher.action.variable.type.setConditionValue="Set to condition value (legacy)"
AdvSceneSwitcher.action.variable.type.roundToInt="Round to nearest integer"
AdvSceneSwitcher.action.variable.type.subString="Set to substring of current value"
AdvSceneSwitcher.action.variable.type.findAndReplace="Find and replace in current value"
@@ -1072,6 +1096,7 @@ AdvSceneSwitcher.action.variable.type.truncateValue="Truncate current value"
AdvSceneSwitcher.action.variable.type.swapValues="Swap variable values"
AdvSceneSwitcher.action.variable.type.trim="Trim whitespace"
AdvSceneSwitcher.action.variable.type.changeCase="Change case"
AdvSceneSwitcher.action.variable.type.randomNumber="Generate random number"
AdvSceneSwitcher.action.variable.case.type.lowerCase="lowercase"
AdvSceneSwitcher.action.variable.case.type.upperCase="UPPERCASE"
AdvSceneSwitcher.action.variable.case.type.capitalized="Capitalized"
@@ -1086,6 +1111,7 @@ AdvSceneSwitcher.action.variable.findAndReplace.replace="Text to replace with"
AdvSceneSwitcher.action.variable.findAndReplace.replace.tooltip="Using \1, \2, etc. to represent first, second, etc. capture groups is supported."
AdvSceneSwitcher.action.variable.subString.begin="Beginning"
AdvSceneSwitcher.action.variable.subString.all="All"
AdvSceneSwitcher.action.variable.generateInteger="Only generate integers"
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!"
@@ -1098,7 +1124,10 @@ AdvSceneSwitcher.action.variable.entry.substringRegex="Assign value of{{regexMat
AdvSceneSwitcher.action.variable.entry.findAndReplace="{{findStr}}{{findRegex}}{{replaceStr}}"
AdvSceneSwitcher.action.variable.entry.userInput.customPrompt="{{useCustomPrompt}}Use custom prompt{{inputPrompt}}"
AdvSceneSwitcher.action.variable.entry.userInput.placeholder="{{useInputPlaceholder}}Fill with placeholder{{inputPlaceholder}}"
AdvSceneSwitcher.action.variable.entry.randomNumber="Generate random number in range from{{randomNumberStart}}to{{randomNumberEnd}}"
AdvSceneSwitcher.action.projector="Projector"
AdvSceneSwitcher.action.projector.action.open="Open"
AdvSceneSwitcher.action.projector.action.close="Close"
AdvSceneSwitcher.action.projector.type.source="Source"
AdvSceneSwitcher.action.projector.type.scene="Scene"
AdvSceneSwitcher.action.projector.type.preview="Preview"
@@ -1107,8 +1136,9 @@ AdvSceneSwitcher.action.projector.type.multiview="Multiview"
AdvSceneSwitcher.action.projector.display="Display"
AdvSceneSwitcher.action.projector.windowed="Windowed"
AdvSceneSwitcher.action.projector.fullscreen="Fullscreen"
AdvSceneSwitcher.action.projector.entry="Open{{windowTypes}}projector of{{types}}{{scenes}}{{sources}}"
AdvSceneSwitcher.action.projector.entry.monitor="on{{monitors}}"
AdvSceneSwitcher.action.projector.entry.close="{{actions}}projector with name{{projectorWindowName}}{{regex}}"
AdvSceneSwitcher.action.projector.entry.open.windowed="{{actions}}{{windowTypes}}projector of{{types}}{{scenes}}{{sources}}"
AdvSceneSwitcher.action.projector.entry.open.fullscreen="{{actions}}{{windowTypes}}projector of{{types}}{{scenes}}{{sources}}on{{monitors}}"
AdvSceneSwitcher.action.midi="MIDI"
AdvSceneSwitcher.action.midi.entry="Send message to{{device}}:"
AdvSceneSwitcher.action.midi.entry.listen="Set MIDI message selection to messages incoming on{{listenDevices}}:{{listenButton}}"
@@ -1128,10 +1158,15 @@ AdvSceneSwitcher.action.twitch.type.clip.create="Create stream clip"
AdvSceneSwitcher.action.twitch.type.chat.announcement.send="Send chat announcement"
AdvSceneSwitcher.action.twitch.type.chat.emoteOnly.enable="Enable chat's emote-only mode"
AdvSceneSwitcher.action.twitch.type.chat.emoteOnly.disable="Disable chat's emote-only mode"
AdvSceneSwitcher.action.twitch.type.sendChatMessage="Send chat message"
AdvSceneSwitcher.action.twitch.type.chat.sendMessage="Send chat message"
AdvSceneSwitcher.action.twitch.type.user.getInfo="Get user information"
AdvSceneSwitcher.action.twitch.type.reward.getInfo="Get channel points reward information"
AdvSceneSwitcher.action.twitch.reward.toggleControl="Toggle reward name / variable selection control"
AdvSceneSwitcher.action.twitch.categorySelectionDisabled="Cannot select category without selecting a Twitch account first!"
AdvSceneSwitcher.action.twitch.entry.default="On{{account}}{{actions}}{{streamTitle}}{{category}}{{markerDescription}}{{clipHasDelay}}{{duration}}{{announcementColor}}{{channel}}"
AdvSceneSwitcher.action.twitch.entry.chat="Using account{{account}}{{actions}}on{{channel}}{{streamTitle}}{{category}}{{markerDescription}}{{clipHasDelay}}{{duration}}{{announcementColor}}"
AdvSceneSwitcher.action.twitch.entry.default="On{{account}}{{actions}}{{streamTitle}}{{category}}{{markerDescription}}{{clipHasDelay}}{{duration}}{{announcementColor}}{{channel}}{{pointsReward}}"
AdvSceneSwitcher.action.twitch.entry.chat="Using account{{account}}{{actions}}on{{channel}}"
AdvSceneSwitcher.action.twitch.entry.user.getInfo="Using account{{account}}{{actions}}for{{userInfoQueryType}}{{userLogin}}{{userId}}"
AdvSceneSwitcher.action.twitch.entry.reward.getInfo="Using account{{account}}{{actions}}for channel{{channel}}{{pointsReward}}{{rewardVariable}}{{toggleRewardSelection}}"
AdvSceneSwitcher.action.twitch.title.title="Enter title"
AdvSceneSwitcher.action.twitch.marker.description="Describe marker"
AdvSceneSwitcher.action.twitch.clip.hasDelay="Add a slight delay before capturing the clip"
@@ -1141,6 +1176,8 @@ AdvSceneSwitcher.action.twitch.announcement.blue="Blue"
AdvSceneSwitcher.action.twitch.announcement.green="Green"
AdvSceneSwitcher.action.twitch.announcement.orange="Orange"
AdvSceneSwitcher.action.twitch.announcement.purple="Purple"
AdvSceneSwitcher.action.twitch.user.getInfo.queryType.id="User Id"
AdvSceneSwitcher.action.twitch.user.getInfo.queryType.login="User login"
AdvSceneSwitcher.action.clipboard="Clipboard"
AdvSceneSwitcher.action.clipboard.type.copy.text="Copy text"
AdvSceneSwitcher.action.clipboard.type.copy.image="Copy image"
@@ -1205,7 +1242,7 @@ AdvSceneSwitcher.validation.text.maxLength="Only %1 characters are allowed in th
AdvSceneSwitcher.variable.select="--select variable--"
AdvSceneSwitcher.variable.add="Add new variable"
AdvSceneSwitcher.variable.configure="Configure variable settings"
AdvSceneSwitcher.variable.invalid="Invalid varialbe selection"
AdvSceneSwitcher.variable.invalid="Invalid variable selection"
AdvSceneSwitcher.variable.name="Name:"
AdvSceneSwitcher.variable.value="Current value:"
AdvSceneSwitcher.variable.save="Save / load behavior"
@@ -1584,12 +1621,21 @@ AdvSceneSwitcher.tempVar.twitch.started_at.shoutoutReceived="Shoutout time"
AdvSceneSwitcher.tempVar.twitch.started_at.shoutoutReceived.description="The UTC timestamp (in RFC3339 format) of when the moderator sent the Shoutout."
AdvSceneSwitcher.tempVar.twitch.id.reward="Reward ID"
AdvSceneSwitcher.tempVar.twitch.id.redemption="Redemption ID"
AdvSceneSwitcher.tempVar.twitch.user_id.reward="User ID redeeming the reward"
AdvSceneSwitcher.tempVar.twitch.user_login.reward="User login redeeming the reward"
AdvSceneSwitcher.tempVar.twitch.user_name.reward="User name redeeming the reward"
AdvSceneSwitcher.tempVar.twitch.user_input.reward="User input for reward"
AdvSceneSwitcher.tempVar.twitch.reward.reward="Reward Information"
AdvSceneSwitcher.tempVar.twitch.reward.reward.description="Basic information about the reward that was redeemed, at the time it was redeemed."
AdvSceneSwitcher.tempVar.twitch.reward.reward="Reward Information (JSON)"
AdvSceneSwitcher.tempVar.twitch.reward.reward.description="Basic information about the reward that was redeemed in JSON form, at the time it was redeemed."
AdvSceneSwitcher.tempVar.twitch.reward.id.reward="Reward ID"
AdvSceneSwitcher.tempVar.twitch.reward.id.reward.description="The ID that uniquely identifies the reward."
AdvSceneSwitcher.tempVar.twitch.reward.title.reward="Reward title"
AdvSceneSwitcher.tempVar.twitch.reward.title.reward.description="The rewards title."
AdvSceneSwitcher.tempVar.twitch.reward.prompt.reward="Reward prompt"
AdvSceneSwitcher.tempVar.twitch.reward.prompt.reward.description="The prompt displayed to the viewer if user input is required."
AdvSceneSwitcher.tempVar.twitch.reward.cost.reward="Reward cost"
AdvSceneSwitcher.tempVar.twitch.reward.cost.reward.description="The rewards cost, in Channel Points."
AdvSceneSwitcher.tempVar.twitch.status.reward="Reward status"
AdvSceneSwitcher.tempVar.twitch.status.reward.description="Defaults to unfulfilled. Possible values are unknown, unfulfilled, fulfilled, and canceled."
AdvSceneSwitcher.tempVar.twitch.redeemed_at.reward="Redemption time"
@@ -1604,22 +1650,37 @@ AdvSceneSwitcher.tempVar.twitch.title.reward="Reward title"
AdvSceneSwitcher.tempVar.twitch.cost.reward="Reward cost"
AdvSceneSwitcher.tempVar.twitch.prompt.reward="Reward description"
AdvSceneSwitcher.tempVar.twitch.is_user_input_required.reward="Reward requires user input"
AdvSceneSwitcher.tempVar.twitch.should_redemptions_skip_request_queue.reward="Redemptions skip request queue"
AdvSceneSwitcher.tempVar.twitch.should_redemptions_skip_request_queue.reward.description="Should redemptions be set to fulfilled status immediately when redeemed and skip the request queue instead of the normal unfulfilled status."
AdvSceneSwitcher.tempVar.twitch.max_per_stream.reward="Reward maximum per stream"
AdvSceneSwitcher.tempVar.twitch.max_per_user_per_stream.reward="Reward maximum per user per stream"
AdvSceneSwitcher.tempVar.twitch.background_color.reward="Reward background color"
AdvSceneSwitcher.tempVar.twitch.image.reward="Reward image"
AdvSceneSwitcher.tempVar.twitch.image.reward="Reward image (JSON)"
AdvSceneSwitcher.tempVar.twitch.image.reward.description="Set of custom images of 1x, 2x and 4x sizes for the reward. Can be null if no images have been uploaded."
AdvSceneSwitcher.tempVar.twitch.default_image.reward="Reward default image"
AdvSceneSwitcher.tempVar.twitch.image.url_4x.reward="Reward image URL"
AdvSceneSwitcher.tempVar.twitch.image.url_4x.reward.description="The URL to the custom image for the reward. This field is null if the broadcaster didnt upload images."
AdvSceneSwitcher.tempVar.twitch.default_image.reward="Reward default image (JSON)"
AdvSceneSwitcher.tempVar.twitch.default_image.reward.description="Set of default images of 1x, 2x and 4x sizes for the reward."
AdvSceneSwitcher.tempVar.twitch.global_cooldown.reward="Reward cooldown"
AdvSceneSwitcher.tempVar.twitch.global_cooldown.reward.description="Whether a cooldown is enabled and what the cooldown is in seconds."
AdvSceneSwitcher.tempVar.twitch.cooldown_expires_at.reward="Reward cooldown expiration"
AdvSceneSwitcher.tempVar.twitch.cooldown_expires_at.reward.description="Timestamp of the cooldown expiration. null if the reward isnt on cooldown."
AdvSceneSwitcher.tempVar.twitch.default_image.url_4x.reward="Reward default image URL"
AdvSceneSwitcher.tempVar.twitch.default_image.url_4x.reward.description="The URL to the default image for the reward."
AdvSceneSwitcher.tempVar.twitch.redemptions_redeemed_current_stream.reward="Redemption count"
AdvSceneSwitcher.tempVar.twitch.redemptions_redeemed_current_stream.reward.description="The number of redemptions redeemed during the current live stream. Counts against the max_per_stream limit. null if the broadcasters stream isnt live or max_per_stream isnt enabled."
AdvSceneSwitcher.tempVar.twitch.redemptions_redeemed_current_stream.reward.description="The number of redemptions redeemed during the current live stream. Counts against the max_per_stream limit. null if the broadcasters stream isnt live or max_per_stream isnt enabled."
AdvSceneSwitcher.tempVar.twitch.should_redemptions_skip_request_queue.reward="Redemptions skip request queue"
AdvSceneSwitcher.tempVar.twitch.should_redemptions_skip_request_queue.reward.description="Should redemptions be set to fulfilled status immediately when redeemed and skip the request queue instead of the normal unfulfilled status."
AdvSceneSwitcher.tempVar.twitch.max_per_stream.reward="Maximum redemptions per stream (JSON)"
AdvSceneSwitcher.tempVar.twitch.max_per_stream.is_enabled.reward="Maximum redemptions per stream enabled"
AdvSceneSwitcher.tempVar.twitch.max_per_stream.is_enabled.reward.description="A Boolean value that determines whether the reward applies a limit on the number of redemptions allowed per live stream. Is true if the reward applies a limit."
AdvSceneSwitcher.tempVar.twitch.max_per_stream.max_per_stream.reward="Maximum redemptions per stream"
AdvSceneSwitcher.tempVar.twitch.max_per_stream.max_per_stream.reward.description="The maximum number of redemptions allowed per live stream."
AdvSceneSwitcher.tempVar.twitch.max_per_user_per_stream.reward="Reward maximum per user per stream (JSON)"
AdvSceneSwitcher.tempVar.twitch.max_per_user_per_stream.is_enabled.reward="Maximum redemptions per user per stream enabled"
AdvSceneSwitcher.tempVar.twitch.max_per_user_per_stream.is_enabled.reward.description="A Boolean value that determines whether the reward applies a limit on the number of redemptions allowed per user per live stream. Is true if the reward applies a limit."
AdvSceneSwitcher.tempVar.twitch.max_per_user_per_stream.max_per_user_per_stream.reward="Maximum redemptions per user per stream"
AdvSceneSwitcher.tempVar.twitch.max_per_user_per_stream.max_per_user_per_stream.reward.description="The settings used to determine whether to apply a maximum to the number of redemptions allowed per user per live stream."
AdvSceneSwitcher.tempVar.twitch.global_cooldown.reward="Global cooldown (JSON)"
AdvSceneSwitcher.tempVar.twitch.global_cooldown.reward.description="Whether a cooldown is enabled and what the cooldown is in seconds."
AdvSceneSwitcher.tempVar.twitch.global_cooldown.is_enabled.reward="Global cooldown enabled"
AdvSceneSwitcher.tempVar.twitch.global_cooldown.is_enabled.reward.description="A Boolean value that determines whether to apply a cooldown period. Is true if a cooldown period is enabled."
AdvSceneSwitcher.tempVar.twitch.global_cooldown.global_cooldown_seconds.reward="Global cooldown"
AdvSceneSwitcher.tempVar.twitch.global_cooldown.global_cooldown_seconds.reward.description="The cooldown period, in seconds."
AdvSceneSwitcher.tempVar.twitch.cooldown_expires_at.reward="Reward cooldown expiration"
AdvSceneSwitcher.tempVar.twitch.cooldown_expires_at.reward.description="Timestamp of the cooldown expiration. null if the reward isnt on cooldown."
AdvSceneSwitcher.tempVar.twitch.charity_name="Charity name"
AdvSceneSwitcher.tempVar.twitch.charity_description="Charity description"
@@ -1788,6 +1849,24 @@ AdvSceneSwitcher.tempVar.twitch.to_broadcaster_user_name.raid.description="The b
AdvSceneSwitcher.tempVar.twitch.viewers.raid="Raid participants"
AdvSceneSwitcher.tempVar.twitch.viewers.raid.description="The number of viewers in the raid."
AdvSceneSwitcher.tempVar.twitch.id.user.getInfo="User ID"
AdvSceneSwitcher.tempVar.twitch.id.user.getInfo.description="The user ID of the queried account."
AdvSceneSwitcher.tempVar.twitch.login.user.getInfo="User login"
AdvSceneSwitcher.tempVar.twitch.login.user.getInfo.description="The user login of the queried account."
AdvSceneSwitcher.tempVar.twitch.display_name.user.getInfo="User name"
AdvSceneSwitcher.tempVar.twitch.display_name.user.getInfo.description="The display name of the queried account."
AdvSceneSwitcher.tempVar.twitch.type.user.getInfo="User type"
AdvSceneSwitcher.tempVar.twitch.type.user.getInfo.description="The type of user. Possible values are:\n admin — Twitch administrator\n global_mod\n staff — Twitch staff\n \"\" — Normal user"
AdvSceneSwitcher.tempVar.twitch.broadcaster_type.user.getInfo="Broadcaster type"
AdvSceneSwitcher.tempVar.twitch.broadcaster_type.user.getInfo.description="The type of broadcaster. Possible values are:\n affiliate — An affiliate broadcaster\n partner — A partner broadcaster\n \"\" — A normal broadcaster"
AdvSceneSwitcher.tempVar.twitch.description.user.getInfo="Description"
AdvSceneSwitcher.tempVar.twitch.description.user.getInfo.description="The user's description of their channel."
AdvSceneSwitcher.tempVar.twitch.profile_image_url.user.getInfo="Profile image URL"
AdvSceneSwitcher.tempVar.twitch.offline_image_url.user.getInfo="Offline image URL"
AdvSceneSwitcher.tempVar.twitch.created_at.user.getInfo="Account creation date"
AdvSceneSwitcher.tempVar.twitch.created_at.user.getInfo.description="The UTC date and time that the user's account was created. The timestamp is in RFC3339 format."
AdvSceneSwitcher.tempVar.audio.output_volume="Output volume"
AdvSceneSwitcher.tempVar.audio.output_volume.description="The volume the audio source is outputting."
AdvSceneSwitcher.tempVar.audio.configured_volume="Configured volume"
@@ -1877,8 +1956,11 @@ AdvSceneSwitcher.tempVar.display.height.description="The height of the display w
AdvSceneSwitcher.tempVar.streaming.keyframeInterval="Stream keyframe interval"
AdvSceneSwitcher.tempVar.streaming.keyframeInterval.description="Stream keyframe interval configured in the OBS settings."
AdvSceneSwitcher.tempVar.streaming.streamKey="Stream key"
AdvSceneSwitcher.tempVar.streaming.durationSeconds="Stream duration"
AdvSceneSwitcher.tempVar.streaming.durationSeconds.description="Seconds passed since the stream was started.\nThis value will be zero if the stream is stopped."
AdvSceneSwitcher.tempVar.streaming.serviceName="Service"
AdvSceneSwitcher.tempVar.streaming.serviceName.description="The name of the streaming service being streamed to (e.g. Twitch)"
AdvSceneSwitcher.tempVar.clipboard.text="Clipboard text"
AdvSceneSwitcher.tempVar.clipboard.text.description="The text contained in the clipboard.\nWill be empty if the clipboard does not contain text."
@@ -1958,6 +2040,25 @@ AdvSceneSwitcher.tempVar.streamDeck.column="Key column"
AdvSceneSwitcher.tempVar.streamDeck.data="Data"
AdvSceneSwitcher.tempVar.streamDeck.data.description="The value of the \"Data\" field configured in the action on the Stream Deck side."
AdvSceneSwitcher.tempVar.queue.size="Size"
AdvSceneSwitcher.tempVar.queue.running="Is running"
AdvSceneSwitcher.tempVar.queue.running.description="Returns \"true\" if the queue is started and \"false\" if it is stopped."
AdvSceneSwitcher.tempVar.screenshot.lastScreenshotPath="Last screenshot path"
AdvSceneSwitcher.tempVar.screenshot.lastScreenshotPath.description="The filesystem path to the last screenshot taken."
AdvSceneSwitcher.tempVar.gameCapture.title="Title"
AdvSceneSwitcher.tempVar.gameCapture.title.description="Window title of the application captured by the source."
AdvSceneSwitcher.tempVar.gameCapture.class="Class"
AdvSceneSwitcher.tempVar.gameCapture.class.description="Window class of the application captured by the source."
AdvSceneSwitcher.tempVar.gameCapture.executable="Executable"
AdvSceneSwitcher.tempVar.gameCapture.executable.description="Executable name of the application captured by the source."
AdvSceneSwitcher.tempVar.http.status="Status code"
AdvSceneSwitcher.tempVar.http.body="Message body"
AdvSceneSwitcher.tempVar.http.error="Error"
AdvSceneSwitcher.tempVar.http.error.description="Empty when no error occurred.\nOther possible values:\n\n * Could not establish connection\n * Failed to bind IP address\n * Failed to read connection\n * Failed to write connection\n * Maximum redirect count exceeded\n * Connection handling canceled\n * SSL connection failed\n * SSL certificate loading failed\n * SSL server verification failed\n * Unsupported HTTP multipart boundary characters\n * Compression failed\n * Connection timed out\n * Proxy connection failed\n * Unknown"
AdvSceneSwitcher.selectScene="--select scene--"
AdvSceneSwitcher.selectPreviousScene="Previous Scene"
AdvSceneSwitcher.selectCurrentScene="Current Scene"
@@ -1996,7 +2097,6 @@ AdvSceneSwitcher.settings.suffix.type.list=" (List)"
AdvSceneSwitcher.settings.suffix.type.color=" (Color)"
AdvSceneSwitcher.settings.suffix.type.button=" (Button)"
AdvSceneSwitcher.settings.suffix.type.font=" (Font)"
AdvSceneSwitcher.settings.suffix.type.font=" (Font)"
AdvSceneSwitcher.settings.suffix.type.editableList=" (Editable list)"
AdvSceneSwitcher.settings.suffix.type.frameRate=" (Frame rate)"
AdvSceneSwitcher.settings.suffix.type.group=" (Group)"

View File

@@ -172,7 +172,6 @@ AdvSceneSwitcher.condition.stream.state.start="Transmisión en ejecución"
AdvSceneSwitcher.condition.stream.state.stop="Transmisión detenida"
AdvSceneSwitcher.condition.stream.state.starting="Inicio de transmisión"
AdvSceneSwitcher.condition.stream.state.stopping="Detener transmisión"
AdvSceneSwitcher.condition.stream.entry="{{streamState}}{{keyFrameInterval}}"
AdvSceneSwitcher.condition.record="Grabación"
AdvSceneSwitcher.condition.record.state.start="Grabación en ejecución"
AdvSceneSwitcher.condition.record.state.pause="Grabación en pausa"
@@ -407,7 +406,7 @@ AdvSceneSwitcher.action.sceneOrder.type.moveDown="Mover hacia abajo"
AdvSceneSwitcher.action.sceneOrder.type.moveTop="Mover al principio"
AdvSceneSwitcher.action.sceneOrder.type.moveBottom="Mover al final"
AdvSceneSwitcher.action.sceneOrder.type.movePosition="Mover a la posición"
AdvSceneSwitcher.action.sceneOrder.entry="En{{scenes}}{{actions}}{{sources}}{{position}}"
AdvSceneSwitcher.action.sceneOrder.entry="En{{scenes}}{{actions}}{{sources}}{{sources2}}{{position}}"
AdvSceneSwitcher.action.sceneTransform="Transformar elemento de escena"
AdvSceneSwitcher.action.sceneTransform.getTransform="Obtener transformación"
AdvSceneSwitcher.action.sceneTransform.entry="En{{scenes}}{{action}}{{rotation}}{{sources}}{{settingSelection}}{{singleSettingValue}}"
@@ -439,7 +438,6 @@ AdvSceneSwitcher.action.random="Aleatorio"
AdvSceneSwitcher.action.random.entry="Ejecute aleatoriamente cualquiera de las siguientes macros (las macros en pausa se ignoran)"
AdvSceneSwitcher.action.systray="Notificación de la bandeja del sistema"
AdvSceneSwitcher.action.screenshot="Captura de pantalla"
AdvSceneSwitcher.action.screenshot.mainOutput="Salida principal de OBS"
AdvSceneSwitcher.action.profile="Perfil"
AdvSceneSwitcher.action.profile.entry="Cambiar perfil activo a {{profiles}}"
AdvSceneSwitcher.action.sceneCollection="Colección de escenas"

View File

@@ -597,7 +597,7 @@ AdvSceneSwitcher.action.sceneOrder.type.moveDown="Déplacer vers le bas"
AdvSceneSwitcher.action.sceneOrder.type.moveTop="Déplacer en haut"
AdvSceneSwitcher.action.sceneOrder.type.moveBottom="Déplacer en bas"
AdvSceneSwitcher.action.sceneOrder.type.movePosition="Déplacer à la position"
AdvSceneSwitcher.action.sceneOrder.entry="Sur{{scenes}}{{actions}}{{sources}}{{position}}"
AdvSceneSwitcher.action.sceneOrder.entry="Sur{{scenes}}{{actions}}{{sources}}{{sources2}}{{position}}"
AdvSceneSwitcher.action.sceneTransform="Transformation de l'élément de la scène"
AdvSceneSwitcher.action.sceneTransform.type.manual="Transform"
AdvSceneSwitcher.action.sceneTransform.type.reset="Réinitialiser la transformation"
@@ -647,7 +647,6 @@ AdvSceneSwitcher.action.screenshot.save.default="Par défaut"
AdvSceneSwitcher.action.screenshot.save.custom="Personnalisé"
AdvSceneSwitcher.action.screenshot.type.source="Source"
AdvSceneSwitcher.action.screenshot.type.scene="Scène"
AdvSceneSwitcher.action.screenshot.mainOutput="Sortie principale d'OBS"
AdvSceneSwitcher.action.screenshot.blackscreenNote="Les sources ou les scènes qui ne sont pas toujours rendues peuvent entraîner des parties de captures d'écran vides."
AdvSceneSwitcher.action.screenshot.entry="Capturer{{targetType}}{{sources}}{{scenes}}et enregistrer à l'emplacement{{saveType}}{{variables}}"
AdvSceneSwitcher.action.profile="Profil"
@@ -718,8 +717,6 @@ AdvSceneSwitcher.action.projector.type.multiview="Multivue"
AdvSceneSwitcher.action.projector.display="Affichage"
AdvSceneSwitcher.action.projector.windowed="Fenêtré"
AdvSceneSwitcher.action.projector.fullscreen="Plein écran"
AdvSceneSwitcher.action.projector.entry="Ouvrir le projecteur{{windowTypes}}de{{types}}{{scenes}}{{sources}}"
AdvSceneSwitcher.action.projector.entry.monitor="sur{{monitors}}"
AdvSceneSwitcher.action.midi="MIDI"
AdvSceneSwitcher.action.midi.entry="Envoyer un message à{{device}}:"
AdvSceneSwitcher.action.midi.entry.listen="Définir la sélection de messages MIDI sur les messages entrants de{{listenDevices}}:{{listenButton}}"

View File

@@ -925,7 +925,7 @@ AdvSceneSwitcher.action.sceneOrder.type.moveDown="下へ移動"
AdvSceneSwitcher.action.sceneOrder.type.moveTop="トップへ移動"
AdvSceneSwitcher.action.sceneOrder.type.moveBottom="一番下へ移動"
AdvSceneSwitcher.action.sceneOrder.type.movePosition="ポジションへの移動"
AdvSceneSwitcher.action.sceneOrder.entry="オン{{scenes}}{{actions}}{{sources}}{{position}}"
AdvSceneSwitcher.action.sceneOrder.entry="オン{{scenes}}{{actions}}{{sources}}{{sources2}}{{position}}"
AdvSceneSwitcher.action.sceneTransform="シーンアイテムの変形"
AdvSceneSwitcher.action.sceneTransform.type.manual="変形"
AdvSceneSwitcher.action.sceneTransform.type.setSingleSetting="トランスフォーム設定を行う"
@@ -979,7 +979,6 @@ AdvSceneSwitcher.action.screenshot.save.default="デフォルト"
AdvSceneSwitcher.action.screenshot.save.custom="カスタム"
; AdvSceneSwitcher.action.screenshot.type.source="Source"
AdvSceneSwitcher.action.screenshot.type.scene="シーン"
AdvSceneSwitcher.action.screenshot.mainOutput="OBSの主な出力"
AdvSceneSwitcher.action.screenshot.blackscreenNote="常にレンダリングされるわけではないソースやシーンにより、スクリーンショットの一部が空白のままになる場合があります。"
AdvSceneSwitcher.action.screenshot.entry="スクリーンショット{{targetType}}{{sources}}{{scenes}}を作成し、{{saveType}}{{variables}}の場所に保存します"
AdvSceneSwitcher.action.profile="プロファイル"
@@ -1068,8 +1067,6 @@ AdvSceneSwitcher.action.projector.type.multiview="マルチビュー"
AdvSceneSwitcher.action.projector.display="ディスプレイ"
AdvSceneSwitcher.action.projector.windowed="ウィンドウ"
AdvSceneSwitcher.action.projector.fullscreen="フルスクリーン"
AdvSceneSwitcher.action.projector.entry="{{types}}{{scenes}}{{sources}}の{{windowTypes}}プロジェクターを開きます"
AdvSceneSwitcher.action.projector.entry.monitor="{{monitors}}上で"
; AdvSceneSwitcher.action.midi="MIDI"
AdvSceneSwitcher.action.midi.entry="メッセージを{{device}}に送信します:"
AdvSceneSwitcher.action.midi.entry.listen="MIDI メッセージの選択を {{listenDevices}} で受信するメッセージに設定します:{{listenButton}}"
@@ -1089,10 +1086,10 @@ AdvSceneSwitcher.action.twitch.type.clip.create="ストリームクリップの
AdvSceneSwitcher.action.twitch.type.chat.announcement.send="チャット通知の送信"
AdvSceneSwitcher.action.twitch.type.chat.emoteOnly.enable="チャットのエモート専用モードを有効にする"
AdvSceneSwitcher.action.twitch.type.chat.emoteOnly.disable="チャットのエモート専用モードを無効にする"
AdvSceneSwitcher.action.twitch.type.sendChatMessage="チャットメッセージを送る"
AdvSceneSwitcher.action.twitch.type.chat.sendMessage="チャットメッセージを送る"
AdvSceneSwitcher.action.twitch.categorySelectionDisabled="Twitchアカウントを選択しないとカテゴリを選択できません"
; AdvSceneSwitcher.action.twitch.entry.default="On{{account}}{{actions}}{{streamTitle}}{{category}}{{markerDescription}}{{clipHasDelay}}{{duration}}{{announcementColor}}{{channel}}"
AdvSceneSwitcher.action.twitch.entry.chat="{{channel}}{{streamTitle}}{{category}}{{markerDescription}}{{clipHasDelay}}{{duration}}{{announcementColor}} でアカウント{{account}}{{actions}}を使用しています"
AdvSceneSwitcher.action.twitch.entry.chat="{{channel}}でアカウント{{account}}{{actions}}を使用しています"
AdvSceneSwitcher.action.twitch.title.title="タイトルを入力してください"
AdvSceneSwitcher.action.twitch.marker.description="マーカーの説明"
AdvSceneSwitcher.action.twitch.clip.hasDelay="クリップをキャプチャする前にわずかな遅延を追加します"

View File

@@ -395,7 +395,6 @@ AdvSceneSwitcher.condition.stream.state.stop="Streaming parado"
AdvSceneSwitcher.condition.stream.state.starting="Iniciando o streaming"
AdvSceneSwitcher.condition.stream.state.stopping="Parando o streaming"
AdvSceneSwitcher.condition.stream.state.keyFrameInterval="Intervalo de keyframe igual a"
AdvSceneSwitcher.condition.stream.entry="{{streamState}}{{keyFrameInterval}}"
AdvSceneSwitcher.condition.record="Gravação"
AdvSceneSwitcher.condition.record.state.start="Gravação em execução"
AdvSceneSwitcher.condition.record.state.pause="Gravação pausada"
@@ -907,7 +906,7 @@ AdvSceneSwitcher.action.sceneOrder.type.moveDown="Mover para baixo"
AdvSceneSwitcher.action.sceneOrder.type.moveTop="Mover para o topo"
AdvSceneSwitcher.action.sceneOrder.type.moveBottom="Mover para o fundo"
AdvSceneSwitcher.action.sceneOrder.type.movePosition="Mover para posição"
AdvSceneSwitcher.action.sceneOrder.entry="Em{{scenes}}{{actions}}{{sources}}{{position}}"
AdvSceneSwitcher.action.sceneOrder.entry="Em{{scenes}}{{actions}}{{sources}}{{sources2}}{{position}}"
AdvSceneSwitcher.action.sceneTransform="Transformação de item de cena"
AdvSceneSwitcher.action.sceneTransform.type.manual="Definir string de transformação"
AdvSceneSwitcher.action.sceneTransform.type.setSingleSetting="Definir configuração de transformação"
@@ -961,7 +960,6 @@ AdvSceneSwitcher.action.screenshot.save.default="Padrão"
AdvSceneSwitcher.action.screenshot.save.custom="Personalizado"
AdvSceneSwitcher.action.screenshot.type.source="Fonte"
AdvSceneSwitcher.action.screenshot.type.scene="Cena"
AdvSceneSwitcher.action.screenshot.mainOutput="Saída principal do OBS"
AdvSceneSwitcher.action.screenshot.blackscreenNote="Fontes ou cenas, que nem sempre são renderizadas, podem resultar em algumas partes das capturas de tela permanecendo em branco."
AdvSceneSwitcher.action.screenshot.entry="Captura de tela{{targetType}}{{sources}}{{scenes}}e salvar em{{saveType}}{{variables}}localização"
AdvSceneSwitcher.action.profile="Perfil"
@@ -1050,8 +1048,6 @@ AdvSceneSwitcher.action.projector.type.multiview="Multivisão"
AdvSceneSwitcher.action.projector.display="Exibição"
AdvSceneSwitcher.action.projector.windowed="Janela"
AdvSceneSwitcher.action.projector.fullscreen="Tela cheia"
AdvSceneSwitcher.action.projector.entry="Abrir{{windowTypes}}projetor de{{types}}{{scenes}}{{sources}}"
AdvSceneSwitcher.action.projector.entry.monitor="em{{monitors}}"
AdvSceneSwitcher.action.midi="MIDI"
AdvSceneSwitcher.action.midi.entry="Enviar mensagem para{{device}}:"
AdvSceneSwitcher.action.midi.entry.listen="Definir seleção de mensagem MIDI para mensagens recebidas em{{listenDevices}}:{{listenButton}}"
@@ -1071,10 +1067,10 @@ AdvSceneSwitcher.action.twitch.type.clip.create="Criar clipe da transmissão"
AdvSceneSwitcher.action.twitch.type.chat.announcement.send="Enviar anúncio de chat"
AdvSceneSwitcher.action.twitch.type.chat.emoteOnly.enable="Habilitar modo apenas emotes no chat"
AdvSceneSwitcher.action.twitch.type.chat.emoteOnly.disable="Desabilitar modo apenas emotes no chat"
AdvSceneSwitcher.action.twitch.type.sendChatMessage="Enviar mensagem de chat"
AdvSceneSwitcher.action.twitch.type.chat.sendMessage="Enviar mensagem de chat"
AdvSceneSwitcher.action.twitch.categorySelectionDisabled="Não é possível selecionar categoria sem selecionar uma conta Twitch primeiro!"
AdvSceneSwitcher.action.twitch.entry.default="Em{{account}}{{actions}}{{streamTitle}}{{category}}{{markerDescription}}{{clipHasDelay}}{{duration}}{{announcementColor}}{{channel}}"
AdvSceneSwitcher.action.twitch.entry.chat="Usando conta{{account}}{{actions}}em{{channel}}{{streamTitle}}{{category}}{{markerDescription}}{{clipHasDelay}}{{duration}}{{announcementColor}}"
AdvSceneSwitcher.action.twitch.entry.default="Em{{account}}{{actions}}{{streamTitle}}{{category}}{{markerDescription}}{{clipHasDelay}}{{duration}}{{announcementColor}}{{channel}}{{pointsReward}}"
AdvSceneSwitcher.action.twitch.entry.chat="Usando conta{{account}}{{actions}}em{{channel}}"
AdvSceneSwitcher.action.twitch.title.title="Digite o título"
AdvSceneSwitcher.action.twitch.marker.description="Descreva o marcador"
AdvSceneSwitcher.action.twitch.clip.hasDelay="Adicionar um leve atraso antes de capturar o clipe"

View File

@@ -149,7 +149,6 @@ AdvSceneSwitcher.condition.stream.state.start="Yayın çalışıyor"
AdvSceneSwitcher.condition.stream.state.stop="Yayın durdu"
AdvSceneSwitcher.condition.stream.state.starting="Yayın başlıyor"
AdvSceneSwitcher.condition.stream.state.stopping="Yayın duruyor"
AdvSceneSwitcher.condition.stream.entry="{{streamState}}{{keyFrameInterval}}"
AdvSceneSwitcher.condition.record="Kayıt"
AdvSceneSwitcher.condition.record.state.start="Kayıt Çalışıyor"
AdvSceneSwitcher.condition.record.state.pause="Kayıt durakladı"
@@ -336,7 +335,7 @@ AdvSceneSwitcher.action.sceneOrder.type.moveDown="Aşağı indir"
AdvSceneSwitcher.action.sceneOrder.type.moveTop="En üste taşı"
AdvSceneSwitcher.action.sceneOrder.type.moveBottom="Aşağıya taşı"
AdvSceneSwitcher.action.sceneOrder.type.movePosition="Konuma taşı"
AdvSceneSwitcher.action.sceneOrder.entry="Açık{{scenes}}{{actions}}{{sources}}{{position}}"
AdvSceneSwitcher.action.sceneOrder.entry="Açık{{scenes}}{{actions}}{{sources}}{{sources2}}{{position}}"
AdvSceneSwitcher.action.sceneTransform="Sahne öğesi dönüşümü"
AdvSceneSwitcher.action.sceneTransform.getTransform="Dönüşümü al"
AdvSceneSwitcher.action.sceneTransform.entry="Açık{{scenes}}{{action}}{{rotation}}{{sources}}{{settingSelection}}{{singleSettingValue}}"
@@ -357,7 +356,6 @@ AdvSceneSwitcher.action.random="Rastgele"
AdvSceneSwitcher.action.random.entry="Aşağıdaki makrolardan herhangi birini rastgele çalıştırın (duraklatılmış makrolar yoksayılır)"
AdvSceneSwitcher.action.systray="Sistem tepsisi bildirimi"
AdvSceneSwitcher.action.screenshot="Ekran görüntüsü"
AdvSceneSwitcher.action.screenshot.mainOutput="OBS'nin ana çıkışı"
AdvSceneSwitcher.action.profile="Profil"
AdvSceneSwitcher.action.profile.entry="Aktif profili şununla değiştir: {{profiles}}"
AdvSceneSwitcher.action.sceneCollection="Sahne koleksiyonu"

File diff suppressed because it is too large Load Diff

13156
deps/exprtk/exprtk.hpp vendored

File diff suppressed because it is too large Load Diff

2
deps/libremidi vendored

View File

@@ -1,6 +1,5 @@
#include "advanced-scene-switcher.hpp"
#include "backup.hpp"
#include "curl-helper.hpp"
#include "log-helper.hpp"
#include "macro-helpers.hpp"
#include "obs-module-helper.hpp"
@@ -19,10 +18,15 @@
#include <obs-frontend-api.h>
#include <QAction>
#include <QDirIterator>
#include <QLibrary>
#include <QMainWindow>
#include <QTextStream>
#include <regex>
#ifdef _WIN32
#include <Windows.h>
#endif
namespace advss {
AdvSceneSwitcher *AdvSceneSwitcher::window = nullptr;
@@ -307,7 +311,7 @@ void SwitcherData::Thread()
}
}
ResetForNextInterval();
RunIntervalResetSteps();
if (match) {
if (macroMatch) {
@@ -357,14 +361,6 @@ void SwitcherData::SetPreconditions()
InvalidateMacroTempVarValues();
}
void SwitcherData::ResetForNextInterval()
{
// Plugin reset functions
for (const auto &func : resetIntervalSteps) {
func();
}
}
bool SwitcherData::CheckForMatch(OBSWeakSource &scene,
OBSWeakSource &transition, int &linger,
bool &setPrevSceneAfterLinger,
@@ -445,7 +441,7 @@ void AutoStartActionQueues();
void SwitcherData::Start()
{
if (!(th && th->isRunning())) {
ResetForNextInterval();
RunIntervalResetSteps();
ResetMacros();
AutoStartActionQueues();
@@ -453,10 +449,7 @@ void SwitcherData::Start()
th = new SwitcherThread();
th->start((QThread::Priority)threadPriority);
// Will be overwritten quickly but might be useful
writeToStatusFile("Advanced Scene Switcher running");
SendWebsocketVendorEvent("AdvancedSceneSwitcherStarted",
nullptr);
RunStartSteps();
}
if (showSystemTrayNotifications) {
@@ -483,11 +476,7 @@ void SwitcherData::Stop()
th->wait();
delete th;
th = nullptr;
writeToStatusFile("Advanced Scene Switcher stopped");
if (!obsIsShuttingDown) {
SendWebsocketVendorEvent("AdvancedSceneSwitcherStopped",
nullptr);
}
RunStopSteps();
}
if (showSystemTrayNotifications) {
@@ -735,7 +724,7 @@ void OpenSettingsWindow()
}
}
void AdvSceneSwitcher::HighligthMacroSettingsButton(bool enable)
void AdvSceneSwitcher::HighlightMacroSettingsButton(bool enable)
{
static QObject *highlight = nullptr;
if ((highlight && enable) || (!highlight && !enable)) {
@@ -755,13 +744,13 @@ void AdvSceneSwitcher::HighligthMacroSettingsButton(bool enable)
highlight = HighlightWidget(ui->macroSettings, Qt::green);
}
void HighligthMacroSettingsButton(bool enable)
void HighlightMacroSettingsButton(bool enable)
{
auto window = GetSettingsWindow();
if (!window) {
return;
}
static_cast<AdvSceneSwitcher *>(window)->HighligthMacroSettingsButton(
static_cast<AdvSceneSwitcher *>(window)->HighlightMacroSettingsButton(
enable);
}

View File

@@ -97,7 +97,7 @@ public:
void SetConditionData(Macro &m);
void SwapActions(Macro *m, int pos1, int pos2);
void SwapConditions(Macro *m, int pos1, int pos2);
void HighligthMacroSettingsButton(bool enable = true);
void HighlightMacroSettingsButton(bool enable = true);
public slots:
void on_macroAdd_clicked();
@@ -386,6 +386,6 @@ private:
};
void OpenSettingsWindow();
void HighligthMacroSettingsButton(bool enable);
void HighlightMacroSettingsButton(bool enable);
} // namespace advss

View File

@@ -344,6 +344,10 @@ void AdvSceneSwitcher::on_importSettings_clicked()
return;
}
// We have to make sure to that no macro is currently being edited while
// the new settings are loaded
ui->macros->clearSelection();
std::lock_guard<std::mutex> lock(switcher->m);
switcher->LoadSettings(obj);
switcher->lastImportPath = path.toStdString();

View File

@@ -21,6 +21,29 @@ bool FileSwitch::pause = false;
static QObject *addPulse = nullptr;
static std::hash<std::string> strHash;
static void writeToStatusFile(const QString &msg)
{
if (!GetSwitcher() || !GetSwitcher()->fileIO.writeEnabled ||
GetSwitcher()->fileIO.writePath.empty()) {
return;
}
QFile file(QString::fromStdString(GetSwitcher()->fileIO.writePath));
if (file.open(QIODevice::ReadWrite)) {
QTextStream stream(&file);
stream << msg << Qt::endl;
}
file.close();
}
static bool _ = []() {
AddStartStep(
[]() { writeToStatusFile("Advanced Scene Switcher running"); });
AddStopStep(
[]() { writeToStatusFile("Advanced Scene Switcher stopped"); });
return true;
}();
void AdvSceneSwitcher::on_browseButton_clicked()
{
QString path = QFileDialog::getOpenFileName(
@@ -113,24 +136,6 @@ void SwitcherData::writeSceneInfoToFile()
}
}
void SwitcherData::writeToStatusFile(const QString &msg)
{
if (!fileIO.writeEnabled || fileIO.writePath.empty()) {
return;
}
QFile file(QString::fromStdString(fileIO.writePath));
if (file.open(QIODevice::ReadWrite)) {
QTextStream stream(&file);
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
stream << msg << "\n";
#else
stream << msg << Qt::endl;
#endif
}
file.close();
}
bool SwitcherData::checkSwitchInfoFromFile(OBSWeakSource &scene,
OBSWeakSource &transition)
{

View File

@@ -384,7 +384,11 @@ static void getProcessListProcps2(QStringList &processes)
return;
}
while ((stack = procps_pids_get_(info, PIDS_FETCH_TASKS_ONLY))) {
#ifdef PROCPS2_USE_INFO
auto cmd = PIDS_VAL(0, str, stack, info);
#else
auto cmd = PIDS_VAL(0, str, stack);
#endif
QString procName(cmd);
if (!procName.isEmpty() && !processes.contains(procName)) {
processes << procName;

View File

@@ -17,6 +17,9 @@ static inline void populateActionSelection(QComboBox *list)
QString entry(obs_module_text(action._name.c_str()));
if (list->findText(entry) == -1) {
list->addItem(entry);
qobject_cast<QListView *>(list->view())
->setRowHidden(list->count() - 1,
action._hidden);
} else {
blog(LOG_WARNING,
"did not insert duplicate action entry with name \"%s\"",

View File

@@ -12,6 +12,7 @@ struct MacroActionInfo {
std::function<std::shared_ptr<MacroAction>(Macro *m)> _create = nullptr;
CreateActionWidget _createWidget = nullptr;
std::string _name;
bool _hidden = false;
};
class MacroActionFactory {

View File

@@ -309,11 +309,11 @@ MacroActionMacroEdit::MacroActionMacroEdit(
_loading = false;
}
void HighligthMacroSettingsButton(bool enable);
void HighlightMacroSettingsButton(bool enable);
MacroActionMacroEdit::~MacroActionMacroEdit()
{
HighligthMacroSettingsButton(false);
HighlightMacroSettingsButton(false);
}
void MacroActionMacroEdit::UpdateEntryData()
@@ -480,7 +480,7 @@ void MacroActionMacroEdit::SetWidgetVisibility()
_inputs->setVisible(_entryData->_action ==
MacroActionMacro::Action::RUN &&
_entryData->_runOptions.setInputs);
HighligthMacroSettingsButton(_entryData->_action ==
HighlightMacroSettingsButton(_entryData->_action ==
MacroActionMacro::Action::RUN &&
_entryData->_runOptions.setInputs &&
!_inputs->HasInputsToSet());

View File

@@ -6,14 +6,17 @@
namespace advss {
MacroActionScript::MacroActionScript(Macro *m, const std::string &id,
const OBSData &defaultSettings,
const std::string &propertiesSignalName,
const std::string &triggerSignal,
const std::string &completionSignal)
MacroActionScript::MacroActionScript(
Macro *m, const std::string &id, const OBSData &defaultSettings,
const std::string &propertiesSignalName,
const std::string &triggerSignalName,
const std::string &completionSignalName,
const std::string &newInstanceSignalName,
const std::string &deletedInstanceSignalName)
: MacroAction(m),
MacroSegmentScript(defaultSettings, propertiesSignalName,
triggerSignal, completionSignal),
triggerSignalName, completionSignalName,
newInstanceSignalName, deletedInstanceSignalName),
_id(id)
{
}
@@ -89,4 +92,29 @@ void MacroActionScript::WaitForCompletion() const
}
}
void MacroActionScript::RegisterTempVarHelper(const std::string &variableId,
const std::string &name,
const std::string &helpText)
{
AddTempvar(variableId, name, helpText);
}
void MacroActionScript::DeregisterAllTempVarsHelper()
{
MacroSegment::SetupTempVars();
}
void MacroActionScript::SetTempVarValueHelper(const std::string &variableId,
const std::string &value)
{
MacroAction::SetTempVarValue(variableId, value);
}
void MacroActionScript::SetupTempVars()
{
// This just exists so MacroSegment::SetupTempVars() is not called.
// We want the ScriptHandler to handle the registration and clearing of
// the temp vars.
}
} // namespace advss

View File

@@ -10,8 +10,10 @@ public:
MacroActionScript(Macro *m, const std::string &id,
const OBSData &defaultSettings,
const std::string &propertiesSignalName,
const std::string &triggerSignal,
const std::string &signalComplete);
const std::string &triggerSignalName,
const std::string &completionSignalName,
const std::string &newInstanceSignalName,
const std::string &deletedInstanceSignalName);
MacroActionScript(const advss::MacroActionScript &);
bool PerformAction();
void LogAction() const;
@@ -22,6 +24,13 @@ public:
private:
void WaitForCompletion() const;
void RegisterTempVarHelper(const std::string &variableId,
const std::string &name,
const std::string &helpText);
void DeregisterAllTempVarsHelper();
void SetTempVarValueHelper(const std::string &variableId,
const std::string &value);
void SetupTempVars();
std::string _id = "";
};

File diff suppressed because it is too large Load Diff

View File

@@ -28,8 +28,8 @@ public:
int GetSegmentIndexValue() const;
void ResolveVariablesToFixedValues();
enum class Type {
SET_FIXED_VALUE,
enum class Action {
SET_VALUE,
APPEND,
APPEND_VAR,
INCREMENT,
@@ -52,18 +52,19 @@ public:
SWAP_VALUES,
TRIM,
CHANGE_CASE,
RANDOM_NUMBER,
};
Type _type = Type::SET_FIXED_VALUE;
Action _action = Action::SET_VALUE;
std::weak_ptr<Variable> _variable;
std::weak_ptr<Variable> _variable2;
StringVariable _strValue = "";
double _numValue = 0;
int _subStringStart = 0;
int _subStringSize = 0;
DoubleVariable _numValue = 0;
IntVariable _subStringStart = 0;
IntVariable _subStringSize = 0;
RegexConfig _subStringRegex = RegexConfig::PartialMatchRegexConfig();
std::string _regexPattern = ".*";
int _regexMatchIdx = 0;
IntVariable _regexMatchIdx = 0;
RegexConfig _findRegex;
StringVariable _findStr = obs_module_text(
"AdvSceneSwitcher.action.variable.findAndReplace.find");
@@ -99,6 +100,9 @@ public:
};
CaseType _caseType = CaseType::LOWER_CASE;
DoubleVariable _randomNumberStart = 0;
DoubleVariable _randomNumberEnd = 100;
bool _generateInteger = true;
private:
void DecrementCurrentSegmentVariableRef();
@@ -108,6 +112,7 @@ private:
void HandleMathExpression(Variable *);
void HandleCaseChange(Variable *);
void SetToSceneItemName(Variable *);
void GenerateRandomNumber(Variable *);
std::weak_ptr<MacroSegment> _macroSegment;
int _segmentIdxLoadValue = -1;
@@ -136,15 +141,15 @@ private slots:
void Variable2Changed(const QString &);
void ActionChanged(int);
void StrValueChanged();
void NumValueChanged(double);
void NumValueChanged(const NumberVariable<double> &value);
void SegmentIndexChanged(const IntVariable &);
void UpdateSegmentVariableValue();
void MacroSegmentOrderChanged();
void SubStringStartChanged(int val);
void SubStringSizeChanged(int val);
void SubStringStartChanged(const NumberVariable<int> &start);
void SubStringSizeChanged(const NumberVariable<int> &size);
void SubStringRegexChanged(const RegexConfig &conf);
void RegexPatternChanged();
void RegexMatchIdxChanged(int val);
void RegexMatchIdxChanged(const NumberVariable<int> &index);
void FindStrValueChanged();
void FindRegexChanged(const RegexConfig &conf);
void ReplaceStrValueChanged();
@@ -161,6 +166,9 @@ private slots:
void StringLengthChanged(const NumberVariable<int> &);
void CharSelectionChanged(const QString &);
void CaseTypeChanged(int index);
void RandomNumberStartChanged(const NumberVariable<double> &);
void RandomNumberEndChanged(const NumberVariable<double> &);
void GenerateIntegerChanged(int);
signals:
void HeaderInfoChanged(const QString &);
@@ -173,18 +181,18 @@ private:
VariableSelection *_variables2;
FilterComboBox *_actions;
VariableTextEdit *_strValue;
QDoubleSpinBox *_numValue;
VariableDoubleSpinBox *_numValue;
MacroSegmentSelection *_segmentIdx;
QLabel *_segmentValueStatus;
ResizingPlainTextEdit *_segmentValue;
QVBoxLayout *_substringLayout;
QHBoxLayout *_subStringIndexEntryLayout;
QHBoxLayout *_subStringRegexEntryLayout;
QSpinBox *_subStringStart;
QSpinBox *_subStringSize;
VariableSpinBox *_subStringStart;
VariableSpinBox *_subStringSize;
RegexConfigWidget *_substringRegex;
ResizingPlainTextEdit *_regexPattern;
QSpinBox *_regexMatchIdx;
VariableSpinBox *_regexMatchIdx;
QHBoxLayout *_findReplaceLayout;
RegexConfigWidget *_findRegex;
VariableTextEdit *_findStr;
@@ -206,6 +214,10 @@ private:
VariableSpinBox *_stringLength;
SingleCharSelection *_paddingCharSelection;
FilterComboBox *_caseType;
VariableDoubleSpinBox *_randomNumberStart;
VariableDoubleSpinBox *_randomNumberEnd;
QCheckBox *_generateInteger;
QVBoxLayout *_randomLayout;
QHBoxLayout *_entryLayout;
std::shared_ptr<MacroActionVariable> _entryData;

View File

@@ -9,7 +9,6 @@
#include <QPushButton>
#include <QHBoxLayout>
#include <QTimer>
#include <QListWidget>
namespace advss {

View File

@@ -27,6 +27,9 @@ bool MacroConditionQueue::CheckCondition()
return false;
}
SetTempVarValue("size", std::to_string(queue->Size()));
SetTempVarValue("running", queue->IsRunning());
switch (_condition) {
case Condition::STARTED:
return queue->IsRunning();
@@ -63,6 +66,18 @@ std::string MacroConditionQueue::GetShortDesc() const
return GetActionQueueName(_queue);
}
void MacroConditionQueue::SetupTempVars()
{
MacroCondition::SetupTempVars();
AddTempvar("size",
obs_module_text("AdvSceneSwitcher.tempVar.queue.size"));
AddTempvar(
"running",
obs_module_text("AdvSceneSwitcher.tempVar.queue.running"),
obs_module_text(
"AdvSceneSwitcher.tempVar.queue.running.description"));
}
static inline void populateQueueTypeSelection(QComboBox *list)
{
for (const auto &[_, name] : conditionTypes) {
@@ -141,6 +156,7 @@ void MacroConditionQueueEdit::UpdateEntryData()
}
_conditions->setCurrentIndex(static_cast<int>(_entryData->_condition));
_queues->SetActionQueue(_entryData->_queue);
_size->SetValue(_entryData->_size);
SetWidgetVisibility();
}

View File

@@ -32,6 +32,8 @@ public:
IntVariable _size = 1;
private:
void SetupTempVars();
static bool _registered;
static const std::string id;
};

View File

@@ -8,10 +8,14 @@ namespace advss {
MacroConditionScript::MacroConditionScript(
Macro *m, const std::string &id, const OBSData &defaultSettings,
const std::string &propertiesSignalName,
const std::string &triggerSignal, const std::string &completionSignal)
const std::string &triggerSignalName,
const std::string &completionSignalName,
const std::string &newInstanceSignalName,
const std::string &deletedInstanceSignalName)
: MacroCondition(m),
MacroSegmentScript(defaultSettings, propertiesSignalName,
triggerSignal, completionSignal),
triggerSignalName, completionSignalName,
newInstanceSignalName, deletedInstanceSignalName),
_id(id)
{
}
@@ -76,4 +80,29 @@ void MacroConditionScript::WaitForCompletion() const
}
}
void MacroConditionScript::RegisterTempVarHelper(const std::string &variableId,
const std::string &name,
const std::string &helpText)
{
AddTempvar(variableId, name, helpText);
}
void MacroConditionScript::DeregisterAllTempVarsHelper()
{
MacroSegment::SetupTempVars();
}
void MacroConditionScript::SetTempVarValueHelper(const std::string &variableId,
const std::string &value)
{
MacroCondition::SetTempVarValue(variableId, value);
}
void MacroConditionScript::SetupTempVars()
{
// This just exists so MacroSegment::SetupTempVars() is not called.
// We want the ScriptHandler to handle the registration and clearing of
// the temp vars.
}
} // namespace advss

View File

@@ -10,8 +10,10 @@ public:
MacroConditionScript(Macro *m, const std::string &id,
const OBSData &defaultSettings,
const std::string &propertiesSignalName,
const std::string &triggerSignal,
const std::string &signalComplete);
const std::string &triggerSignalName,
const std::string &completionSignalName,
const std::string &newInstanceSignalName,
const std::string &deletedInstanceSignalName);
MacroConditionScript(const advss::MacroConditionScript &);
bool CheckCondition();
bool Save(obs_data_t *obj) const;
@@ -20,6 +22,13 @@ public:
private:
void WaitForCompletion() const;
void RegisterTempVarHelper(const std::string &variableId,
const std::string &name,
const std::string &helpText);
void DeregisterAllTempVarsHelper();
void SetTempVarValueHelper(const std::string &variableId,
const std::string &value);
void SetupTempVars();
std::string _id = "";
};

View File

@@ -67,9 +67,7 @@ void SetMacroSwitchedScene(bool value)
{
static bool setupDone = false;
if (!setupDone) {
// Will always be called with switcher lock already held
AddIntervalResetStep([]() { macroSceneSwitched = false; },
false);
AddIntervalResetStep([]() { macroSceneSwitched = false; });
setupDone = true;
}
macroSceneSwitched = value;
@@ -109,6 +107,14 @@ bool MacroWasPausedSince(
return macro ? macro->WasPausedSince(time) : false;
}
bool MacroWasCheckedSinceLastStart(Macro *macro)
{
if (!macro) {
return false;
}
return macro->LastConditionCheckTime().time_since_epoch().count() != 0;
}
void AddMacroHelperThread(Macro *macro, std::thread &&newThread)
{
if (!macro) {

View File

@@ -51,6 +51,7 @@ EXPORT bool MacroIsPaused(Macro *);
EXPORT bool
MacroWasPausedSince(Macro *,
const std::chrono::high_resolution_clock::time_point &);
EXPORT bool MacroWasCheckedSinceLastStart(Macro *);
EXPORT void AddMacroHelperThread(Macro *, std::thread &&);

View File

@@ -181,15 +181,7 @@ void MacroInputEdit::SetInputVariablesAndValues(
_variables = inputs;
_values = values;
if ((size_t)_values.size() < _variables._inputVariables.size()) {
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
for (int i = 0;
_variables._inputVariables.size() - (size_t)_values.size();
i++) {
_values.push_back({});
}
#else
_values.resize(_variables._inputVariables.size());
#endif
}
SetupWidgets();
}
@@ -199,7 +191,7 @@ bool MacroInputEdit::HasInputsToSet() const
return !_variables._inputVariables.empty();
}
void HighligthMacroSettingsButton(bool enable);
void HighlightMacroSettingsButton(bool enable);
void MacroInputEdit::SetupWidgets()
{

View File

@@ -29,6 +29,10 @@ static constexpr std::string_view defaultSettingsParam = "default_settings";
static constexpr std::string_view propertiesSignalParam =
"properties_signal_name";
static constexpr std::string_view triggerSignalParam = "trigger_signal_name";
static constexpr std::string_view newInstanceSignalNameParam =
"new_instance_signal_name";
static constexpr std::string_view deletedInstanceSignalNameParam =
"deleted_instance_signal_name";
static std::string getRegisterScriptSegmentDeclString(const char *funcName)
{
@@ -72,16 +76,55 @@ static const std::string deregisterScriptConditionDeclString =
/* Script variables */
static constexpr std::string_view valueParam = "value";
static constexpr std::string_view tempVarIdParam = "temp_var_id";
static constexpr std::string_view tempVarNameParam = "temp_var_name";
static constexpr std::string_view tempVarHelpParam = "temp_var_help";
static constexpr std::string_view getVariableValueFuncName =
"advss_get_variable_value";
static constexpr std::string_view setVariableValueFuncName =
"advss_set_variable_value";
static constexpr std::string_view registerTempVarFuncName =
"advss_register_temp_var";
static constexpr std::string_view deregisterAllTempVarsFuncName =
"advss_deregister_temp_vars";
static constexpr std::string_view setTempVarValueFuncName =
"advss_set_temp_var_value";
static const std::string getVariableValueDeclString =
std::string("bool ") + getVariableValueFuncName.data() + "(in string " +
nameParam.data() + ", out string " + valueParam.data() + ")";
static const std::string setVariableValueDeclString =
std::string("bool ") + setVariableValueFuncName.data() + "(in string " +
nameParam.data() + ", in string " + valueParam.data() + ")";
static const std::string registerTempVarDeclString =
std::string("bool ") + registerTempVarFuncName.data() + "(in string " +
tempVarIdParam.data() + ", in string " + tempVarNameParam.data() +
", in string " + tempVarHelpParam.data() + ", in int " +
GetInstanceIdParamName().data() + ")";
static const std::string deregisterAllTempVarsDeclString =
std::string("bool ") + deregisterAllTempVarsFuncName.data() +
"(in int " + GetInstanceIdParamName().data() + ")";
static const std::string setTempVarValueDeclString =
std::string("bool ") + setTempVarValueFuncName.data() + "(in string " +
tempVarIdParam.data() + ", in string " + valueParam.data() +
", in int " + GetInstanceIdParamName().data() + ")";
/* Plugin status */
static constexpr std::string_view stopSignalName = "advss_plugin_stopped";
static constexpr std::string_view startSignalName = "advss_plugin_started";
static constexpr std::string_view getRunningStatusFuncName =
"advss_plugin_running";
static constexpr std::string_view resetIntervalSignalName =
"advss_interval_reset";
static const std::string stopSignalDeclString =
std::string("void ") + stopSignalName.data() + "()";
static const std::string startSignalDeclString =
std::string("void ") + startSignalName.data() + "()";
static const std::string resetIntervalDeclString =
std::string("void ") + resetIntervalSignalName.data() + "()";
static const std::string getRunningStatusDeclString =
std::string("bool ") + getRunningStatusFuncName.data() + "()";
static bool setup();
static bool setupDone = setup();
@@ -103,13 +146,42 @@ static bool setup()
&ScriptHandler::GetVariableValue, nullptr);
proc_handler_add(ph, setVariableValueDeclString.c_str(),
&ScriptHandler::SetVariableValue, nullptr);
proc_handler_add(ph, registerTempVarDeclString.c_str(),
&ScriptHandler::RegisterTempVar, nullptr);
proc_handler_add(ph, deregisterAllTempVarsDeclString.c_str(),
&ScriptHandler::DeregisterAllTempVars, nullptr);
proc_handler_add(ph, setTempVarValueDeclString.c_str(),
&ScriptHandler::SetTempVarValue, nullptr);
proc_handler_add(ph, getRunningStatusDeclString.c_str(),
&ScriptHandler::GetRunningStatus, nullptr);
auto sh = obs_get_signal_handler();
signal_handler_add(sh, stopSignalDeclString.c_str());
signal_handler_add(sh, startSignalDeclString.c_str());
signal_handler_add(sh, resetIntervalDeclString.c_str());
static constexpr auto triggerSignal = [](const std::string_view &name) {
auto sh = obs_get_signal_handler();
struct calldata data;
calldata_init(&data);
signal_handler_signal(sh, name.data(), &data);
calldata_free(&data);
};
AddIntervalResetStep([]() { triggerSignal(resetIntervalSignalName); });
AddStartStep([]() { triggerSignal(startSignalName); });
AddStopStep([]() { triggerSignal(stopSignalName); });
return true;
}
static void replaceWhitespace(std::string &string)
static void replaceProblematicChars(std::string &string)
{
std::transform(string.begin(), string.end(), string.begin(), [](char c) {
return std::isspace(static_cast<unsigned char>(c)) ? '_' : c;
if (std::isspace(static_cast<unsigned char>(c)) || c == '(' ||
c == ')' || c == '[' || c == ']' || c == '{' || c == '}') {
return '_';
}
return c;
});
}
@@ -122,7 +194,7 @@ static std::string getTriggerSignal(const std::string &name,
const bool isAction)
{
std::string signal = name;
replaceWhitespace(signal);
replaceProblematicChars(signal);
signal += "_run";
signal += isAction ? "_action" : "_condition";
return signal;
@@ -140,12 +212,32 @@ static std::string getPropertiesSignal(const std::string &name,
const bool isAction)
{
std::string signal = name;
replaceWhitespace(signal);
replaceProblematicChars(signal);
signal += isAction ? "_action" : "_condition";
signal += "_get_properties";
return signal;
}
static std::string getNewInstanceSignal(const std::string &name,
const bool isAction)
{
std::string signal = name;
replaceProblematicChars(signal);
signal += isAction ? "_action" : "_condition";
signal += "_new_instance";
return signal;
}
static std::string getDeletedInstanceSignal(const std::string &name,
const bool isAction)
{
std::string signal = name;
replaceProblematicChars(signal);
signal += isAction ? "_action" : "_condition";
signal += "_deleted_instance";
return signal;
}
void ScriptHandler::RegisterScriptAction(void *, calldata_t *data)
{
const char *actionName;
@@ -177,14 +269,19 @@ void ScriptHandler::RegisterScriptAction(void *, calldata_t *data)
auto triggerSignalName = getTriggerSignal(actionName, true);
auto completionSignalName = getCompletionSignal(actionName, true);
auto propertiesSignalName = getPropertiesSignal(actionName, true);
auto newInstanceSignalName = getNewInstanceSignal(actionName, true);
auto deletedInstanceSignalName =
getDeletedInstanceSignal(actionName, true);
const auto createScriptAction =
[id, defaultSettings, propertiesSignalName, triggerSignalName,
completionSignalName](
completionSignalName, newInstanceSignalName,
deletedInstanceSignalName](
Macro *m) -> std::shared_ptr<MacroAction> {
return std::make_shared<MacroActionScript>(
m, id, defaultSettings, propertiesSignalName,
triggerSignalName, completionSignalName);
triggerSignalName, completionSignalName,
newInstanceSignalName, deletedInstanceSignalName);
};
if (!MacroActionFactory::Register(id, {createScriptAction,
MacroSegmentScriptEdit::Create,
@@ -202,9 +299,15 @@ void ScriptHandler::RegisterScriptAction(void *, calldata_t *data)
triggerSignalName.c_str());
calldata_set_string(data, propertiesSignalParam.data(),
propertiesSignalName.c_str());
calldata_set_string(data, newInstanceSignalNameParam.data(),
newInstanceSignalName.c_str());
calldata_set_string(data, deletedInstanceSignalNameParam.data(),
deletedInstanceSignalName.c_str());
_actions.emplace(id, ScriptSegmentType(id, propertiesSignalName,
triggerSignalName,
completionSignalName));
completionSignalName,
newInstanceSignalName,
deletedInstanceSignalName));
RETURN_SUCCESS();
}
@@ -279,14 +382,19 @@ void ScriptHandler::RegisterScriptCondition(void *, calldata_t *data)
auto triggerSignalName = getTriggerSignal(conditionName, false);
auto completionSignalName = getCompletionSignal(conditionName, false);
auto propertiesSignalName = getPropertiesSignal(conditionName, false);
auto newInstanceSignalName = getNewInstanceSignal(conditionName, false);
auto deletedInstanceSignalName =
getDeletedInstanceSignal(conditionName, false);
const auto createScriptCondition =
[id, defaultSettings, propertiesSignalName, triggerSignalName,
completionSignalName](
completionSignalName, newInstanceSignalName,
deletedInstanceSignalName](
Macro *m) -> std::shared_ptr<MacroCondition> {
return std::make_shared<MacroConditionScript>(
m, id, defaultSettings, propertiesSignalName,
triggerSignalName, completionSignalName);
triggerSignalName, completionSignalName,
newInstanceSignalName, deletedInstanceSignalName);
};
if (!MacroConditionFactory::Register(
id, {createScriptCondition, MacroSegmentScriptEdit::Create,
@@ -304,9 +412,15 @@ void ScriptHandler::RegisterScriptCondition(void *, calldata_t *data)
triggerSignalName.c_str());
calldata_set_string(data, propertiesSignalParam.data(),
propertiesSignalName.c_str());
calldata_set_string(data, newInstanceSignalNameParam.data(),
newInstanceSignalName.c_str());
calldata_set_string(data, deletedInstanceSignalNameParam.data(),
deletedInstanceSignalName.c_str());
_conditions.emplace(id, ScriptSegmentType(id, propertiesSignalName,
triggerSignalName,
completionSignalName));
completionSignalName,
newInstanceSignalName,
deletedInstanceSignalName));
RETURN_SUCCESS();
}
@@ -399,6 +513,97 @@ void ScriptHandler::SetVariableValue(void *, calldata_t *data)
RETURN_SUCCESS();
}
void ScriptHandler::GetRunningStatus(void *ctx, calldata_t *data)
{
calldata_set_bool(data, "is_running", PluginIsRunning());
}
void ScriptHandler::RegisterTempVar(void *, calldata_t *data)
{
const char *variableId;
if (!calldata_get_string(data, tempVarIdParam.data(), &variableId) ||
strlen(variableId) == 0) {
blog(LOG_WARNING, "[%s] failed! \"%s\" parameter missing!",
registerTempVarFuncName.data(), tempVarIdParam.data());
RETURN_FAILURE();
}
const char *name;
if (!calldata_get_string(data, tempVarNameParam.data(), &name) ||
strlen(name) == 0) {
blog(LOG_WARNING, "[%s] failed! \"%s\" parameter missing!",
registerTempVarFuncName.data(), tempVarNameParam.data());
RETURN_FAILURE();
}
const char *helpText;
if (!calldata_get_string(data, tempVarHelpParam.data(), &helpText)) {
blog(LOG_WARNING, "[%s] failed! \"%s\" parameter missing!",
registerTempVarFuncName.data(), tempVarHelpParam.data());
RETURN_FAILURE();
}
long long instanceId;
if (!calldata_get_int(data, GetInstanceIdParamName().data(),
&instanceId)) {
blog(LOG_WARNING, "[%s] failed! \"%s\" parameter missing!",
registerTempVarFuncName.data(),
GetInstanceIdParamName().data());
RETURN_FAILURE();
}
std::lock_guard<std::mutex> lock(_mutex);
MacroSegmentScript::RegisterTempVar(variableId, name, helpText,
instanceId);
RETURN_SUCCESS();
}
void ScriptHandler::DeregisterAllTempVars(void *, calldata_t *data)
{
long long instanceId;
if (!calldata_get_int(data, GetInstanceIdParamName().data(),
&instanceId)) {
blog(LOG_WARNING, "[%s] failed! \"%s\" parameter missing!",
deregisterAllTempVarsFuncName.data(),
GetInstanceIdParamName().data());
RETURN_FAILURE();
}
std::lock_guard<std::mutex> lock(_mutex);
MacroSegmentScript::DeregisterAllTempVars(instanceId);
RETURN_SUCCESS();
}
void ScriptHandler::SetTempVarValue(void *, calldata_t *data)
{
const char *variableId;
if (!calldata_get_string(data, tempVarIdParam.data(), &variableId) ||
strlen(variableId) == 0) {
blog(LOG_WARNING, "[%s] failed! \"%s\" parameter missing!",
setTempVarValueFuncName.data(), tempVarIdParam.data());
RETURN_FAILURE();
}
const char *variableValue;
if (!calldata_get_string(data, valueParam.data(), &variableValue) ||
strlen(variableValue) == 0) {
blog(LOG_WARNING, "[%s] failed! \"%s\" parameter missing!",
setTempVarValueFuncName.data(), valueParam.data());
RETURN_FAILURE();
}
long long instanceId;
if (!calldata_get_int(data, GetInstanceIdParamName().data(),
&instanceId)) {
blog(LOG_WARNING, "[%s] failed! \"%s\" parameter missing!",
setTempVarValueFuncName.data(),
GetInstanceIdParamName().data());
RETURN_FAILURE();
}
std::lock_guard<std::mutex> lock(_mutex);
MacroSegmentScript::SetTempVarValue(variableId, variableValue,
instanceId);
RETURN_SUCCESS();
}
bool ScriptHandler::ActionIdIsValid(const std::string &id)
{
std::lock_guard<std::mutex> lock(_mutex);
@@ -424,24 +629,39 @@ static std::string signalNameToPropertiesSignalDecl(const std::string &name)
static std::string signalNameToCompletionSignalDecl(const std::string &name)
{
return std::string("bool ") + name + "(in int " +
GeCompletionIdParamName().data() + ")";
GetCompletionIdParamName().data() + ")";
}
ScriptSegmentType::ScriptSegmentType(const std::string &id,
const std::string &propertiesSignal,
const std::string &triggerSignal,
const std::string &completionSignal)
static std::string signalNameToInstanceSignalDecl(const std::string &name)
{
return std::string("void ") + name + "(out int " +
GetInstanceIdParamName().data() + ")";
}
ScriptSegmentType::ScriptSegmentType(
const std::string &id, const std::string &propertiesSignalName,
const std::string &triggerSignalName,
const std::string &completionSignalName,
const std::string &newInstanceSignalName,
const std::string &deletedInstanceSignalName)
: _id(id)
{
auto sh = obs_get_signal_handler();
signal_handler_add(
obs_get_signal_handler(),
signalNameToPropertiesSignalDecl(propertiesSignal).c_str());
sh,
signalNameToPropertiesSignalDecl(propertiesSignalName).c_str());
signal_handler_add(
obs_get_signal_handler(),
signalNameToTriggerSignalDecl(triggerSignal).c_str());
sh, signalNameToTriggerSignalDecl(triggerSignalName).c_str());
signal_handler_add(
obs_get_signal_handler(),
signalNameToCompletionSignalDecl(completionSignal).c_str());
sh,
signalNameToCompletionSignalDecl(completionSignalName).c_str());
signal_handler_add(
sh,
signalNameToInstanceSignalDecl(newInstanceSignalName).c_str());
signal_handler_add(
sh, signalNameToInstanceSignalDecl(deletedInstanceSignalName)
.c_str());
}
} // namespace advss

View File

@@ -10,9 +10,11 @@ class ScriptSegmentType {
public:
ScriptSegmentType() = delete;
ScriptSegmentType(const std::string &id,
const std::string &propertiesSignal,
const std::string &triggerSignal,
const std::string &completionSignal);
const std::string &propertiesSignalName,
const std::string &triggerSignalName,
const std::string &completionSignalName,
const std::string &newInstanceSignalName,
const std::string &deletedInstanceSignalName);
private:
std::string _id;
@@ -26,7 +28,11 @@ public:
static void RegisterScriptCondition(void *ctx, calldata_t *data);
static void DeregisterScriptCondition(void *ctx, calldata_t *data);
static void GetVariableValue(void *ctx, calldata_t *data);
static void RegisterTempVar(void *ctx, calldata_t *data);
static void DeregisterAllTempVars(void *ctx, calldata_t *data);
static void SetTempVarValue(void *ctx, calldata_t *data);
static void SetVariableValue(void *ctx, calldata_t *data);
static void GetRunningStatus(void *ctx, calldata_t *data);
static bool ActionIdIsValid(const std::string &id);
static bool ConditionIdIsValid(const std::string &id);
@@ -46,14 +52,19 @@ static constexpr std::string_view GetActionCompletionSignalParamName()
return "completion_signal_name";
}
static constexpr std::string_view GeCompletionIdParamName()
static constexpr std::string_view GetCompletionIdParamName()
{
return "completion_id";
}
static constexpr std::string_view GeResultSignalParamName()
static constexpr std::string_view GetResultSignalParamName()
{
return "result";
}
static constexpr std::string_view GetInstanceIdParamName()
{
return "instance_id";
}
} // namespace advss

View File

@@ -178,11 +178,7 @@ void MacroSegmentList::mousePressEvent(QMouseEvent *event)
{
if (event->button() == Qt::LeftButton ||
event->button() == Qt::RightButton) {
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
_dragPosition = GetDragIndex(event->globalPos());
#else
_dragPosition = GetDragIndex(event->globalPosition().toPoint());
#endif
emit SelectionChanged(_dragPosition);
} else {
_dragPosition = -1;
@@ -300,11 +296,7 @@ void MacroSegmentList::dragMoveEvent(QDragMoveEvent *event)
return;
}
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
_dragCursorPos = (mapToGlobal(event->pos()));
#else
_dragCursorPos = (mapToGlobal(event->position().toPoint()));
#endif
CheckDropLine(_dragCursorPos);
}
@@ -450,16 +442,6 @@ void MacroSegmentList::dropEvent(QDropEvent *event)
{
HideLastDropLine();
auto widget = qobject_cast<QWidget *>(event->source());
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
if (widget && !widget->geometry().contains(event->pos()) &&
widgetIsInLayout(widget, _contentLayout)) {
int dropPosition = GetDropIndex(mapToGlobal(event->pos()));
if (dropPosition == -1) {
return;
}
emit Reorder(dropPosition, _dragPosition);
}
#else
if (widget &&
!widget->geometry().contains(event->position().toPoint()) &&
widgetIsInLayout(widget, _contentLayout)) {
@@ -470,7 +452,6 @@ void MacroSegmentList::dropEvent(QDropEvent *event)
}
emit Reorder(dropPosition, _dragPosition);
}
#endif
_dragPosition = -1;
}

View File

@@ -11,33 +11,106 @@
namespace advss {
static std::atomic_int completionIdCounter = 0;
static std::atomic_int instanceIdCounter = 0;
static std::mutex instanceMtx;
static std::vector<MacroSegmentScript *> instances{};
MacroSegmentScript::MacroSegmentScript(obs_data_t *defaultSettings,
const std::string &propertiesSignalName,
const std::string &triggerSignal,
const std::string &completionSignal)
MacroSegmentScript::MacroSegmentScript(
obs_data_t *defaultSettings, const std::string &propertiesSignalName,
const std::string &triggerSignalName,
const std::string &completionSignalName,
const std::string &newInstanceSignalName,
const std::string &deletedInstanceSignalName)
: _settings(obs_data_get_defaults(defaultSettings)),
_propertiesSignal(propertiesSignalName),
_triggerSignal(triggerSignal),
_completionSignal(completionSignal)
_triggerSignal(triggerSignalName),
_completionSignal(completionSignalName),
_newInstanceSignal(newInstanceSignalName),
_deletedInstanceSignal(deletedInstanceSignalName),
_instanceId(++instanceIdCounter)
{
signal_handler_connect(obs_get_signal_handler(),
completionSignal.c_str(),
completionSignalName.c_str(),
&MacroSegmentScript::CompletionSignalReceived,
this);
std::lock_guard<std::mutex> lock(instanceMtx);
instances.emplace_back(this);
SignalNewInstance();
}
MacroSegmentScript::MacroSegmentScript(const MacroSegmentScript &other)
: _settings(obs_data_create()),
_propertiesSignal(other._propertiesSignal),
_triggerSignal(other._triggerSignal),
_completionSignal(other._completionSignal)
_completionSignal(other._completionSignal),
_newInstanceSignal(other._newInstanceSignal),
_deletedInstanceSignal(other._deletedInstanceSignal),
_instanceId(++instanceIdCounter)
{
signal_handler_connect(obs_get_signal_handler(),
_completionSignal.c_str(),
&MacroSegmentScript::CompletionSignalReceived,
this);
obs_data_apply(_settings.Get(), other._settings.Get());
std::lock_guard<std::mutex> lock(instanceMtx);
instances.emplace_back(this);
SignalNewInstance();
}
MacroSegmentScript::~MacroSegmentScript()
{
auto data = calldata_create();
calldata_set_int(data, GetInstanceIdParamName().data(), _instanceId);
signal_handler_signal(obs_get_signal_handler(),
_deletedInstanceSignal.c_str(), data);
calldata_destroy(data);
std::lock_guard<std::mutex> lock(instanceMtx);
instances.erase(std::remove(instances.begin(), instances.end(), this),
instances.end());
}
void MacroSegmentScript::RegisterTempVar(const std::string &variableId,
const std::string &name,
const std::string &helpText,
int instanceId)
{
std::lock_guard<std::mutex> lock(instanceMtx);
for (auto instance : instances) {
if (instance->_instanceId != instanceId) {
continue;
}
instance->RegisterTempVarHelper(variableId, name, helpText);
break;
}
}
void MacroSegmentScript::DeregisterAllTempVars(int instanceId)
{
std::lock_guard<std::mutex> lock(instanceMtx);
for (auto instance : instances) {
if (instance->_instanceId != instanceId) {
continue;
}
instance->DeregisterAllTempVarsHelper();
}
}
void MacroSegmentScript::SetTempVarValue(const std::string &variableId,
const std::string &value,
int instanceId)
{
std::lock_guard<std::mutex> lock(instanceMtx);
for (auto instance : instances) {
if (instance->_instanceId != instanceId) {
continue;
}
instance->SetTempVarValueHelper(variableId, value);
}
}
bool MacroSegmentScript::Save(obs_data_t *obj) const
@@ -85,8 +158,10 @@ bool MacroSegmentScript::SendTriggerSignal()
auto data = calldata_create();
calldata_set_string(data, GetActionCompletionSignalParamName().data(),
_completionSignal.c_str());
calldata_set_int(data, GeCompletionIdParamName().data(), _completionId);
calldata_set_int(data, GetCompletionIdParamName().data(),
_completionId);
calldata_set_string(data, "settings", obs_data_get_json(GetSettings()));
calldata_set_int(data, GetInstanceIdParamName().data(), _instanceId);
signal_handler_signal(obs_get_signal_handler(), _triggerSignal.c_str(),
data);
calldata_destroy(data);
@@ -101,18 +176,18 @@ void MacroSegmentScript::CompletionSignalReceived(void *param, calldata_t *data)
{
auto segment = static_cast<MacroSegmentScript *>(param);
long long int id;
if (!calldata_get_int(data, GeCompletionIdParamName().data(), &id)) {
if (!calldata_get_int(data, GetCompletionIdParamName().data(), &id)) {
blog(LOG_WARNING,
"received completion signal without \"%s\" parameter",
GeCompletionIdParamName().data());
GetCompletionIdParamName().data());
return;
}
bool result;
if (!calldata_get_bool(data, GeResultSignalParamName().data(),
if (!calldata_get_bool(data, GetResultSignalParamName().data(),
&result)) {
blog(LOG_WARNING,
"received completion signal without \"%s\" parameter",
GeResultSignalParamName().data());
GetResultSignalParamName().data());
return;
}
if (id != segment->_completionId) {
@@ -122,6 +197,19 @@ void MacroSegmentScript::CompletionSignalReceived(void *param, calldata_t *data)
segment->_triggerResult = result;
}
void MacroSegmentScript::SignalNewInstance() const
{
std::thread signalNewInstanceThread([this]() {
auto data = calldata_create();
calldata_set_int(data, GetInstanceIdParamName().data(),
_instanceId);
signal_handler_signal(obs_get_signal_handler(),
_newInstanceSignal.c_str(), data);
calldata_destroy(data);
});
signalNewInstanceThread.detach();
}
obs_properties_t *MacroSegmentScriptEdit::GetProperties(void *obj)
{
auto segmentEdit = reinterpret_cast<MacroSegmentScriptEdit *>(obj);

View File

@@ -15,9 +15,20 @@ class MacroSegmentScript {
public:
MacroSegmentScript(obs_data_t *defaultSettings,
const std::string &propertiesSignalName,
const std::string &triggerSignal,
const std::string &completionSignal);
const std::string &triggerSignalName,
const std::string &completionSignalName,
const std::string &newInstanceSignalName,
const std::string &deletedInstanceSignalName);
MacroSegmentScript(const advss::MacroSegmentScript &);
virtual ~MacroSegmentScript();
static void RegisterTempVar(const std::string &variableId,
const std::string &name,
const std::string &helpText,
int instanceId);
static void DeregisterAllTempVars(int instanceId);
static void SetTempVarValue(const std::string &variableId,
const std::string &value, int instanceId);
protected:
bool Save(obs_data_t *obj) const;
@@ -31,15 +42,27 @@ protected:
double GetTimeoutSeconds() const { return _timeout.Seconds(); };
bool TriggerIsCompleted() const { return _triggerIsComplete; }
virtual void RegisterTempVarHelper(const std::string &variableId,
const std::string &name,
const std::string &helpText) = 0;
virtual void DeregisterAllTempVarsHelper() = 0;
virtual void SetTempVarValueHelper(const std::string &variableId,
const std::string &value) = 0;
const int64_t _instanceId;
private:
virtual void WaitForCompletion() const = 0;
static void CompletionSignalReceived(void *param, calldata_t *data);
void SignalNewInstance() const;
OBSDataAutoRelease _settings;
std::string _propertiesSignal = "";
std::string _triggerSignal = "";
std::string _completionSignal = "";
std::string _newInstanceSignal = "";
std::string _deletedInstanceSignal = "";
std::atomic_bool _triggerIsComplete = {false};
bool _triggerResult = false;
int64_t _completionId = 0;

View File

@@ -21,7 +21,7 @@ MacroSegmentSelection::MacroSegmentSelection(QWidget *parent, Type type,
{
_index->setMinimum(0);
_index->setMaximum(99);
_index->specialValueText("-");
_index->setSpecialValueText("-");
if (!allowVariables) {
_index->DisableVariableSelection();
}

View File

@@ -294,13 +294,8 @@ bool MacroSegmentEdit::eventFilter(QObject *obj, QEvent *ev)
QMouseEvent *newEvent = new QMouseEvent(
mouseEvent->type(),
_headerInfo->mapTo(this, mouseEvent->pos()),
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
mouseEvent->globalPos(),
#else
mouseEvent->globalPosition(),
#endif
mouseEvent->button(), mouseEvent->buttons(),
mouseEvent->modifiers());
mouseEvent->globalPosition(), mouseEvent->button(),
mouseEvent->buttons(), mouseEvent->modifiers());
QApplication::sendEvent(parentWidget(), newEvent);
}
return QWidget::eventFilter(obj, ev);

View File

@@ -51,8 +51,17 @@ protected:
virtual void SetupTempVars();
void AddTempvar(const std::string &id, const std::string &name,
const std::string &description = "");
void SetTempVarValue(const std::string &id, const std::string &value);
template<typename T, typename = std::enable_if_t<
std::is_same<std::decay_t<T>, bool>::value>>
void SetTempVarValue(const std::string &id, T value)
{
SetTempVarValue(id, value ? std::string("true")
: std::string("false"));
}
private:
void ClearAvailableTempvars();
std::optional<const TempVariable>

View File

@@ -178,6 +178,7 @@ void MacroTreeItem::Update(bool force)
QSizePolicy::Maximum);
_expand->setMaximumSize(10, 16);
_expand->setMinimumSize(10, 0);
_expand->setProperty("class", "checkbox-icon indicator-expand");
#ifdef __APPLE__
_expand->setAttribute(Qt::WA_LayoutUsesWidgetRect);
#endif
@@ -888,14 +889,7 @@ void MacroTree::dropEvent(QDropEvent *event)
QModelIndexList indices = selectedIndexes();
DropIndicatorPosition indicator = dropIndicatorPosition();
int row = indexAt(
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
event->position().toPoint()
#else
event->pos()
#endif
)
.row();
int row = indexAt(event->position().toPoint()).row();
bool emptyDrop = row == -1;
if (emptyDrop) {

View File

@@ -244,8 +244,13 @@ bool Macro::CheckConditions(bool ignorePause)
return false;
}
_stop = false;
_matched = false;
for (auto &condition : _conditions) {
if (!condition) {
continue;
}
if (_paused && !ignorePause) {
vblog(LOG_INFO, "Macro %s is paused", _name.c_str());
return false;
@@ -383,6 +388,9 @@ bool Macro::RunActionsHelper(
bool actionsExecutedSuccessfully = true;
for (auto &action : actions) {
if (!action) {
continue;
}
if (action->Enabled()) {
action->LogAction();
actionsExecutedSuccessfully =

View File

@@ -5,6 +5,7 @@
#include "plugin-state-helpers.hpp"
#include "sync-helpers.hpp"
#include "tab-helpers.hpp"
#include "time-helpers.hpp"
#include "ui-helpers.hpp"
#include <QTimer>
@@ -39,8 +40,8 @@ ActionQueueTable *ActionQueueTable::Create()
void ActionQueueTable::Add()
{
auto newQueue = std::make_shared<ActionQueue>();
auto accepted =
ActionQueueSettingsDialog::AskForSettings(this, *newQueue);
auto accepted = ActionQueueSettingsDialog::AskForSettings(
GetSettingsWindow(), *newQueue);
if (!accepted) {
return;
}
@@ -55,6 +56,13 @@ void ActionQueueTable::Add()
QString::fromStdString(newQueue->Name()));
}
static double getSecondsSinceLastEmpty(ActionQueue *queue)
{
const auto time = std::chrono::high_resolution_clock::now() -
queue->GetLastEmptyTime();
return std::chrono::duration_cast<std::chrono::seconds>(time).count();
}
static QStringList getCellLabels(ActionQueue *queue, bool addName = true)
{
assert(queue);
@@ -64,6 +72,7 @@ static QStringList getCellLabels(ActionQueue *queue, bool addName = true)
result << QString::fromStdString(queue->Name());
}
result << QString::number(queue->Size())
<< FormatRelativeTime(getSecondsSinceLastEmpty(queue))
<< QString::fromStdString(obs_module_text(
queue->IsRunning()
? "AdvSceneSwitcher.actionQueueTab.yes"
@@ -115,7 +124,7 @@ static void openSettingsDialog()
auto oldName = queue->Name();
bool accepted = ActionQueueSettingsDialog::AskForSettings(
tabWidget->Table(), *queue.get());
GetSettingsWindow(), *queue.get());
if (accepted && oldName != queue->Name()) {
ActionQueueSignalManager::Instance()->Rename(
QString::fromStdString(oldName),
@@ -199,6 +208,8 @@ ActionQueueTable::ActionQueueTable(QTabWidget *parent)
"AdvSceneSwitcher.actionQueueTab.name.header")
<< obs_module_text(
"AdvSceneSwitcher.actionQueueTab.size.header")
<< obs_module_text(
"AdvSceneSwitcher.actionQueueTab.lastEmpty.header")
<< obs_module_text(
"AdvSceneSwitcher.actionQueueTab.isRunning.header")
<< obs_module_text(

View File

@@ -26,7 +26,10 @@ void SetupActionQueues()
done = true;
}
ActionQueue::ActionQueue() : Item() {}
ActionQueue::ActionQueue() : Item()
{
_lastEmpty = std::chrono::high_resolution_clock::now();
}
ActionQueue::~ActionQueue()
{
@@ -110,6 +113,7 @@ void ActionQueue::Add(const std::shared_ptr<MacroAction> &action)
action->Save(data);
copy->Load(data);
copy->PostLoad();
RunPostLoadSteps();
copy->ResolveVariablesToFixedValues();
_actions.emplace_back(copy);
} else {
@@ -124,6 +128,14 @@ bool ActionQueue::IsEmpty()
return _actions.empty();
}
ActionQueue::TimePoint ActionQueue::GetLastEmptyTime()
{
if (IsEmpty()) {
_lastEmpty = std::chrono::high_resolution_clock::now();
}
return _lastEmpty;
}
size_t ActionQueue::Size()
{
std::lock_guard<std::mutex> lock(_mutex);
@@ -137,6 +149,8 @@ void ActionQueue::RunActions()
{ // Grab next action to run
std::unique_lock<std::mutex> lock(_mutex);
while (_actions.empty() && !_stop) {
_lastEmpty =
std::chrono::high_resolution_clock::now();
_cv.wait(lock);
}
@@ -166,7 +180,7 @@ ActionQueueSettingsDialog::ActionQueueSettingsDialog(QWidget *parent,
"AdvSceneSwitcher.actionQueues.select",
"AdvSceneSwitcher.actionQueues.add",
"AdvSceneSwitcher.actionQueues.nameNotAvailable",
parent),
true, parent),
_queueRunStatus(new QLabel()),
_startStopToggle(new QPushButton()),
_queueSize(new QLabel()),

View File

@@ -2,11 +2,12 @@
#include "item-selection-helpers.hpp"
#include "macro-action.hpp"
#include <chrono>
#include <condition_variable>
#include <deque>
#include <obs-data.h>
#include <QCheckBox>
#include <thread>
#include <condition_variable>
namespace advss {
@@ -14,6 +15,8 @@ class ActionQueueSelection;
class ActionQueueSettingsDialog;
class ActionQueue : public Item {
using TimePoint = std::chrono::high_resolution_clock::time_point;
public:
ActionQueue();
~ActionQueue();
@@ -30,6 +33,7 @@ public:
void Clear();
bool IsEmpty();
TimePoint GetLastEmptyTime();
void Add(const std::shared_ptr<MacroAction> &);
size_t Size();
@@ -44,6 +48,7 @@ private:
std::condition_variable _cv;
std::thread _thread;
std::deque<std::shared_ptr<MacroAction>> _actions;
TimePoint _lastEmpty;
friend ActionQueueSelection;
friend ActionQueueSettingsDialog;

View File

@@ -168,17 +168,6 @@ void SwitcherData::SaveVersion(obs_data_t *obj,
obs_data_set_string(obj, "version", currentVersion.c_str());
}
void SwitcherData::AddIntervalResetStep(std::function<void()> function,
bool tryLock)
{
if (!tryLock) {
resetIntervalSteps.emplace_back(function);
return;
}
std::lock_guard<std::mutex> lock(switcher->m);
resetIntervalSteps.emplace_back(function);
}
void SwitcherData::RunPostLoadSteps()
{
for (const auto &func : postLoadSteps) {

View File

@@ -57,11 +57,9 @@ public:
bool AnySceneTransitionStarted();
void SetPreconditions();
void ResetForNextInterval();
void AddSaveStep(std::function<void(obs_data_t *)>);
void AddLoadStep(std::function<void(obs_data_t *)>);
void AddPostLoadStep(std::function<void()>);
void AddIntervalResetStep(std::function<void()>, bool lock = true);
void RunPostLoadSteps();
bool CheckForMatch(OBSWeakSource &scene, OBSWeakSource &transition,
int &linger, bool &setPreviousSceneAsMatch,
@@ -100,7 +98,6 @@ public:
std::vector<std::function<void(obs_data_t *)>> saveSteps;
std::vector<std::function<void(obs_data_t *)>> loadSteps;
std::vector<std::function<void()>> postLoadSteps;
std::vector<std::function<void()>> resetIntervalSteps;
bool firstBoot = true;
bool transitionActive = false;
@@ -233,7 +230,6 @@ public:
bool checkPause();
void checkDefaultSceneTransitions();
void writeSceneInfoToFile();
void writeToStatusFile(const QString &msg);
void checkSwitchCooldown(bool &match);
std::deque<WindowSwitch> windowSwitches;

View File

@@ -0,0 +1,34 @@
#include "auto-update-tooltip-label.hpp"
namespace advss {
AutoUpdateTooltipLabel::AutoUpdateTooltipLabel(
QWidget *parent, const std::function<QString()> &updateTooltipCallback,
int updateIntervalMs)
: QLabel(parent),
_callback(updateTooltipCallback),
_timer(new QTimer(this)),
_updateIntervalMs(updateIntervalMs)
{
connect(_timer, &QTimer::timeout, this,
&AutoUpdateTooltipLabel::UpdateTooltip);
}
void AutoUpdateTooltipLabel::enterEvent(QEnterEvent *event)
{
_timer->start(_updateIntervalMs);
QLabel::enterEvent(event);
}
void AutoUpdateTooltipLabel::leaveEvent(QEvent *event)
{
_timer->stop();
QLabel::leaveEvent(event);
}
void AutoUpdateTooltipLabel::UpdateTooltip()
{
setToolTip(_callback());
}
} // namespace advss

View File

@@ -0,0 +1,32 @@
#pragma once
#include "export-symbol-helper.hpp"
#include <functional>
#include <QLabel>
#include <QTimer>
namespace advss {
class ADVSS_EXPORT AutoUpdateTooltipLabel : public QLabel {
Q_OBJECT
public:
AutoUpdateTooltipLabel(
QWidget *parent,
const std::function<QString()> &updateTooltipCallback,
int updateIntervalMs = 300);
protected:
void enterEvent(QEnterEvent *event) override;
void leaveEvent(QEvent *event) override;
private slots:
void UpdateTooltip();
private:
const std::function<QString()> _callback;
QTimer *_timer;
const int _updateIntervalMs;
};
} // namespace advss

View File

@@ -29,6 +29,7 @@ void Duration::Load(obs_data_t *obj, const char *name)
// TODO: remove this fallback
if (!data || !obs_data_has_user_value(data, "version") ||
obs_data_get_int(data, "version") != 1) {
_value = 0.;
bool usingDefaultArgs = strcmp("duration", name) == 0;
if (usingDefaultArgs) {
_value = obs_data_get_double(obj, "seconds");

View File

@@ -59,7 +59,10 @@ ItemSelection::ItemSelection(std::deque<std::shared_ptr<Item>> &items,
_conflictStr(conflict)
{
_modify->setMaximumWidth(22);
SetButtonIcon(_modify, ":/settings/images/settings/general.svg");
SetButtonIcon(_modify,
GetThemeTypeName() == "Light"
? ":/settings/images/settings/general.svg"
: "theme:Dark/settings/general.svg");
_modify->setFlat(true);
if (!configureTooltip.empty()) {
_modify->setToolTip(obs_module_text(configureTooltip.data()));
@@ -265,6 +268,7 @@ ItemSettingsDialog::ItemSettingsDialog(const Item &settings,
std::string_view select,
std::string_view add,
std::string_view nameConflict,
bool showNameEmptyWarning,
QWidget *parent)
: QDialog(parent),
_name(new QLineEdit()),
@@ -274,7 +278,8 @@ ItemSettingsDialog::ItemSettingsDialog(const Item &settings,
_items(items),
_selectStr(select),
_addStr(add),
_conflictStr(nameConflict)
_conflictStr(nameConflict),
_showNameEmptyWarning(showNameEmptyWarning)
{
setModal(true);
setWindowModality(Qt::WindowModality::WindowModal);

View File

@@ -45,7 +45,7 @@ public:
std::string_view addString = "AdvSceneSwitcher.item.add",
std::string_view conflictString =
"AdvSceneSwitcher.item.nameNotAvailable",
QWidget *parent = 0);
bool showEmptyNameWarning = true, QWidget *parent = 0);
virtual ~ItemSettingsDialog() = default;
private slots:

View File

@@ -32,7 +32,9 @@ void PreventMouseWheelAdjustWithoutFocus(QWidget *w)
QString(w->metaObject()->className()) ==
"advss::OSCMessageElementEdit" ||
QString(w->metaObject()->className()) ==
"advss::ChatMessagePropertyEdit") {
"advss::ChatMessagePropertyEdit" ||
QString(w->metaObject()->className()) ==
"advss::KeyValueListContainerWidget") {
return;
}
w->setFocusPolicy(Qt::StrongFocus);

View File

@@ -67,6 +67,8 @@ NonModalMessageDialog::NonModalMessageDialog(const QString &message, Type type,
}
case Type::INPUT: {
_inputEdit = new ResizingPlainTextEdit(this);
_inputEdit->setTabChangesFocus(true);
_inputEdit->setFocus();
connect(_inputEdit, &ResizingPlainTextEdit::textChanged, this,
&NonModalMessageDialog::InputChanged);
layout->addWidget(_inputEdit);

View File

@@ -21,6 +21,24 @@ static std::vector<std::function<void()>> &getPluginCleanupSteps()
return steps;
}
static std::vector<std::function<void()>> &getResetIntervalSteps()
{
static std::vector<std::function<void()>> steps;
return steps;
}
static std::vector<std::function<void()>> &getStartSteps()
{
static std::vector<std::function<void()>> steps;
return steps;
}
static std::vector<std::function<void()>> &getStopSteps()
{
static std::vector<std::function<void()>> steps;
return steps;
}
static std::mutex mutex;
void SavePluginSettings(obs_data_t *obj)
@@ -48,9 +66,10 @@ void AddPostLoadStep(std::function<void()> step)
GetSwitcher()->AddPostLoadStep(step);
}
void AddIntervalResetStep(std::function<void()> step, bool lock)
void AddIntervalResetStep(std::function<void()> step)
{
GetSwitcher()->AddIntervalResetStep(step, lock);
std::lock_guard<std::mutex> lock(mutex);
getResetIntervalSteps().emplace_back(step);
}
void RunPostLoadSteps()
@@ -100,6 +119,42 @@ void RunPluginCleanupSteps()
}
}
void RunIntervalResetSteps()
{
std::lock_guard<std::mutex> lock(mutex);
for (const auto &step : getResetIntervalSteps()) {
step();
}
}
void AddStartStep(std::function<void()> step)
{
std::lock_guard<std::mutex> lock(mutex);
getStartSteps().emplace_back(step);
}
void AddStopStep(std::function<void()> step)
{
std::lock_guard<std::mutex> lock(mutex);
getStopSteps().emplace_back(step);
}
void RunStartSteps()
{
std::lock_guard<std::mutex> lock(mutex);
for (const auto &step : getStartSteps()) {
step();
}
}
void RunStopSteps()
{
std::lock_guard<std::mutex> lock(mutex);
for (const auto &step : getStopSteps()) {
step();
}
}
void StopPlugin()
{
GetSwitcher()->Stop();

View File

@@ -11,7 +11,7 @@ EXPORT void LoadPluginSettings(obs_data_t *);
EXPORT void AddSaveStep(std::function<void(obs_data_t *)>);
EXPORT void AddLoadStep(std::function<void(obs_data_t *)>);
EXPORT void AddPostLoadStep(std::function<void()>);
EXPORT void AddIntervalResetStep(std::function<void()>, bool lock = true);
EXPORT void AddIntervalResetStep(std::function<void()>);
EXPORT void RunPostLoadSteps();
EXPORT void AddPluginInitStep(std::function<void()>);
@@ -25,6 +25,11 @@ EXPORT void StopPlugin();
EXPORT void StartPlugin();
EXPORT bool PluginIsRunning();
EXPORT int GetIntervalValue();
void AddStartStep(std::function<void()>);
void AddStopStep(std::function<void()>);
void RunStartSteps();
void RunStopSteps();
void RunIntervalResetSteps();
enum class NoMatchBehavior { NO_SWITCH = 0, SWITCH = 1, RANDOM_SWITCH = 2 };
EXPORT void SetPluginNoMatchBehavior(NoMatchBehavior);

View File

@@ -92,7 +92,10 @@ RegexConfigWidget::RegexConfigWidget(QWidget *parent, bool showEnable)
_openSettings(new QToolButton()),
_enable(new QPushButton())
{
SetButtonIcon(_openSettings, ":/settings/images/settings/general.svg");
SetButtonIcon(_openSettings,
GetThemeTypeName() == "Light"
? ":/settings/images/settings/general.svg"
: "theme:Dark/settings/general.svg");
_openSettings->setToolTip(
obs_module_text("AdvSceneSwitcher.regex.configure"));

View File

@@ -94,7 +94,7 @@ void ResizingPlainTextEdit::AddHeightForScrollBar(bool addHeight)
return;
}
_hScrollBarAddedHeight = verticalScrollBar()->height();
_hScrollBarAddedHeight = horizontalScrollBar()->height();
setFixedHeight(height() + _hScrollBarAddedHeight);
}

View File

@@ -455,35 +455,4 @@ void NotifyUIAboutTempVarChange()
nullptr, false);
}
AutoUpdateTooltipLabel::AutoUpdateTooltipLabel(
QWidget *parent, std::function<QString()> updateFunc)
: QLabel(parent),
_updateFunc(updateFunc),
_timer(new QTimer(this))
{
connect(_timer, &QTimer::timeout, this,
&AutoUpdateTooltipLabel::UpdateTooltip);
}
#if QT_VERSION > QT_VERSION_CHECK(6, 0, 0)
void AutoUpdateTooltipLabel::enterEvent(QEnterEvent *event)
#else
void AutoUpdateTooltipLabel::enterEvent(QEvent *event)
#endif
{
_timer->start(300);
QLabel::enterEvent(event);
}
void AutoUpdateTooltipLabel::leaveEvent(QEvent *event)
{
_timer->stop();
QLabel::leaveEvent(event);
}
void AutoUpdateTooltipLabel::UpdateTooltip()
{
setToolTip(_updateFunc());
}
} // namespace advss

View File

@@ -1,4 +1,5 @@
#pragma once
#include "auto-update-tooltip-label.hpp"
#include "filter-combo-box.hpp"
#include <obs-data.h>
@@ -6,8 +7,6 @@
#include <optional>
#include <QEnterEvent>
#include <QEvent>
#include <QLabel>
#include <QTimer>
#include <QStringList>
#include <string>
@@ -79,29 +78,6 @@ private:
friend TempVariableSelection;
};
class AutoUpdateTooltipLabel : public QLabel {
Q_OBJECT
public:
AutoUpdateTooltipLabel(QWidget *parent,
std::function<QString()> updateFunc);
protected:
#if QT_VERSION > QT_VERSION_CHECK(6, 0, 0)
void enterEvent(QEnterEvent *event) override;
#else
void enterEvent(QEvent *event) override;
#endif
void leaveEvent(QEvent *event) override;
private slots:
void UpdateTooltip();
private:
std::function<QString()> _updateFunc;
QTimer *_timer;
};
class ADVSS_EXPORT TempVariableSelection : public QWidget {
Q_OBJECT

View File

@@ -23,6 +23,14 @@ static bool setupDone = setup();
bool setup()
{
AddPluginPostLoadStep(registerWebsocketVendor);
AddStartStep([]() {
SendWebsocketVendorEvent("AdvancedSceneSwitcherStarted",
nullptr);
});
AddStopStep([]() {
SendWebsocketVendorEvent("AdvancedSceneSwitcherStopped",
nullptr);
});
return true;
}
@@ -109,6 +117,9 @@ void RegisterWebsocketRequest(
void SendWebsocketVendorEvent(const std::string &eventName, obs_data_t *data)
{
if (OBSIsShuttingDown()) {
return;
}
obs_websocket_vendor_emit_event(vendor, eventName.c_str(), data);
}

View File

@@ -29,7 +29,7 @@ private:
T _value = {};
std::weak_ptr<Variable> _variable;
friend class GenericVaraiableSpinbox;
friend class GenericVariableSpinbox;
friend class VariableSpinBox;
friend class VariableDoubleSpinBox;
};

View File

@@ -82,9 +82,10 @@ template<typename T> bool NumberVariable<T>::HasValidValue() const
auto value = var->IntValue();
return value.has_value();
} else if constexpr (std::is_same<T, double>::value) {
auto value = var->IntValue();
auto value = var->DoubleValue();
return value.has_value();
}
assert(false);
return false;
}

View File

@@ -5,8 +5,8 @@
namespace advss {
GenericVaraiableSpinbox::GenericVaraiableSpinbox(QWidget *parent,
bool wholeNumber)
GenericVariableSpinbox::GenericVariableSpinbox(QWidget *parent,
bool wholeNumber)
: QWidget(parent),
_fixedValueInt(new QSpinBox()),
_fixedValueDouble(new QDoubleSpinBox()),
@@ -16,7 +16,9 @@ GenericVaraiableSpinbox::GenericVaraiableSpinbox(QWidget *parent,
{
_toggleType->setCheckable(true);
_toggleType->setMaximumWidth(11);
SetButtonIcon(_toggleType, ":/res/images/dots-vert.svg");
SetButtonIcon(_toggleType, GetThemeTypeName() == "Light"
? ":/res/images/dots-vert.svg"
: "theme:Dark/dots-vert.svg");
QWidget::connect(_fixedValueInt, SIGNAL(valueChanged(int)), this,
SLOT(SetFixedValue(int)));
@@ -48,7 +50,7 @@ GenericVaraiableSpinbox::GenericVaraiableSpinbox(QWidget *parent,
SetVisibility();
}
void GenericVaraiableSpinbox::SetValue(const NumberVariable<int> &number)
void GenericVariableSpinbox::SetValue(const NumberVariable<int> &number)
{
_numberInt = number;
const QSignalBlocker b1(_toggleType);
@@ -59,7 +61,7 @@ void GenericVaraiableSpinbox::SetValue(const NumberVariable<int> &number)
SetVisibility();
}
void GenericVaraiableSpinbox::SetValue(const NumberVariable<double> &number)
void GenericVariableSpinbox::SetValue(const NumberVariable<double> &number)
{
_numberDouble = number;
const QSignalBlocker b1(_toggleType);
@@ -70,44 +72,44 @@ void GenericVaraiableSpinbox::SetValue(const NumberVariable<double> &number)
SetVisibility();
}
void GenericVaraiableSpinbox::DisableVariableSelection()
void GenericVariableSpinbox::DisableVariableSelection()
{
_hideTypeToggle = true;
_toggleType->hide();
ToggleTypeClicked(false);
}
void GenericVaraiableSpinbox::setMinimum(double value)
void GenericVariableSpinbox::setMinimum(double value)
{
_fixedValueInt->setMinimum(value);
_fixedValueDouble->setMinimum(value);
}
void GenericVaraiableSpinbox::setMaximum(double value)
void GenericVariableSpinbox::setMaximum(double value)
{
_fixedValueInt->setMaximum(value);
_fixedValueDouble->setMaximum(value);
}
void GenericVaraiableSpinbox::setPrefix(const QString &prefix)
void GenericVariableSpinbox::setPrefix(const QString &prefix)
{
_fixedValueInt->setPrefix(prefix);
_fixedValueDouble->setPrefix(prefix);
}
void GenericVaraiableSpinbox::setSuffix(const QString &suffix)
void GenericVariableSpinbox::setSuffix(const QString &suffix)
{
_fixedValueInt->setSuffix(suffix);
_fixedValueDouble->setSuffix(suffix);
}
void GenericVaraiableSpinbox::specialValueText(const QString &text)
void GenericVariableSpinbox::setSpecialValueText(const QString &text)
{
_fixedValueInt->setSpecialValueText(text);
_fixedValueDouble->setSpecialValueText(text);
}
void GenericVaraiableSpinbox::SetFixedValue(int value)
void GenericVariableSpinbox::SetFixedValue(int value)
{
_numberInt._value = value;
const QSignalBlocker b(_fixedValueInt);
@@ -115,7 +117,7 @@ void GenericVaraiableSpinbox::SetFixedValue(int value)
EmitSignals();
}
void GenericVaraiableSpinbox::SetFixedValue(double value)
void GenericVariableSpinbox::SetFixedValue(double value)
{
_numberDouble._value = value;
const QSignalBlocker b(_fixedValueDouble);
@@ -123,14 +125,14 @@ void GenericVaraiableSpinbox::SetFixedValue(double value)
EmitSignals();
}
void GenericVaraiableSpinbox::VariableChanged(const QString &name)
void GenericVariableSpinbox::VariableChanged(const QString &name)
{
_numberInt._variable = GetWeakVariableByQString(name);
_numberDouble._variable = GetWeakVariableByQString(name);
EmitSignals();
}
void GenericVaraiableSpinbox::ToggleTypeClicked(bool useVariable)
void GenericVariableSpinbox::ToggleTypeClicked(bool useVariable)
{
_numberInt._type = useVariable ? NumberVariable<int>::Type::VARIABLE
: NumberVariable<int>::Type::FIXED_VALUE;
@@ -142,7 +144,7 @@ void GenericVaraiableSpinbox::ToggleTypeClicked(bool useVariable)
EmitSignals();
}
void GenericVaraiableSpinbox::EmitSignals()
void GenericVariableSpinbox::EmitSignals()
{
if (_wholeNumber) {
emit FixedValueChanged(_numberInt);
@@ -153,7 +155,7 @@ void GenericVaraiableSpinbox::EmitSignals()
}
}
void GenericVaraiableSpinbox::SetVisibility()
void GenericVariableSpinbox::SetVisibility()
{
if (_wholeNumber) {
_fixedValueDouble->hide();
@@ -164,7 +166,7 @@ void GenericVaraiableSpinbox::SetVisibility()
}
}
void GenericVaraiableSpinbox::SetVisibilityInt()
void GenericVariableSpinbox::SetVisibilityInt()
{
if (_numberInt.IsFixedType()) {
_fixedValueInt->show();
@@ -180,7 +182,7 @@ void GenericVaraiableSpinbox::SetVisibilityInt()
updateGeometry();
}
void GenericVaraiableSpinbox::SetVisibilityDouble()
void GenericVariableSpinbox::SetVisibilityDouble()
{
if (_numberDouble.IsFixedType()) {
_fixedValueDouble->show();
@@ -197,12 +199,12 @@ void GenericVaraiableSpinbox::SetVisibilityDouble()
}
VariableSpinBox::VariableSpinBox(QWidget *parent)
: GenericVaraiableSpinbox(parent, true)
: GenericVariableSpinbox(parent, true)
{
}
VariableDoubleSpinBox::VariableDoubleSpinBox(QWidget *parent)
: GenericVaraiableSpinbox(parent, false)
: GenericVariableSpinbox(parent, false)
{
}

View File

@@ -9,10 +9,10 @@
namespace advss {
class ADVSS_EXPORT GenericVaraiableSpinbox : public QWidget {
class ADVSS_EXPORT GenericVariableSpinbox : public QWidget {
Q_OBJECT
public:
GenericVaraiableSpinbox(QWidget *parent, bool wholeNumber);
GenericVariableSpinbox(QWidget *parent, bool wholeNumber);
void SetValue(const NumberVariable<int> &);
void SetValue(const NumberVariable<double> &);
void DisableVariableSelection();
@@ -23,7 +23,7 @@ public:
void setPrefix(const QString &prefix);
void setSuffix(const QString &suffix);
void specialValueText(const QString &text);
void setSpecialValueText(const QString &text);
public slots:
void SetFixedValue(int);
@@ -59,7 +59,7 @@ private:
bool _hideTypeToggle = false;
};
class ADVSS_EXPORT VariableSpinBox : public GenericVaraiableSpinbox {
class ADVSS_EXPORT VariableSpinBox : public GenericVariableSpinbox {
Q_OBJECT
public:
@@ -68,7 +68,7 @@ public:
QSpinBox *SpinBox() { return _fixedValueInt; }
};
class ADVSS_EXPORT VariableDoubleSpinBox : public GenericVaraiableSpinbox {
class ADVSS_EXPORT VariableDoubleSpinBox : public GenericVariableSpinbox {
Q_OBJECT
public:

View File

@@ -43,8 +43,8 @@ VariableTable *VariableTable::Create()
void VariableTable::Add()
{
auto newVariable = std::make_shared<Variable>();
auto accepted =
VariableSettingsDialog::AskForSettings(this, *newVariable);
auto accepted = VariableSettingsDialog::AskForSettings(
GetSettingsWindow(), *newVariable);
if (!accepted) {
return;
}
@@ -184,7 +184,7 @@ static void openSettingsDialog()
auto oldName = variable->Name();
bool accepted = VariableSettingsDialog::AskForSettings(
tabWidget->Table(), *variable.get());
GetSettingsWindow(), *variable.get());
if (accepted && oldName != variable->Name()) {
VariableSignalManager::Instance()->Rename(
QString::fromStdString(oldName),

View File

@@ -137,7 +137,8 @@ VariableSettingsDialog::VariableSettingsDialog(QWidget *parent,
: ItemSettingsDialog(settings, variables,
"AdvSceneSwitcher.variable.select",
"AdvSceneSwitcher.variable.add",
"AdvSceneSwitcher.item.nameNotAvailable", parent),
"AdvSceneSwitcher.item.nameNotAvailable", true,
parent),
_value(new ResizingPlainTextEdit(this)),
_defaultValue(new ResizingPlainTextEdit(this)),
_save(new QComboBox())

View File

@@ -28,6 +28,7 @@ install_advss_plugin_dependency(...)
... to install the plugin and its dependencies.
#]]
add_plugin(http)
add_plugin(midi)
add_plugin(openvr)
add_plugin(stream-deck)

View File

@@ -15,8 +15,8 @@ target_sources(
macro-action-filter.hpp
macro-action-hotkey.cpp
macro-action-hotkey.hpp
macro-action-http.cpp
macro-action-http.hpp
macro-action-http-legacy.cpp
macro-action-http-legacy.hpp
macro-action-log.cpp
macro-action-log.hpp
macro-action-media.cpp
@@ -89,6 +89,8 @@ target_sources(
macro-condition-filter.hpp
macro-condition-folder.cpp
macro-condition-folder.hpp
macro-condition-game-capture.cpp
macro-condition-game-capture.hpp
macro-condition-hotkey.cpp
macro-condition-hotkey.hpp
macro-condition-idle.cpp
@@ -117,6 +119,8 @@ target_sources(
macro-condition-scene-visibility.hpp
macro-condition-scene.cpp
macro-condition-scene.hpp
macro-condition-screenshot.cpp
macro-condition-screenshot.hpp
macro-condition-slideshow.cpp
macro-condition-slideshow.hpp
macro-condition-source.cpp

View File

@@ -461,7 +461,7 @@ MacroActionAudioEdit::MacroActionAudioEdit(
_volumeDB->setMinimum(-100);
_volumeDB->setMaximum(0);
_volumeDB->setSuffix("dB");
_volumeDB->specialValueText("-inf");
_volumeDB->setSpecialValueText("-inf");
_rate->setMinimum(0.01);
_rate->setMaximum(999.);

View File

@@ -4,8 +4,6 @@
#include "slider-spinbox.hpp"
#include "source-selection.hpp"
#include <QSpinBox>
#include <QDoubleSpinBox>
#include <QCheckBox>
#include <QHBoxLayout>

View File

@@ -3,8 +3,6 @@
#include "file-selection.hpp"
#include "variable-text-edit.hpp"
#include <QSpinBox>
namespace advss {
class MacroActionFile : public MacroAction {

View File

@@ -215,7 +215,7 @@ MacroActionFilterEdit::MacroActionFilterEdit(
_settingsString(new VariableTextEdit(this)),
_refreshSettingSelection(new QPushButton(
obs_module_text("AdvSceneSwitcher.action.filter.refresh"))),
_settingsButtons(new QComboBox())
_settingsButtons(new SourceSettingsButtonSelection(this))
{
_filters->setSizeAdjustPolicy(QComboBox::AdjustToContents);
@@ -253,8 +253,10 @@ MacroActionFilterEdit::MacroActionFilterEdit(
SLOT(SelectionChanged(const SourceSetting &)));
QWidget::connect(_refreshSettingSelection, SIGNAL(clicked()), this,
SLOT(RefreshVariableSourceSelectionValue()));
QWidget::connect(_settingsButtons, SIGNAL(currentIndexChanged(int)),
this, SLOT(ButtonChanged(int)));
QWidget::connect(_settingsButtons,
SIGNAL(SelectionChanged(const SourceSettingButton &)),
this,
SLOT(ButtonChanged(const SourceSettingButton &)));
const std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
{"{{sources}}", _sources},
@@ -305,25 +307,18 @@ void MacroActionFilterEdit::UpdateEntryData()
const auto filters =
_entryData->_filter.GetFilters(_entryData->_source);
OBSWeakSource firstFilter = filters.empty() ? nullptr : filters.at(0);
_filterSettings->SetSource(firstFilter);
_filterSettings->SetSetting(_entryData->_setting);
_filterSettings->SetSelection(firstFilter, _entryData->_setting);
_settingsInputMethods->setCurrentIndex(_settingsInputMethods->findData(
static_cast<int>(_entryData->_settingsInputMethod)));
_tempVars->SetVariable(_entryData->_tempVar);
_manualSettingValue->setPlainText(_entryData->_manualSettingValue);
PopulateSourceButtonSelection(_settingsButtons, firstFilter);
_settingsButtons->setCurrentText(
QString::fromStdString(_entryData->_button.ToString()));
_settingsButtons->SetSelection(firstFilter, _entryData->_button);
SetWidgetVisibility();
}
void MacroActionFilterEdit::SourceChanged(const SourceSelection &source)
{
if (_loading || !_entryData) {
return;
}
auto lock = LockContext();
GUARD_LOADING_AND_LOCK();
_entryData->_source = source;
}
@@ -337,11 +332,13 @@ void MacroActionFilterEdit::FilterChanged(const FilterSelection &filter)
auto lock = LockContext();
_entryData->_filter = filter;
}
const auto filters =
_entryData->_filter.GetFilters(_entryData->_source);
OBSWeakSource firstFilter = filters.empty() ? nullptr : filters.at(0);
_filterSettings->SetSource(firstFilter);
PopulateSourceButtonSelection(_settingsButtons, firstFilter);
_settingsButtons->SetSource(firstFilter);
SetWidgetVisibility();
emit HeaderInfoChanged(
QString::fromStdString(_entryData->GetShortDesc()));
@@ -349,11 +346,7 @@ void MacroActionFilterEdit::FilterChanged(const FilterSelection &filter)
void MacroActionFilterEdit::ActionChanged(int value)
{
if (_loading || !_entryData) {
return;
}
auto lock = LockContext();
GUARD_LOADING_AND_LOCK();
_entryData->_action = static_cast<MacroActionFilter::Action>(value);
SetWidgetVisibility();
}
@@ -391,11 +384,7 @@ void MacroActionFilterEdit::GetSettingsClicked()
void MacroActionFilterEdit::SettingsStringChanged()
{
if (_loading || !_entryData) {
return;
}
auto lock = LockContext();
GUARD_LOADING_AND_LOCK();
_entryData->_settingsString =
_settingsString->toPlainText().toStdString();
@@ -405,11 +394,7 @@ void MacroActionFilterEdit::SettingsStringChanged()
void MacroActionFilterEdit::SettingsInputMethodChanged(int idx)
{
if (_loading || !_entryData) {
return;
}
auto lock = LockContext();
GUARD_LOADING_AND_LOCK();
_entryData->_settingsInputMethod =
static_cast<MacroActionFilter::SettingsInputMethod>(
_settingsInputMethods->itemData(idx).toInt());
@@ -418,31 +403,19 @@ void MacroActionFilterEdit::SettingsInputMethodChanged(int idx)
void MacroActionFilterEdit::SelectionChanged(const TempVariableRef &var)
{
if (_loading || !_entryData) {
return;
}
auto lock = LockContext();
GUARD_LOADING_AND_LOCK();
_entryData->_tempVar = var;
}
void MacroActionFilterEdit::SelectionChanged(const SourceSetting &setting)
{
if (_loading || !_entryData) {
return;
}
auto lock = LockContext();
GUARD_LOADING_AND_LOCK();
_entryData->_setting = setting;
}
void MacroActionFilterEdit::ManualSettingsValueChanged()
{
if (_loading || !_entryData) {
return;
}
auto lock = LockContext();
GUARD_LOADING_AND_LOCK();
_entryData->_manualSettingValue =
_manualSettingValue->toPlainText().toStdString();
@@ -457,15 +430,10 @@ void MacroActionFilterEdit::RefreshVariableSourceSelectionValue()
_filterSettings->SetSource(filters.empty() ? nullptr : filters.at(0));
}
void MacroActionFilterEdit::ButtonChanged(int idx)
void MacroActionFilterEdit::ButtonChanged(const SourceSettingButton &button)
{
if (_loading || !_entryData) {
return;
}
auto lock = LockContext();
_entryData->_button = qvariant_cast<SourceSettingButton>(
_settingsButtons->itemData(idx));
GUARD_LOADING_AND_LOCK();
_entryData->_button = button;
}
void MacroActionFilterEdit::SetWidgetVisibility()
@@ -481,8 +449,11 @@ void MacroActionFilterEdit::SetWidgetVisibility()
_entryData->_action == MacroActionFilter::Action::SETTINGS &&
_entryData->_settingsInputMethod ==
MacroActionFilter::SettingsInputMethod::JSON_STRING);
_getSettings->setVisible(_entryData->_action ==
MacroActionFilter::Action::SETTINGS);
_getSettings->setVisible(
_entryData->_action == MacroActionFilter::Action::SETTINGS &&
_entryData->_settingsInputMethod !=
MacroActionFilter::SettingsInputMethod::
INDIVIDUAL_TEMPVAR);
_tempVars->setVisible(_entryData->_action ==
MacroActionFilter::Action::SETTINGS &&
_entryData->_settingsInputMethod ==

View File

@@ -7,7 +7,6 @@
#include "source-setting.hpp"
#include <QComboBox>
#include <QSpinBox>
#include <QPushButton>
namespace advss {
@@ -82,7 +81,7 @@ private slots:
void SelectionChanged(const SourceSetting &);
void ManualSettingsValueChanged();
void RefreshVariableSourceSelectionValue();
void ButtonChanged(int idx);
void ButtonChanged(const SourceSettingButton &);
signals:
void HeaderInfoChanged(const QString &);
@@ -101,7 +100,7 @@ private:
SourceSettingSelection *_filterSettings;
VariableTextEdit *_settingsString;
QPushButton *_refreshSettingSelection;
QComboBox *_settingsButtons;
SourceSettingsButtonSelection *_settingsButtons;
std::shared_ptr<MacroActionFilter> _entryData;
bool _loading = true;

View File

@@ -5,7 +5,6 @@
#include <QComboBox>
#include <QCheckBox>
#include <QSpinBox>
#include <QHBoxLayout>
namespace advss {

View File

@@ -1,4 +1,4 @@
#include "macro-action-http.hpp"
#include "macro-action-http-legacy.hpp"
#include "curl-helper.hpp"
#include "layout-helpers.hpp"
@@ -9,7 +9,7 @@ const std::string MacroActionHttp::id = "http";
bool MacroActionHttp::_registered = MacroActionFactory::Register(
MacroActionHttp::id,
{MacroActionHttp::Create, MacroActionHttpEdit::Create,
"AdvSceneSwitcher.action.http"});
"AdvSceneSwitcher.action.http.legacy", true});
const static std::map<MacroActionHttp::Method, std::string> methods = {
{MacroActionHttp::Method::GET, "AdvSceneSwitcher.action.http.type.get"},

View File

@@ -34,11 +34,11 @@ const static std::map<PluginStateAction, std::string> actionTypes = {
const static std::map<NoMatchBehavior, std::string> noMatchValues = {
{NoMatchBehavior::NO_SWITCH,
"AdvSceneSwitcher.generalTab.generalBehavior.onNoMet.dontSwitch"},
"AdvSceneSwitcher.generalTab.generalBehavior.onNoMatch.dontSwitch"},
{NoMatchBehavior::SWITCH,
"AdvSceneSwitcher.generalTab.generalBehavior.onNoMet.switchTo"},
"AdvSceneSwitcher.generalTab.generalBehavior.onNoMatch.switchTo"},
{NoMatchBehavior::RANDOM_SWITCH,
"AdvSceneSwitcher.generalTab.generalBehavior.onNoMet.switchToRandom"},
"AdvSceneSwitcher.generalTab.generalBehavior.onNoMatch.switchToRandom"},
};
static void stopPlugin()

View File

@@ -3,7 +3,6 @@
#include "file-selection.hpp"
#include <obs.hpp>
#include <QDoubleSpinBox>
#include <QLabel>
namespace advss {

View File

@@ -5,6 +5,8 @@
#include "source-helpers.hpp"
#include <obs-frontend-api.h>
#include <QApplication>
#include <QWindow>
namespace advss {
@@ -28,8 +30,34 @@ const static std::map<MacroActionProjector::Type, std::string> selectionTypes =
"AdvSceneSwitcher.action.projector.type.multiview"},
};
static void closeOBSProjectorWindows(const std::string expectedWindowTitle,
const RegexConfig &regex)
{
for (QWindow *widget : QApplication::allWindows()) {
if (!widget->property("isOBSProjectorWindow").toBool()) {
continue;
}
auto const windowTitle = widget->title().toStdString();
if (!regex.Enabled() && expectedWindowTitle != windowTitle) {
continue;
}
if (!regex.Matches(windowTitle, expectedWindowTitle)) {
continue;
}
widget->close();
}
}
bool MacroActionProjector::PerformAction()
{
if (_action == Action::CLOSE) {
closeOBSProjectorWindows(_projectorWindowName, _regex);
return true;
}
std::string name = "";
const char *type = "";
@@ -78,10 +106,16 @@ bool MacroActionProjector::PerformAction()
void MacroActionProjector::LogAction() const
{
if (_action == Action::CLOSE) {
ablog(LOG_INFO, "closing projector window \"%s\"",
_projectorWindowName.c_str());
return;
}
auto it = selectionTypes.find(_type);
if (it != selectionTypes.end()) {
ablog(LOG_INFO,
"performed projector action \"%s\" with"
"open projector \"%s\" with"
"source \"%s\","
"scene \"%s\","
"monitor %d",
@@ -96,24 +130,30 @@ void MacroActionProjector::LogAction() const
bool MacroActionProjector::Save(obs_data_t *obj) const
{
MacroAction::Save(obj);
obs_data_set_int(obj, "action", static_cast<int>(_action));
obs_data_set_int(obj, "type", static_cast<int>(_type));
obs_data_set_int(obj, "monitor", _monitor);
obs_data_set_string(obj, "monitorName", _monitorName.c_str());
obs_data_set_bool(obj, "fullscreen", _fullscreen);
_scene.Save(obj);
_source.Save(obj);
_projectorWindowName.Save(obj, "projectorWindowName");
_regex.Save(obj);
return true;
}
bool MacroActionProjector::Load(obs_data_t *obj)
{
MacroAction::Load(obj);
_action = static_cast<Action>(obs_data_get_int(obj, "action"));
_type = static_cast<Type>(obs_data_get_int(obj, "type"));
_monitor = obs_data_get_int(obj, "monitor");
_monitorName = obs_data_get_string(obj, "monitorName");
_fullscreen = obs_data_get_bool(obj, "fullscreen");
_scene.Load(obj);
_source.Load(obj);
_projectorWindowName.Load(obj, "projectorWindowName");
_regex.Load(obj);
return true;
}
@@ -131,6 +171,7 @@ void MacroActionProjector::ResolveVariablesToFixedValues()
{
_source.ResolveVariables();
_scene.ResolveVariables();
_projectorWindowName.ResolveVariables();
}
void MacroActionProjector::SetMonitor(int idx)
@@ -166,14 +207,22 @@ bool MacroActionProjector::MonitorSetupChanged() const
QString::fromStdString(_monitorName);
}
static inline void populateSelectionTypes(QComboBox *list)
static void populateActionSelection(QComboBox *list)
{
for (auto entry : selectionTypes) {
list->addItem(obs_module_text(entry.second.c_str()));
list->addItem(obs_module_text(
"AdvSceneSwitcher.action.projector.action.open"));
list->addItem(obs_module_text(
"AdvSceneSwitcher.action.projector.action.close"));
}
static void populateSelectionTypes(QComboBox *list)
{
for (const auto &[_, name] : selectionTypes) {
list->addItem(obs_module_text(name.c_str()));
}
}
static inline void populateWindowTypes(QComboBox *list)
static void populateWindowTypes(QComboBox *list)
{
list->addItem(
obs_module_text("AdvSceneSwitcher.action.projector.windowed"));
@@ -184,14 +233,18 @@ static inline void populateWindowTypes(QComboBox *list)
MacroActionProjectorEdit::MacroActionProjectorEdit(
QWidget *parent, std::shared_ptr<MacroActionProjector> entryData)
: QWidget(parent),
_windowTypes(new QComboBox()),
_actions(new QComboBox()),
_types(new QComboBox()),
_windowTypes(new QComboBox()),
_scenes(new SceneSelectionWidget(window(), true, false, true, true,
true)),
_sources(new SourceSelectionWidget(window(), QStringList(), true)),
_monitorSelection(new QHBoxLayout()),
_monitors(new QComboBox())
_monitors(new QComboBox()),
_projectorWindowName(new VariableLineEdit(this)),
_regex(new RegexConfigWidget(this)),
_layout(new QHBoxLayout(this))
{
populateActionSelection(_actions);
populateWindowTypes(_windowTypes);
populateSelectionTypes(_types);
auto sources = GetSourceNames();
@@ -201,6 +254,8 @@ MacroActionProjectorEdit::MacroActionProjectorEdit(
_monitors->setPlaceholderText(
obs_module_text("AdvSceneSwitcher.selectDisplay"));
QWidget::connect(_actions, SIGNAL(currentIndexChanged(int)), this,
SLOT(ActionChanged(int)));
QWidget::connect(_windowTypes, SIGNAL(currentIndexChanged(int)), this,
SLOT(WindowTypeChanged(int)));
QWidget::connect(_types, SIGNAL(currentIndexChanged(int)), this,
@@ -212,24 +267,15 @@ MacroActionProjectorEdit::MacroActionProjectorEdit(
SLOT(SourceChanged(const SourceSelection &)));
QWidget::connect(_monitors, SIGNAL(currentIndexChanged(int)), this,
SLOT(MonitorChanged(int)));
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
{"{{windowTypes}}", _windowTypes}, {"{{types}}", _types},
{"{{scenes}}", _scenes}, {"{{sources}}", _sources},
{"{{monitors}}", _monitors},
};
PlaceWidgets(obs_module_text(
"AdvSceneSwitcher.action.projector.entry.monitor"),
_monitorSelection, widgetPlaceholders);
QHBoxLayout *mainLayout = new QHBoxLayout;
PlaceWidgets(obs_module_text("AdvSceneSwitcher.action.projector.entry"),
mainLayout, widgetPlaceholders);
mainLayout->insertLayout(mainLayout->count() - 1, _monitorSelection);
setLayout(mainLayout);
QWidget::connect(_projectorWindowName, SIGNAL(editingFinished()), this,
SLOT(ProjectorWindowNameChanged()));
QWidget::connect(_regex,
SIGNAL(RegexConfigChanged(const RegexConfig &)), this,
SLOT(RegexChanged(const RegexConfig &)));
_entryData = entryData;
SetWidgetLayout();
setLayout(_layout);
UpdateEntryData();
_loading = false;
}
@@ -239,11 +285,15 @@ void MacroActionProjectorEdit::UpdateEntryData()
if (!_entryData) {
return;
}
_actions->setCurrentIndex(static_cast<int>(_entryData->_action));
_windowTypes->setCurrentIndex(_entryData->_fullscreen ? 1 : 0);
_types->setCurrentIndex(static_cast<int>(_entryData->_type));
_scenes->SetScene(_entryData->_scene);
_sources->SetSource(_entryData->_source);
_monitors->setCurrentIndex(_entryData->GetMonitor());
_projectorWindowName->setText(_entryData->_projectorWindowName);
_regex->SetRegexConfig(_entryData->_regex);
SetWidgetVisibility();
}
@@ -265,12 +315,26 @@ void MacroActionProjectorEdit::MonitorChanged(int value)
_entryData->SetMonitor(value);
}
void MacroActionProjectorEdit::ProjectorWindowNameChanged()
{
GUARD_LOADING_AND_LOCK();
_entryData->_projectorWindowName =
_projectorWindowName->text().toStdString();
}
void MacroActionProjectorEdit::RegexChanged(const RegexConfig &regex)
{
GUARD_LOADING_AND_LOCK();
_entryData->_regex = regex;
}
void MacroActionProjectorEdit::WindowTypeChanged(int)
{
GUARD_LOADING_AND_LOCK();
_entryData->_fullscreen =
_windowTypes->currentText() ==
obs_module_text("AdvSceneSwitcher.action.projector.fullscreen");
SetWidgetLayout();
SetWidgetVisibility();
}
@@ -281,17 +345,69 @@ void MacroActionProjectorEdit::TypeChanged(int value)
SetWidgetVisibility();
}
void MacroActionProjectorEdit::ActionChanged(int idx)
{
GUARD_LOADING_AND_LOCK();
_entryData->_action = static_cast<MacroActionProjector::Action>(idx);
SetWidgetLayout();
SetWidgetVisibility();
}
void MacroActionProjectorEdit::SetWidgetLayout()
{
const std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
{"{{actions}}", _actions},
{"{{windowTypes}}", _windowTypes},
{"{{types}}", _types},
{"{{scenes}}", _scenes},
{"{{sources}}", _sources},
{"{{monitors}}", _monitors},
{"{{projectorWindowName}}", _projectorWindowName},
{"{{regex}}", _regex},
};
for (const auto &[_, widget] : widgetPlaceholders) {
_layout->removeWidget(widget);
}
ClearLayout(_layout);
const char *layoutText;
if (_entryData->_action == MacroActionProjector::Action::CLOSE) {
layoutText = "AdvSceneSwitcher.action.projector.entry.close";
} else if (_entryData->_fullscreen) {
layoutText =
"AdvSceneSwitcher.action.projector.entry.open.fullscreen";
} else {
layoutText =
"AdvSceneSwitcher.action.projector.entry.open.windowed";
}
PlaceWidgets(obs_module_text(layoutText), _layout, widgetPlaceholders);
}
void MacroActionProjectorEdit::SetWidgetVisibility()
{
if (!_entryData) {
return;
}
_scenes->setVisible(_entryData->_type ==
MacroActionProjector::Type::SCENE);
_sources->setVisible(_entryData->_type ==
MacroActionProjector::Type::SOURCE);
SetLayoutVisible(_monitorSelection, _entryData->_fullscreen);
_projectorWindowName->setVisible(_entryData->_action ==
MacroActionProjector::Action::CLOSE);
_regex->setVisible(_entryData->_action ==
MacroActionProjector::Action::CLOSE);
_types->setVisible(_entryData->_action ==
MacroActionProjector::Action::OPEN);
_windowTypes->setVisible(_entryData->_action ==
MacroActionProjector::Action::OPEN);
_scenes->setVisible(
_entryData->_action == MacroActionProjector::Action::OPEN &&
_entryData->_type == MacroActionProjector::Type::SCENE);
_sources->setVisible(
_entryData->_action == MacroActionProjector::Action::OPEN &&
_entryData->_type == MacroActionProjector::Type::SOURCE);
_monitors->setVisible(_entryData->_action ==
MacroActionProjector::Action::OPEN &&
_entryData->_fullscreen);
adjustSize();
updateGeometry();

View File

@@ -1,7 +1,9 @@
#pragma once
#include "macro-action-edit.hpp"
#include "regex-config.hpp"
#include "scene-selection.hpp"
#include "source-selection.hpp"
#include "variable-line-edit.hpp"
namespace advss {
@@ -19,6 +21,11 @@ public:
void SetMonitor(int);
int GetMonitor() const;
enum class Action {
OPEN,
CLOSE,
};
enum class Type {
SOURCE,
SCENE,
@@ -27,10 +34,13 @@ public:
MULTIVIEW,
};
Action _action = Action::OPEN;
Type _type = Type::SCENE;
SourceSelection _source;
SceneSelection _scene;
bool _fullscreen = true;
StringVariable _projectorWindowName = "Windowed Projector";
RegexConfig _regex = RegexConfig::PartialMatchRegexConfig(true);
private:
bool MonitorSetupChanged() const;
@@ -60,21 +70,28 @@ public:
}
private slots:
void ActionChanged(int value);
void WindowTypeChanged(int value);
void TypeChanged(int value);
void SceneChanged(const SceneSelection &);
void SourceChanged(const SourceSelection &);
void MonitorChanged(int value);
void ProjectorWindowNameChanged();
void RegexChanged(const RegexConfig &);
private:
void SetWidgetLayout();
void SetWidgetVisibility();
QComboBox *_windowTypes;
QComboBox *_actions;
QComboBox *_types;
QComboBox *_windowTypes;
SceneSelectionWidget *_scenes;
SourceSelectionWidget *_sources;
QHBoxLayout *_monitorSelection;
QComboBox *_monitors;
VariableLineEdit *_projectorWindowName;
RegexConfigWidget *_regex;
QHBoxLayout *_layout;
std::shared_ptr<MacroActionProjector> _entryData;
bool _loading = true;

View File

@@ -142,10 +142,10 @@ void MacroActionReplayBufferEdit::UpdateEntryData()
}
_actions->setCurrentIndex(static_cast<int>(_entryData->_action));
_duration->SetDuration(_entryData->_duration);
SetWidgetVisiblity();
SetWidgetVisibility();
}
void MacroActionReplayBufferEdit::SetWidgetVisiblity()
void MacroActionReplayBufferEdit::SetWidgetVisibility()
{
_warning->setVisible(_entryData->_action ==
MacroActionReplayBuffer::Action::SAVE);
@@ -169,7 +169,7 @@ void MacroActionReplayBufferEdit::ActionChanged(int value)
_entryData->_action =
static_cast<MacroActionReplayBuffer::Action>(value);
SetWidgetVisiblity();
SetWidgetVisibility();
}
void MacroActionReplayBufferEdit::DurationChanged(const Duration &duration)

View File

@@ -53,7 +53,7 @@ private slots:
void DurationChanged(const Duration &);
private:
void SetWidgetVisiblity();
void SetWidgetVisibility();
QComboBox *_actions;
QLabel *_warning;

View File

@@ -10,17 +10,21 @@ bool MacroActionSceneOrder::_registered = MacroActionFactory::Register(
{MacroActionSceneOrder::Create, MacroActionSceneOrderEdit::Create,
"AdvSceneSwitcher.action.sceneOrder"});
const static std::map<SceneOrderAction, std::string> actionTypes = {
{SceneOrderAction::MOVE_UP,
const static std::map<MacroActionSceneOrder::Action, std::string> actionTypes = {
{MacroActionSceneOrder::Action::MOVE_UP,
"AdvSceneSwitcher.action.sceneOrder.type.moveUp"},
{SceneOrderAction::MOVE_DOWN,
{MacroActionSceneOrder::Action::MOVE_DOWN,
"AdvSceneSwitcher.action.sceneOrder.type.moveDown"},
{SceneOrderAction::MOVE_TOP,
{MacroActionSceneOrder::Action::MOVE_TOP,
"AdvSceneSwitcher.action.sceneOrder.type.moveTop"},
{SceneOrderAction::MOVE_BOTTOM,
{MacroActionSceneOrder::Action::MOVE_BOTTOM,
"AdvSceneSwitcher.action.sceneOrder.type.moveBottom"},
{SceneOrderAction::POSITION,
{MacroActionSceneOrder::Action::POSITION,
"AdvSceneSwitcher.action.sceneOrder.type.movePosition"},
{MacroActionSceneOrder::Action::ABOVE,
"AdvSceneSwitcher.action.sceneOrder.type.above"},
{MacroActionSceneOrder::Action::BELOW,
"AdvSceneSwitcher.action.sceneOrder.type.below"},
};
static void moveSceneItemsUp(std::vector<OBSSceneItem> &items)
@@ -66,26 +70,128 @@ static void moveSceneItemsPos(std::vector<OBSSceneItem> &items, int pos)
}
}
namespace {
struct PositionData {
obs_scene_item *item = nullptr;
bool found = false;
int position = 0;
};
} // namespace
static bool getSceneItemPositionHelper(obs_scene_t *, obs_sceneitem_t *item,
void *data)
{
auto positionData = reinterpret_cast<PositionData *>(data);
if (obs_sceneitem_is_group(item)) {
obs_scene_t *scene = obs_sceneitem_group_get_scene(item);
obs_scene_enum_items(scene, getSceneItemPositionHelper, data);
}
if (positionData->item == item) {
positionData->found = true;
return false;
}
positionData->position += 1;
return true;
}
static std::optional<int> getSceneItemPosition(const OBSSceneItem &item,
const SceneSelection &scene)
{
auto sceneSource = OBSGetStrongRef(scene.GetScene());
auto obsScene = obs_scene_from_source(sceneSource);
PositionData data{item};
obs_scene_enum_items(obsScene, getSceneItemPositionHelper, &data);
if (!data.found) {
return {};
}
return data.position;
}
static void moveItemFromToHelper(MacroActionSceneOrder::Action action,
const OBSSceneItem &itemToMove,
int currentPosition, int targetPosition)
{
if (action == MacroActionSceneOrder::Action::ABOVE) {
if (currentPosition > targetPosition) {
obs_sceneitem_set_order_position(itemToMove,
targetPosition + 1);
} else {
obs_sceneitem_set_order_position(itemToMove,
targetPosition);
}
} else if (action == MacroActionSceneOrder::Action::BELOW) {
if (currentPosition > targetPosition) {
obs_sceneitem_set_order_position(itemToMove,
targetPosition);
} else {
obs_sceneitem_set_order_position(itemToMove,
targetPosition - 1);
}
}
}
static void moveItemToItemHelper(MacroActionSceneOrder::Action action,
const std::vector<OBSSceneItem> &itemsToMove,
const SceneItemSelection &target,
const SceneSelection &scene)
{
auto targetItems = target.GetSceneItems(scene);
if (targetItems.empty()) {
return;
}
auto targetItem = targetItems.at(0);
for (const auto &item : itemsToMove) {
if (item == targetItem) {
continue;
}
auto targetPosition = getSceneItemPosition(targetItem, scene);
if (!targetPosition) {
continue;
}
auto currentPosition = getSceneItemPosition(item, scene);
if (!currentPosition) {
continue;
}
moveItemFromToHelper(action, item, *currentPosition,
*targetPosition);
}
}
bool MacroActionSceneOrder::PerformAction()
{
auto items = _source.GetSceneItems(_scene);
switch (_action) {
case SceneOrderAction::MOVE_UP:
case Action::MOVE_UP:
moveSceneItemsUp(items);
break;
case SceneOrderAction::MOVE_DOWN:
case Action::MOVE_DOWN:
moveSceneItemsDown(items);
break;
case SceneOrderAction::MOVE_TOP:
case Action::MOVE_TOP:
moveSceneItemsTop(items);
break;
case SceneOrderAction::MOVE_BOTTOM:
case Action::MOVE_BOTTOM:
moveSceneItemsBottom(items);
break;
case SceneOrderAction::POSITION:
case Action::POSITION:
moveSceneItemsPos(items, _position);
break;
case Action::ABOVE:
case Action::BELOW: {
moveItemToItemHelper(_action, items, _source2, _scene);
break;
}
default:
break;
}
@@ -111,6 +217,7 @@ bool MacroActionSceneOrder::Save(obs_data_t *obj) const
MacroAction::Save(obj);
_scene.Save(obj);
_source.Save(obj);
_source2.Save(obj, "sceneItemSelection2");
obs_data_set_int(obj, "action", static_cast<int>(_action));
obs_data_set_int(obj, "position", _position);
return true;
@@ -128,8 +235,8 @@ bool MacroActionSceneOrder::Load(obs_data_t *obj)
MacroAction::Load(obj);
_scene.Load(obj);
_source.Load(obj);
_action =
static_cast<SceneOrderAction>(obs_data_get_int(obj, "action"));
_source2.Load(obj, "sceneItemSelection2");
_action = static_cast<Action>(obs_data_get_int(obj, "action"));
_position = obs_data_get_int(obj, "position");
return true;
}
@@ -167,13 +274,13 @@ static inline void populateActionSelection(QComboBox *list)
MacroActionSceneOrderEdit::MacroActionSceneOrderEdit(
QWidget *parent, std::shared_ptr<MacroActionSceneOrder> entryData)
: QWidget(parent)
: QWidget(parent),
_scenes(new SceneSelectionWidget(this, true, false, false, true)),
_sources(new SceneItemSelectionWidget(this)),
_sources2(new SceneItemSelectionWidget(this)),
_actions(new QComboBox(this)),
_position(new QSpinBox(this))
{
_scenes = new SceneSelectionWidget(window(), true, false, false, true);
_sources = new SceneItemSelectionWidget(parent);
_actions = new QComboBox();
_position = new QSpinBox();
populateActionSelection(_actions);
QWidget::connect(_actions, SIGNAL(currentIndexChanged(int)), this,
@@ -182,23 +289,28 @@ MacroActionSceneOrderEdit::MacroActionSceneOrderEdit(
this, SLOT(SceneChanged(const SceneSelection &)));
QWidget::connect(_scenes, SIGNAL(SceneChanged(const SceneSelection &)),
_sources, SLOT(SceneChanged(const SceneSelection &)));
QWidget::connect(_scenes, SIGNAL(SceneChanged(const SceneSelection &)),
_sources2, SLOT(SceneChanged(const SceneSelection &)));
QWidget::connect(_sources,
SIGNAL(SceneItemChanged(const SceneItemSelection &)),
this, SLOT(SourceChanged(const SceneItemSelection &)));
QWidget::connect(_sources2,
SIGNAL(SceneItemChanged(const SceneItemSelection &)),
this,
SLOT(Source2Changed(const SceneItemSelection &)));
QWidget::connect(_position, SIGNAL(valueChanged(int)), this,
SLOT(PositionChanged(int)));
QHBoxLayout *mainLayout = new QHBoxLayout;
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
{"{{scenes}}", _scenes},
{"{{sources}}", _sources},
{"{{actions}}", _actions},
{"{{position}}", _position},
};
auto layout = new QHBoxLayout;
PlaceWidgets(
obs_module_text("AdvSceneSwitcher.action.sceneOrder.entry"),
mainLayout, widgetPlaceholders);
setLayout(mainLayout);
layout,
{{"{{scenes}}", _scenes},
{"{{sources}}", _sources},
{"{{sources2}}", _sources2},
{"{{actions}}", _actions},
{"{{position}}", _position}});
setLayout(layout);
_entryData = entryData;
UpdateEntryData();
@@ -214,28 +326,20 @@ void MacroActionSceneOrderEdit::UpdateEntryData()
_actions->setCurrentIndex(static_cast<int>(_entryData->_action));
_scenes->SetScene(_entryData->_scene);
_sources->SetSceneItem(_entryData->_source);
_sources2->SetSceneItem(_entryData->_source2);
_position->setValue(_entryData->_position);
_position->setVisible(_entryData->_action ==
SceneOrderAction::POSITION);
SetWidgetVisibility();
}
void MacroActionSceneOrderEdit::SceneChanged(const SceneSelection &s)
{
if (_loading || !_entryData) {
return;
}
auto lock = LockContext();
GUARD_LOADING_AND_LOCK();
_entryData->_scene = s;
}
void MacroActionSceneOrderEdit::SourceChanged(const SceneItemSelection &item)
{
if (_loading || !_entryData) {
return;
}
auto lock = LockContext();
GUARD_LOADING_AND_LOCK();
_entryData->_source = item;
emit HeaderInfoChanged(
QString::fromStdString(_entryData->GetShortDesc()));
@@ -243,26 +347,36 @@ void MacroActionSceneOrderEdit::SourceChanged(const SceneItemSelection &item)
updateGeometry();
}
void MacroActionSceneOrderEdit::Source2Changed(const SceneItemSelection &item)
{
GUARD_LOADING_AND_LOCK();
_entryData->_source2 = item;
adjustSize();
updateGeometry();
}
void MacroActionSceneOrderEdit::ActionChanged(int value)
{
if (_loading || !_entryData) {
return;
}
auto lock = LockContext();
_entryData->_action = static_cast<SceneOrderAction>(value);
_position->setVisible(_entryData->_action ==
SceneOrderAction::POSITION);
GUARD_LOADING_AND_LOCK();
_entryData->_action = static_cast<MacroActionSceneOrder::Action>(value);
SetWidgetVisibility();
}
void MacroActionSceneOrderEdit::PositionChanged(int value)
{
if (_loading || !_entryData) {
return;
}
auto lock = LockContext();
GUARD_LOADING_AND_LOCK();
_entryData->_position = value;
}
void MacroActionSceneOrderEdit::SetWidgetVisibility()
{
_position->setVisible(_entryData->_action ==
MacroActionSceneOrder::Action::POSITION);
_sources2->setVisible(
_entryData->_action == MacroActionSceneOrder::Action::ABOVE ||
_entryData->_action == MacroActionSceneOrder::Action::BELOW);
adjustSize();
updateGeometry();
}
} // namespace advss

View File

@@ -7,14 +7,6 @@
namespace advss {
enum class SceneOrderAction {
MOVE_UP,
MOVE_DOWN,
MOVE_TOP,
MOVE_BOTTOM,
POSITION,
};
class MacroActionSceneOrder : public MacroAction {
public:
MacroActionSceneOrder(Macro *m) : MacroAction(m) {}
@@ -30,7 +22,17 @@ public:
SceneSelection _scene;
SceneItemSelection _source;
SceneOrderAction _action = SceneOrderAction::MOVE_UP;
SceneItemSelection _source2;
enum class Action {
MOVE_UP,
MOVE_DOWN,
MOVE_TOP,
MOVE_BOTTOM,
POSITION,
ABOVE,
BELOW,
};
Action _action = Action::MOVE_UP;
int _position = 0;
private:
@@ -58,20 +60,22 @@ public:
private slots:
void SceneChanged(const SceneSelection &);
void SourceChanged(const SceneItemSelection &);
void Source2Changed(const SceneItemSelection &);
void ActionChanged(int value);
void PositionChanged(int value);
signals:
void HeaderInfoChanged(const QString &);
protected:
private:
void SetWidgetVisibility();
SceneSelectionWidget *_scenes;
SceneItemSelectionWidget *_sources;
SceneItemSelectionWidget *_sources2;
QComboBox *_actions;
QSpinBox *_position;
std::shared_ptr<MacroActionSceneOrder> _entryData;
private:
QHBoxLayout *_mainLayout;
std::shared_ptr<MacroActionSceneOrder> _entryData;
bool _loading = true;
};

View File

@@ -6,8 +6,6 @@
#include "variable-text-edit.hpp"
#include "variable-spinbox.hpp"
#include <QSpinBox>
namespace advss {
class MacroActionSceneTransform : public MacroAction {

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