Compare commits

..

33 Commits

Author SHA1 Message Date
WarmUpTill
b25c1250e5 Try adding tilde key to hotkey action 2024-01-03 19:24:08 +01:00
WarmUpTill
98f3f51ba9 Don't enable drag of macro segment when interacting with scrollbars 2023-12-31 10:13:48 +01:00
WarmUpTill
f0df9ead3c Fix macro segment widgets being squashed below a sensible minimum size
Now a horizontal scroll bar will be displayed in the corresponding
MacroSegmentList widget instead.
2023-12-29 19:31:45 +01:00
WarmUpTill
7b1a57070d Clean up locale 2023-12-29 19:31:45 +01:00
WarmUpTill
a412f2fd5e Fix ResizingPlainTextEdit hiding text in some circumstances
In some scenarios - the exact reason is not known - the widget would
scroll to the very last row, which is always empty, and result in the
text cursor and the already entered text being moved above the displayed
edit area.

It could be fixed by manually scrolling back up (e.g. by attempting to
highlight text with the mouse), but the problem would reappear as soon
as new existing text was modified.

To work around this issue text wrapping was disabled and additional
height will be added for the horizontal scroll bar, if they are visible.

There are still some issues of the height randomly flickering in the
scenarios, which would previously result in the text no longer being
visible.
2023-12-29 19:31:45 +01:00
WarmUpTill
4f0c8c4ab5 Move audio fade helpers to audio action 2023-12-29 19:31:45 +01:00
WarmUpTill
946c90a749 Move tracking of cursor position changes to cursor condition 2023-12-29 19:31:45 +01:00
WarmUpTill
7ba4eaab9c Fix replay buffer save event only triggering in a single condition 2023-12-29 19:31:45 +01:00
WarmUpTill
74553a82fc Remove deprecated "on change" toggle of media condition 2023-12-29 19:31:45 +01:00
WarmUpTill
39fcb2d743 Fix typos 2023-12-29 19:31:45 +01:00
WarmUpTill
7041feaea3 Move handling of stream start / stop time to stream condition 2023-12-29 19:31:45 +01:00
WarmUpTill
9b1ffaeded Remove switcher-data dependency from transition-selection 2023-12-29 19:31:45 +01:00
WarmUpTill
07d397bdac Remove switcher-data dependency from scene-selection 2023-12-29 19:31:45 +01:00
WarmUpTill
a86ff4f1f0 Remove switcher-data dependency from status-control 2023-12-29 19:31:45 +01:00
WarmUpTill
953eca2732 Remove switcher-data dependency from variables 2023-12-29 19:31:45 +01:00
WarmUpTill
d6c5fb149a Fix warning 2023-12-29 19:31:45 +01:00
WarmUpTill
12982708cf Remove curl-helper dependency from switcher-data 2023-12-29 19:31:45 +01:00
WarmUpTill
0cf9e1dd81 Reduce dependencies of macros to switcher data 2023-12-29 19:31:45 +01:00
WarmUpTill
71e20c4983 Move loading / saving of global macro properties 2023-12-29 19:31:45 +01:00
WarmUpTill
2147f6d3bf Rename files 2023-12-29 19:31:45 +01:00
WarmUpTill
23df42d2d3 Add more plugin state helpers 2023-12-29 19:31:45 +01:00
WarmUpTill
bcf0a0b7fd Move functionality to scene-switch-helpers 2023-12-29 19:31:45 +01:00
WarmUpTill
dd78b03a28 Add additional sync-helpers to reduce dependencies to switcher-data 2023-12-29 19:31:45 +01:00
WarmUpTill
84dd5e6930 Add macro-helpers
Added to reduce dependencies to switcher-data
2023-12-29 19:31:45 +01:00
WarmUpTill
dcee98fea7 Move macro segment factory functinos to seperate files 2023-12-29 19:31:45 +01:00
WarmUpTill
9ffb982ae5 Reset date condition type only if necessary 2023-12-29 19:31:45 +01:00
WarmUpTill
428850306f Add option to wait for spawned process to exit 2023-12-25 21:24:48 +01:00
WarmUpTill
abfd6798e3 Adapt to new process start helpers 2023-12-25 21:24:48 +01:00
WarmUpTill
fffadd94de Add helpers to launch process with given config 2023-12-25 21:24:48 +01:00
WarmUpTill
d0919776f3 Add option to set variable to scene item name at given index 2023-12-19 22:59:32 +01:00
WarmUpTill
267855fded Cleanup
* Use OBSSourceAutoRelease
* Do not return true if source selection is invalid
2023-12-17 10:40:29 +01:00
WarmUpTill
4bdebb7feb Fix crash when asking for settings backup
This dialog cannot be modal since other plugins might attempt to display
content which might result in a deadlock.
2023-12-16 19:02:38 +01:00
Michael Kirsch
ab5f69d2ef Decrease the minimal interval time to 10ms 2023-12-13 19:29:14 +01:00
108 changed files with 1653 additions and 1177 deletions

View File

