mirror of
https://github.com/WarmUpTill/SceneSwitcher.git
synced 2026-08-29 04:36:21 -05:00
Compare commits
12 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9d38d22270 | ||
|
|
de007176d5 | ||
|
|
713f695aa4 | ||
|
|
dec5c2d763 | ||
|
|
f090dea136 | ||
|
|
d00986df03 | ||
|
|
03e5397b7e | ||
|
|
94d49db196 | ||
|
|
1483f9d9dc | ||
|
|
99629e8c66 | ||
|
|
ba38b8bf27 | ||
|
|
38513735dd |
4
.github/scripts/.build.zsh
vendored
4
.github/scripts/.build.zsh
vendored
@@ -255,6 +255,10 @@ ${_usage_host:-}"
|
||||
|
||||
cmake_args+=(
|
||||
-DCMAKE_PREFIX_PATH="${advss_deps_path}"
|
||||
-DOPENSSL_ROOT_DIR="${advss_deps_path}"
|
||||
-DOPENSSL_INCLUDE_DIR="${advss_deps_path}/include"
|
||||
-DOPENSSL_CRYPTO_LIBRARY="${advss_deps_path}/lib/libcrypto.a"
|
||||
-DOPENSSL_SSL_LIBRARY="${advss_deps_path}/lib/libssl.a"
|
||||
--preset ${_preset}
|
||||
)
|
||||
|
||||
|
||||
3
.github/scripts/utils.zsh/check_macos
vendored
3
.github/scripts/utils.zsh/check_macos
vendored
@@ -18,7 +18,4 @@ if (( ! ${+commands[brew]} )) {
|
||||
}
|
||||
|
||||
brew bundle --file ${SCRIPT_HOME}/.Brewfile
|
||||
# Workaround to make sure locally built openssl is picked up by cmake
|
||||
brew uninstall --ignore-dependencies openssl@3 || true
|
||||
rehash || true
|
||||
log_group
|
||||
|
||||
2
.github/workflows/build-project.yaml
vendored
2
.github/workflows/build-project.yaml
vendored
@@ -6,7 +6,7 @@ on:
|
||||
description: "Project name detected by parsing build spec file"
|
||||
value: ${{ jobs.check-event.outputs.pluginName }}
|
||||
env:
|
||||
DEP_DIR: .deps/advss-build-dependencies-3
|
||||
DEP_DIR: .deps/advss-build-dependencies-4
|
||||
jobs:
|
||||
check-event:
|
||||
name: Check GitHub Event Data 🔎
|
||||
|
||||
@@ -504,7 +504,11 @@ endif()
|
||||
# --- End of section ---
|
||||
|
||||
add_subdirectory(plugins)
|
||||
add_subdirectory(tests)
|
||||
|
||||
option(ADVSS_ENABLE_TESTS "Build advanced-scene-switcher unit tests" OFF)
|
||||
if(ADVSS_ENABLE_TESTS)
|
||||
add_subdirectory(tests)
|
||||
endif()
|
||||
|
||||
# --- Install ---
|
||||
|
||||
|
||||
@@ -41,7 +41,8 @@
|
||||
"description": "Build for macOS 11.0+ (Universal binary) for CI",
|
||||
"generator": "Xcode",
|
||||
"cacheVariables": {
|
||||
"CMAKE_COMPILE_WARNING_AS_ERROR": true
|
||||
"CMAKE_COMPILE_WARNING_AS_ERROR": true,
|
||||
"ADVSS_ENABLE_TESTS": true
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -69,7 +70,8 @@
|
||||
"displayName": "Windows x64 CI build",
|
||||
"description": "Build for Windows x64 on CI",
|
||||
"cacheVariables": {
|
||||
"CMAKE_COMPILE_WARNING_AS_ERROR": true
|
||||
"CMAKE_COMPILE_WARNING_AS_ERROR": true,
|
||||
"ADVSS_ENABLE_TESTS": true
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -97,7 +99,8 @@
|
||||
"description": "Build for Linux x86_64 on CI",
|
||||
"cacheVariables": {
|
||||
"CMAKE_BUILD_TYPE": "RelWithDebInfo",
|
||||
"CMAKE_COMPILE_WARNING_AS_ERROR": true
|
||||
"CMAKE_COMPILE_WARNING_AS_ERROR": true,
|
||||
"ADVSS_ENABLE_TESTS": true
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -125,7 +128,8 @@
|
||||
"description": "Build for Linux aarch64 on CI",
|
||||
"cacheVariables": {
|
||||
"CMAKE_BUILD_TYPE": "RelWithDebInfo",
|
||||
"CMAKE_COMPILE_WARNING_AS_ERROR": true
|
||||
"CMAKE_COMPILE_WARNING_AS_ERROR": true,
|
||||
"ADVSS_ENABLE_TESTS": true
|
||||
}
|
||||
}
|
||||
],
|
||||
|
||||
@@ -211,8 +211,8 @@ AdvSceneSwitcher.condition.record.state.start="Aufnahme läuft"
|
||||
AdvSceneSwitcher.condition.record.state.pause="Aufnahme pausiert"
|
||||
AdvSceneSwitcher.condition.record.state.stop="Aufnahme gestoppt"
|
||||
AdvSceneSwitcher.condition.process="Prozess"
|
||||
AdvSceneSwitcher.condition.process.entry="{{processes}}{{regex}}läuft{{focused}}und ist fokusiert"
|
||||
AdvSceneSwitcher.condition.process.entry.focus="Aktueller Vordergrundprozess: {{focusProcess}}"
|
||||
AdvSceneSwitcher.condition.process.layout="{{processes}}{{regex}}läuft{{focused}}und ist fokusiert"
|
||||
AdvSceneSwitcher.condition.process.layout.focus="Aktueller Vordergrundprozess: {{focusProcess}}"
|
||||
AdvSceneSwitcher.condition.idle="Leerlauf"
|
||||
AdvSceneSwitcher.condition.idle.entry="Keine Tastatur- oder Mauseingaben für {{duration}}"
|
||||
AdvSceneSwitcher.condition.pluginState="Plugin-Status"
|
||||
@@ -416,7 +416,6 @@ AdvSceneSwitcher.action.replay.type.save="Replay Buffer speichern"
|
||||
AdvSceneSwitcher.action.streaming="Stream"
|
||||
AdvSceneSwitcher.action.streaming.type.stop="Stream stoppen"
|
||||
AdvSceneSwitcher.action.streaming.type.start="Stream starten"
|
||||
AdvSceneSwitcher.action.streaming.entry="{{actions}}{{keyFrameInterval}}{{stringValue}}{{showPassword}}"
|
||||
AdvSceneSwitcher.action.run="Ausführen"
|
||||
AdvSceneSwitcher.action.sceneVisibility="Sichtbarkeit von Szenenelementen"
|
||||
AdvSceneSwitcher.action.sceneVisibility.type.show="Anzeigen"
|
||||
@@ -452,7 +451,7 @@ AdvSceneSwitcher.action.macro="Makro"
|
||||
AdvSceneSwitcher.action.macro.type.pause="Pausieren"
|
||||
AdvSceneSwitcher.action.macro.type.unpause="Nicht mehr pausieren"
|
||||
AdvSceneSwitcher.action.macro.type.resetCounter="Zähler zurücksetzen"
|
||||
AdvSceneSwitcher.action.macro.type.run="Aktionen ausführen"
|
||||
AdvSceneSwitcher.action.macro.type.runActions="Aktionen ausführen"
|
||||
AdvSceneSwitcher.action.macro.type.stop="Aktionen stoppen"
|
||||
AdvSceneSwitcher.action.pluginState="Plugin-Status"
|
||||
AdvSceneSwitcher.action.pluginState.type.stop="Erweiterten Automatischen Szenenwechsler stoppen"
|
||||
|
||||
@@ -484,8 +484,9 @@ AdvSceneSwitcher.condition.record.state.stop="Recording stopped"
|
||||
AdvSceneSwitcher.condition.record.state.duration="Recording duration is longer than"
|
||||
AdvSceneSwitcher.condition.record.entry="{{condition}}{{duration}}"
|
||||
AdvSceneSwitcher.condition.process="Process"
|
||||
AdvSceneSwitcher.condition.process.entry="{{processes}}{{regex}}is running{{focused}}and is focused"
|
||||
AdvSceneSwitcher.condition.process.entry.focus="Current foreground process:{{focusProcess}}"
|
||||
AdvSceneSwitcher.condition.process.layout="{{processes}}{{regex}}is running{{focused}}and is focused"
|
||||
AdvSceneSwitcher.condition.process.layout.focus="Current foreground process:{{focusProcess}}"
|
||||
AdvSceneSwitcher.condition.process.layout.path="{{checkPath}}Match binary path:{{path}}{{pathRegex}}"
|
||||
AdvSceneSwitcher.condition.idle="Idle"
|
||||
AdvSceneSwitcher.condition.idle.entry="No keyboard or mouse inputs for{{duration}}"
|
||||
AdvSceneSwitcher.condition.pluginState="Plugin state"
|
||||
@@ -941,7 +942,8 @@ AdvSceneSwitcher.action.streaming.type.server="Set server URL"
|
||||
AdvSceneSwitcher.action.streaming.type.streamKey="Set stream key"
|
||||
AdvSceneSwitcher.action.streaming.type.username="Set username"
|
||||
AdvSceneSwitcher.action.streaming.type.password="Set password"
|
||||
AdvSceneSwitcher.action.streaming.entry="{{actions}}{{keyFrameInterval}}{{stringValue}}{{showPassword}}"
|
||||
AdvSceneSwitcher.action.streaming.getCurrentValue="Get current value"
|
||||
AdvSceneSwitcher.action.streaming.layout="{{actions}}{{keyFrameInterval}}{{stringValue}}{{showPassword}}{{getCurrentValue}}"
|
||||
AdvSceneSwitcher.action.run="Run"
|
||||
AdvSceneSwitcher.action.run.wait.entry="{{wait}}Wait for process exit or at most {{timeout}}{{waitHelp}}"
|
||||
AdvSceneSwitcher.action.run.wait.help.tooltip="Note that macro properties won't work if you leave this unticked, as the process spawns detached from the rest of logic and there's no control over it."
|
||||
@@ -1070,7 +1072,7 @@ AdvSceneSwitcher.action.macro.type.pause="Pause"
|
||||
AdvSceneSwitcher.action.macro.type.unpause="Unpause"
|
||||
AdvSceneSwitcher.action.macro.type.togglePause="Toggle pause"
|
||||
AdvSceneSwitcher.action.macro.type.resetCounter="Reset counter"
|
||||
AdvSceneSwitcher.action.macro.type.run="Run macro"
|
||||
AdvSceneSwitcher.action.macro.type.runActions="Run macro actions"
|
||||
AdvSceneSwitcher.action.macro.type.run.conditions.ignore="Do not consider condition state"
|
||||
AdvSceneSwitcher.action.macro.type.run.conditions.true="Only if conditions evaluate to true"
|
||||
AdvSceneSwitcher.action.macro.type.run.conditions.false="Only if conditions evaluate to false"
|
||||
@@ -1086,6 +1088,9 @@ AdvSceneSwitcher.action.macro.type.disableAction="Disable action"
|
||||
AdvSceneSwitcher.action.macro.type.enableAction="Enable action"
|
||||
AdvSceneSwitcher.action.macro.type.toggleAction="Toggle action"
|
||||
AdvSceneSwitcher.action.macro.type.getInfo="Get macro info"
|
||||
AdvSceneSwitcher.action.macro.type.runMacro="Run macro"
|
||||
AdvSceneSwitcher.action.macro.type.runMacro.noConditionsWarning="Warning: The selected macro has no conditions!"
|
||||
AdvSceneSwitcher.action.macro.type.runMacro.help="Unlike \"Run macro actions\", this option will immediately re-evaluate the conditions of the selected macro and then run either its actions or else-actions depending on the result."
|
||||
AdvSceneSwitcher.action.macro.type.nestedMacro="Nested macro"
|
||||
AdvSceneSwitcher.action.macro.actionSelectionType.index="at index"
|
||||
AdvSceneSwitcher.action.macro.actionSelectionType.label="with label"
|
||||
@@ -1096,6 +1101,7 @@ AdvSceneSwitcher.action.macro.type.nestedMacro.elseActionHelp="This section allo
|
||||
AdvSceneSwitcher.action.macro.layout.run="{{actions}}{{actionSections}}of{{macros}}"
|
||||
AdvSceneSwitcher.action.macro.layout.run.condition="{{conditionBehaviors}}of{{conditionMacros}}"
|
||||
AdvSceneSwitcher.action.macro.layout.actionState="{{actions}}{{actionSelectionType}}{{actionIndex}}{{label}}{{regex}}{{actionTypes}}in{{actionSections}}section of{{macros}}"
|
||||
AdvSceneSwitcher.action.macro.layout.runMacro="{{actions}}{{runMacroHelp}}{{macros}}"
|
||||
AdvSceneSwitcher.action.macro.layout.other="{{actions}}{{macros}}"
|
||||
AdvSceneSwitcher.action.pluginState="Plugin state"
|
||||
AdvSceneSwitcher.action.pluginState.type.stop="Stop the Advanced Scene Switcher plugin"
|
||||
@@ -1789,6 +1795,10 @@ AdvSceneSwitcher.script.file.layout="Script file:{{path}}{{open}}"
|
||||
|
||||
AdvSceneSwitcher.tempVar.select="--select value--"
|
||||
AdvSceneSwitcher.tempVar.selectionInfo.lastValues="Last values:"
|
||||
AdvSceneSwitcher.tempVar.outputMappings="Save outputs to variables:"
|
||||
AdvSceneSwitcher.tempVar.outputMappings.add="+ Add output mapping"
|
||||
AdvSceneSwitcher.tempVar.outputMappings.toggle="Assign outputs to variables"
|
||||
AdvSceneSwitcher.tempVar.outputMappings.remove="Remove output mapping"
|
||||
|
||||
AdvSceneSwitcher.tempVar.twitch.broadcaster_user_id="Twitch broadcaster user ID"
|
||||
AdvSceneSwitcher.tempVar.twitch.broadcaster_user_id.description="The numerical Twitch user ID of the broadcaster."
|
||||
@@ -2312,6 +2322,7 @@ AdvSceneSwitcher.tempVar.macro.info.secondsSinceLastRun="Seconds since last run"
|
||||
AdvSceneSwitcher.tempVar.macro.info.secondsSinceLastRun.description="The number of seconds elapsed since the macro was last executed. Value is -1 if the macro has never been executed."
|
||||
|
||||
AdvSceneSwitcher.tempVar.process.name="Process name"
|
||||
AdvSceneSwitcher.tempVar.process.path="Process path"
|
||||
|
||||
AdvSceneSwitcher.tempVar.run.process.id="Process ID"
|
||||
AdvSceneSwitcher.tempVar.run.process.id.description="PID of the process assigned by the system."
|
||||
|
||||
@@ -173,7 +173,7 @@ AdvSceneSwitcher.condition.record.state.start="Grabación en ejecución"
|
||||
AdvSceneSwitcher.condition.record.state.pause="Grabación en pausa"
|
||||
AdvSceneSwitcher.condition.record.state.stop="Grabación detenida"
|
||||
AdvSceneSwitcher.condition.process="Proceso"
|
||||
AdvSceneSwitcher.condition.process.entry="{{processes}}{{regex}}se está ejecutando{{focused}}y está enfocado"
|
||||
AdvSceneSwitcher.condition.process.layout="{{processes}}{{regex}}se está ejecutando{{focused}}y está enfocado"
|
||||
AdvSceneSwitcher.condition.idle="Inactivo"
|
||||
AdvSceneSwitcher.condition.idle.entry="No hay entradas de teclado o ratón durante {{duration}}"
|
||||
AdvSceneSwitcher.condition.pluginState="Estado del complemento"
|
||||
@@ -340,7 +340,6 @@ AdvSceneSwitcher.action.replay.type.save="Guardar búfer de reproducción"
|
||||
AdvSceneSwitcher.action.streaming="Transmisión"
|
||||
AdvSceneSwitcher.action.streaming.type.stop="Detener transmisión"
|
||||
AdvSceneSwitcher.action.streaming.type.start="Iniciar transmisión"
|
||||
AdvSceneSwitcher.action.streaming.entry="{{actions}}{{keyFrameInterval}}{{stringValue}}{{showPassword}}"
|
||||
AdvSceneSwitcher.action.run="Ejecutar"
|
||||
AdvSceneSwitcher.action.sceneVisibility="Visibilidad del elemento de escena"
|
||||
AdvSceneSwitcher.action.sceneVisibility.type.show="Mostrar"
|
||||
@@ -372,7 +371,7 @@ AdvSceneSwitcher.action.macro="Macro"
|
||||
AdvSceneSwitcher.action.macro.type.pause="Pausa"
|
||||
AdvSceneSwitcher.action.macro.type.unpause="Reanudar"
|
||||
AdvSceneSwitcher.action.macro.type.resetCounter="Reiniciar contador"
|
||||
AdvSceneSwitcher.action.macro.type.run="Ejecutar"
|
||||
AdvSceneSwitcher.action.macro.type.runActions="Ejecutar"
|
||||
AdvSceneSwitcher.action.macro.type.stop="Detener"
|
||||
AdvSceneSwitcher.action.pluginState="Estado del complemento"
|
||||
AdvSceneSwitcher.action.pluginState.type.stop="Detener el complemento Advanced Scene Switcher"
|
||||
|
||||
@@ -279,8 +279,8 @@ AdvSceneSwitcher.condition.record.state.start="Enregistrement en cours"
|
||||
AdvSceneSwitcher.condition.record.state.pause="Enregistrement en pause"
|
||||
AdvSceneSwitcher.condition.record.state.stop="Arrêt de l'enregistrement"
|
||||
AdvSceneSwitcher.condition.process="Processus"
|
||||
AdvSceneSwitcher.condition.process.entry="{{processes}}{{regex}}en cours d'exécution{{focused}}et est au premier plan"
|
||||
AdvSceneSwitcher.condition.process.entry.focus="Processus au premier plan actuel :{{focusProcess}}"
|
||||
AdvSceneSwitcher.condition.process.layout="{{processes}}{{regex}}en cours d'exécution{{focused}}et est au premier plan"
|
||||
AdvSceneSwitcher.condition.process.layout.focus="Processus au premier plan actuel :{{focusProcess}}"
|
||||
AdvSceneSwitcher.condition.idle="Inactif"
|
||||
AdvSceneSwitcher.condition.idle.entry="Aucune entrée de clavier ou de souris pendant{{duration}}"
|
||||
AdvSceneSwitcher.condition.pluginState="État du plugin"
|
||||
@@ -551,7 +551,7 @@ AdvSceneSwitcher.action.macro="Macro"
|
||||
AdvSceneSwitcher.action.macro.type.pause="Pause"
|
||||
AdvSceneSwitcher.action.macro.type.unpause="Reprendre"
|
||||
AdvSceneSwitcher.action.macro.type.resetCounter="Réinitialiser le compteur"
|
||||
AdvSceneSwitcher.action.macro.type.run="Exécuter les actions"
|
||||
AdvSceneSwitcher.action.macro.type.runActions="Exécuter les actions"
|
||||
AdvSceneSwitcher.action.macro.type.stop="Arrêter les actions"
|
||||
AdvSceneSwitcher.action.macro.type.disableAction="Désactiver l'action"
|
||||
AdvSceneSwitcher.action.macro.type.enableAction="Activer l'action"
|
||||
|
||||
@@ -445,8 +445,8 @@ AdvSceneSwitcher.condition.stream.service.tooltip="現在のサービス名: %1"
|
||||
AdvSceneSwitcher.condition.record.state.duration="録音時間が長くなっています。"
|
||||
; AdvSceneSwitcher.condition.record.entry="{{condition}}{{duration}}"
|
||||
AdvSceneSwitcher.condition.process="プロセス"
|
||||
AdvSceneSwitcher.condition.process.entry="{{processes}}{{regex}}が実行中{{focused}}に集中しています"
|
||||
AdvSceneSwitcher.condition.process.entry.focus="現在のフォアグラウンドプロセス:{{focusProcess}}"
|
||||
AdvSceneSwitcher.condition.process.layout="{{processes}}{{regex}}が実行中{{focused}}に集中しています"
|
||||
AdvSceneSwitcher.condition.process.layout.focus="現在のフォアグラウンドプロセス:{{focusProcess}}"
|
||||
AdvSceneSwitcher.condition.idle="アイドル"
|
||||
AdvSceneSwitcher.condition.idle.entry="{{duration}}の間、キーボードまたはマウスの入力がありません"
|
||||
AdvSceneSwitcher.condition.pluginState="プラグインの状態"
|
||||
@@ -923,7 +923,7 @@ AdvSceneSwitcher.action.macro.type.pause="一時停止"
|
||||
AdvSceneSwitcher.action.macro.type.unpause="一時停止解除"
|
||||
AdvSceneSwitcher.action.macro.type.togglePause="一時停止を切り替え"
|
||||
AdvSceneSwitcher.action.macro.type.resetCounter="カウンターリセット"
|
||||
AdvSceneSwitcher.action.macro.type.run="マクロを実行"
|
||||
AdvSceneSwitcher.action.macro.type.runActions="マクロを実行"
|
||||
AdvSceneSwitcher.action.macro.type.run.conditions.ignore="条件の状態を考慮しない"
|
||||
AdvSceneSwitcher.action.macro.type.run.conditions.true="条件が true と評価された場合のみ"
|
||||
AdvSceneSwitcher.action.macro.type.run.conditions.false="条件が false と評価された場合のみ"
|
||||
|
||||
@@ -395,8 +395,8 @@ AdvSceneSwitcher.condition.record.state.stop="Gravação parada"
|
||||
AdvSceneSwitcher.condition.record.state.duration="Duração da gravação é maior que"
|
||||
AdvSceneSwitcher.condition.record.entry="{{condition}}{{duration}}"
|
||||
AdvSceneSwitcher.condition.process="Processo"
|
||||
AdvSceneSwitcher.condition.process.entry="{{processes}}{{regex}}está em execução{{focused}}e está em foco"
|
||||
AdvSceneSwitcher.condition.process.entry.focus="Processo atual em primeiro plano:{{focusProcess}}"
|
||||
AdvSceneSwitcher.condition.process.layout="{{processes}}{{regex}}está em execução{{focused}}e está em foco"
|
||||
AdvSceneSwitcher.condition.process.layout.focus="Processo atual em primeiro plano:{{focusProcess}}"
|
||||
AdvSceneSwitcher.condition.idle="Inativo"
|
||||
AdvSceneSwitcher.condition.idle.entry="Sem entradas de teclado ou mouse por {{duration}}"
|
||||
AdvSceneSwitcher.condition.pluginState="Estado do plugin"
|
||||
@@ -765,7 +765,6 @@ AdvSceneSwitcher.action.streaming.type.server="Definir URL do servidor"
|
||||
AdvSceneSwitcher.action.streaming.type.streamKey="Definir chave de transmissão"
|
||||
AdvSceneSwitcher.action.streaming.type.username="Definir nome de usuário"
|
||||
AdvSceneSwitcher.action.streaming.type.password="Definir senha"
|
||||
AdvSceneSwitcher.action.streaming.entry="{{actions}}{{keyFrameInterval}}{{stringValue}}{{showPassword}}"
|
||||
AdvSceneSwitcher.action.run="Executar"
|
||||
AdvSceneSwitcher.action.run.wait.entry="{{wait}}Aguardar a saída do processo ou no máximo {{timeout}}{{waitHelp}}"
|
||||
AdvSceneSwitcher.action.run.wait.help.tooltip="Observe que as propriedades da macro não funcionarão se você deixar isso desmarcado, pois o processo é iniciado de forma independente do restante da lógica e não há controle sobre ele."
|
||||
@@ -840,7 +839,7 @@ AdvSceneSwitcher.action.macro="Macro"
|
||||
AdvSceneSwitcher.action.macro.type.pause="Pausar"
|
||||
AdvSceneSwitcher.action.macro.type.unpause="Retomar"
|
||||
AdvSceneSwitcher.action.macro.type.resetCounter="Reiniciar contador"
|
||||
AdvSceneSwitcher.action.macro.type.run="Executar macro"
|
||||
AdvSceneSwitcher.action.macro.type.runActions="Executar macro"
|
||||
AdvSceneSwitcher.action.macro.type.run.conditions.ignore="Não considerar o estado da condição"
|
||||
AdvSceneSwitcher.action.macro.type.run.conditions.true="Apenas se as condições forem verdadeiras"
|
||||
AdvSceneSwitcher.action.macro.type.run.conditions.false="Apenas se as condições forem falsas"
|
||||
|
||||
@@ -103,7 +103,7 @@ AdvSceneSwitcher.condition.record.state.start="Запись запущена"
|
||||
AdvSceneSwitcher.condition.record.state.pause="Запись приостановлена"
|
||||
AdvSceneSwitcher.condition.record.state.stop="Запись остановлена"
|
||||
AdvSceneSwitcher.condition.process="Процесс"
|
||||
AdvSceneSwitcher.condition.process.entry="{{processes}}{{regex}}запущен{{focused}}и сфокусирован"
|
||||
AdvSceneSwitcher.condition.process.layout="{{processes}}{{regex}}запущен{{focused}}и сфокусирован"
|
||||
AdvSceneSwitcher.condition.idle="Простой"
|
||||
AdvSceneSwitcher.condition.idle.entry="Не было ни клавиатуры, ни мыши в течении{{duration}}"
|
||||
AdvSceneSwitcher.condition.pluginState="Состояние плагина"
|
||||
@@ -135,7 +135,6 @@ AdvSceneSwitcher.action.replay.type.save="Сохранить буфер восп
|
||||
AdvSceneSwitcher.action.streaming="Потоковое вещание"
|
||||
AdvSceneSwitcher.action.streaming.type.stop="Остановить потоковое вещание"
|
||||
AdvSceneSwitcher.action.streaming.type.start="Начать потоковое вещание"
|
||||
AdvSceneSwitcher.action.streaming.entry="{{actions}}{{keyFrameInterval}}{{stringValue}}{{showPassword}}"
|
||||
AdvSceneSwitcher.action.run="Запустить"
|
||||
|
||||
|
||||
|
||||
@@ -150,7 +150,7 @@ AdvSceneSwitcher.condition.record.state.start="Kayıt Çalışıyor"
|
||||
AdvSceneSwitcher.condition.record.state.pause="Kayıt durakladı"
|
||||
AdvSceneSwitcher.condition.record.state.stop="Kayıt durdu"
|
||||
AdvSceneSwitcher.condition.process="İşlem"
|
||||
AdvSceneSwitcher.condition.process.entry="{{processes}}{{regex}}çalışıyor{{focused}}ve odaklandı"
|
||||
AdvSceneSwitcher.condition.process.layout="{{processes}}{{regex}}çalışıyor{{focused}}ve odaklandı"
|
||||
AdvSceneSwitcher.condition.idle="Boşta"
|
||||
AdvSceneSwitcher.condition.idle.entry="...için klavye veya fare girişi yok {{duration}}"
|
||||
AdvSceneSwitcher.condition.pluginState="Eklenti durumu"
|
||||
@@ -270,7 +270,6 @@ AdvSceneSwitcher.action.replay.type.save="Tekrar arabelleğini kaydet"
|
||||
AdvSceneSwitcher.action.streaming="Yayın"
|
||||
AdvSceneSwitcher.action.streaming.type.stop="Yayın durdur"
|
||||
AdvSceneSwitcher.action.streaming.type.start="Yayın başlat"
|
||||
AdvSceneSwitcher.action.streaming.entry="{{actions}}{{keyFrameInterval}}{{stringValue}}{{showPassword}}"
|
||||
AdvSceneSwitcher.action.run="Çalıştır"
|
||||
AdvSceneSwitcher.action.sceneVisibility="Sahne öğesi görünürlüğü"
|
||||
AdvSceneSwitcher.action.sceneVisibility.type.show="Göster"
|
||||
@@ -301,7 +300,7 @@ AdvSceneSwitcher.action.macro="Makro"
|
||||
AdvSceneSwitcher.action.macro.type.pause="Duraklat"
|
||||
AdvSceneSwitcher.action.macro.type.unpause="Duraklatma"
|
||||
AdvSceneSwitcher.action.macro.type.resetCounter="Sayacı sıfırla"
|
||||
AdvSceneSwitcher.action.macro.type.run="Çalıştır"
|
||||
AdvSceneSwitcher.action.macro.type.runActions="Çalıştır"
|
||||
AdvSceneSwitcher.action.pluginState="Eklenti durumu"
|
||||
AdvSceneSwitcher.action.pluginState.type.stop="Advanced Scene Switcher eklentisini durdurun"
|
||||
AdvSceneSwitcher.action.pluginState.type.noMatch="Eşleşmeme davranışını değiştirin:"
|
||||
|
||||
@@ -424,8 +424,8 @@ AdvSceneSwitcher.condition.record.state.stop="录制停止"
|
||||
AdvSceneSwitcher.condition.record.state.duration="录制时间长于"
|
||||
AdvSceneSwitcher.condition.record.entry="{{condition}}{{duration}}"
|
||||
AdvSceneSwitcher.condition.process="进程"
|
||||
AdvSceneSwitcher.condition.process.entry="{{processes}}{{regex}}为正在运行中{{focused}}且为焦点"
|
||||
AdvSceneSwitcher.condition.process.entry.focus="当前焦点进程: {{focusProcess}}"
|
||||
AdvSceneSwitcher.condition.process.layout="{{processes}}{{regex}}为正在运行中{{focused}}且为焦点"
|
||||
AdvSceneSwitcher.condition.process.layout.focus="当前焦点进程: {{focusProcess}}"
|
||||
AdvSceneSwitcher.condition.idle="闲置检测"
|
||||
AdvSceneSwitcher.condition.idle.entry="{{duration}}内没有键盘或鼠标输入"
|
||||
AdvSceneSwitcher.condition.pluginState="插件状态"
|
||||
@@ -825,7 +825,6 @@ AdvSceneSwitcher.action.streaming.type.server="设置服务器"
|
||||
AdvSceneSwitcher.action.streaming.type.streamKey="设置推流码"
|
||||
AdvSceneSwitcher.action.streaming.type.username="设置用户名"
|
||||
AdvSceneSwitcher.action.streaming.type.password="设置密码"
|
||||
AdvSceneSwitcher.action.streaming.entry="{{actions}}{{keyFrameInterval}}{{stringValue}}{{showPassword}}"
|
||||
AdvSceneSwitcher.action.run="运行"
|
||||
AdvSceneSwitcher.action.run.wait.entry="{{wait}}进程退出或最多等待{{timeout}}{{waitHelp}}"
|
||||
AdvSceneSwitcher.action.run.wait.help.tooltip="请注意,如果不勾选此选项,宏属性将不起作用,因为进程的启动会脱离逻辑的其他部分,因此无法对其进行控制."
|
||||
@@ -900,7 +899,7 @@ AdvSceneSwitcher.action.macro="宏"
|
||||
AdvSceneSwitcher.action.macro.type.pause="停用"
|
||||
AdvSceneSwitcher.action.macro.type.unpause="启用"
|
||||
AdvSceneSwitcher.action.macro.type.resetCounter="重置计数器"
|
||||
AdvSceneSwitcher.action.macro.type.run="运行宏"
|
||||
AdvSceneSwitcher.action.macro.type.runActions="运行宏"
|
||||
AdvSceneSwitcher.action.macro.type.run.conditions.ignore="忽略条件结果"
|
||||
AdvSceneSwitcher.action.macro.type.run.conditions.true="仅当条件结果为真时"
|
||||
AdvSceneSwitcher.action.macro.type.run.conditions.false="仅当条件结果为假时"
|
||||
|
||||
10
data/res/images/DarkVariable.svg
Normal file
10
data/res/images/DarkVariable.svg
Normal file
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8"?><!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
|
||||
<svg fill="#fefefe" xmlns="http://www.w3.org/2000/svg"
|
||||
width="800px" height="800px" viewBox="0 0 52 52" enable-background="new 0 0 52 52" xml:space="preserve">
|
||||
<path d="M42.6,17.8c2.4,0,7.2-2,7.2-8.4c0-6.4-4.6-6.8-6.1-6.8c-2.8,0-5.6,2-8.1,6.3c-2.5,4.4-5.3,9.1-5.3,9.1
|
||||
l-0.1,0c-0.6-3.1-1.1-5.6-1.3-6.7c-0.5-2.7-3.6-8.4-9.9-8.4c-6.4,0-12.2,3.7-12.2,3.7l0,0C5.8,7.3,5.1,8.5,5.1,9.9
|
||||
c0,2.1,1.7,3.9,3.9,3.9c0.6,0,1.2-0.2,1.7-0.4l0,0c0,0,4.8-2.7,5.9,0c0.3,0.8,0.6,1.7,0.9,2.7c1.2,4.2,2.4,9.1,3.3,13.5l-4.2,6
|
||||
c0,0-4.7-1.7-7.1-1.7s-7.2,2-7.2,8.4s4.6,6.8,6.1,6.8c2.8,0,5.6-2,8.1-6.3c2.5-4.4,5.3-9.1,5.3-9.1c0.8,4,1.5,7.1,1.9,8.5
|
||||
c1.6,4.5,5.3,7.2,10.1,7.2c0,0,5,0,10.9-3.3c1.4-0.6,2.4-2,2.4-3.6c0-2.1-1.7-3.9-3.9-3.9c-0.6,0-1.2,0.2-1.7,0.4l0,0
|
||||
c0,0-4.2,2.4-5.6,0.5c-1-2-1.9-4.6-2.6-7.8c-0.6-2.8-1.3-6.2-2-9.5l4.3-6.2C35.5,16.1,40.2,17.8,42.6,17.8z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 973 B |
10
data/res/images/LightVariable.svg
Normal file
10
data/res/images/LightVariable.svg
Normal file
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8"?><!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
|
||||
<svg fill="#202020" xmlns="http://www.w3.org/2000/svg"
|
||||
width="800px" height="800px" viewBox="0 0 52 52" enable-background="new 0 0 52 52" xml:space="preserve">
|
||||
<path d="M42.6,17.8c2.4,0,7.2-2,7.2-8.4c0-6.4-4.6-6.8-6.1-6.8c-2.8,0-5.6,2-8.1,6.3c-2.5,4.4-5.3,9.1-5.3,9.1
|
||||
l-0.1,0c-0.6-3.1-1.1-5.6-1.3-6.7c-0.5-2.7-3.6-8.4-9.9-8.4c-6.4,0-12.2,3.7-12.2,3.7l0,0C5.8,7.3,5.1,8.5,5.1,9.9
|
||||
c0,2.1,1.7,3.9,3.9,3.9c0.6,0,1.2-0.2,1.7-0.4l0,0c0,0,4.8-2.7,5.9,0c0.3,0.8,0.6,1.7,0.9,2.7c1.2,4.2,2.4,9.1,3.3,13.5l-4.2,6
|
||||
c0,0-4.7-1.7-7.1-1.7s-7.2,2-7.2,8.4s4.6,6.8,6.1,6.8c2.8,0,5.6-2,8.1-6.3c2.5-4.4,5.3-9.1,5.3-9.1c0.8,4,1.5,7.1,1.9,8.5
|
||||
c1.6,4.5,5.3,7.2,10.1,7.2c0,0,5,0,10.9-3.3c1.4-0.6,2.4-2,2.4-3.6c0-2.1-1.7-3.9-3.9-3.9c-0.6,0-1.2,0.2-1.7,0.4l0,0
|
||||
c0,0-4.2,2.4-5.6,0.5c-1-2-1.9-4.6-2.6-7.8c-0.6-2.8-1.3-6.2-2-9.5l4.3-6.2C35.5,16.1,40.2,17.8,42.6,17.8z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 973 B |
@@ -322,8 +322,7 @@ void SwitcherData::SetPreconditions()
|
||||
{
|
||||
// Window title
|
||||
lastTitle = currentTitle;
|
||||
std::string title;
|
||||
GetCurrentWindowTitle(title);
|
||||
auto title = GetCurrentWindowTitle();
|
||||
for (auto &window : ignoreWindowsSwitches) {
|
||||
bool equals = (title == window);
|
||||
bool matches = false;
|
||||
@@ -343,7 +342,7 @@ void SwitcherData::SetPreconditions()
|
||||
currentTitle = title;
|
||||
|
||||
// Process name
|
||||
GetForegroundProcessName(currentForegroundProcess);
|
||||
currentForegroundProcess = GetForegroundProcessName();
|
||||
|
||||
// Macro
|
||||
InvalidateMacroTempVarValues();
|
||||
|
||||
@@ -92,12 +92,11 @@ bool SwitcherData::checkExeSwitch(OBSWeakSource &scene,
|
||||
}
|
||||
|
||||
std::string title = switcher->currentTitle;
|
||||
QStringList runningProcesses;
|
||||
bool ignored = false;
|
||||
bool match = false;
|
||||
|
||||
// Check for match
|
||||
GetProcessList(runningProcesses);
|
||||
const auto runningProcesses = GetProcessList();
|
||||
for (ExecutableSwitch &s : executableSwitches) {
|
||||
if (!s.initialized()) {
|
||||
continue;
|
||||
|
||||
@@ -230,8 +230,7 @@ bool SwitcherData::checkWindowTitleSwitch(OBSWeakSource &scene,
|
||||
|
||||
std::string currentWindowTitle = switcher->currentTitle;
|
||||
bool match = false;
|
||||
std::vector<std::string> windowList;
|
||||
GetWindowList(windowList);
|
||||
const auto windowList = GetWindowList();
|
||||
|
||||
for (WindowSwitch &s : windowSwitches) {
|
||||
if (!s.initialized()) {
|
||||
|
||||
@@ -32,6 +32,9 @@
|
||||
#endif
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
#include <climits>
|
||||
#include <dirent.h>
|
||||
#include <unistd.h>
|
||||
#include "kwin-helpers.h"
|
||||
|
||||
namespace advss {
|
||||
@@ -233,9 +236,9 @@ std::string getWindowName(Window window)
|
||||
return windowTitle;
|
||||
}
|
||||
|
||||
void GetWindowList(std::vector<std::string> &windows)
|
||||
std::vector<std::string> GetWindowList()
|
||||
{
|
||||
windows.resize(0);
|
||||
std::vector<std::string> windows;
|
||||
for (auto window : getTopLevelWindows()) {
|
||||
auto name = getWindowName(window);
|
||||
if (name.empty()) {
|
||||
@@ -243,18 +246,7 @@ void GetWindowList(std::vector<std::string> &windows)
|
||||
}
|
||||
windows.emplace_back(name);
|
||||
}
|
||||
}
|
||||
|
||||
void GetWindowList(QStringList &windows)
|
||||
{
|
||||
windows.clear();
|
||||
for (auto window : getTopLevelWindows()) {
|
||||
auto name = getWindowName(window);
|
||||
if (name.empty()) {
|
||||
continue;
|
||||
}
|
||||
windows << QString::fromStdString(name);
|
||||
}
|
||||
return windows;
|
||||
}
|
||||
|
||||
int getActiveWindow(Window *&window)
|
||||
@@ -278,29 +270,24 @@ int getActiveWindow(Window *&window)
|
||||
&bytes, (uint8_t **)&window);
|
||||
}
|
||||
|
||||
void GetCurrentWindowTitle(std::string &title)
|
||||
std::string GetCurrentWindowTitle()
|
||||
{
|
||||
if (KWin) {
|
||||
title = FocusNotifier::getActiveWindowTitle();
|
||||
return;
|
||||
return FocusNotifier::getActiveWindowTitle();
|
||||
}
|
||||
|
||||
Window *data = 0;
|
||||
if (getActiveWindow(data) != Success || !data) {
|
||||
return;
|
||||
return {};
|
||||
}
|
||||
if (!data[0]) {
|
||||
XFree(data);
|
||||
return;
|
||||
return {};
|
||||
}
|
||||
|
||||
auto name = getWindowName(data[0]);
|
||||
XFree(data);
|
||||
|
||||
if (name.empty()) {
|
||||
return;
|
||||
}
|
||||
title = name;
|
||||
return name;
|
||||
}
|
||||
|
||||
bool windowStatesAreSet(const std::string &windowTitle,
|
||||
@@ -409,16 +396,17 @@ static void getProcessListProcps2(QStringList &processes)
|
||||
#endif
|
||||
}
|
||||
|
||||
void GetProcessList(QStringList &processes)
|
||||
QStringList GetProcessList()
|
||||
{
|
||||
processes.clear();
|
||||
QStringList processes;
|
||||
if (libprocpsSupported) {
|
||||
getProcessListProcps(processes);
|
||||
return;
|
||||
return processes;
|
||||
}
|
||||
if (libprocps2Supported) {
|
||||
getProcessListProcps2(processes);
|
||||
}
|
||||
return processes;
|
||||
}
|
||||
|
||||
long getForegroundProcessPid()
|
||||
@@ -469,24 +457,83 @@ std::string getProcNameFromPid(long pid)
|
||||
return name;
|
||||
}
|
||||
|
||||
void GetForegroundProcessName(QString &proc)
|
||||
std::string GetForegroundProcessName()
|
||||
{
|
||||
std::string temp;
|
||||
GetForegroundProcessName(temp);
|
||||
proc = QString::fromStdString(temp);
|
||||
auto pid = getForegroundProcessPid();
|
||||
return getProcNameFromPid(pid);
|
||||
}
|
||||
|
||||
void GetForegroundProcessName(std::string &proc)
|
||||
static std::string getProcessPathFromPid(long pid)
|
||||
{
|
||||
std::string linkPath = "/proc/" + std::to_string(pid) + "/exe";
|
||||
char buf[PATH_MAX];
|
||||
ssize_t len = readlink(linkPath.c_str(), buf, sizeof(buf) - 1);
|
||||
if (len <= 0) {
|
||||
return {};
|
||||
}
|
||||
buf[len] = '\0';
|
||||
return buf;
|
||||
}
|
||||
|
||||
std::string GetForegroundProcessPath()
|
||||
{
|
||||
proc.resize(0);
|
||||
auto pid = getForegroundProcessPid();
|
||||
proc = getProcNameFromPid(pid);
|
||||
if (pid <= 0) {
|
||||
return {};
|
||||
}
|
||||
return getProcessPathFromPid(pid);
|
||||
}
|
||||
|
||||
QStringList GetProcessPathsFromName(const QString &name)
|
||||
{
|
||||
QStringList paths;
|
||||
const std::string nameStr = name.toStdString();
|
||||
DIR *procDir = opendir("/proc");
|
||||
if (!procDir) {
|
||||
return paths;
|
||||
}
|
||||
|
||||
struct dirent *entry;
|
||||
while ((entry = readdir(procDir)) != nullptr) {
|
||||
bool isPid = (entry->d_name[0] != '\0');
|
||||
for (const char *c = entry->d_name; *c; c++) {
|
||||
if (!isdigit(*c)) {
|
||||
isPid = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!isPid) {
|
||||
continue;
|
||||
}
|
||||
|
||||
std::string pid = entry->d_name;
|
||||
std::string commPath = "/proc/" + pid + "/comm";
|
||||
std::ifstream commFile(commPath);
|
||||
if (!commFile) {
|
||||
continue;
|
||||
}
|
||||
std::string comm;
|
||||
std::getline(commFile, comm);
|
||||
if (comm != nameStr) {
|
||||
continue;
|
||||
}
|
||||
|
||||
std::string path = getProcessPathFromPid(std::stol(pid));
|
||||
if (path.empty()) {
|
||||
continue;
|
||||
}
|
||||
QString qPath = QString::fromStdString(path);
|
||||
if (!paths.contains(qPath)) {
|
||||
paths.append(qPath);
|
||||
}
|
||||
}
|
||||
closedir(procDir);
|
||||
return paths;
|
||||
}
|
||||
|
||||
bool IsInFocus(const QString &executable)
|
||||
{
|
||||
std::string current;
|
||||
GetForegroundProcessName(current);
|
||||
const auto current = GetForegroundProcessName();
|
||||
|
||||
// True if executable switch equals current window
|
||||
bool equals = (executable.toStdString() == current);
|
||||
|
||||
@@ -49,10 +49,12 @@ MacroActionEdit::MacroActionEdit(QWidget *parent,
|
||||
_section->AddHeaderWidget(_enable);
|
||||
_section->AddHeaderWidget(_actionSelection);
|
||||
_section->AddHeaderWidget(_headerInfo);
|
||||
_section->AddHeaderWidget(_varMappingToggle);
|
||||
|
||||
auto actionLayout = new QVBoxLayout;
|
||||
actionLayout->setContentsMargins({7, 7, 7, 7});
|
||||
actionLayout->addWidget(_section);
|
||||
actionLayout->addWidget(_outputMappings);
|
||||
_contentLayout->addLayout(actionLayout);
|
||||
|
||||
auto mainLayout = new QHBoxLayout;
|
||||
@@ -61,7 +63,6 @@ MacroActionEdit::MacroActionEdit(QWidget *parent,
|
||||
mainLayout->addWidget(_frame);
|
||||
setLayout(mainLayout);
|
||||
|
||||
_entryData = entryData;
|
||||
SetupWidgets(true);
|
||||
|
||||
actionStateTimer->start(300);
|
||||
@@ -90,7 +91,10 @@ void MacroActionEdit::SetupWidgets(bool basicSetup)
|
||||
auto widget = MacroActionFactory::CreateWidget(id, this, *_entryData);
|
||||
QWidget::connect(widget, SIGNAL(HeaderInfoChanged(const QString &)),
|
||||
this, SLOT(HeaderInfoChanged(const QString &)));
|
||||
QWidget::connect(widget, SIGNAL(ShowVariableMappings(bool)), this,
|
||||
SLOT(ShowVariableMappings(bool)));
|
||||
_section->SetContent(widget, (*_entryData)->GetCollapsed());
|
||||
SetupVarMappings((*_entryData).get());
|
||||
SetFocusPolicyOfWidgets();
|
||||
|
||||
_allWidgetsAreSetup = true;
|
||||
@@ -121,7 +125,10 @@ void MacroActionEdit::ActionSelectionChanged(const QString &text)
|
||||
auto widget = MacroActionFactory::CreateWidget(id, this, *_entryData);
|
||||
QWidget::connect(widget, SIGNAL(HeaderInfoChanged(const QString &)),
|
||||
this, SLOT(HeaderInfoChanged(const QString &)));
|
||||
QWidget::connect(widget, SIGNAL(ShowVariableMappings(bool)), this,
|
||||
SLOT(ShowVariableMappings(bool)));
|
||||
_section->SetContent(widget);
|
||||
SetupVarMappings((*_entryData).get());
|
||||
SetFocusPolicyOfWidgets();
|
||||
}
|
||||
|
||||
|
||||
@@ -120,6 +120,15 @@ bool MacroActionMacro::PerformAction()
|
||||
case Action::TOGGLE_ACTION:
|
||||
AdjustActionState(macro);
|
||||
break;
|
||||
case Action::RUN_MACRO: {
|
||||
if (_runOptions.skipWhenPaused && macro->Paused()) {
|
||||
break;
|
||||
}
|
||||
const bool conditionsMatched =
|
||||
macro->CheckConditions(true);
|
||||
macro->PerformActions(conditionsMatched, false, true);
|
||||
break;
|
||||
}
|
||||
case Action::GET_INFO: {
|
||||
SetTempVarValue(
|
||||
"conditionCount",
|
||||
@@ -210,6 +219,9 @@ void MacroActionMacro::LogAction() const
|
||||
case Action::GET_INFO:
|
||||
ablog(LOG_INFO, "get info for \"%s\"", macro->Name().c_str());
|
||||
break;
|
||||
case Action::RUN_MACRO:
|
||||
ablog(LOG_INFO, "run macro \"%s\"", macro->Name().c_str());
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -403,7 +415,9 @@ static void populateActionSelection(QComboBox *list)
|
||||
{MacroActionMacro::Action::NESTED_MACRO,
|
||||
"AdvSceneSwitcher.action.macro.type.nestedMacro"},
|
||||
{MacroActionMacro::Action::RUN_ACTIONS,
|
||||
"AdvSceneSwitcher.action.macro.type.run"},
|
||||
"AdvSceneSwitcher.action.macro.type.runActions"},
|
||||
{MacroActionMacro::Action::RUN_MACRO,
|
||||
"AdvSceneSwitcher.action.macro.type.runMacro"},
|
||||
{MacroActionMacro::Action::STOP,
|
||||
"AdvSceneSwitcher.action.macro.type.stop"},
|
||||
{MacroActionMacro::Action::DISABLE_ACTION,
|
||||
@@ -483,6 +497,10 @@ MacroActionMacroEdit::MacroActionMacroEdit(
|
||||
_actionSections(new QComboBox(this)),
|
||||
_skipWhenPaused(new QCheckBox(obs_module_text(
|
||||
"AdvSceneSwitcher.action.macro.type.run.skipWhenPaused"))),
|
||||
_noConditionsWarning(new QLabel(obs_module_text(
|
||||
"AdvSceneSwitcher.action.macro.type.runMacro.noConditionsWarning"))),
|
||||
_runMacroHelp(new HelpIcon(obs_module_text(
|
||||
"AdvSceneSwitcher.action.macro.type.runMacro.help"))),
|
||||
_setInputs(new QCheckBox(obs_module_text(
|
||||
"AdvSceneSwitcher.action.macro.type.run.setInputs"))),
|
||||
_inputs(new MacroInputEdit()),
|
||||
@@ -556,6 +574,7 @@ MacroActionMacroEdit::MacroActionMacroEdit(
|
||||
layout->addLayout(_setInputsLayout);
|
||||
layout->addWidget(_inputs);
|
||||
layout->addWidget(_skipWhenPaused);
|
||||
layout->addWidget(_noConditionsWarning);
|
||||
layout->addWidget(_nestedMacro);
|
||||
setLayout(layout);
|
||||
_entryData = entryData;
|
||||
@@ -725,6 +744,7 @@ void MacroActionMacroEdit::InputsChanged(const StringList &inputs)
|
||||
void MacroActionMacroEdit::SetWidgetVisibility()
|
||||
{
|
||||
_entryLayout->removeWidget(_actions);
|
||||
_entryLayout->removeWidget(_runMacroHelp);
|
||||
_entryLayout->removeWidget(_actionIndex);
|
||||
_entryLayout->removeWidget(_macros);
|
||||
_entryLayout->removeWidget(_actionSections);
|
||||
@@ -740,6 +760,7 @@ void MacroActionMacroEdit::SetWidgetVisibility()
|
||||
|
||||
const std::unordered_map<std::string, QWidget *> placeholders = {
|
||||
{"{{actions}}", _actions},
|
||||
{"{{runMacroHelp}}", _runMacroHelp},
|
||||
{"{{actionIndex}}", _actionIndex},
|
||||
{"{{macros}}", _macros},
|
||||
{"{{actionSections}}", _actionSections},
|
||||
@@ -764,6 +785,9 @@ void MacroActionMacroEdit::SetWidgetVisibility()
|
||||
case MacroActionMacro::Action::GET_INFO:
|
||||
layoutText = "AdvSceneSwitcher.action.macro.layout.other";
|
||||
break;
|
||||
case MacroActionMacro::Action::RUN_MACRO:
|
||||
layoutText = "AdvSceneSwitcher.action.macro.layout.runMacro";
|
||||
break;
|
||||
case MacroActionMacro::Action::RUN_ACTIONS:
|
||||
layoutText = "AdvSceneSwitcher.action.macro.layout.run";
|
||||
break;
|
||||
@@ -790,7 +814,8 @@ void MacroActionMacroEdit::SetWidgetVisibility()
|
||||
}
|
||||
|
||||
if (action == MacroActionMacro::Action::RUN_ACTIONS ||
|
||||
action == MacroActionMacro::Action::STOP) {
|
||||
action == MacroActionMacro::Action::STOP ||
|
||||
action == MacroActionMacro::Action::RUN_MACRO) {
|
||||
_macros->HideSelectedMacro();
|
||||
} else {
|
||||
_macros->ShowAllMacros();
|
||||
@@ -835,8 +860,17 @@ void MacroActionMacroEdit::SetWidgetVisibility()
|
||||
_actionSections->setVisible(
|
||||
action == MacroActionMacro::Action::RUN_ACTIONS ||
|
||||
isModifyingActionState);
|
||||
_skipWhenPaused->setVisible(action ==
|
||||
MacroActionMacro::Action::RUN_ACTIONS);
|
||||
_skipWhenPaused->setVisible(
|
||||
action == MacroActionMacro::Action::RUN_ACTIONS ||
|
||||
action == MacroActionMacro::Action::RUN_MACRO);
|
||||
|
||||
if (action == MacroActionMacro::Action::RUN_MACRO) {
|
||||
auto macro = _entryData->_macro.GetMacro();
|
||||
_noConditionsWarning->setVisible(!macro ||
|
||||
macro->Conditions().empty());
|
||||
} else {
|
||||
_noConditionsWarning->setVisible(false);
|
||||
}
|
||||
|
||||
_nestedMacro->setVisible(action ==
|
||||
MacroActionMacro::Action::NESTED_MACRO);
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
|
||||
#include <QCheckBox>
|
||||
#include <QHBoxLayout>
|
||||
#include <QLabel>
|
||||
|
||||
namespace advss {
|
||||
|
||||
@@ -57,6 +58,7 @@ public:
|
||||
TOGGLE_PAUSE,
|
||||
NESTED_MACRO,
|
||||
GET_INFO,
|
||||
RUN_MACRO,
|
||||
};
|
||||
|
||||
void SetAction(Action);
|
||||
@@ -131,6 +133,8 @@ private:
|
||||
QCheckBox *_reevaluateConditionState;
|
||||
QComboBox *_actionSections;
|
||||
QCheckBox *_skipWhenPaused;
|
||||
QLabel *_noConditionsWarning;
|
||||
HelpIcon *_runMacroHelp;
|
||||
QCheckBox *_setInputs;
|
||||
MacroInputEdit *_inputs;
|
||||
QHBoxLayout *_entryLayout;
|
||||
|
||||
@@ -19,10 +19,11 @@ const std::string MacroActionVariable::id = "variable";
|
||||
|
||||
std::vector<TempVariableRef> MacroActionVariable::GetTempVarRefs() const
|
||||
{
|
||||
if (!_tempVar.HasValidID()) {
|
||||
return {};
|
||||
auto refs = MacroSegment::GetTempVarRefs();
|
||||
if (_tempVar.HasValidID()) {
|
||||
refs.push_back(_tempVar);
|
||||
}
|
||||
return {_tempVar};
|
||||
return refs;
|
||||
}
|
||||
|
||||
bool MacroActionVariable::_registered = MacroActionFactory::Register(
|
||||
|
||||
@@ -126,10 +126,12 @@ MacroConditionEdit::MacroConditionEdit(
|
||||
_section->AddHeaderWidget(_conditionSelection);
|
||||
_section->AddHeaderWidget(_headerInfo);
|
||||
_section->AddHeaderWidget(_dur);
|
||||
_section->AddHeaderWidget(_varMappingToggle);
|
||||
|
||||
QVBoxLayout *conditionLayout = new QVBoxLayout;
|
||||
conditionLayout->setContentsMargins({7, 7, 7, 7});
|
||||
conditionLayout->addWidget(_section);
|
||||
conditionLayout->addWidget(_outputMappings);
|
||||
_contentLayout->addLayout(conditionLayout);
|
||||
|
||||
QHBoxLayout *mainLayout = new QHBoxLayout;
|
||||
@@ -204,7 +206,10 @@ void MacroConditionEdit::SetupWidgets(bool basicSetup)
|
||||
MacroConditionFactory::CreateWidget(id, this, *_entryData);
|
||||
QWidget::connect(widget, SIGNAL(HeaderInfoChanged(const QString &)),
|
||||
this, SLOT(HeaderInfoChanged(const QString &)));
|
||||
QWidget::connect(widget, SIGNAL(ShowVariableMappings(bool)), this,
|
||||
SLOT(ShowVariableMappings(bool)));
|
||||
_section->SetContent(widget, (*_entryData)->GetCollapsed());
|
||||
SetupVarMappings((*_entryData).get());
|
||||
SetFocusPolicyOfWidgets();
|
||||
|
||||
_allWidgetsAreSetup = true;
|
||||
@@ -245,7 +250,10 @@ void MacroConditionEdit::ConditionSelectionChanged(const QString &text)
|
||||
MacroConditionFactory::CreateWidget(id, this, *_entryData);
|
||||
QWidget::connect(widget, SIGNAL(HeaderInfoChanged(const QString &)),
|
||||
this, SLOT(HeaderInfoChanged(const QString &)));
|
||||
QWidget::connect(widget, SIGNAL(ShowVariableMappings(bool)), this,
|
||||
SLOT(ShowVariableMappings(bool)));
|
||||
_section->SetContent(widget);
|
||||
SetupVarMappings((*_entryData).get());
|
||||
_dur->setVisible(MacroConditionFactory::UsesDurationModifier(id));
|
||||
SetFocusPolicyOfWidgets();
|
||||
}
|
||||
|
||||
@@ -8,10 +8,11 @@ const std::string MacroConditionTempVar::id = "temp_var";
|
||||
|
||||
std::vector<TempVariableRef> MacroConditionTempVar::GetTempVarRefs() const
|
||||
{
|
||||
if (!_tempVar.HasValidID()) {
|
||||
return {};
|
||||
auto refs = MacroSegment::GetTempVarRefs();
|
||||
if (_tempVar.HasValidID()) {
|
||||
refs.push_back(_tempVar);
|
||||
}
|
||||
return {_tempVar};
|
||||
return refs;
|
||||
}
|
||||
|
||||
bool MacroConditionTempVar::_registered = MacroConditionFactory::Register(
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
#include "macro-segment.hpp"
|
||||
#include "macro.hpp"
|
||||
#include "mouse-wheel-guard.hpp"
|
||||
#include "path-helpers.hpp"
|
||||
#include "section.hpp"
|
||||
#include "ui-helpers.hpp"
|
||||
#include "variable.hpp"
|
||||
|
||||
#include <QApplication>
|
||||
#include <QEvent>
|
||||
@@ -15,7 +17,48 @@ namespace advss {
|
||||
|
||||
std::vector<TempVariableRef> MacroSegment::GetTempVarRefs() const
|
||||
{
|
||||
return {};
|
||||
std::vector<TempVariableRef> refs;
|
||||
for (const auto &mapping : _varMappings) {
|
||||
if (mapping.tempVar.HasValidID()) {
|
||||
refs.push_back(mapping.tempVar);
|
||||
}
|
||||
}
|
||||
return refs;
|
||||
}
|
||||
|
||||
void MacroSegment::ApplyVarMappings()
|
||||
{
|
||||
auto macro = GetMacro();
|
||||
for (const auto &mapping : _varMappings) {
|
||||
auto var = mapping.variable.lock();
|
||||
if (!var) {
|
||||
continue;
|
||||
}
|
||||
const auto tempVar = mapping.tempVar.GetTempVariable(macro);
|
||||
if (!tempVar) {
|
||||
continue;
|
||||
}
|
||||
const auto value = tempVar->Value();
|
||||
if (!value) {
|
||||
continue;
|
||||
}
|
||||
var->SetValue(*value);
|
||||
}
|
||||
}
|
||||
|
||||
const std::vector<VarMapping> &MacroSegment::GetVarMappings() const
|
||||
{
|
||||
return _varMappings;
|
||||
}
|
||||
|
||||
void MacroSegment::SetVarMappings(const std::vector<VarMapping> &mappings)
|
||||
{
|
||||
_varMappings = mappings;
|
||||
}
|
||||
|
||||
std::vector<TempVariable> MacroSegment::GetOwnTempVars() const
|
||||
{
|
||||
return _tempVariables;
|
||||
}
|
||||
|
||||
MacroSegment::MacroSegment(Macro *m, bool supportsVariableValue)
|
||||
@@ -24,14 +67,32 @@ MacroSegment::MacroSegment(Macro *m, bool supportsVariableValue)
|
||||
{
|
||||
}
|
||||
|
||||
void MacroSegment::SetVarMappingExpanded(bool expanded)
|
||||
{
|
||||
_varMappingExpanded = expanded;
|
||||
}
|
||||
|
||||
bool MacroSegment::Save(obs_data_t *obj) const
|
||||
{
|
||||
OBSDataAutoRelease data = obs_data_create();
|
||||
obs_data_set_bool(data, "collapsed", _collapsed);
|
||||
obs_data_set_bool(data, "varMappingExpanded", _varMappingExpanded);
|
||||
obs_data_set_bool(data, "useCustomLabel", _useCustomLabel);
|
||||
obs_data_set_string(data, "customLabel", _customLabel.c_str());
|
||||
obs_data_set_bool(data, "enabled", _enabled);
|
||||
obs_data_set_int(data, "version", 1);
|
||||
|
||||
OBSDataArrayAutoRelease mappingsArray = obs_data_array_create();
|
||||
for (const auto &mapping : _varMappings) {
|
||||
OBSDataAutoRelease item = obs_data_create();
|
||||
mapping.tempVar.Save(item, GetMacro(), "tempVar");
|
||||
obs_data_set_string(
|
||||
item, "variable",
|
||||
GetWeakVariableName(mapping.variable).c_str());
|
||||
obs_data_array_push_back(mappingsArray, item);
|
||||
}
|
||||
obs_data_set_array(data, "varMappings", mappingsArray);
|
||||
|
||||
obs_data_set_obj(obj, "segmentSettings", data);
|
||||
return true;
|
||||
}
|
||||
@@ -40,6 +101,7 @@ bool MacroSegment::Load(obs_data_t *obj)
|
||||
{
|
||||
OBSDataAutoRelease data = obs_data_get_obj(obj, "segmentSettings");
|
||||
_collapsed = obs_data_get_bool(data, "collapsed");
|
||||
_varMappingExpanded = obs_data_get_bool(data, "varMappingExpanded");
|
||||
_useCustomLabel = obs_data_get_bool(data, "useCustomLabel");
|
||||
_customLabel = obs_data_get_string(data, "customLabel");
|
||||
obs_data_set_default_bool(data, "enabled", true);
|
||||
@@ -50,6 +112,20 @@ bool MacroSegment::Load(obs_data_t *obj)
|
||||
_enabled = obs_data_get_bool(obj, "enabled");
|
||||
}
|
||||
|
||||
_varMappings.clear();
|
||||
OBSDataArrayAutoRelease mappingsArray =
|
||||
obs_data_get_array(data, "varMappings");
|
||||
const size_t count = obs_data_array_count(mappingsArray);
|
||||
_varMappings.reserve(count);
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
OBSDataAutoRelease item = obs_data_array_item(mappingsArray, i);
|
||||
VarMapping mapping;
|
||||
mapping.variable = GetWeakVariableByName(
|
||||
obs_data_get_string(item, "variable"));
|
||||
_varMappings.push_back(std::move(mapping));
|
||||
_varMappings.back().tempVar.Load(item, GetMacro(), "tempVar");
|
||||
}
|
||||
|
||||
ClearAvailableTempvars();
|
||||
return true;
|
||||
}
|
||||
@@ -167,24 +243,6 @@ bool MacroSegment::IsTempVarInUse(const std::string &id) const
|
||||
return false;
|
||||
}
|
||||
|
||||
void MacroSegment::CopyTempVarValuesFrom(const MacroSegment &other)
|
||||
{
|
||||
for (const auto &src : other._tempVariables) {
|
||||
for (auto &dst : _tempVariables) {
|
||||
if (dst.ID() != src.ID()) {
|
||||
continue;
|
||||
}
|
||||
auto value = src.Value();
|
||||
if (value) {
|
||||
dst.SetValue(*value);
|
||||
} else {
|
||||
dst.InvalidateValue();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MacroSegment::SetTempVarValue(const std::string &id,
|
||||
const std::string &value)
|
||||
{
|
||||
@@ -244,11 +302,38 @@ MacroSegmentEdit::MacroSegmentEdit(QWidget *parent)
|
||||
_headerInfo(new QLabel()),
|
||||
_frame(new QWidget),
|
||||
_contentLayout(new QVBoxLayout),
|
||||
_outputMappings(new TempVarOutputMappingsWidget(this)),
|
||||
_varMappingToggle(new QPushButton(this)),
|
||||
_noBorderframe(new QFrame),
|
||||
_borderFrame(new QFrame),
|
||||
_dropLineAbove(new QFrame),
|
||||
_dropLineBelow(new QFrame)
|
||||
{
|
||||
const auto iconPath = QString::fromStdString(GetDataFilePath(
|
||||
"res/images/" + GetThemeTypeName() + "Variable.svg"));
|
||||
_varMappingToggle->setIcon(QIcon(iconPath));
|
||||
_varMappingToggle->setMaximumWidth(22);
|
||||
_varMappingToggle->setFlat(true);
|
||||
_varMappingToggle->setCheckable(true);
|
||||
_varMappingToggle->setVisible(false);
|
||||
_varMappingToggle->setToolTip(obs_module_text(
|
||||
"AdvSceneSwitcher.tempVar.outputMappings.toggle"));
|
||||
|
||||
QWidget::connect(_varMappingToggle, &QPushButton::toggled,
|
||||
_outputMappings,
|
||||
&TempVarOutputMappingsWidget::SetPanelExpanded);
|
||||
QWidget::connect(_varMappingToggle, &QPushButton::toggled, this,
|
||||
[this](bool checked) {
|
||||
if (Data()) {
|
||||
Data()->SetVarMappingExpanded(checked);
|
||||
}
|
||||
});
|
||||
QWidget::connect(_outputMappings,
|
||||
&TempVarOutputMappingsWidget::ExpandableChanged,
|
||||
_varMappingToggle, &QPushButton::setVisible);
|
||||
QWidget::connect(_section, &Section::Collapsed, _outputMappings,
|
||||
&TempVarOutputMappingsWidget::SetSectionCollapsed);
|
||||
|
||||
_dropLineAbove->setLineWidth(3);
|
||||
_dropLineAbove->setFixedHeight(11);
|
||||
_dropLineBelow->setLineWidth(3);
|
||||
@@ -310,6 +395,21 @@ MacroSegmentEdit::MacroSegmentEdit(QWidget *parent)
|
||||
_headerInfo->installEventFilter(this);
|
||||
}
|
||||
|
||||
void MacroSegmentEdit::SetupVarMappings(MacroSegment *segment)
|
||||
{
|
||||
_outputMappings->SetSegment(segment);
|
||||
_varMappingToggle->setChecked(segment &&
|
||||
segment->GetVarMappingExpanded());
|
||||
}
|
||||
|
||||
void MacroSegmentEdit::ShowVariableMappings(bool show)
|
||||
{
|
||||
_varMappingToggle->setChecked(show);
|
||||
if (Data()) {
|
||||
Data()->SetVarMappingExpanded(show);
|
||||
}
|
||||
}
|
||||
|
||||
bool MacroSegmentEdit::eventFilter(QObject *obj, QEvent *ev)
|
||||
{
|
||||
if (obj == _headerInfo && ev->type() == QEvent::MouseMove) {
|
||||
|
||||
@@ -9,15 +9,23 @@
|
||||
|
||||
#include <QWidget>
|
||||
#include <QFrame>
|
||||
#include <QPushButton>
|
||||
#include <QVBoxLayout>
|
||||
#include <QTimer>
|
||||
#include <obs-data.h>
|
||||
#include <memory>
|
||||
|
||||
class QLabel;
|
||||
|
||||
namespace advss {
|
||||
|
||||
class Macro;
|
||||
class Variable;
|
||||
|
||||
struct VarMapping {
|
||||
TempVariableRef tempVar;
|
||||
std::weak_ptr<Variable> variable;
|
||||
};
|
||||
|
||||
class EXPORT MacroSegment : public Lockable {
|
||||
public:
|
||||
@@ -28,6 +36,8 @@ public:
|
||||
int GetIndex() const { return _idx; }
|
||||
void SetCollapsed(bool collapsed) { _collapsed = collapsed; }
|
||||
bool GetCollapsed() const { return _collapsed; }
|
||||
void SetVarMappingExpanded(bool expanded);
|
||||
bool GetVarMappingExpanded() const { return _varMappingExpanded; }
|
||||
void SetUseCustomLabel(bool enable) { _useCustomLabel = enable; }
|
||||
bool GetUseCustomLabel() const { return _useCustomLabel; }
|
||||
void SetCustomLabel(const std::string &label) { _customLabel = label; }
|
||||
@@ -43,6 +53,10 @@ public:
|
||||
void SetEnabled(bool);
|
||||
bool Enabled() const;
|
||||
virtual std::string GetVariableValue() const;
|
||||
void ApplyVarMappings();
|
||||
const std::vector<VarMapping> &GetVarMappings() const;
|
||||
void SetVarMappings(const std::vector<VarMapping> &mappings);
|
||||
std::vector<TempVariable> GetOwnTempVars() const;
|
||||
|
||||
protected:
|
||||
friend bool SupportsVariableValue(MacroSegment *);
|
||||
@@ -57,7 +71,6 @@ protected:
|
||||
|
||||
bool IsTempVarInUse(const std::string &id) const;
|
||||
void SetTempVarValue(const std::string &id, const std::string &value);
|
||||
void CopyTempVarValuesFrom(const MacroSegment &other);
|
||||
|
||||
template<typename T, typename = std::enable_if_t<
|
||||
std::is_same<std::decay_t<T>, bool>::value>>
|
||||
@@ -88,6 +101,7 @@ private:
|
||||
// UI helper
|
||||
bool _highlight = false;
|
||||
bool _collapsed = false;
|
||||
bool _varMappingExpanded = false;
|
||||
bool _enabled = true;
|
||||
|
||||
// Custom header labels
|
||||
@@ -100,6 +114,7 @@ private:
|
||||
int _variableRefs = 0;
|
||||
std::string _variableValue;
|
||||
std::vector<TempVariable> _tempVariables;
|
||||
std::vector<VarMapping> _varMappings;
|
||||
|
||||
friend class Macro;
|
||||
};
|
||||
@@ -119,8 +134,11 @@ public:
|
||||
virtual std::shared_ptr<MacroSegment> Data() const = 0;
|
||||
virtual void SetupWidgets(bool basicSetup = false) = 0;
|
||||
|
||||
void SetupVarMappings(MacroSegment *segment);
|
||||
|
||||
public slots:
|
||||
void HeaderInfoChanged(const QString &);
|
||||
void ShowVariableMappings(bool show);
|
||||
|
||||
protected slots:
|
||||
void Collapsed(bool) const;
|
||||
@@ -135,6 +153,8 @@ protected:
|
||||
QLabel *_headerInfo;
|
||||
QWidget *_frame;
|
||||
QVBoxLayout *_contentLayout;
|
||||
TempVarOutputMappingsWidget *_outputMappings;
|
||||
QPushButton *_varMappingToggle;
|
||||
bool _allWidgetsAreSetup = false;
|
||||
|
||||
private:
|
||||
|
||||
@@ -113,6 +113,7 @@ static bool checkCondition(const std::shared_ptr<MacroCondition> &condition)
|
||||
condition->WithLock([&condition, &conditionMatched]() {
|
||||
conditionMatched = condition->EvaluateCondition();
|
||||
});
|
||||
condition->ApplyVarMappings();
|
||||
const auto endTime = std::chrono::high_resolution_clock::now();
|
||||
const auto timeSpent = endTime - startTime;
|
||||
|
||||
@@ -428,14 +429,11 @@ bool Macro::RunActionsHelper(
|
||||
}
|
||||
if (action->Enabled()) {
|
||||
action->LogAction();
|
||||
std::shared_ptr<MacroAction> actionCopy;
|
||||
action->WithLock([&action, &actionCopy]() {
|
||||
actionCopy = action->Copy();
|
||||
});
|
||||
bool actionResult = actionCopy->PerformAction();
|
||||
action->WithLock([&action, &actionCopy]() {
|
||||
action->CopyTempVarValuesFrom(*actionCopy);
|
||||
bool actionResult = false;
|
||||
action->WithLock([&action, &actionResult]() {
|
||||
actionResult = action->PerformAction();
|
||||
});
|
||||
action->ApplyVarMappings();
|
||||
actionsExecutedSuccessfully =
|
||||
actionsExecutedSuccessfully && actionResult;
|
||||
} else {
|
||||
|
||||
@@ -14,10 +14,9 @@
|
||||
|
||||
namespace advss {
|
||||
|
||||
void GetWindowList(std::vector<std::string> &windows)
|
||||
std::vector<std::string> GetWindowList()
|
||||
{
|
||||
windows.resize(0);
|
||||
|
||||
std::vector<std::string> windows;
|
||||
@autoreleasepool {
|
||||
CFArrayRef cfApps = CGWindowListCopyWindowInfo(
|
||||
kCGWindowListOptionOnScreenOnly, kCGNullWindowID);
|
||||
@@ -49,21 +48,12 @@ void GetWindowList(std::vector<std::string> &windows)
|
||||
apps = nil;
|
||||
CFRelease(cfApps);
|
||||
}
|
||||
return windows;
|
||||
}
|
||||
|
||||
void GetWindowList(QStringList &windows)
|
||||
std::string GetCurrentWindowTitle()
|
||||
{
|
||||
windows.clear();
|
||||
std::vector<std::string> temp;
|
||||
GetWindowList(temp);
|
||||
for (auto &w : temp) {
|
||||
windows << QString::fromStdString(w);
|
||||
}
|
||||
}
|
||||
|
||||
void GetCurrentWindowTitle(std::string &title)
|
||||
{
|
||||
title.resize(0);
|
||||
std::string title;
|
||||
@autoreleasepool {
|
||||
CFArrayRef cfApps = CGWindowListCopyWindowInfo(
|
||||
kCGWindowListOptionOnScreenOnly, kCGNullWindowID);
|
||||
@@ -107,6 +97,7 @@ void GetCurrentWindowTitle(std::string &title)
|
||||
apps = nil;
|
||||
CFRelease(cfApps);
|
||||
}
|
||||
return title;
|
||||
}
|
||||
|
||||
bool isWindowOriginOnScreen(NSDictionary *app, NSScreen *screen,
|
||||
@@ -273,9 +264,9 @@ int SecondsSinceLastInput()
|
||||
return (int)time;
|
||||
}
|
||||
|
||||
void GetProcessList(QStringList &list)
|
||||
QStringList GetProcessList()
|
||||
{
|
||||
list.clear();
|
||||
QStringList list;
|
||||
@autoreleasepool {
|
||||
NSWorkspace *ws = [NSWorkspace sharedWorkspace];
|
||||
NSArray *array = [ws runningApplications];
|
||||
@@ -291,11 +282,11 @@ void GetProcessList(QStringList &list)
|
||||
}
|
||||
}
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
void GetForegroundProcessName(std::string &proc)
|
||||
std::string GetForegroundProcessName()
|
||||
{
|
||||
proc.resize(0);
|
||||
@autoreleasepool {
|
||||
NSWorkspace *ws = [NSWorkspace sharedWorkspace];
|
||||
NSArray *array = [ws runningApplications];
|
||||
@@ -308,23 +299,73 @@ void GetForegroundProcessName(std::string &proc)
|
||||
break;
|
||||
}
|
||||
const char *str = name.UTF8String;
|
||||
proc = std::string(str);
|
||||
if (str) {
|
||||
return str;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
void GetForegroundProcessName(QString &proc)
|
||||
std::string GetForegroundProcessPath()
|
||||
{
|
||||
std::string temp;
|
||||
GetForegroundProcessName(temp);
|
||||
proc = QString::fromStdString(temp);
|
||||
@autoreleasepool {
|
||||
NSWorkspace *ws = [NSWorkspace sharedWorkspace];
|
||||
for (NSRunningApplication *app in [ws runningApplications]) {
|
||||
if (!app.isActive) {
|
||||
continue;
|
||||
}
|
||||
NSURL *url = app.executableURL;
|
||||
if (!url) {
|
||||
break;
|
||||
}
|
||||
const char *str = url.path.UTF8String;
|
||||
if (str) {
|
||||
return str;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
QStringList GetProcessPathsFromName(const QString &name)
|
||||
{
|
||||
QStringList paths;
|
||||
@autoreleasepool {
|
||||
NSWorkspace *ws = [NSWorkspace sharedWorkspace];
|
||||
for (NSRunningApplication *app in [ws runningApplications]) {
|
||||
NSString *appName = app.localizedName;
|
||||
if (!appName) {
|
||||
continue;
|
||||
}
|
||||
const char *nameStr = appName.UTF8String;
|
||||
if (!nameStr ||
|
||||
name != QString::fromUtf8(nameStr)) {
|
||||
continue;
|
||||
}
|
||||
NSURL *url = app.executableURL;
|
||||
if (!url) {
|
||||
continue;
|
||||
}
|
||||
const char *pathStr = url.path.UTF8String;
|
||||
if (!pathStr) {
|
||||
continue;
|
||||
}
|
||||
QString path = QString::fromUtf8(pathStr);
|
||||
if (!paths.contains(path)) {
|
||||
paths.append(path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return paths;
|
||||
}
|
||||
|
||||
bool IsInFocus(const QString &executable)
|
||||
{
|
||||
std::string current;
|
||||
GetForegroundProcessName(current);
|
||||
const auto current = GetForegroundProcessName();
|
||||
|
||||
// True if executable switch equals current window
|
||||
bool equals = (executable.toStdString() == current);
|
||||
|
||||
@@ -11,15 +11,16 @@ namespace advss {
|
||||
|
||||
enum class HotkeyType;
|
||||
|
||||
EXPORT void GetWindowList(std::vector<std::string> &windows);
|
||||
EXPORT void GetWindowList(QStringList &windows);
|
||||
EXPORT void GetCurrentWindowTitle(std::string &title);
|
||||
EXPORT std::vector<std::string> GetWindowList();
|
||||
EXPORT std::string GetCurrentWindowTitle();
|
||||
EXPORT bool IsFullscreen(const std::string &title);
|
||||
EXPORT bool IsMaximized(const std::string &title);
|
||||
EXPORT std::optional<std::string> GetTextInWindow(const std::string &window);
|
||||
EXPORT int SecondsSinceLastInput();
|
||||
EXPORT void GetProcessList(QStringList &processes);
|
||||
EXPORT void GetForegroundProcessName(std::string &name);
|
||||
EXPORT QStringList GetProcessList();
|
||||
EXPORT std::string GetForegroundProcessName();
|
||||
EXPORT std::string GetForegroundProcessPath();
|
||||
EXPORT QStringList GetProcessPathsFromName(const QString &name);
|
||||
EXPORT bool IsInFocus(const QString &executable);
|
||||
void PlatformInit();
|
||||
void PlatformCleanup();
|
||||
|
||||
@@ -55,9 +55,7 @@ static void WriteFirstRun(bool value)
|
||||
|
||||
static QString DetectFocusedWindow()
|
||||
{
|
||||
std::string title;
|
||||
GetCurrentWindowTitle(title);
|
||||
return QString::fromStdString(title);
|
||||
return QString::fromStdString(GetCurrentWindowTitle());
|
||||
}
|
||||
|
||||
// ===========================================================================
|
||||
|
||||
@@ -173,10 +173,9 @@ void PopulateTransitionSelection(QComboBox *sel, bool addCurrent, bool addAny,
|
||||
void PopulateWindowSelection(QComboBox *sel, bool addSelect)
|
||||
{
|
||||
|
||||
std::vector<std::string> windows;
|
||||
GetWindowList(windows);
|
||||
const auto windows = GetWindowList();
|
||||
|
||||
for (std::string &window : windows) {
|
||||
for (const std::string &window : windows) {
|
||||
sel->addItem(window.c_str());
|
||||
}
|
||||
|
||||
@@ -257,8 +256,7 @@ void PopulateMediaSelection(QComboBox *sel, bool addSelect)
|
||||
|
||||
void PopulateProcessSelection(QComboBox *sel, bool addSelect)
|
||||
{
|
||||
QStringList processes;
|
||||
GetProcessList(processes);
|
||||
auto processes = GetProcessList();
|
||||
processes.sort();
|
||||
for (QString &process : processes) {
|
||||
sel->addItem(process);
|
||||
|
||||
@@ -66,4 +66,15 @@ void Lockable::WithLock(const std::function<void()> &func)
|
||||
func();
|
||||
}
|
||||
|
||||
SuspendLock::SuspendLock(Lockable &lockable)
|
||||
: _mtx(static_cast<std::mutex &>(lockable._mtx))
|
||||
{
|
||||
_mtx.unlock();
|
||||
}
|
||||
|
||||
SuspendLock::~SuspendLock()
|
||||
{
|
||||
_mtx.lock();
|
||||
}
|
||||
|
||||
} // namespace advss
|
||||
|
||||
@@ -51,6 +51,24 @@ public:
|
||||
|
||||
private:
|
||||
PerInstanceMutex _mtx;
|
||||
|
||||
friend class SuspendLock;
|
||||
};
|
||||
|
||||
// RAII guard that temporarily releases a Lockable's per-segment lock.
|
||||
// Use this inside PerformAction() / CheckCondition() to unblock the UI
|
||||
// during long-running operations while still running on the original segment.
|
||||
// The caller MUST be holding the lock (i.e. be inside WithLock) when
|
||||
// constructing this object; the lock is re-acquired on destruction.
|
||||
class EXPORT SuspendLock {
|
||||
public:
|
||||
SuspendLock(Lockable &lockable);
|
||||
~SuspendLock();
|
||||
SuspendLock(const SuspendLock &) = delete;
|
||||
SuspendLock &operator=(const SuspendLock &) = delete;
|
||||
|
||||
private:
|
||||
std::mutex &_mtx;
|
||||
};
|
||||
|
||||
} // namespace advss
|
||||
|
||||
@@ -5,11 +5,20 @@
|
||||
#include "macro-edit.hpp"
|
||||
#include "macro-segment.hpp"
|
||||
#include "plugin-state-helpers.hpp"
|
||||
#include "sync-helpers.hpp"
|
||||
#include "ui-helpers.hpp"
|
||||
#include "utility.hpp"
|
||||
#include "variable.hpp"
|
||||
|
||||
#include <QVariant>
|
||||
#include <QAbstractItemView>
|
||||
#include <QHBoxLayout>
|
||||
#include <QLabel>
|
||||
#include <QPropertyAnimation>
|
||||
#include <QPushButton>
|
||||
#include <QToolButton>
|
||||
#include <QVariant>
|
||||
#include <QVBoxLayout>
|
||||
|
||||
#include <atomic>
|
||||
|
||||
Q_DECLARE_METATYPE(advss::TempVariableRef);
|
||||
@@ -731,4 +740,293 @@ void NotifyUIAboutTempVarChange(MacroSegment *segment)
|
||||
segment);
|
||||
}
|
||||
|
||||
TempVarOutputMappingsWidget::TempVarOutputMappingsWidget(QWidget *parent)
|
||||
: QWidget(parent),
|
||||
_rowsLayout(new QVBoxLayout()),
|
||||
_addButton(new QPushButton(
|
||||
obs_module_text("AdvSceneSwitcher.tempVar.outputMappings.add"),
|
||||
this)),
|
||||
_animation(new QPropertyAnimation(this, "maximumHeight", this))
|
||||
{
|
||||
_rowsLayout->setContentsMargins(0, 0, 0, 0);
|
||||
_rowsLayout->setSpacing(2);
|
||||
auto rowsContainer = new QWidget(this);
|
||||
rowsContainer->setLayout(_rowsLayout);
|
||||
|
||||
auto label = new QLabel(
|
||||
obs_module_text("AdvSceneSwitcher.tempVar.outputMappings"),
|
||||
this);
|
||||
|
||||
auto mainLayout = new QVBoxLayout();
|
||||
mainLayout->addWidget(label);
|
||||
mainLayout->addWidget(rowsContainer);
|
||||
mainLayout->addWidget(_addButton);
|
||||
setLayout(mainLayout);
|
||||
|
||||
_animation->setDuration(300);
|
||||
_animation->setEasingCurve(QEasingCurve::InOutQuad);
|
||||
connect(_animation, &QPropertyAnimation::finished, this,
|
||||
&TempVarOutputMappingsWidget::AnimationFinished);
|
||||
|
||||
hide();
|
||||
|
||||
connect(_addButton, &QPushButton::clicked, this,
|
||||
&TempVarOutputMappingsWidget::Add);
|
||||
connect(TempVarSignalManager::Instance(),
|
||||
SIGNAL(SegmentTempVarsChanged(MacroSegment *)), this,
|
||||
SLOT(SegmentTempVarsChanged(MacroSegment *)));
|
||||
}
|
||||
|
||||
void TempVarOutputMappingsWidget::SetSegment(MacroSegment *segment)
|
||||
{
|
||||
_segment = segment;
|
||||
_panelExpanded = false;
|
||||
Rebuild();
|
||||
}
|
||||
|
||||
void TempVarOutputMappingsWidget::Add()
|
||||
{
|
||||
if (!_segment) {
|
||||
return;
|
||||
}
|
||||
auto mappings = _segment->GetVarMappings();
|
||||
mappings.push_back(VarMapping{});
|
||||
{
|
||||
auto lock = LockContext();
|
||||
_segment->SetVarMappings(mappings);
|
||||
}
|
||||
Rebuild();
|
||||
}
|
||||
|
||||
void TempVarOutputMappingsWidget::Remove(int rowIdx)
|
||||
{
|
||||
if (!_segment) {
|
||||
return;
|
||||
}
|
||||
auto mappings = _segment->GetVarMappings();
|
||||
if (rowIdx < 0 || rowIdx >= (int)mappings.size()) {
|
||||
return;
|
||||
}
|
||||
mappings.erase(mappings.begin() + rowIdx);
|
||||
{
|
||||
auto lock = LockContext();
|
||||
_segment->SetVarMappings(mappings);
|
||||
}
|
||||
Rebuild();
|
||||
}
|
||||
|
||||
void TempVarOutputMappingsWidget::SegmentTempVarsChanged(MacroSegment *segment)
|
||||
{
|
||||
if (segment != _segment) {
|
||||
return;
|
||||
}
|
||||
// Re-populate each temp var combobox in case temp vars were added/removed
|
||||
_loading = true;
|
||||
const auto &mappings = _segment->GetVarMappings();
|
||||
for (int i = 0; i < (int)_rows.size(); i++) {
|
||||
auto combo = _rows[i].tempVarCombo;
|
||||
const QSignalBlocker blocker(combo);
|
||||
combo->clear();
|
||||
PopulateTempVarCombo(combo);
|
||||
if (i < (int)mappings.size() &&
|
||||
mappings[i].tempVar.HasValidID()) {
|
||||
QVariant v;
|
||||
v.setValue(mappings[i].tempVar);
|
||||
combo->setCurrentIndex(combo->findData(v));
|
||||
}
|
||||
}
|
||||
_loading = false;
|
||||
UpdateVisibility();
|
||||
}
|
||||
|
||||
void TempVarOutputMappingsWidget::WriteBackMappings()
|
||||
{
|
||||
if (_loading || !_segment) {
|
||||
return;
|
||||
}
|
||||
std::vector<VarMapping> mappings;
|
||||
for (const auto &row : _rows) {
|
||||
VarMapping m;
|
||||
const int idx = row.tempVarCombo->currentIndex();
|
||||
if (idx >= 0) {
|
||||
m.tempVar = row.tempVarCombo->itemData(idx)
|
||||
.value<TempVariableRef>();
|
||||
}
|
||||
|
||||
auto item = row.varSelection->GetCurrentItem();
|
||||
if (!item) {
|
||||
continue;
|
||||
}
|
||||
|
||||
m.variable = GetWeakVariableByName(item->Name());
|
||||
mappings.push_back(std::move(m));
|
||||
}
|
||||
auto lock = LockContext();
|
||||
_segment->SetVarMappings(mappings);
|
||||
}
|
||||
|
||||
void TempVarOutputMappingsWidget::Rebuild()
|
||||
{
|
||||
_loading = true;
|
||||
|
||||
// Remove all existing row widgets from layout and delete them
|
||||
for (auto &row : _rows) {
|
||||
_rowsLayout->removeWidget(row.container);
|
||||
delete row.container;
|
||||
}
|
||||
_rows.clear();
|
||||
|
||||
if (!_segment) {
|
||||
_loading = false;
|
||||
UpdateVisibility();
|
||||
return;
|
||||
}
|
||||
|
||||
const auto &mappings = _segment->GetVarMappings();
|
||||
for (int i = 0; i < (int)mappings.size(); i++) {
|
||||
const auto &mapping = mappings[i];
|
||||
|
||||
auto container = new QWidget(this);
|
||||
auto rowLayout = new QHBoxLayout();
|
||||
rowLayout->setContentsMargins(0, 0, 0, 0);
|
||||
|
||||
auto tempVarCombo = new FilterComboBox(
|
||||
container,
|
||||
obs_module_text("AdvSceneSwitcher.tempVar.select"));
|
||||
tempVarCombo->setSizeAdjustPolicy(QComboBox::AdjustToContents);
|
||||
tempVarCombo->setMaximumWidth(350);
|
||||
tempVarCombo->setDuplicatesEnabled(true);
|
||||
PopulateTempVarCombo(tempVarCombo);
|
||||
if (mapping.tempVar.HasValidID()) {
|
||||
QVariant v;
|
||||
v.setValue(mapping.tempVar);
|
||||
tempVarCombo->setCurrentIndex(
|
||||
tempVarCombo->findData(v));
|
||||
}
|
||||
|
||||
auto arrowLabel = new QLabel(container);
|
||||
{
|
||||
QIcon icon;
|
||||
const auto path = (GetThemeTypeName() == "Light")
|
||||
? "theme:Light/right.svg"
|
||||
: "theme:Dark/right.svg";
|
||||
icon.addFile(QString::fromUtf8(path), QSize(),
|
||||
QIcon::Normal, QIcon::Off);
|
||||
arrowLabel->setPixmap(icon.pixmap(16, 16));
|
||||
}
|
||||
|
||||
auto varSel = new VariableSelection(container);
|
||||
varSel->SetVariable(mapping.variable);
|
||||
|
||||
auto removeBtn = new QToolButton(container);
|
||||
removeBtn->setProperty("themeID", QVariant(QString::fromUtf8(
|
||||
"removeIconSmall")));
|
||||
removeBtn->setProperty(
|
||||
"class", QVariant(QString::fromUtf8("icon-trash")));
|
||||
removeBtn->setToolTip(obs_module_text(
|
||||
"AdvSceneSwitcher.tempVar.outputMappings.remove"));
|
||||
|
||||
rowLayout->addWidget(tempVarCombo, 1);
|
||||
rowLayout->addWidget(arrowLabel);
|
||||
rowLayout->addWidget(varSel, 1);
|
||||
rowLayout->addWidget(removeBtn);
|
||||
container->setLayout(rowLayout);
|
||||
|
||||
_rowsLayout->addWidget(container);
|
||||
_rows.push_back({container, tempVarCombo, varSel});
|
||||
|
||||
const int capturedIdx = i;
|
||||
connect(removeBtn, &QPushButton::clicked, this,
|
||||
[this, capturedIdx]() { Remove(capturedIdx); });
|
||||
connect(tempVarCombo,
|
||||
QOverload<int>::of(&QComboBox::currentIndexChanged),
|
||||
this, &TempVarOutputMappingsWidget::WriteBackMappings);
|
||||
connect(varSel, &VariableSelection::SelectionChanged, this,
|
||||
[this](const QString &) { WriteBackMappings(); });
|
||||
}
|
||||
|
||||
_loading = false;
|
||||
UpdateVisibility();
|
||||
}
|
||||
|
||||
bool TempVarOutputMappingsWidget::IsExpandable() const
|
||||
{
|
||||
return _segment && !_segment->GetOwnTempVars().empty();
|
||||
}
|
||||
|
||||
void TempVarOutputMappingsWidget::SetPanelExpanded(bool expanded)
|
||||
{
|
||||
_panelExpanded = expanded;
|
||||
UpdateHeight();
|
||||
}
|
||||
|
||||
void TempVarOutputMappingsWidget::SetSectionCollapsed(bool collapsed)
|
||||
{
|
||||
_sectionCollapsed = collapsed;
|
||||
UpdateHeight();
|
||||
}
|
||||
|
||||
void TempVarOutputMappingsWidget::AnimateTo(int targetHeight)
|
||||
{
|
||||
if (_animation->state() == QAbstractAnimation::Running) {
|
||||
_animation->stop();
|
||||
}
|
||||
|
||||
if (targetHeight > 0 && !isVisible()) {
|
||||
setMinimumHeight(0);
|
||||
setMaximumHeight(0);
|
||||
show();
|
||||
}
|
||||
|
||||
const int currentMax = maximumHeight();
|
||||
const int currentHeight = (currentMax >= QWIDGETSIZE_MAX) ? height()
|
||||
: currentMax;
|
||||
|
||||
_animationTargetHeight = targetHeight;
|
||||
_animation->setStartValue(currentHeight);
|
||||
_animation->setEndValue(targetHeight);
|
||||
_animation->start();
|
||||
}
|
||||
|
||||
void TempVarOutputMappingsWidget::AnimationFinished()
|
||||
{
|
||||
if (_animationTargetHeight > 0) {
|
||||
setMaximumHeight(QWIDGETSIZE_MAX);
|
||||
} else {
|
||||
hide();
|
||||
setMaximumHeight(QWIDGETSIZE_MAX);
|
||||
}
|
||||
}
|
||||
|
||||
void TempVarOutputMappingsWidget::UpdateHeight()
|
||||
{
|
||||
const bool shouldShow = _panelExpanded && !_sectionCollapsed &&
|
||||
IsExpandable();
|
||||
if (shouldShow) {
|
||||
AnimateTo(sizeHint().height());
|
||||
} else {
|
||||
AnimateTo(0);
|
||||
}
|
||||
}
|
||||
|
||||
void TempVarOutputMappingsWidget::UpdateVisibility()
|
||||
{
|
||||
const bool expandable = IsExpandable();
|
||||
emit ExpandableChanged(expandable);
|
||||
UpdateHeight();
|
||||
}
|
||||
|
||||
void TempVarOutputMappingsWidget::PopulateTempVarCombo(
|
||||
FilterComboBox *combo) const
|
||||
{
|
||||
if (!_segment) {
|
||||
return;
|
||||
}
|
||||
for (const auto &var : _segment->GetOwnTempVars()) {
|
||||
QVariant v;
|
||||
v.setValue(var.GetRef());
|
||||
combo->addItem(QString::fromStdString(var.Name()), v);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace advss
|
||||
|
||||
@@ -5,11 +5,12 @@
|
||||
#include <obs-data.h>
|
||||
#include <mutex>
|
||||
#include <optional>
|
||||
#include <QEnterEvent>
|
||||
#include <QEvent>
|
||||
#include <QStringList>
|
||||
#include <string>
|
||||
|
||||
class QPropertyAnimation;
|
||||
class QPushButton;
|
||||
class QVBoxLayout;
|
||||
|
||||
namespace advss {
|
||||
|
||||
class Macro;
|
||||
@@ -17,6 +18,9 @@ class MacroEdit;
|
||||
class MacroSegment;
|
||||
class TempVariableRef;
|
||||
class TempVariableSelection;
|
||||
class TempVarOutputMappingsWidget;
|
||||
class VariableSelection;
|
||||
struct VarMapping;
|
||||
|
||||
// TempVariables are variables that are local to a given macro.
|
||||
// They can be created and used by macro segments.
|
||||
@@ -127,4 +131,48 @@ private:
|
||||
void NotifyUIAboutTempVarChange(MacroSegment *);
|
||||
EXPORT void IncrementTempVarInUseGeneration();
|
||||
|
||||
class ADVSS_EXPORT TempVarOutputMappingsWidget : public QWidget {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
TempVarOutputMappingsWidget(QWidget *parent);
|
||||
void SetSegment(MacroSegment *segment);
|
||||
void SetPanelExpanded(bool expanded);
|
||||
void SetSectionCollapsed(bool collapsed);
|
||||
bool IsExpandable() const;
|
||||
|
||||
signals:
|
||||
void ExpandableChanged(bool);
|
||||
|
||||
private slots:
|
||||
void Add();
|
||||
void Remove(int rowIdx);
|
||||
void SegmentTempVarsChanged(MacroSegment *segment);
|
||||
void WriteBackMappings();
|
||||
void AnimationFinished();
|
||||
|
||||
private:
|
||||
void Rebuild();
|
||||
void UpdateVisibility();
|
||||
void UpdateHeight();
|
||||
void AnimateTo(int targetHeight);
|
||||
void PopulateTempVarCombo(FilterComboBox *combo) const;
|
||||
|
||||
struct MappingRow {
|
||||
QWidget *container;
|
||||
FilterComboBox *tempVarCombo;
|
||||
VariableSelection *varSelection;
|
||||
};
|
||||
|
||||
MacroSegment *_segment = nullptr;
|
||||
QVBoxLayout *_rowsLayout;
|
||||
QPushButton *_addButton;
|
||||
std::vector<MappingRow> _rows;
|
||||
bool _loading = false;
|
||||
bool _panelExpanded = false;
|
||||
bool _sectionCollapsed = false;
|
||||
int _animationTargetHeight = 0;
|
||||
QPropertyAnimation *_animation;
|
||||
};
|
||||
|
||||
} // namespace advss
|
||||
|
||||
@@ -133,9 +133,9 @@ const std::vector<std::string> getOBSWindows()
|
||||
return lastDoneHelper->windows;
|
||||
}
|
||||
|
||||
void GetWindowList(std::vector<std::string> &windows)
|
||||
std::vector<std::string> GetWindowList()
|
||||
{
|
||||
windows.resize(0);
|
||||
std::vector<std::string> windows;
|
||||
EnumWindowsWithMetro(GetTitleCB, reinterpret_cast<LPARAM>(&windows));
|
||||
|
||||
// Also add OBS windows
|
||||
@@ -147,20 +147,10 @@ void GetWindowList(std::vector<std::string> &windows)
|
||||
|
||||
// Add entry for OBS Studio itself - see GetCurrentWindowTitle()
|
||||
windows.emplace_back("OBS");
|
||||
return windows;
|
||||
}
|
||||
|
||||
void GetWindowList(QStringList &windows)
|
||||
{
|
||||
windows.clear();
|
||||
|
||||
std::vector<std::string> w;
|
||||
GetWindowList(w);
|
||||
for (auto window : w) {
|
||||
windows << QString::fromStdString(window);
|
||||
}
|
||||
}
|
||||
|
||||
void GetCurrentWindowTitle(std::string &title)
|
||||
std::string GetCurrentWindowTitle()
|
||||
{
|
||||
HWND window = GetForegroundWindow();
|
||||
DWORD pid;
|
||||
@@ -178,15 +168,15 @@ void GetCurrentWindowTitle(std::string &title)
|
||||
//
|
||||
// So instead rely on Qt to get the title of the active window.
|
||||
if (GetCurrentProcessId() == pid) {
|
||||
auto window = QApplication::activeWindow();
|
||||
if (window) {
|
||||
title = window->windowTitle().toStdString();
|
||||
} else {
|
||||
title = "OBS";
|
||||
auto obsWindow = QApplication::activeWindow();
|
||||
if (obsWindow) {
|
||||
return obsWindow->windowTitle().toStdString();
|
||||
}
|
||||
return;
|
||||
return "OBS";
|
||||
}
|
||||
std::string title;
|
||||
GetWindowTitle(window, title);
|
||||
return title;
|
||||
}
|
||||
|
||||
static HWND getHWNDfromTitle(const std::string &title)
|
||||
@@ -350,22 +340,22 @@ bool IsFullscreen(const std::string &title)
|
||||
return false;
|
||||
}
|
||||
|
||||
void GetProcessList(QStringList &processes)
|
||||
QStringList GetProcessList()
|
||||
{
|
||||
|
||||
QStringList processes;
|
||||
HANDLE procSnapshot;
|
||||
PROCESSENTRY32 procEntry;
|
||||
|
||||
procSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
|
||||
if (procSnapshot == INVALID_HANDLE_VALUE) {
|
||||
return;
|
||||
return processes;
|
||||
}
|
||||
|
||||
procEntry.dwSize = sizeof(PROCESSENTRY32);
|
||||
|
||||
if (!Process32First(procSnapshot, &procEntry)) {
|
||||
CloseHandle(procSnapshot);
|
||||
return;
|
||||
return processes;
|
||||
}
|
||||
|
||||
do {
|
||||
@@ -383,9 +373,10 @@ void GetProcessList(QStringList &processes)
|
||||
} while (Process32Next(procSnapshot, &procEntry));
|
||||
|
||||
CloseHandle(procSnapshot);
|
||||
return processes;
|
||||
}
|
||||
|
||||
static void GetForegroundProcessName(QString &proc)
|
||||
static QString getForegroundProcessNameStr()
|
||||
{
|
||||
// only checks if the current foreground window is from the same executable,
|
||||
// may return true for any window from a program
|
||||
@@ -396,31 +387,90 @@ static void GetForegroundProcessName(QString &proc)
|
||||
HANDLE process = OpenProcess(
|
||||
PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, processId);
|
||||
if (process == NULL) {
|
||||
return;
|
||||
return {};
|
||||
}
|
||||
|
||||
WCHAR executablePath[600];
|
||||
GetModuleFileNameEx(process, 0, executablePath, 600);
|
||||
CloseHandle(process);
|
||||
|
||||
proc = QString::fromWCharArray(executablePath)
|
||||
.split(QRegularExpression("(/|\\\\)"))
|
||||
.back();
|
||||
return QString::fromWCharArray(executablePath)
|
||||
.split(QRegularExpression("(/|\\\\)"))
|
||||
.back();
|
||||
}
|
||||
|
||||
void GetForegroundProcessName(std::string &proc)
|
||||
std::string GetForegroundProcessName()
|
||||
{
|
||||
QString temp;
|
||||
GetForegroundProcessName(temp);
|
||||
proc = temp.toStdString();
|
||||
return getForegroundProcessNameStr().toStdString();
|
||||
}
|
||||
|
||||
std::string GetForegroundProcessPath()
|
||||
{
|
||||
HWND foregroundWindow = GetForegroundWindow();
|
||||
DWORD processId = 0;
|
||||
GetWindowThreadProcessId(foregroundWindow, &processId);
|
||||
|
||||
HANDLE process = OpenProcess(
|
||||
PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, processId);
|
||||
if (process == NULL) {
|
||||
return {};
|
||||
}
|
||||
|
||||
WCHAR executablePath[600];
|
||||
GetModuleFileNameEx(process, 0, executablePath, 600);
|
||||
CloseHandle(process);
|
||||
|
||||
return QString::fromWCharArray(executablePath).toStdString();
|
||||
}
|
||||
|
||||
QStringList GetProcessPathsFromName(const QString &name)
|
||||
{
|
||||
QStringList paths;
|
||||
HANDLE procSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
|
||||
if (procSnapshot == INVALID_HANDLE_VALUE) {
|
||||
return paths;
|
||||
}
|
||||
|
||||
PROCESSENTRY32 procEntry;
|
||||
procEntry.dwSize = sizeof(PROCESSENTRY32);
|
||||
|
||||
if (!Process32First(procSnapshot, &procEntry)) {
|
||||
CloseHandle(procSnapshot);
|
||||
return paths;
|
||||
}
|
||||
|
||||
do {
|
||||
QString exeName = QString::fromWCharArray(procEntry.szExeFile);
|
||||
if (exeName != name) {
|
||||
continue;
|
||||
}
|
||||
|
||||
HANDLE process =
|
||||
OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ,
|
||||
FALSE, procEntry.th32ProcessID);
|
||||
if (process == NULL) {
|
||||
continue;
|
||||
}
|
||||
|
||||
WCHAR executablePath[600];
|
||||
if (GetModuleFileNameEx(process, 0, executablePath, 600)) {
|
||||
QString path = QString::fromWCharArray(executablePath);
|
||||
if (!paths.contains(path)) {
|
||||
paths.append(path);
|
||||
}
|
||||
}
|
||||
CloseHandle(process);
|
||||
} while (Process32Next(procSnapshot, &procEntry));
|
||||
|
||||
CloseHandle(procSnapshot);
|
||||
return paths;
|
||||
}
|
||||
|
||||
bool IsInFocus(const QString &executable)
|
||||
{
|
||||
// only checks if the current foreground window is from the same executable,
|
||||
// may return true for any window from a program
|
||||
QString foregroundProc;
|
||||
GetForegroundProcessName(foregroundProc);
|
||||
const auto foregroundProc = getForegroundProcessNameStr();
|
||||
|
||||
// True if executable switch equals current window
|
||||
bool equals = (executable == foregroundProc);
|
||||
|
||||
@@ -11,10 +11,11 @@ const std::string MacroActionFilter::id = "filter";
|
||||
|
||||
std::vector<TempVariableRef> MacroActionFilter::GetTempVarRefs() const
|
||||
{
|
||||
if (!_tempVar.HasValidID()) {
|
||||
return {};
|
||||
auto refs = MacroSegment::GetTempVarRefs();
|
||||
if (_tempVar.HasValidID()) {
|
||||
refs.push_back(_tempVar);
|
||||
}
|
||||
return {_tempVar};
|
||||
return refs;
|
||||
}
|
||||
|
||||
bool MacroActionFilter::_registered = MacroActionFactory::Register(
|
||||
|
||||
@@ -95,7 +95,7 @@ static void waitHelper(std::unique_lock<std::mutex> *lock, Macro *macro,
|
||||
}
|
||||
}
|
||||
|
||||
void MacroActionMedia::PerformActionHelper(obs_source_t *source) const
|
||||
void MacroActionMedia::PerformActionHelper(obs_source_t *source)
|
||||
{
|
||||
obs_media_state state = obs_source_media_get_state(source);
|
||||
|
||||
@@ -130,6 +130,7 @@ void MacroActionMedia::PerformActionHelper(obs_source_t *source) const
|
||||
SeekToPercentage(source);
|
||||
break;
|
||||
case Action::WAIT_FOR_PLAYBACK_STOP: {
|
||||
SuspendLock suspendLock(*this);
|
||||
std::unique_lock<std::mutex> lock(*GetMutex());
|
||||
waitHelper(&lock, GetMacro(), source);
|
||||
break;
|
||||
|
||||
@@ -45,7 +45,7 @@ public:
|
||||
SceneSelection _scene;
|
||||
|
||||
private:
|
||||
void PerformActionHelper(obs_source_t *) const;
|
||||
void PerformActionHelper(obs_source_t *);
|
||||
void SeekToPercentage(obs_source_t *source) const;
|
||||
|
||||
static bool _registered;
|
||||
|
||||
@@ -159,7 +159,10 @@ bool MacroActionPlayAudio::PerformAction()
|
||||
|
||||
if (_waitForCompletion) {
|
||||
SetMacroAbortWait(false);
|
||||
waitForPlaybackToEnd(GetMacro(), source, maxMs);
|
||||
{
|
||||
SuspendLock suspendLock(*this);
|
||||
waitForPlaybackToEnd(GetMacro(), source, maxMs);
|
||||
}
|
||||
deactivatePlayback(source, wantsOutput);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -15,9 +15,20 @@ bool MacroActionRun::_registered = MacroActionFactory::Register(
|
||||
bool MacroActionRun::PerformAction()
|
||||
{
|
||||
if (_wait) {
|
||||
_procConfig.StartProcessAndWait(_timeout.Milliseconds());
|
||||
SetTempVarValues();
|
||||
|
||||
// Snapshot config before releasing the lock for the blocking wait
|
||||
auto procConfig = _procConfig;
|
||||
const auto timeout = _timeout.Milliseconds();
|
||||
{
|
||||
SuspendLock suspendLock(*this);
|
||||
procConfig.StartProcessAndWait(timeout);
|
||||
}
|
||||
SetTempVarValue("process.id", procConfig.GetProcessId());
|
||||
SetTempVarValue("process.exitCode",
|
||||
procConfig.GetProcessExitCode());
|
||||
SetTempVarValue("process.stream.output",
|
||||
procConfig.GetProcessOutputStream());
|
||||
SetTempVarValue("process.stream.error",
|
||||
procConfig.GetProcessErrorStream());
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -259,6 +259,12 @@ void MacroActionSourceInteractionEdit::AcceptRecorded(
|
||||
_stepList->SetSteps(_entryData->_steps);
|
||||
}
|
||||
|
||||
void MacroActionSourceInteractionEdit::showEvent(QShowEvent *event)
|
||||
{
|
||||
const QSignalBlocker b(this);
|
||||
UpdateEntryData();
|
||||
}
|
||||
|
||||
void MacroActionSourceInteractionEdit::SetCurrentStepEditor(int row)
|
||||
{
|
||||
if (_stepEditor) {
|
||||
|
||||
@@ -62,6 +62,9 @@ private slots:
|
||||
signals:
|
||||
void HeaderInfoChanged(const QString &);
|
||||
|
||||
protected:
|
||||
void showEvent(QShowEvent *event) override;
|
||||
|
||||
private:
|
||||
void SetCurrentStepEditor(int row);
|
||||
|
||||
|
||||
@@ -15,10 +15,11 @@ const std::string MacroActionSource::id = "source";
|
||||
|
||||
std::vector<TempVariableRef> MacroActionSource::GetTempVarRefs() const
|
||||
{
|
||||
if (!_tempVar.HasValidID()) {
|
||||
return {};
|
||||
auto refs = MacroSegment::GetTempVarRefs();
|
||||
if (_tempVar.HasValidID()) {
|
||||
refs.push_back(_tempVar);
|
||||
}
|
||||
return {_tempVar};
|
||||
return refs;
|
||||
}
|
||||
|
||||
bool MacroActionSource::_registered = MacroActionFactory::Register(
|
||||
@@ -793,6 +794,8 @@ void MacroActionSourceEdit::SetWidgetVisibility()
|
||||
action == MacroActionSource::Action::CLOSE_FILTER_DIALOG ||
|
||||
action == MacroActionSource::Action::CLOSE_PROPERTIES_DIALOG);
|
||||
|
||||
emit ShowVariableMappings(isGetSetting || isGetSettings);
|
||||
|
||||
adjustSize();
|
||||
updateGeometry();
|
||||
}
|
||||
|
||||
@@ -113,6 +113,7 @@ private slots:
|
||||
|
||||
signals:
|
||||
void HeaderInfoChanged(const QString &);
|
||||
void ShowVariableMappings(bool show);
|
||||
|
||||
private:
|
||||
void SetWidgetVisibility();
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include "ui-helpers.hpp"
|
||||
|
||||
#include <obs-frontend-api.h>
|
||||
#include <obs.hpp>
|
||||
#include <util/config-file.h>
|
||||
|
||||
namespace advss {
|
||||
@@ -183,6 +184,8 @@ MacroActionStreamEdit::MacroActionStreamEdit(
|
||||
_keyFrameInterval(new VariableSpinBox()),
|
||||
_stringValue(new VariableLineEdit(this)),
|
||||
_showPassword(new QPushButton()),
|
||||
_getCurrentValue(new QPushButton(obs_module_text(
|
||||
"AdvSceneSwitcher.action.streaming.getCurrentValue"))),
|
||||
_layout(new QHBoxLayout())
|
||||
{
|
||||
_keyFrameInterval->setMinimum(0);
|
||||
@@ -208,13 +211,17 @@ MacroActionStreamEdit::MacroActionStreamEdit(
|
||||
SLOT(ShowPassword()));
|
||||
QWidget::connect(_showPassword, SIGNAL(released()), this,
|
||||
SLOT(HidePassword()));
|
||||
QWidget::connect(_getCurrentValue, SIGNAL(clicked()), this,
|
||||
SLOT(GetCurrentValueClicked()));
|
||||
|
||||
PlaceWidgets(obs_module_text("AdvSceneSwitcher.action.streaming.entry"),
|
||||
_layout,
|
||||
{{"{{actions}}", _actions},
|
||||
{"{{keyFrameInterval}}", _keyFrameInterval},
|
||||
{"{{stringValue}}", _stringValue},
|
||||
{"{{showPassword}}", _showPassword}});
|
||||
PlaceWidgets(
|
||||
obs_module_text("AdvSceneSwitcher.action.streaming.layout"),
|
||||
_layout,
|
||||
{{"{{actions}}", _actions},
|
||||
{"{{keyFrameInterval}}", _keyFrameInterval},
|
||||
{"{{stringValue}}", _stringValue},
|
||||
{"{{showPassword}}", _showPassword},
|
||||
{"{{getCurrentValue}}", _getCurrentValue}});
|
||||
setLayout(_layout);
|
||||
|
||||
_entryData = entryData;
|
||||
@@ -291,6 +298,62 @@ void MacroActionStreamEdit::SetWidgetVisibility()
|
||||
_stringValue->setEchoMode(QLineEdit::Normal);
|
||||
_showPassword->hide();
|
||||
}
|
||||
_getCurrentValue->setVisible(
|
||||
action == MacroActionStream::Action::KEYFRAME_INTERVAL ||
|
||||
action == MacroActionStream::Action::SERVER ||
|
||||
action == MacroActionStream::Action::STREAM_KEY ||
|
||||
action == MacroActionStream::Action::USERNAME ||
|
||||
action == MacroActionStream::Action::PASSWORD);
|
||||
}
|
||||
|
||||
void MacroActionStreamEdit::GetCurrentValueClicked()
|
||||
{
|
||||
if (!_entryData) {
|
||||
return;
|
||||
}
|
||||
switch (_entryData->_action) {
|
||||
case MacroActionStream::Action::KEYFRAME_INTERVAL: {
|
||||
const auto configPath =
|
||||
GetPathInProfileDir("streamEncoder.json");
|
||||
OBSDataAutoRelease settings =
|
||||
obs_data_create_from_json_file_safe(configPath.c_str(),
|
||||
"bak");
|
||||
if (!settings) {
|
||||
break;
|
||||
}
|
||||
_keyFrameInterval->SetFixedValue(
|
||||
(int)obs_data_get_int(settings, "keyint_sec"));
|
||||
break;
|
||||
}
|
||||
case MacroActionStream::Action::SERVER:
|
||||
case MacroActionStream::Action::STREAM_KEY:
|
||||
case MacroActionStream::Action::USERNAME:
|
||||
case MacroActionStream::Action::PASSWORD: {
|
||||
static const std::map<MacroActionStream::Action, const char *>
|
||||
settingsKeys = {
|
||||
{MacroActionStream::Action::SERVER, "server"},
|
||||
{MacroActionStream::Action::STREAM_KEY, "key"},
|
||||
{MacroActionStream::Action::USERNAME,
|
||||
"username"},
|
||||
{MacroActionStream::Action::PASSWORD,
|
||||
"password"},
|
||||
};
|
||||
auto service = obs_frontend_get_streaming_service();
|
||||
OBSDataAutoRelease settings = obs_service_get_settings(service);
|
||||
if (!settings) {
|
||||
obs_service_release(service);
|
||||
break;
|
||||
}
|
||||
const char *val = obs_data_get_string(
|
||||
settings, settingsKeys.at(_entryData->_action));
|
||||
if (val) {
|
||||
_stringValue->setText(QString(val));
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void MacroActionStreamEdit::ActionChanged(int value)
|
||||
|
||||
@@ -67,12 +67,14 @@ private slots:
|
||||
void StringValueChanged();
|
||||
void ShowPassword();
|
||||
void HidePassword();
|
||||
void GetCurrentValueClicked();
|
||||
|
||||
protected:
|
||||
QComboBox *_actions;
|
||||
VariableSpinBox *_keyFrameInterval;
|
||||
VariableLineEdit *_stringValue;
|
||||
QPushButton *_showPassword;
|
||||
QPushButton *_getCurrentValue;
|
||||
std::shared_ptr<MacroActionStream> _entryData;
|
||||
|
||||
private:
|
||||
|
||||
@@ -57,8 +57,11 @@ bool MacroActionWait::PerformAction()
|
||||
std::chrono::milliseconds((int)(sleepDuration * 1000));
|
||||
|
||||
SetMacroAbortWait(false);
|
||||
std::unique_lock<std::mutex> lock(*GetMutex());
|
||||
waitHelper(&lock, GetMacro(), time);
|
||||
{
|
||||
SuspendLock suspendLock(*this);
|
||||
std::unique_lock<std::mutex> lock(*GetMutex());
|
||||
waitHelper(&lock, GetMacro(), time);
|
||||
}
|
||||
|
||||
return !MacroWaitShouldAbort();
|
||||
}
|
||||
|
||||
@@ -42,8 +42,7 @@ void CloseWindow(const std::string &) {}
|
||||
|
||||
std::optional<std::string> MacroActionWindow::GetMatchingWindow() const
|
||||
{
|
||||
std::vector<std::string> windowList;
|
||||
GetWindowList(windowList);
|
||||
const auto windowList = GetWindowList();
|
||||
|
||||
if (!_regex.Enabled()) {
|
||||
if (std::find(windowList.begin(), windowList.end(),
|
||||
|
||||
@@ -16,54 +16,100 @@ bool MacroConditionProcess::_registered = MacroConditionFactory::Register(
|
||||
|
||||
bool MacroConditionProcess::CheckCondition()
|
||||
{
|
||||
QStringList runningProcesses;
|
||||
QString proc = QString::fromStdString(_process);
|
||||
GetProcessList(runningProcesses);
|
||||
std::string foregroundProcessName;
|
||||
GetForegroundProcessName(foregroundProcessName);
|
||||
|
||||
const auto foregroundProcessName = GetForegroundProcessName();
|
||||
SetVariableValue(foregroundProcessName);
|
||||
|
||||
if (!_regex.Enabled()) {
|
||||
if (runningProcesses.contains(proc) &&
|
||||
(!_checkFocus || IsInFocus(proc))) {
|
||||
SetTempVarValue("name", proc.toStdString());
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
const QString proc = QString::fromStdString(_process);
|
||||
|
||||
int matchIndex = -1;
|
||||
bool foundMatch = false;
|
||||
for (const auto &process : runningProcesses) {
|
||||
matchIndex++;
|
||||
if (_regex.Matches(process, proc)) {
|
||||
foundMatch = true;
|
||||
break;
|
||||
if (_checkFocus) {
|
||||
// Check name and path against the same foreground process
|
||||
// instance to avoid false positives when multiple processes
|
||||
// share the same name
|
||||
const auto foregroundPath = GetForegroundProcessPath();
|
||||
const QString foregroundName =
|
||||
QString::fromStdString(foregroundProcessName);
|
||||
|
||||
SetTempVarValue("name", foregroundProcessName);
|
||||
SetTempVarValue("path", foregroundPath);
|
||||
|
||||
bool nameMatches =
|
||||
_regex.Enabled() ? _regex.Matches(foregroundName, proc)
|
||||
: (foregroundName == proc);
|
||||
if (!nameMatches) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (!foundMatch) {
|
||||
return false;
|
||||
}
|
||||
if (!_checkFocus) {
|
||||
SetTempVarValue("name",
|
||||
runningProcesses.at(matchIndex).toStdString());
|
||||
|
||||
if (_checkPath) {
|
||||
const QString pathPattern =
|
||||
QString::fromStdString(_processPath);
|
||||
const QString qForegroundPath =
|
||||
QString::fromStdString(foregroundPath);
|
||||
bool pathMatches =
|
||||
_pathRegex.Enabled()
|
||||
? _pathRegex.Matches(qForegroundPath,
|
||||
pathPattern)
|
||||
: qForegroundPath == pathPattern;
|
||||
if (!pathMatches) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
if (!IsInFocus(proc)) {
|
||||
return false;
|
||||
|
||||
const auto runningProcesses = GetProcessList();
|
||||
|
||||
for (const auto &process : runningProcesses) {
|
||||
bool nameMatches = _regex.Enabled()
|
||||
? _regex.Matches(process, proc)
|
||||
: (process == proc);
|
||||
if (!nameMatches) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!_checkPath) {
|
||||
SetTempVarValue("name", process.toStdString());
|
||||
return true;
|
||||
}
|
||||
|
||||
const auto paths = GetProcessPathsFromName(process);
|
||||
|
||||
const QString pathPattern =
|
||||
QString::fromStdString(_processPath);
|
||||
bool foundMatchingPath = false;
|
||||
for (const auto &path : paths) {
|
||||
bool pathMatches =
|
||||
_pathRegex.Enabled()
|
||||
? _pathRegex.Matches(path, pathPattern)
|
||||
: path == pathPattern;
|
||||
if (pathMatches) {
|
||||
SetTempVarValue("path", path.toStdString());
|
||||
foundMatchingPath = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!foundMatchingPath) {
|
||||
continue;
|
||||
}
|
||||
|
||||
SetTempVarValue("name", process.toStdString());
|
||||
return true;
|
||||
}
|
||||
SetTempVarValue("name", foregroundProcessName);
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool MacroConditionProcess::Save(obs_data_t *obj) const
|
||||
{
|
||||
MacroCondition::Save(obj);
|
||||
_process.Save(obj, "process");
|
||||
obs_data_set_bool(obj, "focus", _checkFocus);
|
||||
_regex.Save(obj);
|
||||
obs_data_set_int(obj, "version", 1);
|
||||
obs_data_set_bool(obj, "focus", _checkFocus);
|
||||
obs_data_set_bool(obj, "checkPath", _checkPath);
|
||||
_processPath.Save(obj, "processPath");
|
||||
_pathRegex.Save(obj, "pathRegex");
|
||||
obs_data_set_int(obj, "version", 2);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -78,6 +124,9 @@ bool MacroConditionProcess::Load(obs_data_t *obj)
|
||||
} else {
|
||||
_regex.Load(obj);
|
||||
}
|
||||
_checkPath = obs_data_get_bool(obj, "checkPath");
|
||||
_processPath.Load(obj, "processPath");
|
||||
_pathRegex.Load(obj, "pathRegex");
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -91,6 +140,8 @@ void MacroConditionProcess::SetupTempVars()
|
||||
MacroCondition::SetupTempVars();
|
||||
AddTempvar("name",
|
||||
obs_module_text("AdvSceneSwitcher.tempVar.process.name"));
|
||||
AddTempvar("path",
|
||||
obs_module_text("AdvSceneSwitcher.tempVar.process.path"));
|
||||
}
|
||||
|
||||
MacroConditionProcessEdit::MacroConditionProcessEdit(
|
||||
@@ -100,13 +151,20 @@ MacroConditionProcessEdit::MacroConditionProcessEdit(
|
||||
_regex(new RegexConfigWidget(this)),
|
||||
_focused(new QCheckBox()),
|
||||
_focusProcess(new QLabel()),
|
||||
_focusLayout(new QHBoxLayout())
|
||||
_focusLayout(new QHBoxLayout()),
|
||||
_checkPath(new QCheckBox()),
|
||||
_processPath(new VariableLineEdit(this)),
|
||||
_pathRegex(new RegexConfigWidget(this)),
|
||||
_pathLayout(new QHBoxLayout())
|
||||
{
|
||||
_processSelection->setEditable(true);
|
||||
_processSelection->setMaxVisibleItems(20);
|
||||
_processSelection->setToolTip(
|
||||
obs_module_text("AdvSceneSwitcher.tooltip.availableVariables"));
|
||||
|
||||
_processPath->setToolTip(
|
||||
obs_module_text("AdvSceneSwitcher.tooltip.availableVariables"));
|
||||
|
||||
QWidget::connect(_processSelection,
|
||||
SIGNAL(currentTextChanged(const QString &)), this,
|
||||
SLOT(ProcessChanged(const QString &)));
|
||||
@@ -115,6 +173,13 @@ MacroConditionProcessEdit::MacroConditionProcessEdit(
|
||||
SLOT(RegexChanged(const RegexConfig &)));
|
||||
QWidget::connect(_focused, SIGNAL(stateChanged(int)), this,
|
||||
SLOT(FocusChanged(int)));
|
||||
QWidget::connect(_checkPath, SIGNAL(stateChanged(int)), this,
|
||||
SLOT(CheckPathChanged(int)));
|
||||
QWidget::connect(_processPath, SIGNAL(textChanged(const QString &)),
|
||||
this, SLOT(ProcessPathChanged(const QString &)));
|
||||
QWidget::connect(_pathRegex,
|
||||
SIGNAL(RegexConfigChanged(const RegexConfig &)), this,
|
||||
SLOT(PathRegexChanged(const RegexConfig &)));
|
||||
QWidget::connect(&_timer, SIGNAL(timeout()), this,
|
||||
SLOT(UpdateFocusProcess()));
|
||||
|
||||
@@ -125,18 +190,25 @@ MacroConditionProcessEdit::MacroConditionProcessEdit(
|
||||
{"{{regex}}", _regex},
|
||||
{"{{focused}}", _focused},
|
||||
{"{{focusProcess}}", _focusProcess},
|
||||
{"{{checkPath}}", _checkPath},
|
||||
{"{{path}}", _processPath},
|
||||
{"{{pathRegex}}", _pathRegex},
|
||||
};
|
||||
|
||||
auto entryLayout = new QHBoxLayout;
|
||||
PlaceWidgets(
|
||||
obs_module_text("AdvSceneSwitcher.condition.process.entry"),
|
||||
obs_module_text("AdvSceneSwitcher.condition.process.layout"),
|
||||
entryLayout, widgetPlaceholders);
|
||||
PlaceWidgets(obs_module_text(
|
||||
"AdvSceneSwitcher.condition.process.entry.focus"),
|
||||
"AdvSceneSwitcher.condition.process.layout.focus"),
|
||||
_focusLayout, widgetPlaceholders);
|
||||
PlaceWidgets(obs_module_text(
|
||||
"AdvSceneSwitcher.condition.process.layout.path"),
|
||||
_pathLayout, widgetPlaceholders);
|
||||
auto mainLayout = new QVBoxLayout;
|
||||
mainLayout->addLayout(entryLayout);
|
||||
mainLayout->addLayout(_focusLayout);
|
||||
mainLayout->addLayout(_pathLayout);
|
||||
setLayout(mainLayout);
|
||||
|
||||
_entryData = entryData;
|
||||
@@ -178,11 +250,31 @@ void MacroConditionProcessEdit::FocusChanged(int state)
|
||||
SetWidgetVisibility();
|
||||
}
|
||||
|
||||
void MacroConditionProcessEdit::CheckPathChanged(int state)
|
||||
{
|
||||
GUARD_LOADING_AND_LOCK();
|
||||
_entryData->_checkPath = state;
|
||||
SetWidgetVisibility();
|
||||
}
|
||||
|
||||
void MacroConditionProcessEdit::ProcessPathChanged(const QString &text)
|
||||
{
|
||||
GUARD_LOADING_AND_LOCK();
|
||||
_entryData->_processPath = text.toStdString();
|
||||
}
|
||||
|
||||
void MacroConditionProcessEdit::PathRegexChanged(const RegexConfig &conf)
|
||||
{
|
||||
GUARD_LOADING_AND_LOCK();
|
||||
_entryData->_pathRegex = conf;
|
||||
adjustSize();
|
||||
updateGeometry();
|
||||
}
|
||||
|
||||
void MacroConditionProcessEdit::UpdateFocusProcess()
|
||||
{
|
||||
std::string name;
|
||||
GetForegroundProcessName(name);
|
||||
_focusProcess->setText(QString::fromStdString(name));
|
||||
_focusProcess->setText(
|
||||
QString::fromStdString(GetForegroundProcessName()));
|
||||
}
|
||||
|
||||
void MacroConditionProcessEdit::SetWidgetVisibility()
|
||||
@@ -191,6 +283,13 @@ void MacroConditionProcessEdit::SetWidgetVisibility()
|
||||
return;
|
||||
}
|
||||
SetLayoutVisible(_focusLayout, _entryData->_checkFocus);
|
||||
_processPath->setVisible(_entryData->_checkPath);
|
||||
_pathRegex->setVisible(_entryData->_checkPath);
|
||||
if (_entryData->_checkPath) {
|
||||
RemoveStretchIfPresent(_pathLayout);
|
||||
} else {
|
||||
AddStretchIfNecessary(_pathLayout);
|
||||
}
|
||||
adjustSize();
|
||||
updateGeometry();
|
||||
}
|
||||
@@ -205,6 +304,9 @@ void MacroConditionProcessEdit::UpdateEntryData()
|
||||
_entryData->_process.UnresolvedValue().c_str());
|
||||
_regex->SetRegexConfig(_entryData->_regex);
|
||||
_focused->setChecked(_entryData->_checkFocus);
|
||||
_checkPath->setChecked(_entryData->_checkPath);
|
||||
_processPath->setText(_entryData->_processPath);
|
||||
_pathRegex->SetRegexConfig(_entryData->_pathRegex);
|
||||
SetWidgetVisibility();
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#pragma once
|
||||
#include "macro-condition-edit.hpp"
|
||||
#include "regex-config.hpp"
|
||||
#include "variable-string.hpp"
|
||||
#include "variable-line-edit.hpp"
|
||||
|
||||
#include <QCheckBox>
|
||||
|
||||
@@ -21,8 +21,11 @@ public:
|
||||
}
|
||||
|
||||
StringVariable _process;
|
||||
bool _checkFocus = true;
|
||||
RegexConfig _regex = RegexConfig::PartialMatchRegexConfig();
|
||||
bool _checkFocus = true;
|
||||
bool _checkPath = false;
|
||||
StringVariable _processPath;
|
||||
RegexConfig _pathRegex = RegexConfig::PartialMatchRegexConfig();
|
||||
|
||||
private:
|
||||
void SetupTempVars();
|
||||
@@ -54,6 +57,9 @@ private slots:
|
||||
void ProcessChanged(const QString &text);
|
||||
void RegexChanged(const RegexConfig &);
|
||||
void FocusChanged(int state);
|
||||
void CheckPathChanged(int state);
|
||||
void ProcessPathChanged(const QString &text);
|
||||
void PathRegexChanged(const RegexConfig &);
|
||||
void UpdateFocusProcess();
|
||||
signals:
|
||||
void HeaderInfoChanged(const QString &);
|
||||
@@ -66,6 +72,10 @@ private:
|
||||
QCheckBox *_focused;
|
||||
QLabel *_focusProcess;
|
||||
QHBoxLayout *_focusLayout;
|
||||
QCheckBox *_checkPath;
|
||||
VariableLineEdit *_processPath;
|
||||
RegexConfigWidget *_pathRegex;
|
||||
QHBoxLayout *_pathLayout;
|
||||
QTimer _timer;
|
||||
std::shared_ptr<MacroConditionProcess> _entryData;
|
||||
bool _loading = true;
|
||||
|
||||
@@ -135,8 +135,7 @@ static bool foregroundWindowChanged()
|
||||
|
||||
bool MacroConditionWindow::CheckCondition()
|
||||
{
|
||||
std::vector<std::string> windowList;
|
||||
GetWindowList(windowList);
|
||||
const auto windowList = GetWindowList();
|
||||
bool match = false;
|
||||
if (_windowRegex.Enabled()) {
|
||||
match = WindowRegexMatches(windowList);
|
||||
|
||||
@@ -112,48 +112,56 @@ static URLInfo getURLInfo(const std::string &input, bool keepParams)
|
||||
|
||||
bool MacroActionHttp::PerformAction()
|
||||
{
|
||||
// Capture all config while holding the segment lock
|
||||
const auto [host, path] = getURLInfo(_url, !_setParams);
|
||||
|
||||
httplib::Client cli(host);
|
||||
setTimeout(cli, _timeout);
|
||||
const auto params = _setParams ? getParams(_params) : httplib::Params();
|
||||
const auto headers = _setHeaders ? getHeaders(_headers)
|
||||
: httplib::Headers();
|
||||
const auto method = _method;
|
||||
const std::string body = _body;
|
||||
const std::string contentType = _contentType;
|
||||
|
||||
// Release the segment lock for the blocking network call
|
||||
httplib::Result response;
|
||||
switch (_method) {
|
||||
case MacroActionHttp::Method::GET:
|
||||
response = cli.Get(path, params, headers);
|
||||
break;
|
||||
case MacroActionHttp::Method::POST: {
|
||||
const auto pathWithParam =
|
||||
httplib::append_query_params(path, params);
|
||||
response =
|
||||
cli.Post(pathWithParam, headers, _body, _contentType);
|
||||
break;
|
||||
}
|
||||
case MacroActionHttp::Method::PUT: {
|
||||
const auto pathWithParam =
|
||||
httplib::append_query_params(path, params);
|
||||
response = cli.Put(pathWithParam, headers, _body, _contentType);
|
||||
break;
|
||||
}
|
||||
case MacroActionHttp::Method::PATCH: {
|
||||
const auto pathWithParam =
|
||||
httplib::append_query_params(path, params);
|
||||
response =
|
||||
cli.Patch(pathWithParam, headers, _body, _contentType);
|
||||
break;
|
||||
}
|
||||
case MacroActionHttp::Method::DELETE: {
|
||||
const auto pathWithParam =
|
||||
httplib::append_query_params(path, params);
|
||||
response =
|
||||
cli.Delete(pathWithParam, headers, _body, _contentType);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
{
|
||||
SuspendLock suspendLock(*this);
|
||||
switch (method) {
|
||||
case MacroActionHttp::Method::GET:
|
||||
response = cli.Get(path, params, headers);
|
||||
break;
|
||||
case MacroActionHttp::Method::POST: {
|
||||
const auto pathWithParam =
|
||||
httplib::append_query_params(path, params);
|
||||
response = cli.Post(pathWithParam, headers, body,
|
||||
contentType);
|
||||
break;
|
||||
}
|
||||
case MacroActionHttp::Method::PUT: {
|
||||
const auto pathWithParam =
|
||||
httplib::append_query_params(path, params);
|
||||
response = cli.Put(pathWithParam, headers, body,
|
||||
contentType);
|
||||
break;
|
||||
}
|
||||
case MacroActionHttp::Method::PATCH: {
|
||||
const auto pathWithParam =
|
||||
httplib::append_query_params(path, params);
|
||||
response = cli.Patch(pathWithParam, headers, body,
|
||||
contentType);
|
||||
break;
|
||||
}
|
||||
case MacroActionHttp::Method::DELETE: {
|
||||
const auto pathWithParam =
|
||||
httplib::append_query_params(path, params);
|
||||
response = cli.Delete(pathWithParam, headers, body,
|
||||
contentType);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (VerboseLoggingEnabled() && !response) {
|
||||
|
||||
@@ -35,6 +35,7 @@ bool MacroActionScript::PerformAction()
|
||||
return true;
|
||||
}
|
||||
|
||||
SuspendLock suspendLock(static_cast<MacroAction &>(*this));
|
||||
(void)SendTriggerSignal();
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -36,6 +36,7 @@ bool MacroConditionScript::CheckCondition()
|
||||
return false;
|
||||
}
|
||||
|
||||
SuspendLock suspendLock(static_cast<MacroCondition &>(*this));
|
||||
return SendTriggerSignal();
|
||||
}
|
||||
|
||||
|
||||
@@ -1840,7 +1840,10 @@ void MacroActionTwitchEdit::SetWidgetLayout()
|
||||
|
||||
const char *layoutText;
|
||||
const char *layout2Text = nullptr;
|
||||
switch (_entryData->GetAction()) {
|
||||
|
||||
const auto action = _entryData->GetAction();
|
||||
|
||||
switch (action) {
|
||||
case MacroActionTwitch::Action::SEND_CHAT_MESSAGE:
|
||||
layoutText = obs_module_text(
|
||||
"AdvSceneSwitcher.action.twitch.layout.chat");
|
||||
@@ -1892,6 +1895,12 @@ void MacroActionTwitchEdit::SetWidgetLayout()
|
||||
|
||||
_layout->setContentsMargins(0, 0, 0, 0);
|
||||
_layout2->setContentsMargins(0, 0, 0, 0);
|
||||
|
||||
const bool showVariableMapping =
|
||||
action == MacroActionTwitch::Action::CHANNEL_GET_INFO ||
|
||||
action == MacroActionTwitch::Action::USER_GET_INFO ||
|
||||
action == MacroActionTwitch::Action::POINTS_REWARD_GET_INFO;
|
||||
emit ShowVariableMappings(showVariableMapping);
|
||||
}
|
||||
|
||||
void MacroActionTwitchEdit::UpdateEntryData()
|
||||
|
||||
@@ -267,6 +267,7 @@ private slots:
|
||||
|
||||
signals:
|
||||
void HeaderInfoChanged(const QString &);
|
||||
void ShowVariableMappings(bool show);
|
||||
|
||||
protected:
|
||||
std::shared_ptr<MacroActionTwitch> _entryData;
|
||||
|
||||
@@ -187,6 +187,13 @@ target_sources(
|
||||
${ADVSS_SOURCE_DIR}/lib/utils/file-selection.cpp
|
||||
${ADVSS_SOURCE_DIR}/lib/variables/variable-text-edit.cpp)
|
||||
|
||||
# --- macro-condition-process --- #
|
||||
|
||||
target_sources(
|
||||
${PROJECT_NAME}
|
||||
PRIVATE test-macro-condition-process.cpp stubs/platform-funcs.cpp
|
||||
${ADVSS_SOURCE_DIR}/plugins/base/macro-condition-process.cpp)
|
||||
|
||||
# --- Testing --- #
|
||||
|
||||
enable_testing()
|
||||
|
||||
101
tests/stubs/platform-funcs.cpp
Normal file
101
tests/stubs/platform-funcs.cpp
Normal file
@@ -0,0 +1,101 @@
|
||||
#include "platform-funcs.hpp"
|
||||
#include "selection-helpers.hpp"
|
||||
|
||||
#include <QComboBox>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace advss {
|
||||
|
||||
namespace {
|
||||
|
||||
std::string g_foregroundProcessName;
|
||||
std::string g_foregroundProcessPath;
|
||||
QStringList g_processList;
|
||||
QStringList g_processPathsFromName;
|
||||
|
||||
} // namespace
|
||||
|
||||
void SetStubForegroundProcessName(const std::string &name)
|
||||
{
|
||||
g_foregroundProcessName = name;
|
||||
}
|
||||
|
||||
void SetStubForegroundProcessPath(const std::string &path)
|
||||
{
|
||||
g_foregroundProcessPath = path;
|
||||
}
|
||||
|
||||
void SetStubProcessList(const QStringList &list)
|
||||
{
|
||||
g_processList = list;
|
||||
}
|
||||
|
||||
void SetStubProcessPaths(const QStringList &paths)
|
||||
{
|
||||
g_processPathsFromName = paths;
|
||||
}
|
||||
|
||||
// --- platform-funcs.hpp stubs ---
|
||||
|
||||
std::vector<std::string> GetWindowList()
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
std::string GetCurrentWindowTitle()
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
bool IsFullscreen(const std::string &)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool IsMaximized(const std::string &)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
std::optional<std::string> GetTextInWindow(const std::string &)
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
int SecondsSinceLastInput()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
QStringList GetProcessList()
|
||||
{
|
||||
return g_processList;
|
||||
}
|
||||
|
||||
std::string GetForegroundProcessName()
|
||||
{
|
||||
return g_foregroundProcessName;
|
||||
}
|
||||
|
||||
std::string GetForegroundProcessPath()
|
||||
{
|
||||
return g_foregroundProcessPath;
|
||||
}
|
||||
|
||||
QStringList GetProcessPathsFromName(const QString &)
|
||||
{
|
||||
return g_processPathsFromName;
|
||||
}
|
||||
|
||||
bool IsInFocus(const QString &)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// --- selection-helpers.hpp stubs ---
|
||||
|
||||
void PopulateProcessSelection(QComboBox *, bool) {}
|
||||
|
||||
} // namespace advss
|
||||
@@ -13,7 +13,10 @@ using advss::MacroConditionFile;
|
||||
static void writeFile(const QString &path, const QString &content)
|
||||
{
|
||||
QFile f(path);
|
||||
f.open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate);
|
||||
if (!f.open(QIODevice::WriteOnly | QIODevice::Text |
|
||||
QIODevice::Truncate)) {
|
||||
return;
|
||||
}
|
||||
QTextStream(&f) << content;
|
||||
}
|
||||
|
||||
@@ -155,7 +158,9 @@ TEST_CASE("DATE_CHANGE: triggers when modification date changes",
|
||||
// Explicitly set the modification time to a known future value so the
|
||||
// test is not sensitive to filesystem mtime resolution.
|
||||
QFile f(path);
|
||||
f.open(QIODevice::ReadWrite);
|
||||
if (!f.open(QIODevice::ReadWrite)) {
|
||||
return;
|
||||
}
|
||||
f.setFileTime(QDateTime::currentDateTime().addSecs(10),
|
||||
QFileDevice::FileModificationTime);
|
||||
f.close();
|
||||
|
||||
170
tests/test-macro-condition-process.cpp
Normal file
170
tests/test-macro-condition-process.cpp
Normal file
@@ -0,0 +1,170 @@
|
||||
#include "catch.hpp"
|
||||
#include "macro-condition-process.hpp"
|
||||
|
||||
namespace advss {
|
||||
void SetStubForegroundProcessName(const std::string &);
|
||||
void SetStubForegroundProcessPath(const std::string &);
|
||||
void SetStubProcessList(const QStringList &);
|
||||
void SetStubProcessPaths(const QStringList &);
|
||||
} // namespace advss
|
||||
|
||||
using advss::MacroConditionProcess;
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Name matching — no focus, no path
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
TEST_CASE("no focus: exact name match returns true",
|
||||
"[macro-condition-process]")
|
||||
{
|
||||
advss::SetStubProcessList({"game.exe", "obs64.exe"});
|
||||
|
||||
MacroConditionProcess cond(nullptr);
|
||||
cond._process = "game.exe";
|
||||
cond._checkFocus = false;
|
||||
|
||||
REQUIRE(cond.CheckCondition());
|
||||
}
|
||||
|
||||
TEST_CASE("no focus: exact name mismatch returns false",
|
||||
"[macro-condition-process]")
|
||||
{
|
||||
advss::SetStubProcessList({"obs64.exe"});
|
||||
|
||||
MacroConditionProcess cond(nullptr);
|
||||
cond._process = "game.exe";
|
||||
cond._checkFocus = false;
|
||||
|
||||
REQUIRE_FALSE(cond.CheckCondition());
|
||||
}
|
||||
|
||||
TEST_CASE("no focus: regex name match returns true",
|
||||
"[macro-condition-process]")
|
||||
{
|
||||
advss::SetStubProcessList({"game.exe", "obs64.exe"});
|
||||
|
||||
MacroConditionProcess cond(nullptr);
|
||||
cond._process = "game.*";
|
||||
cond._checkFocus = false;
|
||||
cond._regex.SetEnabled(true);
|
||||
|
||||
REQUIRE(cond.CheckCondition());
|
||||
}
|
||||
|
||||
TEST_CASE("no focus: regex name mismatch returns false",
|
||||
"[macro-condition-process]")
|
||||
{
|
||||
advss::SetStubProcessList({"obs64.exe"});
|
||||
|
||||
MacroConditionProcess cond(nullptr);
|
||||
cond._process = "game.*";
|
||||
cond._checkFocus = false;
|
||||
cond._regex.SetEnabled(true);
|
||||
|
||||
REQUIRE_FALSE(cond.CheckCondition());
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Focus — name only
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
TEST_CASE("focus: foreground name matches returns true",
|
||||
"[macro-condition-process]")
|
||||
{
|
||||
advss::SetStubForegroundProcessName("game.exe");
|
||||
advss::SetStubForegroundProcessPath("C:/Games/Game/game.exe");
|
||||
|
||||
MacroConditionProcess cond(nullptr);
|
||||
cond._process = "game.exe";
|
||||
cond._checkFocus = true;
|
||||
|
||||
REQUIRE(cond.CheckCondition());
|
||||
}
|
||||
|
||||
TEST_CASE("focus: foreground name does not match returns false",
|
||||
"[macro-condition-process]")
|
||||
{
|
||||
advss::SetStubForegroundProcessName("obs64.exe");
|
||||
advss::SetStubForegroundProcessPath("C:/OBS/obs64.exe");
|
||||
|
||||
MacroConditionProcess cond(nullptr);
|
||||
cond._process = "game.exe";
|
||||
cond._checkFocus = true;
|
||||
|
||||
REQUIRE_FALSE(cond.CheckCondition());
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Path matching — no focus
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
TEST_CASE("no focus with path: name and path both match returns true",
|
||||
"[macro-condition-process]")
|
||||
{
|
||||
advss::SetStubProcessList({"game.exe"});
|
||||
advss::SetStubProcessPaths({"C:/Steam/steamapps/common/Game/game.exe"});
|
||||
|
||||
MacroConditionProcess cond(nullptr);
|
||||
cond._process = "game.exe";
|
||||
cond._checkFocus = false;
|
||||
cond._checkPath = true;
|
||||
cond._processPath = "C:/Steam/steamapps/common/Game/game.exe";
|
||||
|
||||
REQUIRE(cond.CheckCondition());
|
||||
}
|
||||
|
||||
TEST_CASE("no focus with path: name matches but path does not returns false",
|
||||
"[macro-condition-process]")
|
||||
{
|
||||
advss::SetStubProcessList({"game.exe"});
|
||||
advss::SetStubProcessPaths({"C:/Epic/Games/Game/game.exe"});
|
||||
|
||||
MacroConditionProcess cond(nullptr);
|
||||
cond._process = "game.exe";
|
||||
cond._checkFocus = false;
|
||||
cond._checkPath = true;
|
||||
cond._processPath = "Steam";
|
||||
|
||||
REQUIRE_FALSE(cond.CheckCondition());
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Focus + path
|
||||
//
|
||||
// When both focus and path are checked, both must be satisfied by the *same*
|
||||
// foreground process instance. A background process that matches the path
|
||||
// but is not in focus must not cause a false positive.
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
TEST_CASE("focus with path: foreground matches both name and path returns true",
|
||||
"[macro-condition-process]")
|
||||
{
|
||||
advss::SetStubForegroundProcessName("game.exe");
|
||||
advss::SetStubForegroundProcessPath(
|
||||
"C:/Steam/steamapps/common/Game/game.exe");
|
||||
|
||||
MacroConditionProcess cond(nullptr);
|
||||
cond._process = "game.exe";
|
||||
cond._checkFocus = true;
|
||||
cond._checkPath = true;
|
||||
cond._processPath = "C:/Steam/steamapps/common/Game/game.exe";
|
||||
|
||||
REQUIRE(cond.CheckCondition());
|
||||
}
|
||||
|
||||
TEST_CASE(
|
||||
"focus with path: foreground name matches but path does not returns false",
|
||||
"[macro-condition-process]")
|
||||
{
|
||||
advss::SetStubForegroundProcessName("game.exe");
|
||||
advss::SetStubForegroundProcessPath("C:/Epic/Games/Game/game.exe");
|
||||
advss::SetStubProcessPaths({"C:/Steam/steamapps/common/Game/game.exe"});
|
||||
|
||||
MacroConditionProcess cond(nullptr);
|
||||
cond._process = "game.exe";
|
||||
cond._checkFocus = true;
|
||||
cond._checkPath = true;
|
||||
cond._processPath = "C:/Steam/steamapps/common/Game/game.exe";
|
||||
|
||||
REQUIRE_FALSE(cond.CheckCondition());
|
||||
}
|
||||
Reference in New Issue
Block a user