@@ -97,6 +97,8 @@ target_sources(
src/macro-core/macro-action-clipboard.hpp src/macro-core/macro-action-clipboard.hpp
src/macro-core/macro-action-edit.cpp src/macro-core/macro-action-edit.cpp
src/macro-core/macro-action-edit.hpp src/macro-core/macro-action-edit.hpp
src/macro-core/macro-action-factory.cpp
src/macro-core/macro-action-factory.hpp
src/macro-core/macro-action-file.cpp src/macro-core/macro-action-file.cpp
src/macro-core/macro-action-file.hpp src/macro-core/macro-action-file.hpp
src/macro-core/macro-action-filter.cpp src/macro-core/macro-action-filter.cpp
@@ -173,6 +175,8 @@ target_sources(
src/macro-core/macro-condition-display.hpp src/macro-core/macro-condition-display.hpp
src/macro-core/macro-condition-edit.cpp src/macro-core/macro-condition-edit.cpp
src/macro-core/macro-condition-edit.hpp src/macro-core/macro-condition-edit.hpp
src/macro-core/macro-condition-factory.cpp
src/macro-core/macro-condition-factory.hpp
src/macro-core/macro-condition-file.cpp src/macro-core/macro-condition-file.cpp
src/macro-core/macro-condition-file.hpp src/macro-core/macro-condition-file.hpp
src/macro-core/macro-condition-filter.cpp src/macro-core/macro-condition-filter.cpp
@@ -233,6 +237,8 @@ target_sources(
src/macro-core/macro-condition.hpp src/macro-core/macro-condition.hpp
src/macro-core/macro-dock.cpp src/macro-core/macro-dock.cpp
src/macro-core/macro-dock.hpp src/macro-core/macro-dock.hpp
src/macro-core/macro-helpers.cpp
src/macro-core/macro-helpers.hpp
src/macro-core/macro-properties.cpp src/macro-core/macro-properties.cpp
src/macro-core/macro-properties.hpp src/macro-core/macro-properties.hpp
src/macro-core/macro-ref.cpp src/macro-core/macro-ref.cpp
@@ -291,8 +297,8 @@ target_sources(
src/utils/obs-module-helper.hpp src/utils/obs-module-helper.hpp
src/utils/osc-helpers.cpp src/utils/osc-helpers.cpp
src/utils/osc-helpers.hpp src/utils/osc-helpers.hpp
src/utils/plugin-state-helper.cpp src/utils/plugin-state-helpers.cpp
src/utils/plugin-state-helper.hpp src/utils/plugin-state-helpers.hpp
src/utils/priority-helper.cpp src/utils/priority-helper.cpp
src/utils/priority-helper.hpp src/utils/priority-helper.hpp
src/utils/process-config.cpp src/utils/process-config.cpp
@@ -323,8 +329,8 @@ target_sources(
src/utils/striped-frame.hpp src/utils/striped-frame.hpp
src/utils/switch-button.cpp src/utils/switch-button.cpp
src/utils/switch-button.hpp src/utils/switch-button.hpp
src/utils/sync-helper.cpp src/utils/sync-helpers.cpp
src/utils/sync-helper.hpp src/utils/sync-helpers.hpp
src/utils/temp-variable.cpp src/utils/temp-variable.cpp
src/utils/temp-variable.hpp src/utils/temp-variable.hpp
src/utils/transition-selection.cpp src/utils/transition-selection.cpp

View File

@@ -152,11 +152,13 @@ AdvSceneSwitcher.condition.file.type.contentChange="Inhalt geändert"
AdvSceneSwitcher.condition.file.type.dateChange="Änderungsdatum geändert" AdvSceneSwitcher.condition.file.type.dateChange="Änderungsdatum geändert"
AdvSceneSwitcher.condition.file.remote="Entfernte Datei" AdvSceneSwitcher.condition.file.remote="Entfernte Datei"
AdvSceneSwitcher.condition.file.local="Lokale Datei" AdvSceneSwitcher.condition.file.local="Lokale Datei"
AdvSceneSwitcher.condition.file.entry.line1="{{fileType}}{{filePath}}{{conditions}}{{useRegex}}"
AdvSceneSwitcher.condition.file.entry.line2="{{matchText}}"
AdvSceneSwitcher.condition.file.entry.line3="{{checkModificationDate}}{{checkFileContent}}"
AdvSceneSwitcher.condition.media="Medien" AdvSceneSwitcher.condition.media="Medien"
AdvSceneSwitcher.condition.media.source="Quelle" AdvSceneSwitcher.condition.media.source="Quelle"
AdvSceneSwitcher.condition.media.anyOnScene="Beliebige Medienquelle in" AdvSceneSwitcher.condition.media.anyOnScene="Beliebige Medienquelle in"
AdvSceneSwitcher.condition.media.allOnScene="Alle Medienquellen in" AdvSceneSwitcher.condition.media.allOnScene="Alle Medienquellen in"
AdvSceneSwitcher.condition.media.matchOnChange="Nur bei Änderung übereinstimmen (Hinweis: Diese Option wird in einer zukünftigen Version entfernt werden - bitte stattdessen Dauer-Modifikatoren verwenden)"
AdvSceneSwitcher.condition.media.inconsistencyInfo="Leider verhalten sich nicht alle Medien-Quell-Typen gleich (z.B. Medien-Quelle vs. VLC-Video-Quelle \"Gestoppt\"-Status).\nBitte experimentieren, was in eurem Setup funktioniert!" AdvSceneSwitcher.condition.media.inconsistencyInfo="Leider verhalten sich nicht alle Medien-Quell-Typen gleich (z.B. Medien-Quelle vs. VLC-Video-Quelle \"Gestoppt\"-Status).\nBitte experimentieren, was in eurem Setup funktioniert!"
AdvSceneSwitcher.condition.media.entry="{{sourceTypes}}{{mediaSources}}{{scenes}}Status ist{{states}}und{{timeRestrictions}}{{time}}" AdvSceneSwitcher.condition.media.entry="{{sourceTypes}}{{mediaSources}}{{scenes}}Status ist{{states}}und{{timeRestrictions}}{{time}}"
AdvSceneSwitcher.condition.video="Video" AdvSceneSwitcher.condition.video="Video"
@@ -571,7 +573,6 @@ AdvSceneSwitcher.action.variable.invalidSelection="Ungültige Auswahl!"
AdvSceneSwitcher.action.variable.actionNoVariableSupport="Das Abrufen von Variablenwerten aus %1 Aktionen wird nicht unterstützt!" AdvSceneSwitcher.action.variable.actionNoVariableSupport="Das Abrufen von Variablenwerten aus %1 Aktionen wird nicht unterstützt!"
AdvSceneSwitcher.action.variable.conditionNoVariableSupport="Das Abrufen von Variablenwerten aus %1 Bedingungen wird nicht unterstützt!" AdvSceneSwitcher.action.variable.conditionNoVariableSupport="Das Abrufen von Variablenwerten aus %1 Bedingungen wird nicht unterstützt!"
AdvSceneSwitcher.action.variable.currentSegmentValue="Aktueller Wert:" AdvSceneSwitcher.action.variable.currentSegmentValue="Aktueller Wert:"
AdvSceneSwitcher.action.variable.entry="{{actions}}{{variables}}{{variables2}}{{strValue}}{{numValue}}{{segmentIndex}}{{mathExpression}}{{envVariableName}}{{scenes}}{{tempVars}}"
; Transition Tab ; Transition Tab

View File

@@ -195,22 +195,18 @@ AdvSceneSwitcher.condition.window.entry.text="{{checkText}}Window contains text{
AdvSceneSwitcher.condition.window.entry.text.note="This option might not work on every text being displayed in a window.\nIf that is the case, you can consider using the Video conditions OCR check instead." AdvSceneSwitcher.condition.window.entry.text.note="This option might not work on every text being displayed in a window.\nIf that is the case, you can consider using the Video conditions OCR check instead."
AdvSceneSwitcher.condition.window.entry.currentFocus="Current focus window:{{focusWindow}}" AdvSceneSwitcher.condition.window.entry.currentFocus="Current focus window:{{focusWindow}}"
AdvSceneSwitcher.condition.file="File" AdvSceneSwitcher.condition.file="File"
AdvSceneSwitcher.condition.file.type.match="Matches" AdvSceneSwitcher.condition.file.type.match="matches"
AdvSceneSwitcher.condition.file.type.contentChange="Content changed" AdvSceneSwitcher.condition.file.type.contentChange="content changed"
AdvSceneSwitcher.condition.file.type.dateChange="Modification date changed" AdvSceneSwitcher.condition.file.type.dateChange="modification date changed"
AdvSceneSwitcher.condition.file.type.changesMatch="Changed lines match"
AdvSceneSwitcher.condition.file.remote="Remote file" AdvSceneSwitcher.condition.file.remote="Remote file"
AdvSceneSwitcher.condition.file.local="Local file" AdvSceneSwitcher.condition.file.local="Local file"
AdvSceneSwitcher.condition.file.changeMatchType.any="Any lines" AdvSceneSwitcher.condition.file.entry.line1="{{fileType}}{{filePath}}{{conditions}}{{useRegex}}"
AdvSceneSwitcher.condition.file.changeMatchType.added="Added lines"
AdvSceneSwitcher.condition.file.changeMatchType.removed="Removed lines"
AdvSceneSwitcher.condition.file.entry.line1="{{fileType}}{{filePath}}{{conditions}}{{changeMatchType}}{{useRegex}}"
AdvSceneSwitcher.condition.file.entry.line2="{{matchText}}" AdvSceneSwitcher.condition.file.entry.line2="{{matchText}}"
AdvSceneSwitcher.condition.file.entry.line3="{{checkModificationDate}}{{checkFileContent}}"
AdvSceneSwitcher.condition.media="Media" AdvSceneSwitcher.condition.media="Media"
AdvSceneSwitcher.condition.media.source="Source" AdvSceneSwitcher.condition.media.source="Source"
AdvSceneSwitcher.condition.media.anyOnScene="Any media source on" AdvSceneSwitcher.condition.media.anyOnScene="Any media source on"
AdvSceneSwitcher.condition.media.allOnScene="All media sources on" AdvSceneSwitcher.condition.media.allOnScene="All media sources on"
AdvSceneSwitcher.condition.media.matchOnChange="Only match on change (Note: This option will be removed in a future version - please use duration modifiers instead)"
AdvSceneSwitcher.condition.media.inconsistencyInfo="Unfortunately not all media source types behave the same (e.g. Media Source vs. VLC Video Source \"Stopped\" state).\nSo please experiment what works for your setup!" AdvSceneSwitcher.condition.media.inconsistencyInfo="Unfortunately not all media source types behave the same (e.g. Media Source vs. VLC Video Source \"Stopped\" state).\nSo please experiment what works for your setup!"
AdvSceneSwitcher.condition.media.entry="{{sourceTypes}}{{mediaSources}}{{scenes}}state is{{states}}and{{timeRestrictions}}{{time}}" AdvSceneSwitcher.condition.media.entry="{{sourceTypes}}{{mediaSources}}{{scenes}}state is{{states}}and{{timeRestrictions}}{{time}}"
AdvSceneSwitcher.condition.video="Video" AdvSceneSwitcher.condition.video="Video"
@@ -613,6 +609,7 @@ AdvSceneSwitcher.action.streaming.type.username="Set username"
AdvSceneSwitcher.action.streaming.type.password="Set password" AdvSceneSwitcher.action.streaming.type.password="Set password"
AdvSceneSwitcher.action.streaming.entry="{{actions}}{{keyFrameInterval}}{{stringValue}}{{showPassword}}" AdvSceneSwitcher.action.streaming.entry="{{actions}}{{keyFrameInterval}}{{stringValue}}{{showPassword}}"
AdvSceneSwitcher.action.run="Run" AdvSceneSwitcher.action.run="Run"
AdvSceneSwitcher.action.run.entry.wait="{{wait}}Wait for process exit or at most {{timeout}}"
AdvSceneSwitcher.action.sceneVisibility="Scene item visibility" AdvSceneSwitcher.action.sceneVisibility="Scene item visibility"
AdvSceneSwitcher.action.sceneVisibility.type.show="Show" AdvSceneSwitcher.action.sceneVisibility.type.show="Show"
AdvSceneSwitcher.action.sceneVisibility.type.hide="Hide" AdvSceneSwitcher.action.sceneVisibility.type.hide="Hide"
@@ -823,6 +820,7 @@ AdvSceneSwitcher.action.variable.type.sceneItemCount="Set to scene item count of
AdvSceneSwitcher.action.variable.type.stringLength="Set to length of string" AdvSceneSwitcher.action.variable.type.stringLength="Set to length of string"
AdvSceneSwitcher.action.variable.type.extractJson="Extract json field with name" AdvSceneSwitcher.action.variable.type.extractJson="Extract json field with name"
AdvSceneSwitcher.action.variable.type.setToTempvar="Set to macro property" AdvSceneSwitcher.action.variable.type.setToTempvar="Set to macro property"
AdvSceneSwitcher.action.variable.type.sceneItemName="Set to scene item name at index"
AdvSceneSwitcher.action.variable.askForValuePromptDefault="Assign value to variable \"%1\":" AdvSceneSwitcher.action.variable.askForValuePromptDefault="Assign value to variable \"%1\":"
AdvSceneSwitcher.action.variable.askForValuePrompt="Assign value to variable:" AdvSceneSwitcher.action.variable.askForValuePrompt="Assign value to variable:"
AdvSceneSwitcher.action.variable.mathExpression.example="( 1 + 2 * 3 ) / 4" AdvSceneSwitcher.action.variable.mathExpression.example="( 1 + 2 * 3 ) / 4"
@@ -834,7 +832,7 @@ AdvSceneSwitcher.action.variable.invalidSelection="Invalid selection!"
AdvSceneSwitcher.action.variable.actionNoVariableSupport="Getting variable values from %1 actions is not supported!" AdvSceneSwitcher.action.variable.actionNoVariableSupport="Getting variable values from %1 actions is not supported!"
AdvSceneSwitcher.action.variable.conditionNoVariableSupport="Getting variable values from %1 conditions is not supported!" AdvSceneSwitcher.action.variable.conditionNoVariableSupport="Getting variable values from %1 conditions is not supported!"
AdvSceneSwitcher.action.variable.currentSegmentValue="Current value:" AdvSceneSwitcher.action.variable.currentSegmentValue="Current value:"
AdvSceneSwitcher.action.variable.entry="{{actions}}{{variables}}{{variables2}}{{strValue}}{{numValue}}{{segmentIndex}}{{mathExpression}}{{envVariableName}}{{scenes}}{{tempVars}}" AdvSceneSwitcher.action.variable.entry="{{actions}}{{variables}}{{variables2}}{{strValue}}{{numValue}}{{segmentIndex}}{{mathExpression}}{{envVariableName}}{{scenes}}{{tempVars}}{{sceneItemIndex}}"
AdvSceneSwitcher.action.variable.entry.substringIndex="Substring start:{{subStringStart}}Substring size:{{subStringSize}}" AdvSceneSwitcher.action.variable.entry.substringIndex="Substring start:{{subStringStart}}Substring size:{{subStringSize}}"
AdvSceneSwitcher.action.variable.entry.substringRegex="Assign value of{{regexMatchIdx}}match using regular expression:" AdvSceneSwitcher.action.variable.entry.substringRegex="Assign value of{{regexMatchIdx}}match using regular expression:"
AdvSceneSwitcher.action.variable.entry.findAndReplace="{{findStr}}{{replaceStr}}" AdvSceneSwitcher.action.variable.entry.findAndReplace="{{findStr}}{{replaceStr}}"
@@ -1296,9 +1294,9 @@ AdvSceneSwitcher.tempVar.twitch.started_at.shoutoutReceived="Shoutout time"
AdvSceneSwitcher.tempVar.twitch.started_at.shoutoutReceived.description="The UTC timestamp (in RFC3339 format) of when the moderator sent the Shoutout." AdvSceneSwitcher.tempVar.twitch.started_at.shoutoutReceived.description="The UTC timestamp (in RFC3339 format) of when the moderator sent the Shoutout."
AdvSceneSwitcher.tempVar.twitch.id.reward="Reward id" AdvSceneSwitcher.tempVar.twitch.id.reward="Reward id"
AdvSceneSwitcher.tempVar.twitch.user_id.reward="User id redeeming reward" AdvSceneSwitcher.tempVar.twitch.user_id.reward="User id redeeming the reward"
AdvSceneSwitcher.tempVar.twitch.user_login.reward="User login redeeming reward" AdvSceneSwitcher.tempVar.twitch.user_login.reward="User login redeeming the reward"
AdvSceneSwitcher.tempVar.twitch.user_name.reward="User login redeeming reward" AdvSceneSwitcher.tempVar.twitch.user_name.reward="User name redeeming the reward"
AdvSceneSwitcher.tempVar.twitch.user_input.reward="User input for reward" AdvSceneSwitcher.tempVar.twitch.user_input.reward="User input for reward"
AdvSceneSwitcher.tempVar.twitch.reward.reward="Reward Information" AdvSceneSwitcher.tempVar.twitch.reward.reward="Reward Information"
AdvSceneSwitcher.tempVar.twitch.reward.reward.description="Basic information about the reward that was redeemed, at the time it was redeemed." AdvSceneSwitcher.tempVar.twitch.reward.reward.description="Basic information about the reward that was redeemed, at the time it was redeemed."

View File

@@ -123,10 +123,12 @@ AdvSceneSwitcher.condition.scene.entry.line1="{{sceneType}}{{scenes}}{{pattern}}
AdvSceneSwitcher.condition.scene.entry.line2="{{useTransitionTargetScene}}" AdvSceneSwitcher.condition.scene.entry.line2="{{useTransitionTargetScene}}"
AdvSceneSwitcher.condition.window="Ventana" AdvSceneSwitcher.condition.window="Ventana"
AdvSceneSwitcher.condition.file="Archivo" AdvSceneSwitcher.condition.file="Archivo"
AdvSceneSwitcher.condition.file.entry.line1="Contenido de{{fileType}}{{filePath}}{{conditions}}{{useRegex}}"
AdvSceneSwitcher.condition.file.entry.line2="{{matchText}}"
AdvSceneSwitcher.condition.file.entry.line3="{{checkModificationDate}}{{checkFileContent}}"
AdvSceneSwitcher.condition.media="Medios" AdvSceneSwitcher.condition.media="Medios"
AdvSceneSwitcher.condition.media.anyOnScene="Cualquier fuente multimedia activada" AdvSceneSwitcher.condition.media.anyOnScene="Cualquier fuente multimedia activada"
AdvSceneSwitcher.condition.media.allOnScene="Todas las fuentes de medios activadas" AdvSceneSwitcher.condition.media.allOnScene="Todas las fuentes de medios activadas"
AdvSceneSwitcher.condition.media.matchOnChange="Solo coincidir con el cambio (Nota: esta opción se eliminará en una versión futura; use modificadores de duración en su lugar)"
AdvSceneSwitcher.condition.media.inconsistencyInfo="Desafortunadamente, no todos los tipos de fuentes de medios se comportan de la misma manera (p. ej., fuente de medios frente a estado \"Detenido\" de fuente de video VLC).\n¡Así que experimente lo que funciona para su configuración!" AdvSceneSwitcher.condition.media.inconsistencyInfo="Desafortunadamente, no todos los tipos de fuentes de medios se comportan de la misma manera (p. ej., fuente de medios frente a estado \"Detenido\" de fuente de video VLC).\n¡Así que experimente lo que funciona para su configuración!"
AdvSceneSwitcher.condition.media.entry="El estado de{{sourceTypes}}{{mediaSources}}{{scenes}}es{{states}}y{{timeRestrictions}}{{time}}" AdvSceneSwitcher.condition.media.entry="El estado de{{sourceTypes}}{{mediaSources}}{{scenes}}es{{states}}y{{timeRestrictions}}{{time}}"
AdvSceneSwitcher.condition.video="Video" AdvSceneSwitcher.condition.video="Video"

View File

@@ -197,7 +197,6 @@ AdvSceneSwitcher.condition.media="Média"
AdvSceneSwitcher.condition.media.source="Source" AdvSceneSwitcher.condition.media.source="Source"
AdvSceneSwitcher.condition.media.anyOnScene="Toute source média sur la scène" AdvSceneSwitcher.condition.media.anyOnScene="Toute source média sur la scène"
AdvSceneSwitcher.condition.media.allOnScene="Toutes les sources média sur la scène" AdvSceneSwitcher.condition.media.allOnScene="Toutes les sources média sur la scène"
AdvSceneSwitcher.condition.media.matchOnChange="Ne correspondre qu'au changement (Remarque : Cette option sera supprimée dans une version future - veuillez plutôt utiliser les modificateurs de durée)"
AdvSceneSwitcher.condition.media.inconsistencyInfo="Malheureusement, toutes les sources média ne se comportent pas de la même manière (par exemple, l'état \"Arrêté\" de la source Media par rapport à la source Vidéo VLC).\nPar conséquent, veuillez expérimenter ce qui fonctionne le mieux pour votre configuration !" AdvSceneSwitcher.condition.media.inconsistencyInfo="Malheureusement, toutes les sources média ne se comportent pas de la même manière (par exemple, l'état \"Arrêté\" de la source Media par rapport à la source Vidéo VLC).\nPar conséquent, veuillez expérimenter ce qui fonctionne le mieux pour votre configuration !"
AdvSceneSwitcher.condition.media.entry="{{sourceTypes}}{{mediaSources}}{{scenes}}l'état est{{states}}et{{timeRestrictions}}{{time}}" AdvSceneSwitcher.condition.media.entry="{{sourceTypes}}{{mediaSources}}{{scenes}}l'état est{{states}}et{{timeRestrictions}}{{time}}"
AdvSceneSwitcher.condition.video="Vidéo" AdvSceneSwitcher.condition.video="Vidéo"
@@ -740,7 +739,6 @@ AdvSceneSwitcher.action.twitch.type.channel.info.title.set="Définir le titre du
AdvSceneSwitcher.action.twitch.type.channel.info.category.set="Définir la catégorie du flux" AdvSceneSwitcher.action.twitch.type.channel.info.category.set="Définir la catégorie du flux"
AdvSceneSwitcher.action.twitch.type.commercial.start="Démarrer une publicité d'une durée de" AdvSceneSwitcher.action.twitch.type.commercial.start="Démarrer une publicité d'une durée de"
AdvSceneSwitcher.action.twitch.categorySelectionDisabled="Impossible de sélectionner une catégorie sans avoir d'abord sélectionné un compte Twitch !" AdvSceneSwitcher.action.twitch.categorySelectionDisabled="Impossible de sélectionner une catégorie sans avoir d'abord sélectionné un compte Twitch !"
AdvSceneSwitcher.action.twitch.entry.line1="Sur{{account}}{{actions}}{{streamTitle}}{{category}}{{markerDescription}}{{clipHasDelay}}{{duration}}{{announcementColor}}{{channel}}"
; Transition Tab ; Transition Tab
AdvSceneSwitcher.transitionTab.title="Transition" AdvSceneSwitcher.transitionTab.title="Transition"

View File

@@ -87,6 +87,8 @@ AdvSceneSwitcher.condition.scene.type.current="Текущий"
AdvSceneSwitcher.condition.scene.type.previous="Предыдущий" AdvSceneSwitcher.condition.scene.type.previous="Предыдущий"
AdvSceneSwitcher.condition.window="Окно" AdvSceneSwitcher.condition.window="Окно"
AdvSceneSwitcher.condition.file="Файл" AdvSceneSwitcher.condition.file="Файл"
AdvSceneSwitcher.condition.file.entry.line2="{{matchText}}"
AdvSceneSwitcher.condition.file.entry.line3="{{checkModificationDate}}{{checkFileContent}}"
AdvSceneSwitcher.condition.media="Медиа" AdvSceneSwitcher.condition.media="Медиа"
AdvSceneSwitcher.condition.video="Видео" AdvSceneSwitcher.condition.video="Видео"
AdvSceneSwitcher.condition.video.condition.match="точно соответствует" AdvSceneSwitcher.condition.video.condition.match="точно соответствует"

View File

@@ -113,10 +113,12 @@ AdvSceneSwitcher.condition.scene.entry.line1="{{sceneType}}{{scenes}}{{pattern}}
AdvSceneSwitcher.condition.scene.entry.line2="{{useTransitionTargetScene}}" AdvSceneSwitcher.condition.scene.entry.line2="{{useTransitionTargetScene}}"
AdvSceneSwitcher.condition.window="Pencere" AdvSceneSwitcher.condition.window="Pencere"
AdvSceneSwitcher.condition.file="Dosya" AdvSceneSwitcher.condition.file="Dosya"
AdvSceneSwitcher.condition.file.entry.line1="İçerik{{fileType}}{{filePath}}{{conditions}}{{useRegex}}"
AdvSceneSwitcher.condition.file.entry.line2="{{matchText}}"
AdvSceneSwitcher.condition.file.entry.line3="{{checkModificationDate}}{{checkFileContent}}"
AdvSceneSwitcher.condition.media="Medya" AdvSceneSwitcher.condition.media="Medya"
AdvSceneSwitcher.condition.media.anyOnScene="Herhangi bir medya kaynağı" AdvSceneSwitcher.condition.media.anyOnScene="Herhangi bir medya kaynağı"
AdvSceneSwitcher.condition.media.allOnScene="Tüm medya kaynakları " AdvSceneSwitcher.condition.media.allOnScene="Tüm medya kaynakları "
AdvSceneSwitcher.condition.media.matchOnChange="Yalnızca değişiklikle eşleştirin (Not: Bu seçenek gelecekteki bir sürümde kaldırılacaktır - lütfen bunun yerine zaman kısıtlamalarını kullanın)"
AdvSceneSwitcher.condition.media.entry="{{sourceTypes}}{{mediaSources}}{{scenes}}durumu{{states}}ve{{timeRestrictions}}{{time}}" AdvSceneSwitcher.condition.media.entry="{{sourceTypes}}{{mediaSources}}{{scenes}}durumu{{states}}ve{{timeRestrictions}}{{time}}"
AdvSceneSwitcher.condition.video="Video" AdvSceneSwitcher.condition.video="Video"
AdvSceneSwitcher.condition.video.condition.match="Tam olarak eşleşir" AdvSceneSwitcher.condition.video.condition.match="Tam olarak eşleşir"

View File

@@ -178,11 +178,13 @@ AdvSceneSwitcher.condition.file.type.contentChange="内容已更改"
AdvSceneSwitcher.condition.file.type.dateChange="修改日期已更改" AdvSceneSwitcher.condition.file.type.dateChange="修改日期已更改"
AdvSceneSwitcher.condition.file.remote="远程文件" AdvSceneSwitcher.condition.file.remote="远程文件"
AdvSceneSwitcher.condition.file.local="本地文件" AdvSceneSwitcher.condition.file.local="本地文件"
AdvSceneSwitcher.condition.file.entry.line1="{{fileType}}{{filePath}}{{conditions}}{{useRegex}}"
AdvSceneSwitcher.condition.file.entry.line2="{{matchText}}"
AdvSceneSwitcher.condition.file.entry.line3="{{checkModificationDate}}{{checkFileContent}}"
AdvSceneSwitcher.condition.media="媒体" AdvSceneSwitcher.condition.media="媒体"
AdvSceneSwitcher.condition.media.source="源" AdvSceneSwitcher.condition.media.source="源"
AdvSceneSwitcher.condition.media.anyOnScene="任何媒体源" AdvSceneSwitcher.condition.media.anyOnScene="任何媒体源"
AdvSceneSwitcher.condition.media.allOnScene="所有媒体源" AdvSceneSwitcher.condition.media.allOnScene="所有媒体源"
AdvSceneSwitcher.condition.media.matchOnChange="仅在更改时匹配(注意:此选项将在未来版本中删除-请改用时间限制)"
AdvSceneSwitcher.condition.media.inconsistencyInfo="不幸的是并非所有媒体源类型的行为都相同例如媒体源与VLC视频源“停止”状态。\n所以请尝试适合您的设置" AdvSceneSwitcher.condition.media.inconsistencyInfo="不幸的是并非所有媒体源类型的行为都相同例如媒体源与VLC视频源“停止”状态。\n所以请尝试适合您的设置"
AdvSceneSwitcher.condition.media.entry="{{sourceTypes}}{{mediaSources}}{{scenes}} 的状态是 {{states}} 和 {{timeRestrictions}}{{time}}" AdvSceneSwitcher.condition.media.entry="{{sourceTypes}}{{mediaSources}}{{scenes}} 的状态是 {{states}} 和 {{timeRestrictions}}{{time}}"
AdvSceneSwitcher.condition.video="视频" AdvSceneSwitcher.condition.video="视频"
@@ -647,7 +649,6 @@ AdvSceneSwitcher.action.variable.invalidSelection="无效选择!"
AdvSceneSwitcher.action.variable.actionNoVariableSupport="不支持从 %1 个操作获取变量值!" AdvSceneSwitcher.action.variable.actionNoVariableSupport="不支持从 %1 个操作获取变量值!"
AdvSceneSwitcher.action.variable.conditionNoVariableSupport="不支持从 %1 条件中获取变量值!" AdvSceneSwitcher.action.variable.conditionNoVariableSupport="不支持从 %1 条件中获取变量值!"
AdvSceneSwitcher.action.variable.currentSegmentValue="当前值:" AdvSceneSwitcher.action.variable.currentSegmentValue="当前值:"
AdvSceneSwitcher.action.variable.entry="{{actions}}{{variables}}{{variables2}}{{strValue}}{{numValue}}{{segmentIndex}}{{mathExpression}}{{envVariableName}}{{scenes}}{{tempVars}}"
AdvSceneSwitcher.action.variable.entry.substringIndex="子字符串开始:{{subStringStart}} 子字符串大小:{{subStringSize}}" AdvSceneSwitcher.action.variable.entry.substringIndex="子字符串开始:{{subStringStart}} 子字符串大小:{{subStringSize}}"
AdvSceneSwitcher.action.variable.entry.substringRegex="使用正则表达式为 {{regexMatchIdx}} 匹配的值:" AdvSceneSwitcher.action.variable.entry.substringRegex="使用正则表达式为 {{regexMatchIdx}} 匹配的值:"
AdvSceneSwitcher.action.variable.entry.findAndReplace="{{findStr}}{{replaceStr}}" AdvSceneSwitcher.action.variable.entry.findAndReplace="{{findStr}}{{replaceStr}}"

View File

@@ -119,7 +119,7 @@
<string notr="true">ms</string> <string notr="true">ms</string>
</property> </property>
<property name="minimum"> <property name="minimum">
<number>50</number> <number>10</number>
</property> </property>
<property name="maximum"> <property name="maximum">
<number>20000</number> <number>20000</number>

View File

@@ -1,5 +1,6 @@
#include "advanced-scene-switcher.hpp" #include "advanced-scene-switcher.hpp"
#include "switcher-data.hpp" #include "switcher-data.hpp"
#include "macro-helpers.hpp"
#include "status-control.hpp" #include "status-control.hpp"
#include "scene-switch-helpers.hpp" #include "scene-switch-helpers.hpp"
#include "curl-helper.hpp" #include "curl-helper.hpp"
@@ -10,6 +11,7 @@
#include <QMainWindow> #include <QMainWindow>
#include <QAction> #include <QAction>
#include <QFileDialog> #include <QFileDialog>
#include <QTextStream>
#include <QDirIterator> #include <QDirIterator>
#include <regex> #include <regex>
#include <filesystem> #include <filesystem>
@@ -135,7 +137,7 @@ bool AdvSceneSwitcher::eventFilter(QObject *obj, QEvent *event)
/****************************************************************************** /******************************************************************************
* Saving and loading * Saving and loading
******************************************************************************/ ******************************************************************************/
static void AskForBackup(obs_data_t *obj); static void AskForBackup(const QString &json);
static void SaveSceneSwitcher(obs_data_t *save_data, bool saving, void *) static void SaveSceneSwitcher(obs_data_t *save_data, bool saving, void *)
{ {
@@ -156,16 +158,25 @@ static void SaveSceneSwitcher(obs_data_t *save_data, bool saving, void *)
switcher->Stop(); switcher->Stop();
switcher->m.lock(); switcher->m.lock();
obs_data_t *obj = OBSDataAutoRelease obj =
obs_data_get_obj(save_data, "advanced-scene-switcher"); obs_data_get_obj(save_data, "advanced-scene-switcher");
if (!obj) { if (!obj) {
obj = obs_data_create(); obj = obs_data_create();
} }
if (switcher->VersionChanged(obj, g_GIT_SHA1)) { if (switcher->VersionChanged(obj, g_GIT_SHA1)) {
AskForBackup(obj); auto json = obs_data_get_json(obj);
static QString jsonQString = json ? json : "";
std::thread t([]() {
obs_queue_task(
OBS_TASK_UI,
[](void *) {
AskForBackup(jsonQString);
},
nullptr, false);
});
t.detach();
} }
switcher->LoadSettings(obj); switcher->LoadSettings(obj);
obs_data_release(obj);
switcher->m.unlock(); switcher->m.unlock();
if (!switcher->stop) { if (!switcher->stop) {
@@ -174,12 +185,12 @@ static void SaveSceneSwitcher(obs_data_t *save_data, bool saving, void *)
} }
} }
static void AskForBackup(obs_data_t *obj) static void AskForBackup(const QString &json)
{ {
bool backupSettings = DisplayMessage( const bool backupWasConfirmed = DisplayMessage(
obs_module_text("AdvSceneSwitcher.askBackup"), true); obs_module_text("AdvSceneSwitcher.askBackup"), true, false);
if (!backupSettings) { if (!backupWasConfirmed) {
return; return;
} }
@@ -198,8 +209,8 @@ static void AskForBackup(obs_data_t *obj)
if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) { if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
return; return;
} }
auto out = QTextStream(&file);
obs_data_save_json(obj, file.fileName().toUtf8().constData()); out << json;
} }
/****************************************************************************** /******************************************************************************
@@ -226,7 +237,6 @@ void SwitcherData::Thread()
// if a longer transition is used than the configured check interval // if a longer transition is used than the configured check interval
bool setPrevSceneAfterLinger = false; bool setPrevSceneAfterLinger = false;
bool macroMatch = false; bool macroMatch = false;
macroSceneSwitched = false;
endTime = std::chrono::high_resolution_clock::now(); endTime = std::chrono::high_resolution_clock::now();
auto runTime = auto runTime =
std::chrono::duration_cast<std::chrono::milliseconds>( std::chrono::duration_cast<std::chrono::milliseconds>(
@@ -337,12 +347,6 @@ void SwitcherData::SetPreconditions()
// Process name // Process name
GetForegroundProcessName(currentForegroundProcess); GetForegroundProcessName(currentForegroundProcess);
// Cursor
std::pair<int, int> cursorPos = GetCursorPos();
cursorPosChanged = cursorPos.first != switcher->lastCursorPos.first ||
cursorPos.second != switcher->lastCursorPos.second;
lastCursorPos = GetCursorPos();
// Macro // Macro
InvalidateMacroTempVarValues(); InvalidateMacroTempVarValues();
} }
@@ -424,7 +428,7 @@ bool SwitcherData::CheckForMatch(OBSWeakSource &scene,
static void ResetMacros() static void ResetMacros()
{ {
for (auto &m : switcher->macros) { for (auto &m : GetMacros()) {
m->ResetRunCount(); m->ResetRunCount();
m->ResetTimers(); m->ResetTimers();
} }
@@ -467,9 +471,9 @@ void SwitcherData::Stop()
if (th && th->isRunning()) { if (th && th->isRunning()) {
stop = true; stop = true;
cv.notify_all(); cv.notify_all();
abortMacroWait = true; SetMacroAbortWait(true);
macroWaitCv.notify_all(); GetMacroWaitCV().notify_all();
macroTransitionCv.notify_all(); GetMacroTransitionCV().notify_all();
// Not waiting if a dialog was closed is a workaround to avoid // Not waiting if a dialog was closed is a workaround to avoid
// deadlocks when a variable input dialog is opened while Stop() // deadlocks when a variable input dialog is opened while Stop()
@@ -542,9 +546,6 @@ extern "C" void FreeSceneSwitcher()
static void handleSceneChange() static void handleSceneChange()
{ {
switcher->lastSceneChangeTime =
std::chrono::high_resolution_clock::now();
// Stop waiting if scene was changed // Stop waiting if scene was changed
if (switcher->SceneChangedDuringWait()) { if (switcher->SceneChangedDuringWait()) {
switcher->cv.notify_one(); switcher->cv.notify_one();
@@ -611,28 +612,9 @@ static void handlePeviewSceneChange()
} }
} }
static void setReplayBufferSaved() static void handleTransitionEnd()
{ {
switcher->replayBufferSaved = true; GetMacroTransitionCV().notify_all();
}
static void setTranstionEnd()
{
switcher->lastTransitionEndTime =
std::chrono::high_resolution_clock::now();
switcher->macroTransitionCv.notify_all();
}
static void setStreamStarting()
{
switcher->lastStreamStartingTime =
std::chrono::high_resolution_clock::now();
}
static void setStreamStopping()
{
switcher->lastStreamStoppingTime =
std::chrono::high_resolution_clock::now();
} }
static void handleShutdown() static void handleShutdown()
@@ -641,10 +623,10 @@ static void handleShutdown()
return; return;
} }
switcher->obsIsShuttingDown = true; switcher->obsIsShuttingDown = true;
if (switcher->shutdownConditionCount) { if (ShutdownCheckIsNecessary()) {
switcher->Stop(); switcher->Stop();
switcher->CheckMacros(); CheckMacros();
switcher->RunMacros(); RunMacros();
// Unfortunately this will not work as OBS will now allow saving // Unfortunately this will not work as OBS will now allow saving
// the scene collection data at this point, So any OBS specific // the scene collection data at this point, So any OBS specific
@@ -701,19 +683,8 @@ static void OBSEvent(enum obs_frontend_event event, void *switcher)
case OBS_FRONTEND_EVENT_STREAMING_STOPPED: case OBS_FRONTEND_EVENT_STREAMING_STOPPED:
resetLiveTime(); resetLiveTime();
break; break;
#if LIBOBS_API_VER >= MAKE_SEMANTIC_VERSION(26, 0, 0)
case OBS_FRONTEND_EVENT_REPLAY_BUFFER_SAVED:
setReplayBufferSaved();
break;
#endif
case OBS_FRONTEND_EVENT_TRANSITION_STOPPED: case OBS_FRONTEND_EVENT_TRANSITION_STOPPED:
setTranstionEnd(); handleTransitionEnd();
break;
case OBS_FRONTEND_EVENT_STREAMING_STARTING:
setStreamStarting();
break;
case OBS_FRONTEND_EVENT_STREAMING_STOPPING:
setStreamStopping();
break; break;
#if LIBOBS_API_VER >= MAKE_SEMANTIC_VERSION(27, 2, 0) #if LIBOBS_API_VER >= MAKE_SEMANTIC_VERSION(27, 2, 0)
case OBS_FRONTEND_EVENT_SCENE_COLLECTION_CHANGING: case OBS_FRONTEND_EVENT_SCENE_COLLECTION_CHANGING:

View File

@@ -3,6 +3,7 @@
#include "status-control.hpp" #include "status-control.hpp"
#include "file-selection.hpp" #include "file-selection.hpp"
#include "filter-combo-box.hpp" #include "filter-combo-box.hpp"
#include "variable.hpp"
#include "utility.hpp" #include "utility.hpp"
#include "version.h" #include "version.h"
@@ -512,6 +513,7 @@ void SwitcherData::LoadSettings(obs_data_t *obj)
} }
LoadMacros(obj); LoadMacros(obj);
LoadGlobalMacroProperties(obj);
loadWindowTitleSwitches(obj); loadWindowTitleSwitches(obj);
loadScreenRegionSwitches(obj); loadScreenRegionSwitches(obj);
loadPauseSwitches(obj); loadPauseSwitches(obj);
@@ -548,6 +550,7 @@ void SwitcherData::SaveSettings(obs_data_t *obj)
saveSceneGroups(obj); saveSceneGroups(obj);
SaveMacros(obj); SaveMacros(obj);
SaveGlobalMacroProperties(obj);
SaveVariables(obj); SaveVariables(obj);
saveWindowTitleSwitches(obj); saveWindowTitleSwitches(obj);
saveScreenRegionSwitches(obj); saveScreenRegionSwitches(obj);

View File

@@ -160,7 +160,9 @@ enum class HotkeyType {
Key_NumpadMultiply, Key_NumpadMultiply,
Key_NumpadDivide, Key_NumpadDivide,
Key_NumpadDecimal, Key_NumpadDecimal,
Key_NumpadEnter Key_NumpadEnter,
Key_Tilde,
}; };
} // namespace advss } // namespace advss

View File

@@ -12,6 +12,11 @@ namespace advss {
static QMetaObject::Connection addPulse; static QMetaObject::Connection addPulse;
SceneGroupEditWidget *typeEdit = nullptr; SceneGroupEditWidget *typeEdit = nullptr;
std::deque<SceneGroup> &GetSceneGroups()
{
return switcher->sceneGroups;
}
void SceneGroup::advanceIdx() void SceneGroup::advanceIdx()
{ {
currentIdx++; currentIdx++;

View File

@@ -1,5 +1,5 @@
#pragma once #pragma once
#include <deque>
#include <vector> #include <vector>
#include <QDialog> #include <QDialog>
#include <QLabel> #include <QLabel>
@@ -87,5 +87,6 @@ private:
SceneGroup *GetSceneGroupByName(const char *name); SceneGroup *GetSceneGroupByName(const char *name);
SceneGroup *GetSceneGroupByQString(const QString &name); SceneGroup *GetSceneGroupByQString(const QString &name);
std::deque<SceneGroup> &GetSceneGroups();
} // namespace advss } // namespace advss

View File

@@ -177,17 +177,17 @@ static std::string getRemoteData(std::string &url)
{ {
std::string readBuffer; std::string readBuffer;
switcher->curl.SetOpt(CURLOPT_URL, url.c_str()); CurlHelper::SetOpt(CURLOPT_URL, url.c_str());
switcher->curl.SetOpt(CURLOPT_WRITEFUNCTION, WriteCallback); CurlHelper::SetOpt(CURLOPT_WRITEFUNCTION, WriteCallback);
switcher->curl.SetOpt(CURLOPT_WRITEDATA, &readBuffer); CurlHelper::SetOpt(CURLOPT_WRITEDATA, &readBuffer);
// Set timeout to at least one second // Set timeout to at least one second
int timeout = switcher->interval / 1000; int timeout = switcher->interval / 1000;
if (timeout == 0) { if (timeout == 0) {
timeout = 1; timeout = 1;
} }
switcher->curl.SetOpt(CURLOPT_TIMEOUT, 1); CurlHelper::SetOpt(CURLOPT_TIMEOUT, 1);
switcher->curl.Perform(); CurlHelper::Perform();
return readBuffer; return readBuffer;
} }
@@ -422,7 +422,7 @@ void AdvSceneSwitcher::SetupFileTab()
obs_module_text("AdvSceneSwitcher.fileTab.remoteFileWarning2")); obs_module_text("AdvSceneSwitcher.fileTab.remoteFileWarning2"));
ui->remoteFileWarningLabel->hide(); ui->remoteFileWarningLabel->hide();
if (switcher->curl.Initialized()) { if (CurlHelper::Initialized()) {
ui->libcurlWarning->setVisible(false); ui->libcurlWarning->setVisible(false);
} }

View File

@@ -555,6 +555,9 @@ static std::unordered_map<HotkeyType, long> keyTable = {
{HotkeyType::Key_8, XK_8}, {HotkeyType::Key_8, XK_8},
{HotkeyType::Key_9, XK_9}, {HotkeyType::Key_9, XK_9},
// Other
{HotkeyType::Key_Tilde, XK_asciitilde},
{HotkeyType::Key_F1, XK_F1}, {HotkeyType::Key_F1, XK_F1},
{HotkeyType::Key_F2, XK_F2}, {HotkeyType::Key_F2, XK_F2},
{HotkeyType::Key_F3, XK_F3}, {HotkeyType::Key_F3, XK_F3},

View File

@@ -1,5 +1,5 @@
#include "macro-action-audio.hpp" #include "macro-action-audio.hpp"
#include "switcher-data.hpp" #include "macro.hpp"
#include "utility.hpp" #include "utility.hpp"
namespace advss { namespace advss {
@@ -36,6 +36,16 @@ const static std::map<MacroActionAudio::FadeType, std::string> fadeTypes = {
"AdvSceneSwitcher.action.audio.fade.type.rate"}, "AdvSceneSwitcher.action.audio.fade.type.rate"},
}; };
namespace {
struct FadeInfo {
std::atomic_bool active = {false};
std::atomic_int id = {0};
};
}
static FadeInfo masterAudioFade;
static std::unordered_map<std::string, FadeInfo> audioFades;
constexpr auto fadeInterval = std::chrono::milliseconds(100); constexpr auto fadeInterval = std::chrono::milliseconds(100);
constexpr float minFade = 0.000001f; constexpr float minFade = 0.000001f;
@@ -58,10 +68,9 @@ auto set_master_volume = obs_set_master_volume;
void MacroActionAudio::SetFadeActive(bool value) void MacroActionAudio::SetFadeActive(bool value)
{ {
if (_action == Action::SOURCE_VOLUME) { if (_action == Action::SOURCE_VOLUME) {
switcher->activeAudioFades[_audioSource.ToString()].active = audioFades[_audioSource.ToString()].active = value;
value;
} else { } else {
switcher->masterAudioFade.active = value; masterAudioFade.active = value;
} }
} }
@@ -69,14 +78,13 @@ bool MacroActionAudio::FadeActive()
{ {
bool active = true; bool active = true;
if (_action == Action::SOURCE_VOLUME) { if (_action == Action::SOURCE_VOLUME) {
auto it = switcher->activeAudioFades.find( auto it = audioFades.find(_audioSource.ToString());
_audioSource.ToString()); if (it == audioFades.end()) {
if (it == switcher->activeAudioFades.end()) {
return false; return false;
} }
active = it->second.active; active = it->second.active;
} else { } else {
active = switcher->masterAudioFade.active; active = masterAudioFade.active;
} }
return active; return active;
@@ -86,14 +94,13 @@ std::atomic_int *MacroActionAudio::GetFadeIdPtr()
{ {
if (_action == Action::SOURCE_VOLUME) { if (_action == Action::SOURCE_VOLUME) {
auto it = switcher->activeAudioFades.find( auto it = audioFades.find(_audioSource.ToString());
_audioSource.ToString()); if (it == audioFades.end()) {
if (it == switcher->activeAudioFades.end()) {
return nullptr; return nullptr;
} }
return &it->second.id; return &it->second.id;
} }
return &switcher->masterAudioFade.id; return &masterAudioFade.id;
} }
void MacroActionAudio::SetVolume(float vol) void MacroActionAudio::SetVolume(float vol)

View File

@@ -1,6 +1,5 @@
#include "macro-action-clipboard.hpp" #include "macro-action-clipboard.hpp"
#include "curl-helper.hpp" #include "curl-helper.hpp"
#include "switcher-data.hpp"
#include <QApplication> #include <QApplication>
#include <QClipboard> #include <QClipboard>
@@ -34,17 +33,17 @@ static std::optional<QImage> getImageFromUrl(const char *url)
{ {
QByteArray response; QByteArray response;
switcher->curl.SetOpt(CURLOPT_URL, url); CurlHelper::SetOpt(CURLOPT_URL, url);
switcher->curl.SetOpt(CURLOPT_HTTPGET, 1L); CurlHelper::SetOpt(CURLOPT_HTTPGET, 1L);
switcher->curl.SetOpt(CURLOPT_TIMEOUT_MS, 30000); CurlHelper::SetOpt(CURLOPT_TIMEOUT_MS, 30000);
switcher->curl.SetOpt(CURLOPT_WRITEFUNCTION, writeCallback); CurlHelper::SetOpt(CURLOPT_WRITEFUNCTION, writeCallback);
switcher->curl.SetOpt(CURLOPT_WRITEDATA, &response); CurlHelper::SetOpt(CURLOPT_WRITEDATA, &response);
auto code = switcher->curl.Perform(); auto code = CurlHelper::Perform();
if (code != CURLE_OK) { if (code != CURLE_OK) {
blog(LOG_WARNING, blog(LOG_WARNING,
"Retrieving image failed in %s with error: %s", __func__, "Retrieving image failed in %s with error: %s", __func__,
switcher->curl.GetError(code)); CurlHelper::GetError(code));
return {}; return {};
} }

View File

@@ -1,6 +1,8 @@
#include "advanced-scene-switcher.hpp"
#include "switcher-data.hpp"
#include "macro-action-edit.hpp" #include "macro-action-edit.hpp"
#include "macro-helpers.hpp"
#include "macro.hpp"
#include "macro-properties.hpp"
#include "advanced-scene-switcher.hpp"
#include "macro-action-scene-switch.hpp" #include "macro-action-scene-switch.hpp"
#include "section.hpp" #include "section.hpp"
#include "switch-button.hpp" #include "switch-button.hpp"
@@ -10,61 +12,9 @@
namespace advss { namespace advss {
std::map<std::string, MacroActionInfo> &MacroActionFactory::GetMap()
{
static std::map<std::string, MacroActionInfo> _methods;
return _methods;
}
bool MacroActionFactory::Register(const std::string &id, MacroActionInfo info)
{
if (auto it = GetMap().find(id); it == GetMap().end()) {
GetMap()[id] = info;
return true;
}
return false;
}
std::shared_ptr<MacroAction> MacroActionFactory::Create(const std::string &id,
Macro *m)
{
if (auto it = GetMap().find(id); it != GetMap().end())
return it->second._createFunc(m);
return nullptr;
}
QWidget *MacroActionFactory::CreateWidget(const std::string &id,
QWidget *parent,
std::shared_ptr<MacroAction> action)
{
if (auto it = GetMap().find(id); it != GetMap().end())
return it->second._createWidgetFunc(parent, action);
return nullptr;
}
std::string MacroActionFactory::GetActionName(const std::string &id)
{
if (auto it = GetMap().find(id); it != GetMap().end()) {
return it->second._name;
}
return "unknown action";
}
std::string MacroActionFactory::GetIdByName(const QString &name)
{
for (auto it : GetMap()) {
if (name == obs_module_text(it.second._name.c_str())) {
return it.first;
}
}
return "";
}
static inline void populateActionSelection(QComboBox *list) static inline void populateActionSelection(QComboBox *list)
{ {
for (auto &[_, action] : MacroActionFactory::GetActionTypes()) { for (const auto &[_, action] : MacroActionFactory::GetActionTypes()) {
QString entry(obs_module_text(action._name.c_str())); QString entry(obs_module_text(action._name.c_str()));
if (list->findText(entry) == -1) { if (list->findText(entry) == -1) {
list->addItem(entry); list->addItem(entry);
@@ -80,7 +30,8 @@ static inline void populateActionSelection(QComboBox *list)
MacroActionEdit::MacroActionEdit(QWidget *parent, MacroActionEdit::MacroActionEdit(QWidget *parent,
std::shared_ptr<MacroAction> *entryData, std::shared_ptr<MacroAction> *entryData,
const std::string &id) const std::string &id)
: MacroSegmentEdit(switcher->macroProperties._highlightActions, parent), : MacroSegmentEdit(GetGlobalMacroProperties()._highlightActions,
parent),
_actionSelection(new FilterComboBox()), _actionSelection(new FilterComboBox()),
_enable(new SwitchButton()), _enable(new SwitchButton()),
_entryData(entryData) _entryData(entryData)
@@ -134,7 +85,7 @@ void MacroActionEdit::ActionSelectionChanged(const QString &text)
auto idx = _entryData->get()->GetIndex(); auto idx = _entryData->get()->GetIndex();
auto macro = _entryData->get()->GetMacro(); auto macro = _entryData->get()->GetMacro();
{ {
std::lock_guard<std::mutex> lock(switcher->m); auto lock = LockContext();
_entryData->reset(); _entryData->reset();
*_entryData = MacroActionFactory::Create(id, macro); *_entryData = MacroActionFactory::Create(id, macro);
(*_entryData)->SetIndex(idx); (*_entryData)->SetIndex(idx);
@@ -185,7 +136,7 @@ void MacroActionEdit::ActionEnableChanged(bool value)
return; return;
} }
std::lock_guard<std::mutex> lock(switcher->m); auto lock = LockContext();
(*_entryData)->SetEnabled(value); (*_entryData)->SetEnabled(value);
SetDisableEffect(!value); SetDisableEffect(!value);
} }
@@ -229,7 +180,7 @@ void AdvSceneSwitcher::AddMacroAction(int idx)
id = temp.GetId(); id = temp.GetId();
} }
{ {
std::lock_guard<std::mutex> lock(switcher->m); auto lock = LockContext();
macro->Actions().emplace( macro->Actions().emplace(
macro->Actions().begin() + idx, macro->Actions().begin() + idx,
MacroActionFactory::Create(id, macro.get())); MacroActionFactory::Create(id, macro.get()));
@@ -280,11 +231,11 @@ void AdvSceneSwitcher::RemoveMacroAction(int idx)
} }
{ {
std::lock_guard<std::mutex> lock(switcher->m); auto lock = LockContext();
ui->actionsList->Remove(idx); ui->actionsList->Remove(idx);
macro->Actions().erase(macro->Actions().begin() + idx); macro->Actions().erase(macro->Actions().begin() + idx);
switcher->abortMacroWait = true; SetMacroAbortWait(true);
switcher->macroWaitCv.notify_all(); GetMacroWaitCV().notify_all();
macro->UpdateActionIndices(); macro->UpdateActionIndices();
SetActionData(*macro); SetActionData(*macro);
} }
@@ -425,7 +376,7 @@ void AdvSceneSwitcher::SwapActions(Macro *m, int pos1, int pos2)
std::swap(pos1, pos2); std::swap(pos1, pos2);
} }
std::lock_guard<std::mutex> lock(switcher->m); auto lock = LockContext();
iter_swap(m->Actions().begin() + pos1, m->Actions().begin() + pos2); iter_swap(m->Actions().begin() + pos1, m->Actions().begin() + pos2);
m->UpdateActionIndices(); m->UpdateActionIndices();
auto widget1 = static_cast<MacroActionEdit *>( auto widget1 = static_cast<MacroActionEdit *>(
@@ -485,7 +436,7 @@ void AdvSceneSwitcher::MacroElseActionReorder(int to, int from)
return; return;
} }
{ {
std::lock_guard<std::mutex> lock(switcher->m); auto lock = LockContext();
auto action = macro->ElseActions().at(from); auto action = macro->ElseActions().at(from);
macro->ElseActions().erase(macro->ElseActions().begin() + from); macro->ElseActions().erase(macro->ElseActions().begin() + from);
macro->ElseActions().insert(macro->ElseActions().begin() + to, macro->ElseActions().insert(macro->ElseActions().begin() + to,
@@ -518,7 +469,7 @@ void AdvSceneSwitcher::AddMacroElseAction(int idx)
id = temp.GetId(); id = temp.GetId();
} }
{ {
std::lock_guard<std::mutex> lock(switcher->m); auto lock = LockContext();
macro->ElseActions().emplace( macro->ElseActions().emplace(
macro->ElseActions().begin() + idx, macro->ElseActions().begin() + idx,
MacroActionFactory::Create(id, macro.get())); MacroActionFactory::Create(id, macro.get()));
@@ -550,11 +501,11 @@ void AdvSceneSwitcher::RemoveMacroElseAction(int idx)
} }
{ {
std::lock_guard<std::mutex> lock(switcher->m); auto lock = LockContext();
ui->elseActionsList->Remove(idx); ui->elseActionsList->Remove(idx);
macro->ElseActions().erase(macro->ElseActions().begin() + idx); macro->ElseActions().erase(macro->ElseActions().begin() + idx);
switcher->abortMacroWait = true; SetMacroAbortWait(true);
switcher->macroWaitCv.notify_all(); GetMacroWaitCV().notify_all();
macro->UpdateElseActionIndices(); macro->UpdateElseActionIndices();
SetActionData(*macro); SetActionData(*macro);
} }
@@ -572,7 +523,7 @@ void AdvSceneSwitcher::SwapElseActions(Macro *m, int pos1, int pos2)
std::swap(pos1, pos2); std::swap(pos1, pos2);
} }
std::lock_guard<std::mutex> lock(switcher->m); auto lock = LockContext();
iter_swap(m->ElseActions().begin() + pos1, iter_swap(m->ElseActions().begin() + pos1,
m->ElseActions().begin() + pos2); m->ElseActions().begin() + pos2);
m->UpdateElseActionIndices(); m->UpdateElseActionIndices();
@@ -635,7 +586,7 @@ void AdvSceneSwitcher::MacroActionReorder(int to, int from)
return; return;
} }
{ {
std::lock_guard<std::mutex> lock(switcher->m); auto lock = LockContext();
auto action = macro->Actions().at(from); auto action = macro->Actions().at(from);
macro->Actions().erase(macro->Actions().begin() + from); macro->Actions().erase(macro->Actions().begin() + from);
macro->Actions().insert(macro->Actions().begin() + to, action); macro->Actions().insert(macro->Actions().begin() + to, action);

View File

@@ -1,5 +1,6 @@
#pragma once #pragma once
#include "macro-action.hpp" #include "macro-action.hpp"
#include "macro-action-factory.hpp"
#include "filter-combo-box.hpp" #include "filter-combo-box.hpp"
#include <memory> #include <memory>
@@ -8,32 +9,6 @@ namespace advss {
class SwitchButton; class SwitchButton;
struct MacroActionInfo {
using TCreateMethod = std::shared_ptr<MacroAction> (*)(Macro *m);
using TCreateWidgetMethod = QWidget *(*)(QWidget *parent,
std::shared_ptr<MacroAction>);
TCreateMethod _createFunc = nullptr;
TCreateWidgetMethod _createWidgetFunc = nullptr;
std::string _name;
};
class MacroActionFactory {
public:
MacroActionFactory() = delete;
static bool Register(const std::string &id, MacroActionInfo);
static std::shared_ptr<MacroAction> Create(const std::string &id,
Macro *m);
static QWidget *CreateWidget(const std::string &id, QWidget *parent,
std::shared_ptr<MacroAction> action);
static auto GetActionTypes() { return GetMap(); }
static std::string GetActionName(const std::string &id);
static std::string GetIdByName(const QString &name);
private:
static std::map<std::string, MacroActionInfo> &GetMap();
};
class MacroActionEdit : public MacroSegmentEdit { class MacroActionEdit : public MacroSegmentEdit {
Q_OBJECT Q_OBJECT

View File

@@ -0,0 +1,57 @@
#include "macro-action-factory.hpp"
namespace advss {
std::map<std::string, MacroActionInfo> &MacroActionFactory::GetMap()
{
static std::map<std::string, MacroActionInfo> _methods;
return _methods;
}
bool MacroActionFactory::Register(const std::string &id, MacroActionInfo info)
{
if (auto it = GetMap().find(id); it == GetMap().end()) {
GetMap()[id] = info;
return true;
}
return false;
}
std::shared_ptr<MacroAction> MacroActionFactory::Create(const std::string &id,
Macro *m)
{
if (auto it = GetMap().find(id); it != GetMap().end())
return it->second._create(m);
return nullptr;
}
QWidget *MacroActionFactory::CreateWidget(const std::string &id,
QWidget *parent,
std::shared_ptr<MacroAction> action)
{
if (auto it = GetMap().find(id); it != GetMap().end())
return it->second._createWidget(parent, action);
return nullptr;
}
std::string MacroActionFactory::GetActionName(const std::string &id)
{
if (auto it = GetMap().find(id); it != GetMap().end()) {
return it->second._name;
}
return "unknown action";
}
std::string MacroActionFactory::GetIdByName(const QString &name)
{
for (auto it : GetMap()) {
if (name == obs_module_text(it.second._name.c_str())) {
return it.first;
}
}
return "";
}
} // namespace advss

View File

@@ -0,0 +1,34 @@
#pragma once
#include "macro-action.hpp"
#include <memory>
namespace advss {
struct MacroActionInfo {
using CreateAction = std::shared_ptr<MacroAction> (*)(Macro *m);
using CreateActionWidget = QWidget *(*)(QWidget *parent,
std::shared_ptr<MacroAction>);
CreateAction _create = nullptr;
CreateActionWidget _createWidget = nullptr;
std::string _name;
};
class MacroActionFactory {
public:
MacroActionFactory() = delete;
static bool Register(const std::string &id, MacroActionInfo);
static std::shared_ptr<MacroAction> Create(const std::string &id,
Macro *m);
static QWidget *CreateWidget(const std::string &id, QWidget *parent,
std::shared_ptr<MacroAction> action);
static auto GetActionTypes() { return GetMap(); }
static std::string GetActionName(const std::string &id);
static std::string GetIdByName(const QString &name);
private:
static std::map<std::string, MacroActionInfo> &GetMap();
};
} // namespace advss

View File

@@ -14,14 +14,14 @@ bool MacroActionHotkey::_registered = MacroActionFactory::Register(
{MacroActionHotkey::Create, MacroActionHotkeyEdit::Create, {MacroActionHotkey::Create, MacroActionHotkeyEdit::Create,
"AdvSceneSwitcher.action.hotkey"}); "AdvSceneSwitcher.action.hotkey"});
const static std::map<MacroActionHotkey::Action, std::string> actionTypes = { static const std::map<MacroActionHotkey::Action, std::string> actionTypes = {
{MacroActionHotkey::Action::OBS_HOTKEY, {MacroActionHotkey::Action::OBS_HOTKEY,
"AdvSceneSwitcher.action.hotkey.type.obs"}, "AdvSceneSwitcher.action.hotkey.type.obs"},
{MacroActionHotkey::Action::CUSTOM, {MacroActionHotkey::Action::CUSTOM,
"AdvSceneSwitcher.action.hotkey.type.custom"}, "AdvSceneSwitcher.action.hotkey.type.custom"},
}; };
const static std::map<obs_hotkey_registerer_type, std::string> hotkeyTypes = { static const std::map<obs_hotkey_registerer_type, std::string> hotkeyTypes = {
{OBS_HOTKEY_REGISTERER_FRONTEND, {OBS_HOTKEY_REGISTERER_FRONTEND,
"AdvSceneSwitcher.action.hotkey.type.obs.type.frontend"}, "AdvSceneSwitcher.action.hotkey.type.obs.type.frontend"},
{OBS_HOTKEY_REGISTERER_SOURCE, {OBS_HOTKEY_REGISTERER_SOURCE,
@@ -34,7 +34,7 @@ const static std::map<obs_hotkey_registerer_type, std::string> hotkeyTypes = {
"AdvSceneSwitcher.action.hotkey.type.obs.type.service"}, "AdvSceneSwitcher.action.hotkey.type.obs.type.service"},
}; };
static std::unordered_map<HotkeyType, long> keyTable = { static const std::unordered_map<HotkeyType, long> keyTable = {
// Chars // Chars
{HotkeyType::Key_A, OBS_KEY_A}, {HotkeyType::Key_A, OBS_KEY_A},
{HotkeyType::Key_B, OBS_KEY_B}, {HotkeyType::Key_B, OBS_KEY_B},
@@ -75,6 +75,9 @@ static std::unordered_map<HotkeyType, long> keyTable = {
{HotkeyType::Key_8, OBS_KEY_8}, {HotkeyType::Key_8, OBS_KEY_8},
{HotkeyType::Key_9, OBS_KEY_9}, {HotkeyType::Key_9, OBS_KEY_9},
// Other
{HotkeyType::Key_Tilde, OBS_KEY_ASCIITILDE},
{HotkeyType::Key_F1, OBS_KEY_F1}, {HotkeyType::Key_F1, OBS_KEY_F1},
{HotkeyType::Key_F2, OBS_KEY_F2}, {HotkeyType::Key_F2, OBS_KEY_F2},
{HotkeyType::Key_F3, OBS_KEY_F3}, {HotkeyType::Key_F3, OBS_KEY_F3},
@@ -525,6 +528,7 @@ static inline void populateKeySelection(QComboBox *list)
list->addItem("NumpadDivide"); list->addItem("NumpadDivide");
list->addItem("NumpadDecimal"); list->addItem("NumpadDecimal");
list->addItem("NumpadEnter"); list->addItem("NumpadEnter");
list->addItem("Tilde");
} }
static void populateActionSelection(QComboBox *list) static void populateActionSelection(QComboBox *list)

View File

@@ -1,5 +1,4 @@
#include "macro-action-http.hpp" #include "macro-action-http.hpp"
#include "switcher-data.hpp"
#include "utility.hpp" #include "utility.hpp"
#include "curl-helper.hpp" #include "curl-helper.hpp"
@@ -36,44 +35,44 @@ void MacroActionHttp::SetupHeaders()
} }
struct curl_slist *headers = nullptr; struct curl_slist *headers = nullptr;
for (auto &header : _headers) { for (auto &header : _headers) {
headers = switcher->curl.SlistAppend(headers, header.c_str()); headers = CurlHelper::SlistAppend(headers, header.c_str());
} }
if (!_headers.empty()) { if (!_headers.empty()) {
switcher->curl.SetOpt(CURLOPT_HTTPHEADER, headers); CurlHelper::SetOpt(CURLOPT_HTTPHEADER, headers);
} }
} }
void MacroActionHttp::Get() void MacroActionHttp::Get()
{ {
switcher->curl.SetOpt(CURLOPT_URL, _url.c_str()); CurlHelper::SetOpt(CURLOPT_URL, _url.c_str());
switcher->curl.SetOpt(CURLOPT_HTTPGET, 1L); CurlHelper::SetOpt(CURLOPT_HTTPGET, 1L);
switcher->curl.SetOpt(CURLOPT_TIMEOUT_MS, _timeout.Milliseconds()); CurlHelper::SetOpt(CURLOPT_TIMEOUT_MS, _timeout.Milliseconds());
SetupHeaders(); SetupHeaders();
std::string response; std::string response;
if (IsReferencedInVars()) { if (IsReferencedInVars()) {
switcher->curl.SetOpt(CURLOPT_WRITEFUNCTION, WriteCB); CurlHelper::SetOpt(CURLOPT_WRITEFUNCTION, WriteCB);
} else { } else {
switcher->curl.SetOpt(CURLOPT_WRITEFUNCTION, DropCB); CurlHelper::SetOpt(CURLOPT_WRITEFUNCTION, DropCB);
} }
switcher->curl.SetOpt(CURLOPT_WRITEDATA, &response); CurlHelper::SetOpt(CURLOPT_WRITEDATA, &response);
switcher->curl.Perform(); CurlHelper::Perform();
SetVariableValue(response); SetVariableValue(response);
} }
void MacroActionHttp::Post() void MacroActionHttp::Post()
{ {
switcher->curl.SetOpt(CURLOPT_URL, _url.c_str()); CurlHelper::SetOpt(CURLOPT_URL, _url.c_str());
switcher->curl.SetOpt(CURLOPT_POSTFIELDS, _data.c_str()); CurlHelper::SetOpt(CURLOPT_POSTFIELDS, _data.c_str());
switcher->curl.SetOpt(CURLOPT_TIMEOUT_MS, _timeout.Milliseconds()); CurlHelper::SetOpt(CURLOPT_TIMEOUT_MS, _timeout.Milliseconds());
SetupHeaders(); SetupHeaders();
switcher->curl.Perform(); CurlHelper::Perform();
} }
bool MacroActionHttp::PerformAction() bool MacroActionHttp::PerformAction()
{ {
if (!switcher->curl.Initialized()) { if (!CurlHelper::Initialized()) {
blog(LOG_WARNING, blog(LOG_WARNING,
"cannot perform http action (curl not found)"); "cannot perform http action (curl not found)");
return true; return true;

View File

@@ -1,5 +1,5 @@
#include "macro-action-plugin-state.hpp" #include "macro-action-plugin-state.hpp"
#include "plugin-state-helper.hpp" #include "plugin-state-helpers.hpp"
#include "utility.hpp" #include "utility.hpp"
#include <thread> #include <thread>

View File

@@ -14,9 +14,14 @@ bool MacroActionRun::_registered = MacroActionFactory::Register(
bool MacroActionRun::PerformAction() bool MacroActionRun::PerformAction()
{ {
bool procStarted = QProcess::startDetached( if (_wait) {
QString::fromStdString(_procConfig.Path()), _procConfig.Args(), _procConfig.StartProcessAndWait(_timeout.Milliseconds());
QString::fromStdString(_procConfig.WorkingDir())); return true;
}
bool procStarted = _procConfig.StartProcessDetached();
// Fall back to using default application to open given file
if (!procStarted && _procConfig.Args().empty()) { if (!procStarted && _procConfig.Args().empty()) {
vblog(LOG_INFO, "run \"%s\" using QDesktopServices", vblog(LOG_INFO, "run \"%s\" using QDesktopServices",
_procConfig.Path().c_str()); _procConfig.Path().c_str());
@@ -35,6 +40,9 @@ bool MacroActionRun::Save(obs_data_t *obj) const
{ {
MacroAction::Save(obj); MacroAction::Save(obj);
_procConfig.Save(obj); _procConfig.Save(obj);
_timeout.Save(obj);
obs_data_set_bool(obj, "wait", _wait);
obs_data_set_int(obj, "version", 1);
return true; return true;
} }
@@ -42,6 +50,12 @@ bool MacroActionRun::Load(obs_data_t *obj)
{ {
MacroAction::Load(obj); MacroAction::Load(obj);
_procConfig.Load(obj); _procConfig.Load(obj);
// TODO: Remove this fallback in a future version
if (!obs_data_has_user_value(obj, "version")) {
return true;
}
_timeout.Load(obj);
_wait = obs_data_get_bool(obj, "wait");
return true; return true;
} }
@@ -52,14 +66,30 @@ std::string MacroActionRun::GetShortDesc() const
MacroActionRunEdit::MacroActionRunEdit( MacroActionRunEdit::MacroActionRunEdit(
QWidget *parent, std::shared_ptr<MacroActionRun> entryData) QWidget *parent, std::shared_ptr<MacroActionRun> entryData)
: QWidget(parent), _procConfig(new ProcessConfigEdit(this)) : QWidget(parent),
_procConfig(new ProcessConfigEdit(this)),
_waitLayout(new QHBoxLayout()),
_wait(new QCheckBox()),
_timeout(new DurationSelection(this, true, 0.1))
{ {
QWidget::connect(_procConfig, QWidget::connect(_procConfig,
SIGNAL(ConfigChanged(const ProcessConfig &)), this, SIGNAL(ConfigChanged(const ProcessConfig &)), this,
SLOT(ProcessConfigChanged(const ProcessConfig &))); SLOT(ProcessConfigChanged(const ProcessConfig &)));
QWidget::connect(_procConfig, SIGNAL(AdvancedSettingsEnabled()), this,
SLOT(ProcessConfigAdvancedSettingsShown()));
QWidget::connect(_wait, SIGNAL(stateChanged(int)), this,
SLOT(WaitChanged(int)));
QWidget::connect(_timeout, SIGNAL(DurationChanged(const Duration &)),
this, SLOT(TimeoutChanged(const Duration &)));
auto *layout = new QVBoxLayout; PlaceWidgets(obs_module_text("AdvSceneSwitcher.action.run.entry.wait"),
_waitLayout,
{{"{{wait}}", _wait}, {"{{timeout}}", _timeout}});
SetLayoutVisible(_waitLayout, false);
auto layout = new QVBoxLayout;
layout->addWidget(_procConfig); layout->addWidget(_procConfig);
layout->addLayout(_waitLayout);
setLayout(layout); setLayout(layout);
_entryData = entryData; _entryData = entryData;
@@ -73,6 +103,31 @@ void MacroActionRunEdit::UpdateEntryData()
return; return;
} }
_procConfig->SetProcessConfig(_entryData->_procConfig); _procConfig->SetProcessConfig(_entryData->_procConfig);
_wait->setChecked(_entryData->_wait);
_timeout->SetDuration(_entryData->_timeout);
}
void MacroActionRunEdit::ProcessConfigAdvancedSettingsShown()
{
SetLayoutVisible(_waitLayout, true);
}
void MacroActionRunEdit::WaitChanged(int value)
{
if (_loading || !_entryData) {
return;
}
auto lock = LockContext();
_entryData->_wait = value;
}
void MacroActionRunEdit::TimeoutChanged(const Duration &timeout)
{
if (_loading || !_entryData) {
return;
}
auto lock = LockContext();
_entryData->_timeout = timeout;
} }
void MacroActionRunEdit::ProcessConfigChanged(const ProcessConfig &conf) void MacroActionRunEdit::ProcessConfigChanged(const ProcessConfig &conf)

View File

@@ -1,6 +1,9 @@
#pragma once #pragma once
#include "macro-action-edit.hpp" #include "macro-action-edit.hpp"
#include "process-config.hpp" #include "process-config.hpp"
#include "duration-control.hpp"
#include <QCheckBox>
namespace advss { namespace advss {
@@ -19,6 +22,8 @@ public:
} }
ProcessConfig _procConfig; ProcessConfig _procConfig;
bool _wait = false;
Duration _timeout = 1;
private: private:
static bool _registered; static bool _registered;
@@ -42,14 +47,19 @@ public:
private slots: private slots:
void ProcessConfigChanged(const ProcessConfig &); void ProcessConfigChanged(const ProcessConfig &);
void ProcessConfigAdvancedSettingsShown();
void WaitChanged(int);
void TimeoutChanged(const Duration &);
signals: signals:
void HeaderInfoChanged(const QString &); void HeaderInfoChanged(const QString &);
protected:
std::shared_ptr<MacroActionRun> _entryData;
private: private:
ProcessConfigEdit *_procConfig; ProcessConfigEdit *_procConfig;
QHBoxLayout *_waitLayout;
QCheckBox *_wait;
DurationSelection *_timeout;
std::shared_ptr<MacroActionRun> _entryData;
bool _loading = true; bool _loading = true;
}; };

View File

@@ -1,5 +1,5 @@
#include "macro-action-scene-collection.hpp" #include "macro-action-scene-collection.hpp"
#include "plugin-state-helper.hpp" #include "plugin-state-helpers.hpp"
#include "utility.hpp" #include "utility.hpp"
namespace advss { namespace advss {

View File

@@ -1,5 +1,7 @@
#include "macro-action-scene-switch.hpp" #include "macro-action-scene-switch.hpp"
#include "switcher-data.hpp" #include "macro-helpers.hpp"
#include "plugin-state-helpers.hpp"
#include "macro.hpp"
#include "scene-switch-helpers.hpp" #include "scene-switch-helpers.hpp"
#include "utility.hpp" #include "utility.hpp"
@@ -7,7 +9,7 @@ namespace advss {
using namespace std::chrono_literals; using namespace std::chrono_literals;
const std::string MacroActionSwitchScene::id = "scene_switch"; const std::string MacroActionSwitchScene::id = GetSceneSwitchActionId().data();
bool MacroActionSwitchScene::_registered = MacroActionFactory::Register( bool MacroActionSwitchScene::_registered = MacroActionFactory::Register(
MacroActionSwitchScene::id, MacroActionSwitchScene::id,
@@ -33,9 +35,9 @@ static void waitForTransitionChange(OBSWeakSource &transition,
} }
bool stillTransitioning = true; bool stillTransitioning = true;
while (stillTransitioning && !switcher->abortMacroWait && while (stillTransitioning && !MacroWaitShouldAbort() &&
!macro->GetStop()) { !macro->GetStop()) {
switcher->macroTransitionCv.wait_for(*lock, time); GetMacroTransitionCV().wait_for(*lock, time);
float t = obs_transition_get_time(source); float t = obs_transition_get_time(source);
stillTransitioning = t < 1.0f && t > 0.0f; stillTransitioning = t < 1.0f && t > 0.0f;
} }
@@ -49,8 +51,8 @@ static void waitForTransitionChangeFixedDuration(
auto time = std::chrono::high_resolution_clock::now() + auto time = std::chrono::high_resolution_clock::now() +
std::chrono::milliseconds(duration); std::chrono::milliseconds(duration);
while (!switcher->abortMacroWait && !macro->GetStop()) { while (!MacroWaitShouldAbort() && !macro->GetStop()) {
if (switcher->macroTransitionCv.wait_until(*lock, time) == if (GetMacroTransitionCV().wait_until(*lock, time) ==
std::cv_status::timeout) { std::cv_status::timeout) {
break; break;
} }
@@ -91,11 +93,16 @@ static OBSWeakSource getOverrideTransition(OBSWeakSource &scene)
return transition; return transition;
} }
static int getExpectedTransitionDuration(OBSWeakSource &scene, OBSWeakSource &t, static int getExpectedTransitionDuration(OBSWeakSource &scene,
OBSWeakSource &transiton_,
double duration) double duration)
{ {
OBSWeakSource transition = t; OBSWeakSource transition = transiton_;
if (!switcher->transitionOverrideOverride) {
// If we are not modifying the transition override, we will have to
// check, if a transition override is set by the user and use its
// duration instead
if (!ShouldModifyTransitionOverrides()) {
auto overrideTransition = getOverrideTransition(scene); auto overrideTransition = getOverrideTransition(scene);
if (overrideTransition) { if (overrideTransition) {
transition = overrideTransition; transition = overrideTransition;
@@ -104,6 +111,7 @@ static int getExpectedTransitionDuration(OBSWeakSource &scene, OBSWeakSource &t,
} }
} }
} }
if (isUsingFixedLengthTransition(transition)) { if (isUsingFixedLengthTransition(transition)) {
return -1; // no API is available to access the fixed duration return -1; // no API is available to access the fixed duration
} }
@@ -118,9 +126,9 @@ bool MacroActionSwitchScene::WaitForTransition(OBSWeakSource &scene,
{ {
const int expectedTransitionDuration = getExpectedTransitionDuration( const int expectedTransitionDuration = getExpectedTransitionDuration(
scene, transition, _duration.Seconds()); scene, transition, _duration.Seconds());
switcher->abortMacroWait = false; SetMacroAbortWait(false);
std::unique_lock<std::mutex> lock(switcher->m); std::unique_lock<std::mutex> lock(*GetMutex());
if (expectedTransitionDuration < 0) { if (expectedTransitionDuration < 0) {
waitForTransitionChange(transition, &lock, GetMacro()); waitForTransitionChange(transition, &lock, GetMacro());
} else { } else {
@@ -128,7 +136,7 @@ bool MacroActionSwitchScene::WaitForTransition(OBSWeakSource &scene,
&lock, GetMacro()); &lock, GetMacro());
} }
return !switcher->abortMacroWait; return !MacroWaitShouldAbort();
} }
bool MacroActionSwitchScene::PerformAction() bool MacroActionSwitchScene::PerformAction()
@@ -145,8 +153,7 @@ bool MacroActionSwitchScene::PerformAction()
auto transition = _transition.GetTransition(); auto transition = _transition.GetTransition();
SwitchScene({scene, transition, (int)(_duration.Milliseconds())}, SwitchScene({scene, transition, (int)(_duration.Milliseconds())},
obs_frontend_preview_program_mode_active()); obs_frontend_preview_program_mode_active());
if (_blockUntilTransitionDone && scene && if (_blockUntilTransitionDone && scene && scene != GetCurrentScene()) {
scene != switcher->currentScene) {
return WaitForTransition(scene, transition); return WaitForTransition(scene, transition);
} }
return true; return true;

View File

@@ -1,6 +1,5 @@
#include "macro-action-variable.hpp" #include "macro-action-variable.hpp"
#include "macro-condition-edit.hpp" #include "macro-condition-edit.hpp"
#include "switcher-data.hpp"
#include "advanced-scene-switcher.hpp" #include "advanced-scene-switcher.hpp"
#include "macro.hpp" #include "macro.hpp"
#include "math-helpers.hpp" #include "math-helpers.hpp"
@@ -51,6 +50,8 @@ const static std::map<MacroActionVariable::Type, std::string> actionTypes = {
"AdvSceneSwitcher.action.variable.type.extractJson"}, "AdvSceneSwitcher.action.variable.type.extractJson"},
{MacroActionVariable::Type::SET_TO_TEMPVAR, {MacroActionVariable::Type::SET_TO_TEMPVAR,
"AdvSceneSwitcher.action.variable.type.setToTempvar"}, "AdvSceneSwitcher.action.variable.type.setToTempvar"},
{MacroActionVariable::Type::SCENE_ITEM_NAME,
"AdvSceneSwitcher.action.variable.type.sceneItemName"},
}; };
static void apppend(Variable &var, const std::string &value) static void apppend(Variable &var, const std::string &value)
@@ -134,6 +135,49 @@ void MacroActionVariable::HandleMathExpression(Variable *var)
var->SetValue(std::get<double>(result)); var->SetValue(std::get<double>(result));
} }
struct GetSceneItemNameHelper {
int curIdx = 0;
int targetIdx = 0;
std::string name = "";
};
static bool getSceneItemAtIdx(obs_scene_t *, obs_sceneitem_t *item, void *ptr)
{
auto data = reinterpret_cast<GetSceneItemNameHelper *>(ptr);
if (obs_sceneitem_is_group(item)) {
obs_scene_t *scene = obs_sceneitem_group_get_scene(item);
obs_scene_enum_items(scene, getSceneItemAtIdx, ptr);
}
if (data->curIdx == data->targetIdx) {
data->name =
obs_source_get_name(obs_sceneitem_get_source(item));
data->curIdx--;
return false;
}
data->curIdx--;
return true;
}
void MacroActionVariable::SetToSceneItemName(Variable *var)
{
auto weakSource = _scene.GetScene();
if (!weakSource) {
var->SetValue("");
return;
}
auto index = _sceneItemIndex.GetValue();
if (index < 1) {
var->SetValue("");
return;
}
auto sceneItemCount = GetSceneItemCount(weakSource);
OBSSourceAutoRelease source = obs_weak_source_get_source(weakSource);
auto scene = obs_scene_from_source(source);
GetSceneItemNameHelper data{sceneItemCount, index};
obs_scene_enum_items(scene, getSceneItemAtIdx, &data);
var->SetValue(data.name);
}
struct AskForInputParams { struct AskForInputParams {
QString prompt; QString prompt;
QString placeholder; QString placeholder;
@@ -268,6 +312,9 @@ bool MacroActionVariable::PerformAction()
var->SetValue(*value); var->SetValue(*value);
return true; return true;
} }
case Type::SCENE_ITEM_NAME:
SetToSceneItemName(var.get());
return true;
} }
return true; return true;
@@ -299,6 +346,7 @@ bool MacroActionVariable::Save(obs_data_t *obj) const
_envVariableName.Save(obj, "environmentVariableName"); _envVariableName.Save(obj, "environmentVariableName");
_scene.Save(obj); _scene.Save(obj);
_tempVar.Save(obj); _tempVar.Save(obj);
_sceneItemIndex.Save(obj, "sceneItemIndex");
return true; return true;
} }
@@ -328,6 +376,7 @@ bool MacroActionVariable::Load(obs_data_t *obj)
_envVariableName.Load(obj, "environmentVariableName"); _envVariableName.Load(obj, "environmentVariableName");
_scene.Load(obj); _scene.Load(obj);
_tempVar.Load(obj, GetMacro()); _tempVar.Load(obj, GetMacro());
_sceneItemIndex.Load(obj, "sceneItemIndex");
return true; return true;
} }
@@ -457,7 +506,8 @@ MacroActionVariableEdit::MacroActionVariableEdit(
_envVariable(new VariableLineEdit(this)), _envVariable(new VariableLineEdit(this)),
_scenes(new SceneSelectionWidget(this, true, false, true, true, _scenes(new SceneSelectionWidget(this, true, false, true, true,
true)), true)),
_tempVars(new TempVariableSelection(this)) _tempVars(new TempVariableSelection(this)),
_sceneItemIndex(new VariableSpinBox())
{ {
_numValue->setMinimum(-9999999999); _numValue->setMinimum(-9999999999);
_numValue->setMaximum(9999999999); _numValue->setMaximum(9999999999);
@@ -475,6 +525,7 @@ MacroActionVariableEdit::MacroActionVariableEdit(
_regexMatchIdx->setSuffix("."); _regexMatchIdx->setSuffix(".");
_inputPrompt->setSizePolicy(QSizePolicy::MinimumExpanding, _inputPrompt->setSizePolicy(QSizePolicy::MinimumExpanding,
QSizePolicy::Preferred); QSizePolicy::Preferred);
_sceneItemIndex->setMinimum(1);
populateTypeSelection(_actions); populateTypeSelection(_actions);
QWidget::connect(_variables, SIGNAL(SelectionChanged(const QString &)), QWidget::connect(_variables, SIGNAL(SelectionChanged(const QString &)),
@@ -523,6 +574,10 @@ MacroActionVariableEdit::MacroActionVariableEdit(
QWidget::connect(_tempVars, QWidget::connect(_tempVars,
SIGNAL(SelectionChanged(const TempVariableRef &)), SIGNAL(SelectionChanged(const TempVariableRef &)),
this, SLOT(SelectionChanged(const TempVariableRef &))); this, SLOT(SelectionChanged(const TempVariableRef &)));
QWidget::connect(
_sceneItemIndex,
SIGNAL(NumberVariableChanged(const NumberVariable<int> &)),
this, SLOT(SceneItemIndexChanged(const NumberVariable<int> &)));
std::unordered_map<std::string, QWidget *> widgetPlaceholders = { std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
{"{{variables}}", _variables}, {"{{variables}}", _variables},
@@ -544,6 +599,7 @@ MacroActionVariableEdit::MacroActionVariableEdit(
{"{{envVariableName}}", _envVariable}, {"{{envVariableName}}", _envVariable},
{"{{scenes}}", _scenes}, {"{{scenes}}", _scenes},
{"{{tempVars}}", _tempVars}, {"{{tempVars}}", _tempVars},
{"{{sceneItemIndex}}", _sceneItemIndex},
}; };
auto entryLayout = new QHBoxLayout; auto entryLayout = new QHBoxLayout;
PlaceWidgets(obs_module_text("AdvSceneSwitcher.action.variable.entry"), PlaceWidgets(obs_module_text("AdvSceneSwitcher.action.variable.entry"),
@@ -638,6 +694,7 @@ void MacroActionVariableEdit::UpdateEntryData()
_envVariable->setText(_entryData->_envVariableName); _envVariable->setText(_entryData->_envVariableName);
_scenes->SetScene(_entryData->_scene); _scenes->SetScene(_entryData->_scene);
_tempVars->SetVariable(_entryData->_tempVar); _tempVars->SetVariable(_entryData->_tempVar);
_sceneItemIndex->SetValue(_entryData->_sceneItemIndex);
SetWidgetVisibility(); SetWidgetVisibility();
} }
@@ -982,6 +1039,17 @@ void MacroActionVariableEdit::SelectionChanged(const TempVariableRef &var)
_entryData->_tempVar = var; _entryData->_tempVar = var;
} }
void MacroActionVariableEdit::SceneItemIndexChanged(
const NumberVariable<int> &value)
{
if (_loading || !_entryData) {
return;
}
auto lock = LockContext();
_entryData->_sceneItemIndex = value;
}
void MacroActionVariableEdit::SetWidgetVisibility() void MacroActionVariableEdit::SetWidgetVisibility()
{ {
if (!_entryData) { if (!_entryData) {
@@ -1058,10 +1126,15 @@ void MacroActionVariableEdit::SetWidgetVisibility()
} }
_envVariable->setVisible(_entryData->_type == _envVariable->setVisible(_entryData->_type ==
MacroActionVariable::Type::ENV_VARIABLE); MacroActionVariable::Type::ENV_VARIABLE);
_scenes->setVisible(_entryData->_type == _scenes->setVisible(
MacroActionVariable::Type::SCENE_ITEM_COUNT); _entryData->_type ==
MacroActionVariable::Type::SCENE_ITEM_COUNT ||
_entryData->_type ==
MacroActionVariable::Type::SCENE_ITEM_NAME);
_tempVars->setVisible(_entryData->_type == _tempVars->setVisible(_entryData->_type ==
MacroActionVariable::Type::SET_TO_TEMPVAR); MacroActionVariable::Type::SET_TO_TEMPVAR);
_sceneItemIndex->setVisible(_entryData->_type ==
MacroActionVariable::Type::SCENE_ITEM_NAME);
adjustSize(); adjustSize();
updateGeometry(); updateGeometry();
} }

View File

@@ -6,6 +6,7 @@
#include "scene-selection.hpp" #include "scene-selection.hpp"
#include "variable-line-edit.hpp" #include "variable-line-edit.hpp"
#include "variable-text-edit.hpp" #include "variable-text-edit.hpp"
#include "variable-spinbox.hpp"
namespace advss { namespace advss {
@@ -44,6 +45,7 @@ public:
STRING_LENGTH, STRING_LENGTH,
EXTRACT_JSON, EXTRACT_JSON,
SET_TO_TEMPVAR, SET_TO_TEMPVAR,
SCENE_ITEM_NAME,
}; };
Type _type = Type::SET_FIXED_VALUE; Type _type = Type::SET_FIXED_VALUE;
@@ -75,6 +77,7 @@ public:
#endif #endif
SceneSelection _scene; SceneSelection _scene;
TempVariableRef _tempVar; TempVariableRef _tempVar;
IntVariable _sceneItemIndex = 1;
private: private:
void DecrementCurrentSegmentVariableRef(); void DecrementCurrentSegmentVariableRef();
@@ -82,6 +85,7 @@ private:
void HandleRegexSubString(Variable *); void HandleRegexSubString(Variable *);
void HandleFindAndReplace(Variable *); void HandleFindAndReplace(Variable *);
void HandleMathExpression(Variable *); void HandleMathExpression(Variable *);
void SetToSceneItemName(Variable *);
std::weak_ptr<MacroSegment> _macroSegment; std::weak_ptr<MacroSegment> _macroSegment;
int _segmentIdxLoadValue = -1; int _segmentIdxLoadValue = -1;
@@ -129,6 +133,7 @@ private slots:
void EnvVariableChanged(); void EnvVariableChanged();
void SceneChanged(const SceneSelection &); void SceneChanged(const SceneSelection &);
void SelectionChanged(const TempVariableRef &var); void SelectionChanged(const TempVariableRef &var);
void SceneItemIndexChanged(const NumberVariable<int> &);
signals: signals:
void HeaderInfoChanged(const QString &); void HeaderInfoChanged(const QString &);
@@ -167,6 +172,7 @@ private:
VariableLineEdit *_envVariable; VariableLineEdit *_envVariable;
SceneSelectionWidget *_scenes; SceneSelectionWidget *_scenes;
TempVariableSelection *_tempVars; TempVariableSelection *_tempVars;
VariableSpinBox *_sceneItemIndex;
std::shared_ptr<MacroActionVariable> _entryData; std::shared_ptr<MacroActionVariable> _entryData;
QTimer _timer; QTimer _timer;

View File

@@ -1,5 +1,7 @@
#include "macro-action-wait.hpp" #include "macro-action-wait.hpp"
#include "switcher-data.hpp" #include "macro-helpers.hpp"
#include "macro.hpp"
#include "sync-helpers.hpp"
#include "utility.hpp" #include "utility.hpp"
#include <random> #include <random>
@@ -24,8 +26,8 @@ static std::default_random_engine re(rd());
static void waitHelper(std::unique_lock<std::mutex> *lock, Macro *macro, static void waitHelper(std::unique_lock<std::mutex> *lock, Macro *macro,
std::chrono::high_resolution_clock::time_point &time) std::chrono::high_resolution_clock::time_point &time)
{ {
while (!switcher->abortMacroWait && !macro->GetStop()) { while (!MacroWaitShouldAbort() && !macro->GetStop()) {
if (switcher->macroWaitCv.wait_until(*lock, time) == if (GetMacroWaitCV().wait_until(*lock, time) ==
std::cv_status::timeout) { std::cv_status::timeout) {
break; break;
} }
@@ -53,11 +55,11 @@ bool MacroActionWait::PerformAction()
auto time = std::chrono::high_resolution_clock::now() + auto time = std::chrono::high_resolution_clock::now() +
std::chrono::milliseconds((int)(sleepDuration * 1000)); std::chrono::milliseconds((int)(sleepDuration * 1000));
switcher->abortMacroWait = false; SetMacroAbortWait(false);
std::unique_lock<std::mutex> lock(switcher->m); std::unique_lock<std::mutex> lock(*GetMutex());
waitHelper(&lock, GetMacro(), time); waitHelper(&lock, GetMacro(), time);
return !switcher->abortMacroWait; return !MacroWaitShouldAbort();
} }
bool MacroActionWait::Save(obs_data_t *obj) const bool MacroActionWait::Save(obs_data_t *obj) const

View File

@@ -62,17 +62,17 @@ MacroConditionAudio::~MacroConditionAudio()
bool MacroConditionAudio::CheckOutputCondition() bool MacroConditionAudio::CheckOutputCondition()
{ {
bool ret = false; bool ret = false;
auto s = obs_weak_source_get_source(_audioSource.GetSource()); OBSSourceAutoRelease source =
obs_weak_source_get_source(_audioSource.GetSource());
// peak will have a value from -60 db to 0 db so we need to scale it
double curVolume = ((double)_peak + 60) * 1.7; double curVolume = ((double)_peak + 60) * 1.7;
switch (_outputCondition) { switch (_outputCondition) {
case OutputCondition::ABOVE: case OutputCondition::ABOVE:
// peak will have a value from -60 db to 0 db
ret = curVolume > _volume; ret = curVolume > _volume;
break; break;
case OutputCondition::BELOW: case OutputCondition::BELOW:
// peak will have a value from -60 db to 0 db
ret = curVolume < _volume; ret = curVolume < _volume;
break; break;
default: default:
@@ -84,21 +84,21 @@ bool MacroConditionAudio::CheckOutputCondition()
// Reset for next check // Reset for next check
_peak = -std::numeric_limits<float>::infinity(); _peak = -std::numeric_limits<float>::infinity();
obs_source_release(s);
if (_audioSource.GetType() == SourceSelection::Type::VARIABLE) { if (_audioSource.GetType() == SourceSelection::Type::VARIABLE) {
ResetVolmeter(); ResetVolmeter();
} }
return ret; return ret && source;
} }
bool MacroConditionAudio::CheckVolumeCondition() bool MacroConditionAudio::CheckVolumeCondition()
{ {
bool ret = false; bool ret = false;
auto s = obs_weak_source_get_source(_audioSource.GetSource()); OBSSourceAutoRelease source =
obs_weak_source_get_source(_audioSource.GetSource());
float curVolume = obs_source_get_volume(s); float curVolume = obs_source_get_volume(source);
bool muted = obs_source_muted(s); bool muted = obs_source_muted(source);
switch (_volumeCondition) { switch (_volumeCondition) {
case VolumeCondition::ABOVE: case VolumeCondition::ABOVE:
@@ -126,10 +126,9 @@ bool MacroConditionAudio::CheckVolumeCondition()
} }
SetTempVarValue("configured_volume", std::to_string(curVolume)); SetTempVarValue("configured_volume", std::to_string(curVolume));
SetTempVarValue("muted", muted ? "true" : "false"); SetTempVarValue("muted", (source && muted) ? "true" : "false");
obs_source_release(s); return ret && source;
return ret;
} }
bool MacroConditionAudio::CheckSyncOffset() bool MacroConditionAudio::CheckSyncOffset()
@@ -139,8 +138,9 @@ bool MacroConditionAudio::CheckSyncOffset()
} }
bool ret = false; bool ret = false;
auto s = obs_weak_source_get_source(_audioSource.GetSource()); OBSSourceAutoRelease source =
auto curOffset = obs_source_get_sync_offset(s) / nsPerMs; obs_weak_source_get_source(_audioSource.GetSource());
auto curOffset = obs_source_get_sync_offset(source) / nsPerMs;
if (_outputCondition == OutputCondition::ABOVE) { if (_outputCondition == OutputCondition::ABOVE) {
ret = curOffset > _syncOffset; ret = curOffset > _syncOffset;
} else { } else {
@@ -148,8 +148,7 @@ bool MacroConditionAudio::CheckSyncOffset()
} }
SetVariableValue(std::to_string(curOffset)); SetVariableValue(std::to_string(curOffset));
SetTempVarValue("sync_offset", std::to_string(curOffset)); SetTempVarValue("sync_offset", std::to_string(curOffset));
obs_source_release(s); return ret && source;
return ret;
} }
bool MacroConditionAudio::CheckMonitor() bool MacroConditionAudio::CheckMonitor()
@@ -159,13 +158,13 @@ bool MacroConditionAudio::CheckMonitor()
} }
bool ret = false; bool ret = false;
auto s = obs_weak_source_get_source(_audioSource.GetSource()); OBSSourceAutoRelease source =
ret = obs_source_get_monitoring_type(s) == _monitorType; obs_weak_source_get_source(_audioSource.GetSource());
ret = obs_source_get_monitoring_type(source) == _monitorType;
SetVariableValue(""); SetVariableValue("");
SetTempVarValue("monitor", SetTempVarValue("monitor",
std::to_string(obs_source_get_monitoring_type(s))); std::to_string(obs_source_get_monitoring_type(source)));
obs_source_release(s); return ret && source;
return ret;
} }
bool MacroConditionAudio::CheckBalance() bool MacroConditionAudio::CheckBalance()
@@ -175,8 +174,9 @@ bool MacroConditionAudio::CheckBalance()
} }
bool ret = false; bool ret = false;
auto s = obs_weak_source_get_source(_audioSource.GetSource()); OBSSourceAutoRelease source =
auto curBalance = obs_source_get_balance_value(s); obs_weak_source_get_source(_audioSource.GetSource());
auto curBalance = obs_source_get_balance_value(source);
if (_outputCondition == OutputCondition::ABOVE) { if (_outputCondition == OutputCondition::ABOVE) {
ret = curBalance > _balance; ret = curBalance > _balance;
} else { } else {
@@ -184,8 +184,7 @@ bool MacroConditionAudio::CheckBalance()
} }
SetVariableValue(std::to_string(curBalance)); SetVariableValue(std::to_string(curBalance));
SetTempVarValue("balance", std::to_string(curBalance)); SetTempVarValue("balance", std::to_string(curBalance));
obs_source_release(s); return ret && source;
return ret;
} }
bool MacroConditionAudio::CheckCondition() bool MacroConditionAudio::CheckCondition()
@@ -239,13 +238,12 @@ obs_volmeter_t *AddVolmeterToSource(MacroConditionAudio *entry,
obs_volmeter_t *volmeter = obs_volmeter_create(OBS_FADER_LOG); obs_volmeter_t *volmeter = obs_volmeter_create(OBS_FADER_LOG);
obs_volmeter_add_callback(volmeter, MacroConditionAudio::SetVolumeLevel, obs_volmeter_add_callback(volmeter, MacroConditionAudio::SetVolumeLevel,
entry); entry);
obs_source_t *as = obs_weak_source_get_source(source); OBSSourceAutoRelease audioSource = obs_weak_source_get_source(source);
if (!obs_volmeter_attach_source(volmeter, as)) { if (!obs_volmeter_attach_source(volmeter, audioSource)) {
const char *name = obs_source_get_name(as); const char *name = obs_source_get_name(audioSource);
blog(LOG_WARNING, "failed to attach volmeter to source %s", blog(LOG_WARNING, "failed to attach volmeter to source %s",
name); name);
} }
obs_source_release(as);
return volmeter; return volmeter;
} }
@@ -455,10 +453,9 @@ MacroConditionAudioEdit::MacroConditionAudioEdit(
void MacroConditionAudioEdit::UpdateVolmeterSource() void MacroConditionAudioEdit::UpdateVolmeterSource()
{ {
delete _volMeter; delete _volMeter;
obs_source_t *soruce = obs_weak_source_get_source( OBSSourceAutoRelease soruce = obs_weak_source_get_source(
_entryData->_audioSource.GetSource()); _entryData->_audioSource.GetSource());
_volMeter = new VolControl(soruce); _volMeter = new VolControl(soruce.Get());
obs_source_release(soruce);
QLayout *layout = this->layout(); QLayout *layout = this->layout();
layout->addWidget(_volMeter); layout->addWidget(_volMeter);

View File

@@ -1,5 +1,4 @@
#include "macro-condition-cursor.hpp" #include "macro-condition-cursor.hpp"
#include "switcher-data.hpp"
#include "platform-funcs.hpp" #include "platform-funcs.hpp"
#include "utility.hpp" #include "utility.hpp"
@@ -61,13 +60,15 @@ bool MacroConditionCursor::CheckCondition()
std::to_string(cursorPos.second)); std::to_string(cursorPos.second));
break; break;
case Condition::MOVING: case Condition::MOVING:
ret = switcher->cursorPosChanged; ret = cursorPos.first != _lastCursorPosition.first ||
cursorPos.second != _lastCursorPosition.second;
break; break;
case Condition::CLICK: case Condition::CLICK:
ret = CheckClick(); ret = CheckClick();
break; break;
} }
_lastCheckTime = std::chrono::high_resolution_clock::now(); _lastCheckTime = std::chrono::high_resolution_clock::now();
_lastCursorPosition = cursorPos;
if (GetVariableValue().empty()) { if (GetVariableValue().empty()) {
SetVariableValue(ret ? "true" : "false"); SetVariableValue(ret ? "true" : "false");

View File

@@ -41,6 +41,7 @@ public:
private: private:
bool CheckClick(); bool CheckClick();
std::chrono::high_resolution_clock::time_point _lastCheckTime{}; std::chrono::high_resolution_clock::time_point _lastCheckTime{};
std::pair<int, int> _lastCursorPosition;
static bool _registered; static bool _registered;
static const std::string id; static const std::string id;

View File

@@ -638,10 +638,24 @@ void MacroConditionDateEdit::AdvancedSettingsToggleClicked()
{ {
auto lock = LockContext(); auto lock = LockContext();
_entryData->_dayOfWeekCheck = !_entryData->_dayOfWeekCheck; _entryData->_dayOfWeekCheck = !_entryData->_dayOfWeekCheck;
_entryData->_condition = MacroConditionDate::Condition::AT;
} }
_condition->setCurrentIndex(0);
_weekCondition->setCurrentIndex(0); switch (_entryData->_condition) {
case MacroConditionDate::Condition::BETWEEN:
case MacroConditionDate::Condition::PATTERN:
_condition->setCurrentIndex(0);
_weekCondition->setCurrentIndex(0);
break;
case MacroConditionDate::Condition::AT:
case MacroConditionDate::Condition::AFTER:
case MacroConditionDate::Condition::BEFORE:
_weekCondition->setCurrentIndex(
static_cast<int>(_entryData->_condition));
_condition->setCurrentIndex(
static_cast<int>(_entryData->_condition));
break;
}
SetWidgetStatus(); SetWidgetStatus();
emit HeaderInfoChanged( emit HeaderInfoChanged(
QString::fromStdString(_entryData->GetShortDesc())); QString::fromStdString(_entryData->GetShortDesc()));

View File

@@ -1,84 +1,24 @@
#include "advanced-scene-switcher.hpp"
#include "switcher-data.hpp"
#include "macro-condition-edit.hpp" #include "macro-condition-edit.hpp"
#include "macro.hpp"
#include "macro-properties.hpp"
#include "advanced-scene-switcher.hpp"
#include "macro-condition-scene.hpp" #include "macro-condition-scene.hpp"
#include "section.hpp" #include "section.hpp"
#include "utility.hpp" #include "utility.hpp"
namespace advss { namespace advss {
std::map<std::string, MacroConditionInfo> &MacroConditionFactory::GetMap()
{
static std::map<std::string, MacroConditionInfo> _methods;
return _methods;
}
bool MacroConditionFactory::Register(const std::string &id,
MacroConditionInfo info)
{
if (auto it = GetMap().find(id); it == GetMap().end()) {
GetMap()[id] = info;
return true;
}
return false;
}
std::shared_ptr<MacroCondition>
MacroConditionFactory::Create(const std::string &id, Macro *m)
{
if (auto it = GetMap().find(id); it != GetMap().end()) {
return it->second._createFunc(m);
}
return nullptr;
}
QWidget *
MacroConditionFactory::CreateWidget(const std::string &id, QWidget *parent,
std::shared_ptr<MacroCondition> cond)
{
if (auto it = GetMap().find(id); it != GetMap().end()) {
return it->second._createWidgetFunc(parent, cond);
}
return nullptr;
}
std::string MacroConditionFactory::GetConditionName(const std::string &id)
{
if (auto it = GetMap().find(id); it != GetMap().end()) {
return it->second._name;
}
return "unknown condition";
}
std::string MacroConditionFactory::GetIdByName(const QString &name)
{
for (auto it : GetMap()) {
if (name == obs_module_text(it.second._name.c_str())) {
return it.first;
}
}
return "";
}
bool MacroConditionFactory::UsesDurationModifier(const std::string &id)
{
if (auto it = GetMap().find(id); it != GetMap().end()) {
return it->second._useDurationModifier;
}
return false;
}
static inline void populateLogicSelection(QComboBox *list, bool root = false) static inline void populateLogicSelection(QComboBox *list, bool root = false)
{ {
if (root) { if (root) {
for (auto entry : MacroCondition::logicTypes) { for (const auto &entry : MacroCondition::logicTypes) {
if (static_cast<int>(entry.first) < logic_root_offset) { if (static_cast<int>(entry.first) < logic_root_offset) {
list->addItem(obs_module_text( list->addItem(obs_module_text(
entry.second._name.c_str())); entry.second._name.c_str()));
} }
} }
} else { } else {
for (auto entry : MacroCondition::logicTypes) { for (const auto &entry : MacroCondition::logicTypes) {
if (static_cast<int>(entry.first) >= if (static_cast<int>(entry.first) >=
logic_root_offset) { logic_root_offset) {
list->addItem(obs_module_text( list->addItem(obs_module_text(
@@ -90,7 +30,7 @@ static inline void populateLogicSelection(QComboBox *list, bool root = false)
static inline void populateConditionSelection(QComboBox *list) static inline void populateConditionSelection(QComboBox *list)
{ {
for (auto &[_, condition] : for (const auto &[_, condition] :
MacroConditionFactory::GetConditionTypes()) { MacroConditionFactory::GetConditionTypes()) {
QString entry(obs_module_text(condition._name.c_str())); QString entry(obs_module_text(condition._name.c_str()));
if (list->findText(entry) == -1) { if (list->findText(entry) == -1) {
@@ -179,7 +119,7 @@ void DurationModifierEdit::Collapse(bool collapse)
MacroConditionEdit::MacroConditionEdit( MacroConditionEdit::MacroConditionEdit(
QWidget *parent, std::shared_ptr<MacroCondition> *entryData, QWidget *parent, std::shared_ptr<MacroCondition> *entryData,
const std::string &id, bool root) const std::string &id, bool root)
: MacroSegmentEdit(switcher->macroProperties._highlightConditions, : MacroSegmentEdit(GetGlobalMacroProperties()._highlightConditions,
parent), parent),
_logicSelection(new QComboBox()), _logicSelection(new QComboBox()),
_conditionSelection(new FilterComboBox()), _conditionSelection(new FilterComboBox()),

View File

@@ -1,38 +1,12 @@
#pragma once #pragma once
#include "macro-condition.hpp" #include "macro-condition.hpp"
#include "macro-condition-factory.hpp"
#include "filter-combo-box.hpp" #include "filter-combo-box.hpp"
#include <memory> #include <memory>
namespace advss { namespace advss {
struct MacroConditionInfo {
using TCreateMethod = std::shared_ptr<MacroCondition> (*)(Macro *m);
using TCreateWidgetMethod =
QWidget *(*)(QWidget *parent, std::shared_ptr<MacroCondition>);
TCreateMethod _createFunc = nullptr;
TCreateWidgetMethod _createWidgetFunc = nullptr;
std::string _name;
bool _useDurationModifier = true;
};
class MacroConditionFactory {
public:
MacroConditionFactory() = delete;
static bool Register(const std::string &, MacroConditionInfo);
static std::shared_ptr<MacroCondition> Create(const std::string &,
Macro *m);
static QWidget *CreateWidget(const std::string &id, QWidget *parent,
std::shared_ptr<MacroCondition>);
static auto GetConditionTypes() { return GetMap(); }
static std::string GetConditionName(const std::string &);
static std::string GetIdByName(const QString &name);
static bool UsesDurationModifier(const std::string &id);
private:
static std::map<std::string, MacroConditionInfo> &GetMap();
};
class DurationModifierEdit : public QWidget { class DurationModifierEdit : public QWidget {
Q_OBJECT Q_OBJECT
public: public:

View File

@@ -0,0 +1,66 @@
#include "macro-condition-factory.hpp"
namespace advss {
std::map<std::string, MacroConditionInfo> &MacroConditionFactory::GetMap()
{
static std::map<std::string, MacroConditionInfo> _methods;
return _methods;
}
bool MacroConditionFactory::Register(const std::string &id,
MacroConditionInfo info)
{
if (auto it = GetMap().find(id); it == GetMap().end()) {
GetMap()[id] = info;
return true;
}
return false;
}
std::shared_ptr<MacroCondition>
MacroConditionFactory::Create(const std::string &id, Macro *m)
{
if (auto it = GetMap().find(id); it != GetMap().end()) {
return it->second._create(m);
}
return nullptr;
}
QWidget *
MacroConditionFactory::CreateWidget(const std::string &id, QWidget *parent,
std::shared_ptr<MacroCondition> cond)
{
if (auto it = GetMap().find(id); it != GetMap().end()) {
return it->second._createWidget(parent, cond);
}
return nullptr;
}
std::string MacroConditionFactory::GetConditionName(const std::string &id)
{
if (auto it = GetMap().find(id); it != GetMap().end()) {
return it->second._name;
}
return "unknown condition";
}
std::string MacroConditionFactory::GetIdByName(const QString &name)
{
for (auto it : GetMap()) {
if (name == obs_module_text(it.second._name.c_str())) {
return it.first;
}
}
return "";
}
bool MacroConditionFactory::UsesDurationModifier(const std::string &id)
{
if (auto it = GetMap().find(id); it != GetMap().end()) {
return it->second._useDurationModifier;
}
return false;
}
} // namespace advss

View File

@@ -0,0 +1,35 @@
#pragma once
#include "macro-condition.hpp"
#include <memory>
namespace advss {
struct MacroConditionInfo {
using CreateCondition = std::shared_ptr<MacroCondition> (*)(Macro *m);
using CreateConditionWidget =
QWidget *(*)(QWidget *parent, std::shared_ptr<MacroCondition>);
CreateCondition _create = nullptr;
CreateConditionWidget _createWidget = nullptr;
std::string _name;
bool _useDurationModifier = true;
};
class MacroConditionFactory {
public:
MacroConditionFactory() = delete;
static bool Register(const std::string &, MacroConditionInfo);
static std::shared_ptr<MacroCondition> Create(const std::string &,
Macro *m);
static QWidget *CreateWidget(const std::string &id, QWidget *parent,
std::shared_ptr<MacroCondition>);
static auto GetConditionTypes() { return GetMap(); }
static std::string GetConditionName(const std::string &);
static std::string GetIdByName(const QString &name);
static bool UsesDurationModifier(const std::string &id);
private:
static std::map<std::string, MacroConditionInfo> &GetMap();
};
} // namespace advss

View File

@@ -1,12 +1,11 @@
#include "macro-condition-file.hpp" #include "macro-condition-file.hpp"
#include "utility.hpp" #include "utility.hpp"
#include "switcher-data.hpp"
#include "curl-helper.hpp" #include "curl-helper.hpp"
#include "plugin-state-helpers.hpp"
#include <QTextStream> #include <QTextStream>
#include <QFileDialog> #include <QFileDialog>
#include <array> #include <regex>
#include <tuple>
namespace advss { namespace advss {
@@ -29,76 +28,99 @@ static size_t WriteCallback(void *contents, size_t size, size_t nmemb,
static std::string getRemoteData(std::string &url) static std::string getRemoteData(std::string &url)
{ {
std::string readBuffer; std::string readBuffer;
switcher->curl.SetOpt(CURLOPT_URL, url.c_str()); CurlHelper::SetOpt(CURLOPT_URL, url.c_str());
switcher->curl.SetOpt(CURLOPT_WRITEFUNCTION, WriteCallback); CurlHelper::SetOpt(CURLOPT_WRITEFUNCTION, WriteCallback);
switcher->curl.SetOpt(CURLOPT_WRITEDATA, &readBuffer); CurlHelper::SetOpt(CURLOPT_WRITEDATA, &readBuffer);
// Set timeout to at least one second // Set timeout to at least one second
int timeout = switcher->interval / 1000; int timeout = GetIntervalValue() / 1000;
if (timeout == 0) { if (timeout == 0) {
timeout = 1; timeout = 1;
} }
switcher->curl.SetOpt(CURLOPT_TIMEOUT, 1); CurlHelper::SetOpt(CURLOPT_TIMEOUT, 1);
switcher->curl.Perform(); CurlHelper::Perform();
return readBuffer; return readBuffer;
} }
static bool stringsMatch(const QString &text, const std::string &pattern, bool MacroConditionFile::MatchFileContent(QString &filedata)
const RegexConfig &regex)
{ {
auto qpattern = QString::fromStdString(pattern); if (_onlyMatchIfChanged) {
if (regex.Enabled()) { size_t newHash = strHash(filedata.toUtf8().constData());
return regex.Matches(text, qpattern); if (newHash == _lastHash) {
} return false;
auto textCopy = text;
return CompareIgnoringLineEnding(qpattern, textCopy);
}
static std::optional<QString> getFileContent(MacroConditionFile::FileType type,
const std::string &filePath)
{
QString filedata;
switch (type) {
case MacroConditionFile::FileType::LOCAL: {
std::string path = filePath;
QFile file(QString::fromStdString(path));
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
return {};
} }
filedata = QTextStream(&file).readAll(); _lastHash = newHash;
file.close();
} break;
case MacroConditionFile::FileType::REMOTE: {
std::string path = filePath;
std::string data = getRemoteData(path);
filedata = QString::fromStdString(data);
} break;
default:
break;
} }
return filedata;
if (_regex.Enabled()) {
auto expr = _regex.GetRegularExpression(_text);
if (!expr.isValid()) {
return false;
}
auto match = expr.match(filedata);
return match.hasMatch();
}
QString text = QString::fromStdString(_text);
return CompareIgnoringLineEnding(text, filedata);
} }
bool MacroConditionFile::CheckFileContentMatch() bool MacroConditionFile::CheckRemoteFileContent()
{ {
auto fileData = getFileContent(_fileType, _file); std::string path = _file;
if (!fileData) { std::string data = getRemoteData(path);
SetVariableValue(data);
QString qdata = QString::fromStdString(data);
return MatchFileContent(qdata);
}
bool MacroConditionFile::CheckLocalFileContent()
{
QFile file(QString::fromStdString(_file));
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
return false; return false;
} }
const bool match = stringsMatch(*fileData, _text, _regex);
SetVariableValue(fileData->toStdString()); if (_useTime) {
QDateTime newLastMod = QFileInfo(file).lastModified();
if (_lastMod == newLastMod) {
return false;
}
_lastMod = newLastMod;
}
QString filedata = QTextStream(&file).readAll();
SetVariableValue(filedata.toStdString());
bool match = MatchFileContent(filedata);
file.close();
return match; return match;
} }
bool MacroConditionFile::CheckChangeContent() bool MacroConditionFile::CheckChangeContent()
{ {
auto filedata = getFileContent(_fileType, _file); QString filedata;
if (!filedata) { switch (_fileType) {
return false; case FileType::LOCAL: {
std::string path = _file;
QFile file(QString::fromStdString(path));
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
return false;
}
filedata = QTextStream(&file).readAll();
file.close();
} break;
case FileType::REMOTE: {
std::string path = _file;
std::string data = getRemoteData(path);
QString filedata = QString::fromStdString(data);
} break;
default:
break;
} }
size_t newHash = strHash(filedata->toUtf8().constData());
size_t newHash = strHash(filedata.toUtf8().constData());
const bool contentChanged = newHash != _lastHash; const bool contentChanged = newHash != _lastHash;
_lastHash = newHash; _lastHash = newHash;
SetVariableValue(contentChanged ? "true" : "false");
return contentChanged; return contentChanged;
} }
@@ -113,94 +135,35 @@ bool MacroConditionFile::CheckChangeDate()
SetVariableValue(newLastMod.toString().toStdString()); SetVariableValue(newLastMod.toString().toStdString());
const bool dateChanged = _lastMod != newLastMod; const bool dateChanged = _lastMod != newLastMod;
_lastMod = newLastMod; _lastMod = newLastMod;
SetVariableValue(dateChanged ? "true" : "false");
return dateChanged; return dateChanged;
} }
static QStringList splitLines(const QString &string)
{
static auto regex = QRegularExpression("\n|\r\n|\r");
return string.split(regex);
}
bool MacroConditionFile::CheckChangedMatch()
{
static bool setupDone = false;
if (!setupDone) {
auto content = getFileContent(_fileType, _file);
if (!content) {
return false;
}
_previousContent = splitLines(*content);
setupDone = true;
return false;
}
auto currentFileContent = getFileContent(_fileType, _file);
if (!currentFileContent) {
return false;
}
auto currentContent = splitLines(*currentFileContent);
auto newContent = currentContent;
QStringList addedLines;
QStringList removedLines;
for (const auto &line : newContent) {
if (auto it = std::find(_previousContent.begin(),
_previousContent.end(),
line) == _previousContent.end()) {
addedLines.push_back(line);
}
}
for (const auto &line : _previousContent) {
if (std::find(newContent.begin(), newContent.end(), line) ==
newContent.end()) {
removedLines.push_back(line);
}
}
_previousContent = currentContent;
if (_changeMatchType == ChangeMatchType::ADDED ||
_changeMatchType == ChangeMatchType::ANY) {
for (const auto &line : addedLines) {
if (stringsMatch(line, _text, _regex)) {
return true;
}
}
}
if (_changeMatchType == ChangeMatchType::REMOVED ||
_changeMatchType == ChangeMatchType::ANY) {
for (const auto &line : removedLines) {
if (stringsMatch(line, _text, _regex)) {
return true;
}
}
}
return false;
}
bool MacroConditionFile::CheckCondition() bool MacroConditionFile::CheckCondition()
{ {
bool ret = false;
switch (_condition) { switch (_condition) {
case MacroConditionFile::ConditionType::MATCH: case MacroConditionFile::ConditionType::MATCH:
return CheckFileContentMatch(); if (_fileType == FileType::REMOTE) {
ret = CheckRemoteFileContent();
break;
}
ret = CheckLocalFileContent();
break;
case MacroConditionFile::ConditionType::CONTENT_CHANGE: case MacroConditionFile::ConditionType::CONTENT_CHANGE:
return CheckChangeContent(); ret = CheckChangeContent();
break;
case MacroConditionFile::ConditionType::DATE_CHANGE: case MacroConditionFile::ConditionType::DATE_CHANGE:
return CheckChangeDate(); ret = CheckChangeDate();
case MacroConditionFile::ConditionType::CHANGES_MATCH: break;
return CheckChangedMatch();
default: default:
break; break;
} }
return false; if (GetVariableValue().empty()) {
SetVariableValue(ret ? "true" : "false");
}
return ret;
} }
bool MacroConditionFile::Save(obs_data_t *obj) const bool MacroConditionFile::Save(obs_data_t *obj) const
@@ -210,9 +173,9 @@ bool MacroConditionFile::Save(obs_data_t *obj) const
_file.Save(obj, "file"); _file.Save(obj, "file");
_text.Save(obj, "text"); _text.Save(obj, "text");
obs_data_set_int(obj, "fileType", static_cast<int>(_fileType)); obs_data_set_int(obj, "fileType", static_cast<int>(_fileType));
obs_data_set_int(obj, "changeMatchType",
static_cast<int>(_changeMatchType));
obs_data_set_int(obj, "condition", static_cast<int>(_condition)); obs_data_set_int(obj, "condition", static_cast<int>(_condition));
obs_data_set_bool(obj, "useTime", _useTime);
obs_data_set_bool(obj, "onlyMatchIfChanged", _onlyMatchIfChanged);
return true; return true;
} }
@@ -228,10 +191,10 @@ bool MacroConditionFile::Load(obs_data_t *obj)
_file.Load(obj, "file"); _file.Load(obj, "file");
_text.Load(obj, "text"); _text.Load(obj, "text");
_fileType = static_cast<FileType>(obs_data_get_int(obj, "fileType")); _fileType = static_cast<FileType>(obs_data_get_int(obj, "fileType"));
_changeMatchType = static_cast<ChangeMatchType>(
obs_data_get_int(obj, "changeMatchType"));
_condition = _condition =
static_cast<ConditionType>(obs_data_get_int(obj, "condition")); static_cast<ConditionType>(obs_data_get_int(obj, "condition"));
_useTime = obs_data_get_bool(obj, "useTime");
_onlyMatchIfChanged = obs_data_get_bool(obj, "onlyMatchIfChanged");
return true; return true;
} }
@@ -242,79 +205,39 @@ std::string MacroConditionFile::GetShortDesc() const
static void populateFileTypes(QComboBox *list) static void populateFileTypes(QComboBox *list)
{ {
static constexpr std::array< list->addItem(obs_module_text("AdvSceneSwitcher.condition.file.local"));
std::tuple<MacroConditionFile::FileType, std::string_view>, 2> list->addItem(
fileTypes = {{{MacroConditionFile::FileType::LOCAL, obs_module_text("AdvSceneSwitcher.condition.file.remote"));
"AdvSceneSwitcher.condition.file.local"},
{MacroConditionFile::FileType::REMOTE,
"AdvSceneSwitcher.condition.file.remote"}}};
for (const auto &[type, name] : fileTypes) {
list->addItem(obs_module_text(name.data()),
static_cast<int>(type));
}
} }
static void populateConditions(QComboBox *list) static void populateConditions(QComboBox *list)
{ {
static constexpr std::array< list->addItem(
std::tuple<MacroConditionFile::ConditionType, std::string_view>, obs_module_text("AdvSceneSwitcher.condition.file.type.match"));
4> list->addItem(obs_module_text(
conditionTypes = {{ "AdvSceneSwitcher.condition.file.type.contentChange"));
{MacroConditionFile::ConditionType::MATCH, list->addItem(obs_module_text(
"AdvSceneSwitcher.condition.file.type.match"}, "AdvSceneSwitcher.condition.file.type.dateChange"));
{MacroConditionFile::ConditionType::CONTENT_CHANGE,
"AdvSceneSwitcher.condition.file.type.contentChange"},
{MacroConditionFile::ConditionType::DATE_CHANGE,
"AdvSceneSwitcher.condition.file.type.dateChange"},
{MacroConditionFile::ConditionType::CHANGES_MATCH,
"AdvSceneSwitcher.condition.file.type.changesMatch"},
}};
for (const auto &[type, name] : conditionTypes) {
list->addItem(obs_module_text(name.data()),
static_cast<int>(type));
}
}
static void populateChangeMatchTypes(QComboBox *list)
{
static constexpr std::array<
std::tuple<MacroConditionFile::ChangeMatchType, std::string_view>,
3>
matchTypes = {{
{MacroConditionFile::ChangeMatchType::ANY,
"AdvSceneSwitcher.condition.file.changeMatchType.any"},
{MacroConditionFile::ChangeMatchType::ADDED,
"AdvSceneSwitcher.condition.file.changeMatchType.added"},
{MacroConditionFile::ChangeMatchType::REMOVED,
"AdvSceneSwitcher.condition.file.changeMatchType.removed"},
}};
for (const auto &[type, name] : matchTypes) {
list->addItem(obs_module_text(name.data()),
static_cast<int>(type));
}
} }
MacroConditionFileEdit::MacroConditionFileEdit( MacroConditionFileEdit::MacroConditionFileEdit(
QWidget *parent, std::shared_ptr<MacroConditionFile> entryData) QWidget *parent, std::shared_ptr<MacroConditionFile> entryData)
: QWidget(parent), : QWidget(parent),
_fileTypes(new QComboBox()), _fileTypes(new QComboBox()),
_changeMatchTypes(new QComboBox()),
_conditions(new QComboBox()), _conditions(new QComboBox()),
_filePath(new FileSelection()), _filePath(new FileSelection()),
_matchText(new VariableTextEdit(this)), _matchText(new VariableTextEdit(this)),
_regex(new RegexConfigWidget(parent)) _regex(new RegexConfigWidget(parent)),
_checkModificationDate(new QCheckBox(obs_module_text(
"AdvSceneSwitcher.fileTab.checkfileContentTime"))),
_checkFileContent(new QCheckBox(
obs_module_text("AdvSceneSwitcher.fileTab.checkfileContent")))
{ {
populateFileTypes(_fileTypes); populateFileTypes(_fileTypes);
populateConditions(_conditions); populateConditions(_conditions);
populateChangeMatchTypes(_changeMatchTypes);
QWidget::connect(_fileTypes, SIGNAL(currentIndexChanged(int)), this, QWidget::connect(_fileTypes, SIGNAL(currentIndexChanged(int)), this,
SLOT(FileTypeChanged(int))); SLOT(FileTypeChanged(int)));
QWidget::connect(_changeMatchTypes, SIGNAL(currentIndexChanged(int)),
this, SLOT(ChangeMatchTypeChanged(int)));
QWidget::connect(_conditions, SIGNAL(currentIndexChanged(int)), this, QWidget::connect(_conditions, SIGNAL(currentIndexChanged(int)), this,
SLOT(ConditionChanged(int))); SLOT(ConditionChanged(int)));
QWidget::connect(_filePath, SIGNAL(PathChanged(const QString &)), this, QWidget::connect(_filePath, SIGNAL(PathChanged(const QString &)), this,
@@ -323,30 +246,41 @@ MacroConditionFileEdit::MacroConditionFileEdit(
SLOT(MatchTextChanged())); SLOT(MatchTextChanged()));
QWidget::connect(_regex, SIGNAL(RegexConfigChanged(RegexConfig)), this, QWidget::connect(_regex, SIGNAL(RegexConfigChanged(RegexConfig)), this,
SLOT(RegexChanged(RegexConfig))); SLOT(RegexChanged(RegexConfig)));
QWidget::connect(_checkModificationDate, SIGNAL(stateChanged(int)),
this, SLOT(CheckModificationDateChanged(int)));
QWidget::connect(_checkFileContent, SIGNAL(stateChanged(int)), this,
SLOT(OnlyMatchIfChangedChanged(int)));
const std::unordered_map<std::string, QWidget *> widgetPlaceholders = { std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
{"{{fileType}}", _fileTypes}, {"{{fileType}}", _fileTypes},
{"{{changeMatchType}}", _changeMatchTypes},
{"{{conditions}}", _conditions}, {"{{conditions}}", _conditions},
{"{{filePath}}", _filePath}, {"{{filePath}}", _filePath},
{"{{matchText}}", _matchText}, {"{{matchText}}", _matchText},
{"{{useRegex}}", _regex}, {"{{useRegex}}", _regex},
{"{{checkModificationDate}}", _checkModificationDate},
{"{{checkFileContent}}", _checkFileContent},
}; };
auto line1Layout = new QHBoxLayout; QVBoxLayout *mainLayout = new QVBoxLayout;
QHBoxLayout *line1Layout = new QHBoxLayout;
QHBoxLayout *line2Layout = new QHBoxLayout;
QHBoxLayout *line3Layout = new QHBoxLayout;
line1Layout->setContentsMargins(0, 0, 0, 0); line1Layout->setContentsMargins(0, 0, 0, 0);
line2Layout->setContentsMargins(0, 0, 0, 0);
line3Layout->setContentsMargins(0, 0, 0, 0);
PlaceWidgets( PlaceWidgets(
obs_module_text("AdvSceneSwitcher.condition.file.entry.line1"), obs_module_text("AdvSceneSwitcher.condition.file.entry.line1"),
line1Layout, widgetPlaceholders); line1Layout, widgetPlaceholders);
auto line2Layout = new QHBoxLayout;
line2Layout->setContentsMargins(0, 0, 0, 0);
PlaceWidgets( PlaceWidgets(
obs_module_text("AdvSceneSwitcher.condition.file.entry.line2"), obs_module_text("AdvSceneSwitcher.condition.file.entry.line2"),
line2Layout, widgetPlaceholders, false); line2Layout, widgetPlaceholders, false);
PlaceWidgets(
auto mainLayout = new QVBoxLayout; obs_module_text("AdvSceneSwitcher.condition.file.entry.line3"),
line3Layout, widgetPlaceholders);
mainLayout->addLayout(line1Layout); mainLayout->addLayout(line1Layout);
mainLayout->addLayout(line2Layout); mainLayout->addLayout(line2Layout);
mainLayout->addLayout(line3Layout);
setLayout(mainLayout); setLayout(mainLayout);
_entryData = entryData; _entryData = entryData;
@@ -360,15 +294,21 @@ void MacroConditionFileEdit::UpdateEntryData()
return; return;
} }
_fileTypes->setCurrentIndex( _fileTypes->setCurrentIndex(static_cast<int>(_entryData->_fileType));
_fileTypes->findData(static_cast<int>(_entryData->_fileType))); _conditions->setCurrentIndex(static_cast<int>(_entryData->_condition));
_conditions->setCurrentIndex(_conditions->findData(
static_cast<int>(_entryData->_condition)));
_changeMatchTypes->setCurrentIndex(_changeMatchTypes->findData(
static_cast<int>(_entryData->_changeMatchType)));
_filePath->SetPath(_entryData->_file); _filePath->SetPath(_entryData->_file);
_matchText->setPlainText(_entryData->_text); _matchText->setPlainText(_entryData->_text);
_regex->SetRegexConfig(_entryData->_regex); _regex->SetRegexConfig(_entryData->_regex);
_checkModificationDate->setChecked(_entryData->_useTime);
_checkFileContent->setChecked(_entryData->_onlyMatchIfChanged);
// TODO: Remove in future version
if (!_entryData->_useTime) {
_checkModificationDate->hide();
}
if (!_entryData->_onlyMatchIfChanged) {
_checkFileContent->hide();
}
SetWidgetVisibility(); SetWidgetVisibility();
} }
@@ -379,25 +319,19 @@ void MacroConditionFileEdit::FileTypeChanged(int index)
return; return;
} }
auto type = static_cast<MacroConditionFile::FileType>( MacroConditionFile::FileType type =
_fileTypes->itemData(index).toInt()); static_cast<MacroConditionFile::FileType>(index);
_filePath->Button()->setEnabled(type ==
MacroConditionFile::FileType::LOCAL);
auto lock = LockContext();
_entryData->_fileType = type;
}
void MacroConditionFileEdit::ChangeMatchTypeChanged(int index) if (type == MacroConditionFile::FileType::LOCAL) {
{ _filePath->Button()->setDisabled(false);
if (_loading || !_entryData) { _checkModificationDate->setDisabled(false);
return; } else {
_filePath->Button()->setDisabled(true);
_checkModificationDate->setDisabled(true);
} }
auto lock = LockContext(); auto lock = LockContext();
_entryData->_changeMatchType = _entryData->_fileType = type;
static_cast<MacroConditionFile::ChangeMatchType>(
_changeMatchTypes->itemData(index).toInt());
SetWidgetVisibility();
} }
void MacroConditionFileEdit::ConditionChanged(int index) void MacroConditionFileEdit::ConditionChanged(int index)
@@ -407,8 +341,8 @@ void MacroConditionFileEdit::ConditionChanged(int index)
} }
auto lock = LockContext(); auto lock = LockContext();
_entryData->_condition = static_cast<MacroConditionFile::ConditionType>( _entryData->_condition =
_conditions->itemData(index).toInt()); static_cast<MacroConditionFile::ConditionType>(index);
SetWidgetVisibility(); SetWidgetVisibility();
} }
@@ -449,25 +383,44 @@ void MacroConditionFileEdit::RegexChanged(RegexConfig conf)
updateGeometry(); updateGeometry();
} }
void MacroConditionFileEdit::CheckModificationDateChanged(int state)
{
if (_loading || !_entryData) {
return;
}
auto lock = LockContext();
_entryData->_useTime = state;
}
void MacroConditionFileEdit::OnlyMatchIfChangedChanged(int state)
{
if (_loading || !_entryData) {
return;
}
auto lock = LockContext();
_entryData->_onlyMatchIfChanged = state;
}
void MacroConditionFileEdit::SetWidgetVisibility() void MacroConditionFileEdit::SetWidgetVisibility()
{ {
if (!_entryData) { if (!_entryData) {
return; return;
} }
_changeMatchTypes->setVisible( _matchText->setVisible(_entryData->_condition ==
MacroConditionFile::ConditionType::MATCH);
_regex->setVisible(_entryData->_condition ==
MacroConditionFile::ConditionType::MATCH);
_checkModificationDate->setVisible(
_entryData->_useTime &&
_entryData->_condition == _entryData->_condition ==
MacroConditionFile::ConditionType::CHANGES_MATCH); MacroConditionFile::ConditionType::MATCH);
_matchText->setVisible( _checkFileContent->setVisible(
_entryData->_onlyMatchIfChanged &&
_entryData->_condition == _entryData->_condition ==
MacroConditionFile::ConditionType::MATCH || MacroConditionFile::ConditionType::MATCH);
_entryData->_condition ==
MacroConditionFile::ConditionType::CHANGES_MATCH);
_regex->setVisible(
_entryData->_condition ==
MacroConditionFile::ConditionType::MATCH ||
_entryData->_condition ==
MacroConditionFile::ConditionType::CHANGES_MATCH);
adjustSize(); adjustSize();
updateGeometry(); updateGeometry();
} }

View File

@@ -10,7 +10,6 @@
#include <QLineEdit> #include <QLineEdit>
#include <QPushButton> #include <QPushButton>
#include <QCheckBox> #include <QCheckBox>
#include <QStringList>
namespace advss { namespace advss {
@@ -36,31 +35,27 @@ public:
MATCH, MATCH,
CONTENT_CHANGE, CONTENT_CHANGE,
DATE_CHANGE, DATE_CHANGE,
CHANGES_MATCH,
};
enum class ChangeMatchType {
ANY,
ADDED,
REMOVED,
}; };
StringVariable _file = obs_module_text("AdvSceneSwitcher.enterPath"); StringVariable _file = obs_module_text("AdvSceneSwitcher.enterPath");
StringVariable _text = obs_module_text("AdvSceneSwitcher.enterText"); StringVariable _text = obs_module_text("AdvSceneSwitcher.enterText");
FileType _fileType = FileType::LOCAL; FileType _fileType = FileType::LOCAL;
ChangeMatchType _changeMatchType = ChangeMatchType::ANY;
ConditionType _condition = ConditionType::MATCH; ConditionType _condition = ConditionType::MATCH;
RegexConfig _regex; RegexConfig _regex;
// TODO: Remove in future version
bool _useTime = false;
bool _onlyMatchIfChanged = false;
private: private:
bool CheckFileContentMatch(); bool MatchFileContent(QString &filedata);
bool CheckRemoteFileContent();
bool CheckLocalFileContent();
bool CheckChangeContent(); bool CheckChangeContent();
bool CheckChangeDate(); bool CheckChangeDate();
bool CheckChangedMatch();
QDateTime _lastMod; QDateTime _lastMod;
size_t _lastHash = 0; size_t _lastHash = 0;
QStringList _previousContent;
static bool _registered; static bool _registered;
static const std::string id; static const std::string id;
}; };
@@ -83,21 +78,23 @@ public:
private slots: private slots:
void FileTypeChanged(int index); void FileTypeChanged(int index);
void ChangeMatchTypeChanged(int index);
void ConditionChanged(int index); void ConditionChanged(int index);
void PathChanged(const QString &text); void PathChanged(const QString &text);
void MatchTextChanged(); void MatchTextChanged();
void RegexChanged(RegexConfig); void RegexChanged(RegexConfig);
void CheckModificationDateChanged(int state);
void OnlyMatchIfChangedChanged(int state);
signals: signals:
void HeaderInfoChanged(const QString &); void HeaderInfoChanged(const QString &);
protected: protected:
QComboBox *_fileTypes; QComboBox *_fileTypes;
QComboBox *_changeMatchTypes;
QComboBox *_conditions; QComboBox *_conditions;
FileSelection *_filePath; FileSelection *_filePath;
VariableTextEdit *_matchText; VariableTextEdit *_matchText;
RegexConfigWidget *_regex; RegexConfigWidget *_regex;
QCheckBox *_checkModificationDate;
QCheckBox *_checkFileContent;
std::shared_ptr<MacroConditionFile> _entryData; std::shared_ptr<MacroConditionFile> _entryData;
private: private:

View File

@@ -1,5 +1,7 @@
#include "macro-condition-media.hpp" #include "macro-condition-media.hpp"
#include "switcher-data.hpp" #include "macro.hpp"
#include "plugin-state-helpers.hpp"
#include "scene-switch-helpers.hpp"
#include "utility.hpp" #include "utility.hpp"
namespace advss { namespace advss {
@@ -148,26 +150,20 @@ bool MacroConditionMedia::CheckMediaMatch()
if (!_source.GetSource()) { if (!_source.GetSource()) {
return false; return false;
} }
bool match = false;
bool matched = CheckState() && CheckTime(); bool matched = CheckState() && CheckTime();
if (matched && !(_onlyMatchOnChagne && _alreadyMatched)) {
match = true;
}
_alreadyMatched = matched;
// reset for next check // reset for next check
_stopped = false; _stopped = false;
_ended = false; _ended = false;
_next = false; _next = false;
return match; return matched;
} }
void MacroConditionMedia::HandleSceneChange() void MacroConditionMedia::HandleSceneChange()
{ {
UpdateMediaSourcesOfSceneList(); UpdateMediaSourcesOfSceneList();
_lastConfigureScene = switcher->currentScene; _lastConfigureScene = GetCurrentScene();
} }
bool MacroConditionMedia::CheckCondition() bool MacroConditionMedia::CheckCondition()
@@ -194,7 +190,7 @@ bool MacroConditionMedia::CheckCondition()
break; break;
} }
if (_lastConfigureScene != switcher->currentScene) { if (_lastConfigureScene != GetCurrentScene()) {
HandleSceneChange(); HandleSceneChange();
} }
@@ -210,7 +206,6 @@ bool MacroConditionMedia::Save(obs_data_t *obj) const
obs_data_set_int(obj, "state", static_cast<int>(_state)); obs_data_set_int(obj, "state", static_cast<int>(_state));
obs_data_set_int(obj, "restriction", static_cast<int>(_restriction)); obs_data_set_int(obj, "restriction", static_cast<int>(_restriction));
_time.Save(obj); _time.Save(obj);
obs_data_set_bool(obj, "matchOnChagne", _onlyMatchOnChagne);
obs_data_set_int(obj, "version", 0); obs_data_set_int(obj, "version", 0);
return true; return true;
} }
@@ -268,7 +263,6 @@ bool MacroConditionMedia::Load(obs_data_t *obj)
_restriction = static_cast<MacroConditionMedia::Time>( _restriction = static_cast<MacroConditionMedia::Time>(
obs_data_get_int(obj, "restriction")); obs_data_get_int(obj, "restriction"));
_time.Load(obj); _time.Load(obj);
_onlyMatchOnChagne = obs_data_get_bool(obj, "matchOnChagne");
if (_sourceType == Type::SOURCE) { if (_sourceType == Type::SOURCE) {
obs_source_t *mediasource = obs_source_t *mediasource =
@@ -408,9 +402,7 @@ MacroConditionMediaEdit::MacroConditionMediaEdit(
_sources(new SourceSelectionWidget(this, QStringList(), true)), _sources(new SourceSelectionWidget(this, QStringList(), true)),
_states(new QComboBox()), _states(new QComboBox()),
_timeRestrictions(new QComboBox()), _timeRestrictions(new QComboBox()),
_time(new DurationSelection()), _time(new DurationSelection())
_onChange(new QCheckBox(obs_module_text(
"AdvSceneSwitcher.condition.media.matchOnChange")))
{ {
_states->setToolTip(obs_module_text( _states->setToolTip(obs_module_text(
"AdvSceneSwitcher.condition.media.inconsistencyInfo")); "AdvSceneSwitcher.condition.media.inconsistencyInfo"));
@@ -432,14 +424,12 @@ MacroConditionMediaEdit::MacroConditionMediaEdit(
this, SLOT(TimeRestrictionChanged(int))); this, SLOT(TimeRestrictionChanged(int)));
QWidget::connect(_time, SIGNAL(DurationChanged(const Duration &)), this, QWidget::connect(_time, SIGNAL(DurationChanged(const Duration &)), this,
SLOT(TimeChanged(const Duration &))); SLOT(TimeChanged(const Duration &)));
QWidget::connect(_onChange, SIGNAL(stateChanged(int)), this,
SLOT(OnChangeChanged(int)));
populateSourceTypes(_sourceTypes); populateSourceTypes(_sourceTypes);
populateMediaStates(_states); populateMediaStates(_states);
populateMediaTimes(_timeRestrictions); populateMediaTimes(_timeRestrictions);
QHBoxLayout *entryLayout = new QHBoxLayout; auto layout = new QHBoxLayout;
std::unordered_map<std::string, QWidget *> widgetPlaceholders = { std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
{"{{sourceTypes}}", _sourceTypes}, {"{{sourceTypes}}", _sourceTypes},
{"{{mediaSources}}", _sources}, {"{{mediaSources}}", _sources},
@@ -449,11 +439,8 @@ MacroConditionMediaEdit::MacroConditionMediaEdit(
{"{{time}}", _time}, {"{{time}}", _time},
}; };
PlaceWidgets(obs_module_text("AdvSceneSwitcher.condition.media.entry"), PlaceWidgets(obs_module_text("AdvSceneSwitcher.condition.media.entry"),
entryLayout, widgetPlaceholders); layout, widgetPlaceholders);
QVBoxLayout *mainLayout = new QVBoxLayout; setLayout(layout);
mainLayout->addLayout(entryLayout);
mainLayout->addWidget(_onChange);
setLayout(mainLayout);
_entryData = entryData; _entryData = entryData;
UpdateEntryData(); UpdateEntryData();
@@ -572,28 +559,12 @@ void MacroConditionMediaEdit::TimeChanged(const Duration &dur)
} }
} }
void MacroConditionMediaEdit::OnChangeChanged(int value)
{
if (_loading || !_entryData) {
return;
}
auto lock = LockContext();
_entryData->_onlyMatchOnChagne = value;
if (_entryData->_sourceType != MacroConditionMedia::Type::SOURCE) {
_entryData->UpdateMediaSourcesOfSceneList();
}
}
void MacroConditionMediaEdit::SetWidgetVisibility() void MacroConditionMediaEdit::SetWidgetVisibility()
{ {
_sources->setVisible(_entryData->_sourceType == _sources->setVisible(_entryData->_sourceType ==
MacroConditionMedia::Type::SOURCE); MacroConditionMedia::Type::SOURCE);
_scenes->setVisible(_entryData->_sourceType != _scenes->setVisible(_entryData->_sourceType !=
MacroConditionMedia::Type::SOURCE); MacroConditionMedia::Type::SOURCE);
if (!_onChange->isChecked()) {
_onChange->hide();
}
} }
static int getIdxFromMediaState(MacroConditionMedia::State state) static int getIdxFromMediaState(MacroConditionMedia::State state)
@@ -625,7 +596,6 @@ void MacroConditionMediaEdit::UpdateEntryData()
MacroConditionMedia::Time::TIME_RESTRICTION_NONE) { MacroConditionMedia::Time::TIME_RESTRICTION_NONE) {
_time->setDisabled(true); _time->setDisabled(true);
} }
_onChange->setChecked(_entryData->_onlyMatchOnChagne);
SetWidgetVisibility(); SetWidgetVisibility();
} }

View File

@@ -72,7 +72,6 @@ public:
OBSWeakSource _rawSource = nullptr; OBSWeakSource _rawSource = nullptr;
std::vector<MacroConditionMedia> _sourceGroup; std::vector<MacroConditionMedia> _sourceGroup;
Duration _time; Duration _time;
bool _onlyMatchOnChagne = false;
private: private:
bool CheckTime(); bool CheckTime();
@@ -84,9 +83,7 @@ private:
bool _stopped = false; bool _stopped = false;
bool _ended = false; bool _ended = false;
bool _next = false; bool _next = false;
// TODO: Remove _alreadyMatched as it does not make much sense when
// time restrictions for macro conditions are available.
bool _alreadyMatched = false;
// Workaround to enable use of "ended" to specify end of VLC playlist // Workaround to enable use of "ended" to specify end of VLC playlist
bool _previousStateEnded = false; bool _previousStateEnded = false;
// Used to keep track of scene changes // Used to keep track of scene changes
@@ -119,7 +116,6 @@ private slots:
void StateChanged(int index); void StateChanged(int index);
void TimeRestrictionChanged(int index); void TimeRestrictionChanged(int index);
void TimeChanged(const Duration &seconds); void TimeChanged(const Duration &seconds);
void OnChangeChanged(int);
signals: signals:
void HeaderInfoChanged(const QString &); void HeaderInfoChanged(const QString &);
@@ -130,7 +126,6 @@ protected:
QComboBox *_states; QComboBox *_states;
QComboBox *_timeRestrictions; QComboBox *_timeRestrictions;
DurationSelection *_time; DurationSelection *_time;
QCheckBox *_onChange;
std::shared_ptr<MacroConditionMedia> _entryData; std::shared_ptr<MacroConditionMedia> _entryData;
private: private:

View File

@@ -1,5 +1,6 @@
#include "macro-condition-plugin-state.hpp" #include "macro-condition-plugin-state.hpp"
#include "switcher-data.hpp" #include "macro-helpers.hpp"
#include "plugin-state-helpers.hpp"
#include "utility.hpp" #include "utility.hpp"
namespace advss { namespace advss {
@@ -33,7 +34,7 @@ const static std::map<MacroConditionPluginState::Condition, std::string>
MacroConditionPluginState::~MacroConditionPluginState() MacroConditionPluginState::~MacroConditionPluginState()
{ {
if (_condition == Condition::OBS_SHUTDOWN) { if (_condition == Condition::OBS_SHUTDOWN) {
switcher->shutdownConditionCount--; GetShutdownConditionCount()--;
} }
} }
@@ -41,15 +42,15 @@ bool MacroConditionPluginState::CheckCondition()
{ {
switch (_condition) { switch (_condition) {
case Condition::PLUGIN_SCENE_CHANGE: case Condition::PLUGIN_SCENE_CHANGE:
return switcher->macroSceneSwitched; return MacroSwitchedScene();
case Condition::PLUGIN_RUNNING: case Condition::PLUGIN_RUNNING:
return true; return true;
case Condition::OBS_SHUTDOWN: case Condition::OBS_SHUTDOWN:
return switcher->obsIsShuttingDown; return OBSIsShuttingDown();
case Condition::PLUGIN_START: case Condition::PLUGIN_START:
return switcher->firstInterval; return IsFirstInterval();
case Condition::PLUGIN_RESTART: case Condition::PLUGIN_RESTART:
return switcher->firstIntervalAfterStop; return IsFirstIntervalAfterStop();
case Condition::SCENE_COLLECTION_CHANGE: case Condition::SCENE_COLLECTION_CHANGE:
if (_firstCheckAfterSceneCollectionChange) { if (_firstCheckAfterSceneCollectionChange) {
_firstCheckAfterSceneCollectionChange = false; _firstCheckAfterSceneCollectionChange = false;
@@ -83,25 +84,33 @@ bool MacroConditionPluginState::Load(obs_data_t *obj)
obs_data_get_int(obj, "condition")); obs_data_get_int(obj, "condition"));
switch (oldValue) { switch (oldValue) {
case PluginStateCondition::SCENE_SWITCHED: case PluginStateCondition::SCENE_SWITCHED:
_condition = Condition::PLUGIN_SCENE_CHANGE; SetCondition(Condition::PLUGIN_SCENE_CHANGE);
break; break;
case PluginStateCondition::RUNNING: case PluginStateCondition::RUNNING:
_condition = Condition::PLUGIN_RUNNING; SetCondition(Condition::PLUGIN_RUNNING);
break; break;
case PluginStateCondition::SHUTDOWN: case PluginStateCondition::SHUTDOWN:
_condition = Condition::OBS_SHUTDOWN; SetCondition(Condition::OBS_SHUTDOWN);
break; break;
} }
} else { } else {
_condition = static_cast<Condition>( SetCondition(static_cast<Condition>(
obs_data_get_int(obj, "condition")); obs_data_get_int(obj, "condition")));
}
if (_condition == Condition::OBS_SHUTDOWN) {
switcher->shutdownConditionCount++;
} }
return true; return true;
} }
void MacroConditionPluginState::SetCondition(Condition newValue)
{
if (_condition == Condition::OBS_SHUTDOWN) {
GetShutdownConditionCount()--;
}
if (newValue == Condition::OBS_SHUTDOWN) {
GetShutdownConditionCount()++;
}
_condition = newValue;
}
static inline void populateConditionSelection(QComboBox *list) static inline void populateConditionSelection(QComboBox *list)
{ {
for (const auto &[action, name] : pluginStateConditionTypes) { for (const auto &[action, name] : pluginStateConditionTypes) {
@@ -148,17 +157,9 @@ void MacroConditionPluginStateEdit::ConditionChanged(int idx)
} }
auto lock = LockContext(); auto lock = LockContext();
if (_entryData->_condition == _entryData->SetCondition(
MacroConditionPluginState::Condition::OBS_SHUTDOWN) {
switcher->shutdownConditionCount--;
}
_entryData->_condition =
static_cast<MacroConditionPluginState::Condition>( static_cast<MacroConditionPluginState::Condition>(
_condition->itemData(idx).toInt()); _condition->itemData(idx).toInt()));
if (_entryData->_condition ==
MacroConditionPluginState::Condition::OBS_SHUTDOWN) {
switcher->shutdownConditionCount++;
}
SetWidgetVisibility(); SetWidgetVisibility();
} }
@@ -168,15 +169,15 @@ void MacroConditionPluginStateEdit::UpdateEntryData()
return; return;
} }
_condition->setCurrentIndex( _condition->setCurrentIndex(_condition->findData(
_condition->findData(static_cast<int>(_entryData->_condition))); static_cast<int>(_entryData->GetCondition())));
SetWidgetVisibility(); SetWidgetVisibility();
} }
void MacroConditionPluginStateEdit::SetWidgetVisibility() void MacroConditionPluginStateEdit::SetWidgetVisibility()
{ {
_shutdownLimitation->setVisible( _shutdownLimitation->setVisible(
_entryData->_condition == _entryData->GetCondition() ==
MacroConditionPluginState::Condition::OBS_SHUTDOWN); MacroConditionPluginState::Condition::OBS_SHUTDOWN);
adjustSize(); adjustSize();
updateGeometry(); updateGeometry();

View File

@@ -26,10 +26,11 @@ public:
SCENE_COLLECTION_CHANGE, SCENE_COLLECTION_CHANGE,
PLUGIN_SCENE_CHANGE, PLUGIN_SCENE_CHANGE,
}; };
void SetCondition(Condition);
Condition _condition = Condition::PLUGIN_SCENE_CHANGE; Condition GetCondition() const { return _condition; }
private: private:
Condition _condition = Condition::PLUGIN_SCENE_CHANGE;
bool _firstCheckAfterSceneCollectionChange = true; bool _firstCheckAfterSceneCollectionChange = true;
const static uint32_t _version; const static uint32_t _version;

View File

@@ -1,5 +1,4 @@
#include "macro-condition-process.hpp" #include "macro-condition-process.hpp"
#include "switcher-data.hpp"
#include "platform-funcs.hpp" #include "platform-funcs.hpp"
#include "utility.hpp" #include "utility.hpp"
@@ -172,8 +171,9 @@ void MacroConditionProcessEdit::FocusChanged(int state)
void MacroConditionProcessEdit::UpdateFocusProcess() void MacroConditionProcessEdit::UpdateFocusProcess()
{ {
_focusProcess->setText( std::string name;
QString::fromStdString(switcher->currentForegroundProcess)); GetForegroundProcessName(name);
_focusProcess->setText(QString::fromStdString(name));
} }
void MacroConditionProcessEdit::SetWidgetVisibility() void MacroConditionProcessEdit::SetWidgetVisibility()

View File

@@ -178,6 +178,8 @@ bool SetupRecordingTimer()
case OBS_FRONTEND_EVENT_RECORDING_UNPAUSED: case OBS_FRONTEND_EVENT_RECORDING_UNPAUSED:
recordingPaused = false; recordingPaused = false;
break; break;
default:
break;
}; };
}; };
obs_frontend_add_event_callback(handleRecordingEvents, nullptr); obs_frontend_add_event_callback(handleRecordingEvents, nullptr);

View File

@@ -1,5 +1,4 @@
#include "macro-condition-replay-buffer.hpp" #include "macro-condition-replay-buffer.hpp"
#include "switcher-data.hpp"
#include "utility.hpp" #include "utility.hpp"
namespace advss { namespace advss {
@@ -11,35 +10,63 @@ bool MacroConditionReplayBuffer::_registered = MacroConditionFactory::Register(
MacroConditionReplayBufferEdit::Create, MacroConditionReplayBufferEdit::Create,
"AdvSceneSwitcher.condition.replay"}); "AdvSceneSwitcher.condition.replay"});
const static std::map<ReplayBufferState, std::string> ReplayBufferStates = { const static std::map<MacroConditionReplayBuffer::Condition, std::string>
{ReplayBufferState::STOP, conditions = {
"AdvSceneSwitcher.condition.replay.state.stopped"}, {MacroConditionReplayBuffer::Condition::STOP,
{ReplayBufferState::START, "AdvSceneSwitcher.condition.replay.state.stopped"},
"AdvSceneSwitcher.condition.replay.state.started"}, {MacroConditionReplayBuffer::Condition::START,
{ReplayBufferState::SAVE, "AdvSceneSwitcher.condition.replay.state.started"},
"AdvSceneSwitcher.condition.replay.state.saved"}, {MacroConditionReplayBuffer::Condition::SAVE,
"AdvSceneSwitcher.condition.replay.state.saved"},
}; };
static bool replayBufferSaved = false;
static std::chrono::high_resolution_clock::time_point replayBufferSaveTime{};
static bool setupReplayBufferEventHandler();
static bool replayBufferEventHandlerIsSetup = setupReplayBufferEventHandler();
static bool setupReplayBufferEventHandler()
{
static auto handleReplayBufferEvent = [](enum obs_frontend_event event,
void *) {
switch (event) {
case OBS_FRONTEND_EVENT_REPLAY_BUFFER_SAVED:
replayBufferSaveTime =
std::chrono::high_resolution_clock::now();
break;
default:
break;
};
};
obs_frontend_add_event_callback(handleReplayBufferEvent, nullptr);
return true;
}
bool MacroConditionReplayBuffer::ReplayBufferWasSaved()
{
bool timersAreInitialized =
_saveTime.time_since_epoch().count() != 0 &&
replayBufferSaveTime.time_since_epoch().count() != 0;
bool newSaveOccurred = timersAreInitialized &&
_saveTime != replayBufferSaveTime;
_saveTime = replayBufferSaveTime;
return newSaveOccurred;
}
bool MacroConditionReplayBuffer::CheckCondition() bool MacroConditionReplayBuffer::CheckCondition()
{ {
bool stateMatch = false;
switch (_state) { switch (_state) {
case ReplayBufferState::STOP: case Condition::STOP:
stateMatch = !obs_frontend_replay_buffer_active(); return !obs_frontend_replay_buffer_active();
break; case Condition::START:
case ReplayBufferState::START: return obs_frontend_replay_buffer_active();
stateMatch = obs_frontend_replay_buffer_active(); case Condition::SAVE:
break; return ReplayBufferWasSaved();
case ReplayBufferState::SAVE:
if (switcher->replayBufferSaved) {
stateMatch = true;
switcher->replayBufferSaved = false;
}
break;
default: default:
break; break;
} }
return stateMatch; return false;
} }
bool MacroConditionReplayBuffer::Save(obs_data_t *obj) const bool MacroConditionReplayBuffer::Save(obs_data_t *obj) const
@@ -52,37 +79,30 @@ bool MacroConditionReplayBuffer::Save(obs_data_t *obj) const
bool MacroConditionReplayBuffer::Load(obs_data_t *obj) bool MacroConditionReplayBuffer::Load(obs_data_t *obj)
{ {
MacroCondition::Load(obj); MacroCondition::Load(obj);
_state = static_cast<ReplayBufferState>(obs_data_get_int(obj, "state")); _state = static_cast<Condition>(obs_data_get_int(obj, "state"));
return true; return true;
} }
static inline void populateStateSelection(QComboBox *list) static inline void populateStateSelection(QComboBox *list)
{ {
for (auto entry : ReplayBufferStates) { for (const auto &entry : conditions) {
list->addItem(obs_module_text(entry.second.c_str())); list->addItem(obs_module_text(entry.second.c_str()));
} }
} }
MacroConditionReplayBufferEdit::MacroConditionReplayBufferEdit( MacroConditionReplayBufferEdit::MacroConditionReplayBufferEdit(
QWidget *parent, std::shared_ptr<MacroConditionReplayBuffer> entryData) QWidget *parent, std::shared_ptr<MacroConditionReplayBuffer> entryData)
: QWidget(parent) : QWidget(parent), _state(new QComboBox())
{ {
_state = new QComboBox();
QWidget::connect(_state, SIGNAL(currentIndexChanged(int)), this, QWidget::connect(_state, SIGNAL(currentIndexChanged(int)), this,
SLOT(StateChanged(int))); SLOT(StateChanged(int)));
populateStateSelection(_state); populateStateSelection(_state);
QHBoxLayout *mainLayout = new QHBoxLayout; auto layout = new QHBoxLayout;
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
{"{{state}}", _state},
};
PlaceWidgets(obs_module_text("AdvSceneSwitcher.condition.replay.entry"), PlaceWidgets(obs_module_text("AdvSceneSwitcher.condition.replay.entry"),
mainLayout, widgetPlaceholders); layout, {{"{{state}}", _state}});
setLayout(mainLayout); setLayout(layout);
_entryData = entryData; _entryData = entryData;
UpdateEntryData(); UpdateEntryData();
@@ -96,7 +116,8 @@ void MacroConditionReplayBufferEdit::StateChanged(int value)
} }
auto lock = LockContext(); auto lock = LockContext();
_entryData->_state = static_cast<ReplayBufferState>(value); _entryData->_state =
static_cast<MacroConditionReplayBuffer::Condition>(value);
} }
void MacroConditionReplayBufferEdit::UpdateEntryData() void MacroConditionReplayBufferEdit::UpdateEntryData()

View File

@@ -6,12 +6,6 @@
namespace advss { namespace advss {
enum class ReplayBufferState {
STOP,
START,
SAVE,
};
class MacroConditionReplayBuffer : public MacroCondition { class MacroConditionReplayBuffer : public MacroCondition {
public: public:
MacroConditionReplayBuffer(Macro *m) : MacroCondition(m) {} MacroConditionReplayBuffer(Macro *m) : MacroCondition(m) {}
@@ -24,9 +18,18 @@ public:
return std::make_shared<MacroConditionReplayBuffer>(m); return std::make_shared<MacroConditionReplayBuffer>(m);
} }
ReplayBufferState _state = ReplayBufferState::STOP; enum class Condition {
STOP,
START,
SAVE,
};
Condition _state = Condition::STOP;
private: private:
bool ReplayBufferWasSaved();
std::chrono::high_resolution_clock::time_point _saveTime = {};
static bool _registered; static bool _registered;
static const std::string id; static const std::string id;
}; };

View File

@@ -2,7 +2,6 @@
#include "utility.hpp" #include "utility.hpp"
#include <QProcess> #include <QProcess>
#include <QDesktopServices>
namespace advss { namespace advss {
@@ -28,17 +27,18 @@ bool MacroConditionRun::CheckCondition()
bool ret = false; bool ret = false;
switch (_procStatus) { switch (_error) {
case MacroConditionRun::Status::FAILED_TO_START: case ProcessConfig::ProcStartError::FAILED_TO_START:
SetVariableValue("Failed to start process"); SetVariableValue("Failed to start process");
ret = false;
break; break;
case MacroConditionRun::Status::TIMEOUT: case ProcessConfig::ProcStartError::TIMEOUT:
SetVariableValue("Timeout while running process"); SetVariableValue("Timeout while running process");
ret = false;
break; break;
case MacroConditionRun::Status::OK: case ProcessConfig::ProcStartError::CRASH:
ret = _checkExitCode ? _exitCode == _procExitCode : true; SetVariableValue("Timeout while running process");
break;
case ProcessConfig::ProcStartError::NONE:
ret = _checkExitCode ? _exitCodeToCheck == _procExitCode : true;
SetVariableValue(std::to_string(_procExitCode)); SetVariableValue(std::to_string(_procExitCode));
break; break;
default: default:
@@ -56,42 +56,14 @@ bool MacroConditionRun::CheckCondition()
void MacroConditionRun::RunProcess() void MacroConditionRun::RunProcess()
{ {
QProcess process; auto result = _procConfig.StartProcessAndWait(_timeout.Milliseconds());
process.setWorkingDirectory(
QString::fromStdString(_procConfig.WorkingDir()));
process.start(QString::fromStdString(_procConfig.Path()),
_procConfig.Args());
int timeout = _timeout.Milliseconds();
vblog(LOG_INFO, "run \"%s\" with a timeout of %d ms", if (std::holds_alternative<ProcessConfig::ProcStartError>(result)) {
_procConfig.Path().c_str(), timeout); _error = std::get<ProcessConfig::ProcStartError>(result);
} else {
bool procFinishedInTime = process.waitForFinished(timeout); _error = ProcessConfig::ProcStartError::NONE;
_procExitCode = std::get<int>(result);
if (!procFinishedInTime) {
if (process.error() == QProcess::FailedToStart) {
vblog(LOG_INFO, "failed to start \"%s\"!",
_procConfig.Path().c_str());
_procStatus = Status::FAILED_TO_START;
} else {
vblog(LOG_INFO,
"timeout while running \"%s\"\nAttempting to kill process!",
_procConfig.Path().c_str());
process.kill();
process.waitForFinished();
_procStatus = Status::TIMEOUT;
}
} }
bool validExitCode = process.exitStatus() == QProcess::NormalExit;
if ((_checkExitCode && !validExitCode) || !procFinishedInTime) {
_threadDone = true;
return;
}
_procExitCode = process.exitCode();
_procStatus = Status::OK;
_threadDone = true; _threadDone = true;
} }
@@ -100,7 +72,7 @@ bool MacroConditionRun::Save(obs_data_t *obj) const
MacroCondition::Save(obj); MacroCondition::Save(obj);
_procConfig.Save(obj); _procConfig.Save(obj);
obs_data_set_bool(obj, "checkExitCode", _checkExitCode); obs_data_set_bool(obj, "checkExitCode", _checkExitCode);
obs_data_set_int(obj, "exitCode", _exitCode); obs_data_set_int(obj, "exitCode", _exitCodeToCheck);
_timeout.Save(obj, "timeout"); _timeout.Save(obj, "timeout");
return true; return true;
} }
@@ -110,7 +82,7 @@ bool MacroConditionRun::Load(obs_data_t *obj)
MacroCondition::Load(obj); MacroCondition::Load(obj);
_procConfig.Load(obj); _procConfig.Load(obj);
_checkExitCode = obs_data_get_bool(obj, "checkExitCode"); _checkExitCode = obs_data_get_bool(obj, "checkExitCode");
_exitCode = obs_data_get_int(obj, "exitCode"); _exitCodeToCheck = obs_data_get_int(obj, "exitCode");
_timeout.Load(obj, "timeout"); _timeout.Load(obj, "timeout");
return true; return true;
} }
@@ -174,7 +146,7 @@ void MacroConditionRunEdit::UpdateEntryData()
_procConfig->SetProcessConfig(_entryData->_procConfig); _procConfig->SetProcessConfig(_entryData->_procConfig);
_timeout->SetDuration(_entryData->_timeout); _timeout->SetDuration(_entryData->_timeout);
_checkExitCode->setChecked(_entryData->_checkExitCode); _checkExitCode->setChecked(_entryData->_checkExitCode);
_exitCode->setValue(_entryData->_exitCode); _exitCode->setValue(_entryData->_exitCodeToCheck);
} }
void MacroConditionRunEdit::TimeoutChanged(const Duration &dur) void MacroConditionRunEdit::TimeoutChanged(const Duration &dur)
@@ -204,7 +176,7 @@ void MacroConditionRunEdit::ExitCodeChanged(int exitCode)
} }
auto lock = LockContext(); auto lock = LockContext();
_entryData->_exitCode = exitCode; _entryData->_exitCodeToCheck = exitCode;
} }
void MacroConditionRunEdit::ProcessConfigChanged(const ProcessConfig &conf) void MacroConditionRunEdit::ProcessConfigChanged(const ProcessConfig &conf)

View File

@@ -25,22 +25,15 @@ public:
ProcessConfig _procConfig; ProcessConfig _procConfig;
bool _checkExitCode = true; bool _checkExitCode = true;
int _exitCode = 0; int _exitCodeToCheck = 0;
Duration _timeout = Duration(0.1); Duration _timeout = Duration(0.1);
private: private:
enum class Status {
NONE,
FAILED_TO_START,
TIMEOUT,
OK,
};
void RunProcess(); void RunProcess();
std::thread _thread; std::thread _thread;
std::atomic_bool _threadDone{true}; std::atomic_bool _threadDone{true};
Status _procStatus = Status::NONE; ProcessConfig::ProcStartError _error =
ProcessConfig::ProcStartError::NONE;
int _procExitCode = 0; int _procExitCode = 0;
static bool _registered; static bool _registered;

View File

@@ -1,5 +1,5 @@
#include "macro-condition-scene.hpp" #include "macro-condition-scene.hpp"
#include "switcher-data.hpp" #include "scene-switch-helpers.hpp"
#include "utility.hpp" #include "utility.hpp"
namespace advss { namespace advss {
@@ -53,23 +53,22 @@ static OBSWeakSource getCurrentSceneHelper(bool useTransitionTargetScene)
obs_source_release(current); obs_source_release(current);
return weak; return weak;
} }
return switcher->currentScene; return GetCurrentScene();
} }
static OBSWeakSource getPreviousSceneHelper(bool useTransitionTargetScene) static OBSWeakSource getPreviousSceneHelper(bool useTransitionTargetScene)
{ {
if (switcher->AnySceneTransitionStarted() && useTransitionTargetScene) { if (AnySceneTransitionStarted() && useTransitionTargetScene) {
return switcher->currentScene; return GetCurrentScene();
} }
return switcher->previousScene; return GetPreviousScene();
} }
bool MacroConditionScene::CheckCondition() bool MacroConditionScene::CheckCondition()
{ {
bool sceneChanged = _lastSceneChangeTime != bool sceneChanged = _lastSceneChangeTime != GetLastSceneChangeTime();
switcher->lastSceneChangeTime;
if (sceneChanged) { if (sceneChanged) {
_lastSceneChangeTime = switcher->lastSceneChangeTime; _lastSceneChangeTime = GetLastSceneChangeTime();
} }
switch (_type) { switch (_type) {
@@ -95,14 +94,14 @@ bool MacroConditionScene::CheckCondition()
return scene == _scene.GetScene(false); return scene == _scene.GetScene(false);
} }
case Type::CHANGED: case Type::CHANGED:
SetVariableValue(GetWeakSourceName(switcher->currentScene)); SetVariableValue(GetWeakSourceName(GetCurrentScene()));
SetTempVarValue("current", SetTempVarValue("current",
GetWeakSourceName(switcher->currentScene)); GetWeakSourceName(GetCurrentScene()));
return sceneChanged; return sceneChanged;
case Type::NOT_CHANGED: case Type::NOT_CHANGED:
SetVariableValue(GetWeakSourceName(switcher->currentScene)); SetVariableValue(GetWeakSourceName(GetCurrentScene()));
SetTempVarValue("current", SetTempVarValue("current",
GetWeakSourceName(switcher->currentScene)); GetWeakSourceName(GetCurrentScene()));
return !sceneChanged; return !sceneChanged;
case Type::CURRENT_PATTERN: { case Type::CURRENT_PATTERN: {
auto scene = getCurrentSceneHelper(_useTransitionTargetScene); auto scene = getCurrentSceneHelper(_useTransitionTargetScene);
@@ -189,8 +188,8 @@ bool MacroConditionScene::Load(obs_data_t *obj)
break; break;
default: default:
blog(LOG_WARNING, blog(LOG_WARNING,
"failed to convert scene condition type (%d) for macro %s", "failed to convert scene condition type (%d)",
oldType, GetMacro()->Name().c_str()); oldType);
_type = Type::CURRENT; _type = Type::CURRENT;
break; break;
} }

View File

@@ -1,5 +1,4 @@
#include "macro-condition-streaming.hpp" #include "macro-condition-streaming.hpp"
#include "switcher-data.hpp"
#include "utility.hpp" #include "utility.hpp"
namespace advss { namespace advss {
@@ -25,6 +24,32 @@ const static std::map<MacroConditionStream::Condition, std::string>
"AdvSceneSwitcher.condition.stream.state.keyFrameInterval"}, "AdvSceneSwitcher.condition.stream.state.keyFrameInterval"},
}; };
static bool setupStreamingEventHandler();
static bool steamingEventHandlerIsSetup = setupStreamingEventHandler();
static std::chrono::high_resolution_clock::time_point streamStartTime{};
static std::chrono::high_resolution_clock::time_point streamStopTime{};
bool setupStreamingEventHandler()
{
static auto handleStreamingEvents = [](enum obs_frontend_event event,
void *) {
switch (event) {
case OBS_FRONTEND_EVENT_STREAMING_STARTING:
streamStartTime =
std::chrono::high_resolution_clock::now();
break;
case OBS_FRONTEND_EVENT_STREAMING_STOPPING:
streamStopTime =
std::chrono::high_resolution_clock::now();
break;
default:
break;
};
};
obs_frontend_add_event_callback(handleStreamingEvents, nullptr);
return true;
}
int MacroConditionStream::GetKeyFrameInterval() int MacroConditionStream::GetKeyFrameInterval()
{ {
const auto configPath = GetPathInProfileDir("streamEncoder.json"); const auto configPath = GetPathInProfileDir("streamEncoder.json");
@@ -42,10 +67,8 @@ bool MacroConditionStream::CheckCondition()
{ {
bool match = false; bool match = false;
bool streamStarting = switcher->lastStreamStartingTime != bool streamStarting = streamStartTime != _lastStreamStartingTime;
_lastStreamStartingTime; bool streamStopping = streamStopTime != _lastStreamStoppingTime;
bool streamStopping = switcher->lastStreamStoppingTime !=
_lastStreamStoppingTime;
switch (_condition) { switch (_condition) {
case Condition::STOP: case Condition::STOP:
@@ -68,10 +91,10 @@ bool MacroConditionStream::CheckCondition()
} }
if (streamStarting) { if (streamStarting) {
_lastStreamStartingTime = switcher->lastStreamStartingTime; _lastStreamStartingTime = streamStartTime;
} }
if (streamStopping) { if (streamStopping) {
_lastStreamStoppingTime = switcher->lastStreamStoppingTime; _lastStreamStoppingTime = streamStopTime;
} }
return match; return match;
} }

View File

@@ -1,5 +1,5 @@
#include "macro-condition-transition.hpp" #include "macro-condition-transition.hpp"
#include "switcher-data.hpp" #include "scene-switch-helpers.hpp"
#include "utility.hpp" #include "utility.hpp"
namespace advss { namespace advss {
@@ -50,8 +50,8 @@ static bool isTargetScene(OBSWeakSource &target)
bool MacroConditionTransition::CheckCondition() bool MacroConditionTransition::CheckCondition()
{ {
bool anyTransitionEnded = _lastTransitionEndTime != bool anyTransitionEnded = _lastTransitionEndTime !=
switcher->lastTransitionEndTime; GetLastTransitionEndTime();
bool anyTransitionStarted = switcher->AnySceneTransitionStarted(); bool anyTransitionStarted = AnySceneTransitionStarted();
bool transitionStarted = false; bool transitionStarted = false;
bool transitionEnded = false; bool transitionEnded = false;
@@ -82,7 +82,7 @@ bool MacroConditionTransition::CheckCondition()
break; break;
case TransitionCondition::TRANSITION_SOURCE: case TransitionCondition::TRANSITION_SOURCE:
ret = anyTransitionStarted && ret = anyTransitionStarted &&
_scene.GetScene() == switcher->currentScene; _scene.GetScene() == GetCurrentScene();
break; break;
case TransitionCondition::TRANSITION_TARGET: { case TransitionCondition::TRANSITION_TARGET: {
auto scene = _scene.GetScene(); auto scene = _scene.GetScene();
@@ -101,7 +101,7 @@ bool MacroConditionTransition::CheckCondition()
_ended = false; _ended = false;
} }
if (anyTransitionEnded) { if (anyTransitionEnded) {
_lastTransitionEndTime = switcher->lastTransitionEndTime; _lastTransitionEndTime = GetLastTransitionEndTime();
} }
return ret; return ret;

View File

@@ -1,5 +1,4 @@
#include "macro-condition-websocket.hpp" #include "macro-condition-websocket.hpp"
#include "switcher-data.hpp"
#include "utility.hpp" #include "utility.hpp"
#include <regex> #include <regex>

View File

@@ -1,5 +1,5 @@
#include "macro-condition-window.hpp" #include "macro-condition-window.hpp"
#include "switcher-data.hpp" #include "plugin-state-helpers.hpp"
#include "platform-funcs.hpp" #include "platform-funcs.hpp"
#include "utility.hpp" #include "utility.hpp"
@@ -43,7 +43,8 @@ static bool windowContainsText(const std::string &window,
bool MacroConditionWindow::WindowMatchesRequirements( bool MacroConditionWindow::WindowMatchesRequirements(
const std::string &window) const const std::string &window) const
{ {
const bool focusCheckOK = (!_focus || window == switcher->currentTitle); const bool focusCheckOK =
(!_focus || window == ForegroundWindowTitle());
if (!focusCheckOK) { if (!focusCheckOK) {
return false; return false;
} }
@@ -104,13 +105,13 @@ void MacroConditionWindow::SetVariableValueBasedOnMatch(
auto text = GetTextInWindow(matchWindow); auto text = GetTextInWindow(matchWindow);
SetVariableValue(text.value_or("")); SetVariableValue(text.value_or(""));
} else { } else {
SetVariableValue(switcher->currentTitle); SetVariableValue(ForegroundWindowTitle());
} }
} }
static bool foregroundWindowChanged() static bool foregroundWindowChanged()
{ {
return switcher->currentTitle != switcher->lastTitle; return ForegroundWindowTitle() != PreviousForegroundWindowTitle();
} }
bool MacroConditionWindow::CheckCondition() bool MacroConditionWindow::CheckCondition()
@@ -432,7 +433,7 @@ void MacroConditionWindowEdit::WindowFocusChanged(int state)
void MacroConditionWindowEdit::UpdateFocusWindow() void MacroConditionWindowEdit::UpdateFocusWindow()
{ {
_focusWindow->setText(QString::fromStdString(switcher->currentTitle)); _focusWindow->setText(QString::fromStdString(ForegroundWindowTitle()));
} }
void MacroConditionWindowEdit::SetWidgetVisibility() void MacroConditionWindowEdit::SetWidgetVisibility()

View File

@@ -0,0 +1,59 @@
#include "macro-helpers.hpp"
#include "plugin-state-helpers.hpp"
namespace advss {
static std::atomic_bool abortMacroWait = {false};
static std::atomic_bool macroSceneSwitched = {false};
static std::atomic_int shutdownConditionCount = {0};
std::condition_variable &GetMacroWaitCV()
{
static std::condition_variable cv;
return cv;
}
std::condition_variable &GetMacroTransitionCV()
{
static std::condition_variable cv;
return cv;
}
std::atomic_bool &MacroWaitShouldAbort()
{
return abortMacroWait;
}
void SetMacroAbortWait(bool value)
{
abortMacroWait = value;
}
bool ShutdownCheckIsNecessary()
{
return shutdownConditionCount > 0;
}
std::atomic_int &GetShutdownConditionCount()
{
return shutdownConditionCount;
}
void SetMacroSwitchedScene(bool value)
{
static bool setupDone = false;
if (!setupDone) {
// Will always be called with switcher lock already held
AddIntervalResetStep([]() { macroSceneSwitched = false; },
false);
setupDone = true;
}
macroSceneSwitched = value;
}
bool MacroSwitchedScene()
{
return macroSceneSwitched;
}
} // namespace advss

View File

@@ -0,0 +1,21 @@
#pragma once
#include <atomic>
#include <condition_variable>
#include <string_view>
namespace advss {
constexpr std::string_view GetSceneSwitchActionId()
{
return "scene_switch";
}
std::condition_variable &GetMacroWaitCV();
std::condition_variable &GetMacroTransitionCV();
std::atomic_bool &MacroWaitShouldAbort();
void SetMacroAbortWait(bool);
bool ShutdownCheckIsNecessary();
std::atomic_int &GetShutdownConditionCount();
void SetMacroSwitchedScene(bool value);
bool MacroSwitchedScene();
} // namespace advss

View File

@@ -9,6 +9,8 @@
namespace advss { namespace advss {
static MacroProperties macroProperties;
void MacroProperties::Save(obs_data_t *obj) const void MacroProperties::Save(obs_data_t *obj) const
{ {
auto data = obs_data_create(); auto data = obs_data_create();
@@ -325,4 +327,19 @@ bool MacroPropertiesDialog::AskForSettings(QWidget *parent,
return true; return true;
} }
MacroProperties &GetGlobalMacroProperties()
{
return macroProperties;
}
void SaveGlobalMacroProperties(obs_data_t *obj)
{
macroProperties.Save(obj);
}
void LoadGlobalMacroProperties(obs_data_t *obj)
{
macroProperties.Load(obj);
}
} // namespace advss } // namespace advss

View File

@@ -68,4 +68,8 @@ private:
int _conditionsFalseTextRow = -1; int _conditionsFalseTextRow = -1;
}; };
MacroProperties &GetGlobalMacroProperties();
void SaveGlobalMacroProperties(obs_data_t *obj);
void LoadGlobalMacroProperties(obs_data_t *obj);
} // namespace advss } // namespace advss

View File

@@ -44,8 +44,28 @@ MacroSegmentList::~MacroSegmentList()
} }
} }
static bool posIsInScrollbar(const QScrollBar *scrollbar, const QPoint &pos)
{
if (!scrollbar) {
return false;
}
if (!scrollbar->isVisible()) {
return false;
}
const auto geo = scrollbar->geometry();
const auto globalGeo = QRect(scrollbar->mapToGlobal(geo.topLeft()),
scrollbar->mapToGlobal(geo.bottomRight()));
return globalGeo.contains(pos);
}
int MacroSegmentList::GetDragIndex(const QPoint &pos) int MacroSegmentList::GetDragIndex(const QPoint &pos)
{ {
// Don't drag widget when interacting with the scrollbars
if (posIsInScrollbar(horizontalScrollBar(), mapTo(this, pos)) ||
posIsInScrollbar(verticalScrollBar(), mapTo(this, pos))) {
return -1;
}
for (int idx = 0; idx < _contentLayout->count(); ++idx) { for (int idx = 0; idx < _contentLayout->count(); ++idx) {
auto item = _contentLayout->itemAt(idx); auto item = _contentLayout->itemAt(idx);
if (!item) { if (!item) {
@@ -161,7 +181,7 @@ void MacroSegmentList::mousePressEvent(QMouseEvent *event)
#else #else
_dragPosition = GetDragIndex(event->globalPosition().toPoint()); _dragPosition = GetDragIndex(event->globalPosition().toPoint());
#endif #endif
emit SelectionChagned(_dragPosition); emit SelectionChanged(_dragPosition);
} else { } else {
_dragPosition = -1; _dragPosition = -1;
} }

View File

@@ -28,7 +28,7 @@ public:
QVBoxLayout *ContentLayout() { return _contentLayout; } QVBoxLayout *ContentLayout() { return _contentLayout; }
signals: signals:
void SelectionChagned(int idx); void SelectionChanged(int idx);
void Reorder(int source, int target); void Reorder(int source, int target);
protected: protected:

View File

@@ -4,7 +4,7 @@
// so it makes sense to include them here: // so it makes sense to include them here:
#include "log-helper.hpp" #include "log-helper.hpp"
#include "obs-module-helper.hpp" #include "obs-module-helper.hpp"
#include "sync-helper.hpp" #include "sync-helpers.hpp"
#include "temp-variable.hpp" #include "temp-variable.hpp"
#include <QWidget> #include <QWidget>

View File

@@ -1,6 +1,6 @@
#include "macro-selection.hpp" #include "macro-selection.hpp"
#include "macro.hpp"
#include "advanced-scene-switcher.hpp" #include "advanced-scene-switcher.hpp"
#include "switcher-data.hpp"
#include "utility.hpp" #include "utility.hpp"
#include <QStandardItemModel> #include <QStandardItemModel>
@@ -12,7 +12,7 @@ MacroSelection::MacroSelection(QWidget *parent)
: FilterComboBox(parent, : FilterComboBox(parent,
obs_module_text("AdvSceneSwitcher.selectMacro")) obs_module_text("AdvSceneSwitcher.selectMacro"))
{ {
for (const auto &m : switcher->macros) { for (const auto &m : GetMacros()) {
if (m->IsGroup()) { if (m->IsGroup()) {
continue; continue;
} }

View File

@@ -63,7 +63,7 @@ bool AdvSceneSwitcher::AddNewMacro(std::shared_ptr<Macro> &res,
} }
res = std::make_shared<Macro>( res = std::make_shared<Macro>(
name, switcher->macroProperties._newMacroRegisterHotkeys); name, GetGlobalMacroProperties()._newMacroRegisterHotkeys);
return true; return true;
} }
@@ -197,8 +197,8 @@ static void addGroupSubitems(std::vector<std::shared_ptr<Macro>> &macros,
subitems.reserve(group->GroupSize()); subitems.reserve(group->GroupSize());
// Find all subitems // Find all subitems
for (auto it = switcher->macros.begin(); it < switcher->macros.end(); auto allMacros = GetMacros();
it++) { for (auto it = allMacros.begin(); it < allMacros.end(); it++) {
if ((*it)->Name() == group->Name()) { if ((*it)->Name() == group->Name()) {
for (uint32_t i = 1; i <= group->GroupSize(); i++) { for (uint32_t i = 1; i <= group->GroupSize(); i++) {
subitems.emplace_back(*std::next(it, i)); subitems.emplace_back(*std::next(it, i));
@@ -243,7 +243,7 @@ void AdvSceneSwitcher::ExportMacros()
obs_data_array_push_back(macroArray, obj); obs_data_array_push_back(macroArray, obj);
} }
obs_data_set_array(data, "macros", macroArray); obs_data_set_array(data, "macros", macroArray);
switcher->SaveVariables(data); SaveVariables(data);
obs_data_set_string(data, "version", g_GIT_TAG); obs_data_set_string(data, "version", g_GIT_TAG);
auto json = obs_data_get_json(data); auto json = obs_data_get_json(data);
QString exportString(json); QString exportString(json);
@@ -382,7 +382,7 @@ static void importVariables(obs_data_t *obj)
if (variableWithNameExists(var->Name())) { if (variableWithNameExists(var->Name())) {
continue; continue;
} }
switcher->variables.emplace_back(var); GetVariables().emplace_back(var);
} }
} }
@@ -429,7 +429,7 @@ void AdvSceneSwitcher::ImportMacros()
} }
importedMacros.emplace_back(macro); importedMacros.emplace_back(macro);
switcher->macros.emplace_back(macro); GetMacros().emplace_back(macro);
if (groupSize > 0 && !macro->IsGroup()) { if (groupSize > 0 && !macro->IsGroup()) {
Macro::PrepareMoveToGroup(group, macro); Macro::PrepareMoveToGroup(group, macro);
groupSize--; groupSize--;
@@ -449,8 +449,8 @@ void AdvSceneSwitcher::ImportMacros()
macro->PostLoad(); macro->PostLoad();
} }
ui->macros->Reset(switcher->macros, ui->macros->Reset(GetMacros(),
switcher->macroProperties._highlightExecuted); GetGlobalMacroProperties()._highlightExecuted);
} }
void AdvSceneSwitcher::on_macroName_editingFinished() void AdvSceneSwitcher::on_macroName_editingFinished()
@@ -729,8 +729,8 @@ void AdvSceneSwitcher::MacroSelectionChanged()
void AdvSceneSwitcher::HighlightOnChange() void AdvSceneSwitcher::HighlightOnChange()
{ {
if (!switcher->macroProperties._highlightActions && if (!GetGlobalMacroProperties()._highlightActions &&
!switcher->macroProperties._highlightExecuted) { !GetGlobalMacroProperties()._highlightExecuted) {
return; return;
} }
@@ -747,13 +747,13 @@ void AdvSceneSwitcher::HighlightOnChange()
void AdvSceneSwitcher::on_macroProperties_clicked() void AdvSceneSwitcher::on_macroProperties_clicked()
{ {
MacroProperties prop = switcher->macroProperties; MacroProperties prop = GetGlobalMacroProperties();
bool accepted = MacroPropertiesDialog::AskForSettings( bool accepted = MacroPropertiesDialog::AskForSettings(
this, prop, GetSelectedMacro().get()); this, prop, GetSelectedMacro().get());
if (!accepted) { if (!accepted) {
return; return;
} }
switcher->macroProperties = prop; GetGlobalMacroProperties() = prop;
emit HighlightMacrosChanged(prop._highlightExecuted); emit HighlightMacrosChanged(prop._highlightExecuted);
emit HighlightActionsChanged(prop._highlightActions); emit HighlightActionsChanged(prop._highlightActions);
emit HighlightConditionsChanged(prop._highlightConditions); emit HighlightConditionsChanged(prop._highlightConditions);
@@ -789,11 +789,11 @@ void AdvSceneSwitcher::SetupMacroTab()
{ {
ui->macroElseActions->installEventFilter(this); ui->macroElseActions->installEventFilter(this);
if (switcher->macros.size() == 0 && !switcher->disableHints) { if (GetMacros().size() == 0 && !switcher->disableHints) {
addPulse = PulseWidget(ui->macroAdd, QColor(Qt::green)); addPulse = PulseWidget(ui->macroAdd, QColor(Qt::green));
} }
ui->macros->Reset(switcher->macros, ui->macros->Reset(GetMacros(),
switcher->macroProperties._highlightExecuted); GetGlobalMacroProperties()._highlightExecuted);
connect(ui->macros, SIGNAL(MacroSelectionAboutToChange()), this, connect(ui->macros, SIGNAL(MacroSelectionAboutToChange()), this,
SLOT(MacroSelectionAboutToChange())); SLOT(MacroSelectionAboutToChange()));
connect(ui->macros, SIGNAL(MacroSelectionChanged()), this, connect(ui->macros, SIGNAL(MacroSelectionChanged()), this,
@@ -802,21 +802,21 @@ void AdvSceneSwitcher::SetupMacroTab()
ui->conditionsList->SetHelpMsg( ui->conditionsList->SetHelpMsg(
obs_module_text("AdvSceneSwitcher.macroTab.editConditionHelp")); obs_module_text("AdvSceneSwitcher.macroTab.editConditionHelp"));
connect(ui->conditionsList, &MacroSegmentList::SelectionChagned, this, connect(ui->conditionsList, &MacroSegmentList::SelectionChanged, this,
&AdvSceneSwitcher::MacroConditionSelectionChanged); &AdvSceneSwitcher::MacroConditionSelectionChanged);
connect(ui->conditionsList, &MacroSegmentList::Reorder, this, connect(ui->conditionsList, &MacroSegmentList::Reorder, this,
&AdvSceneSwitcher::MacroConditionReorder); &AdvSceneSwitcher::MacroConditionReorder);
ui->actionsList->SetHelpMsg( ui->actionsList->SetHelpMsg(
obs_module_text("AdvSceneSwitcher.macroTab.editActionHelp")); obs_module_text("AdvSceneSwitcher.macroTab.editActionHelp"));
connect(ui->actionsList, &MacroSegmentList::SelectionChagned, this, connect(ui->actionsList, &MacroSegmentList::SelectionChanged, this,
&AdvSceneSwitcher::MacroActionSelectionChanged); &AdvSceneSwitcher::MacroActionSelectionChanged);
connect(ui->actionsList, &MacroSegmentList::Reorder, this, connect(ui->actionsList, &MacroSegmentList::Reorder, this,
&AdvSceneSwitcher::MacroActionReorder); &AdvSceneSwitcher::MacroActionReorder);
ui->elseActionsList->SetHelpMsg(obs_module_text( ui->elseActionsList->SetHelpMsg(obs_module_text(
"AdvSceneSwitcher.macroTab.editElseActionHelp")); "AdvSceneSwitcher.macroTab.editElseActionHelp"));
connect(ui->elseActionsList, &MacroSegmentList::SelectionChagned, this, connect(ui->elseActionsList, &MacroSegmentList::SelectionChanged, this,
&AdvSceneSwitcher::MacroElseActionSelectionChanged); &AdvSceneSwitcher::MacroElseActionSelectionChanged);
connect(ui->elseActionsList, &MacroSegmentList::Reorder, this, connect(ui->elseActionsList, &MacroSegmentList::Reorder, this,
&AdvSceneSwitcher::MacroElseActionReorder); &AdvSceneSwitcher::MacroElseActionReorder);

View File

@@ -1,7 +1,7 @@
#include "macro-tree.hpp" #include "macro-tree.hpp"
#include "macro.hpp" #include "macro.hpp"
#include "sync-helpers.hpp"
#include "utility.hpp" #include "utility.hpp"
#include "switcher-data.hpp"
#include <obs.h> #include <obs.h>
#include <string> #include <string>
@@ -395,7 +395,7 @@ CountItemsVisibleInModel(const std::deque<std::shared_ptr<Macro>> &macros)
void MacroTreeModel::Add(std::shared_ptr<Macro> item) void MacroTreeModel::Add(std::shared_ptr<Macro> item)
{ {
std::lock_guard<std::mutex> lock(switcher->m); auto lock = LockContext();
auto idx = CountItemsVisibleInModel(_macros); auto idx = CountItemsVisibleInModel(_macros);
beginInsertRows(QModelIndex(), idx, idx); beginInsertRows(QModelIndex(), idx, idx);
_macros.emplace_back(item); _macros.emplace_back(item);
@@ -408,7 +408,7 @@ void MacroTreeModel::Add(std::shared_ptr<Macro> item)
void MacroTreeModel::Remove(std::shared_ptr<Macro> item) void MacroTreeModel::Remove(std::shared_ptr<Macro> item)
{ {
std::lock_guard<std::mutex> lock(switcher->m); auto lock = LockContext();
auto uiStartIdx = GetItemModelIndex(item); auto uiStartIdx = GetItemModelIndex(item);
if (uiStartIdx == -1) { if (uiStartIdx == -1) {
return; return;
@@ -643,7 +643,7 @@ void MacroTreeModel::GroupSelectedItems(QModelIndexList &indices)
return; return;
} }
std::lock_guard<std::mutex> lock(switcher->m); auto lock = LockContext();
QString name = GetNewGroupName(); QString name = GetNewGroupName();
std::vector<std::shared_ptr<Macro>> itemsToGroup; std::vector<std::shared_ptr<Macro>> itemsToGroup;
itemsToGroup.reserve(indices.size()); itemsToGroup.reserve(indices.size());
@@ -691,7 +691,7 @@ void MacroTreeModel::UngroupSelectedGroups(QModelIndexList &indices)
return; return;
} }
std::lock_guard<std::mutex> lock(switcher->m); auto lock = LockContext();
for (int i = indices.count() - 1; i >= 0; i--) { for (int i = indices.count() - 1; i >= 0; i--) {
std::shared_ptr<Macro> item = _macros[ModelIndexToMacroIndex( std::shared_ptr<Macro> item = _macros[ModelIndexToMacroIndex(
indices[i].row(), _macros)]; indices[i].row(), _macros)];
@@ -1055,7 +1055,7 @@ void MacroTree::dropEvent(QDropEvent *event)
} }
// Move items in backend // Move items in backend
std::lock_guard<std::mutex> lock(switcher->m); auto lock = LockContext();
int to = row; int to = row;
try { try {
to = ModelIndexToMacroIndex(row, items); to = ModelIndexToMacroIndex(row, items);
@@ -1181,7 +1181,7 @@ void MacroTree::Remove(std::shared_ptr<Macro> item) const
void MacroTree::Up(std::shared_ptr<Macro> item) const void MacroTree::Up(std::shared_ptr<Macro> item) const
{ {
std::lock_guard<std::mutex> lock(switcher->m); auto lock = LockContext();
auto above = GetModel()->Neighbor(item, true); auto above = GetModel()->Neighbor(item, true);
if (!above) { if (!above) {
return; return;
@@ -1207,7 +1207,7 @@ void MacroTree::Up(std::shared_ptr<Macro> item) const
void MacroTree::Down(std::shared_ptr<Macro> item) const void MacroTree::Down(std::shared_ptr<Macro> item) const
{ {
std::lock_guard<std::mutex> lock(switcher->m); auto lock = LockContext();
auto below = GetModel()->Neighbor(item, false); auto below = GetModel()->Neighbor(item, false);
if (!below) { if (!below) {
return; return;

View File

@@ -1,20 +1,24 @@
#include "macro.hpp" #include "macro.hpp"
#include "macro-action-edit.hpp" #include "macro-action-factory.hpp"
#include "macro-condition-edit.hpp" #include "macro-condition-factory.hpp"
#include "macro-dock.hpp" #include "macro-dock.hpp"
#include "macro-action-scene-switch.hpp" #include "macro-helpers.hpp"
#include "switcher-data.hpp" #include "plugin-state-helpers.hpp"
#include "hotkey.hpp" #include "hotkey.hpp"
#include "sync-helpers.hpp"
#include "utility.hpp"
#include <limits> #include <limits>
#undef max #undef max
#include <chrono> #include <chrono>
#include <unordered_map> #include <unordered_map>
#include <QAction>
#include <QMainWindow> #include <QMainWindow>
namespace advss { namespace advss {
constexpr int perfLogThreshold = 300; static constexpr int perfLogThreshold = 300;
static std::deque<std::shared_ptr<Macro>> macros;
Macro::Macro(const std::string &name, const bool addHotkey) Macro::Macro(const std::string &name, const bool addHotkey)
{ {
@@ -33,7 +37,7 @@ Macro::~Macro()
// Keep the dock widgets in case of shutdown so they can be restored by // Keep the dock widgets in case of shutdown so they can be restored by
// OBS on startup // OBS on startup
if (!switcher->obsIsShuttingDown) { if (!OBSIsShuttingDown()) {
RemoveDock(); RemoveDock();
} }
} }
@@ -53,9 +57,8 @@ Macro::CreateGroup(const std::string &name,
void Macro::RemoveGroup(std::shared_ptr<Macro> group) void Macro::RemoveGroup(std::shared_ptr<Macro> group)
{ {
auto it = std::find(switcher->macros.begin(), switcher->macros.end(), auto it = std::find(macros.begin(), macros.end(), group);
group); if (it == macros.end()) {
if (it == switcher->macros.end()) {
return; return;
} }
@@ -65,12 +68,12 @@ void Macro::RemoveGroup(std::shared_ptr<Macro> group)
(*m)->SetParent(nullptr); (*m)->SetParent(nullptr);
} }
switcher->macros.erase(it); macros.erase(it);
} }
void Macro::PrepareMoveToGroup(Macro *group, std::shared_ptr<Macro> item) void Macro::PrepareMoveToGroup(Macro *group, std::shared_ptr<Macro> item)
{ {
for (auto &m : switcher->macros) { for (const auto &m : macros) {
if (m.get() == group) { if (m.get() == group) {
PrepareMoveToGroup(m, item); PrepareMoveToGroup(m, item);
return; return;
@@ -351,7 +354,7 @@ void Macro::AddHelperThread(std::thread &&newThread)
void Macro::Stop() void Macro::Stop()
{ {
_stop = true; _stop = true;
switcher->macroWaitCv.notify_all(); GetMacroWaitCV().notify_all();
for (auto &t : _helperThreads) { for (auto &t : _helperThreads) {
if (t.joinable()) { if (t.joinable()) {
t.join(); t.join();
@@ -743,15 +746,13 @@ bool Macro::PostLoad()
bool Macro::SwitchesScene() const bool Macro::SwitchesScene() const
{ {
MacroActionSwitchScene temp(nullptr);
auto sceneSwitchId = temp.GetId();
for (const auto &a : _actions) { for (const auto &a : _actions) {
if (a->GetId() == sceneSwitchId) { if (a->GetId() == GetSceneSwitchActionId()) {
return true; return true;
} }
} }
for (const auto &a : _elseActions) { for (const auto &a : _elseActions) {
if (a->GetId() == sceneSwitchId) { if (a->GetId() == GetSceneSwitchActionId()) {
return true; return true;
} }
} }
@@ -947,7 +948,7 @@ void Macro::EnableDock(bool value)
// geometry set here. // geometry set here.
// The function calls here are only intended to attempt to restore the // The function calls here are only intended to attempt to restore the
// dock status when switching scene collections. // dock status when switching scene collections.
if (switcher->startupLoadDone) { if (InitialLoadIsComplete()) {
_dock->setVisible(_dockIsVisible); _dock->setVisible(_dockIsVisible);
if (window->dockWidgetArea(_dock) != _dockArea) { if (window->dockWidgetArea(_dock) != _dockArea) {
window->addDockWidget(_dockArea, _dock); window->addDockWidget(_dockArea, _dock);
@@ -1163,12 +1164,10 @@ void Macro::SetHotkeysDesc() const
_name, _togglePauseHotkey); _name, _togglePauseHotkey);
} }
void SwitcherData::SaveMacros(obs_data_t *obj) void SaveMacros(obs_data_t *obj)
{ {
switcher->macroProperties.Save(obj);
obs_data_array_t *macroArray = obs_data_array_create(); obs_data_array_t *macroArray = obs_data_array_create();
for (auto &m : macros) { for (const auto &m : macros) {
obs_data_t *array_obj = obs_data_create(); obs_data_t *array_obj = obs_data_create();
m->Save(array_obj); m->Save(array_obj);
@@ -1180,10 +1179,9 @@ void SwitcherData::SaveMacros(obs_data_t *obj)
obs_data_array_release(macroArray); obs_data_array_release(macroArray);
} }
void SwitcherData::LoadMacros(obs_data_t *obj) void LoadMacros(obs_data_t *obj)
{ {
Hotkey::ClearAllHotkeys(); Hotkey::ClearAllHotkeys();
switcher->macroProperties.Load(obj);
macros.clear(); macros.clear();
obs_data_array_t *macroArray = obs_data_get_array(obj, "macros"); obs_data_array_t *macroArray = obs_data_get_array(obj, "macros");
@@ -1200,7 +1198,7 @@ void SwitcherData::LoadMacros(obs_data_t *obj)
int groupCount = 0; int groupCount = 0;
std::shared_ptr<Macro> group; std::shared_ptr<Macro> group;
std::vector<std::shared_ptr<Macro>> invalidGroups; std::vector<std::shared_ptr<Macro>> invalidGroups;
for (auto &m : macros) { for (const auto &m : macros) {
if (groupCount && m->IsGroup()) { if (groupCount && m->IsGroup()) {
blog(LOG_ERROR, blog(LOG_ERROR,
"nested group detected - will delete \"%s\"", "nested group detected - will delete \"%s\"",
@@ -1235,23 +1233,28 @@ void SwitcherData::LoadMacros(obs_data_t *obj)
} }
} }
bool SwitcherData::CheckMacros() std::deque<std::shared_ptr<Macro>> &GetMacros()
{ {
bool ret = false; return macros;
for (auto &m : macros) { }
bool CheckMacros()
{
bool matchFound = false;
for (const auto &m : macros) {
if (m->CeckMatch() || m->ElseActions().size() > 0) { if (m->CeckMatch() || m->ElseActions().size() > 0) {
ret = true; matchFound = true;
// This has to be performed here for now as actions are // This has to be performed here for now as actions are
// not performed immediately after checking conditions. // not performed immediately after checking conditions.
if (m->SwitchesScene()) { if (m->SwitchesScene()) {
switcher->macroSceneSwitched = true; SetMacroSwitchedScene(true);
} }
} }
} }
return ret; return matchFound;
} }
bool SwitcherData::RunMacros() bool RunMacros()
{ {
// Create copy of macro list as elements might be removed, inserted, or // Create copy of macro list as elements might be removed, inserted, or
// reordered while macros are currently being executed. // reordered while macros are currently being executed.
@@ -1269,15 +1272,16 @@ bool SwitcherData::RunMacros()
// the constructor of AdvSceneSwitcher() to complete. // the constructor of AdvSceneSwitcher() to complete.
// The constructor of AdvSceneSwitcher() cannot continue however as it // The constructor of AdvSceneSwitcher() cannot continue however as it
// cannot lock the main switcher mutex. // cannot lock the main switcher mutex.
if (GetLock()) { auto lock = GetLoopLock();
GetLock()->unlock(); if (lock) {
lock->unlock();
} }
for (auto &m : runPhaseMacros) { for (auto &m : runPhaseMacros) {
if (!m || !m->ShouldRunActions()) { if (!m || !m->ShouldRunActions()) {
continue; continue;
} }
if (firstInterval && m->SkipExecOnStart()) { if (IsFirstInterval() && m->SkipExecOnStart()) {
blog(LOG_INFO, blog(LOG_INFO,
"skip execution of macro \"%s\" at startup", "skip execution of macro \"%s\" at startup",
m->Name().c_str()); m->Name().c_str());
@@ -1288,15 +1292,15 @@ bool SwitcherData::RunMacros()
blog(LOG_WARNING, "abort macro: %s", m->Name().c_str()); blog(LOG_WARNING, "abort macro: %s", m->Name().c_str());
} }
} }
if (GetLock()) { if (lock) {
GetLock()->lock(); lock->lock();
} }
return true; return true;
} }
Macro *GetMacroByName(const char *name) Macro *GetMacroByName(const char *name)
{ {
for (auto &m : switcher->macros) { for (const auto &m : macros) {
if (m->Name() == name) { if (m->Name() == name) {
return m.get(); return m.get();
} }
@@ -1312,7 +1316,7 @@ Macro *GetMacroByQString(const QString &name)
std::weak_ptr<Macro> GetWeakMacroByName(const char *name) std::weak_ptr<Macro> GetWeakMacroByName(const char *name)
{ {
for (auto &m : switcher->macros) { for (const auto &m : macros) {
if (m->Name() == name) { if (m->Name() == name) {
return m; return m;
} }
@@ -1323,7 +1327,7 @@ std::weak_ptr<Macro> GetWeakMacroByName(const char *name)
void InvalidateMacroTempVarValues() void InvalidateMacroTempVarValues()
{ {
for (auto &m : switcher->macros) { for (const auto &m : macros) {
m->InvalidateTempVarValues(); m->InvalidateTempVarValues();
} }
} }

View File

@@ -202,6 +202,11 @@ private:
QAction *_dockAction = nullptr; QAction *_dockAction = nullptr;
}; };
void LoadMacros(obs_data_t *obj);
void SaveMacros(obs_data_t *obj);
std::deque<std::shared_ptr<Macro>> &GetMacros();
bool CheckMacros();
bool RunMacros();
Macro *GetMacroByName(const char *name); Macro *GetMacroByName(const char *name);
Macro *GetMacroByQString(const QString &name); Macro *GetMacroByQString(const QString &name);
std::weak_ptr<Macro> GetWeakMacroByName(const char *name); std::weak_ptr<Macro> GetWeakMacroByName(const char *name);

View File

@@ -3,8 +3,8 @@
#include <utility.hpp> #include <utility.hpp>
#include <log-helper.hpp> #include <log-helper.hpp>
#include <obs-module-helper.hpp> #include <obs-module-helper.hpp>
#include <plugin-state-helper.hpp> #include <plugin-state-helpers.hpp>
#include <sync-helper.hpp> #include <sync-helpers.hpp>
namespace advss { namespace advss {

View File

@@ -3,7 +3,7 @@
#include "twitch-helpers.hpp" #include "twitch-helpers.hpp"
#include <log-helper.hpp> #include <log-helper.hpp>
#include <plugin-state-helper.hpp> #include <plugin-state-helpers.hpp>
namespace advss { namespace advss {

View File

@@ -3,7 +3,7 @@
#include "twitch-helpers.hpp" #include "twitch-helpers.hpp"
#include <log-helper.hpp> #include <log-helper.hpp>
#include <plugin-state-helper.hpp> #include <plugin-state-helpers.hpp>
namespace advss { namespace advss {

View File

@@ -1,7 +1,7 @@
#include "token.hpp" #include "token.hpp"
#include "twitch-helpers.hpp" #include "twitch-helpers.hpp"
#include <plugin-state-helper.hpp> #include <plugin-state-helpers.hpp>
#include <log-helper.hpp> #include <log-helper.hpp>
#include <obs-module-helper.hpp> #include <obs-module-helper.hpp>
#include <utility.hpp> #include <utility.hpp>

View File

@@ -1,7 +1,7 @@
#include "macro-condition-video.hpp" #include "macro-condition-video.hpp"
#include <macro-condition-edit.hpp> #include <macro-condition-edit.hpp>
#include <switcher-data.hpp> #include <plugin-state-helpers.hpp>
#include <utility.hpp> #include <utility.hpp>
#include <QFileDialog> #include <QFileDialog>
@@ -222,8 +222,8 @@ void MacroConditionVideo::GetScreenshot(bool blocking)
_areaParameters.area.width, _areaParameters.area.width,
_areaParameters.area.height); _areaParameters.area.height);
} }
new (&_screenshotData) ScreenshotHelper( new (&_screenshotData) ScreenshotHelper(source, screenshotArea,
source, screenshotArea, blocking, GetSwitcher()->interval); blocking, GetIntervalValue());
obs_source_release(source); obs_source_release(source);
_getNextScreenshot = false; _getNextScreenshot = false;
} }
@@ -1110,9 +1110,9 @@ MacroConditionVideoEdit::MacroConditionVideoEdit(
"AdvSceneSwitcher.condition.video.patternMatchMode.tip")); "AdvSceneSwitcher.condition.video.patternMatchMode.tip"));
populatePatternMatchModeSelection(_patternMatchMode); populatePatternMatchModeSelection(_patternMatchMode);
_throttleCount->setMinimum(1 * GetSwitcher()->interval); _throttleCount->setMinimum(1 * GetIntervalValue());
_throttleCount->setMaximum(10 * GetSwitcher()->interval); _throttleCount->setMaximum(10 * GetIntervalValue());
_throttleCount->setSingleStep(GetSwitcher()->interval); _throttleCount->setSingleStep(GetIntervalValue());
_brightness->setSizePolicy(QSizePolicy::MinimumExpanding, _brightness->setSizePolicy(QSizePolicy::MinimumExpanding,
QSizePolicy::Preferred); QSizePolicy::Preferred);
@@ -1507,7 +1507,7 @@ void MacroConditionVideoEdit::ThrottleCountChanged(int value)
} }
auto lock = LockContext(); auto lock = LockContext();
_entryData->_throttleCount = value / GetSwitcher()->interval; _entryData->_throttleCount = value / GetIntervalValue();
} }
void MacroConditionVideoEdit::ShowMatchClicked() void MacroConditionVideoEdit::ShowMatchClicked()
@@ -1629,7 +1629,7 @@ void MacroConditionVideoEdit::UpdateEntryData()
_entryData->_patternMatchParameters.matchMode)); _entryData->_patternMatchParameters.matchMode));
_throttleEnable->setChecked(_entryData->_throttleEnabled); _throttleEnable->setChecked(_entryData->_throttleEnabled);
_throttleCount->setValue(_entryData->_throttleCount * _throttleCount->setValue(_entryData->_throttleCount *
GetSwitcher()->interval); GetIntervalValue());
UpdatePreviewTooltip(); UpdatePreviewTooltip();
SetupPreviewDialogParams(); SetupPreviewDialogParams();
SetWidgetVisibility(); SetWidgetVisibility();

View File

@@ -383,6 +383,9 @@ static std::map<HotkeyType, CGKeyCode> keyTable = {
{HotkeyType::Key_8, kVK_ANSI_8}, {HotkeyType::Key_8, kVK_ANSI_8},
{HotkeyType::Key_9, kVK_ANSI_9}, {HotkeyType::Key_9, kVK_ANSI_9},
// Other
// Not sure which key to use for tilde
{HotkeyType::Key_F1, kVK_F1}, {HotkeyType::Key_F1, kVK_F1},
{HotkeyType::Key_F2, kVK_F2}, {HotkeyType::Key_F2, kVK_F2},
{HotkeyType::Key_F3, kVK_F3}, {HotkeyType::Key_F3, kVK_F3},

View File

@@ -1,5 +1,5 @@
#include "status-control.hpp" #include "status-control.hpp"
#include "switcher-data.hpp" #include "plugin-state-helpers.hpp"
#include "utility.hpp" #include "utility.hpp"
#include <obs-module.h> #include <obs-module.h>
@@ -51,10 +51,10 @@ StatusControl::StatusControl(QWidget *parent, bool noLayout)
setLayout(layout); setLayout(layout);
} }
if (switcher->stop) { if (PluginIsRunning()) {
SetStopped();
} else {
SetStarted(); SetStarted();
} else {
SetStopped();
} }
connect(&_timer, SIGNAL(timeout()), this, SLOT(UpdateStatus())); connect(&_timer, SIGNAL(timeout()), this, SLOT(UpdateStatus()));
_timer.start(1000); _timer.start(1000);
@@ -62,22 +62,18 @@ StatusControl::StatusControl(QWidget *parent, bool noLayout)
void StatusControl::ButtonClicked() void StatusControl::ButtonClicked()
{ {
if (switcher->th && switcher->th->isRunning()) { if (PluginIsRunning()) {
switcher->Stop(); StopPlugin();
SetStopped(); SetStopped();
} else { } else {
switcher->Start(); StartPlugin();
SetStarted(); SetStarted();
} }
} }
void StatusControl::UpdateStatus() void StatusControl::UpdateStatus()
{ {
if (!switcher) { if (PluginIsRunning()) {
return;
}
if (switcher->th && switcher->th->isRunning()) {
if (!_setToStopped) { if (!_setToStopped) {
return; return;
} }
@@ -104,7 +100,7 @@ void StatusControl::SetStopped()
_button->setText( _button->setText(
obs_module_text("AdvSceneSwitcher.generalTab.status.start")); obs_module_text("AdvSceneSwitcher.generalTab.status.start"));
_status->setText(obs_module_text("AdvSceneSwitcher.status.inactive")); _status->setText(obs_module_text("AdvSceneSwitcher.status.inactive"));
if (!switcher->disableHints) { if (HighlightUIElementsEnabled()) {
_pulse = PulseWidget(_status, QColor(Qt::red), _pulse = PulseWidget(_status, QColor(Qt::red),
QColor(0, 0, 0, 0)); QColor(0, 0, 0, 0));
} }

View File

@@ -14,6 +14,11 @@ std::mutex *GetSwitcherMutex()
return switcher ? &switcher->m : nullptr; return switcher ? &switcher->m : nullptr;
} }
std::unique_lock<std::mutex> *GetSwitcherLoopLock()
{
return switcher ? switcher->mainLoopLock : nullptr;
}
bool VerboseLoggingEnabled() bool VerboseLoggingEnabled()
{ {
return switcher ? switcher->verbose : false; return switcher ? switcher->verbose : false;
@@ -168,8 +173,13 @@ void SwitcherData::SaveVersion(obs_data_t *obj,
obs_data_set_string(obj, "version", currentVersion.c_str()); obs_data_set_string(obj, "version", currentVersion.c_str());
} }
void SwitcherData::AddIntervalResetStep(std::function<void()> function) void SwitcherData::AddIntervalResetStep(std::function<void()> function,
bool tryLock)
{ {
if (!tryLock) {
resetIntervalSteps.emplace_back(function);
return;
}
std::lock_guard<std::mutex> lock(switcher->m); std::lock_guard<std::mutex> lock(switcher->m);
resetIntervalSteps.emplace_back(function); resetIntervalSteps.emplace_back(function);
} }

View File

@@ -18,10 +18,9 @@
#include "macro-properties.hpp" #include "macro-properties.hpp"
#include "duration-control.hpp" #include "duration-control.hpp"
#include "curl-helper.hpp"
#include "priority-helper.hpp" #include "priority-helper.hpp"
#include "log-helper.hpp" #include "log-helper.hpp"
#include "plugin-state-helper.hpp" #include "plugin-state-helpers.hpp"
#include <condition_variable> #include <condition_variable>
#include <vector> #include <vector>
@@ -29,7 +28,6 @@
#include <mutex> #include <mutex>
#include <QDateTime> #include <QDateTime>
#include <QThread> #include <QThread>
#include <curl/curl.h>
#include <unordered_map> #include <unordered_map>
namespace advss { namespace advss {
@@ -47,6 +45,7 @@ class SwitcherData;
extern SwitcherData *switcher; extern SwitcherData *switcher;
SwitcherData *GetSwitcher(); SwitcherData *GetSwitcher();
std::mutex *GetSwitcherMutex(); std::mutex *GetSwitcherMutex();
std::unique_lock<std::mutex> *GetSwitcherLoopLock();
bool VerboseLoggingEnabled(); bool VerboseLoggingEnabled();
class SwitcherData { class SwitcherData {
@@ -54,7 +53,6 @@ public:
void Thread(); void Thread();
void Start(); void Start();
void Stop(); void Stop();
std::unique_lock<std::mutex> *GetLock() { return mainLoopLock; }
const char *Translate(const char *); const char *Translate(const char *);
obs_module_t *GetModule(); obs_module_t *GetModule();
@@ -68,28 +66,22 @@ public:
void AddSaveStep(std::function<void(obs_data_t *)>); void AddSaveStep(std::function<void(obs_data_t *)>);
void AddLoadStep(std::function<void(obs_data_t *)>); void AddLoadStep(std::function<void(obs_data_t *)>);
void AddPostLoadStep(std::function<void()>); void AddPostLoadStep(std::function<void()>);
void AddIntervalResetStep(std::function<void()>); void AddIntervalResetStep(std::function<void()>, bool lock = true);
bool CheckForMatch(OBSWeakSource &scene, OBSWeakSource &transition, bool CheckForMatch(OBSWeakSource &scene, OBSWeakSource &transition,
int &linger, bool &setPreviousSceneAsMatch, int &linger, bool &setPreviousSceneAsMatch,
bool &macroMatch); bool &macroMatch);
bool CheckMacros();
bool RunMacros();
void CheckNoMatchSwitch(bool &match, OBSWeakSource &scene, void CheckNoMatchSwitch(bool &match, OBSWeakSource &scene,
OBSWeakSource &transition, int &sleep); OBSWeakSource &transition, int &sleep);
/* --- Start of saving / loading section --- */ /* --- Start of saving / loading section --- */
void SaveSettings(obs_data_t *obj); void SaveSettings(obs_data_t *obj);
void SaveMacros(obs_data_t *obj);
void SaveVariables(obs_data_t *obj);
void SaveGeneralSettings(obs_data_t *obj); void SaveGeneralSettings(obs_data_t *obj);
void SaveHotkeys(obs_data_t *obj); void SaveHotkeys(obs_data_t *obj);
void SaveUISettings(obs_data_t *obj); void SaveUISettings(obs_data_t *obj);
void SaveVersion(obs_data_t *obj, const std::string &currentVersion); void SaveVersion(obs_data_t *obj, const std::string &currentVersion);
void LoadSettings(obs_data_t *obj); void LoadSettings(obs_data_t *obj);
void LoadMacros(obs_data_t *obj);
void LoadVariables(obs_data_t *obj);
void LoadGeneralSettings(obs_data_t *obj); void LoadGeneralSettings(obs_data_t *obj);
void LoadHotkeys(obs_data_t *obj); void LoadHotkeys(obs_data_t *obj);
void LoadUISettings(obs_data_t *obj); void LoadUISettings(obs_data_t *obj);
@@ -110,9 +102,6 @@ public:
std::unique_lock<std::mutex> *mainLoopLock = nullptr; std::unique_lock<std::mutex> *mainLoopLock = nullptr;
bool stop = false; bool stop = false;
std::condition_variable cv; std::condition_variable cv;
std::condition_variable macroWaitCv;
std::atomic_bool abortMacroWait = {false};
std::condition_variable macroTransitionCv;
std::vector<std::function<void(obs_data_t *)>> saveSteps; std::vector<std::function<void(obs_data_t *)>> saveSteps;
std::vector<std::function<void(obs_data_t *)>> loadSteps; std::vector<std::function<void(obs_data_t *)>> loadSteps;
@@ -122,21 +111,14 @@ public:
bool firstBoot = true; bool firstBoot = true;
bool transitionActive = false; bool transitionActive = false;
bool sceneColletionStop = false; bool sceneColletionStop = false;
bool replayBufferSaved = false;
bool obsIsShuttingDown = false; bool obsIsShuttingDown = false;
bool firstInterval = true; bool firstInterval = true;
bool firstIntervalAfterStop = true; bool firstIntervalAfterStop = true;
int shutdownConditionCount = 0;
bool startupLoadDone = false; bool startupLoadDone = false;
obs_source_t *waitScene = nullptr; obs_source_t *waitScene = nullptr;
OBSWeakSource currentScene = nullptr; OBSWeakSource currentScene = nullptr;
OBSWeakSource previousScene = nullptr; OBSWeakSource previousScene = nullptr;
std::chrono::high_resolution_clock::time_point lastSceneChangeTime{};
std::chrono::high_resolution_clock::time_point lastTransitionEndTime{};
std::chrono::high_resolution_clock::time_point lastStreamStartingTime{};
std::chrono::high_resolution_clock::time_point lastStreamStoppingTime{};
std::chrono::high_resolution_clock::time_point lastMatchTime;
/* --- Start of General tab section --- */ /* --- Start of General tab section --- */
@@ -161,25 +143,9 @@ public:
/* --- End of General tab section --- */ /* --- End of General tab section --- */
struct AudioFadeInfo {
std::atomic_bool active = {false};
std::atomic_int id = {0};
};
AudioFadeInfo masterAudioFade;
std::unordered_map<std::string, AudioFadeInfo> activeAudioFades;
MacroProperties macroProperties;
std::deque<std::shared_ptr<Macro>> macros;
bool macroSceneSwitched = false;
Curlhelper curl;
std::deque<std::shared_ptr<Item>> variables;
std::string lastTitle; std::string lastTitle;
std::string currentTitle; std::string currentTitle;
std::string currentForegroundProcess; std::string currentForegroundProcess;
std::pair<int, int> lastCursorPos = {0, 0};
bool cursorPosChanged = false;
std::vector<int> functionNamesByPriority = std::vector<int> functionNamesByPriority =
GetDefaultFunctionPriorityList(); GetDefaultFunctionPriorityList();

View File

@@ -1,7 +1,7 @@
#include "connection-manager.hpp" #include "connection-manager.hpp"
#include "utility.hpp" #include "utility.hpp"
#include "obs-module-helper.hpp" #include "obs-module-helper.hpp"
#include "plugin-state-helper.hpp" #include "plugin-state-helpers.hpp"
#include "name-dialog.hpp" #include "name-dialog.hpp"
#include <algorithm> #include <algorithm>

View File

@@ -15,7 +15,7 @@ constexpr auto curl_library_name = "libcurl.4.dylib";
constexpr auto curl_library_name = "libcurl.so.4"; constexpr auto curl_library_name = "libcurl.so.4";
#endif #endif
Curlhelper::Curlhelper() CurlHelper::CurlHelper()
{ {
if (LoadLib()) { if (LoadLib()) {
_curl = _init(); _curl = _init();
@@ -23,7 +23,7 @@ Curlhelper::Curlhelper()
} }
} }
Curlhelper::~Curlhelper() CurlHelper::~CurlHelper()
{ {
if (_lib) { if (_lib) {
if (_cleanup) { if (_cleanup) {
@@ -34,32 +34,46 @@ Curlhelper::~Curlhelper()
} }
} }
curl_slist *Curlhelper::SlistAppend(curl_slist *list, const char *string) CurlHelper &CurlHelper::GetInstance()
{ {
if (!_initialized) { static CurlHelper curl;
return curl;
}
bool CurlHelper::Initialized()
{
return GetInstance()._initialized;
}
curl_slist *CurlHelper::SlistAppend(curl_slist *list, const char *string)
{
auto &curl = GetInstance();
if (!curl._initialized) {
return nullptr; return nullptr;
} }
return _slistAppend(list, string); return curl._slistAppend(list, string);
} }
CURLcode Curlhelper::Perform() CURLcode CurlHelper::Perform()
{ {
if (!_initialized) { auto &curl = GetInstance();
if (!curl._initialized) {
return CURLE_FAILED_INIT; return CURLE_FAILED_INIT;
} }
return _perform(_curl); return curl._perform(curl._curl);
} }
char *Curlhelper::GetError(CURLcode code) char *CurlHelper::GetError(CURLcode code)
{ {
if (!_initialized) { auto &curl = GetInstance();
if (!curl._initialized) {
return (char *)"CURL initialization failed"; return (char *)"CURL initialization failed";
} }
return _error(code); return curl._error(code);
} }
bool Curlhelper::LoadLib() bool CurlHelper::LoadLib()
{ {
_lib = new QLibrary(curl_library_name, nullptr); _lib = new QLibrary(curl_library_name, nullptr);
if (Resolve()) { if (Resolve()) {
@@ -103,7 +117,7 @@ bool Curlhelper::LoadLib()
return false; return false;
} }
bool Curlhelper::Resolve() bool CurlHelper::Resolve()
{ {
_init = (initFunction)_lib->resolve("curl_easy_init"); _init = (initFunction)_lib->resolve("curl_easy_init");
_setopt = (setOptFunction)_lib->resolve("curl_easy_setopt"); _setopt = (setOptFunction)_lib->resolve("curl_easy_setopt");

View File

@@ -1,31 +1,35 @@
#pragma once #pragma once
#include <curl/curl.h> #include <curl/curl.h>
#include <QLibrary> #include <QLibrary>
#include <atomic>
namespace advss { namespace advss {
typedef CURL *(*initFunction)(void); class CurlHelper {
typedef CURLcode (*setOptFunction)(CURL *, CURLoption, ...);
typedef struct curl_slist *(*slistAppendFunction)(struct curl_slist *list,
const char *string);
typedef CURLcode (*performFunction)(CURL *);
typedef void (*cleanupFunction)(CURL *);
typedef char *(*errorFunction)(CURLcode);
class Curlhelper {
public: public:
Curlhelper(); static bool Initialized();
~Curlhelper(); template<typename... Args> static CURLcode SetOpt(CURLoption, Args...);
static struct curl_slist *SlistAppend(struct curl_slist *list,
bool Initialized() { return _initialized; } const char *string);
static CURLcode Perform();
template<typename... Args> CURLcode SetOpt(CURLoption, Args...); static char *GetError(CURLcode code);
struct curl_slist *SlistAppend(struct curl_slist *list,
const char *string);
CURLcode Perform();
char *GetError(CURLcode code);
private: private:
CurlHelper();
CurlHelper(const CurlHelper &) = delete;
CurlHelper &operator=(const CurlHelper &) = delete;
~CurlHelper();
typedef CURL *(*initFunction)(void);
typedef CURLcode (*setOptFunction)(CURL *, CURLoption, ...);
typedef struct curl_slist *(*slistAppendFunction)(
struct curl_slist *list, const char *string);
typedef CURLcode (*performFunction)(CURL *);
typedef void (*cleanupFunction)(CURL *);
typedef char *(*errorFunction)(CURLcode);
static CurlHelper &GetInstance();
bool LoadLib(); bool LoadLib();
bool Resolve(); bool Resolve();
@@ -37,16 +41,17 @@ private:
errorFunction _error = nullptr; errorFunction _error = nullptr;
CURL *_curl = nullptr; CURL *_curl = nullptr;
QLibrary *_lib; QLibrary *_lib;
bool _initialized = false; std::atomic_bool _initialized = {false};
}; };
template<typename... Args> template<typename... Args>
inline CURLcode Curlhelper::SetOpt(CURLoption option, Args... args) inline CURLcode CurlHelper::SetOpt(CURLoption option, Args... args)
{ {
if (!_initialized) { auto &curl = GetInstance();
if (!curl._initialized) {
return CURLE_FAILED_INIT; return CURLE_FAILED_INIT;
} }
return _setopt(_curl, option, args...); return curl._setopt(curl._curl, option, args...);
} }
} // namespace advss } // namespace advss

View File

@@ -54,7 +54,7 @@ FilterComboBox::FilterComboBox(QWidget *parent, const QString &placehodler)
connect(c, QOverload<const QModelIndex &>::of(&QCompleter::highlighted), connect(c, QOverload<const QModelIndex &>::of(&QCompleter::highlighted),
this, &FilterComboBox::CompleterHighlightChanged); this, &FilterComboBox::CompleterHighlightChanged);
connect(lineEdit(), &QLineEdit::textChanged, this, connect(lineEdit(), &QLineEdit::textChanged, this,
&FilterComboBox::TextChagned); &FilterComboBox::TextChanged);
} }
void FilterComboBox::SetFilterBehaviourEnabled(bool value) void FilterComboBox::SetFilterBehaviourEnabled(bool value)
@@ -108,7 +108,7 @@ void FilterComboBox::CompleterHighlightChanged(const QModelIndex &index)
emit currentTextChanged(text); emit currentTextChanged(text);
} }
void FilterComboBox::TextChagned(const QString &text) void FilterComboBox::TextChanged(const QString &text)
{ {
auto c = completer(); auto c = completer();
const bool completerActive = c->completionCount() > 0; const bool completerActive = c->completionCount() > 0;

View File

@@ -19,7 +19,7 @@ protected:
private slots: private slots:
void CompleterHighlightChanged(const QModelIndex &); void CompleterHighlightChanged(const QModelIndex &);
void TextChagned(const QString &); void TextChanged(const QString &);
private: private:
int _lastCompleterHighlightRow = -1; int _lastCompleterHighlightRow = -1;

View File

@@ -1,7 +1,7 @@
#include "macro-segment-selection.hpp" #include "macro-segment-selection.hpp"
#include "advanced-scene-switcher.hpp" #include "advanced-scene-switcher.hpp"
#include "obs-module-helper.hpp" #include "obs-module-helper.hpp"
#include "switcher-data.hpp" #include "plugin-state-helpers.hpp"
#include "utility.hpp" #include "utility.hpp"
namespace advss { namespace advss {
@@ -108,7 +108,7 @@ void MacroSegmentSelection::IndexChanged(const IntVariable &value)
void MacroSegmentSelection::MarkSelectedSegment() void MacroSegmentSelection::MarkSelectedSegment()
{ {
if (!GetSwitcher() || GetSwitcher()->disableHints) { if (!HighlightUIElementsEnabled()) {
return; return;
} }

View File

@@ -1,4 +1,4 @@
#include "plugin-state-helper.hpp" #include "plugin-state-helpers.hpp"
#include "switcher-data.hpp" #include "switcher-data.hpp"
namespace advss { namespace advss {
@@ -23,9 +23,9 @@ void AddPostLoadStep(std::function<void()> step)
GetSwitcher()->AddPostLoadStep(step); GetSwitcher()->AddPostLoadStep(step);
} }
void AddIntervalResetStep(std::function<void()> step) void AddIntervalResetStep(std::function<void()> step, bool lock)
{ {
GetSwitcher()->AddIntervalResetStep(step); GetSwitcher()->AddIntervalResetStep(step, lock);
} }
void StopPlugin() void StopPlugin()
@@ -38,24 +38,70 @@ void StartPlugin()
GetSwitcher()->Start(); GetSwitcher()->Start();
} }
bool PluginIsRunning()
{
return GetSwitcher() && GetSwitcher()->th &&
GetSwitcher()->th->isRunning();
}
int GetIntervalValue()
{
return GetSwitcher()->interval;
}
void SetPluginNoMatchBehavior(NoMatchBehavior behavior) void SetPluginNoMatchBehavior(NoMatchBehavior behavior)
{ {
GetSwitcher()->switchIfNotMatching = behavior; GetSwitcher()->switchIfNotMatching = behavior;
} }
void SetNoMatchScene(const OBSWeakSource &scene)
{
GetSwitcher()->nonMatchingScene = scene;
}
NoMatchBehavior GetPluginNoMatchBehavior() NoMatchBehavior GetPluginNoMatchBehavior()
{ {
return GetSwitcher()->switchIfNotMatching; return GetSwitcher()->switchIfNotMatching;
} }
void SetNoMatchScene(const OBSWeakSource &scene)
{
GetSwitcher()->nonMatchingScene = scene;
}
std::string ForegroundWindowTitle()
{
return GetSwitcher()->currentTitle;
}
std::string PreviousForegroundWindowTitle()
{
return GetSwitcher()->lastTitle;
}
bool SettingsWindowIsOpened() bool SettingsWindowIsOpened()
{ {
return GetSwitcher()->settingsWindowOpened; return GetSwitcher()->settingsWindowOpened;
} }
bool HighlightUIElementsEnabled()
{
return GetSwitcher() && !GetSwitcher()->disableHints;
}
bool OBSIsShuttingDown()
{
return GetSwitcher()->obsIsShuttingDown;
}
bool InitialLoadIsComplete()
{
return GetSwitcher()->startupLoadDone;
}
bool IsFirstInterval()
{
return GetSwitcher()->firstInterval;
}
bool IsFirstIntervalAfterStop()
{
return GetSwitcher()->firstIntervalAfterStop;
}
} // namespace advss } // namespace advss

View File

@@ -1,6 +1,7 @@
#pragma once #pragma once
#include <functional> #include <functional>
#include <obs.hpp> #include <obs.hpp>
#include <string>
namespace advss { namespace advss {
@@ -9,16 +10,27 @@ void LoadPluginSettings(obs_data_t *);
void AddSaveStep(std::function<void(obs_data_t *)>); void AddSaveStep(std::function<void(obs_data_t *)>);
void AddLoadStep(std::function<void(obs_data_t *)>); void AddLoadStep(std::function<void(obs_data_t *)>);
void AddPostLoadStep(std::function<void()>); void AddPostLoadStep(std::function<void()>);
void AddIntervalResetStep(std::function<void()>); void AddIntervalResetStep(std::function<void()>, bool lock = true);
void StopPlugin(); void StopPlugin();
void StartPlugin(); void StartPlugin();
bool PluginIsRunning();
int GetIntervalValue();
enum class NoMatchBehavior { NO_SWITCH = 0, SWITCH = 1, RANDOM_SWITCH = 2 }; enum class NoMatchBehavior { NO_SWITCH = 0, SWITCH = 1, RANDOM_SWITCH = 2 };
void SetPluginNoMatchBehavior(NoMatchBehavior); void SetPluginNoMatchBehavior(NoMatchBehavior);
NoMatchBehavior GetPluginNoMatchBehavior(); NoMatchBehavior GetPluginNoMatchBehavior();
void SetNoMatchScene(const OBSWeakSource &); void SetNoMatchScene(const OBSWeakSource &);
std::string ForegroundWindowTitle();
std::string PreviousForegroundWindowTitle();
bool SettingsWindowIsOpened(); bool SettingsWindowIsOpened();
bool HighlightUIElementsEnabled();
bool OBSIsShuttingDown();
bool InitialLoadIsComplete();
bool IsFirstInterval();
bool IsFirstIntervalAfterStop();
} // namespace advss } // namespace advss

View File

@@ -1,4 +1,5 @@
#include "process-config.hpp" #include "process-config.hpp"
#include "log-helper.hpp"
#include "name-dialog.hpp" #include "name-dialog.hpp"
#include "utility.hpp" #include "utility.hpp"
@@ -37,7 +38,7 @@ bool ProcessConfig::Load(obs_data_t *obj)
return true; return true;
} }
QStringList ProcessConfig::Args() QStringList ProcessConfig::Args() const
{ {
QStringList result; QStringList result;
for (auto &arg : _args) { for (auto &arg : _args) {
@@ -46,6 +47,42 @@ QStringList ProcessConfig::Args()
return result; return result;
} }
bool ProcessConfig::StartProcessDetached() const
{
return QProcess::startDetached(QString::fromStdString(Path()), Args(),
QString::fromStdString(WorkingDir()));
}
std::variant<int, ProcessConfig::ProcStartError>
ProcessConfig::StartProcessAndWait(int timeout) const
{
QProcess process;
process.setWorkingDirectory(QString::fromStdString(WorkingDir()));
process.start(QString::fromStdString(Path()), Args());
vblog(LOG_INFO, "run \"%s\" with a timeout of %d ms", Path().c_str(),
timeout);
if (!process.waitForFinished(timeout)) {
if (process.error() == QProcess::FailedToStart) {
vblog(LOG_INFO, "failed to start \"%s\"!",
Path().c_str());
return ProcStartError::FAILED_TO_START;
}
vblog(LOG_INFO,
"timeout while running \"%s\"\nAttempting to kill process!",
Path().c_str());
process.kill();
process.waitForFinished();
return ProcStartError::TIMEOUT;
}
if (process.exitStatus() == QProcess::NormalExit) {
return process.exitCode();
}
vblog(LOG_INFO, "process \"%s\" crashed!", Path().c_str());
return ProcStartError::CRASH;
}
ProcessConfigEdit::ProcessConfigEdit(QWidget *parent) ProcessConfigEdit::ProcessConfigEdit(QWidget *parent)
: QWidget(parent), : QWidget(parent),
_filePath(new FileSelection()), _filePath(new FileSelection()),
@@ -91,7 +128,7 @@ ProcessConfigEdit::ProcessConfigEdit(QWidget *parent)
_advancedSettingsLayout->addWidget(_argList); _advancedSettingsLayout->addWidget(_argList);
_advancedSettingsLayout->addLayout(workingDirectoryLayout); _advancedSettingsLayout->addLayout(workingDirectoryLayout);
auto *mainLayout = new QVBoxLayout; auto mainLayout = new QVBoxLayout;
mainLayout->setContentsMargins(0, 0, 0, 0); mainLayout->setContentsMargins(0, 0, 0, 0);
mainLayout->addLayout(entryLayout); mainLayout->addLayout(entryLayout);
mainLayout->addLayout(_advancedSettingsLayout); mainLayout->addLayout(_advancedSettingsLayout);
@@ -143,6 +180,9 @@ void ProcessConfigEdit::ShowAdvancedSettings(bool showAdvancedSettings)
_showAdvancedSettings->setVisible(!showAdvancedSettings); _showAdvancedSettings->setVisible(!showAdvancedSettings);
adjustSize(); adjustSize();
updateGeometry(); updateGeometry();
if (showAdvancedSettings) {
emit AdvancedSettingsEnabled();
}
} }
} // namespace advss } // namespace advss

View File

@@ -3,12 +3,13 @@
#include "string-list.hpp" #include "string-list.hpp"
#include <obs-data.h> #include <obs-data.h>
#include <obs-module.h> #include <obs-module-helper.hpp>
#include <QPushButton> #include <QPushButton>
#include <QListWidget> #include <QListWidget>
#include <QStringList> #include <QStringList>
#include <QVBoxLayout> #include <QVBoxLayout>
#include <variant>
namespace advss { namespace advss {
@@ -17,10 +18,21 @@ public:
bool Save(obs_data_t *obj) const; bool Save(obs_data_t *obj) const;
bool Load(obs_data_t *obj); bool Load(obs_data_t *obj);
std::string Path() { return _path; } std::string Path() const { return _path; }
std::string UnresolvedPath() const { return _path.UnresolvedValue(); } std::string UnresolvedPath() const { return _path.UnresolvedValue(); }
std::string WorkingDir() { return _workingDirectory; } std::string WorkingDir() const { return _workingDirectory; }
QStringList Args(); // Resolves variables QStringList Args() const; // Resolves variables
enum class ProcStartError {
NONE,
FAILED_TO_START,
TIMEOUT,
CRASH,
};
std::variant<int, ProcStartError>
StartProcessAndWait(int timeoutInMs) const;
bool StartProcessDetached() const;
private: private:
StringVariable _path = obs_module_text("AdvSceneSwitcher.enterPath"); StringVariable _path = obs_module_text("AdvSceneSwitcher.enterPath");
@@ -44,6 +56,7 @@ private slots:
void ArgsChanged(const StringList &); void ArgsChanged(const StringList &);
signals: signals:
void ConfigChanged(const ProcessConfig &); void ConfigChanged(const ProcessConfig &);
void AdvancedSettingsEnabled();
private: private:
void ShowAdvancedSettings(bool); void ShowAdvancedSettings(bool);

View File

@@ -51,23 +51,6 @@ RegexConfig::GetRegularExpression(const std::string &expr) const
return GetRegularExpression(QString::fromStdString(expr)); return GetRegularExpression(QString::fromStdString(expr));
} }
bool RegexConfig::Matches(const QString &text, const QString &expression) const
{
auto regex = GetRegularExpression(expression);
if (!regex.isValid()) {
return false;
}
auto match = regex.match(text);
return match.hasMatch();
}
bool RegexConfig::Matches(const std::string &text,
const std::string &expression) const
{
return Matches(QString::fromStdString(text),
QString::fromStdString(expression));
}
RegexConfig RegexConfig::PartialMatchRegexConfig() RegexConfig RegexConfig::PartialMatchRegexConfig()
{ {
RegexConfig conf; RegexConfig conf;

View File

@@ -29,9 +29,6 @@ public:
}; };
QRegularExpression GetRegularExpression(const QString &) const; QRegularExpression GetRegularExpression(const QString &) const;
QRegularExpression GetRegularExpression(const std::string &) const; QRegularExpression GetRegularExpression(const std::string &) const;
bool Matches(const QString &text, const QString &expression) const;
bool Matches(const std::string &text,
const std::string &expression) const;
static RegexConfig PartialMatchRegexConfig(); static RegexConfig PartialMatchRegexConfig();

View File

@@ -1,6 +1,7 @@
#include "resizing-text-edit.hpp" #include "resizing-text-edit.hpp"
#include <obs-module.h> #include <obs-module-helper.hpp>
#include <QScrollBar>
#include <utility.hpp> #include <utility.hpp>
namespace advss { namespace advss {
@@ -14,10 +15,12 @@ ResizingPlainTextEdit::ResizingPlainTextEdit(QWidget *parent,
_minLines(minLines), _minLines(minLines),
_paddingLines(paddingLines) _paddingLines(paddingLines)
{ {
setWordWrapMode(QTextOption::NoWrap);
QWidget::connect(this, SIGNAL(textChanged()), this, QWidget::connect(this, SIGNAL(textChanged()), this,
SLOT(ResizeTexteditArea())); SLOT(ResizeTexteditArea()));
QWidget::connect(this, SIGNAL(textChanged()), this, QWidget::connect(this, SIGNAL(textChanged()), this,
SLOT(PreventExceedingMaxLength())); SLOT(PreventExceedingMaxLength()));
verticalScrollBar()->installEventFilter(this);
} }
int ResizingPlainTextEdit::maxLength() int ResizingPlainTextEdit::maxLength()
@@ -30,19 +33,35 @@ void ResizingPlainTextEdit::setMaxLength(int maxLength)
_maxLength = maxLength; _maxLength = maxLength;
} }
bool ResizingPlainTextEdit::eventFilter(QObject *obj, QEvent *event)
{
if (obj == horizontalScrollBar()) {
if (event->type() == QEvent::Show) {
AddHeightForScrollBar(true);
} else if (event->type() == QEvent::Hide) {
AddHeightForScrollBar(false);
}
}
return QPlainTextEdit::eventFilter(obj, event);
}
void ResizingPlainTextEdit::ResizeTexteditArea() void ResizingPlainTextEdit::ResizeTexteditArea()
{ {
QFontMetrics f(font()); QFontMetrics f(font());
int rowHeight = f.lineSpacing(); int rowHeight = f.lineSpacing();
int numLines = document()->blockCount(); int numLines = document()->blockCount();
int height = 0;
if (numLines + _paddingLines < _minLines) { if (numLines + _paddingLines < _minLines) {
setFixedHeight(_minLines * rowHeight); height = _minLines * rowHeight;
} else if (numLines + _paddingLines < _scrollAt) { } else if (numLines + _paddingLines < _scrollAt) {
setFixedHeight((numLines + _paddingLines) * rowHeight); height = (numLines + _paddingLines) * rowHeight;
} else { } else {
setFixedHeight(_scrollAt * rowHeight); height = _scrollAt * rowHeight;
} }
setFixedHeight(height + _hScrollBarAddedHeight);
adjustSize(); adjustSize();
updateGeometry(); updateGeometry();
} }
@@ -62,4 +81,21 @@ void ResizingPlainTextEdit::PreventExceedingMaxLength()
} }
} }
void ResizingPlainTextEdit::AddHeightForScrollBar(bool addHeight)
{
if (!addHeight) {
setFixedHeight(height() - _hScrollBarAddedHeight);
_hScrollBarAddedHeight = 0;
return;
}
// Check if we have already added space for the scroll bar
if (_hScrollBarAddedHeight > 0) {
return;
}
_hScrollBarAddedHeight = verticalScrollBar()->height();
setFixedHeight(height() + _hScrollBarAddedHeight);
}
} // namespace advss } // namespace advss

View File

@@ -13,15 +13,21 @@ public:
int maxLength(); int maxLength();
void setMaxLength(int maxLength); void setMaxLength(int maxLength);
protected:
bool eventFilter(QObject *obj, QEvent *event) override;
private slots: private slots:
void ResizeTexteditArea(); void ResizeTexteditArea();
void PreventExceedingMaxLength(); void PreventExceedingMaxLength();
private: private:
void AddHeightForScrollBar(bool addHeight);
const int _scrollAt; const int _scrollAt;
const int _minLines; const int _minLines;
const int _paddingLines; const int _paddingLines;
int _maxLength = -1; int _maxLength = -1;
int _hScrollBarAddedHeight = 0;
}; };
} // namespace advss } // namespace advss

View File

@@ -1,5 +1,7 @@
#include "scene-selection.hpp" #include "scene-selection.hpp"
#include "switcher-data.hpp" #include "obs-module-helper.hpp"
#include "scene-group.hpp"
#include "scene-switch-helpers.hpp"
#include "utility.hpp" #include "utility.hpp"
namespace advss { namespace advss {
@@ -108,9 +110,9 @@ OBSWeakSource SceneSelection::GetScene(bool advance) const
} }
return _group->getCurrentScene(); return _group->getCurrentScene();
case Type::PREVIOUS: case Type::PREVIOUS:
return switcher->previousScene; return GetPreviousScene();
case Type::CURRENT: case Type::CURRENT:
return switcher->currentScene; return GetCurrentScene();
case Type::PREVIEW: { case Type::PREVIEW: {
auto s = obs_frontend_get_current_preview_scene(); auto s = obs_frontend_get_current_preview_scene();
auto scene = obs_source_get_weak_source(s); auto scene = obs_source_get_weak_source(s);
@@ -237,7 +239,7 @@ static QStringList getScenesList()
static QStringList getSceneGroupsList() static QStringList getSceneGroupsList()
{ {
QStringList list; QStringList list;
for (const auto &sg : switcher->sceneGroups) { for (const auto &sg : GetSceneGroups()) {
list << QString::fromStdString(sg.name); list << QString::fromStdString(sg.name);
} }
list.sort(); list.sort();

View File

@@ -1,10 +1,36 @@
#include "scene-switch-helpers.hpp" #include "scene-switch-helpers.hpp"
#include "switcher-data.hpp" #include "log-helper.hpp"
#include "obs-frontend-api.h" #include "obs-frontend-api.h"
#include "switcher-data.hpp"
#include "utility.hpp" #include "utility.hpp"
namespace advss { namespace advss {
static std::chrono::high_resolution_clock::time_point lastSceneChangeTime{};
static std::chrono::high_resolution_clock::time_point lastTransitionEndTime{};
static bool setupEventHandler();
static bool eventHandlerIsSetup = setupEventHandler();
static bool setupEventHandler()
{
static auto handleEvents = [](enum obs_frontend_event event, void *) {
switch (event) {
case OBS_FRONTEND_EVENT_SCENE_CHANGED:
lastSceneChangeTime =
std::chrono::high_resolution_clock::now();
break;
case OBS_FRONTEND_EVENT_TRANSITION_STOPPED:
lastTransitionEndTime =
std::chrono::high_resolution_clock::now();
break;
default:
break;
};
};
obs_frontend_add_event_callback(handleEvents, nullptr);
return true;
}
static std::pair<obs_weak_source_t *, int> static std::pair<obs_weak_source_t *, int>
getNextTransition(obs_weak_source_t *scene1, obs_weak_source_t *scene2) getNextTransition(obs_weak_source_t *scene1, obs_weak_source_t *scene2)
{ {
@@ -35,10 +61,9 @@ void SetNextTransition(const SceneSwitchInfo &sceneSwitch,
// 3. Current transition settings // 3. Current transition settings
// Transition Tab // Transition Tab
obs_weak_source_t *currentScene = OBSWeakSourceAutoRelease currentScene =
obs_source_get_weak_source(currentSource); obs_source_get_weak_source(currentSource);
auto tinfo = getNextTransition(currentScene, sceneSwitch.scene); auto tinfo = getNextTransition(currentScene, sceneSwitch.scene);
obs_weak_source_release(currentScene);
OBSWeakSource nextTransition = tinfo.first; OBSWeakSource nextTransition = tinfo.first;
int nextTransitionDuration = tinfo.second; int nextTransitionDuration = tinfo.second;
@@ -53,23 +78,23 @@ void SetNextTransition(const SceneSwitchInfo &sceneSwitch,
// Current transition settings // Current transition settings
if (!nextTransition) { if (!nextTransition) {
auto ct = obs_frontend_get_current_transition(); OBSSourceAutoRelease currentTransition =
nextTransition = obs_source_get_weak_source(ct); obs_frontend_get_current_transition();
nextTransition = obs_source_get_weak_source(currentTransition);
obs_weak_source_release(nextTransition); obs_weak_source_release(nextTransition);
obs_source_release(ct);
} }
if (!nextTransitionDuration) { if (!nextTransitionDuration) {
nextTransitionDuration = obs_frontend_get_transition_duration(); nextTransitionDuration = obs_frontend_get_transition_duration();
} }
if (switcher->adjustActiveTransitionType) { if (ShouldModifyActiveTransition()) {
obs_frontend_set_transition_duration(nextTransitionDuration); obs_frontend_set_transition_duration(nextTransitionDuration);
auto t = obs_weak_source_get_source(nextTransition); OBSSourceAutoRelease transition =
obs_frontend_set_current_transition(t); obs_weak_source_get_source(nextTransition);
obs_source_release(t); obs_frontend_set_current_transition(transition);
} }
if (switcher->transitionOverrideOverride) { if (ShouldModifyTransitionOverrides()) {
OverwriteTransitionOverride({sceneSwitch.scene, nextTransition, OverwriteTransitionOverride({sceneSwitch.scene, nextTransition,
nextTransitionDuration}, nextTransitionDuration},
td); td);
@@ -78,8 +103,8 @@ void SetNextTransition(const SceneSwitchInfo &sceneSwitch,
void OverwriteTransitionOverride(const SceneSwitchInfo &ssi, TransitionData &td) void OverwriteTransitionOverride(const SceneSwitchInfo &ssi, TransitionData &td)
{ {
obs_source_t *scene = obs_weak_source_get_source(ssi.scene); OBSSourceAutoRelease scene = obs_weak_source_get_source(ssi.scene);
obs_data_t *data = obs_source_get_private_settings(scene); OBSDataAutoRelease data = obs_source_get_private_settings(scene);
td.name = obs_data_get_string(data, "transition"); td.name = obs_data_get_string(data, "transition");
td.duration = obs_data_get_int(data, "transition_duration"); td.duration = obs_data_get_int(data, "transition_duration");
@@ -88,19 +113,13 @@ void OverwriteTransitionOverride(const SceneSwitchInfo &ssi, TransitionData &td)
obs_data_set_string(data, "transition", name.c_str()); obs_data_set_string(data, "transition", name.c_str());
obs_data_set_int(data, "transition_duration", ssi.duration); obs_data_set_int(data, "transition_duration", ssi.duration);
obs_data_release(data);
obs_source_release(scene);
} }
void RestoreTransitionOverride(obs_source_t *scene, const TransitionData &td) void RestoreTransitionOverride(obs_source_t *scene, const TransitionData &td)
{ {
obs_data_t *data = obs_source_get_private_settings(scene); OBSDataAutoRelease data = obs_source_get_private_settings(scene);
obs_data_set_string(data, "transition", td.name.c_str()); obs_data_set_string(data, "transition", td.name.c_str());
obs_data_set_int(data, "transition_duration", td.duration); obs_data_set_int(data, "transition_duration", td.duration);
obs_data_release(data);
} }
void SwitchScene(const SceneSwitchInfo &sceneSwitch, bool force) void SwitchScene(const SceneSwitchInfo &sceneSwitch, bool force)
@@ -110,36 +129,67 @@ void SwitchScene(const SceneSwitchInfo &sceneSwitch, bool force)
return; return;
} }
obs_source_t *source = obs_weak_source_get_source(sceneSwitch.scene); OBSSourceAutoRelease source =
obs_source_t *currentSource = obs_frontend_get_current_scene(); obs_weak_source_get_source(sceneSwitch.scene);
OBSSourceAutoRelease currentSource = obs_frontend_get_current_scene();
if (source && (source != currentSource || force)) { if (source && (source != currentSource || force)) {
TransitionData currentTransitionData; TransitionData currentTransitionData;
SetNextTransition(sceneSwitch, currentSource, SetNextTransition(sceneSwitch, currentSource,
currentTransitionData); currentTransitionData);
obs_frontend_set_current_scene(source); obs_frontend_set_current_scene(source);
if (switcher->transitionOverrideOverride) { if (ShouldModifyTransitionOverrides()) {
RestoreTransitionOverride(source, RestoreTransitionOverride(source,
currentTransitionData); currentTransitionData);
} }
if (switcher->verbose) { vblog(LOG_INFO, "switched scene");
blog(LOG_INFO, "switched scene");
}
if (switcher->networkConfig.ShouldSendSceneChange()) { if (switcher->networkConfig.ShouldSendSceneChange()) {
switcher->server.sendMessage(sceneSwitch); switcher->server.sendMessage(sceneSwitch);
} }
} }
obs_source_release(currentSource);
obs_source_release(source);
} }
void SwitchPreviewScene(const OBSWeakSource &ws) void SwitchPreviewScene(const OBSWeakSource &ws)
{ {
auto source = obs_weak_source_get_source(ws); OBSSourceAutoRelease source = obs_weak_source_get_source(ws);
obs_frontend_set_current_preview_scene(source); obs_frontend_set_current_preview_scene(source);
obs_source_release(source); }
std::chrono::high_resolution_clock::time_point GetLastTransitionEndTime()
{
return lastTransitionEndTime;
}
std::chrono::high_resolution_clock::time_point GetLastSceneChangeTime()
{
return lastSceneChangeTime;
}
OBSWeakSource GetCurrentScene()
{
return switcher->currentScene;
}
OBSWeakSource GetPreviousScene()
{
return switcher->previousScene;
}
bool AnySceneTransitionStarted()
{
return switcher->AnySceneTransitionStarted();
}
bool ShouldModifyTransitionOverrides()
{
return switcher->transitionOverrideOverride;
}
bool ShouldModifyActiveTransition()
{
return switcher->adjustActiveTransitionType;
} }
} // namespace advss } // namespace advss

View File

@@ -1,5 +1,6 @@
#pragma once #pragma once
#include <obs.hpp> #include <obs.hpp>
#include <chrono>
#include <string> #include <string>
namespace advss { namespace advss {
@@ -15,12 +16,24 @@ struct TransitionData {
int duration = 0; int duration = 0;
}; };
void SetNextTransition(const SceneSwitchInfo &ssi, obs_source_t *currentSource, void SwitchScene(const SceneSwitchInfo &, bool force = false);
TransitionData &td); void SwitchPreviewScene(const OBSWeakSource &scene);
void OverwriteTransitionOverride(const SceneSwitchInfo &ssi,
TransitionData &td); std::chrono::high_resolution_clock::time_point GetLastTransitionEndTime();
void RestoreTransitionOverride(obs_source_t *scene, const TransitionData &td); std::chrono::high_resolution_clock::time_point GetLastSceneChangeTime();
void SwitchScene(const SceneSwitchInfo &ssi, bool force = false);
void SwitchPreviewScene(const OBSWeakSource &ws); OBSWeakSource GetCurrentScene();
OBSWeakSource GetPreviousScene();
bool ShouldModifyTransitionOverrides();
bool ShouldModifyActiveTransition();
void OverwriteTransitionOverride(const SceneSwitchInfo &, TransitionData &);
void RestoreTransitionOverride(obs_source_t *scene, const TransitionData &);
bool AnySceneTransitionStarted();
// TODO: Remove at some point
void SetNextTransition(const SceneSwitchInfo &, obs_source_t *currentSource,
TransitionData &);
} // namespace advss } // namespace advss

View File

@@ -125,6 +125,7 @@ bool Section::eventFilter(QObject *obj, QEvent *event)
// filter will cause a segfault for some reason // filter will cause a segfault for some reason
SetupAnimations(); SetupAnimations();
} }
setMinimumWidth(_content->sizeHint().width());
} }
return QObject::eventFilter(obj, event); return QObject::eventFilter(obj, event);
} }

View File

@@ -1,11 +0,0 @@
#include "sync-helper.hpp"
namespace advss {
std::mutex *GetSwitcherMutex();
std::lock_guard<std::mutex> LockContext()
{
return std::lock_guard<std::mutex>(*GetSwitcherMutex());
}
} // namespace advss

View File

@@ -0,0 +1,23 @@
#include "sync-helpers.hpp"
namespace advss {
std::mutex *GetSwitcherMutex();
std::unique_lock<std::mutex> *GetSwitcherLoopLock();
std::mutex *GetMutex()
{
return GetSwitcherMutex();
}
std::lock_guard<std::mutex> LockContext()
{
return std::lock_guard<std::mutex>(*GetSwitcherMutex());
}
std::unique_lock<std::mutex> *GetLoopLock()
{
return GetSwitcherLoopLock();
}
} // namespace advss

View File

@@ -3,6 +3,8 @@
namespace advss { namespace advss {
[[nodiscard]] std::mutex *GetMutex();
[[nodiscard]] std::lock_guard<std::mutex> LockContext(); [[nodiscard]] std::lock_guard<std::mutex> LockContext();
[[nodiscard]] std::unique_lock<std::mutex> *GetLoopLock();
} // namespace advss } // namespace advss

View File

@@ -2,7 +2,7 @@
#include "advanced-scene-switcher.hpp" #include "advanced-scene-switcher.hpp"
#include "macro.hpp" #include "macro.hpp"
#include "macro-segment.hpp" #include "macro-segment.hpp"
#include "plugin-state-helper.hpp" #include "plugin-state-helpers.hpp"
#include "utility.hpp" #include "utility.hpp"
#include <QVariant> #include <QVariant>

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