mirror of
https://github.com/WarmUpTill/SceneSwitcher.git
synced 2026-08-29 04:36:21 -05:00
Compare commits
36 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2ce0b35921 | ||
|
|
33d2def126 | ||
|
|
c44b0a2ac2 | ||
|
|
3abeb936b3 | ||
|
|
cef513f775 | ||
|
|
a66961eac1 | ||
|
|
92c8767e1a | ||
|
|
5b12468a46 | ||
|
|
675021c284 | ||
|
|
6bf122d0e8 | ||
|
|
8b8e479098 | ||
|
|
717d41a527 | ||
|
|
ad5587e084 | ||
|
|
f37f384b5a | ||
|
|
dad36689c4 | ||
|
|
0bd7d004ad | ||
|
|
f8c589c7a2 | ||
|
|
e714df7ad0 | ||
|
|
6277da1bbd | ||
|
|
1bef309264 | ||
|
|
2e1d660e17 | ||
|
|
3f492f468a | ||
|
|
c86bba84b3 | ||
|
|
c468b0698b | ||
|
|
2af82fe22a | ||
|
|
5d720219bf | ||
|
|
484e1aa191 | ||
|
|
0fdfe757b4 | ||
|
|
bc5a04651f | ||
|
|
d9dfc1913d | ||
|
|
1310d0b3f3 | ||
|
|
139df8a641 | ||
|
|
acf1ae63aa | ||
|
|
041a9e7f19 | ||
|
|
427ed34a1b | ||
|
|
45fa4dac8f |
@@ -27,6 +27,8 @@ Examples are ...
|
||||
/usr/lib/x86_64-linux-gnu/obs-plugins/
|
||||
/usr/share/obs/obs-plugins/
|
||||
~/.config/obs-studio/plugins/advanced-scene-switcher/bin/64bit/
|
||||
~/.local/share/flatpak/app/com.obsproject.Studio/x86_64/stable/active/files/lib/obs-plugins/
|
||||
/var/lib/flatpak/app/com.obsproject.Studio/x86_64/stable/active/files/lib/obs-plugins/
|
||||
|
||||
Unfortunately the expected location of the locale, which can be found in the data folder, can vary also.
|
||||
The data folder usually should be copied to:
|
||||
@@ -34,5 +36,7 @@ The data folder usually should be copied to:
|
||||
|
||||
If this does not work you can try to copy the "locale" folder found inside the data folder to:
|
||||
/usr/share/obs/obs-plugins/advanced-scene-switcher/locale
|
||||
~/.local/share/flatpak/app/com.obsproject.Studio/x86_64/stable/active/files/share/obs/obs-plugins/advanced-scene-switcher/locale
|
||||
/var/lib/flatpak/app/com.obsproject.Studio/x86_64/stable/active/files/share/obs/obs-plugins/advanced-scene-switcher/locale
|
||||
|
||||
In doubt please check where other "en-US.ini" files are located on your system.
|
||||
|
||||
@@ -63,8 +63,6 @@ set(advanced-scene-switcher_HEADERS
|
||||
src/headers/advanced-scene-switcher.hpp
|
||||
src/headers/switcher-data-structs.hpp
|
||||
src/headers/utility.hpp
|
||||
src/headers/curl-helper.hpp
|
||||
src/headers/volume-control.hpp
|
||||
src/headers/scene-group.hpp
|
||||
src/headers/scene-trigger.hpp
|
||||
src/headers/switch-audio.hpp
|
||||
@@ -82,6 +80,34 @@ set(advanced-scene-switcher_HEADERS
|
||||
src/headers/switch-sequence.hpp
|
||||
src/headers/switch-video.hpp
|
||||
src/headers/switch-generic.hpp
|
||||
src/headers/macro-action-edit.hpp
|
||||
src/headers/macro-action-audio.hpp
|
||||
src/headers/macro-action-recording.hpp
|
||||
src/headers/macro-action-replay-buffer.hpp
|
||||
src/headers/macro-action-run.hpp
|
||||
src/headers/macro-action-streaming.hpp
|
||||
src/headers/macro-action-switch-scene.hpp
|
||||
src/headers/macro-action-wait.hpp
|
||||
src/headers/macro-condition-edit.hpp
|
||||
src/headers/macro-condition-audio.hpp
|
||||
src/headers/macro-condition-file.hpp
|
||||
src/headers/macro-condition-idle.hpp
|
||||
src/headers/macro-condition-media.hpp
|
||||
src/headers/macro-condition-plugin-state.hpp
|
||||
src/headers/macro-condition-process.hpp
|
||||
src/headers/macro-condition-recording.hpp
|
||||
src/headers/macro-condition-region.hpp
|
||||
src/headers/macro-condition-scene.hpp
|
||||
src/headers/macro-condition-streaming.hpp
|
||||
src/headers/macro-condition-video.hpp
|
||||
src/headers/macro-condition-window.hpp
|
||||
src/headers/macro.hpp
|
||||
src/headers/curl-helper.hpp
|
||||
src/headers/screenshot-helper.hpp
|
||||
src/headers/name-dialog.hpp
|
||||
src/headers/duration-control.hpp
|
||||
src/headers/section.hpp
|
||||
src/headers/volume-control.hpp
|
||||
src/headers/version.h
|
||||
)
|
||||
|
||||
@@ -110,7 +136,34 @@ set(advanced-scene-switcher_SOURCES
|
||||
src/switch-audio.cpp
|
||||
src/switch-video.cpp
|
||||
src/switch-generic.cpp
|
||||
src/macro-action-edit.cpp
|
||||
src/macro-action-audio.cpp
|
||||
src/macro-action-recording.cpp
|
||||
src/macro-action-replay-buffer.cpp
|
||||
src/macro-action-run.cpp
|
||||
src/macro-action-streaming.cpp
|
||||
src/macro-action-switch-scene.cpp
|
||||
src/macro-action-wait.cpp
|
||||
src/macro-condition-edit.cpp
|
||||
src/macro-condition-audio.cpp
|
||||
src/macro-condition-file.cpp
|
||||
src/macro-condition-idle.cpp
|
||||
src/macro-condition-media.cpp
|
||||
src/macro-condition-plugin-state.cpp
|
||||
src/macro-condition-process.cpp
|
||||
src/macro-condition-recording.cpp
|
||||
src/macro-condition-region.cpp
|
||||
src/macro-condition-scene.cpp
|
||||
src/macro-condition-streaming.cpp
|
||||
src/macro-condition-video.cpp
|
||||
src/macro-condition-window.cpp
|
||||
src/macro.cpp
|
||||
src/macro-tab.cpp
|
||||
src/curl-helper.cpp
|
||||
src/screenshot-helper.cpp
|
||||
src/name-dialog.cpp
|
||||
src/duration-control.cpp
|
||||
src/section.cpp
|
||||
src/volume-control.cpp
|
||||
src/version.cpp
|
||||
)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# SceneSwitcher
|
||||
An automated scene switcher for OBS Studio
|
||||
|
||||
More information can be found on https://obsproject.com/forum/resources/automatic-scene-switching.395/
|
||||
More information can be found on https://obsproject.com/forum/resources/automatic-scene-switching.395/ and see https://github.com/WarmUpTill/SceneSwitcher/blob/master/CI/release/README.txt for installing releases.
|
||||
|
||||
## Compiling in tree (recommended)
|
||||
Add the "SceneSwitcher" source directory to your obs-studio source directory under obs-studio/UI/frontend-plugins/:
|
||||
|
||||
@@ -61,7 +61,7 @@ AdvSceneSwitcher.transitionTab.transitionBehaviorSelectionError="Mindestens eine
|
||||
AdvSceneSwitcher.transitionTab.transitionForAToB="Szenenübergänge für automatisierte Szenenwechsel von Szene A zu Szene B"
|
||||
AdvSceneSwitcher.transitionTab.transitionsHelp="<html><head/><body><p>Diese Einstellungen beeinflussen <span style=\"font-style:italic;\">nur</span> vom Szenenwechsler ausgelöste Szenenübergänge - Siehe <a href=\"https://obsproject.com/forum/resources/transition-table.1174/\"><span style=\" text-decoration: underline; color:#268bd2;\">Transition Table</span></a> um auch manuelle Szenenübergänge zu konfigurieren.<br/>Einstellungen auf diesem Tab haben Vorrang, vor denen welche auf den übrigen Tabs konfiguriert wurden.<br/><br/>Klicke auf das Plus Symbol, um einen neuen Eintrag hinzuzufügen.</p></body></html>"
|
||||
AdvSceneSwitcher.transitionTab.defaultTransition="Ändere den Szenenübergang wenn eine Szene aktiv ist"
|
||||
AdvSceneSwitcher.transitionTab.entry="Wechsle von {{scenes}} zu {{scenes2}} mit {{transitions}}"
|
||||
AdvSceneSwitcher.transitionTab.entry="Wechsle von {{scenes}} zu {{scenes2}} mit {{transitions}} mit einer Dauer von {{duration}}"
|
||||
AdvSceneSwitcher.transitionTab.defaultTransitionEntry="Wenn {{scenes}} aktiv ist ändere den Szenenübergang zu {{transitions}}"
|
||||
AdvSceneSwitcher.transitionTab.defaultTransitionsHelp="Klicke auf das Plus Symbol, um einen neuen Eintrag hinzuzufügen."
|
||||
AdvSceneSwitcher.transitionTab.defaultTransition.delay="Wechsle Szenenübergang {{defTransitionDelay}} nach Szenenwechsel."
|
||||
@@ -193,8 +193,8 @@ AdvSceneSwitcher.sceneSequenceTab.fileType="Text Dateien (*.txt)"
|
||||
AdvSceneSwitcher.sceneSequenceTab.interruptible="kann unterbrochen werden"
|
||||
AdvSceneSwitcher.sceneSequenceTab.interruptibleHint="Andere Szenenwechselmethoden können diese Sequenz unterbrechen"
|
||||
AdvSceneSwitcher.sceneSequenceTab.extendEdit="Szenenfolge erweitern"
|
||||
AdvSceneSwitcher.sceneSequenceTab.extendEntry="Nach {{delay}} {{delayUnits}} wechsle zu {{scenes}} mit {{transitions}}"
|
||||
AdvSceneSwitcher.sceneSequenceTab.entry="Wenn {{startScenes}} aktiv ist wechsle zu {{scenes}} nach {{delay}} {{delayUnits}} mit {{transitions}} {{interruptible}}"
|
||||
AdvSceneSwitcher.sceneSequenceTab.extendEntry="Nach {{delay}} wechsle zu {{scenes}} mit {{transitions}}"
|
||||
AdvSceneSwitcher.sceneSequenceTab.entry="Wenn {{startScenes}} aktiv ist wechsle zu {{scenes}} nach {{delay}} mit {{transitions}} {{interruptible}}"
|
||||
AdvSceneSwitcher.sceneSequenceTab.help="Dieser Tab ermöglicht es automatisiert zu einer anderen Szene zu wechseln, nachdem eine Szene für die konfigurierte Zeit aktiv war.\nZum Beispiel kann so automatisch zwischen zwei Szenen hin und her gewechselt werden.\n\nKlicke auf das markierte Plus Symbol, um einen neuen Eintrag hinzuzufügen."
|
||||
|
||||
; Audio Tab
|
||||
@@ -202,9 +202,9 @@ AdvSceneSwitcher.audioTab.title="Audio"
|
||||
AdvSceneSwitcher.audioTab.condition.above="über"
|
||||
AdvSceneSwitcher.audioTab.condition.below="unter"
|
||||
AdvSceneSwitcher.audioTab.ignoreInactiveSource="außer Audio Quelle ist inaktiv"
|
||||
AdvSceneSwitcher.audioTab.entry="Wenn die Lautstärke von {{audioSources}} {{condition}} {{volumeWidget}} ist für {{duration}} wechsle zu {{scenes}} mit {{transitions}} {{ignoreInactiveSource}}"
|
||||
AdvSceneSwitcher.audioTab.entry="Wenn die Lautstärke von {{audioSources}} {{condition}} {{volumeWidget}} ist für {{duration}} Sekunden wechsle zu {{scenes}} mit {{transitions}} {{ignoreInactiveSource}}"
|
||||
AdvSceneSwitcher.audioTab.multiMatchfallbackCondition="Wenn mehrere Einträge zutreffen ..."
|
||||
AdvSceneSwitcher.audioTab.multiMatchfallback="... für {{duration}} wechsle zu {{scenes}} mit {{transitions}}"
|
||||
AdvSceneSwitcher.audioTab.multiMatchfallback="... für {{duration}} Sekunden wechsle zu {{scenes}} mit {{transitions}}"
|
||||
AdvSceneSwitcher.audioTab.help="Dieser Tab ermöglicht es basierend auf der Lautstärke von Audioquellen Szenen zu wechseln.\nSo kann zum Beispiel automatisch zu einer Szene gewechselt werden, wenn die Lautstärke eines Mikrofons eine konfigurierte Schwelle überschreitet.\n\nKlicke auf das markierte Plus Symbol, um einen neuen Eintrag hinzuzufügen."
|
||||
|
||||
; Video Tab
|
||||
@@ -215,6 +215,7 @@ AdvSceneSwitcher.videoTab.condition.match="genau übereinstimmt mit"
|
||||
AdvSceneSwitcher.videoTab.condition.match.tooltip="Eine genaue Übereinstimmung setzt voraus, dass sowohl Zielquelle als auch das Bild die gleiche Auflösung besitzen.\nZusätzlich muss jeder einzelne Pixel zwischen beiden Bildquellen übereinstimmen weshalb Bildformate, welche das Bild komprimieren (z. B. .JPG) nicht zu empfehlen sind."
|
||||
AdvSceneSwitcher.videoTab.condition.differ="nicht übereinstimmt mit"
|
||||
AdvSceneSwitcher.videoTab.condition.hasNotChanged="sich nicht verändert"
|
||||
AdvSceneSwitcher.videoTab.condition.hasNotChanged="sich verändert"
|
||||
AdvSceneSwitcher.videoTab.ignoreInactiveSource="außer Video Quelle ist inaktiv"
|
||||
AdvSceneSwitcher.videoTab.entry="Wenn {{videoSources}} {{condition}} {{filePath}} {{browseButton}} für {{duration}} wechsle zu {{scenes}} mit {{transitions}} {{ignoreInactiveSource}}"
|
||||
AdvSceneSwitcher.videoTab.help="<html><head/><body><p>Dieser Tab ermöglicht es basierend auf der Videoausgabe von Quellen scenen zu wechseln.<br/>Für eine noch bessere Implementierung dieser Funktionalität siehe <a href=\"https://obsproject.com/forum/resources/pixel-match-switcher.1202/\"><span style=\" text-decoration: underline; color:#268bd2;\">Pixel Match Switcher</span></a>.<br/><br/>Klicke auf das markierte Plus Symbol, um einen neuen Eintrag hinzuzufügen..</p></body></html>"
|
||||
|
||||
@@ -51,6 +51,111 @@ AdvSceneSwitcher.generalTab.priority.media="Media"
|
||||
AdvSceneSwitcher.generalTab.priority.time="Time"
|
||||
AdvSceneSwitcher.generalTab.priority.audio="Audio"
|
||||
AdvSceneSwitcher.generalTab.priority.video="Video"
|
||||
AdvSceneSwitcher.generalTab.priority.macro="Macro"
|
||||
|
||||
; Macro Tab
|
||||
AdvSceneSwitcher.macroTab.title="Macro"
|
||||
AdvSceneSwitcher.macroTab.macros="Macros"
|
||||
AdvSceneSwitcher.macroTab.help="Macros allow you to execute a string of actions depending on multiple conditions.\n\nClick on the highlighted plus symbol to add a new Macro."
|
||||
AdvSceneSwitcher.macroTab.editConditionHelp="This section allows you to define Macro conditions.\n\nSelect an existing or add a new Macro on the left."
|
||||
AdvSceneSwitcher.macroTab.editActionHelp="This section allows you to define Macro actions.\n\nSelect an existing or add a new Macro on the left."
|
||||
AdvSceneSwitcher.macroTab.edit="Edit macro"
|
||||
AdvSceneSwitcher.macroTab.edit.logic="Logic type:"
|
||||
AdvSceneSwitcher.macroTab.edit.condition="Condition type:"
|
||||
AdvSceneSwitcher.macroTab.edit.action="Action type:"
|
||||
AdvSceneSwitcher.macroTab.add="Add new macro"
|
||||
AdvSceneSwitcher.macroTab.name="Name:"
|
||||
AdvSceneSwitcher.macroTab.defaultname="Macro %1"
|
||||
AdvSceneSwitcher.macroTab.exists="Macro name exists already"
|
||||
AdvSceneSwitcher.macroTab.copy="Create copy"
|
||||
; Macro Logic
|
||||
AdvSceneSwitcher.logic.none="Ignore entry"
|
||||
AdvSceneSwitcher.logic.and="And"
|
||||
AdvSceneSwitcher.logic.or="Or"
|
||||
AdvSceneSwitcher.logic.andNot="And not"
|
||||
AdvSceneSwitcher.logic.orNot="Or not"
|
||||
AdvSceneSwitcher.logic.rootNone="If"
|
||||
AdvSceneSwitcher.logic.not="If not"
|
||||
; Macro Conditions
|
||||
AdvSceneSwitcher.condition.audio="Audio"
|
||||
AdvSceneSwitcher.ondition.audio.state.below="Below"
|
||||
AdvSceneSwitcher.ondition.audio.state.above="Above"
|
||||
AdvSceneSwitcher.condition.audio.entry="Volume of {{audioSources}} is {{condition}} {{volume}} for {{duration}} seconds"
|
||||
AdvSceneSwitcher.condition.region="Screen region"
|
||||
AdvSceneSwitcher.condition.region.entry="Cursor is in {{minX}} {{minY}} x {{maxX}} {{maxY}}"
|
||||
AdvSceneSwitcher.condition.scene="Scene"
|
||||
AdvSceneSwitcher.condition.scene.type.current="Current"
|
||||
AdvSceneSwitcher.condition.scene.type.previous="Previous"
|
||||
AdvSceneSwitcher.condition.scene.entry="{{sceneType}} scene is {{scenes}} for {{duration}}"
|
||||
AdvSceneSwitcher.condition.window="Window"
|
||||
AdvSceneSwitcher.condition.window.entry.line1="{{windows}} exist and ..."
|
||||
AdvSceneSwitcher.condition.window.entry.line2="... is {{fullscreen}} fullscreen {{maximized}} maximized {{focused}} focused"
|
||||
AdvSceneSwitcher.condition.file="File"
|
||||
AdvSceneSwitcher.condition.file.entry.line1="Content of {{fileType}} {{filePath}} {{browseButton}} matches:"
|
||||
AdvSceneSwitcher.condition.file.entry.line2="{{matchText}}"
|
||||
AdvSceneSwitcher.condition.file.entry.line3="{{useRegex}} {{checkModificationDate}} {{checkFileContent}}"
|
||||
AdvSceneSwitcher.condition.media="Media"
|
||||
AdvSceneSwitcher.condition.media.entry="{{mediaSources}} state is {{states}} and {{timeRestrictions}} {{time}}"
|
||||
AdvSceneSwitcher.condition.video="Video"
|
||||
AdvSceneSwitcher.condition.video.condition.match="exactly matches"
|
||||
AdvSceneSwitcher.condition.video.condition.differ="does not match"
|
||||
AdvSceneSwitcher.condition.video.condition.hasChanged="has changed"
|
||||
AdvSceneSwitcher.condition.video.condition.hasNotChanged="has not changed"
|
||||
AdvSceneSwitcher.condition.video.condition.noImage="has no output"
|
||||
AdvSceneSwitcher.condition.video.askFileAction="Do you want to use an existing file or create a screenshot of the currently selected source?"
|
||||
AdvSceneSwitcher.condition.video.askFileAction.file="Use existing file"
|
||||
AdvSceneSwitcher.condition.video.askFileAction.screenshot="Create screenshot"
|
||||
AdvSceneSwitcher.condition.video.entry="{{videoSources}} {{condition}} {{filePath}} {{browseButton}} for {{duration}}"
|
||||
AdvSceneSwitcher.condition.stream="Streaming"
|
||||
AdvSceneSwitcher.condition.stream.state.start="Stream running"
|
||||
AdvSceneSwitcher.condition.stream.state.stop="Stream stopped"
|
||||
AdvSceneSwitcher.condition.stream.entry="{{streamState}} for {{duration}}"
|
||||
AdvSceneSwitcher.condition.record="Recording"
|
||||
AdvSceneSwitcher.condition.record.state.start="Recording running"
|
||||
AdvSceneSwitcher.condition.record.state.pause="Recording paused"
|
||||
AdvSceneSwitcher.condition.record.state.stop="Recording stopped"
|
||||
AdvSceneSwitcher.condition.record.entry="{{recordState}} for {{duration}}"
|
||||
AdvSceneSwitcher.condition.process="Process"
|
||||
AdvSceneSwitcher.condition.process.entry="{{processes}} is running {{focused}} and is focused"
|
||||
AdvSceneSwitcher.condition.idle="Idle"
|
||||
AdvSceneSwitcher.condition.idle.entry="No keyboard or mouse inputs for {{duration}}"
|
||||
AdvSceneSwitcher.condition.pluginState="Plugin state"
|
||||
AdvSceneSwitcher.condition.pluginState.state.sceneSwitched="Automated scene change was triggered in this interval"
|
||||
AdvSceneSwitcher.condition.pluginState.entry="{{condition}}"
|
||||
|
||||
; Macro Actions
|
||||
AdvSceneSwitcher.action.switchScene="Switch scene"
|
||||
AdvSceneSwitcher.action.scene.entry="Switch to scene {{scenes}} using {{transitions}} with a duration of {{duration}} seconds"
|
||||
AdvSceneSwitcher.action.wait="Wait"
|
||||
AdvSceneSwitcher.action.wait.type.fixed="fixed"
|
||||
AdvSceneSwitcher.action.wait.type.random="random"
|
||||
AdvSceneSwitcher.action.wait.entry.fixed="Wait for {{waitType}} duration of {{duration}}"
|
||||
AdvSceneSwitcher.action.wait.entry.random="Wait for {{waitType}} duration from {{duration}} to {{duration2}}"
|
||||
AdvSceneSwitcher.action.audio="Audio"
|
||||
AdvSceneSwitcher.action.audio.type.mute="Mute"
|
||||
AdvSceneSwitcher.action.audio.type.unmute="Unmute"
|
||||
AdvSceneSwitcher.action.audio.type.sourceVolume="Set source volume"
|
||||
AdvSceneSwitcher.action.audio.type.masterVolume="Set master volume"
|
||||
AdvSceneSwitcher.action.audio.entry="{{actions}} {{audioSources}} {{volume}}"
|
||||
AdvSceneSwitcher.action.recording="Recording"
|
||||
AdvSceneSwitcher.action.recording.type.stop="Stop recording"
|
||||
AdvSceneSwitcher.action.recording.type.start="Start recording"
|
||||
AdvSceneSwitcher.action.recording.type.pause="Pause recording"
|
||||
AdvSceneSwitcher.action.recording.type.unpause="Unpause recording"
|
||||
AdvSceneSwitcher.action.recording.pause.hint="Note that depending on your recording settings you might not be able to pause recording"
|
||||
AdvSceneSwitcher.action.recording.entry="{{actions}}{{pauseHint}}"
|
||||
AdvSceneSwitcher.action.replay="Replay buffer"
|
||||
AdvSceneSwitcher.action.replay.type.stop="Stop replay buffer"
|
||||
AdvSceneSwitcher.action.replay.type.start="Start replay buffer"
|
||||
AdvSceneSwitcher.action.replay.type.save="Save replay buffer"
|
||||
AdvSceneSwitcher.action.replay.entry="{{actions}}"
|
||||
AdvSceneSwitcher.action.streaming="Streaming"
|
||||
AdvSceneSwitcher.action.streaming.type.stop="Stop streaming"
|
||||
AdvSceneSwitcher.action.streaming.type.start="Start streaming"
|
||||
AdvSceneSwitcher.action.streaming.entry="{{actions}}"
|
||||
AdvSceneSwitcher.action.run="Run"
|
||||
AdvSceneSwitcher.action.run.entry="Run {{filePath}} {{browseButton}}"
|
||||
|
||||
|
||||
; Transition Tab
|
||||
AdvSceneSwitcher.transitionTab.title="Transition"
|
||||
@@ -61,7 +166,7 @@ AdvSceneSwitcher.transitionTab.transitionBehaviorSelectionError="At least one op
|
||||
AdvSceneSwitcher.transitionTab.transitionForAToB="Use transition for automated scene switch from scene A to scene B"
|
||||
AdvSceneSwitcher.transitionTab.transitionsHelp="<html><head/><body><p>These settings <span style=\"font-style:italic;\">only</span> affect transitions caused by the scene switcher - Check out <a href=\"https://obsproject.com/forum/resources/transition-table.1174/\"><span style=\" text-decoration: underline; color:#268bd2;\">Transition Table</span></a> if you want to configure this for manual scene changes.<br/>Settings defined here take priority over transition settings configured elsewhere in the scene switcher.<br/><br/>Click the plus symbol below to add a new entry.</p></body></html>"
|
||||
AdvSceneSwitcher.transitionTab.defaultTransition="Change transition if scene is active"
|
||||
AdvSceneSwitcher.transitionTab.entry="Switch from {{scenes}} to {{scenes2}} using {{transitions}}"
|
||||
AdvSceneSwitcher.transitionTab.entry="Switch from {{scenes}} to {{scenes2}} using {{transitions}} with a duration of {{duration}}"
|
||||
AdvSceneSwitcher.transitionTab.defaultTransitionEntry="When scene {{scenes}} is active change default scene transition to {{transitions}}"
|
||||
AdvSceneSwitcher.transitionTab.defaultTransitionsHelp="Click on the plus symbol to add an entry."
|
||||
AdvSceneSwitcher.transitionTab.defaultTransition.delay="Switch transition {{defTransitionDelay}} after scene change."
|
||||
@@ -192,9 +297,9 @@ AdvSceneSwitcher.sceneSequenceTab.loadSuccess="Advanced Scene Switcher settings
|
||||
AdvSceneSwitcher.sceneSequenceTab.fileType="Text files (*.txt)"
|
||||
AdvSceneSwitcher.sceneSequenceTab.interruptible="interruptible"
|
||||
AdvSceneSwitcher.sceneSequenceTab.interruptibleHint="Other switching methods are allowed to interrupt this scene sequence"
|
||||
AdvSceneSwitcher.sceneSequenceTab.entry="When {{startScenes}} is active switch to {{scenes}} after {{delay}} {{delayUnits}} using {{transitions}} {{interruptible}}"
|
||||
AdvSceneSwitcher.sceneSequenceTab.entry="When {{startScenes}} is active switch to {{scenes}} after {{delay}} using {{transitions}} {{interruptible}}"
|
||||
AdvSceneSwitcher.sceneSequenceTab.extendEdit="Extend Sequence"
|
||||
AdvSceneSwitcher.sceneSequenceTab.extendEntry="After {{delay}} {{delayUnits}} switch to {{scenes}} using {{transitions}}"
|
||||
AdvSceneSwitcher.sceneSequenceTab.extendEntry="After {{delay}} switch to {{scenes}} using {{transitions}}"
|
||||
AdvSceneSwitcher.sceneSequenceTab.help="This tab will allow you to automatically switch to a different scene if a scene was active for a configured period of time.\nFor example, you could automatically cycle back and forth between two scenes automatically.\n\nClick on the highlighted plus symbol to continue."
|
||||
|
||||
; Audio Tab
|
||||
@@ -202,9 +307,9 @@ AdvSceneSwitcher.audioTab.title="Audio"
|
||||
AdvSceneSwitcher.audioTab.condition.above="above"
|
||||
AdvSceneSwitcher.audioTab.condition.below="below"
|
||||
AdvSceneSwitcher.audioTab.ignoreInactiveSource="unless source is inactive"
|
||||
AdvSceneSwitcher.audioTab.entry="When the volume of {{audioSources}} is {{condition}} {{volumeWidget}} for {{duration}} switch to {{scenes}} using {{transitions}} {{ignoreInactiveSource}}"
|
||||
AdvSceneSwitcher.audioTab.entry="When the volume of {{audioSources}} is {{condition}} {{volumeWidget}} for {{duration}} seconds switch to {{scenes}} using {{transitions}} {{ignoreInactiveSource}}"
|
||||
AdvSceneSwitcher.audioTab.multiMatchfallbackCondition="If multiple entries match ..."
|
||||
AdvSceneSwitcher.audioTab.multiMatchfallback="... for {{duration}} switch to {{scenes}} using {{transitions}}"
|
||||
AdvSceneSwitcher.audioTab.multiMatchfallback="... for {{duration}} seconds switch to {{scenes}} using {{transitions}}"
|
||||
AdvSceneSwitcher.audioTab.help="This tab will allow you to switch scenes based on the volume of sources.\nFor example, you could automatically switch to a different scene if the volume of your microphone reaches a certain threshold.\n\nClick on the highlighted plus symbol to continue."
|
||||
|
||||
; Video Tab
|
||||
@@ -215,6 +320,7 @@ AdvSceneSwitcher.videoTab.condition.match="exactly matches"
|
||||
AdvSceneSwitcher.videoTab.condition.match.tooltip="An exact match requires the target and the source image to be of the same resolution.\nAdditionally every single pixel needs to match, which is why use of image formats which use compression (e.g. .JPG) is not recommended!"
|
||||
AdvSceneSwitcher.videoTab.condition.differ="does not match"
|
||||
AdvSceneSwitcher.videoTab.condition.hasNotChanged="has not changed"
|
||||
AdvSceneSwitcher.videoTab.condition.hasChanged="has changed"
|
||||
AdvSceneSwitcher.videoTab.ignoreInactiveSource="unless source is inactive"
|
||||
AdvSceneSwitcher.videoTab.entry="When {{videoSources}} {{condition}} {{filePath}} {{browseButton}} for {{duration}} switch to {{scenes}} using {{transitions}} {{ignoreInactiveSource}}"
|
||||
AdvSceneSwitcher.videoTab.help="<html><head/><body><p>This tab will allow you to switch scenes based on the current video output of selected sources.<br/>Make sure to check out <a href=\"https://obsproject.com/forum/resources/pixel-match-switcher.1202/\"><span style=\" text-decoration: underline; color:#268bd2;\">Pixel Match Switcher</span></a> for an even better implementation of this functionality.<br/><br/> Click on the highlighted plus symbol to continue.</p></body></html>"
|
||||
|
||||
@@ -168,7 +168,7 @@ AdvSceneSwitcher.sceneSequenceTab.loadTitle="从文件加载序列 ..."
|
||||
AdvSceneSwitcher.sceneSequenceTab.loadFail="导入失败"
|
||||
AdvSceneSwitcher.sceneSequenceTab.loadSuccess="导入失败成功"
|
||||
AdvSceneSwitcher.sceneSequenceTab.fileType="文本文件 (*.txt)"
|
||||
AdvSceneSwitcher.sceneSequenceTab.entry="当场景 {{startScenes}} 被激活 {{delay}} {{delayUnits}}后使用转场特效 {{transitions}} 切换到场景 {{scenes}} {{interruptible}}"
|
||||
AdvSceneSwitcher.sceneSequenceTab.entry="当场景 {{startScenes}} 被激活 {{delay}} 后使用转场特效 {{transitions}} 切换到场景 {{scenes}} {{interruptible}}"
|
||||
|
||||
; Audio Tab
|
||||
AdvSceneSwitcher.audioTab.title="音频"
|
||||
|
||||
@@ -226,7 +226,7 @@
|
||||
</layout>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_46">
|
||||
<layout class="QHBoxLayout" name="noMatchLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="sizePolicy">
|
||||
@@ -243,22 +243,12 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QDoubleSpinBox" name="noMatchDelay">
|
||||
<property name="suffix">
|
||||
<string>s</string>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<double>999999.989999999990687</double>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||
<layout class="QHBoxLayout" name="cooldownLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
@@ -266,26 +256,6 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QDoubleSpinBox" name="cooldownTime">
|
||||
<property name="suffix">
|
||||
<string>s</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_2">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
@@ -560,6 +530,414 @@
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="macroTab">
|
||||
<attribute name="title">
|
||||
<string>AdvSceneSwitcher.macroTab.title</string>
|
||||
</attribute>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_11" stretch="1,3">
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox_5">
|
||||
<property name="title">
|
||||
<string>AdvSceneSwitcher.macroTab.macros</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_36">
|
||||
<item>
|
||||
<layout class="QGridLayout" name="gridLayout_26">
|
||||
<item row="0" column="0">
|
||||
<widget class="QListWidget" name="macros">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="sortingEnabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="macroHelp">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>AdvSceneSwitcher.macroTab.help</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignCenter</set>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_61">
|
||||
<item>
|
||||
<widget class="QPushButton" name="macroAdd">
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>22</width>
|
||||
<height>22</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="flat">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="themeID" stdset="0">
|
||||
<string notr="true">addIconSmall</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="macroRemove">
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>22</width>
|
||||
<height>22</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="flat">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="themeID" stdset="0">
|
||||
<string notr="true">removeIconSmall</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_130">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeType">
|
||||
<enum>QSizePolicy::Fixed</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>5</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="Line" name="line_46">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_131">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeType">
|
||||
<enum>QSizePolicy::Fixed</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>5</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="macroUp">
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>22</width>
|
||||
<height>22</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset>
|
||||
<normaloff>../../../forms/images/up.svg</normaloff>../../../forms/images/up.svg</iconset>
|
||||
</property>
|
||||
<property name="flat">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="themeID" stdset="0">
|
||||
<string>upArrowIconSmall</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="macroDown">
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>22</width>
|
||||
<height>22</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset>
|
||||
<normaloff>../../../forms/images/down.svg</normaloff>../../../forms/images/down.svg</iconset>
|
||||
</property>
|
||||
<property name="flat">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="themeID" stdset="0">
|
||||
<string>downArrowIconSmall</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="Line" name="line_4">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_65">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="macroEdit">
|
||||
<property name="title">
|
||||
<string>AdvSceneSwitcher.macroTab.edit</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_34" stretch="1,20,10">
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_18" stretch="0,0,0">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_20">
|
||||
<property name="text">
|
||||
<string>AdvSceneSwitcher.macroTab.name</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLineEdit" name="macroName"/>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_13">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QScrollArea" name="macroConditions">
|
||||
<property name="widgetResizable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<widget class="QWidget" name="scrollAreaWidgetContents_2">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>767</width>
|
||||
<height>293</height>
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_35" stretch="10,1">
|
||||
<item>
|
||||
<layout class="QGridLayout" name="gridLayout_27">
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="macroEditConditionHelp">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>AdvSceneSwitcher.macroTab.editConditionHelp</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignCenter</set>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<layout class="QVBoxLayout" name="macroEditConditionLayout"/>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_12">
|
||||
<item>
|
||||
<widget class="QPushButton" name="conditionAdd">
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>22</width>
|
||||
<height>22</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="flat">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="themeID" stdset="0">
|
||||
<string notr="true">addIconSmall</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="conditionRemove">
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>22</width>
|
||||
<height>22</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="flat">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="themeID" stdset="0">
|
||||
<string notr="true">removeIconSmall</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_15">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QScrollArea" name="macroActions">
|
||||
<property name="widgetResizable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<widget class="QWidget" name="scrollAreaWidgetContents_3">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>767</width>
|
||||
<height>145</height>
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_33" stretch="10,1">
|
||||
<item>
|
||||
<layout class="QGridLayout" name="gridLayout_25">
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="macroEditActionHelp">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>AdvSceneSwitcher.macroTab.editActionHelp</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignCenter</set>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<layout class="QVBoxLayout" name="macroEditActionLayout"/>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_21">
|
||||
<item>
|
||||
<widget class="QPushButton" name="actionAdd">
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>22</width>
|
||||
<height>22</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="flat">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="themeID" stdset="0">
|
||||
<string notr="true">addIconSmall</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="actionRemove">
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>22</width>
|
||||
<height>22</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="flat">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="themeID" stdset="0">
|
||||
<string notr="true">removeIconSmall</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_14">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="transitionsTab">
|
||||
<attribute name="title">
|
||||
<string>AdvSceneSwitcher.transitionTab.title</string>
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
|
||||
#include "headers/advanced-scene-switcher.hpp"
|
||||
#include "headers/curl-helper.hpp"
|
||||
#include "headers/utility.hpp"
|
||||
#include "headers/version.h"
|
||||
|
||||
SwitcherData *switcher = nullptr;
|
||||
@@ -63,6 +64,7 @@ void AdvSceneSwitcher::loadUI()
|
||||
setupNetworkTab();
|
||||
setupSceneGroupTab();
|
||||
setupTriggerTab();
|
||||
setupMacroTab();
|
||||
|
||||
setTabOrder();
|
||||
restoreWindowGeo();
|
||||
@@ -73,28 +75,6 @@ void AdvSceneSwitcher::loadUI()
|
||||
/******************************************************************************
|
||||
* UI helpers
|
||||
******************************************************************************/
|
||||
bool AdvSceneSwitcher::DisplayMessage(QString msg, bool question)
|
||||
{
|
||||
if (question) {
|
||||
QMessageBox::StandardButton reply;
|
||||
reply = QMessageBox::question(
|
||||
nullptr, "Advanced Scene Switcher", msg,
|
||||
QMessageBox::Yes | QMessageBox::No);
|
||||
if (reply == QMessageBox::Yes) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
QMessageBox Msgbox;
|
||||
Msgbox.setWindowTitle("Advanced Scene Switcher");
|
||||
Msgbox.setText(msg);
|
||||
Msgbox.exec();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::addSelectionEntry(QComboBox *sel,
|
||||
const char *description,
|
||||
bool selectable, const char *tooltip)
|
||||
@@ -479,6 +459,11 @@ void SwitcherData::Thread()
|
||||
bool match = false;
|
||||
OBSWeakSource scene;
|
||||
OBSWeakSource transition;
|
||||
// The previous scene might have changed during the linger duration,
|
||||
// if a longer transition is used than the configured check interval
|
||||
bool setPrevSceneAfterLinger = false;
|
||||
bool macroMatch = false;
|
||||
macroSceneSwitched = false;
|
||||
|
||||
endTime = std::chrono::high_resolution_clock::now();
|
||||
|
||||
@@ -505,15 +490,16 @@ void SwitcherData::Thread()
|
||||
sleep = 0;
|
||||
linger = 0;
|
||||
|
||||
switcher->Prune();
|
||||
if (switcher->stop) {
|
||||
Prune();
|
||||
if (stop) {
|
||||
break;
|
||||
}
|
||||
if (checkPause()) {
|
||||
continue;
|
||||
}
|
||||
match = checkForMatch(scene, transition, linger);
|
||||
if (switcher->stop) {
|
||||
match = checkForMatch(scene, transition, linger,
|
||||
setPrevSceneAfterLinger, macroMatch);
|
||||
if (stop) {
|
||||
break;
|
||||
}
|
||||
checkNoMatchSwitch(match, scene, transition, sleep);
|
||||
@@ -526,7 +512,7 @@ void SwitcherData::Thread()
|
||||
|
||||
cv.wait_for(lock, duration);
|
||||
|
||||
if (switcher->stop) {
|
||||
if (stop) {
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -536,6 +522,8 @@ void SwitcherData::Thread()
|
||||
|
||||
match = false;
|
||||
linger = 0;
|
||||
} else if (setPrevSceneAfterLinger) {
|
||||
scene = previousScene;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -548,12 +536,11 @@ void SwitcherData::Thread()
|
||||
lock.unlock();
|
||||
|
||||
if (match) {
|
||||
if (networkConfig.ServerEnabled) {
|
||||
server.sendMessage(scene, transition);
|
||||
if (macroMatch) {
|
||||
runMacros();
|
||||
} else {
|
||||
switchScene({scene, transition, 0});
|
||||
}
|
||||
switchScene(scene, transition,
|
||||
tansitionOverrideOverride,
|
||||
adjustActiveTransitionType, verbose);
|
||||
}
|
||||
|
||||
writeSceneInfoToFile();
|
||||
@@ -563,12 +550,15 @@ void SwitcherData::Thread()
|
||||
}
|
||||
|
||||
bool SwitcherData::checkForMatch(OBSWeakSource &scene,
|
||||
OBSWeakSource &transition, int &linger)
|
||||
OBSWeakSource &transition, int &linger,
|
||||
bool &setPrevSceneAfterLinger,
|
||||
bool ¯oMatch)
|
||||
{
|
||||
bool match = false;
|
||||
|
||||
if (uninterruptibleSceneSequenceActive) {
|
||||
checkSceneSequence(match, scene, transition, linger);
|
||||
match = checkSceneSequence(scene, transition, linger,
|
||||
setPrevSceneAfterLinger);
|
||||
if (match) {
|
||||
return match;
|
||||
}
|
||||
@@ -577,39 +567,46 @@ bool SwitcherData::checkForMatch(OBSWeakSource &scene,
|
||||
for (int switchFuncName : functionNamesByPriority) {
|
||||
switch (switchFuncName) {
|
||||
case read_file_func:
|
||||
checkSwitchInfoFromFile(match, scene, transition);
|
||||
checkFileContent(match, scene, transition);
|
||||
match = checkSwitchInfoFromFile(scene, transition) ||
|
||||
checkFileContent(scene, transition);
|
||||
break;
|
||||
case idle_func:
|
||||
checkIdleSwitch(match, scene, transition);
|
||||
match = checkIdleSwitch(scene, transition);
|
||||
break;
|
||||
case exe_func:
|
||||
checkExeSwitch(match, scene, transition);
|
||||
match = checkExeSwitch(scene, transition);
|
||||
break;
|
||||
case screen_region_func:
|
||||
checkScreenRegionSwitch(match, scene, transition);
|
||||
match = checkScreenRegionSwitch(scene, transition);
|
||||
break;
|
||||
case window_title_func:
|
||||
checkWindowTitleSwitch(match, scene, transition);
|
||||
match = checkWindowTitleSwitch(scene, transition);
|
||||
break;
|
||||
case round_trip_func:
|
||||
checkSceneSequence(match, scene, transition, linger);
|
||||
match = checkSceneSequence(scene, transition, linger,
|
||||
setPrevSceneAfterLinger);
|
||||
break;
|
||||
case media_func:
|
||||
checkMediaSwitch(match, scene, transition);
|
||||
match = checkMediaSwitch(scene, transition);
|
||||
break;
|
||||
case time_func:
|
||||
checkTimeSwitch(match, scene, transition);
|
||||
match = checkTimeSwitch(scene, transition);
|
||||
break;
|
||||
case audio_func:
|
||||
checkAudioSwitch(match, scene, transition);
|
||||
match = checkAudioSwitch(scene, transition);
|
||||
break;
|
||||
case video_func:
|
||||
checkVideoSwitch(match, scene, transition);
|
||||
match = checkVideoSwitch(scene, transition);
|
||||
break;
|
||||
case macro_func:
|
||||
if (checkMacros()) {
|
||||
match = true;
|
||||
macroMatch = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (switcher->stop) {
|
||||
if (stop) {
|
||||
return false;
|
||||
}
|
||||
if (match) {
|
||||
@@ -619,31 +616,33 @@ bool SwitcherData::checkForMatch(OBSWeakSource &scene,
|
||||
return match;
|
||||
}
|
||||
|
||||
void switchScene(OBSWeakSource &scene, OBSWeakSource &transition,
|
||||
bool transitionOverrideOverride,
|
||||
bool adjustActiveTransitionType, bool verbose)
|
||||
void switchScene(sceneSwitchInfo sceneSwitch)
|
||||
{
|
||||
if (!scene && verbose) {
|
||||
if (!sceneSwitch.scene && switcher->verbose) {
|
||||
blog(LOG_INFO, "nothing to switch to");
|
||||
return;
|
||||
}
|
||||
|
||||
obs_source_t *source = obs_weak_source_get_source(scene);
|
||||
obs_source_t *source = obs_weak_source_get_source(sceneSwitch.scene);
|
||||
obs_source_t *currentSource = obs_frontend_get_current_scene();
|
||||
|
||||
if (source && source != currentSource) {
|
||||
transitionData td;
|
||||
setNextTransition(scene, currentSource, transition,
|
||||
transitionOverrideOverride,
|
||||
adjustActiveTransitionType, td);
|
||||
transitionData currentTransitionData;
|
||||
setNextTransition(sceneSwitch, currentSource,
|
||||
currentTransitionData);
|
||||
obs_frontend_set_current_scene(source);
|
||||
if (transitionOverrideOverride) {
|
||||
restoreTransitionOverride(source, td);
|
||||
if (switcher->transitionOverrideOverride) {
|
||||
restoreTransitionOverride(source,
|
||||
currentTransitionData);
|
||||
}
|
||||
|
||||
if (verbose) {
|
||||
if (switcher->verbose) {
|
||||
blog(LOG_INFO, "switched scene");
|
||||
}
|
||||
|
||||
if (switcher->networkConfig.ServerEnabled) {
|
||||
switcher->server.sendMessage(sceneSwitch);
|
||||
}
|
||||
}
|
||||
obs_source_release(currentSource);
|
||||
obs_source_release(source);
|
||||
@@ -653,9 +652,8 @@ void SwitcherData::Start()
|
||||
{
|
||||
if (!(th && th->isRunning())) {
|
||||
stop = false;
|
||||
switcher->th = new SwitcherThread();
|
||||
switcher->th->start(
|
||||
(QThread::Priority)switcher->threadPriority);
|
||||
th = new SwitcherThread();
|
||||
th->start((QThread::Priority)threadPriority);
|
||||
|
||||
// Will be overwritten quickly but might be useful
|
||||
writeToStatusFile("Advanced Scene Switcher running");
|
||||
@@ -674,7 +672,7 @@ void SwitcherData::Start()
|
||||
void SwitcherData::Stop()
|
||||
{
|
||||
if (th && th->isRunning()) {
|
||||
switcher->stop = true;
|
||||
stop = true;
|
||||
transitionCv.notify_one();
|
||||
cv.notify_one();
|
||||
th->wait();
|
||||
@@ -737,7 +735,7 @@ void handleSceneChange(SwitcherData *s)
|
||||
|
||||
if (switcher->networkConfig.ServerEnabled &&
|
||||
switcher->networkConfig.SendAll) {
|
||||
switcher->server.sendMessage(ws, nullptr);
|
||||
switcher->server.sendMessage({ws, nullptr, 0});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
139
src/duration-control.cpp
Normal file
139
src/duration-control.cpp
Normal file
@@ -0,0 +1,139 @@
|
||||
#include "headers/duration-control.hpp"
|
||||
#include "obs-module.h"
|
||||
|
||||
#include <sstream>
|
||||
#include <iomanip>
|
||||
#include <QHBoxLayout>
|
||||
|
||||
void Duration::Save(obs_data_t *obj, const char *secondsName,
|
||||
const char *unitName)
|
||||
{
|
||||
obs_data_set_double(obj, secondsName, seconds);
|
||||
obs_data_set_int(obj, unitName, static_cast<int>(displayUnit));
|
||||
}
|
||||
|
||||
void Duration::Load(obs_data_t *obj, const char *secondsName,
|
||||
const char *unitName)
|
||||
{
|
||||
seconds = obs_data_get_double(obj, secondsName);
|
||||
displayUnit =
|
||||
static_cast<DurationUnit>(obs_data_get_int(obj, unitName));
|
||||
}
|
||||
|
||||
bool Duration::DurationReached()
|
||||
{
|
||||
if (_startTime.time_since_epoch().count() == 0) {
|
||||
_startTime = std::chrono::high_resolution_clock::now();
|
||||
}
|
||||
|
||||
auto runTime = std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||
std::chrono::high_resolution_clock::now() - _startTime);
|
||||
return runTime.count() >= seconds * 1000;
|
||||
}
|
||||
|
||||
void Duration::Reset()
|
||||
{
|
||||
_startTime = {};
|
||||
}
|
||||
|
||||
int durationUnitToMultiplier(DurationUnit u)
|
||||
{
|
||||
switch (u) {
|
||||
case DurationUnit::SECONDS:
|
||||
return 1;
|
||||
case DurationUnit::MINUTES:
|
||||
return 60;
|
||||
case DurationUnit::HOURS:
|
||||
return 3600;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::string durationUnitToString(DurationUnit u)
|
||||
{
|
||||
switch (u) {
|
||||
case DurationUnit::SECONDS:
|
||||
return obs_module_text("AdvSceneSwitcher.unit.secends");
|
||||
case DurationUnit::MINUTES:
|
||||
return obs_module_text("AdvSceneSwitcher.unit.minutes");
|
||||
case DurationUnit::HOURS:
|
||||
return obs_module_text("AdvSceneSwitcher.unit.hours");
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
std::string Duration::ToString()
|
||||
{
|
||||
std::ostringstream ss;
|
||||
ss << std::fixed << std::setprecision(2)
|
||||
<< seconds / durationUnitToMultiplier(displayUnit) << " "
|
||||
<< durationUnitToString(displayUnit);
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
static void populateUnits(QComboBox *list)
|
||||
{
|
||||
list->addItem(obs_module_text("AdvSceneSwitcher.unit.secends"));
|
||||
list->addItem(obs_module_text("AdvSceneSwitcher.unit.minutes"));
|
||||
list->addItem(obs_module_text("AdvSceneSwitcher.unit.hours"));
|
||||
}
|
||||
|
||||
DurationSelection::DurationSelection(QWidget *parent, bool showUnitSelection)
|
||||
: QWidget(parent), _unitMultiplier(1)
|
||||
{
|
||||
_duration = new QDoubleSpinBox(parent);
|
||||
_duration->setMaximum(86400); // 24 hours
|
||||
|
||||
_unitSelection = new QComboBox();
|
||||
populateUnits(_unitSelection);
|
||||
|
||||
QWidget::connect(_duration, SIGNAL(valueChanged(double)), this,
|
||||
SLOT(_DurationChanged(double)));
|
||||
QWidget::connect(_unitSelection, SIGNAL(currentIndexChanged(int)), this,
|
||||
SLOT(_UnitChanged(int)));
|
||||
|
||||
QHBoxLayout *layout = new QHBoxLayout;
|
||||
layout->addWidget(_duration);
|
||||
if (showUnitSelection) {
|
||||
layout->addWidget(_unitSelection);
|
||||
}
|
||||
setLayout(layout);
|
||||
}
|
||||
|
||||
void DurationSelection::SetValue(double value)
|
||||
{
|
||||
_duration->setValue(value / _unitMultiplier);
|
||||
}
|
||||
|
||||
void DurationSelection::SetUnit(DurationUnit u)
|
||||
{
|
||||
_unitSelection->setCurrentIndex(static_cast<int>(u));
|
||||
}
|
||||
|
||||
void DurationSelection::SetDuration(Duration d)
|
||||
{
|
||||
SetUnit(d.displayUnit);
|
||||
SetValue(d.seconds);
|
||||
}
|
||||
|
||||
void DurationSelection::_DurationChanged(double value)
|
||||
{
|
||||
emit DurationChanged(value * _unitMultiplier);
|
||||
}
|
||||
|
||||
void DurationSelection::_UnitChanged(int idx)
|
||||
{
|
||||
DurationUnit unit = static_cast<DurationUnit>(idx);
|
||||
double prevMultiplier = _unitMultiplier;
|
||||
_unitMultiplier = durationUnitToMultiplier(unit);
|
||||
_duration->setValue(_duration->value() *
|
||||
(prevMultiplier / _unitMultiplier));
|
||||
|
||||
emit UnitChanged(unit);
|
||||
}
|
||||
432
src/general.cpp
432
src/general.cpp
@@ -4,8 +4,6 @@
|
||||
#include "headers/utility.hpp"
|
||||
#include "headers/version.h"
|
||||
|
||||
constexpr auto tab_count = 17;
|
||||
|
||||
QMetaObject::Connection inactivePluse;
|
||||
|
||||
void AdvSceneSwitcher::on_close_clicked()
|
||||
@@ -61,14 +59,44 @@ void AdvSceneSwitcher::on_noMatchRandomSwitch_clicked()
|
||||
ui->randomDisabledWarning->setVisible(false);
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::on_noMatchDelay_valueChanged(double i)
|
||||
void AdvSceneSwitcher::NoMatchDelayDurationChanged(double sec)
|
||||
{
|
||||
if (loading) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
switcher->noMatchDelay = i;
|
||||
switcher->noMatchDelay.seconds = sec;
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::NoMatchDelayUnitChanged(DurationUnit unit)
|
||||
{
|
||||
if (loading) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
switcher->noMatchDelay.displayUnit = unit;
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::CooldownDurationChanged(double sec)
|
||||
{
|
||||
if (loading) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
switcher->cooldown.seconds = sec;
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::CooldownUnitChanged(DurationUnit unit)
|
||||
{
|
||||
if (loading) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
switcher->cooldown.displayUnit = unit;
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::on_startupBehavior_currentIndexChanged(int index)
|
||||
@@ -102,16 +130,6 @@ void AdvSceneSwitcher::on_noMatchSwitchScene_currentTextChanged(
|
||||
UpdateNonMatchingScene(text);
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::on_cooldownTime_valueChanged(double i)
|
||||
{
|
||||
if (loading) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
switcher->cooldown = i;
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::on_checkInterval_valueChanged(int value)
|
||||
{
|
||||
if (loading) {
|
||||
@@ -302,51 +320,54 @@ int findTabIndex(QTabWidget *tabWidget, int pos)
|
||||
tabName = "generalTab";
|
||||
break;
|
||||
case 1:
|
||||
tabName = "transitionsTab";
|
||||
tabName = "macroTab";
|
||||
break;
|
||||
case 2:
|
||||
tabName = "pauseTab";
|
||||
tabName = "transitionsTab";
|
||||
break;
|
||||
case 3:
|
||||
tabName = "windowTitleTab";
|
||||
tabName = "pauseTab";
|
||||
break;
|
||||
case 4:
|
||||
tabName = "executableTab";
|
||||
tabName = "windowTitleTab";
|
||||
break;
|
||||
case 5:
|
||||
tabName = "screenRegionTab";
|
||||
tabName = "executableTab";
|
||||
break;
|
||||
case 6:
|
||||
tabName = "mediaTab";
|
||||
tabName = "screenRegionTab";
|
||||
break;
|
||||
case 7:
|
||||
tabName = "fileTab";
|
||||
tabName = "mediaTab";
|
||||
break;
|
||||
case 8:
|
||||
tabName = "randomTab";
|
||||
tabName = "fileTab";
|
||||
break;
|
||||
case 9:
|
||||
tabName = "timeTab";
|
||||
tabName = "randomTab";
|
||||
break;
|
||||
case 10:
|
||||
tabName = "idleTab";
|
||||
tabName = "timeTab";
|
||||
break;
|
||||
case 11:
|
||||
tabName = "sceneSequenceTab";
|
||||
tabName = "idleTab";
|
||||
break;
|
||||
case 12:
|
||||
tabName = "audioTab";
|
||||
tabName = "sceneSequenceTab";
|
||||
break;
|
||||
case 13:
|
||||
tabName = "videoTab";
|
||||
tabName = "audioTab";
|
||||
break;
|
||||
case 14:
|
||||
tabName = "networkTab";
|
||||
tabName = "videoTab";
|
||||
break;
|
||||
case 15:
|
||||
tabName = "sceneGroupTab";
|
||||
tabName = "networkTab";
|
||||
break;
|
||||
case 16:
|
||||
tabName = "sceneGroupTab";
|
||||
break;
|
||||
case 17:
|
||||
tabName = "sceneTriggerTab";
|
||||
break;
|
||||
}
|
||||
@@ -365,6 +386,10 @@ int findTabIndex(QTabWidget *tabWidget, int pos)
|
||||
|
||||
void AdvSceneSwitcher::setTabOrder()
|
||||
{
|
||||
if (!switcher->tabOrderValid()) {
|
||||
switcher->resetTabOrder();
|
||||
}
|
||||
|
||||
QTabBar *bar = ui->tabWidget->tabBar();
|
||||
for (int i = 0; i < bar->count(); ++i) {
|
||||
int curPos = findTabIndex(ui->tabWidget, switcher->tabOrder[i]);
|
||||
@@ -410,6 +435,7 @@ void SwitcherData::loadSettings(obs_data_t *obj)
|
||||
}
|
||||
|
||||
loadSceneGroups(obj);
|
||||
loadMacros(obj);
|
||||
loadWindowTitleSwitches(obj);
|
||||
loadScreenRegionSwitches(obj);
|
||||
loadPauseSwitches(obj);
|
||||
@@ -435,7 +461,11 @@ void SwitcherData::saveSettings(obs_data_t *obj)
|
||||
return;
|
||||
}
|
||||
|
||||
// Needs to be loaded before any entries which might rely on scene group
|
||||
// selections to be available.
|
||||
saveSceneGroups(obj);
|
||||
|
||||
saveMacros(obj);
|
||||
saveWindowTitleSwitches(obj);
|
||||
saveScreenRegionSwitches(obj);
|
||||
savePauseSwitches(obj);
|
||||
@@ -458,112 +488,95 @@ void SwitcherData::saveSettings(obs_data_t *obj)
|
||||
|
||||
void SwitcherData::saveGeneralSettings(obs_data_t *obj)
|
||||
{
|
||||
obs_data_set_int(obj, "interval", switcher->interval);
|
||||
obs_data_set_int(obj, "interval", interval);
|
||||
|
||||
std::string nonMatchingSceneName =
|
||||
GetWeakSourceName(switcher->nonMatchingScene);
|
||||
std::string nonMatchingSceneName = GetWeakSourceName(nonMatchingScene);
|
||||
obs_data_set_string(obj, "non_matching_scene",
|
||||
nonMatchingSceneName.c_str());
|
||||
obs_data_set_int(obj, "switch_if_not_matching",
|
||||
switcher->switchIfNotMatching);
|
||||
obs_data_set_double(obj, "noMatchDelay", switcher->noMatchDelay);
|
||||
obs_data_set_int(obj, "switch_if_not_matching", switchIfNotMatching);
|
||||
noMatchDelay.Save(obj, "noMatchDelay", "noMatchDelayUnit");
|
||||
|
||||
obs_data_set_double(obj, "cooldown", switcher->cooldown);
|
||||
cooldown.Save(obj, "cooldown", "cooldownUnit");
|
||||
|
||||
obs_data_set_bool(obj, "active", !switcher->stop);
|
||||
obs_data_set_int(obj, "startup_behavior", switcher->startupBehavior);
|
||||
obs_data_set_bool(obj, "active", !stop);
|
||||
obs_data_set_int(obj, "startup_behavior", startupBehavior);
|
||||
|
||||
obs_data_set_int(obj, "autoStartEvent",
|
||||
static_cast<int>(switcher->autoStartEvent));
|
||||
static_cast<int>(autoStartEvent));
|
||||
|
||||
obs_data_set_bool(obj, "verbose", switcher->verbose);
|
||||
obs_data_set_bool(obj, "disableHints", switcher->disableHints);
|
||||
obs_data_set_bool(obj, "verbose", verbose);
|
||||
obs_data_set_bool(obj, "disableHints", disableHints);
|
||||
|
||||
obs_data_set_int(obj, "priority0",
|
||||
switcher->functionNamesByPriority[0]);
|
||||
obs_data_set_int(obj, "priority1",
|
||||
switcher->functionNamesByPriority[1]);
|
||||
obs_data_set_int(obj, "priority2",
|
||||
switcher->functionNamesByPriority[2]);
|
||||
obs_data_set_int(obj, "priority3",
|
||||
switcher->functionNamesByPriority[3]);
|
||||
obs_data_set_int(obj, "priority4",
|
||||
switcher->functionNamesByPriority[4]);
|
||||
obs_data_set_int(obj, "priority5",
|
||||
switcher->functionNamesByPriority[5]);
|
||||
obs_data_set_int(obj, "priority6",
|
||||
switcher->functionNamesByPriority[6]);
|
||||
obs_data_set_int(obj, "priority7",
|
||||
switcher->functionNamesByPriority[7]);
|
||||
obs_data_set_int(obj, "priority8",
|
||||
switcher->functionNamesByPriority[8]);
|
||||
obs_data_set_int(obj, "priority9",
|
||||
switcher->functionNamesByPriority[9]);
|
||||
obs_data_set_int(obj, "priority0", functionNamesByPriority[0]);
|
||||
obs_data_set_int(obj, "priority1", functionNamesByPriority[1]);
|
||||
obs_data_set_int(obj, "priority2", functionNamesByPriority[2]);
|
||||
obs_data_set_int(obj, "priority3", functionNamesByPriority[3]);
|
||||
obs_data_set_int(obj, "priority4", functionNamesByPriority[4]);
|
||||
obs_data_set_int(obj, "priority5", functionNamesByPriority[5]);
|
||||
obs_data_set_int(obj, "priority6", functionNamesByPriority[6]);
|
||||
obs_data_set_int(obj, "priority7", functionNamesByPriority[7]);
|
||||
obs_data_set_int(obj, "priority8", functionNamesByPriority[8]);
|
||||
obs_data_set_int(obj, "priority9", functionNamesByPriority[9]);
|
||||
obs_data_set_int(obj, "priority10", functionNamesByPriority[10]);
|
||||
|
||||
obs_data_set_int(obj, "threadPriority", switcher->threadPriority);
|
||||
obs_data_set_int(obj, "threadPriority", threadPriority);
|
||||
|
||||
// After fresh install of OBS the vector can be empty
|
||||
// as save() might be called before first load()
|
||||
if (tabOrder.size() < tab_count) {
|
||||
resetTabOrder();
|
||||
}
|
||||
|
||||
obs_data_set_int(obj, "generalTabPos", switcher->tabOrder[0]);
|
||||
obs_data_set_int(obj, "transitionTabPos", switcher->tabOrder[1]);
|
||||
obs_data_set_int(obj, "pauseTabPos", switcher->tabOrder[2]);
|
||||
obs_data_set_int(obj, "titleTabPos", switcher->tabOrder[3]);
|
||||
obs_data_set_int(obj, "exeTabPos", switcher->tabOrder[4]);
|
||||
obs_data_set_int(obj, "regionTabPos", switcher->tabOrder[5]);
|
||||
obs_data_set_int(obj, "mediaTabPos", switcher->tabOrder[6]);
|
||||
obs_data_set_int(obj, "fileTabPos", switcher->tabOrder[7]);
|
||||
obs_data_set_int(obj, "randomTabPos", switcher->tabOrder[8]);
|
||||
obs_data_set_int(obj, "timeTabPos", switcher->tabOrder[9]);
|
||||
obs_data_set_int(obj, "idleTabPos", switcher->tabOrder[10]);
|
||||
obs_data_set_int(obj, "sequenceTabPos", switcher->tabOrder[11]);
|
||||
obs_data_set_int(obj, "audioTabPos", switcher->tabOrder[12]);
|
||||
obs_data_set_int(obj, "videoTabPos", switcher->tabOrder[13]);
|
||||
obs_data_set_int(obj, "networkTabPos", switcher->tabOrder[14]);
|
||||
obs_data_set_int(obj, "sceneGroupTabPos", switcher->tabOrder[15]);
|
||||
obs_data_set_int(obj, "triggerTabPos", switcher->tabOrder[16]);
|
||||
obs_data_set_int(obj, "generalTabPos", tabOrder[0]);
|
||||
obs_data_set_int(obj, "macroTabPos", tabOrder[1]);
|
||||
obs_data_set_int(obj, "transitionTabPos", tabOrder[2]);
|
||||
obs_data_set_int(obj, "pauseTabPos", tabOrder[3]);
|
||||
obs_data_set_int(obj, "titleTabPos", tabOrder[4]);
|
||||
obs_data_set_int(obj, "exeTabPos", tabOrder[5]);
|
||||
obs_data_set_int(obj, "regionTabPos", tabOrder[6]);
|
||||
obs_data_set_int(obj, "mediaTabPos", tabOrder[7]);
|
||||
obs_data_set_int(obj, "fileTabPos", tabOrder[8]);
|
||||
obs_data_set_int(obj, "randomTabPos", tabOrder[9]);
|
||||
obs_data_set_int(obj, "timeTabPos", tabOrder[10]);
|
||||
obs_data_set_int(obj, "idleTabPos", tabOrder[11]);
|
||||
obs_data_set_int(obj, "sequenceTabPos", tabOrder[12]);
|
||||
obs_data_set_int(obj, "audioTabPos", tabOrder[13]);
|
||||
obs_data_set_int(obj, "videoTabPos", tabOrder[14]);
|
||||
obs_data_set_int(obj, "networkTabPos", tabOrder[15]);
|
||||
obs_data_set_int(obj, "sceneGroupTabPos", tabOrder[16]);
|
||||
obs_data_set_int(obj, "triggerTabPos", tabOrder[17]);
|
||||
|
||||
obs_data_set_bool(obj, "saveWindowGeo", saveWindowGeo);
|
||||
obs_data_set_int(obj, "windowPosX", switcher->windowPos.x());
|
||||
obs_data_set_int(obj, "windowPosY", switcher->windowPos.y());
|
||||
obs_data_set_int(obj, "windowWidth", switcher->windowSize.width());
|
||||
obs_data_set_int(obj, "windowHeight", switcher->windowSize.height());
|
||||
obs_data_set_int(obj, "windowPosX", windowPos.x());
|
||||
obs_data_set_int(obj, "windowPosY", windowPos.y());
|
||||
obs_data_set_int(obj, "windowWidth", windowSize.width());
|
||||
obs_data_set_int(obj, "windowHeight", windowSize.height());
|
||||
}
|
||||
|
||||
void SwitcherData::loadGeneralSettings(obs_data_t *obj)
|
||||
{
|
||||
obs_data_set_default_int(obj, "interval", default_interval);
|
||||
switcher->interval = obs_data_get_int(obj, "interval");
|
||||
interval = obs_data_get_int(obj, "interval");
|
||||
|
||||
obs_data_set_default_int(obj, "switch_if_not_matching", NO_SWITCH);
|
||||
switcher->switchIfNotMatching =
|
||||
switchIfNotMatching =
|
||||
(NoMatch)obs_data_get_int(obj, "switch_if_not_matching");
|
||||
std::string nonMatchingScene =
|
||||
std::string nonMatchingSceneName =
|
||||
obs_data_get_string(obj, "non_matching_scene");
|
||||
switcher->nonMatchingScene =
|
||||
GetWeakSourceByName(nonMatchingScene.c_str());
|
||||
switcher->noMatchDelay = obs_data_get_double(obj, "noMatchDelay");
|
||||
nonMatchingScene = GetWeakSourceByName(nonMatchingSceneName.c_str());
|
||||
noMatchDelay.Load(obj, "noMatchDelay", "noMatchDelayUnit");
|
||||
|
||||
switcher->cooldown = obs_data_get_double(obj, "cooldown");
|
||||
cooldown.Load(obj, "cooldown", "cooldownUnit");
|
||||
|
||||
switcher->stop = !obs_data_get_bool(obj, "active");
|
||||
switcher->startupBehavior =
|
||||
stop = !obs_data_get_bool(obj, "active");
|
||||
startupBehavior =
|
||||
(StartupBehavior)obs_data_get_int(obj, "startup_behavior");
|
||||
if (switcher->startupBehavior == START) {
|
||||
switcher->stop = false;
|
||||
if (startupBehavior == START) {
|
||||
stop = false;
|
||||
}
|
||||
if (switcher->startupBehavior == STOP) {
|
||||
switcher->stop = true;
|
||||
if (startupBehavior == STOP) {
|
||||
stop = true;
|
||||
}
|
||||
|
||||
switcher->autoStartEvent = static_cast<AutoStartEvent>(
|
||||
autoStartEvent = static_cast<AutoStartEvent>(
|
||||
obs_data_get_int(obj, "autoStartEvent"));
|
||||
|
||||
switcher->verbose = obs_data_get_bool(obj, "verbose");
|
||||
switcher->disableHints = obs_data_get_bool(obj, "disableHints");
|
||||
verbose = obs_data_get_bool(obj, "verbose");
|
||||
disableHints = obs_data_get_bool(obj, "disableHints");
|
||||
|
||||
obs_data_set_default_int(obj, "priority0", default_priority_0);
|
||||
obs_data_set_default_int(obj, "priority1", default_priority_1);
|
||||
@@ -575,95 +588,73 @@ void SwitcherData::loadGeneralSettings(obs_data_t *obj)
|
||||
obs_data_set_default_int(obj, "priority7", default_priority_7);
|
||||
obs_data_set_default_int(obj, "priority8", default_priority_8);
|
||||
|
||||
switcher->functionNamesByPriority[0] =
|
||||
(obs_data_get_int(obj, "priority0"));
|
||||
switcher->functionNamesByPriority[1] =
|
||||
(obs_data_get_int(obj, "priority1"));
|
||||
switcher->functionNamesByPriority[2] =
|
||||
(obs_data_get_int(obj, "priority2"));
|
||||
switcher->functionNamesByPriority[3] =
|
||||
(obs_data_get_int(obj, "priority3"));
|
||||
switcher->functionNamesByPriority[4] =
|
||||
(obs_data_get_int(obj, "priority4"));
|
||||
switcher->functionNamesByPriority[5] =
|
||||
(obs_data_get_int(obj, "priority5"));
|
||||
switcher->functionNamesByPriority[6] =
|
||||
(obs_data_get_int(obj, "priority6"));
|
||||
switcher->functionNamesByPriority[7] =
|
||||
(obs_data_get_int(obj, "priority7"));
|
||||
switcher->functionNamesByPriority[8] =
|
||||
(obs_data_get_int(obj, "priority8"));
|
||||
switcher->functionNamesByPriority[9] =
|
||||
(obs_data_get_int(obj, "priority9"));
|
||||
if (!switcher->prioFuncsValid()) {
|
||||
switcher->functionNamesByPriority[0] = (default_priority_0);
|
||||
switcher->functionNamesByPriority[1] = (default_priority_1);
|
||||
switcher->functionNamesByPriority[2] = (default_priority_2);
|
||||
switcher->functionNamesByPriority[3] = (default_priority_3);
|
||||
switcher->functionNamesByPriority[4] = (default_priority_4);
|
||||
switcher->functionNamesByPriority[5] = (default_priority_5);
|
||||
switcher->functionNamesByPriority[6] = (default_priority_6);
|
||||
switcher->functionNamesByPriority[7] = (default_priority_7);
|
||||
switcher->functionNamesByPriority[8] = (default_priority_8);
|
||||
switcher->functionNamesByPriority[9] = (default_priority_9);
|
||||
functionNamesByPriority[0] = (obs_data_get_int(obj, "priority0"));
|
||||
functionNamesByPriority[1] = (obs_data_get_int(obj, "priority1"));
|
||||
functionNamesByPriority[2] = (obs_data_get_int(obj, "priority2"));
|
||||
functionNamesByPriority[3] = (obs_data_get_int(obj, "priority3"));
|
||||
functionNamesByPriority[4] = (obs_data_get_int(obj, "priority4"));
|
||||
functionNamesByPriority[5] = (obs_data_get_int(obj, "priority5"));
|
||||
functionNamesByPriority[6] = (obs_data_get_int(obj, "priority6"));
|
||||
functionNamesByPriority[7] = (obs_data_get_int(obj, "priority7"));
|
||||
functionNamesByPriority[8] = (obs_data_get_int(obj, "priority8"));
|
||||
functionNamesByPriority[9] = (obs_data_get_int(obj, "priority9"));
|
||||
functionNamesByPriority[10] = (obs_data_get_int(obj, "priority10"));
|
||||
if (!prioFuncsValid()) {
|
||||
functionNamesByPriority[0] = (default_priority_0);
|
||||
functionNamesByPriority[1] = (default_priority_1);
|
||||
functionNamesByPriority[2] = (default_priority_2);
|
||||
functionNamesByPriority[3] = (default_priority_3);
|
||||
functionNamesByPriority[4] = (default_priority_4);
|
||||
functionNamesByPriority[5] = (default_priority_5);
|
||||
functionNamesByPriority[6] = (default_priority_6);
|
||||
functionNamesByPriority[7] = (default_priority_7);
|
||||
functionNamesByPriority[8] = (default_priority_8);
|
||||
functionNamesByPriority[9] = (default_priority_9);
|
||||
functionNamesByPriority[10] = (default_priority_10);
|
||||
}
|
||||
|
||||
obs_data_set_default_int(obj, "threadPriority",
|
||||
QThread::NormalPriority);
|
||||
switcher->threadPriority = obs_data_get_int(obj, "threadPriority");
|
||||
threadPriority = obs_data_get_int(obj, "threadPriority");
|
||||
|
||||
obs_data_set_default_int(obj, "generalTabPos", 0);
|
||||
obs_data_set_default_int(obj, "transitionTabPos", 1);
|
||||
obs_data_set_default_int(obj, "pauseTabPos", 2);
|
||||
obs_data_set_default_int(obj, "titleTabPos", 3);
|
||||
obs_data_set_default_int(obj, "exeTabPos", 4);
|
||||
obs_data_set_default_int(obj, "regionTabPos", 5);
|
||||
obs_data_set_default_int(obj, "mediaTabPos", 6);
|
||||
obs_data_set_default_int(obj, "fileTabPos", 7);
|
||||
obs_data_set_default_int(obj, "randomTabPos", 8);
|
||||
obs_data_set_default_int(obj, "timeTabPos", 9);
|
||||
obs_data_set_default_int(obj, "idleTabPos", 10);
|
||||
obs_data_set_default_int(obj, "sequenceTabPos", 11);
|
||||
obs_data_set_default_int(obj, "audioTabPos", 12);
|
||||
obs_data_set_default_int(obj, "videoTabPos", 13);
|
||||
obs_data_set_default_int(obj, "networkTabPos", 14);
|
||||
obs_data_set_default_int(obj, "sceneGroupTabPos", 15);
|
||||
obs_data_set_default_int(obj, "triggerTabPos", 16);
|
||||
obs_data_set_default_int(obj, "macroTabPos", 1);
|
||||
obs_data_set_default_int(obj, "transitionTabPos", 2);
|
||||
obs_data_set_default_int(obj, "pauseTabPos", 3);
|
||||
obs_data_set_default_int(obj, "titleTabPos", 4);
|
||||
obs_data_set_default_int(obj, "exeTabPos", 5);
|
||||
obs_data_set_default_int(obj, "regionTabPos", 6);
|
||||
obs_data_set_default_int(obj, "mediaTabPos", 7);
|
||||
obs_data_set_default_int(obj, "fileTabPos", 8);
|
||||
obs_data_set_default_int(obj, "randomTabPos", 9);
|
||||
obs_data_set_default_int(obj, "timeTabPos", 10);
|
||||
obs_data_set_default_int(obj, "idleTabPos", 11);
|
||||
obs_data_set_default_int(obj, "sequenceTabPos", 12);
|
||||
obs_data_set_default_int(obj, "audioTabPos", 13);
|
||||
obs_data_set_default_int(obj, "videoTabPos", 14);
|
||||
obs_data_set_default_int(obj, "networkTabPos", 15);
|
||||
obs_data_set_default_int(obj, "sceneGroupTabPos", 16);
|
||||
obs_data_set_default_int(obj, "triggerTabPos", 17);
|
||||
|
||||
switcher->tabOrder.emplace_back(
|
||||
(int)(obs_data_get_int(obj, "generalTabPos")));
|
||||
switcher->tabOrder.emplace_back(
|
||||
(int)(obs_data_get_int(obj, "transitionTabPos")));
|
||||
switcher->tabOrder.emplace_back(
|
||||
(int)(obs_data_get_int(obj, "pauseTabPos")));
|
||||
switcher->tabOrder.emplace_back(
|
||||
(int)(obs_data_get_int(obj, "titleTabPos")));
|
||||
switcher->tabOrder.emplace_back(
|
||||
(int)(obs_data_get_int(obj, "exeTabPos")));
|
||||
switcher->tabOrder.emplace_back(
|
||||
(int)(obs_data_get_int(obj, "regionTabPos")));
|
||||
switcher->tabOrder.emplace_back(
|
||||
(int)(obs_data_get_int(obj, "mediaTabPos")));
|
||||
switcher->tabOrder.emplace_back(
|
||||
(int)(obs_data_get_int(obj, "fileTabPos")));
|
||||
switcher->tabOrder.emplace_back(
|
||||
(int)(obs_data_get_int(obj, "randomTabPos")));
|
||||
switcher->tabOrder.emplace_back(
|
||||
(int)(obs_data_get_int(obj, "timeTabPos")));
|
||||
switcher->tabOrder.emplace_back(
|
||||
(int)(obs_data_get_int(obj, "idleTabPos")));
|
||||
switcher->tabOrder.emplace_back(
|
||||
(int)(obs_data_get_int(obj, "sequenceTabPos")));
|
||||
switcher->tabOrder.emplace_back(
|
||||
(int)(obs_data_get_int(obj, "audioTabPos")));
|
||||
switcher->tabOrder.emplace_back(
|
||||
(int)(obs_data_get_int(obj, "videoTabPos")));
|
||||
switcher->tabOrder.emplace_back(
|
||||
(int)(obs_data_get_int(obj, "networkTabPos")));
|
||||
switcher->tabOrder.emplace_back(
|
||||
(int)(obs_data_get_int(obj, "sceneGroupTabPos")));
|
||||
switcher->tabOrder.emplace_back(
|
||||
(int)(obs_data_get_int(obj, "triggerTabPos")));
|
||||
tabOrder.clear();
|
||||
tabOrder.emplace_back((int)(obs_data_get_int(obj, "generalTabPos")));
|
||||
tabOrder.emplace_back((int)(obs_data_get_int(obj, "macroTabPos")));
|
||||
tabOrder.emplace_back((int)(obs_data_get_int(obj, "transitionTabPos")));
|
||||
tabOrder.emplace_back((int)(obs_data_get_int(obj, "pauseTabPos")));
|
||||
tabOrder.emplace_back((int)(obs_data_get_int(obj, "titleTabPos")));
|
||||
tabOrder.emplace_back((int)(obs_data_get_int(obj, "exeTabPos")));
|
||||
tabOrder.emplace_back((int)(obs_data_get_int(obj, "regionTabPos")));
|
||||
tabOrder.emplace_back((int)(obs_data_get_int(obj, "mediaTabPos")));
|
||||
tabOrder.emplace_back((int)(obs_data_get_int(obj, "fileTabPos")));
|
||||
tabOrder.emplace_back((int)(obs_data_get_int(obj, "randomTabPos")));
|
||||
tabOrder.emplace_back((int)(obs_data_get_int(obj, "timeTabPos")));
|
||||
tabOrder.emplace_back((int)(obs_data_get_int(obj, "idleTabPos")));
|
||||
tabOrder.emplace_back((int)(obs_data_get_int(obj, "sequenceTabPos")));
|
||||
tabOrder.emplace_back((int)(obs_data_get_int(obj, "audioTabPos")));
|
||||
tabOrder.emplace_back((int)(obs_data_get_int(obj, "videoTabPos")));
|
||||
tabOrder.emplace_back((int)(obs_data_get_int(obj, "networkTabPos")));
|
||||
tabOrder.emplace_back((int)(obs_data_get_int(obj, "sceneGroupTabPos")));
|
||||
tabOrder.emplace_back((int)(obs_data_get_int(obj, "triggerTabPos")));
|
||||
|
||||
if (!tabOrderValid()) {
|
||||
resetTabOrder();
|
||||
@@ -678,28 +669,33 @@ void SwitcherData::loadGeneralSettings(obs_data_t *obj)
|
||||
|
||||
bool SwitcherData::tabOrderValid()
|
||||
{
|
||||
auto tmp = tabOrder;
|
||||
std::sort(tmp.begin(), tmp.end());
|
||||
auto it = std::unique(tmp.begin(), tmp.end());
|
||||
return it == tmp.end();
|
||||
auto tmp = std::vector<int>(tab_count);
|
||||
std::iota(tmp.begin(), tmp.end(), 0);
|
||||
|
||||
for (auto &p : tmp) {
|
||||
auto it = std::find(tabOrder.begin(), tabOrder.end(), p);
|
||||
if (it == tabOrder.end()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void SwitcherData::resetTabOrder()
|
||||
{
|
||||
tabOrder = std::vector<int>(tab_count);
|
||||
std::iota(switcher->tabOrder.begin(), switcher->tabOrder.end(), 0);
|
||||
std::iota(tabOrder.begin(), tabOrder.end(), 0);
|
||||
}
|
||||
|
||||
void SwitcherData::checkNoMatchSwitch(bool &match, OBSWeakSource &scene,
|
||||
OBSWeakSource &transition, int &sleep)
|
||||
{
|
||||
if (match) {
|
||||
noMatchCount = 0;
|
||||
noMatchDelay.Reset();
|
||||
return;
|
||||
}
|
||||
|
||||
if ((noMatchCount * interval) / 1000.0 < noMatchDelay) {
|
||||
noMatchCount++;
|
||||
if (!noMatchDelay.DurationReached()) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -709,27 +705,22 @@ void SwitcherData::checkNoMatchSwitch(bool &match, OBSWeakSource &scene,
|
||||
transition = nullptr;
|
||||
}
|
||||
if (switchIfNotMatching == RANDOM_SWITCH) {
|
||||
checkRandom(match, scene, transition, sleep);
|
||||
match = checkRandom(scene, transition, sleep);
|
||||
}
|
||||
}
|
||||
|
||||
void SwitcherData::checkSwitchCooldown(bool &match)
|
||||
{
|
||||
if (!match || cooldown == 0.) {
|
||||
if (!match) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto now = std::chrono::high_resolution_clock::now();
|
||||
auto timePassed = std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||
now - lastMatchTime);
|
||||
|
||||
if (timePassed.count() > cooldown * 1000) {
|
||||
lastMatchTime = now;
|
||||
return;
|
||||
if (cooldown.DurationReached()) {
|
||||
cooldown.Reset();
|
||||
} else {
|
||||
match = false;
|
||||
vblog(LOG_INFO, "cooldown active - ignoring match");
|
||||
}
|
||||
|
||||
match = false;
|
||||
vblog(LOG_INFO, "cooldown active - ignoring match");
|
||||
}
|
||||
|
||||
void populateStartupBehavior(QComboBox *cb)
|
||||
@@ -785,14 +776,29 @@ void AdvSceneSwitcher::setupGeneralTab()
|
||||
}
|
||||
ui->noMatchSwitchScene->setCurrentText(
|
||||
GetWeakSourceName(switcher->nonMatchingScene).c_str());
|
||||
ui->noMatchDelay->setValue(switcher->noMatchDelay);
|
||||
ui->noMatchDelay->setToolTip(obs_module_text(
|
||||
|
||||
DurationSelection *noMatchDelay = new DurationSelection();
|
||||
noMatchDelay->SetDuration(switcher->noMatchDelay);
|
||||
noMatchDelay->setToolTip(obs_module_text(
|
||||
"AdvSceneSwitcher.generalTab.generalBehavior.onNoMetDelayTooltip"));
|
||||
ui->noMatchLayout->addWidget(noMatchDelay);
|
||||
QWidget::connect(noMatchDelay, SIGNAL(DurationChanged(double)), this,
|
||||
SLOT(NoMatchDelayDurationChanged(double)));
|
||||
QWidget::connect(noMatchDelay, SIGNAL(UnitChanged(DurationUnit)), this,
|
||||
SLOT(NoMatchDelayUnitChanged(DurationUnit)));
|
||||
|
||||
ui->checkInterval->setValue(switcher->interval);
|
||||
|
||||
ui->cooldownTime->setValue(switcher->cooldown);
|
||||
ui->cooldownTime->setToolTip(obs_module_text(
|
||||
DurationSelection *cooldownTime = new DurationSelection();
|
||||
cooldownTime->SetDuration(switcher->cooldown);
|
||||
cooldownTime->setToolTip(obs_module_text(
|
||||
"AdvSceneSwitcher.generalTab.generalBehavior.cooldownHint"));
|
||||
ui->cooldownLayout->addWidget(cooldownTime);
|
||||
ui->cooldownLayout->addStretch();
|
||||
QWidget::connect(cooldownTime, SIGNAL(DurationChanged(double)), this,
|
||||
SLOT(CooldownDurationChanged(double)));
|
||||
QWidget::connect(cooldownTime, SIGNAL(UnitChanged(DurationUnit)), this,
|
||||
SLOT(CooldownUnitChanged(DurationUnit)));
|
||||
|
||||
ui->verboseLogging->setChecked(switcher->verbose);
|
||||
ui->saveWindowGeo->setChecked(switcher->saveWindowGeo);
|
||||
@@ -841,6 +847,10 @@ void AdvSceneSwitcher::setupGeneralTab()
|
||||
s = obs_module_text(
|
||||
"AdvSceneSwitcher.generalTab.priority.video");
|
||||
break;
|
||||
case macro_func:
|
||||
s = obs_module_text(
|
||||
"AdvSceneSwitcher.generalTab.priority.macro");
|
||||
break;
|
||||
}
|
||||
QString text(s.c_str());
|
||||
QListWidgetItem *item =
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
|
||||
#define blog(level, msg, ...) blog(level, "[adv-ss] " msg, ##__VA_ARGS__)
|
||||
#define vblog(level, msg, ...) \
|
||||
if (verbose) { \
|
||||
if (switcher->verbose) { \
|
||||
blog(level, msg, ##__VA_ARGS__); \
|
||||
}
|
||||
|
||||
@@ -43,6 +43,10 @@ public:
|
||||
void OpenSequenceExtendEdit(SequenceWidget *sw);
|
||||
void SetEditSceneGroup(SceneGroup &sg);
|
||||
|
||||
bool addNewMacro(std::string &name);
|
||||
Macro *getSelectedMacro();
|
||||
void SetEditMacro(Macro &m);
|
||||
|
||||
void loadUI();
|
||||
void setupGeneralTab();
|
||||
void setupTitleTab();
|
||||
@@ -61,10 +65,10 @@ public:
|
||||
void setupTriggerTab();
|
||||
void setupVideoTab();
|
||||
void setupNetworkTab();
|
||||
void setupMacroTab();
|
||||
void setTabOrder();
|
||||
void restoreWindowGeo();
|
||||
|
||||
static bool DisplayMessage(QString msg, bool question = false);
|
||||
static void AskBackup(obs_data_t *obj);
|
||||
|
||||
static void addSelectionEntry(QComboBox *sel, const char *description,
|
||||
@@ -115,8 +119,10 @@ public slots:
|
||||
void on_noMatchDontSwitch_clicked();
|
||||
void on_noMatchSwitch_clicked();
|
||||
void on_noMatchRandomSwitch_clicked();
|
||||
void on_noMatchDelay_valueChanged(double i);
|
||||
void on_cooldownTime_valueChanged(double i);
|
||||
void NoMatchDelayDurationChanged(double);
|
||||
void NoMatchDelayUnitChanged(DurationUnit);
|
||||
void CooldownDurationChanged(double);
|
||||
void CooldownUnitChanged(DurationUnit);
|
||||
void on_startupBehavior_currentIndexChanged(int index);
|
||||
void on_autoStartEvent_currentIndexChanged(int index);
|
||||
void on_noMatchSwitchScene_currentTextChanged(const QString &text);
|
||||
@@ -125,6 +131,19 @@ public slots:
|
||||
void on_tabMoved(int from, int to);
|
||||
void on_tabWidget_currentChanged(int index);
|
||||
|
||||
void on_macroAdd_clicked();
|
||||
void on_macroRemove_clicked();
|
||||
void on_macroUp_clicked();
|
||||
void on_macroDown_clicked();
|
||||
void on_macroName_editingFinished();
|
||||
void on_macros_currentRowChanged(int idx);
|
||||
void on_conditionAdd_clicked();
|
||||
void on_conditionRemove_clicked();
|
||||
void on_actionAdd_clicked();
|
||||
void on_actionRemove_clicked();
|
||||
void showMacroContextMenu(const QPoint &);
|
||||
void copyMacro();
|
||||
|
||||
void on_screenRegionSwitches_currentRowChanged(int idx);
|
||||
void on_showFrame_clicked();
|
||||
void on_screenRegionAdd_clicked();
|
||||
@@ -286,16 +305,11 @@ bool isInFocus(const QString &executable);
|
||||
* Sceneswitch helper
|
||||
******************************************************************************/
|
||||
|
||||
void setNextTransition(OBSWeakSource &targetScene, obs_source_t *currentSource,
|
||||
OBSWeakSource &transition,
|
||||
bool transitionOverrideOverride,
|
||||
bool adjustActiveTransitionType, transitionData &td);
|
||||
void overwriteTransitionOverride(obs_weak_source_t *sceneWs,
|
||||
obs_source_t *transition, transitionData &td);
|
||||
void setNextTransition(sceneSwitchInfo &ssi, obs_source_t *currentSource,
|
||||
transitionData &td);
|
||||
void overwriteTransitionOverride(sceneSwitchInfo ssi, transitionData &td);
|
||||
void restoreTransitionOverride(obs_source_t *scene, transitionData td);
|
||||
void switchScene(OBSWeakSource &scene, OBSWeakSource &transition,
|
||||
bool transitionOverrideOverride,
|
||||
bool adjustActiveTransitionType, bool verbose);
|
||||
void switchScene(sceneSwitchInfo ssi);
|
||||
|
||||
/******************************************************************************
|
||||
* Main SwitcherData
|
||||
|
||||
55
src/headers/duration-control.hpp
Normal file
55
src/headers/duration-control.hpp
Normal file
@@ -0,0 +1,55 @@
|
||||
#pragma once
|
||||
#include <QWidget>
|
||||
#include <QDoubleSpinBox>
|
||||
#include <QComboBox>
|
||||
#include <chrono>
|
||||
|
||||
#include "obs-data.h"
|
||||
|
||||
enum class DurationUnit {
|
||||
SECONDS,
|
||||
MINUTES,
|
||||
HOURS,
|
||||
};
|
||||
|
||||
class Duration {
|
||||
public:
|
||||
void Save(obs_data_t *obj, const char *secondsName = "seconds",
|
||||
const char *unitName = "displayUnit");
|
||||
void Load(obs_data_t *obj, const char *secondsName = "seconds",
|
||||
const char *unitName = "displayUnit");
|
||||
|
||||
bool DurationReached();
|
||||
void Reset();
|
||||
std::string ToString();
|
||||
|
||||
double seconds = 0.;
|
||||
// only used for UI
|
||||
DurationUnit displayUnit = DurationUnit::SECONDS;
|
||||
|
||||
private:
|
||||
std::chrono::high_resolution_clock::time_point _startTime;
|
||||
};
|
||||
|
||||
class DurationSelection : public QWidget {
|
||||
Q_OBJECT
|
||||
public:
|
||||
DurationSelection(QWidget *parent = nullptr,
|
||||
bool showUnitSelection = true);
|
||||
void SetValue(double value);
|
||||
void SetUnit(DurationUnit u);
|
||||
void SetDuration(Duration d);
|
||||
|
||||
private slots:
|
||||
void _DurationChanged(double value);
|
||||
void _UnitChanged(int idx);
|
||||
signals:
|
||||
void DurationChanged(double value); // always reutrn value in seconds
|
||||
void UnitChanged(DurationUnit u);
|
||||
|
||||
private:
|
||||
QDoubleSpinBox *_duration;
|
||||
QComboBox *_unitSelection;
|
||||
|
||||
double _unitMultiplier;
|
||||
};
|
||||
63
src/headers/macro-action-audio.hpp
Normal file
63
src/headers/macro-action-audio.hpp
Normal file
@@ -0,0 +1,63 @@
|
||||
#pragma once
|
||||
#include <QSpinBox>
|
||||
#include "macro-action-edit.hpp"
|
||||
|
||||
enum class AudioAction {
|
||||
MUTE,
|
||||
UNMUTE,
|
||||
SOURCE_VOLUME,
|
||||
MASTER_VOLUME,
|
||||
};
|
||||
|
||||
class MacroActionAudio : public MacroAction {
|
||||
public:
|
||||
bool PerformAction();
|
||||
void LogAction();
|
||||
bool Save(obs_data_t *obj);
|
||||
bool Load(obs_data_t *obj);
|
||||
int GetId() { return id; };
|
||||
static std::shared_ptr<MacroAction> Create()
|
||||
{
|
||||
return std::make_shared<MacroActionAudio>();
|
||||
}
|
||||
|
||||
OBSWeakSource _audioSource;
|
||||
AudioAction _action = AudioAction::MUTE;
|
||||
int _volume = 0;
|
||||
|
||||
private:
|
||||
static bool _registered;
|
||||
static const int id;
|
||||
};
|
||||
|
||||
class MacroActionAudioEdit : public QWidget {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
MacroActionAudioEdit(
|
||||
QWidget *parent,
|
||||
std::shared_ptr<MacroActionAudio> entryData = nullptr);
|
||||
void UpdateEntryData();
|
||||
static QWidget *Create(QWidget *parent,
|
||||
std::shared_ptr<MacroAction> action)
|
||||
{
|
||||
return new MacroActionAudioEdit(
|
||||
parent,
|
||||
std::dynamic_pointer_cast<MacroActionAudio>(action));
|
||||
}
|
||||
|
||||
private slots:
|
||||
void SourceChanged(const QString &text);
|
||||
void ActionChanged(int value);
|
||||
void VolumeChanged(int value);
|
||||
|
||||
protected:
|
||||
QComboBox *_audioSources;
|
||||
QComboBox *_actions;
|
||||
QSpinBox *_volumePercent;
|
||||
std::shared_ptr<MacroActionAudio> _entryData;
|
||||
|
||||
private:
|
||||
QHBoxLayout *_mainLayout;
|
||||
bool _loading = true;
|
||||
};
|
||||
54
src/headers/macro-action-edit.hpp
Normal file
54
src/headers/macro-action-edit.hpp
Normal file
@@ -0,0 +1,54 @@
|
||||
#pragma once
|
||||
|
||||
#include <QWidget>
|
||||
#include <QComboBox>
|
||||
#include <QVBoxLayout>
|
||||
#include <QGroupBox>
|
||||
#include <deque>
|
||||
#include "macro.hpp"
|
||||
#include "section.hpp"
|
||||
|
||||
struct MacroActionInfo {
|
||||
using TCreateMethod = std::shared_ptr<MacroAction> (*)();
|
||||
using TCreateWidgetMethod = QWidget *(*)(QWidget *parent,
|
||||
std::shared_ptr<MacroAction>);
|
||||
TCreateMethod _createFunc;
|
||||
TCreateWidgetMethod _createWidgetFunc;
|
||||
std::string _name;
|
||||
};
|
||||
|
||||
class MacroActionFactory {
|
||||
public:
|
||||
MacroActionFactory() = delete;
|
||||
static bool Register(int id, MacroActionInfo);
|
||||
static std::shared_ptr<MacroAction> Create(const int id);
|
||||
static QWidget *CreateWidget(const int id, QWidget *parent,
|
||||
std::shared_ptr<MacroAction> action);
|
||||
static auto GetActionTypes() { return _methods; }
|
||||
|
||||
private:
|
||||
static std::map<int, MacroActionInfo> _methods;
|
||||
};
|
||||
|
||||
class MacroActionEdit : public QWidget {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
MacroActionEdit(QWidget *parent = nullptr,
|
||||
std::shared_ptr<MacroAction> * = nullptr, int type = 0,
|
||||
bool startCollapsed = false);
|
||||
void UpdateEntryData(int type);
|
||||
void Collapse(bool collapsed);
|
||||
|
||||
private slots:
|
||||
void ActionSelectionChanged(int idx);
|
||||
|
||||
protected:
|
||||
QComboBox *_actionSelection;
|
||||
Section *_section;
|
||||
|
||||
std::shared_ptr<MacroAction> *_entryData;
|
||||
|
||||
private:
|
||||
bool _loading = true;
|
||||
};
|
||||
59
src/headers/macro-action-recording.hpp
Normal file
59
src/headers/macro-action-recording.hpp
Normal file
@@ -0,0 +1,59 @@
|
||||
#pragma once
|
||||
#include <QDoubleSpinBox>
|
||||
#include <QLabel>
|
||||
#include "macro-action-edit.hpp"
|
||||
|
||||
enum class RecordAction {
|
||||
STOP,
|
||||
START,
|
||||
PAUSE,
|
||||
UNPAUSE,
|
||||
};
|
||||
|
||||
class MacroActionRecord : public MacroAction {
|
||||
public:
|
||||
bool PerformAction();
|
||||
void LogAction();
|
||||
bool Save(obs_data_t *obj);
|
||||
bool Load(obs_data_t *obj);
|
||||
int GetId() { return id; };
|
||||
static std::shared_ptr<MacroAction> Create()
|
||||
{
|
||||
return std::make_shared<MacroActionRecord>();
|
||||
}
|
||||
|
||||
RecordAction _action = RecordAction::STOP;
|
||||
|
||||
private:
|
||||
static bool _registered;
|
||||
static const int id;
|
||||
};
|
||||
|
||||
class MacroActionRecordEdit : public QWidget {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
MacroActionRecordEdit(
|
||||
QWidget *parent,
|
||||
std::shared_ptr<MacroActionRecord> entryData = nullptr);
|
||||
void UpdateEntryData();
|
||||
static QWidget *Create(QWidget *parent,
|
||||
std::shared_ptr<MacroAction> action)
|
||||
{
|
||||
return new MacroActionRecordEdit(
|
||||
parent,
|
||||
std::dynamic_pointer_cast<MacroActionRecord>(action));
|
||||
}
|
||||
|
||||
private slots:
|
||||
void ActionChanged(int value);
|
||||
|
||||
protected:
|
||||
QComboBox *_actions;
|
||||
QLabel *_pauseHint;
|
||||
std::shared_ptr<MacroActionRecord> _entryData;
|
||||
|
||||
private:
|
||||
QHBoxLayout *_mainLayout;
|
||||
bool _loading = true;
|
||||
};
|
||||
66
src/headers/macro-action-replay-buffer.hpp
Normal file
66
src/headers/macro-action-replay-buffer.hpp
Normal file
@@ -0,0 +1,66 @@
|
||||
#pragma once
|
||||
#include <QDoubleSpinBox>
|
||||
#include "macro-action-edit.hpp"
|
||||
#include "duration-control.hpp"
|
||||
|
||||
enum class ReplayBufferAction {
|
||||
STOP,
|
||||
START,
|
||||
SAVE,
|
||||
};
|
||||
|
||||
class MacroActionReplayBuffer : public MacroAction {
|
||||
public:
|
||||
bool PerformAction();
|
||||
void LogAction();
|
||||
bool Save(obs_data_t *obj);
|
||||
bool Load(obs_data_t *obj);
|
||||
int GetId() { return id; };
|
||||
static std::shared_ptr<MacroAction> Create()
|
||||
{
|
||||
return std::make_shared<MacroActionReplayBuffer>();
|
||||
}
|
||||
|
||||
ReplayBufferAction _action = ReplayBufferAction::STOP;
|
||||
|
||||
private:
|
||||
// Add artifical delay before trying to save the replay buffer again.
|
||||
//
|
||||
// Continiously calling obs_frontend_replay_buffer_save() does not
|
||||
// result in any output actually being written.
|
||||
// OBS_FRONTEND_EVENT_REPLAY_BUFFER_SAVED also seems to be sent before
|
||||
// any data is written.
|
||||
Duration _duration;
|
||||
|
||||
static bool _registered;
|
||||
static const int id;
|
||||
};
|
||||
|
||||
class MacroActionReplayBufferEdit : public QWidget {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
MacroActionReplayBufferEdit(
|
||||
QWidget *parent,
|
||||
std::shared_ptr<MacroActionReplayBuffer> entryData = nullptr);
|
||||
void UpdateEntryData();
|
||||
static QWidget *Create(QWidget *parent,
|
||||
std::shared_ptr<MacroAction> action)
|
||||
{
|
||||
return new MacroActionReplayBufferEdit(
|
||||
parent,
|
||||
std::dynamic_pointer_cast<MacroActionReplayBuffer>(
|
||||
action));
|
||||
}
|
||||
|
||||
private slots:
|
||||
void ActionChanged(int value);
|
||||
|
||||
protected:
|
||||
QComboBox *_actions;
|
||||
std::shared_ptr<MacroActionReplayBuffer> _entryData;
|
||||
|
||||
private:
|
||||
QHBoxLayout *_mainLayout;
|
||||
bool _loading = true;
|
||||
};
|
||||
52
src/headers/macro-action-run.hpp
Normal file
52
src/headers/macro-action-run.hpp
Normal file
@@ -0,0 +1,52 @@
|
||||
#pragma once
|
||||
#include "macro-action-edit.hpp"
|
||||
|
||||
#include <QLineEdit>
|
||||
#include <QPushButton>
|
||||
|
||||
class MacroActionRun : public MacroAction {
|
||||
public:
|
||||
bool PerformAction();
|
||||
void LogAction();
|
||||
bool Save(obs_data_t *obj);
|
||||
bool Load(obs_data_t *obj);
|
||||
int GetId() { return id; };
|
||||
static std::shared_ptr<MacroAction> Create()
|
||||
{
|
||||
return std::make_shared<MacroActionRun>();
|
||||
}
|
||||
|
||||
std::string _path = obs_module_text("AdvSceneSwitcher.enterPath");
|
||||
|
||||
private:
|
||||
static bool _registered;
|
||||
static const int id;
|
||||
};
|
||||
|
||||
class MacroActionRunEdit : public QWidget {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
MacroActionRunEdit(QWidget *parent,
|
||||
std::shared_ptr<MacroActionRun> entryData = nullptr);
|
||||
void UpdateEntryData();
|
||||
static QWidget *Create(QWidget *parent,
|
||||
std::shared_ptr<MacroAction> action)
|
||||
{
|
||||
return new MacroActionRunEdit(
|
||||
parent,
|
||||
std::dynamic_pointer_cast<MacroActionRun>(action));
|
||||
}
|
||||
|
||||
private slots:
|
||||
void FilePathChanged();
|
||||
void BrowseButtonClicked();
|
||||
|
||||
protected:
|
||||
std::shared_ptr<MacroActionRun> _entryData;
|
||||
|
||||
private:
|
||||
QLineEdit *_filePath;
|
||||
QPushButton *_browseButton;
|
||||
bool _loading = true;
|
||||
};
|
||||
60
src/headers/macro-action-streaming.hpp
Normal file
60
src/headers/macro-action-streaming.hpp
Normal file
@@ -0,0 +1,60 @@
|
||||
#pragma once
|
||||
#include <QDoubleSpinBox>
|
||||
#include "macro-action-edit.hpp"
|
||||
#include "duration-control.hpp"
|
||||
|
||||
enum class StreamAction {
|
||||
STOP,
|
||||
START,
|
||||
};
|
||||
|
||||
class MacroActionStream : public MacroAction {
|
||||
public:
|
||||
bool PerformAction();
|
||||
void LogAction();
|
||||
bool Save(obs_data_t *obj);
|
||||
bool Load(obs_data_t *obj);
|
||||
int GetId() { return id; };
|
||||
static std::shared_ptr<MacroAction> Create()
|
||||
{
|
||||
return std::make_shared<MacroActionStream>();
|
||||
}
|
||||
|
||||
StreamAction _action = StreamAction::STOP;
|
||||
|
||||
private:
|
||||
// Acts as a safeguard for misconfigured streaming setups leading to an
|
||||
// endless error spam.
|
||||
Duration _retryCooldown;
|
||||
|
||||
static bool _registered;
|
||||
static const int id;
|
||||
};
|
||||
|
||||
class MacroActionStreamEdit : public QWidget {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
MacroActionStreamEdit(
|
||||
QWidget *parent,
|
||||
std::shared_ptr<MacroActionStream> entryData = nullptr);
|
||||
void UpdateEntryData();
|
||||
static QWidget *Create(QWidget *parent,
|
||||
std::shared_ptr<MacroAction> action)
|
||||
{
|
||||
return new MacroActionStreamEdit(
|
||||
parent,
|
||||
std::dynamic_pointer_cast<MacroActionStream>(action));
|
||||
}
|
||||
|
||||
private slots:
|
||||
void ActionChanged(int value);
|
||||
|
||||
protected:
|
||||
QComboBox *_actions;
|
||||
std::shared_ptr<MacroActionStream> _entryData;
|
||||
|
||||
private:
|
||||
QHBoxLayout *_mainLayout;
|
||||
bool _loading = true;
|
||||
};
|
||||
49
src/headers/macro-action-switch-scene.hpp
Normal file
49
src/headers/macro-action-switch-scene.hpp
Normal file
@@ -0,0 +1,49 @@
|
||||
#pragma once
|
||||
#include "macro-action-edit.hpp"
|
||||
#include "switch-generic.hpp"
|
||||
#include "duration-control.hpp"
|
||||
|
||||
class MacroActionSwitchScene : public MacroAction, public SceneSwitcherEntry {
|
||||
public:
|
||||
bool PerformAction();
|
||||
void LogAction();
|
||||
bool Save(obs_data_t *obj);
|
||||
bool Load(obs_data_t *obj);
|
||||
int GetId() { return id; };
|
||||
|
||||
static std::shared_ptr<MacroAction> Create()
|
||||
{
|
||||
return std::make_shared<MacroActionSwitchScene>();
|
||||
}
|
||||
Duration _duration;
|
||||
|
||||
private:
|
||||
const char *getType() { return "MacroActionSwitchScene"; }
|
||||
|
||||
static bool _registered;
|
||||
static const int id;
|
||||
};
|
||||
|
||||
class MacroActionSwitchSceneEdit : public SwitchWidget {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
MacroActionSwitchSceneEdit(
|
||||
QWidget *parent,
|
||||
std::shared_ptr<MacroActionSwitchScene> entryData = nullptr);
|
||||
static QWidget *Create(QWidget *parent,
|
||||
std::shared_ptr<MacroAction> action)
|
||||
{
|
||||
return new MacroActionSwitchSceneEdit(
|
||||
parent,
|
||||
std::dynamic_pointer_cast<MacroActionSwitchScene>(
|
||||
action));
|
||||
}
|
||||
|
||||
private slots:
|
||||
void DurationChanged(double seconds);
|
||||
|
||||
protected:
|
||||
DurationSelection *_duration;
|
||||
std::shared_ptr<MacroActionSwitchScene> _entryData;
|
||||
};
|
||||
64
src/headers/macro-action-wait.hpp
Normal file
64
src/headers/macro-action-wait.hpp
Normal file
@@ -0,0 +1,64 @@
|
||||
#pragma once
|
||||
#include <QDoubleSpinBox>
|
||||
#include "macro-action-edit.hpp"
|
||||
#include "duration-control.hpp"
|
||||
|
||||
enum class WaitType {
|
||||
FIXED,
|
||||
RANDOM,
|
||||
};
|
||||
|
||||
class MacroActionWait : public MacroAction {
|
||||
public:
|
||||
bool PerformAction();
|
||||
bool Save(obs_data_t *obj);
|
||||
bool Load(obs_data_t *obj);
|
||||
int GetId() { return id; };
|
||||
static std::shared_ptr<MacroAction> Create()
|
||||
{
|
||||
return std::make_shared<MacroActionWait>();
|
||||
}
|
||||
Duration _duration;
|
||||
Duration _duration2;
|
||||
WaitType _waitType = WaitType::FIXED;
|
||||
|
||||
private:
|
||||
static bool _registered;
|
||||
static const int id;
|
||||
};
|
||||
|
||||
class MacroActionWaitEdit : public QWidget {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
MacroActionWaitEdit(
|
||||
QWidget *parent,
|
||||
std::shared_ptr<MacroActionWait> entryData = nullptr);
|
||||
void UpdateEntryData();
|
||||
void SetupFixedDurationEdit();
|
||||
void SetupRandomDurationEdit();
|
||||
static QWidget *Create(QWidget *parent,
|
||||
std::shared_ptr<MacroAction> action)
|
||||
{
|
||||
return new MacroActionWaitEdit(
|
||||
parent,
|
||||
std::dynamic_pointer_cast<MacroActionWait>(action));
|
||||
}
|
||||
|
||||
private slots:
|
||||
void DurationChanged(double value);
|
||||
void DurationUnitChanged(DurationUnit unit);
|
||||
void Duration2Changed(double value);
|
||||
void Duration2UnitChanged(DurationUnit unit);
|
||||
void TypeChanged(int value);
|
||||
|
||||
protected:
|
||||
DurationSelection *_duration;
|
||||
DurationSelection *_duration2;
|
||||
QComboBox *_waitType;
|
||||
std::shared_ptr<MacroActionWait> _entryData;
|
||||
|
||||
private:
|
||||
QHBoxLayout *_mainLayout;
|
||||
bool _loading = true;
|
||||
};
|
||||
78
src/headers/macro-condition-audio.hpp
Normal file
78
src/headers/macro-condition-audio.hpp
Normal file
@@ -0,0 +1,78 @@
|
||||
#pragma once
|
||||
#include "macro.hpp"
|
||||
#include "volume-control.hpp"
|
||||
#include <limits>
|
||||
#include <QWidget>
|
||||
#include <QComboBox>
|
||||
#include <chrono>
|
||||
|
||||
#include "duration-control.hpp"
|
||||
|
||||
enum class AudioCondition {
|
||||
ABOVE,
|
||||
BELOW,
|
||||
};
|
||||
|
||||
class MacroConditionAudio : public MacroCondition {
|
||||
public:
|
||||
~MacroConditionAudio();
|
||||
bool CheckCondition();
|
||||
bool Save(obs_data_t *obj);
|
||||
bool Load(obs_data_t *obj);
|
||||
int GetId() { return id; };
|
||||
static std::shared_ptr<MacroCondition> Create()
|
||||
{
|
||||
return std::make_shared<MacroConditionAudio>();
|
||||
}
|
||||
static void SetVolumeLevel(void *data,
|
||||
const float magnitude[MAX_AUDIO_CHANNELS],
|
||||
const float peak[MAX_AUDIO_CHANNELS],
|
||||
const float inputPeak[MAX_AUDIO_CHANNELS]);
|
||||
void ResetVolmeter();
|
||||
|
||||
OBSWeakSource _audioSource;
|
||||
int _volume = 0;
|
||||
AudioCondition _condition = AudioCondition::ABOVE;
|
||||
Duration _duration;
|
||||
obs_volmeter_t *_volmeter = nullptr;
|
||||
|
||||
private:
|
||||
float _peak = -std::numeric_limits<float>::infinity();
|
||||
static bool _registered;
|
||||
static const int id;
|
||||
};
|
||||
|
||||
class MacroConditionAudioEdit : public QWidget {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
MacroConditionAudioEdit(
|
||||
QWidget *parent,
|
||||
std::shared_ptr<MacroConditionAudio> cond = nullptr);
|
||||
void UpdateEntryData();
|
||||
static QWidget *Create(QWidget *parent,
|
||||
std::shared_ptr<MacroCondition> cond)
|
||||
{
|
||||
return new MacroConditionAudioEdit(
|
||||
parent,
|
||||
std::dynamic_pointer_cast<MacroConditionAudio>(cond));
|
||||
}
|
||||
void UpdateVolmeterSource();
|
||||
|
||||
private slots:
|
||||
void SourceChanged(const QString &text);
|
||||
void VolumeThresholdChanged(int vol);
|
||||
void ConditionChanged(int cond);
|
||||
void DurationChanged(double seconds);
|
||||
|
||||
protected:
|
||||
QComboBox *_audioSources;
|
||||
QComboBox *_condition;
|
||||
QSpinBox *_volume;
|
||||
DurationSelection *_duration;
|
||||
VolControl *_volMeter = nullptr;
|
||||
std::shared_ptr<MacroConditionAudio> _entryData;
|
||||
|
||||
private:
|
||||
bool _loading = true;
|
||||
};
|
||||
59
src/headers/macro-condition-edit.hpp
Normal file
59
src/headers/macro-condition-edit.hpp
Normal file
@@ -0,0 +1,59 @@
|
||||
#pragma once
|
||||
|
||||
#include "advanced-scene-switcher.hpp"
|
||||
#include "macro.hpp"
|
||||
#include "macro-condition-scene.hpp"
|
||||
#include "section.hpp"
|
||||
#include "utility.hpp"
|
||||
|
||||
#include <QGroupBox>
|
||||
|
||||
struct MacroConditionInfo {
|
||||
using TCreateMethod = std::shared_ptr<MacroCondition> (*)();
|
||||
using TCreateWidgetMethod =
|
||||
QWidget *(*)(QWidget *parent, std::shared_ptr<MacroCondition>);
|
||||
TCreateMethod _createFunc;
|
||||
TCreateWidgetMethod _createWidgetFunc;
|
||||
std::string _name;
|
||||
};
|
||||
|
||||
class MacroConditionFactory {
|
||||
public:
|
||||
MacroConditionFactory() = delete;
|
||||
static bool Register(int id, MacroConditionInfo);
|
||||
static std::shared_ptr<MacroCondition> Create(const int id);
|
||||
static QWidget *CreateWidget(const int id, QWidget *parent,
|
||||
std::shared_ptr<MacroCondition>);
|
||||
static auto GetConditionTypes() { return _methods; }
|
||||
|
||||
private:
|
||||
static std::map<int, MacroConditionInfo> _methods;
|
||||
};
|
||||
|
||||
class MacroConditionEdit : public QWidget {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
MacroConditionEdit(QWidget *parent = nullptr,
|
||||
std::shared_ptr<MacroCondition> * = nullptr,
|
||||
int type = 0, bool root = true,
|
||||
bool startCollapsed = false);
|
||||
bool IsRootNode();
|
||||
void UpdateEntryData(int type);
|
||||
void Collapse(bool collapsed);
|
||||
|
||||
private slots:
|
||||
void LogicSelectionChanged(int idx);
|
||||
void ConditionSelectionChanged(int idx);
|
||||
|
||||
protected:
|
||||
QComboBox *_logicSelection;
|
||||
QComboBox *_conditionSelection;
|
||||
Section *_section;
|
||||
|
||||
std::shared_ptr<MacroCondition> *_entryData;
|
||||
|
||||
private:
|
||||
bool _isRoot = true;
|
||||
bool _loading = true;
|
||||
};
|
||||
82
src/headers/macro-condition-file.hpp
Normal file
82
src/headers/macro-condition-file.hpp
Normal file
@@ -0,0 +1,82 @@
|
||||
#pragma once
|
||||
#include "macro.hpp"
|
||||
#include <QWidget>
|
||||
#include <QComboBox>
|
||||
#include <QDateTime>
|
||||
#include <QLineEdit>
|
||||
#include <QPushButton>
|
||||
#include <QPlainTextEdit>
|
||||
#include <QCheckBox>
|
||||
|
||||
enum class FileType {
|
||||
LOCAL,
|
||||
REMOTE,
|
||||
};
|
||||
|
||||
class MacroConditionFile : public MacroCondition {
|
||||
public:
|
||||
bool CheckCondition();
|
||||
bool Save(obs_data_t *obj);
|
||||
bool Load(obs_data_t *obj);
|
||||
int GetId() { return id; };
|
||||
static std::shared_ptr<MacroCondition> Create()
|
||||
{
|
||||
return std::make_shared<MacroConditionFile>();
|
||||
}
|
||||
|
||||
std::string _file = obs_module_text("AdvSceneSwitcher.enterPath");
|
||||
std::string _text = obs_module_text("AdvSceneSwitcher.enterText");
|
||||
FileType _fileType = FileType::LOCAL;
|
||||
bool _useRegex = false;
|
||||
bool _useTime = false;
|
||||
bool _onlyMatchIfChanged = false;
|
||||
|
||||
private:
|
||||
bool matchFileContent(QString &filedata);
|
||||
bool checkRemoteFileContent();
|
||||
bool checkLocalFileContent();
|
||||
|
||||
QDateTime _lastMod;
|
||||
size_t _lastHash = 0;
|
||||
static bool _registered;
|
||||
static const int id;
|
||||
};
|
||||
|
||||
class MacroConditionFileEdit : public QWidget {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
MacroConditionFileEdit(
|
||||
QWidget *parent,
|
||||
std::shared_ptr<MacroConditionFile> cond = nullptr);
|
||||
void UpdateEntryData();
|
||||
static QWidget *Create(QWidget *parent,
|
||||
std::shared_ptr<MacroCondition> cond)
|
||||
{
|
||||
return new MacroConditionFileEdit(
|
||||
parent,
|
||||
std::dynamic_pointer_cast<MacroConditionFile>(cond));
|
||||
}
|
||||
|
||||
private slots:
|
||||
void FileTypeChanged(int index);
|
||||
void FilePathChanged();
|
||||
void BrowseButtonClicked();
|
||||
void MatchTextChanged();
|
||||
void UseRegexChanged(int state);
|
||||
void CheckModificationDateChanged(int state);
|
||||
void OnlyMatchIfChangedChanged(int state);
|
||||
|
||||
protected:
|
||||
QComboBox *_fileType;
|
||||
QLineEdit *_filePath;
|
||||
QPushButton *_browseButton;
|
||||
QPlainTextEdit *_matchText;
|
||||
QCheckBox *_useRegex;
|
||||
QCheckBox *_checkModificationDate;
|
||||
QCheckBox *_checkFileContent;
|
||||
std::shared_ptr<MacroConditionFile> _entryData;
|
||||
|
||||
private:
|
||||
bool _loading = true;
|
||||
};
|
||||
51
src/headers/macro-condition-idle.hpp
Normal file
51
src/headers/macro-condition-idle.hpp
Normal file
@@ -0,0 +1,51 @@
|
||||
#pragma once
|
||||
#include "macro.hpp"
|
||||
#include <QWidget>
|
||||
#include <QComboBox>
|
||||
#include "duration-control.hpp"
|
||||
|
||||
class MacroConditionIdle : public MacroCondition {
|
||||
public:
|
||||
bool CheckCondition();
|
||||
bool Save(obs_data_t *obj);
|
||||
bool Load(obs_data_t *obj);
|
||||
int GetId() { return id; };
|
||||
static std::shared_ptr<MacroCondition> Create()
|
||||
{
|
||||
return std::make_shared<MacroConditionIdle>();
|
||||
}
|
||||
|
||||
Duration _duration;
|
||||
|
||||
private:
|
||||
static bool _registered;
|
||||
static const int id;
|
||||
};
|
||||
|
||||
class MacroConditionIdleEdit : public QWidget {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
MacroConditionIdleEdit(
|
||||
QWidget *parent,
|
||||
std::shared_ptr<MacroConditionIdle> cond = nullptr);
|
||||
void UpdateEntryData();
|
||||
static QWidget *Create(QWidget *parent,
|
||||
std::shared_ptr<MacroCondition> cond)
|
||||
{
|
||||
return new MacroConditionIdleEdit(
|
||||
parent,
|
||||
std::dynamic_pointer_cast<MacroConditionIdle>(cond));
|
||||
}
|
||||
|
||||
private slots:
|
||||
void DurationChanged(double seconds);
|
||||
void DurationUnitChanged(DurationUnit unit);
|
||||
|
||||
protected:
|
||||
DurationSelection *_duration;
|
||||
std::shared_ptr<MacroConditionIdle> _entryData;
|
||||
|
||||
private:
|
||||
bool _loading = true;
|
||||
};
|
||||
103
src/headers/macro-condition-media.hpp
Normal file
103
src/headers/macro-condition-media.hpp
Normal file
@@ -0,0 +1,103 @@
|
||||
#pragma once
|
||||
#include "macro.hpp"
|
||||
#include <limits>
|
||||
#include <QWidget>
|
||||
#include <QComboBox>
|
||||
|
||||
#include "duration-control.hpp"
|
||||
|
||||
enum class MediaTimeRestriction {
|
||||
TIME_RESTRICTION_NONE,
|
||||
TIME_RESTRICTION_SHORTER,
|
||||
TIME_RESTRICTION_LONGER,
|
||||
TIME_RESTRICTION_REMAINING_SHORTER,
|
||||
TIME_RESTRICTION_REMAINING_LONGER,
|
||||
};
|
||||
|
||||
constexpr auto custom_media_states_offset = 100;
|
||||
|
||||
enum class MediaState {
|
||||
// OBS's internal states
|
||||
OBS_MEDIA_STATE_NONE,
|
||||
OBS_MEDIA_STATE_PLAYING,
|
||||
OBS_MEDIA_STATE_OPENING,
|
||||
OBS_MEDIA_STATE_BUFFERING,
|
||||
OBS_MEDIA_STATE_PAUSED,
|
||||
OBS_MEDIA_STATE_STOPPED,
|
||||
OBS_MEDIA_STATE_ENDED,
|
||||
OBS_MEDIA_STATE_ERROR,
|
||||
// Just a marker
|
||||
LAST_OBS_MEDIA_STATE,
|
||||
// states added for use in the plugin
|
||||
PLAYED_TO_END = custom_media_states_offset,
|
||||
ANY,
|
||||
};
|
||||
|
||||
class MacroConditionMedia : public MacroCondition {
|
||||
public:
|
||||
~MacroConditionMedia();
|
||||
bool CheckCondition();
|
||||
bool Save(obs_data_t *obj);
|
||||
bool Load(obs_data_t *obj);
|
||||
int GetId() { return id; };
|
||||
static std::shared_ptr<MacroCondition> Create()
|
||||
{
|
||||
return std::make_shared<MacroConditionMedia>();
|
||||
}
|
||||
void ClearSignalHandler();
|
||||
void ResetSignalHandler();
|
||||
static void MediaStopped(void *data, calldata_t *);
|
||||
static void MediaEnded(void *data, calldata_t *);
|
||||
|
||||
OBSWeakSource _source = nullptr;
|
||||
MediaState _state = MediaState::OBS_MEDIA_STATE_NONE;
|
||||
MediaTimeRestriction _restriction =
|
||||
MediaTimeRestriction::TIME_RESTRICTION_NONE;
|
||||
Duration _time;
|
||||
|
||||
private:
|
||||
std::atomic_bool _stopped = {false};
|
||||
std::atomic_bool _ended = {false};
|
||||
// Trigger scene change only once even if media state might trigger repeatedly
|
||||
bool _alreadyMatched = false;
|
||||
// Workaround to enable use of "ended" to specify end of VLC playlist
|
||||
bool _previousStateEnded = false;
|
||||
bool _playedToEnd = false;
|
||||
|
||||
static bool _registered;
|
||||
static const int id;
|
||||
};
|
||||
|
||||
class MacroConditionMediaEdit : public QWidget {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
MacroConditionMediaEdit(
|
||||
QWidget *parent,
|
||||
std::shared_ptr<MacroConditionMedia> cond = nullptr);
|
||||
void UpdateEntryData();
|
||||
static QWidget *Create(QWidget *parent,
|
||||
std::shared_ptr<MacroCondition> cond)
|
||||
{
|
||||
return new MacroConditionMediaEdit(
|
||||
parent,
|
||||
std::dynamic_pointer_cast<MacroConditionMedia>(cond));
|
||||
}
|
||||
|
||||
private slots:
|
||||
void SourceChanged(const QString &text);
|
||||
void StateChanged(int index);
|
||||
void TimeRestrictionChanged(int index);
|
||||
void TimeChanged(double seconds);
|
||||
void TimeUnitChanged(DurationUnit unit);
|
||||
|
||||
protected:
|
||||
QComboBox *_mediaSources;
|
||||
QComboBox *_states;
|
||||
QComboBox *_timeRestrictions;
|
||||
DurationSelection *_time;
|
||||
std::shared_ptr<MacroConditionMedia> _entryData;
|
||||
|
||||
private:
|
||||
bool _loading = true;
|
||||
};
|
||||
54
src/headers/macro-condition-plugin-state.hpp
Normal file
54
src/headers/macro-condition-plugin-state.hpp
Normal file
@@ -0,0 +1,54 @@
|
||||
#pragma once
|
||||
#include "macro.hpp"
|
||||
#include <QWidget>
|
||||
#include <QComboBox>
|
||||
|
||||
enum class PluginStateCondition {
|
||||
SCENESWITCHED,
|
||||
};
|
||||
|
||||
class MacroConditionPluginState : public MacroCondition {
|
||||
public:
|
||||
bool CheckCondition();
|
||||
bool Save(obs_data_t *obj);
|
||||
bool Load(obs_data_t *obj);
|
||||
int GetId() { return id; };
|
||||
static std::shared_ptr<MacroCondition> Create()
|
||||
{
|
||||
return std::make_shared<MacroConditionPluginState>();
|
||||
}
|
||||
|
||||
PluginStateCondition _condition = PluginStateCondition::SCENESWITCHED;
|
||||
|
||||
private:
|
||||
static bool _registered;
|
||||
static const int id;
|
||||
};
|
||||
|
||||
class MacroConditionPluginStateEdit : public QWidget {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
MacroConditionPluginStateEdit(
|
||||
QWidget *parent,
|
||||
std::shared_ptr<MacroConditionPluginState> cond = nullptr);
|
||||
void UpdateEntryData();
|
||||
static QWidget *Create(QWidget *parent,
|
||||
std::shared_ptr<MacroCondition> cond)
|
||||
{
|
||||
return new MacroConditionPluginStateEdit(
|
||||
parent,
|
||||
std::dynamic_pointer_cast<MacroConditionPluginState>(
|
||||
cond));
|
||||
}
|
||||
|
||||
private slots:
|
||||
void ConditionChanged(int cond);
|
||||
|
||||
protected:
|
||||
QComboBox *_condition;
|
||||
std::shared_ptr<MacroConditionPluginState> _entryData;
|
||||
|
||||
private:
|
||||
bool _loading = true;
|
||||
};
|
||||
52
src/headers/macro-condition-process.hpp
Normal file
52
src/headers/macro-condition-process.hpp
Normal file
@@ -0,0 +1,52 @@
|
||||
#pragma once
|
||||
#include "macro.hpp"
|
||||
#include <QWidget>
|
||||
#include <QComboBox>
|
||||
|
||||
class MacroConditionProcess : public MacroCondition {
|
||||
public:
|
||||
bool CheckCondition();
|
||||
bool Save(obs_data_t *obj);
|
||||
bool Load(obs_data_t *obj);
|
||||
int GetId() { return id; };
|
||||
static std::shared_ptr<MacroCondition> Create()
|
||||
{
|
||||
return std::make_shared<MacroConditionProcess>();
|
||||
}
|
||||
|
||||
std::string _process;
|
||||
bool _focus = true;
|
||||
|
||||
private:
|
||||
static bool _registered;
|
||||
static const int id;
|
||||
};
|
||||
|
||||
class MacroConditionProcessEdit : public QWidget {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
MacroConditionProcessEdit(
|
||||
QWidget *parent,
|
||||
std::shared_ptr<MacroConditionProcess> cond = nullptr);
|
||||
void UpdateEntryData();
|
||||
static QWidget *Create(QWidget *parent,
|
||||
std::shared_ptr<MacroCondition> cond)
|
||||
{
|
||||
return new MacroConditionProcessEdit(
|
||||
parent,
|
||||
std::dynamic_pointer_cast<MacroConditionProcess>(cond));
|
||||
}
|
||||
|
||||
private slots:
|
||||
void ProcessChanged(const QString &text);
|
||||
void FocusChanged(int state);
|
||||
|
||||
protected:
|
||||
QComboBox *_processSelection;
|
||||
QCheckBox *_focused;
|
||||
std::shared_ptr<MacroConditionProcess> _entryData;
|
||||
|
||||
private:
|
||||
bool _loading = true;
|
||||
};
|
||||
60
src/headers/macro-condition-recording.hpp
Normal file
60
src/headers/macro-condition-recording.hpp
Normal file
@@ -0,0 +1,60 @@
|
||||
#pragma once
|
||||
#include "macro.hpp"
|
||||
#include <QWidget>
|
||||
#include <QComboBox>
|
||||
#include "duration-control.hpp"
|
||||
|
||||
enum class RecordState {
|
||||
STOP,
|
||||
PAUSE,
|
||||
START,
|
||||
};
|
||||
|
||||
class MacroConditionRecord : public MacroCondition {
|
||||
public:
|
||||
bool CheckCondition();
|
||||
bool Save(obs_data_t *obj);
|
||||
bool Load(obs_data_t *obj);
|
||||
int GetId() { return id; };
|
||||
static std::shared_ptr<MacroCondition> Create()
|
||||
{
|
||||
return std::make_shared<MacroConditionRecord>();
|
||||
}
|
||||
|
||||
RecordState _recordState;
|
||||
Duration _duration;
|
||||
|
||||
private:
|
||||
static bool _registered;
|
||||
static const int id;
|
||||
};
|
||||
|
||||
class MacroConditionRecordEdit : public QWidget {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
MacroConditionRecordEdit(
|
||||
QWidget *parent,
|
||||
std::shared_ptr<MacroConditionRecord> cond = nullptr);
|
||||
void UpdateEntryData();
|
||||
static QWidget *Create(QWidget *parent,
|
||||
std::shared_ptr<MacroCondition> cond)
|
||||
{
|
||||
return new MacroConditionRecordEdit(
|
||||
parent,
|
||||
std::dynamic_pointer_cast<MacroConditionRecord>(cond));
|
||||
}
|
||||
|
||||
private slots:
|
||||
void StateChanged(int value);
|
||||
void DurationChanged(double seconds);
|
||||
void DurationUnitChanged(DurationUnit unit);
|
||||
|
||||
protected:
|
||||
QComboBox *_recordState;
|
||||
DurationSelection *_duration;
|
||||
std::shared_ptr<MacroConditionRecord> _entryData;
|
||||
|
||||
private:
|
||||
bool _loading = true;
|
||||
};
|
||||
55
src/headers/macro-condition-region.hpp
Normal file
55
src/headers/macro-condition-region.hpp
Normal file
@@ -0,0 +1,55 @@
|
||||
#pragma once
|
||||
#include "macro.hpp"
|
||||
#include <QWidget>
|
||||
#include <QComboBox>
|
||||
|
||||
class MacroConditionRegion : public MacroCondition {
|
||||
public:
|
||||
bool CheckCondition();
|
||||
bool Save(obs_data_t *obj);
|
||||
bool Load(obs_data_t *obj);
|
||||
int GetId() { return id; };
|
||||
static std::shared_ptr<MacroCondition> Create()
|
||||
{
|
||||
return std::make_shared<MacroConditionRegion>();
|
||||
}
|
||||
|
||||
int _minX = 0, _minY = 0, _maxX = 0, _maxY = 0;
|
||||
|
||||
private:
|
||||
static bool _registered;
|
||||
static const int id;
|
||||
};
|
||||
|
||||
class MacroConditionRegionEdit : public QWidget {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
MacroConditionRegionEdit(
|
||||
QWidget *parent,
|
||||
std::shared_ptr<MacroConditionRegion> cond = nullptr);
|
||||
void UpdateEntryData();
|
||||
static QWidget *Create(QWidget *parent,
|
||||
std::shared_ptr<MacroCondition> cond)
|
||||
{
|
||||
return new MacroConditionRegionEdit(
|
||||
parent,
|
||||
std::dynamic_pointer_cast<MacroConditionRegion>(cond));
|
||||
}
|
||||
|
||||
private slots:
|
||||
void MinXChanged(int pos);
|
||||
void MinYChanged(int pos);
|
||||
void MaxXChanged(int pos);
|
||||
void MaxYChanged(int pos);
|
||||
|
||||
protected:
|
||||
QSpinBox *_minX;
|
||||
QSpinBox *_minY;
|
||||
QSpinBox *_maxX;
|
||||
QSpinBox *_maxY;
|
||||
std::shared_ptr<MacroConditionRegion> _entryData;
|
||||
|
||||
private:
|
||||
bool _loading = true;
|
||||
};
|
||||
62
src/headers/macro-condition-scene.hpp
Normal file
62
src/headers/macro-condition-scene.hpp
Normal file
@@ -0,0 +1,62 @@
|
||||
#pragma once
|
||||
#include "macro.hpp"
|
||||
#include <QWidget>
|
||||
#include <QComboBox>
|
||||
#include "duration-control.hpp"
|
||||
|
||||
enum class SceneType {
|
||||
CURRENT,
|
||||
PREVIOUS,
|
||||
};
|
||||
|
||||
class MacroConditionScene : public MacroCondition {
|
||||
public:
|
||||
bool CheckCondition();
|
||||
bool Save(obs_data_t *obj);
|
||||
bool Load(obs_data_t *obj);
|
||||
int GetId() { return id; };
|
||||
static std::shared_ptr<MacroCondition> Create()
|
||||
{
|
||||
return std::make_shared<MacroConditionScene>();
|
||||
}
|
||||
|
||||
OBSWeakSource _scene;
|
||||
SceneType _type;
|
||||
Duration _duration;
|
||||
|
||||
private:
|
||||
static bool _registered;
|
||||
static const int id;
|
||||
};
|
||||
|
||||
class MacroConditionSceneEdit : public QWidget {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
MacroConditionSceneEdit(
|
||||
QWidget *parent,
|
||||
std::shared_ptr<MacroConditionScene> cond = nullptr);
|
||||
void UpdateEntryData();
|
||||
static QWidget *Create(QWidget *parent,
|
||||
std::shared_ptr<MacroCondition> cond)
|
||||
{
|
||||
return new MacroConditionSceneEdit(
|
||||
parent,
|
||||
std::dynamic_pointer_cast<MacroConditionScene>(cond));
|
||||
}
|
||||
|
||||
private slots:
|
||||
void SceneChanged(const QString &text);
|
||||
void TypeChanged(int value);
|
||||
void DurationChanged(double seconds);
|
||||
void DurationUnitChanged(DurationUnit unit);
|
||||
|
||||
protected:
|
||||
QComboBox *_sceneSelection;
|
||||
QComboBox *_sceneType;
|
||||
DurationSelection *_duration;
|
||||
std::shared_ptr<MacroConditionScene> _entryData;
|
||||
|
||||
private:
|
||||
bool _loading = true;
|
||||
};
|
||||
59
src/headers/macro-condition-streaming.hpp
Normal file
59
src/headers/macro-condition-streaming.hpp
Normal file
@@ -0,0 +1,59 @@
|
||||
#pragma once
|
||||
#include "macro.hpp"
|
||||
#include <QWidget>
|
||||
#include <QComboBox>
|
||||
#include "duration-control.hpp"
|
||||
|
||||
enum class StreamState {
|
||||
STOP,
|
||||
START,
|
||||
};
|
||||
|
||||
class MacroConditionStream : public MacroCondition {
|
||||
public:
|
||||
bool CheckCondition();
|
||||
bool Save(obs_data_t *obj);
|
||||
bool Load(obs_data_t *obj);
|
||||
int GetId() { return id; };
|
||||
static std::shared_ptr<MacroCondition> Create()
|
||||
{
|
||||
return std::make_shared<MacroConditionStream>();
|
||||
}
|
||||
|
||||
StreamState _streamState;
|
||||
Duration _duration;
|
||||
|
||||
private:
|
||||
static bool _registered;
|
||||
static const int id;
|
||||
};
|
||||
|
||||
class MacroConditionStreamEdit : public QWidget {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
MacroConditionStreamEdit(
|
||||
QWidget *parent,
|
||||
std::shared_ptr<MacroConditionStream> cond = nullptr);
|
||||
void UpdateEntryData();
|
||||
static QWidget *Create(QWidget *parent,
|
||||
std::shared_ptr<MacroCondition> cond)
|
||||
{
|
||||
return new MacroConditionStreamEdit(
|
||||
parent,
|
||||
std::dynamic_pointer_cast<MacroConditionStream>(cond));
|
||||
}
|
||||
|
||||
private slots:
|
||||
void StateChanged(int value);
|
||||
void DurationChanged(double seconds);
|
||||
void DurationUnitChanged(DurationUnit unit);
|
||||
|
||||
protected:
|
||||
QComboBox *_streamState;
|
||||
DurationSelection *_duration;
|
||||
std::shared_ptr<MacroConditionStream> _entryData;
|
||||
|
||||
private:
|
||||
bool _loading = true;
|
||||
};
|
||||
82
src/headers/macro-condition-video.hpp
Normal file
82
src/headers/macro-condition-video.hpp
Normal file
@@ -0,0 +1,82 @@
|
||||
#pragma once
|
||||
#include "macro.hpp"
|
||||
#include <QWidget>
|
||||
#include <QComboBox>
|
||||
#include <chrono>
|
||||
#include "duration-control.hpp"
|
||||
#include "screenshot-helper.hpp"
|
||||
|
||||
enum class VideoCondition {
|
||||
MATCH,
|
||||
DIFFER,
|
||||
HAS_NOT_CHANGED,
|
||||
HAS_CHANGED,
|
||||
NO_IMAGE,
|
||||
};
|
||||
|
||||
class MacroConditionVideo : public MacroCondition {
|
||||
public:
|
||||
bool CheckCondition();
|
||||
bool Save(obs_data_t *obj);
|
||||
bool Load(obs_data_t *obj);
|
||||
int GetId() { return id; };
|
||||
QImage GetMatchImage() { return _matchImage; };
|
||||
static std::shared_ptr<MacroCondition> Create()
|
||||
{
|
||||
return std::make_shared<MacroConditionVideo>();
|
||||
}
|
||||
void GetScreenshot();
|
||||
bool LoadImageFromFile();
|
||||
|
||||
OBSWeakSource _videoSource;
|
||||
VideoCondition _condition = VideoCondition::MATCH;
|
||||
Duration _duration;
|
||||
std::string _file = obs_module_text("AdvSceneSwitcher.enterPath");
|
||||
|
||||
private:
|
||||
bool Compare();
|
||||
|
||||
std::unique_ptr<AdvSSScreenshotObj> _screenshotData = nullptr;
|
||||
QImage _matchImage;
|
||||
static bool _registered;
|
||||
static const int id;
|
||||
};
|
||||
|
||||
class MacroConditionVideoEdit : public QWidget {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
MacroConditionVideoEdit(
|
||||
QWidget *parent,
|
||||
std::shared_ptr<MacroConditionVideo> cond = nullptr);
|
||||
void UpdateEntryData();
|
||||
static QWidget *Create(QWidget *parent,
|
||||
std::shared_ptr<MacroCondition> cond)
|
||||
{
|
||||
return new MacroConditionVideoEdit(
|
||||
parent,
|
||||
std::dynamic_pointer_cast<MacroConditionVideo>(cond));
|
||||
}
|
||||
|
||||
void UpdatePreviewTooltip();
|
||||
void SetFilePath(const QString &text);
|
||||
|
||||
private slots:
|
||||
void SourceChanged(const QString &text);
|
||||
void ConditionChanged(int cond);
|
||||
void FilePathChanged();
|
||||
void BrowseButtonClicked();
|
||||
void DurationChanged(double seconds);
|
||||
void DurationUnitChanged(DurationUnit unit);
|
||||
|
||||
protected:
|
||||
QComboBox *_videoSelection;
|
||||
DurationSelection *_duration;
|
||||
QComboBox *_condition;
|
||||
QLineEdit *_filePath;
|
||||
QPushButton *_browseButton;
|
||||
std::shared_ptr<MacroConditionVideo> _entryData;
|
||||
|
||||
private:
|
||||
bool _loading = true;
|
||||
};
|
||||
64
src/headers/macro-condition-window.hpp
Normal file
64
src/headers/macro-condition-window.hpp
Normal file
@@ -0,0 +1,64 @@
|
||||
#pragma once
|
||||
#include "macro.hpp"
|
||||
#include <QWidget>
|
||||
#include <QComboBox>
|
||||
|
||||
class MacroConditionWindow : public MacroCondition {
|
||||
public:
|
||||
bool CheckCondition();
|
||||
bool Save(obs_data_t *obj);
|
||||
bool Load(obs_data_t *obj);
|
||||
int GetId() { return id; };
|
||||
static std::shared_ptr<MacroCondition> Create()
|
||||
{
|
||||
return std::make_shared<MacroConditionWindow>();
|
||||
}
|
||||
|
||||
private:
|
||||
bool CheckWindowTitleSwitchDirect(std::string ¤tWindowTitle);
|
||||
bool CheckWindowTitleSwitchRegex(std::string ¤tWindowTitle,
|
||||
std::vector<std::string> &windowList);
|
||||
|
||||
public:
|
||||
std::string _window;
|
||||
bool _fullscreen = false;
|
||||
bool _maximized = false;
|
||||
bool _focus = true;
|
||||
|
||||
private:
|
||||
static bool _registered;
|
||||
static const int id;
|
||||
};
|
||||
|
||||
class MacroConditionWindowEdit : public QWidget {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
MacroConditionWindowEdit(
|
||||
QWidget *parent,
|
||||
std::shared_ptr<MacroConditionWindow> cond = nullptr);
|
||||
void UpdateEntryData();
|
||||
static QWidget *Create(QWidget *parent,
|
||||
std::shared_ptr<MacroCondition> cond)
|
||||
{
|
||||
return new MacroConditionWindowEdit(
|
||||
parent,
|
||||
std::dynamic_pointer_cast<MacroConditionWindow>(cond));
|
||||
}
|
||||
|
||||
private slots:
|
||||
void WindowChanged(const QString &text);
|
||||
void FullscreenChanged(int state);
|
||||
void MaximizedChanged(int state);
|
||||
void FocusChanged(int state);
|
||||
|
||||
protected:
|
||||
QComboBox *_windowSelection;
|
||||
QCheckBox *_fullscreen;
|
||||
QCheckBox *_maximized;
|
||||
QCheckBox *_focused;
|
||||
std::shared_ptr<MacroConditionWindow> _entryData;
|
||||
|
||||
private:
|
||||
bool _loading = true;
|
||||
};
|
||||
88
src/headers/macro.hpp
Normal file
88
src/headers/macro.hpp
Normal file
@@ -0,0 +1,88 @@
|
||||
#pragma once
|
||||
#include <string>
|
||||
#include <deque>
|
||||
#include <memory>
|
||||
#include <map>
|
||||
#include <obs.hpp>
|
||||
#include <obs-module.h>
|
||||
|
||||
constexpr auto macro_func = 10;
|
||||
constexpr auto default_priority_10 = macro_func;
|
||||
|
||||
constexpr auto logic_root_offset = 100;
|
||||
|
||||
enum class LogicType {
|
||||
ROOT_NONE = 0,
|
||||
ROOT_NOT,
|
||||
ROOT_LAST,
|
||||
// leave some space for potential expansion
|
||||
NONE = 100,
|
||||
AND,
|
||||
OR,
|
||||
AND_NOT,
|
||||
OR_NOT,
|
||||
LAST,
|
||||
};
|
||||
|
||||
static inline bool isRootLogicType(LogicType l)
|
||||
{
|
||||
return static_cast<int>(l) < logic_root_offset;
|
||||
}
|
||||
|
||||
struct LogicTypeInfo {
|
||||
std::string _name;
|
||||
};
|
||||
|
||||
class MacroCondition {
|
||||
public:
|
||||
virtual bool CheckCondition() = 0;
|
||||
virtual bool Save(obs_data_t *obj) = 0;
|
||||
virtual bool Load(obs_data_t *obj) = 0;
|
||||
virtual int GetId() = 0;
|
||||
LogicType GetLogicType() { return _logic; }
|
||||
void SetLogicType(LogicType logic) { _logic = logic; }
|
||||
|
||||
static const std::map<LogicType, LogicTypeInfo> logicTypes;
|
||||
|
||||
private:
|
||||
LogicType _logic;
|
||||
};
|
||||
|
||||
class MacroAction {
|
||||
public:
|
||||
virtual bool PerformAction() = 0;
|
||||
virtual bool Save(obs_data_t *obj) = 0;
|
||||
virtual bool Load(obs_data_t *obj) = 0;
|
||||
virtual int GetId() = 0;
|
||||
virtual void LogAction();
|
||||
};
|
||||
|
||||
class Macro {
|
||||
|
||||
public:
|
||||
Macro(std::string name = "");
|
||||
virtual ~Macro();
|
||||
|
||||
bool CeckMatch();
|
||||
bool PerformAction();
|
||||
bool Matched() { return _matched; }
|
||||
std::string Name() { return _name; }
|
||||
void SetName(std::string name) { _name = name; }
|
||||
std::deque<std::shared_ptr<MacroCondition>> &Conditions()
|
||||
{
|
||||
return _conditions;
|
||||
}
|
||||
std::deque<std::shared_ptr<MacroAction>> &Actions() { return _actions; }
|
||||
|
||||
bool Save(obs_data_t *obj);
|
||||
bool Load(obs_data_t *obj);
|
||||
|
||||
// Helper function for plugin state condition regarding scene change
|
||||
bool SwitchesScene();
|
||||
|
||||
private:
|
||||
std::string _name = "";
|
||||
std::deque<std::shared_ptr<MacroCondition>> _conditions;
|
||||
std::deque<std::shared_ptr<MacroAction>> _actions;
|
||||
bool _matched = false;
|
||||
};
|
||||
24
src/headers/name-dialog.hpp
Normal file
24
src/headers/name-dialog.hpp
Normal file
@@ -0,0 +1,24 @@
|
||||
#pragma once
|
||||
|
||||
#include <QDialog>
|
||||
#include <QLabel>
|
||||
#include <QLineEdit>
|
||||
|
||||
// Based on OBS's NameDialog
|
||||
class AdvSSNameDialog : public QDialog {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
AdvSSNameDialog(QWidget *parent);
|
||||
|
||||
// Returns true if user clicks OK, false otherwise
|
||||
// userTextInput returns string that user typed into dialog
|
||||
static bool AskForName(QWidget *parent, const QString &title,
|
||||
const QString &text, std::string &userTextInput,
|
||||
const QString &placeHolder = QString(""),
|
||||
int maxSize = 170);
|
||||
|
||||
private:
|
||||
QLabel *label;
|
||||
QLineEdit *userText;
|
||||
};
|
||||
@@ -54,11 +54,6 @@ struct SceneGroup {
|
||||
}
|
||||
};
|
||||
|
||||
enum class SwitchTargetType {
|
||||
Scene,
|
||||
SceneGroup,
|
||||
};
|
||||
|
||||
class SceneGroupEditWidget : public QWidget {
|
||||
Q_OBJECT
|
||||
|
||||
@@ -87,22 +82,3 @@ private:
|
||||
|
||||
SceneGroup *sceneGroup = nullptr;
|
||||
};
|
||||
|
||||
// Based on OBS's NameDialog
|
||||
class SGNameDialog : public QDialog {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
SGNameDialog(QWidget *parent);
|
||||
|
||||
// Returns true if user clicks OK, false otherwise
|
||||
// userTextInput returns string that user typed into dialog
|
||||
static bool AskForName(QWidget *parent, const QString &title,
|
||||
const QString &text, std::string &userTextInput,
|
||||
const QString &placeHolder = QString(""),
|
||||
int maxSize = 170);
|
||||
|
||||
private:
|
||||
QLabel *label;
|
||||
QLineEdit *userText;
|
||||
};
|
||||
|
||||
29
src/headers/screenshot-helper.hpp
Normal file
29
src/headers/screenshot-helper.hpp
Normal file
@@ -0,0 +1,29 @@
|
||||
#pragma once
|
||||
#include <obs.hpp>
|
||||
#include <string>
|
||||
#include <QImage>
|
||||
#include <chrono>
|
||||
|
||||
class AdvSSScreenshotObj {
|
||||
public:
|
||||
AdvSSScreenshotObj(obs_source_t *source);
|
||||
~AdvSSScreenshotObj();
|
||||
|
||||
void Screenshot();
|
||||
void Download();
|
||||
void Copy();
|
||||
void MarkDone();
|
||||
|
||||
gs_texrender_t *texrender = nullptr;
|
||||
gs_stagesurf_t *stagesurf = nullptr;
|
||||
OBSWeakSource weakSource;
|
||||
std::string path;
|
||||
QImage image;
|
||||
uint32_t cx = 0;
|
||||
uint32_t cy = 0;
|
||||
|
||||
int stage = 0;
|
||||
|
||||
bool done = false;
|
||||
std::chrono::high_resolution_clock::time_point time;
|
||||
};
|
||||
31
src/headers/section.hpp
Normal file
31
src/headers/section.hpp
Normal file
@@ -0,0 +1,31 @@
|
||||
#pragma once
|
||||
|
||||
#include <QFrame>
|
||||
#include <QGridLayout>
|
||||
#include <QParallelAnimationGroup>
|
||||
#include <QScrollArea>
|
||||
#include <QToolButton>
|
||||
#include <QWidget>
|
||||
|
||||
class Section : public QWidget {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit Section(const int animationDuration = 300,
|
||||
QWidget *parent = 0);
|
||||
|
||||
void SetContent(QWidget *w);
|
||||
void AddHeaderWidget(QWidget *);
|
||||
|
||||
public slots:
|
||||
void Collapse(bool collapsed);
|
||||
|
||||
private:
|
||||
QGridLayout *_mainLayout;
|
||||
QHBoxLayout *_headerWidgetLayout;
|
||||
QToolButton *_toggleButton;
|
||||
QFrame *_headerLine;
|
||||
QParallelAnimationGroup *_toggleAnimation = nullptr;
|
||||
QScrollArea *_contentArea = nullptr;
|
||||
int _animationDuration;
|
||||
};
|
||||
@@ -3,6 +3,7 @@
|
||||
#include <limits>
|
||||
|
||||
#include "switch-generic.hpp"
|
||||
#include "duration-control.hpp"
|
||||
#include "volume-control.hpp"
|
||||
|
||||
constexpr auto audio_func = 8;
|
||||
@@ -18,9 +19,8 @@ struct AudioSwitch : virtual SceneSwitcherEntry {
|
||||
OBSWeakSource audioSource = nullptr;
|
||||
int volumeThreshold = 0;
|
||||
audioCondition condition = ABOVE;
|
||||
double duration = 0;
|
||||
Duration duration;
|
||||
bool ignoreInactiveSource = true;
|
||||
unsigned int matchCount = 0;
|
||||
float peak = -std::numeric_limits<float>::infinity();
|
||||
obs_volmeter_t *volmeter = nullptr;
|
||||
|
||||
@@ -50,8 +50,7 @@ struct AudioSwitchFallback : virtual SceneSwitcherEntry {
|
||||
void load(obs_data_t *obj);
|
||||
|
||||
bool enable = false;
|
||||
double duration = 0;
|
||||
unsigned int matchCount = 0;
|
||||
Duration duration;
|
||||
};
|
||||
|
||||
class AudioSwitchWidget : public SwitchWidget {
|
||||
@@ -77,7 +76,7 @@ private:
|
||||
QComboBox *audioSources;
|
||||
QComboBox *condition;
|
||||
QSpinBox *audioVolumeThreshold;
|
||||
QDoubleSpinBox *duration;
|
||||
DurationSelection *duration;
|
||||
QCheckBox *ignoreInactiveSource;
|
||||
VolControl *volMeter;
|
||||
|
||||
@@ -94,7 +93,7 @@ private slots:
|
||||
void DurationChanged(double dur);
|
||||
|
||||
private:
|
||||
QDoubleSpinBox *duration;
|
||||
DurationSelection *duration;
|
||||
|
||||
AudioSwitchFallback *switchData;
|
||||
};
|
||||
|
||||
@@ -2,6 +2,13 @@
|
||||
#include <obs.hpp>
|
||||
#include <QComboBox>
|
||||
|
||||
#include "scene-group.hpp"
|
||||
|
||||
enum class SwitchTargetType {
|
||||
Scene,
|
||||
SceneGroup,
|
||||
};
|
||||
|
||||
struct SceneSwitcherEntry {
|
||||
SwitchTargetType targetType = SwitchTargetType::Scene;
|
||||
SceneGroup *group = nullptr;
|
||||
|
||||
@@ -22,6 +22,8 @@ using websocketpp::connection_hdl;
|
||||
typedef websocketpp::server<websocketpp::config::asio> server;
|
||||
typedef websocketpp::client<websocketpp::config::asio_client> client;
|
||||
|
||||
struct sceneSwitchInfo;
|
||||
|
||||
class NetworkConfig {
|
||||
public:
|
||||
NetworkConfig();
|
||||
@@ -51,7 +53,7 @@ public:
|
||||
virtual ~WSServer();
|
||||
void start(quint16 port, bool lockToIPv4);
|
||||
void stop();
|
||||
void sendMessage(OBSWeakSource scene, OBSWeakSource transition);
|
||||
void sendMessage(sceneSwitchInfo sceneSwitch);
|
||||
QThreadPool *threadPool() { return &_threadPool; }
|
||||
|
||||
private:
|
||||
|
||||
@@ -2,24 +2,17 @@
|
||||
#include <QVBoxLayout>
|
||||
|
||||
#include "switch-generic.hpp"
|
||||
#include "duration-control.hpp"
|
||||
|
||||
constexpr auto round_trip_func = 1;
|
||||
constexpr auto default_priority_1 = round_trip_func;
|
||||
|
||||
typedef enum {
|
||||
SECONDS,
|
||||
MINUTES,
|
||||
HOURS,
|
||||
} delay_units;
|
||||
|
||||
struct SceneSequenceSwitch : SceneSwitcherEntry {
|
||||
static bool pause;
|
||||
SwitchTargetType startTargetType = SwitchTargetType::Scene;
|
||||
OBSWeakSource startScene = nullptr;
|
||||
double delay = 0;
|
||||
int delayMultiplier = 1;
|
||||
Duration delay;
|
||||
bool interruptible = false;
|
||||
unsigned int matchCount = 0;
|
||||
|
||||
// nullptr marks start point and reaching end of extended sequence
|
||||
SceneSequenceSwitch *activeSequence = nullptr;
|
||||
@@ -57,22 +50,20 @@ public:
|
||||
|
||||
static void swapSwitchData(SequenceWidget *s1, SequenceWidget *s2);
|
||||
|
||||
void UpdateDelay();
|
||||
void UpdateWidgetStatus(bool showExtendText);
|
||||
void setExtendedSequenceStartScene();
|
||||
|
||||
private slots:
|
||||
void SceneChanged(const QString &text);
|
||||
void DelayChanged(double delay);
|
||||
void DelayUnitsChanged(int idx);
|
||||
void DelayUnitsChanged(DurationUnit);
|
||||
void StartSceneChanged(const QString &text);
|
||||
void InterruptibleChanged(int state);
|
||||
void ExtendClicked();
|
||||
void ReduceClicked();
|
||||
|
||||
protected:
|
||||
QDoubleSpinBox *delay;
|
||||
QComboBox *delayUnits;
|
||||
DurationSelection *delay;
|
||||
QComboBox *startScenes;
|
||||
QCheckBox *interruptible;
|
||||
QVBoxLayout *extendSequenceLayout;
|
||||
|
||||
@@ -1,17 +1,24 @@
|
||||
#pragma once
|
||||
#include <QDoubleSpinBox>
|
||||
|
||||
#include "switch-generic.hpp"
|
||||
|
||||
struct SceneTransition : SceneSwitcherEntry {
|
||||
OBSWeakSource scene2 = nullptr;
|
||||
double duration = 0.3;
|
||||
|
||||
const char *getType() { return "transition"; }
|
||||
bool initialized();
|
||||
bool valid();
|
||||
void save(obs_data_t *obj);
|
||||
void load(obs_data_t *obj);
|
||||
|
||||
inline SceneTransition(){};
|
||||
inline SceneTransition(OBSWeakSource scene_, OBSWeakSource scene2_,
|
||||
OBSWeakSource transition_)
|
||||
: SceneSwitcherEntry(scene_, transition_), scene2(scene2_)
|
||||
OBSWeakSource transition_, double duration_)
|
||||
: SceneSwitcherEntry(scene_, transition_),
|
||||
scene2(scene2_),
|
||||
duration(duration_)
|
||||
{
|
||||
}
|
||||
};
|
||||
@@ -21,6 +28,8 @@ struct DefaultSceneTransition : SceneSwitcherEntry {
|
||||
static unsigned int delay;
|
||||
|
||||
const char *getType() { return "def_transition"; }
|
||||
void save(obs_data_t *obj);
|
||||
void load(obs_data_t *obj);
|
||||
bool checkMatch(OBSWeakSource currentScene);
|
||||
void setTransition();
|
||||
|
||||
@@ -45,9 +54,11 @@ public:
|
||||
|
||||
private slots:
|
||||
void Scene2Changed(const QString &text);
|
||||
void DurationChanged(double dur);
|
||||
|
||||
private:
|
||||
QComboBox *scenes2;
|
||||
QDoubleSpinBox *duration;
|
||||
|
||||
SceneTransition *switchData;
|
||||
};
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#include <QSpinBox>
|
||||
|
||||
#include "switch-generic.hpp"
|
||||
#include "screenshot-helper.hpp"
|
||||
|
||||
constexpr auto video_func = 9;
|
||||
constexpr auto default_priority_9 = video_func;
|
||||
@@ -10,30 +11,7 @@ enum class videoSwitchType {
|
||||
MATCH,
|
||||
DIFFER,
|
||||
HAS_NOT_CHANGED,
|
||||
};
|
||||
|
||||
class AdvSSScreenshotObj {
|
||||
public:
|
||||
AdvSSScreenshotObj(obs_source_t *source);
|
||||
~AdvSSScreenshotObj();
|
||||
|
||||
void Screenshot();
|
||||
void Download();
|
||||
void Copy();
|
||||
void MarkDone();
|
||||
|
||||
gs_texrender_t *texrender = nullptr;
|
||||
gs_stagesurf_t *stagesurf = nullptr;
|
||||
OBSWeakSource weakSource;
|
||||
std::string path;
|
||||
QImage image;
|
||||
uint32_t cx = 0;
|
||||
uint32_t cy = 0;
|
||||
|
||||
int stage = 0;
|
||||
|
||||
bool done = false;
|
||||
std::chrono::high_resolution_clock::time_point time;
|
||||
HAS_CHANGED,
|
||||
};
|
||||
|
||||
struct VideoSwitch : virtual SceneSwitcherEntry {
|
||||
|
||||
@@ -24,9 +24,13 @@
|
||||
#include "switch-video.hpp"
|
||||
#include "switch-network.hpp"
|
||||
|
||||
#include "macro.hpp"
|
||||
#include "duration-control.hpp"
|
||||
|
||||
constexpr auto default_interval = 300;
|
||||
constexpr auto previous_scene_name = "Previous Scene";
|
||||
constexpr auto current_transition_name = "Current Transition";
|
||||
constexpr auto tab_count = 18;
|
||||
|
||||
typedef enum { NO_SWITCH = 0, SWITCH = 1, RANDOM_SWITCH = 2 } NoMatch;
|
||||
typedef enum { PERSIST = 0, START = 1, STOP = 2 } StartupBehavior;
|
||||
@@ -38,6 +42,13 @@ enum class AutoStartEvent {
|
||||
RECORINDG_OR_STREAMING,
|
||||
};
|
||||
|
||||
typedef struct sceneSwitchInfo {
|
||||
OBSWeakSource scene;
|
||||
OBSWeakSource transition;
|
||||
int duration = 0;
|
||||
|
||||
} sceneSwitchInfo;
|
||||
|
||||
typedef struct transitionData {
|
||||
std::string name = "";
|
||||
int duration = 0;
|
||||
@@ -61,7 +72,7 @@ struct SwitcherData {
|
||||
bool verbose = false;
|
||||
bool disableHints = false;
|
||||
bool showFrame = false;
|
||||
bool tansitionOverrideOverride = false;
|
||||
bool transitionOverrideOverride = false;
|
||||
bool adjustActiveTransitionType = true;
|
||||
|
||||
int interval = default_interval;
|
||||
@@ -73,14 +84,18 @@ struct SwitcherData {
|
||||
SceneGroup *lastRandomSceneGroup;
|
||||
OBSWeakSource nonMatchingScene;
|
||||
NoMatch switchIfNotMatching = NO_SWITCH;
|
||||
double noMatchDelay = 0;
|
||||
double noMatchCount = 0;
|
||||
Duration noMatchDelay;
|
||||
StartupBehavior startupBehavior = PERSIST;
|
||||
AutoStartEvent autoStartEvent = AutoStartEvent::NEVER;
|
||||
|
||||
double cooldown = 0.;
|
||||
Duration cooldown;
|
||||
std::chrono::high_resolution_clock::time_point lastMatchTime;
|
||||
|
||||
std::deque<Macro> macros;
|
||||
// Allow only one macro scene change per scene switcher interval
|
||||
// with the top macro having the highest priority
|
||||
bool macroSceneSwitched = false;
|
||||
|
||||
std::deque<WindowSwitch> windowSwitches;
|
||||
std::vector<std::string> ignoreIdleWindows;
|
||||
std::string lastTitle;
|
||||
@@ -131,7 +146,7 @@ struct SwitcherData {
|
||||
default_priority_0, default_priority_1, default_priority_2,
|
||||
default_priority_3, default_priority_4, default_priority_5,
|
||||
default_priority_6, default_priority_7, default_priority_8,
|
||||
default_priority_9};
|
||||
default_priority_9, default_priority_10};
|
||||
|
||||
struct ThreadPrio {
|
||||
std::string name;
|
||||
@@ -160,7 +175,7 @@ struct SwitcherData {
|
||||
|
||||
uint32_t threadPriority = QThread::NormalPriority;
|
||||
|
||||
std::vector<int> tabOrder;
|
||||
std::vector<int> tabOrder = std::vector<int>(tab_count);
|
||||
|
||||
bool hotkeysRegistered = false;
|
||||
obs_hotkey_id startHotkey = 0;
|
||||
@@ -188,33 +203,29 @@ struct SwitcherData {
|
||||
void writeToStatusFile(QString status);
|
||||
|
||||
bool checkForMatch(OBSWeakSource &scene, OBSWeakSource &transition,
|
||||
int &linger);
|
||||
void checkSceneSequence(bool &match, OBSWeakSource &scene,
|
||||
OBSWeakSource &transition, int &linger);
|
||||
void checkIdleSwitch(bool &match, OBSWeakSource &scene,
|
||||
OBSWeakSource &transition);
|
||||
void checkWindowTitleSwitch(bool &match, OBSWeakSource &scene,
|
||||
int &linger, bool &setPreviousSceneAsMatch,
|
||||
bool ¯oMatch);
|
||||
bool checkMacros();
|
||||
bool runMacros();
|
||||
bool checkSceneSequence(OBSWeakSource &scene, OBSWeakSource &transition,
|
||||
int &linger, bool &setPrevSceneAfterLinger);
|
||||
bool checkIdleSwitch(OBSWeakSource &scene, OBSWeakSource &transition);
|
||||
bool checkWindowTitleSwitch(OBSWeakSource &scene,
|
||||
OBSWeakSource &transition);
|
||||
void checkExeSwitch(bool &match, OBSWeakSource &scene,
|
||||
OBSWeakSource &transition);
|
||||
void checkScreenRegionSwitch(bool &match, OBSWeakSource &scene,
|
||||
bool checkExeSwitch(OBSWeakSource &scene, OBSWeakSource &transition);
|
||||
bool checkScreenRegionSwitch(OBSWeakSource &scene,
|
||||
OBSWeakSource &transition);
|
||||
void checkSwitchInfoFromFile(bool &match, OBSWeakSource &scene,
|
||||
bool checkSwitchInfoFromFile(OBSWeakSource &scene,
|
||||
OBSWeakSource &transition);
|
||||
void checkFileContent(bool &match, OBSWeakSource &scene,
|
||||
OBSWeakSource &transition);
|
||||
void checkRandom(bool &match, OBSWeakSource &scene,
|
||||
OBSWeakSource &transition, int &delay);
|
||||
void checkMediaSwitch(bool &match, OBSWeakSource &scene,
|
||||
OBSWeakSource &transition);
|
||||
void checkTimeSwitch(bool &match, OBSWeakSource &scene,
|
||||
OBSWeakSource &transition);
|
||||
void checkAudioSwitch(bool &match, OBSWeakSource &scene,
|
||||
OBSWeakSource &transition);
|
||||
bool checkFileContent(OBSWeakSource &scene, OBSWeakSource &transition);
|
||||
bool checkRandom(OBSWeakSource &scene, OBSWeakSource &transition,
|
||||
int &delay);
|
||||
bool checkMediaSwitch(OBSWeakSource &scene, OBSWeakSource &transition);
|
||||
bool checkTimeSwitch(OBSWeakSource &scene, OBSWeakSource &transition);
|
||||
bool checkAudioSwitch(OBSWeakSource &scene, OBSWeakSource &transition);
|
||||
void checkAudioSwitchFallback(OBSWeakSource &scene,
|
||||
OBSWeakSource &transition);
|
||||
void checkVideoSwitch(bool &match, OBSWeakSource &scene,
|
||||
OBSWeakSource &transition);
|
||||
bool checkVideoSwitch(OBSWeakSource &scene, OBSWeakSource &transition);
|
||||
void checkNoMatchSwitch(bool &match, OBSWeakSource &scene,
|
||||
OBSWeakSource &transition, int &sleep);
|
||||
void checkSwitchCooldown(bool &match);
|
||||
@@ -223,6 +234,7 @@ struct SwitcherData {
|
||||
void checkDefaultSceneTransitions();
|
||||
|
||||
void saveSettings(obs_data_t *obj);
|
||||
void saveMacros(obs_data_t *obj);
|
||||
void saveWindowTitleSwitches(obs_data_t *obj);
|
||||
void saveScreenRegionSwitches(obs_data_t *obj);
|
||||
void savePauseSwitches(obs_data_t *obj);
|
||||
@@ -244,10 +256,10 @@ struct SwitcherData {
|
||||
void saveVersion(obs_data_t *obj, std::string currentVersion);
|
||||
|
||||
void loadSettings(obs_data_t *obj);
|
||||
void loadMacros(obs_data_t *obj);
|
||||
void loadWindowTitleSwitches(obs_data_t *obj);
|
||||
void loadScreenRegionSwitches(obs_data_t *obj);
|
||||
void loadPauseSwitches(obs_data_t *obj);
|
||||
void loadOldPauseSwitches(obs_data_t *obj);
|
||||
void loadSceneSequenceSwitches(obs_data_t *obj);
|
||||
void loadSceneTransitions(obs_data_t *obj);
|
||||
void loadIdleSwitches(obs_data_t *obj);
|
||||
|
||||
@@ -1,22 +1,16 @@
|
||||
#pragma once
|
||||
|
||||
#include <QString>
|
||||
#include <QTextStream>
|
||||
#include <QLayout>
|
||||
#include <QLabel>
|
||||
#include <QMessageBox>
|
||||
#include <unordered_map>
|
||||
|
||||
#include <obs.hpp>
|
||||
#include <obs-module.h>
|
||||
#include <obs-frontend-api.h>
|
||||
|
||||
static inline bool SceneGroupValid(SceneGroup *group)
|
||||
{
|
||||
if (group) {
|
||||
return group->name != invalid_scene_group_name;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline bool WeakSourceValid(obs_weak_source_t *ws)
|
||||
{
|
||||
obs_source_t *source = obs_weak_source_get_source(ws);
|
||||
@@ -39,26 +33,6 @@ static inline std::string GetWeakSourceName(obs_weak_source_t *weak_source)
|
||||
return name;
|
||||
}
|
||||
|
||||
static inline SceneGroup *GetSceneGroupByName(const char *name)
|
||||
{
|
||||
if (!switcher) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
for (SceneGroup &sg : switcher->sceneGroups) {
|
||||
if (sg.name == name) {
|
||||
return &sg;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static inline SceneGroup *GetSceneGroupByQString(const QString &name)
|
||||
{
|
||||
return GetSceneGroupByName(name.toUtf8().constData());
|
||||
}
|
||||
|
||||
static inline OBSWeakSource GetWeakSourceByName(const char *name)
|
||||
{
|
||||
OBSWeakSource weak;
|
||||
@@ -182,3 +156,61 @@ placeWidgets(std::string text, QBoxLayout *layout,
|
||||
layout->addStretch();
|
||||
}
|
||||
}
|
||||
|
||||
static inline void clearLayout(QLayout *layout)
|
||||
{
|
||||
QLayoutItem *item;
|
||||
while ((item = layout->takeAt(0))) {
|
||||
if (item->layout()) {
|
||||
clearLayout(item->layout());
|
||||
delete item->layout();
|
||||
}
|
||||
if (item->widget()) {
|
||||
delete item->widget();
|
||||
}
|
||||
delete item;
|
||||
}
|
||||
}
|
||||
|
||||
static inline bool compareIgnoringLineEnding(QString &s1, QString &s2)
|
||||
{
|
||||
// Let QT deal with different types of lineendings
|
||||
QTextStream s1stream(&s1);
|
||||
QTextStream s2stream(&s2);
|
||||
|
||||
while (!s1stream.atEnd() || !s2stream.atEnd()) {
|
||||
QString s1s = s1stream.readLine();
|
||||
QString s2s = s2stream.readLine();
|
||||
if (s1s != s2s) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!s1stream.atEnd() && !s2stream.atEnd()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline bool DisplayMessage(QString msg, bool question = false)
|
||||
{
|
||||
if (question) {
|
||||
QMessageBox::StandardButton reply;
|
||||
reply = QMessageBox::question(
|
||||
nullptr, "Advanced Scene Switcher", msg,
|
||||
QMessageBox::Yes | QMessageBox::No);
|
||||
if (reply == QMessageBox::Yes) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
QMessageBox Msgbox;
|
||||
Msgbox.setWindowTitle("Advanced Scene Switcher");
|
||||
Msgbox.setText(msg);
|
||||
Msgbox.exec();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -68,41 +68,38 @@ void registerHotkeys()
|
||||
|
||||
void SwitcherData::saveHotkeys(obs_data_t *obj)
|
||||
{
|
||||
obs_data_array_t *startHotkeyArrray =
|
||||
obs_hotkey_save(switcher->startHotkey);
|
||||
obs_data_array_t *startHotkeyArrray = obs_hotkey_save(startHotkey);
|
||||
obs_data_set_array(obj, "startHotkey", startHotkeyArrray);
|
||||
obs_data_array_release(startHotkeyArrray);
|
||||
|
||||
obs_data_array_t *stopHotkeyArrray =
|
||||
obs_hotkey_save(switcher->stopHotkey);
|
||||
obs_data_array_t *stopHotkeyArrray = obs_hotkey_save(stopHotkey);
|
||||
|
||||
obs_data_set_array(obj, "stopHotkey", stopHotkeyArrray);
|
||||
obs_data_array_release(stopHotkeyArrray);
|
||||
|
||||
obs_data_array_t *toggleHotkeyArrray =
|
||||
obs_hotkey_save(switcher->toggleHotkey);
|
||||
obs_data_array_t *toggleHotkeyArrray = obs_hotkey_save(toggleHotkey);
|
||||
obs_data_set_array(obj, "toggleHotkey", toggleHotkeyArrray);
|
||||
obs_data_array_release(toggleHotkeyArrray);
|
||||
}
|
||||
|
||||
void SwitcherData::loadHotkeys(obs_data_t *obj)
|
||||
{
|
||||
if (!switcher->hotkeysRegistered) {
|
||||
if (!hotkeysRegistered) {
|
||||
registerHotkeys();
|
||||
}
|
||||
|
||||
obs_data_array_t *startHotkeyArrray =
|
||||
obs_data_get_array(obj, "startHotkey");
|
||||
obs_hotkey_load(switcher->startHotkey, startHotkeyArrray);
|
||||
obs_hotkey_load(startHotkey, startHotkeyArrray);
|
||||
obs_data_array_release(startHotkeyArrray);
|
||||
|
||||
obs_data_array_t *stopHotkeyArrray =
|
||||
obs_data_get_array(obj, "stopHotkey");
|
||||
obs_hotkey_load(switcher->stopHotkey, stopHotkeyArrray);
|
||||
obs_hotkey_load(stopHotkey, stopHotkeyArrray);
|
||||
obs_data_array_release(stopHotkeyArrray);
|
||||
|
||||
obs_data_array_t *toggleHotkeyArrray =
|
||||
obs_data_get_array(obj, "toggleHotkey");
|
||||
obs_hotkey_load(switcher->toggleHotkey, toggleHotkeyArrray);
|
||||
obs_hotkey_load(toggleHotkey, toggleHotkeyArrray);
|
||||
obs_data_array_release(toggleHotkeyArrray);
|
||||
}
|
||||
|
||||
@@ -14,7 +14,9 @@
|
||||
#undef Status
|
||||
#undef Unsorted
|
||||
#include <util/platform.h>
|
||||
#include "../headers/advanced-scene-switcher.hpp"
|
||||
#include <vector>
|
||||
#include <QStringList>
|
||||
#include <QRegularExpression>
|
||||
|
||||
static Display *xdisplay = 0;
|
||||
|
||||
|
||||
186
src/macro-action-audio.cpp
Normal file
186
src/macro-action-audio.cpp
Normal file
@@ -0,0 +1,186 @@
|
||||
#include "headers/macro-action-audio.hpp"
|
||||
#include "headers/advanced-scene-switcher.hpp"
|
||||
#include "headers/utility.hpp"
|
||||
|
||||
const int MacroActionAudio::id = 2;
|
||||
|
||||
bool MacroActionAudio::_registered = MacroActionFactory::Register(
|
||||
MacroActionAudio::id,
|
||||
{MacroActionAudio::Create, MacroActionAudioEdit::Create,
|
||||
"AdvSceneSwitcher.action.audio"});
|
||||
|
||||
const static std::map<AudioAction, std::string> actionTypes = {
|
||||
{AudioAction::MUTE, "AdvSceneSwitcher.action.audio.type.mute"},
|
||||
{AudioAction::UNMUTE, "AdvSceneSwitcher.action.audio.type.unmute"},
|
||||
{AudioAction::SOURCE_VOLUME,
|
||||
"AdvSceneSwitcher.action.audio.type.sourceVolume"},
|
||||
{AudioAction::MASTER_VOLUME,
|
||||
"AdvSceneSwitcher.action.audio.type.masterVolume"},
|
||||
};
|
||||
|
||||
bool MacroActionAudio::PerformAction()
|
||||
{
|
||||
auto s = obs_weak_source_get_source(_audioSource);
|
||||
switch (_action) {
|
||||
case AudioAction::MUTE:
|
||||
obs_source_set_muted(s, true);
|
||||
break;
|
||||
case AudioAction::UNMUTE:
|
||||
obs_source_set_muted(s, false);
|
||||
break;
|
||||
case AudioAction::SOURCE_VOLUME:
|
||||
obs_source_set_volume(s, (float)_volume / 100.0f);
|
||||
break;
|
||||
case AudioAction::MASTER_VOLUME:
|
||||
obs_set_master_volume((float)_volume / 100.0f);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
obs_source_release(s);
|
||||
return true;
|
||||
}
|
||||
|
||||
void MacroActionAudio::LogAction()
|
||||
{
|
||||
auto it = actionTypes.find(_action);
|
||||
if (it != actionTypes.end()) {
|
||||
vblog(LOG_INFO,
|
||||
"performed action \"%s\" for source \"%s\" with volume %d",
|
||||
it->second.c_str(),
|
||||
GetWeakSourceName(_audioSource).c_str(), _volume);
|
||||
} else {
|
||||
blog(LOG_WARNING, "ignored unknown audio action %d",
|
||||
static_cast<int>(_action));
|
||||
}
|
||||
}
|
||||
|
||||
bool MacroActionAudio::Save(obs_data_t *obj)
|
||||
{
|
||||
MacroAction::Save(obj);
|
||||
obs_data_set_string(obj, "audioSource",
|
||||
GetWeakSourceName(_audioSource).c_str());
|
||||
obs_data_set_int(obj, "action", static_cast<int>(_action));
|
||||
obs_data_set_int(obj, "volume", _volume);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MacroActionAudio::Load(obs_data_t *obj)
|
||||
{
|
||||
MacroAction::Load(obj);
|
||||
const char *audioSourceName = obs_data_get_string(obj, "audioSource");
|
||||
_audioSource = GetWeakSourceByName(audioSourceName);
|
||||
_action = static_cast<AudioAction>(obs_data_get_int(obj, "action"));
|
||||
_volume = obs_data_get_int(obj, "volume");
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline void populateActionSelection(QComboBox *list)
|
||||
{
|
||||
for (auto entry : actionTypes) {
|
||||
list->addItem(obs_module_text(entry.second.c_str()));
|
||||
}
|
||||
}
|
||||
|
||||
MacroActionAudioEdit::MacroActionAudioEdit(
|
||||
QWidget *parent, std::shared_ptr<MacroActionAudio> entryData)
|
||||
: QWidget(parent)
|
||||
{
|
||||
_audioSources = new QComboBox();
|
||||
_actions = new QComboBox();
|
||||
_volumePercent = new QSpinBox();
|
||||
_volumePercent->setMinimum(0);
|
||||
_volumePercent->setMaximum(2000);
|
||||
_volumePercent->setSuffix("%");
|
||||
|
||||
populateActionSelection(_actions);
|
||||
AdvSceneSwitcher::populateAudioSelection(_audioSources);
|
||||
|
||||
QWidget::connect(_actions, SIGNAL(currentIndexChanged(int)), this,
|
||||
SLOT(ActionChanged(int)));
|
||||
QWidget::connect(_audioSources,
|
||||
SIGNAL(currentTextChanged(const QString &)), this,
|
||||
SLOT(SourceChanged(const QString &)));
|
||||
QWidget::connect(_volumePercent, SIGNAL(valueChanged(int)), this,
|
||||
SLOT(VolumeChanged(int)));
|
||||
|
||||
QHBoxLayout *mainLayout = new QHBoxLayout;
|
||||
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
|
||||
{"{{audioSources}}", _audioSources},
|
||||
{"{{actions}}", _actions},
|
||||
{"{{volume}}", _volumePercent},
|
||||
};
|
||||
placeWidgets(obs_module_text("AdvSceneSwitcher.action.audio.entry"),
|
||||
mainLayout, widgetPlaceholders);
|
||||
setLayout(mainLayout);
|
||||
|
||||
_entryData = entryData;
|
||||
UpdateEntryData();
|
||||
_loading = false;
|
||||
}
|
||||
|
||||
bool hasVolumeControl(AudioAction action)
|
||||
{
|
||||
return action == AudioAction::SOURCE_VOLUME ||
|
||||
action == AudioAction::MASTER_VOLUME;
|
||||
}
|
||||
|
||||
bool hasSourceControl(AudioAction action)
|
||||
{
|
||||
return action != AudioAction::MASTER_VOLUME;
|
||||
}
|
||||
|
||||
void MacroActionAudioEdit::UpdateEntryData()
|
||||
{
|
||||
if (!_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
_audioSources->setCurrentText(
|
||||
GetWeakSourceName(_entryData->_audioSource).c_str());
|
||||
_actions->setCurrentIndex(static_cast<int>(_entryData->_action));
|
||||
_volumePercent->setValue(_entryData->_volume);
|
||||
|
||||
if (hasVolumeControl(_entryData->_action)) {
|
||||
_volumePercent->show();
|
||||
} else {
|
||||
_volumePercent->hide();
|
||||
}
|
||||
|
||||
if (hasSourceControl(_entryData->_action)) {
|
||||
_audioSources->show();
|
||||
} else {
|
||||
_audioSources->hide();
|
||||
}
|
||||
}
|
||||
|
||||
void MacroActionAudioEdit::SourceChanged(const QString &text)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_audioSource = GetWeakSourceByQString(text);
|
||||
}
|
||||
|
||||
void MacroActionAudioEdit::ActionChanged(int value)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_action = static_cast<AudioAction>(value);
|
||||
UpdateEntryData();
|
||||
}
|
||||
|
||||
void MacroActionAudioEdit::VolumeChanged(int value)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_volume = value;
|
||||
}
|
||||
135
src/macro-action-edit.cpp
Normal file
135
src/macro-action-edit.cpp
Normal file
@@ -0,0 +1,135 @@
|
||||
#include "headers/macro-action-edit.hpp"
|
||||
#include "headers/macro-action-switch-scene.hpp"
|
||||
#include "headers/utility.hpp"
|
||||
#include "headers/advanced-scene-switcher.hpp"
|
||||
|
||||
std::map<int, MacroActionInfo> MacroActionFactory::_methods;
|
||||
|
||||
bool MacroActionFactory::Register(int id, MacroActionInfo info)
|
||||
{
|
||||
if (auto it = _methods.find(id); it == _methods.end()) {
|
||||
_methods[id] = info;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
std::shared_ptr<MacroAction> MacroActionFactory::Create(const int id)
|
||||
{
|
||||
if (auto it = _methods.find(id); it != _methods.end())
|
||||
return it->second._createFunc();
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
QWidget *MacroActionFactory::CreateWidget(const int id, QWidget *parent,
|
||||
std::shared_ptr<MacroAction> action)
|
||||
{
|
||||
if (auto it = _methods.find(id); it != _methods.end())
|
||||
return it->second._createWidgetFunc(parent, action);
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static inline void populateActionSelection(QComboBox *list)
|
||||
{
|
||||
for (auto entry : MacroActionFactory::GetActionTypes()) {
|
||||
list->addItem(obs_module_text(entry.second._name.c_str()));
|
||||
}
|
||||
}
|
||||
|
||||
MacroActionEdit::MacroActionEdit(QWidget *parent,
|
||||
std::shared_ptr<MacroAction> *entryData,
|
||||
int type, bool startCollapsed)
|
||||
: QWidget(parent)
|
||||
{
|
||||
_actionSelection = new QComboBox();
|
||||
_section = new Section(300);
|
||||
|
||||
QWidget::connect(_actionSelection, SIGNAL(currentIndexChanged(int)),
|
||||
this, SLOT(ActionSelectionChanged(int)));
|
||||
|
||||
populateActionSelection(_actionSelection);
|
||||
|
||||
_section->AddHeaderWidget(_actionSelection);
|
||||
|
||||
QVBoxLayout *mainLayout = new QVBoxLayout;
|
||||
mainLayout->addWidget(_section);
|
||||
setLayout(mainLayout);
|
||||
|
||||
_entryData = entryData;
|
||||
UpdateEntryData(type);
|
||||
|
||||
_loading = false;
|
||||
_section->Collapse(startCollapsed);
|
||||
}
|
||||
|
||||
void MacroActionEdit::ActionSelectionChanged(int idx)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->reset();
|
||||
*_entryData = MacroActionFactory::Create(idx);
|
||||
auto widget =
|
||||
MacroActionFactory::CreateWidget(idx, window(), *_entryData);
|
||||
_section->SetContent(widget);
|
||||
_section->Collapse(false);
|
||||
}
|
||||
|
||||
void MacroActionEdit::UpdateEntryData(int type)
|
||||
{
|
||||
_actionSelection->setCurrentIndex(type);
|
||||
auto widget =
|
||||
MacroActionFactory::CreateWidget(type, window(), *_entryData);
|
||||
_section->SetContent(widget);
|
||||
}
|
||||
|
||||
void MacroActionEdit::Collapse(bool collapsed)
|
||||
{
|
||||
_section->Collapse(collapsed);
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::on_actionAdd_clicked()
|
||||
{
|
||||
auto macro = getSelectedMacro();
|
||||
if (!macro) {
|
||||
return;
|
||||
}
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
macro->Actions().emplace_back(MacroActionFactory::Create(0));
|
||||
auto newEntry = new MacroActionEdit(this, ¯o->Actions().back(), 0);
|
||||
ui->macroEditActionLayout->addWidget(newEntry);
|
||||
ui->macroEditActionHelp->setVisible(false);
|
||||
newEntry->Collapse(false);
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::on_actionRemove_clicked()
|
||||
{
|
||||
auto macro = getSelectedMacro();
|
||||
if (!macro) {
|
||||
return;
|
||||
}
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
if (macro->Actions().empty()) {
|
||||
return;
|
||||
}
|
||||
macro->Actions().pop_back();
|
||||
|
||||
int count = ui->macroEditActionLayout->count();
|
||||
auto item = ui->macroEditActionLayout->takeAt(count - 1);
|
||||
|
||||
if (item) {
|
||||
auto widget = item->widget();
|
||||
if (widget) {
|
||||
widget->setVisible(false);
|
||||
}
|
||||
delete item;
|
||||
}
|
||||
|
||||
if (count == 1) {
|
||||
ui->macroEditActionHelp->setVisible(true);
|
||||
}
|
||||
}
|
||||
133
src/macro-action-recording.cpp
Normal file
133
src/macro-action-recording.cpp
Normal file
@@ -0,0 +1,133 @@
|
||||
#include "headers/macro-action-recording.hpp"
|
||||
#include "headers/advanced-scene-switcher.hpp"
|
||||
#include "headers/utility.hpp"
|
||||
|
||||
const int MacroActionRecord::id = 4;
|
||||
|
||||
bool MacroActionRecord::_registered = MacroActionFactory::Register(
|
||||
MacroActionRecord::id,
|
||||
{MacroActionRecord::Create, MacroActionRecordEdit::Create,
|
||||
"AdvSceneSwitcher.action.recording"});
|
||||
|
||||
const static std::map<RecordAction, std::string> actionTypes = {
|
||||
{RecordAction::STOP, "AdvSceneSwitcher.action.recording.type.stop"},
|
||||
{RecordAction::START, "AdvSceneSwitcher.action.recording.type.start"},
|
||||
{RecordAction::PAUSE, "AdvSceneSwitcher.action.recording.type.pause"},
|
||||
{RecordAction::UNPAUSE,
|
||||
"AdvSceneSwitcher.action.recording.type.unpause"},
|
||||
};
|
||||
|
||||
bool MacroActionRecord::PerformAction()
|
||||
{
|
||||
switch (_action) {
|
||||
case RecordAction::STOP:
|
||||
if (obs_frontend_recording_active()) {
|
||||
obs_frontend_recording_stop();
|
||||
}
|
||||
break;
|
||||
case RecordAction::START:
|
||||
if (!obs_frontend_recording_active()) {
|
||||
obs_frontend_recording_start();
|
||||
}
|
||||
break;
|
||||
case RecordAction::PAUSE:
|
||||
if (obs_frontend_recording_active() &&
|
||||
!obs_frontend_recording_paused()) {
|
||||
obs_frontend_recording_pause(true);
|
||||
}
|
||||
break;
|
||||
case RecordAction::UNPAUSE:
|
||||
if (obs_frontend_recording_active() &&
|
||||
obs_frontend_recording_paused()) {
|
||||
obs_frontend_recording_pause(false);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void MacroActionRecord::LogAction()
|
||||
{
|
||||
auto it = actionTypes.find(_action);
|
||||
if (it != actionTypes.end()) {
|
||||
vblog(LOG_INFO, "performed action \"%s\"", it->second.c_str());
|
||||
} else {
|
||||
blog(LOG_WARNING, "ignored unknown recording action %d",
|
||||
static_cast<int>(_action));
|
||||
}
|
||||
}
|
||||
|
||||
bool MacroActionRecord::Save(obs_data_t *obj)
|
||||
{
|
||||
MacroAction::Save(obj);
|
||||
obs_data_set_int(obj, "action", static_cast<int>(_action));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MacroActionRecord::Load(obs_data_t *obj)
|
||||
{
|
||||
MacroAction::Load(obj);
|
||||
_action = static_cast<RecordAction>(obs_data_get_int(obj, "action"));
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline void populateActionSelection(QComboBox *list)
|
||||
{
|
||||
for (auto entry : actionTypes) {
|
||||
list->addItem(obs_module_text(entry.second.c_str()));
|
||||
}
|
||||
}
|
||||
|
||||
MacroActionRecordEdit::MacroActionRecordEdit(
|
||||
QWidget *parent, std::shared_ptr<MacroActionRecord> entryData)
|
||||
: QWidget(parent)
|
||||
{
|
||||
_actions = new QComboBox();
|
||||
_pauseHint = new QLabel(obs_module_text(
|
||||
"AdvSceneSwitcher.action.recording.pause.hint"));
|
||||
|
||||
populateActionSelection(_actions);
|
||||
|
||||
QWidget::connect(_actions, SIGNAL(currentIndexChanged(int)), this,
|
||||
SLOT(ActionChanged(int)));
|
||||
|
||||
QHBoxLayout *mainLayout = new QHBoxLayout;
|
||||
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
|
||||
{"{{actions}}", _actions},
|
||||
{"{{pauseHint}}", _pauseHint},
|
||||
};
|
||||
placeWidgets(obs_module_text("AdvSceneSwitcher.action.recording.entry"),
|
||||
mainLayout, widgetPlaceholders);
|
||||
setLayout(mainLayout);
|
||||
|
||||
_entryData = entryData;
|
||||
UpdateEntryData();
|
||||
_loading = false;
|
||||
}
|
||||
|
||||
bool isPauseAction(RecordAction a)
|
||||
{
|
||||
return a == RecordAction::PAUSE || a == RecordAction::UNPAUSE;
|
||||
}
|
||||
|
||||
void MacroActionRecordEdit::UpdateEntryData()
|
||||
{
|
||||
if (!_entryData) {
|
||||
return;
|
||||
}
|
||||
_actions->setCurrentIndex(static_cast<int>(_entryData->_action));
|
||||
_pauseHint->setVisible(isPauseAction(_entryData->_action));
|
||||
}
|
||||
|
||||
void MacroActionRecordEdit::ActionChanged(int value)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_action = static_cast<RecordAction>(value);
|
||||
_pauseHint->setVisible(isPauseAction(_entryData->_action));
|
||||
}
|
||||
121
src/macro-action-replay-buffer.cpp
Normal file
121
src/macro-action-replay-buffer.cpp
Normal file
@@ -0,0 +1,121 @@
|
||||
#include "headers/macro-action-replay-buffer.hpp"
|
||||
#include "headers/advanced-scene-switcher.hpp"
|
||||
#include "headers/utility.hpp"
|
||||
|
||||
const int MacroActionReplayBuffer::id = 5;
|
||||
|
||||
bool MacroActionReplayBuffer::_registered = MacroActionFactory::Register(
|
||||
MacroActionReplayBuffer::id,
|
||||
{MacroActionReplayBuffer::Create, MacroActionReplayBufferEdit::Create,
|
||||
"AdvSceneSwitcher.action.replay"});
|
||||
|
||||
const static std::map<ReplayBufferAction, std::string> actionTypes = {
|
||||
{ReplayBufferAction::STOP, "AdvSceneSwitcher.action.replay.type.stop"},
|
||||
{ReplayBufferAction::START,
|
||||
"AdvSceneSwitcher.action.replay.type.start"},
|
||||
{ReplayBufferAction::SAVE, "AdvSceneSwitcher.action.replay.type.save"},
|
||||
};
|
||||
|
||||
bool MacroActionReplayBuffer::PerformAction()
|
||||
{
|
||||
switch (_action) {
|
||||
case ReplayBufferAction::STOP:
|
||||
if (obs_frontend_replay_buffer_active()) {
|
||||
obs_frontend_replay_buffer_stop();
|
||||
}
|
||||
break;
|
||||
case ReplayBufferAction::START:
|
||||
if (!obs_frontend_replay_buffer_active()) {
|
||||
obs_frontend_replay_buffer_start();
|
||||
}
|
||||
break;
|
||||
case ReplayBufferAction::SAVE:
|
||||
if (obs_frontend_replay_buffer_active() &&
|
||||
_duration.DurationReached()) {
|
||||
obs_frontend_replay_buffer_save();
|
||||
// Default buffer size is 20s so waiting for 10s before
|
||||
// trying to save again seems reasonable
|
||||
_duration.seconds = 10;
|
||||
_duration.Reset();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void MacroActionReplayBuffer::LogAction()
|
||||
{
|
||||
auto it = actionTypes.find(_action);
|
||||
if (it != actionTypes.end()) {
|
||||
vblog(LOG_INFO, "performed action \"%s\"", it->second.c_str());
|
||||
} else {
|
||||
blog(LOG_WARNING, "ignored unknown replay buffer action %d",
|
||||
static_cast<int>(_action));
|
||||
}
|
||||
}
|
||||
|
||||
bool MacroActionReplayBuffer::Save(obs_data_t *obj)
|
||||
{
|
||||
MacroAction::Save(obj);
|
||||
obs_data_set_int(obj, "action", static_cast<int>(_action));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MacroActionReplayBuffer::Load(obs_data_t *obj)
|
||||
{
|
||||
MacroAction::Load(obj);
|
||||
_action = static_cast<ReplayBufferAction>(
|
||||
obs_data_get_int(obj, "action"));
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline void populateActionSelection(QComboBox *list)
|
||||
{
|
||||
for (auto entry : actionTypes) {
|
||||
list->addItem(obs_module_text(entry.second.c_str()));
|
||||
}
|
||||
}
|
||||
|
||||
MacroActionReplayBufferEdit::MacroActionReplayBufferEdit(
|
||||
QWidget *parent, std::shared_ptr<MacroActionReplayBuffer> entryData)
|
||||
: QWidget(parent)
|
||||
{
|
||||
_actions = new QComboBox();
|
||||
|
||||
populateActionSelection(_actions);
|
||||
|
||||
QWidget::connect(_actions, SIGNAL(currentIndexChanged(int)), this,
|
||||
SLOT(ActionChanged(int)));
|
||||
|
||||
QHBoxLayout *mainLayout = new QHBoxLayout;
|
||||
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
|
||||
{"{{actions}}", _actions},
|
||||
};
|
||||
placeWidgets(obs_module_text("AdvSceneSwitcher.action.replay.entry"),
|
||||
mainLayout, widgetPlaceholders);
|
||||
setLayout(mainLayout);
|
||||
|
||||
_entryData = entryData;
|
||||
UpdateEntryData();
|
||||
_loading = false;
|
||||
}
|
||||
|
||||
void MacroActionReplayBufferEdit::UpdateEntryData()
|
||||
{
|
||||
if (!_entryData) {
|
||||
return;
|
||||
}
|
||||
_actions->setCurrentIndex(static_cast<int>(_entryData->_action));
|
||||
}
|
||||
|
||||
void MacroActionReplayBufferEdit::ActionChanged(int value)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_action = static_cast<ReplayBufferAction>(value);
|
||||
}
|
||||
98
src/macro-action-run.cpp
Normal file
98
src/macro-action-run.cpp
Normal file
@@ -0,0 +1,98 @@
|
||||
#include "headers/macro-action-run.hpp"
|
||||
#include "headers/advanced-scene-switcher.hpp"
|
||||
#include "headers/utility.hpp"
|
||||
|
||||
#include <QProcess>
|
||||
#include <QFileDialog>
|
||||
|
||||
const int MacroActionRun::id = 6;
|
||||
|
||||
bool MacroActionRun::_registered = MacroActionFactory::Register(
|
||||
MacroActionRun::id, {MacroActionRun::Create, MacroActionRunEdit::Create,
|
||||
"AdvSceneSwitcher.action.run"});
|
||||
|
||||
bool MacroActionRun::PerformAction()
|
||||
{
|
||||
QProcess::startDetached(QString::fromStdString(_path), QStringList());
|
||||
return true;
|
||||
}
|
||||
|
||||
void MacroActionRun::LogAction()
|
||||
{
|
||||
vblog(LOG_INFO, "run \"%s\"", _path.c_str());
|
||||
}
|
||||
|
||||
bool MacroActionRun::Save(obs_data_t *obj)
|
||||
{
|
||||
MacroAction::Save(obj);
|
||||
obs_data_set_string(obj, "path", _path.c_str());
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MacroActionRun::Load(obs_data_t *obj)
|
||||
{
|
||||
MacroAction::Load(obj);
|
||||
_path = obs_data_get_string(obj, "path");
|
||||
return true;
|
||||
}
|
||||
|
||||
MacroActionRunEdit::MacroActionRunEdit(
|
||||
QWidget *parent, std::shared_ptr<MacroActionRun> entryData)
|
||||
: QWidget(parent)
|
||||
{
|
||||
_filePath = new QLineEdit();
|
||||
_browseButton =
|
||||
new QPushButton(obs_module_text("AdvSceneSwitcher.browse"));
|
||||
_browseButton->setStyleSheet("border:1px solid gray;");
|
||||
|
||||
QWidget::connect(_filePath, SIGNAL(editingFinished()), this,
|
||||
SLOT(FilePathChanged()));
|
||||
QWidget::connect(_browseButton, SIGNAL(clicked()), this,
|
||||
SLOT(BrowseButtonClicked()));
|
||||
|
||||
QHBoxLayout *mainLayout = new QHBoxLayout;
|
||||
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
|
||||
{"{{filePath}}", _filePath},
|
||||
{"{{browseButton}}", _browseButton},
|
||||
};
|
||||
placeWidgets(obs_module_text("AdvSceneSwitcher.action.run.entry"),
|
||||
mainLayout, widgetPlaceholders);
|
||||
setLayout(mainLayout);
|
||||
|
||||
_entryData = entryData;
|
||||
UpdateEntryData();
|
||||
_loading = false;
|
||||
}
|
||||
|
||||
void MacroActionRunEdit::UpdateEntryData()
|
||||
{
|
||||
if (!_entryData) {
|
||||
return;
|
||||
}
|
||||
_filePath->setText(QString::fromStdString(_entryData->_path));
|
||||
}
|
||||
|
||||
void MacroActionRunEdit::FilePathChanged()
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_path = _filePath->text().toUtf8().constData();
|
||||
}
|
||||
|
||||
void MacroActionRunEdit::BrowseButtonClicked()
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
QString path = QFileDialog::getOpenFileName(this);
|
||||
if (path.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
_filePath->setText(path);
|
||||
FilePathChanged();
|
||||
}
|
||||
111
src/macro-action-streaming.cpp
Normal file
111
src/macro-action-streaming.cpp
Normal file
@@ -0,0 +1,111 @@
|
||||
#include "headers/macro-action-streaming.hpp"
|
||||
#include "headers/advanced-scene-switcher.hpp"
|
||||
#include "headers/utility.hpp"
|
||||
|
||||
const int MacroActionStream::id = 3;
|
||||
|
||||
bool MacroActionStream::_registered = MacroActionFactory::Register(
|
||||
MacroActionStream::id,
|
||||
{MacroActionStream::Create, MacroActionStreamEdit::Create,
|
||||
"AdvSceneSwitcher.action.streaming"});
|
||||
|
||||
const static std::map<StreamAction, std::string> actionTypes = {
|
||||
{StreamAction::STOP, "AdvSceneSwitcher.action.streaming.type.stop"},
|
||||
{StreamAction::START, "AdvSceneSwitcher.action.streaming.type.start"},
|
||||
};
|
||||
|
||||
bool MacroActionStream::PerformAction()
|
||||
{
|
||||
switch (_action) {
|
||||
case StreamAction::STOP:
|
||||
if (obs_frontend_streaming_active()) {
|
||||
obs_frontend_streaming_stop();
|
||||
}
|
||||
break;
|
||||
case StreamAction::START:
|
||||
if (!obs_frontend_streaming_active() &&
|
||||
_retryCooldown.DurationReached()) {
|
||||
obs_frontend_streaming_start();
|
||||
_retryCooldown.seconds++;
|
||||
_retryCooldown.Reset();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void MacroActionStream::LogAction()
|
||||
{
|
||||
auto it = actionTypes.find(_action);
|
||||
if (it != actionTypes.end()) {
|
||||
vblog(LOG_INFO, "performed action \"%s\"", it->second.c_str());
|
||||
} else {
|
||||
blog(LOG_WARNING, "ignored unknown streaming action %d",
|
||||
static_cast<int>(_action));
|
||||
}
|
||||
}
|
||||
|
||||
bool MacroActionStream::Save(obs_data_t *obj)
|
||||
{
|
||||
MacroAction::Save(obj);
|
||||
obs_data_set_int(obj, "action", static_cast<int>(_action));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MacroActionStream::Load(obs_data_t *obj)
|
||||
{
|
||||
MacroAction::Load(obj);
|
||||
_action = static_cast<StreamAction>(obs_data_get_int(obj, "action"));
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline void populateActionSelection(QComboBox *list)
|
||||
{
|
||||
for (auto entry : actionTypes) {
|
||||
list->addItem(obs_module_text(entry.second.c_str()));
|
||||
}
|
||||
}
|
||||
|
||||
MacroActionStreamEdit::MacroActionStreamEdit(
|
||||
QWidget *parent, std::shared_ptr<MacroActionStream> entryData)
|
||||
: QWidget(parent)
|
||||
{
|
||||
_actions = new QComboBox();
|
||||
|
||||
populateActionSelection(_actions);
|
||||
|
||||
QWidget::connect(_actions, SIGNAL(currentIndexChanged(int)), this,
|
||||
SLOT(ActionChanged(int)));
|
||||
|
||||
QHBoxLayout *mainLayout = new QHBoxLayout;
|
||||
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
|
||||
{"{{actions}}", _actions},
|
||||
};
|
||||
placeWidgets(obs_module_text("AdvSceneSwitcher.action.streaming.entry"),
|
||||
mainLayout, widgetPlaceholders);
|
||||
setLayout(mainLayout);
|
||||
|
||||
_entryData = entryData;
|
||||
UpdateEntryData();
|
||||
_loading = false;
|
||||
}
|
||||
|
||||
void MacroActionStreamEdit::UpdateEntryData()
|
||||
{
|
||||
if (!_entryData) {
|
||||
return;
|
||||
}
|
||||
_actions->setCurrentIndex(static_cast<int>(_entryData->_action));
|
||||
}
|
||||
|
||||
void MacroActionStreamEdit::ActionChanged(int value)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_action = static_cast<StreamAction>(value);
|
||||
}
|
||||
73
src/macro-action-switch-scene.cpp
Normal file
73
src/macro-action-switch-scene.cpp
Normal file
@@ -0,0 +1,73 @@
|
||||
#include "headers/macro-action-switch-scene.hpp"
|
||||
#include "headers/advanced-scene-switcher.hpp"
|
||||
#include "headers/utility.hpp"
|
||||
|
||||
const int MacroActionSwitchScene::id = 0;
|
||||
|
||||
bool MacroActionSwitchScene::_registered = MacroActionFactory::Register(
|
||||
MacroActionSwitchScene::id,
|
||||
{MacroActionSwitchScene::Create, MacroActionSwitchSceneEdit::Create,
|
||||
"AdvSceneSwitcher.action.switchScene"});
|
||||
|
||||
bool MacroActionSwitchScene::PerformAction()
|
||||
{
|
||||
OBSWeakSource scene = getScene();
|
||||
switchScene({scene, transition, (int)(_duration.seconds * 1000)});
|
||||
return true;
|
||||
}
|
||||
|
||||
void MacroActionSwitchScene::LogAction()
|
||||
{
|
||||
if (switcher->verbose) {
|
||||
logMatch();
|
||||
}
|
||||
}
|
||||
|
||||
bool MacroActionSwitchScene::Save(obs_data_t *obj)
|
||||
{
|
||||
MacroAction::Save(obj);
|
||||
SceneSwitcherEntry::save(obj);
|
||||
_duration.Save(obj);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MacroActionSwitchScene::Load(obs_data_t *obj)
|
||||
{
|
||||
MacroAction::Load(obj);
|
||||
SceneSwitcherEntry::load(obj);
|
||||
_duration.Load(obj);
|
||||
return true;
|
||||
}
|
||||
|
||||
MacroActionSwitchSceneEdit::MacroActionSwitchSceneEdit(
|
||||
QWidget *parent, std::shared_ptr<MacroActionSwitchScene> entryData)
|
||||
: SwitchWidget(parent, entryData.get(), true, true)
|
||||
{
|
||||
_duration = new DurationSelection(parent, false);
|
||||
QWidget::connect(_duration, SIGNAL(DurationChanged(double)), this,
|
||||
SLOT(DurationChanged(double)));
|
||||
|
||||
QHBoxLayout *mainLayout = new QHBoxLayout;
|
||||
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
|
||||
{"{{scenes}}", scenes},
|
||||
{"{{transitions}}", transitions},
|
||||
{"{{duration}}", _duration},
|
||||
};
|
||||
placeWidgets(obs_module_text("AdvSceneSwitcher.action.scene.entry"),
|
||||
mainLayout, widgetPlaceholders);
|
||||
setLayout(mainLayout);
|
||||
|
||||
_entryData = entryData;
|
||||
_duration->SetDuration(_entryData->_duration);
|
||||
SwitchWidget::loading = false;
|
||||
}
|
||||
|
||||
void MacroActionSwitchSceneEdit::DurationChanged(double seconds)
|
||||
{
|
||||
if (SwitchWidget::loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_duration.seconds = seconds;
|
||||
}
|
||||
206
src/macro-action-wait.cpp
Normal file
206
src/macro-action-wait.cpp
Normal file
@@ -0,0 +1,206 @@
|
||||
#include "headers/macro-action-wait.hpp"
|
||||
#include "headers/advanced-scene-switcher.hpp"
|
||||
#include "headers/utility.hpp"
|
||||
|
||||
#include <random>
|
||||
|
||||
const int MacroActionWait::id = 1;
|
||||
|
||||
bool MacroActionWait::_registered = MacroActionFactory::Register(
|
||||
MacroActionWait::id,
|
||||
{MacroActionWait::Create, MacroActionWaitEdit::Create,
|
||||
"AdvSceneSwitcher.action.wait"});
|
||||
|
||||
static std::map<WaitType, std::string> waitTypes = {
|
||||
{WaitType::FIXED, "AdvSceneSwitcher.action.wait.type.fixed"},
|
||||
{WaitType::RANDOM, "AdvSceneSwitcher.action.wait.type.random"},
|
||||
};
|
||||
|
||||
static std::random_device rd;
|
||||
static std::default_random_engine re(rd());
|
||||
|
||||
bool MacroActionWait::PerformAction()
|
||||
{
|
||||
double sleep_duration;
|
||||
if (_waitType == WaitType::FIXED) {
|
||||
sleep_duration = _duration.seconds;
|
||||
} else {
|
||||
double min = (_duration.seconds < _duration2.seconds)
|
||||
? _duration.seconds
|
||||
: _duration2.seconds;
|
||||
double max = (_duration.seconds < _duration2.seconds)
|
||||
? _duration2.seconds
|
||||
: _duration.seconds;
|
||||
std::uniform_real_distribution<double> unif(min, max);
|
||||
sleep_duration = unif(re);
|
||||
}
|
||||
vblog(LOG_INFO, "perform action wait with duration of %f",
|
||||
sleep_duration);
|
||||
|
||||
std::unique_lock<std::mutex> lock(switcher->m);
|
||||
switcher->cv.wait_for(lock, std::chrono::milliseconds((
|
||||
long long)(sleep_duration * 1000)));
|
||||
return !switcher->stop;
|
||||
}
|
||||
|
||||
bool MacroActionWait::Save(obs_data_t *obj)
|
||||
{
|
||||
MacroAction::Save(obj);
|
||||
_duration.Save(obj);
|
||||
_duration2.Save(obj, "seconds2", "displayUnit2");
|
||||
obs_data_set_int(obj, "waitType", static_cast<int>(_waitType));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MacroActionWait::Load(obs_data_t *obj)
|
||||
{
|
||||
MacroAction::Load(obj);
|
||||
_duration.Load(obj);
|
||||
_duration2.Load(obj, "seconds2", "displayUnit2");
|
||||
_waitType = static_cast<WaitType>(obs_data_get_int(obj, "waitType"));
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline void populateTypeSelection(QComboBox *list)
|
||||
{
|
||||
for (auto entry : waitTypes) {
|
||||
list->addItem(obs_module_text(entry.second.c_str()));
|
||||
}
|
||||
}
|
||||
|
||||
MacroActionWaitEdit::MacroActionWaitEdit(
|
||||
QWidget *parent, std::shared_ptr<MacroActionWait> entryData)
|
||||
: QWidget(parent)
|
||||
{
|
||||
_duration = new DurationSelection();
|
||||
_duration2 = new DurationSelection();
|
||||
_waitType = new QComboBox();
|
||||
|
||||
populateTypeSelection(_waitType);
|
||||
|
||||
QWidget::connect(_duration, SIGNAL(DurationChanged(double)), this,
|
||||
SLOT(DurationChanged(double)));
|
||||
QWidget::connect(_duration, SIGNAL(UnitChanged(DurationUnit)), this,
|
||||
SLOT(DurationUnitChanged(DurationUnit)));
|
||||
QWidget::connect(_duration2, SIGNAL(DurationChanged(double)), this,
|
||||
SLOT(Duration2Changed(double)));
|
||||
QWidget::connect(_duration2, SIGNAL(UnitChanged(DurationUnit)), this,
|
||||
SLOT(Duration2UnitChanged(DurationUnit)));
|
||||
QWidget::connect(_waitType, SIGNAL(currentIndexChanged(int)), this,
|
||||
SLOT(TypeChanged(int)));
|
||||
|
||||
_mainLayout = new QHBoxLayout;
|
||||
setLayout(_mainLayout);
|
||||
|
||||
_entryData = entryData;
|
||||
UpdateEntryData();
|
||||
_loading = false;
|
||||
}
|
||||
|
||||
void MacroActionWaitEdit::UpdateEntryData()
|
||||
{
|
||||
if (!_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (_entryData->_waitType == WaitType::FIXED) {
|
||||
SetupFixedDurationEdit();
|
||||
} else {
|
||||
SetupRandomDurationEdit();
|
||||
}
|
||||
|
||||
_duration->SetDuration(_entryData->_duration);
|
||||
_duration2->SetDuration(_entryData->_duration2);
|
||||
_waitType->setCurrentIndex(static_cast<int>(_entryData->_waitType));
|
||||
}
|
||||
|
||||
void MacroActionWaitEdit::SetupFixedDurationEdit()
|
||||
{
|
||||
_mainLayout->removeWidget(_duration);
|
||||
_mainLayout->removeWidget(_duration2);
|
||||
_mainLayout->removeWidget(_waitType);
|
||||
clearLayout(_mainLayout);
|
||||
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
|
||||
{"{{duration}}", _duration},
|
||||
{"{{waitType}}", _waitType},
|
||||
};
|
||||
placeWidgets(
|
||||
obs_module_text("AdvSceneSwitcher.action.wait.entry.fixed"),
|
||||
_mainLayout, widgetPlaceholders);
|
||||
_duration2->hide();
|
||||
}
|
||||
|
||||
void MacroActionWaitEdit::SetupRandomDurationEdit()
|
||||
{
|
||||
_mainLayout->removeWidget(_duration);
|
||||
_mainLayout->removeWidget(_duration2);
|
||||
_mainLayout->removeWidget(_waitType);
|
||||
clearLayout(_mainLayout);
|
||||
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
|
||||
{"{{duration}}", _duration},
|
||||
{"{{duration2}}", _duration2},
|
||||
{"{{waitType}}", _waitType},
|
||||
};
|
||||
placeWidgets(
|
||||
obs_module_text("AdvSceneSwitcher.action.wait.entry.random"),
|
||||
_mainLayout, widgetPlaceholders);
|
||||
_duration2->show();
|
||||
}
|
||||
|
||||
void MacroActionWaitEdit::TypeChanged(int value)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
WaitType type = static_cast<WaitType>(value);
|
||||
|
||||
if (type == WaitType::FIXED) {
|
||||
SetupFixedDurationEdit();
|
||||
} else {
|
||||
SetupRandomDurationEdit();
|
||||
}
|
||||
|
||||
_entryData->_waitType = type;
|
||||
}
|
||||
|
||||
void MacroActionWaitEdit::DurationChanged(double seconds)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_duration.seconds = seconds;
|
||||
}
|
||||
|
||||
void MacroActionWaitEdit::DurationUnitChanged(DurationUnit unit)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_duration.displayUnit = unit;
|
||||
}
|
||||
|
||||
void MacroActionWaitEdit::Duration2Changed(double seconds)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_duration2.seconds = seconds;
|
||||
}
|
||||
|
||||
void MacroActionWaitEdit::Duration2UnitChanged(DurationUnit unit)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_duration2.displayUnit = unit;
|
||||
}
|
||||
239
src/macro-condition-audio.cpp
Normal file
239
src/macro-condition-audio.cpp
Normal file
@@ -0,0 +1,239 @@
|
||||
#include "headers/macro-condition-edit.hpp"
|
||||
#include "headers/macro-condition-audio.hpp"
|
||||
#include "headers/utility.hpp"
|
||||
#include "headers/advanced-scene-switcher.hpp"
|
||||
|
||||
const int MacroConditionAudio::id = 3;
|
||||
|
||||
bool MacroConditionAudio::_registered = MacroConditionFactory::Register(
|
||||
MacroConditionAudio::id,
|
||||
{MacroConditionAudio::Create, MacroConditionAudioEdit::Create,
|
||||
"AdvSceneSwitcher.condition.audio"});
|
||||
|
||||
static std::map<AudioCondition, std::string> audioConditionTypes = {
|
||||
{AudioCondition::ABOVE, "AdvSceneSwitcher.ondition.audio.state.above"},
|
||||
{AudioCondition::BELOW, "AdvSceneSwitcher.ondition.audio.state.below"},
|
||||
};
|
||||
|
||||
MacroConditionAudio::~MacroConditionAudio()
|
||||
{
|
||||
obs_volmeter_remove_callback(_volmeter, SetVolumeLevel, this);
|
||||
obs_volmeter_destroy(_volmeter);
|
||||
}
|
||||
|
||||
bool MacroConditionAudio::CheckCondition()
|
||||
{
|
||||
// peak will have a value from -60 db to 0 db
|
||||
bool volumeThresholdreached = false;
|
||||
|
||||
if (_condition == AudioCondition::ABOVE) {
|
||||
volumeThresholdreached = ((double)_peak + 60) * 1.7 > _volume;
|
||||
} else {
|
||||
volumeThresholdreached = ((double)_peak + 60) * 1.7 < _volume;
|
||||
}
|
||||
|
||||
// Reset for next check
|
||||
_peak = -std::numeric_limits<float>::infinity();
|
||||
|
||||
if (!volumeThresholdreached) {
|
||||
_duration.Reset();
|
||||
return false;
|
||||
}
|
||||
return _duration.DurationReached();
|
||||
}
|
||||
|
||||
bool MacroConditionAudio::Save(obs_data_t *obj)
|
||||
{
|
||||
MacroCondition::Save(obj);
|
||||
obs_data_set_string(obj, "audioSource",
|
||||
GetWeakSourceName(_audioSource).c_str());
|
||||
obs_data_set_int(obj, "volume", _volume);
|
||||
obs_data_set_int(obj, "condition", static_cast<int>(_condition));
|
||||
_duration.Save(obj);
|
||||
return true;
|
||||
}
|
||||
|
||||
obs_volmeter_t *AddVolmeterToSource(MacroConditionAudio *entry,
|
||||
obs_weak_source *source)
|
||||
{
|
||||
obs_volmeter_t *volmeter = obs_volmeter_create(OBS_FADER_LOG);
|
||||
obs_volmeter_add_callback(volmeter, MacroConditionAudio::SetVolumeLevel,
|
||||
entry);
|
||||
obs_source_t *as = obs_weak_source_get_source(source);
|
||||
if (!obs_volmeter_attach_source(volmeter, as)) {
|
||||
const char *name = obs_source_get_name(as);
|
||||
blog(LOG_WARNING, "failed to attach volmeter to source %s",
|
||||
name);
|
||||
}
|
||||
obs_source_release(as);
|
||||
|
||||
return volmeter;
|
||||
}
|
||||
|
||||
bool MacroConditionAudio::Load(obs_data_t *obj)
|
||||
{
|
||||
MacroCondition::Load(obj);
|
||||
const char *audioSourceName = obs_data_get_string(obj, "audioSource");
|
||||
_audioSource = GetWeakSourceByName(audioSourceName);
|
||||
|
||||
_volume = obs_data_get_int(obj, "volume");
|
||||
_condition =
|
||||
static_cast<AudioCondition>(obs_data_get_int(obj, "condition"));
|
||||
_duration.Load(obj);
|
||||
|
||||
_volmeter = AddVolmeterToSource(this, _audioSource);
|
||||
return true;
|
||||
}
|
||||
|
||||
void MacroConditionAudio::SetVolumeLevel(
|
||||
void *data, const float magnitude[MAX_AUDIO_CHANNELS],
|
||||
const float peak[MAX_AUDIO_CHANNELS],
|
||||
const float inputPeak[MAX_AUDIO_CHANNELS])
|
||||
{
|
||||
UNUSED_PARAMETER(magnitude);
|
||||
UNUSED_PARAMETER(inputPeak);
|
||||
MacroConditionAudio *c = static_cast<MacroConditionAudio *>(data);
|
||||
|
||||
for (int i = 0; i < MAX_AUDIO_CHANNELS; i++) {
|
||||
if (peak[i] > c->_peak) {
|
||||
c->_peak = peak[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MacroConditionAudio::ResetVolmeter()
|
||||
{
|
||||
obs_volmeter_remove_callback(_volmeter, SetVolumeLevel, this);
|
||||
obs_volmeter_destroy(_volmeter);
|
||||
|
||||
_volmeter = AddVolmeterToSource(this, _audioSource);
|
||||
}
|
||||
|
||||
static inline void populateConditionSelection(QComboBox *list)
|
||||
{
|
||||
for (auto entry : audioConditionTypes) {
|
||||
list->addItem(obs_module_text(entry.second.c_str()));
|
||||
}
|
||||
}
|
||||
|
||||
MacroConditionAudioEdit::MacroConditionAudioEdit(
|
||||
QWidget *parent, std::shared_ptr<MacroConditionAudio> entryData)
|
||||
: QWidget(parent)
|
||||
{
|
||||
_audioSources = new QComboBox();
|
||||
_condition = new QComboBox();
|
||||
_volume = new QSpinBox();
|
||||
_duration = new DurationSelection(parent, false);
|
||||
|
||||
_volume->setSuffix("%");
|
||||
_volume->setMaximum(100);
|
||||
_volume->setMinimum(0);
|
||||
|
||||
QWidget::connect(_volume, SIGNAL(valueChanged(int)), this,
|
||||
SLOT(VolumeThresholdChanged(int)));
|
||||
QWidget::connect(_condition, SIGNAL(currentIndexChanged(int)), this,
|
||||
SLOT(ConditionChanged(int)));
|
||||
QWidget::connect(_duration, SIGNAL(DurationChanged(double)), this,
|
||||
SLOT(DurationChanged(double)));
|
||||
QWidget::connect(_audioSources,
|
||||
SIGNAL(currentTextChanged(const QString &)), this,
|
||||
SLOT(SourceChanged(const QString &)));
|
||||
|
||||
AdvSceneSwitcher::populateAudioSelection(_audioSources);
|
||||
populateConditionSelection(_condition);
|
||||
|
||||
QHBoxLayout *switchLayout = new QHBoxLayout;
|
||||
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
|
||||
{"{{audioSources}}", _audioSources},
|
||||
{"{{volume}}", _volume},
|
||||
{"{{condition}}", _condition},
|
||||
{"{{duration}}", _duration}};
|
||||
placeWidgets(obs_module_text("AdvSceneSwitcher.condition.audio.entry"),
|
||||
switchLayout, widgetPlaceholders);
|
||||
|
||||
QVBoxLayout *mainLayout = new QVBoxLayout;
|
||||
|
||||
mainLayout->addLayout(switchLayout);
|
||||
|
||||
setLayout(mainLayout);
|
||||
|
||||
_entryData = entryData;
|
||||
UpdateEntryData();
|
||||
_loading = false;
|
||||
}
|
||||
|
||||
void MacroConditionAudioEdit::UpdateVolmeterSource()
|
||||
{
|
||||
delete _volMeter;
|
||||
obs_source_t *soruce =
|
||||
obs_weak_source_get_source(_entryData->_audioSource);
|
||||
_volMeter = new VolControl(soruce);
|
||||
obs_source_release(soruce);
|
||||
|
||||
QLayout *layout = this->layout();
|
||||
layout->addWidget(_volMeter);
|
||||
|
||||
QWidget::connect(_volMeter->GetSlider(), SIGNAL(valueChanged(int)),
|
||||
_volume, SLOT(setValue(int)));
|
||||
QWidget::connect(_volume, SIGNAL(valueChanged(int)),
|
||||
_volMeter->GetSlider(), SLOT(setValue(int)));
|
||||
|
||||
// Slider will default to 0 so set it manually once
|
||||
_volMeter->GetSlider()->setValue(_entryData->_volume);
|
||||
}
|
||||
|
||||
void MacroConditionAudioEdit::SourceChanged(const QString &text)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_audioSource = GetWeakSourceByQString(text);
|
||||
_entryData->ResetVolmeter();
|
||||
UpdateVolmeterSource();
|
||||
}
|
||||
|
||||
void MacroConditionAudioEdit::VolumeThresholdChanged(int vol)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_volume = vol;
|
||||
}
|
||||
|
||||
void MacroConditionAudioEdit::ConditionChanged(int cond)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_condition = static_cast<AudioCondition>(cond);
|
||||
}
|
||||
|
||||
void MacroConditionAudioEdit::DurationChanged(double seconds)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_duration.seconds = seconds;
|
||||
}
|
||||
|
||||
void MacroConditionAudioEdit::UpdateEntryData()
|
||||
{
|
||||
if (!_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
_audioSources->setCurrentText(
|
||||
GetWeakSourceName(_entryData->_audioSource).c_str());
|
||||
_volume->setValue(_entryData->_volume);
|
||||
_condition->setCurrentIndex(static_cast<int>(_entryData->_condition));
|
||||
_duration->SetDuration(_entryData->_duration);
|
||||
UpdateVolmeterSource();
|
||||
}
|
||||
194
src/macro-condition-edit.cpp
Normal file
194
src/macro-condition-edit.cpp
Normal file
@@ -0,0 +1,194 @@
|
||||
#include "headers/macro-condition-edit.hpp"
|
||||
|
||||
std::map<int, MacroConditionInfo> MacroConditionFactory::_methods;
|
||||
|
||||
bool MacroConditionFactory::Register(int id, MacroConditionInfo info)
|
||||
{
|
||||
if (auto it = _methods.find(id); it == _methods.end()) {
|
||||
_methods[id] = info;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
std::shared_ptr<MacroCondition> MacroConditionFactory::Create(const int id)
|
||||
{
|
||||
if (auto it = _methods.find(id); it != _methods.end())
|
||||
return it->second._createFunc();
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
QWidget *
|
||||
MacroConditionFactory::CreateWidget(const int id, QWidget *parent,
|
||||
std::shared_ptr<MacroCondition> cond)
|
||||
{
|
||||
if (auto it = _methods.find(id); it != _methods.end())
|
||||
return it->second._createWidgetFunc(parent, cond);
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static inline void populateLogicSelection(QComboBox *list, bool root = false)
|
||||
{
|
||||
if (root) {
|
||||
for (auto entry : MacroCondition::logicTypes) {
|
||||
if (static_cast<int>(entry.first) < logic_root_offset) {
|
||||
list->addItem(obs_module_text(
|
||||
entry.second._name.c_str()));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (auto entry : MacroCondition::logicTypes) {
|
||||
if (static_cast<int>(entry.first) >=
|
||||
logic_root_offset) {
|
||||
list->addItem(obs_module_text(
|
||||
entry.second._name.c_str()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static inline void populateConditionSelection(QComboBox *list)
|
||||
{
|
||||
for (auto entry : MacroConditionFactory::GetConditionTypes()) {
|
||||
list->addItem(obs_module_text(entry.second._name.c_str()));
|
||||
}
|
||||
}
|
||||
|
||||
MacroConditionEdit::MacroConditionEdit(
|
||||
QWidget *parent, std::shared_ptr<MacroCondition> *entryData, int type,
|
||||
bool root, bool startCollapsed)
|
||||
: QWidget(parent)
|
||||
{
|
||||
_logicSelection = new QComboBox();
|
||||
_conditionSelection = new QComboBox();
|
||||
_section = new Section(300);
|
||||
|
||||
QWidget::connect(_logicSelection, SIGNAL(currentIndexChanged(int)),
|
||||
this, SLOT(LogicSelectionChanged(int)));
|
||||
QWidget::connect(_conditionSelection, SIGNAL(currentIndexChanged(int)),
|
||||
this, SLOT(ConditionSelectionChanged(int)));
|
||||
|
||||
populateLogicSelection(_logicSelection, root);
|
||||
populateConditionSelection(_conditionSelection);
|
||||
|
||||
_section->AddHeaderWidget(_logicSelection);
|
||||
_section->AddHeaderWidget(_conditionSelection);
|
||||
|
||||
QVBoxLayout *mainLayout = new QVBoxLayout;
|
||||
mainLayout->addWidget(_section);
|
||||
setLayout(mainLayout);
|
||||
|
||||
_entryData = entryData;
|
||||
_isRoot = root;
|
||||
UpdateEntryData(type);
|
||||
_loading = false;
|
||||
_section->Collapse(startCollapsed);
|
||||
}
|
||||
|
||||
void MacroConditionEdit::LogicSelectionChanged(int idx)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
LogicType type;
|
||||
if (IsRootNode()) {
|
||||
type = static_cast<LogicType>(idx);
|
||||
} else {
|
||||
type = static_cast<LogicType>(idx + logic_root_offset);
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
(*_entryData)->SetLogicType(type);
|
||||
}
|
||||
|
||||
bool MacroConditionEdit::IsRootNode()
|
||||
{
|
||||
return _isRoot;
|
||||
}
|
||||
|
||||
void MacroConditionEdit::UpdateEntryData(int type)
|
||||
{
|
||||
_conditionSelection->setCurrentIndex(type);
|
||||
auto widget = MacroConditionFactory::CreateWidget(type, window(),
|
||||
*_entryData);
|
||||
auto logic = (*_entryData)->GetLogicType();
|
||||
if (IsRootNode()) {
|
||||
_logicSelection->setCurrentIndex(static_cast<int>(logic));
|
||||
} else {
|
||||
_logicSelection->setCurrentIndex(static_cast<int>(logic) -
|
||||
logic_root_offset);
|
||||
}
|
||||
_section->SetContent(widget);
|
||||
}
|
||||
|
||||
void MacroConditionEdit::Collapse(bool collapsed)
|
||||
{
|
||||
_section->Collapse(collapsed);
|
||||
}
|
||||
|
||||
void MacroConditionEdit::ConditionSelectionChanged(int idx)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
auto logic = (*_entryData)->GetLogicType();
|
||||
_entryData->reset();
|
||||
*_entryData = MacroConditionFactory::Create(idx);
|
||||
(*_entryData)->SetLogicType(logic);
|
||||
auto widget =
|
||||
MacroConditionFactory::CreateWidget(idx, window(), *_entryData);
|
||||
_section->SetContent(widget);
|
||||
_section->Collapse(false);
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::on_conditionAdd_clicked()
|
||||
{
|
||||
auto macro = getSelectedMacro();
|
||||
if (!macro) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
bool root = macro->Conditions().size() == 0;
|
||||
macro->Conditions().emplace_back(MacroConditionFactory::Create(0));
|
||||
auto logic = root ? LogicType::ROOT_NONE : LogicType::NONE;
|
||||
macro->Conditions().back()->SetLogicType(logic);
|
||||
auto newEntry = new MacroConditionEdit(
|
||||
this, ¯o->Conditions().back(), 0, root);
|
||||
ui->macroEditConditionLayout->addWidget(newEntry);
|
||||
ui->macroEditConditionHelp->setVisible(false);
|
||||
newEntry->Collapse(false);
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::on_conditionRemove_clicked()
|
||||
{
|
||||
auto macro = getSelectedMacro();
|
||||
if (!macro) {
|
||||
return;
|
||||
}
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
if (macro->Conditions().empty()) {
|
||||
return;
|
||||
}
|
||||
macro->Conditions().pop_back();
|
||||
|
||||
int count = ui->macroEditConditionLayout->count();
|
||||
auto item = ui->macroEditConditionLayout->takeAt(count - 1);
|
||||
|
||||
if (item) {
|
||||
auto widget = item->widget();
|
||||
if (widget) {
|
||||
widget->setVisible(false);
|
||||
}
|
||||
delete item;
|
||||
}
|
||||
|
||||
if (count == 1) {
|
||||
ui->macroEditConditionHelp->setVisible(true);
|
||||
}
|
||||
}
|
||||
293
src/macro-condition-file.cpp
Normal file
293
src/macro-condition-file.cpp
Normal file
@@ -0,0 +1,293 @@
|
||||
#include "headers/macro-condition-edit.hpp"
|
||||
#include "headers/macro-condition-file.hpp"
|
||||
#include "headers/utility.hpp"
|
||||
#include "headers/advanced-scene-switcher.hpp"
|
||||
#include "headers/curl-helper.hpp"
|
||||
|
||||
#include <QTextStream>
|
||||
#include <QFileDialog>
|
||||
|
||||
const int MacroConditionFile::id = 4;
|
||||
|
||||
bool MacroConditionFile::_registered = MacroConditionFactory::Register(
|
||||
MacroConditionFile::id,
|
||||
{MacroConditionFile::Create, MacroConditionFileEdit::Create,
|
||||
"AdvSceneSwitcher.condition.file"});
|
||||
|
||||
static std::hash<std::string> strHash;
|
||||
|
||||
static size_t WriteCallback(void *contents, size_t size, size_t nmemb,
|
||||
void *userp)
|
||||
{
|
||||
((std::string *)userp)->append((char *)contents, size * nmemb);
|
||||
return size * nmemb;
|
||||
}
|
||||
|
||||
static std::string getRemoteData(std::string &url)
|
||||
{
|
||||
std::string readBuffer;
|
||||
|
||||
if (switcher->curl && f_curl_setopt && f_curl_perform) {
|
||||
f_curl_setopt(switcher->curl, CURLOPT_URL, url.c_str());
|
||||
f_curl_setopt(switcher->curl, CURLOPT_WRITEFUNCTION,
|
||||
WriteCallback);
|
||||
f_curl_setopt(switcher->curl, CURLOPT_WRITEDATA, &readBuffer);
|
||||
f_curl_perform(switcher->curl);
|
||||
}
|
||||
return readBuffer;
|
||||
}
|
||||
|
||||
bool MacroConditionFile::matchFileContent(QString &filedata)
|
||||
{
|
||||
if (_onlyMatchIfChanged) {
|
||||
size_t newHash = strHash(filedata.toUtf8().constData());
|
||||
if (newHash == _lastHash) {
|
||||
return false;
|
||||
}
|
||||
_lastHash = newHash;
|
||||
}
|
||||
|
||||
if (_useRegex) {
|
||||
QRegExp rx(QString::fromStdString(_text));
|
||||
return rx.exactMatch(filedata);
|
||||
}
|
||||
|
||||
QString text = QString::fromStdString(_text);
|
||||
return compareIgnoringLineEnding(text, filedata);
|
||||
}
|
||||
|
||||
bool MacroConditionFile::checkRemoteFileContent()
|
||||
{
|
||||
std::string data = getRemoteData(_file);
|
||||
QString qdata = QString::fromStdString(data);
|
||||
return matchFileContent(qdata);
|
||||
}
|
||||
|
||||
bool MacroConditionFile::checkLocalFileContent()
|
||||
{
|
||||
QString t = QString::fromStdString(_text);
|
||||
QFile file(QString::fromStdString(_file));
|
||||
if (_file.empty() || !file.open(QIODevice::ReadOnly)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (_useTime) {
|
||||
QDateTime newLastMod = QFileInfo(file).lastModified();
|
||||
if (_lastMod == newLastMod) {
|
||||
return false;
|
||||
}
|
||||
_lastMod = newLastMod;
|
||||
}
|
||||
|
||||
QString filedata = QTextStream(&file).readAll();
|
||||
bool match = matchFileContent(filedata);
|
||||
|
||||
file.close();
|
||||
return match;
|
||||
}
|
||||
|
||||
bool MacroConditionFile::CheckCondition()
|
||||
{
|
||||
if (_fileType == FileType::REMOTE) {
|
||||
return checkRemoteFileContent();
|
||||
} else {
|
||||
return checkLocalFileContent();
|
||||
}
|
||||
}
|
||||
|
||||
bool MacroConditionFile::Save(obs_data_t *obj)
|
||||
{
|
||||
MacroCondition::Save(obj);
|
||||
obs_data_set_string(obj, "file", _file.c_str());
|
||||
obs_data_set_string(obj, "text", _text.c_str());
|
||||
obs_data_set_int(obj, "fileType", static_cast<int>(_fileType));
|
||||
obs_data_set_bool(obj, "useRegex", _useRegex);
|
||||
obs_data_set_bool(obj, "useTime", _useTime);
|
||||
obs_data_set_bool(obj, "onlyMatchIfChanged", _onlyMatchIfChanged);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MacroConditionFile::Load(obs_data_t *obj)
|
||||
{
|
||||
MacroCondition::Load(obj);
|
||||
_file = obs_data_get_string(obj, "file");
|
||||
_text = obs_data_get_string(obj, "text");
|
||||
_fileType = static_cast<FileType>(obs_data_get_int(obj, "fileType"));
|
||||
_useRegex = obs_data_get_bool(obj, "useRegex");
|
||||
_useTime = obs_data_get_bool(obj, "useTime");
|
||||
_onlyMatchIfChanged = obs_data_get_bool(obj, "onlyMatchIfChanged");
|
||||
return true;
|
||||
}
|
||||
|
||||
MacroConditionFileEdit::MacroConditionFileEdit(
|
||||
QWidget *parent, std::shared_ptr<MacroConditionFile> entryData)
|
||||
: QWidget(parent)
|
||||
{
|
||||
_fileType = new QComboBox();
|
||||
_filePath = new QLineEdit();
|
||||
_browseButton =
|
||||
new QPushButton(obs_module_text("AdvSceneSwitcher.browse"));
|
||||
_browseButton->setStyleSheet("border:1px solid gray;");
|
||||
_matchText = new QPlainTextEdit();
|
||||
_useRegex = new QCheckBox(
|
||||
obs_module_text("AdvSceneSwitcher.fileTab.useRegExp"));
|
||||
_checkModificationDate = new QCheckBox(obs_module_text(
|
||||
"AdvSceneSwitcher.fileTab.checkfileContentTime"));
|
||||
_checkFileContent = new QCheckBox(
|
||||
obs_module_text("AdvSceneSwitcher.fileTab.checkfileContent"));
|
||||
|
||||
QWidget::connect(_fileType, SIGNAL(currentIndexChanged(int)), this,
|
||||
SLOT(FileTypeChanged(int)));
|
||||
QWidget::connect(_filePath, SIGNAL(editingFinished()), this,
|
||||
SLOT(FilePathChanged()));
|
||||
QWidget::connect(_browseButton, SIGNAL(clicked()), this,
|
||||
SLOT(BrowseButtonClicked()));
|
||||
QWidget::connect(_matchText, SIGNAL(textChanged()), this,
|
||||
SLOT(MatchTextChanged()));
|
||||
QWidget::connect(_useRegex, SIGNAL(stateChanged(int)), this,
|
||||
SLOT(UseRegexChanged(int)));
|
||||
QWidget::connect(_checkModificationDate, SIGNAL(stateChanged(int)),
|
||||
this, SLOT(CheckModificationDateChanged(int)));
|
||||
QWidget::connect(_checkFileContent, SIGNAL(stateChanged(int)), this,
|
||||
SLOT(OnlyMatchIfChangedChanged(int)));
|
||||
|
||||
_fileType->addItem(obs_module_text("AdvSceneSwitcher.fileTab.local"));
|
||||
_fileType->addItem(obs_module_text("AdvSceneSwitcher.fileTab.remote"));
|
||||
|
||||
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
|
||||
{"{{fileType}}", _fileType},
|
||||
{"{{filePath}}", _filePath},
|
||||
{"{{browseButton}}", _browseButton},
|
||||
{"{{matchText}}", _matchText},
|
||||
{"{{useRegex}}", _useRegex},
|
||||
{"{{checkModificationDate}}", _checkModificationDate},
|
||||
{"{{checkFileContent}}", _checkFileContent},
|
||||
};
|
||||
|
||||
QVBoxLayout *mainLayout = new QVBoxLayout;
|
||||
QHBoxLayout *line1Layout = new QHBoxLayout;
|
||||
QHBoxLayout *line2Layout = new QHBoxLayout;
|
||||
QHBoxLayout *line3Layout = new QHBoxLayout;
|
||||
placeWidgets(
|
||||
obs_module_text("AdvSceneSwitcher.condition.file.entry.line1"),
|
||||
line1Layout, widgetPlaceholders);
|
||||
placeWidgets(
|
||||
obs_module_text("AdvSceneSwitcher.condition.file.entry.line2"),
|
||||
line2Layout, widgetPlaceholders, false);
|
||||
placeWidgets(
|
||||
obs_module_text("AdvSceneSwitcher.condition.file.entry.line3"),
|
||||
line3Layout, widgetPlaceholders);
|
||||
mainLayout->addLayout(line1Layout);
|
||||
mainLayout->addLayout(line2Layout);
|
||||
mainLayout->addLayout(line3Layout);
|
||||
|
||||
setLayout(mainLayout);
|
||||
|
||||
_entryData = entryData;
|
||||
UpdateEntryData();
|
||||
_loading = false;
|
||||
}
|
||||
|
||||
void MacroConditionFileEdit::UpdateEntryData()
|
||||
{
|
||||
if (!_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
_fileType->setCurrentIndex(static_cast<int>(_entryData->_fileType));
|
||||
|
||||
_filePath->setText(QString::fromStdString(_entryData->_file));
|
||||
_matchText->setPlainText(QString::fromStdString(_entryData->_text));
|
||||
_useRegex->setChecked(_entryData->_useRegex);
|
||||
_checkModificationDate->setChecked(_entryData->_useTime);
|
||||
_checkFileContent->setChecked(_entryData->_onlyMatchIfChanged);
|
||||
}
|
||||
|
||||
void MacroConditionFileEdit::FileTypeChanged(int index)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
FileType type = static_cast<FileType>(index);
|
||||
|
||||
if (type == FileType::LOCAL) {
|
||||
_browseButton->setDisabled(false);
|
||||
_checkModificationDate->setDisabled(false);
|
||||
} else {
|
||||
_browseButton->setDisabled(true);
|
||||
_checkModificationDate->setDisabled(true);
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_fileType = type;
|
||||
}
|
||||
|
||||
void MacroConditionFileEdit::FilePathChanged()
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_file = _filePath->text().toUtf8().constData();
|
||||
}
|
||||
|
||||
void MacroConditionFileEdit::BrowseButtonClicked()
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
QString path = QFileDialog::getOpenFileName(
|
||||
this,
|
||||
tr(obs_module_text("AdvSceneSwitcher.fileTab.selectRead")),
|
||||
QDir::currentPath(),
|
||||
tr(obs_module_text("AdvSceneSwitcher.fileTab.anyFileType")));
|
||||
if (path.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
_filePath->setText(path);
|
||||
FilePathChanged();
|
||||
}
|
||||
|
||||
void MacroConditionFileEdit::MatchTextChanged()
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_text = _matchText->toPlainText().toUtf8().constData();
|
||||
}
|
||||
|
||||
void MacroConditionFileEdit::UseRegexChanged(int state)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_useRegex = state;
|
||||
}
|
||||
|
||||
void MacroConditionFileEdit::CheckModificationDateChanged(int state)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_useTime = state;
|
||||
}
|
||||
|
||||
void MacroConditionFileEdit::OnlyMatchIfChangedChanged(int state)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_onlyMatchIfChanged = state;
|
||||
}
|
||||
83
src/macro-condition-idle.cpp
Normal file
83
src/macro-condition-idle.cpp
Normal file
@@ -0,0 +1,83 @@
|
||||
#include "headers/macro-condition-edit.hpp"
|
||||
#include "headers/macro-condition-idle.hpp"
|
||||
#include "headers/utility.hpp"
|
||||
#include "headers/advanced-scene-switcher.hpp"
|
||||
|
||||
const int MacroConditionIdle::id = 10;
|
||||
|
||||
bool MacroConditionIdle::_registered = MacroConditionFactory::Register(
|
||||
MacroConditionIdle::id,
|
||||
{MacroConditionIdle::Create, MacroConditionIdleEdit::Create,
|
||||
"AdvSceneSwitcher.condition.idle"});
|
||||
|
||||
bool MacroConditionIdle::CheckCondition()
|
||||
{
|
||||
return secondsSinceLastInput() >= _duration.seconds;
|
||||
}
|
||||
|
||||
bool MacroConditionIdle::Save(obs_data_t *obj)
|
||||
{
|
||||
MacroCondition::Save(obj);
|
||||
_duration.Save(obj);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MacroConditionIdle::Load(obs_data_t *obj)
|
||||
{
|
||||
MacroCondition::Load(obj);
|
||||
_duration.Load(obj);
|
||||
return true;
|
||||
}
|
||||
|
||||
MacroConditionIdleEdit::MacroConditionIdleEdit(
|
||||
QWidget *parent, std::shared_ptr<MacroConditionIdle> entryData)
|
||||
: QWidget(parent)
|
||||
{
|
||||
_duration = new DurationSelection();
|
||||
|
||||
QWidget::connect(_duration, SIGNAL(DurationChanged(double)), this,
|
||||
SLOT(DurationChanged(double)));
|
||||
QWidget::connect(_duration, SIGNAL(UnitChanged(DurationUnit)), this,
|
||||
SLOT(DurationUnitChanged(DurationUnit)));
|
||||
|
||||
QHBoxLayout *mainLayout = new QHBoxLayout;
|
||||
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
|
||||
{"{{duration}}", _duration},
|
||||
};
|
||||
placeWidgets(obs_module_text("AdvSceneSwitcher.condition.idle.entry"),
|
||||
mainLayout, widgetPlaceholders);
|
||||
setLayout(mainLayout);
|
||||
|
||||
_entryData = entryData;
|
||||
UpdateEntryData();
|
||||
_loading = false;
|
||||
}
|
||||
|
||||
void MacroConditionIdleEdit::DurationChanged(double seconds)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_duration.seconds = seconds;
|
||||
}
|
||||
|
||||
void MacroConditionIdleEdit::DurationUnitChanged(DurationUnit unit)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_duration.displayUnit = unit;
|
||||
}
|
||||
|
||||
void MacroConditionIdleEdit::UpdateEntryData()
|
||||
{
|
||||
if (!_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
_duration->SetDuration(_entryData->_duration);
|
||||
}
|
||||
376
src/macro-condition-media.cpp
Normal file
376
src/macro-condition-media.cpp
Normal file
@@ -0,0 +1,376 @@
|
||||
#include "headers/macro-condition-edit.hpp"
|
||||
#include "headers/macro-condition-media.hpp"
|
||||
#include "headers/utility.hpp"
|
||||
#include "headers/advanced-scene-switcher.hpp"
|
||||
|
||||
const int MacroConditionMedia::id = 5;
|
||||
|
||||
bool MacroConditionMedia::_registered = MacroConditionFactory::Register(
|
||||
MacroConditionMedia::id,
|
||||
{MacroConditionMedia::Create, MacroConditionMediaEdit::Create,
|
||||
"AdvSceneSwitcher.condition.media"});
|
||||
|
||||
static std::map<MediaTimeRestriction, std::string> mediaTimeRestrictions = {
|
||||
{MediaTimeRestriction::TIME_RESTRICTION_NONE,
|
||||
"AdvSceneSwitcher.mediaTab.timeRestriction.none"},
|
||||
{MediaTimeRestriction::TIME_RESTRICTION_SHORTER,
|
||||
"AdvSceneSwitcher.mediaTab.timeRestriction.shorter"},
|
||||
{MediaTimeRestriction::TIME_RESTRICTION_LONGER,
|
||||
"AdvSceneSwitcher.mediaTab.timeRestriction.longer"},
|
||||
{MediaTimeRestriction::TIME_RESTRICTION_REMAINING_SHORTER,
|
||||
"AdvSceneSwitcher.mediaTab.timeRestriction.remainShorter"},
|
||||
{MediaTimeRestriction::TIME_RESTRICTION_REMAINING_LONGER,
|
||||
"AdvSceneSwitcher.mediaTab.timeRestriction.remainLonger"},
|
||||
};
|
||||
|
||||
static std::map<MediaState, std::string> mediaStates = {
|
||||
{MediaState::OBS_MEDIA_STATE_NONE,
|
||||
"AdvSceneSwitcher.mediaTab.states.none"},
|
||||
{MediaState::OBS_MEDIA_STATE_PLAYING,
|
||||
"AdvSceneSwitcher.mediaTab.states.playing"},
|
||||
{MediaState::OBS_MEDIA_STATE_OPENING,
|
||||
"AdvSceneSwitcher.mediaTab.states.opening"},
|
||||
{MediaState::OBS_MEDIA_STATE_BUFFERING,
|
||||
"AdvSceneSwitcher.mediaTab.states.buffering"},
|
||||
{MediaState::OBS_MEDIA_STATE_PAUSED,
|
||||
"AdvSceneSwitcher.mediaTab.states.paused"},
|
||||
{MediaState::OBS_MEDIA_STATE_STOPPED,
|
||||
"AdvSceneSwitcher.mediaTab.states.stopped"},
|
||||
{MediaState::OBS_MEDIA_STATE_ENDED,
|
||||
"AdvSceneSwitcher.mediaTab.states.ended"},
|
||||
{MediaState::OBS_MEDIA_STATE_ERROR,
|
||||
"AdvSceneSwitcher.mediaTab.states.error"},
|
||||
{MediaState::PLAYED_TO_END,
|
||||
"AdvSceneSwitcher.mediaTab.states.playedToEnd"},
|
||||
{MediaState::ANY, "AdvSceneSwitcher.mediaTab.states.any"},
|
||||
};
|
||||
|
||||
MacroConditionMedia::~MacroConditionMedia()
|
||||
{
|
||||
obs_source_t *mediasource = obs_weak_source_get_source(_source);
|
||||
signal_handler_t *sh = obs_source_get_signal_handler(mediasource);
|
||||
signal_handler_disconnect(sh, "media_stopped", MediaStopped, this);
|
||||
signal_handler_disconnect(sh, "media_ended", MediaEnded, this);
|
||||
obs_source_release(mediasource);
|
||||
}
|
||||
|
||||
bool matchTime(const int64_t currentTime, const int64_t duration,
|
||||
const MediaTimeRestriction restriction, const int64_t time)
|
||||
{
|
||||
bool matchedTimeNone =
|
||||
(restriction == MediaTimeRestriction::TIME_RESTRICTION_NONE);
|
||||
bool matchedTimeLonger =
|
||||
(restriction ==
|
||||
MediaTimeRestriction::TIME_RESTRICTION_LONGER) &&
|
||||
(currentTime > time);
|
||||
bool matchedTimeShorter =
|
||||
(restriction ==
|
||||
MediaTimeRestriction::TIME_RESTRICTION_SHORTER) &&
|
||||
(currentTime < time);
|
||||
bool matchedTimeRemainLonger =
|
||||
(restriction ==
|
||||
MediaTimeRestriction::TIME_RESTRICTION_REMAINING_LONGER) &&
|
||||
(duration > currentTime && duration - currentTime > time);
|
||||
bool matchedTimeRemainShorter =
|
||||
(restriction ==
|
||||
MediaTimeRestriction::TIME_RESTRICTION_REMAINING_SHORTER) &&
|
||||
(duration > currentTime && duration - currentTime < time);
|
||||
|
||||
return matchedTimeNone || matchedTimeLonger || matchedTimeShorter ||
|
||||
matchedTimeRemainLonger || matchedTimeRemainShorter;
|
||||
}
|
||||
|
||||
bool MacroConditionMedia::CheckCondition()
|
||||
{
|
||||
if (!_source) {
|
||||
return false;
|
||||
}
|
||||
bool match = false;
|
||||
|
||||
obs_source_t *source = obs_weak_source_get_source(_source);
|
||||
auto duration = obs_source_media_get_duration(source);
|
||||
auto time = obs_source_media_get_time(source);
|
||||
obs_media_state currentState = obs_source_media_get_state(source);
|
||||
obs_source_release(source);
|
||||
|
||||
// To be able to compare to obs_media_state more easily
|
||||
int expectedState = static_cast<int>(_state);
|
||||
|
||||
bool matchedStopped = expectedState == OBS_MEDIA_STATE_STOPPED &&
|
||||
_stopped;
|
||||
|
||||
// The signal for the state ended is intentionally not used here
|
||||
// so matchedEnded can be used to specify the end of a VLC playlist
|
||||
// by two consequtive matches of OBS_MEDIA_STATE_ENDED
|
||||
//
|
||||
// This was done to reduce the likelyhood of interpreting a single
|
||||
// OBS_MEDIA_STATE_ENDED caught by obs_source_media_get_state()
|
||||
// as the end of the playlist of the VLC source, while actually being
|
||||
// in the middle of switching to the next item of the playlist
|
||||
//
|
||||
// If there is a separate obs_media_sate in the future for the
|
||||
// "end of playlist reached" signal the line below can be used
|
||||
// and an additional check for this new singal can be introduced
|
||||
//
|
||||
// bool matchedEnded = _state == OBS_MEDIA_STATE_ENDED && _ended;
|
||||
|
||||
bool ended = false;
|
||||
|
||||
if (currentState == OBS_MEDIA_STATE_ENDED) {
|
||||
ended = _previousStateEnded;
|
||||
_previousStateEnded = true;
|
||||
} else {
|
||||
_previousStateEnded = false;
|
||||
}
|
||||
|
||||
bool matchedEnded = ended && (expectedState == OBS_MEDIA_STATE_ENDED);
|
||||
|
||||
// match if playedToEnd was true in last interval
|
||||
// and playback is currently ended
|
||||
bool matchedPlayedToEnd = _state == MediaState::PLAYED_TO_END &&
|
||||
_playedToEnd && ended;
|
||||
|
||||
// interval * 2 to make sure not to miss any state changes
|
||||
// which happened during check of the conditions
|
||||
_playedToEnd = _playedToEnd ||
|
||||
(duration - time <= switcher->interval * 2);
|
||||
|
||||
// reset for next check
|
||||
if (ended) {
|
||||
_playedToEnd = false;
|
||||
}
|
||||
_stopped = false;
|
||||
_ended = false;
|
||||
|
||||
bool matchedState =
|
||||
(currentState == expectedState || _state == MediaState::ANY) ||
|
||||
matchedStopped || matchedEnded || matchedPlayedToEnd;
|
||||
|
||||
bool matchedTime =
|
||||
matchTime(time, duration, _restriction, _time.seconds * 1000);
|
||||
bool matched = matchedState && matchedTime;
|
||||
|
||||
if (matched && !_alreadyMatched) {
|
||||
match = true;
|
||||
}
|
||||
|
||||
_alreadyMatched = matched;
|
||||
|
||||
return match;
|
||||
}
|
||||
|
||||
bool MacroConditionMedia::Save(obs_data_t *obj)
|
||||
{
|
||||
MacroCondition::Save(obj);
|
||||
obs_data_set_string(obj, "source", GetWeakSourceName(_source).c_str());
|
||||
|
||||
obs_data_set_int(obj, "state", static_cast<int>(_state));
|
||||
obs_data_set_int(obj, "restriction", static_cast<int>(_restriction));
|
||||
_time.Save(obj);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MacroConditionMedia::Load(obs_data_t *obj)
|
||||
{
|
||||
MacroCondition::Load(obj);
|
||||
const char *sourceName = obs_data_get_string(obj, "source");
|
||||
_source = GetWeakSourceByName(sourceName);
|
||||
|
||||
_state = static_cast<MediaState>(obs_data_get_int(obj, "state"));
|
||||
_restriction = static_cast<MediaTimeRestriction>(
|
||||
obs_data_get_int(obj, "restriction"));
|
||||
_time.Load(obj);
|
||||
|
||||
obs_source_t *mediasource = obs_weak_source_get_source(_source);
|
||||
signal_handler_t *sh = obs_source_get_signal_handler(mediasource);
|
||||
signal_handler_connect(sh, "media_stopped", MediaStopped, this);
|
||||
signal_handler_connect(sh, "media_ended", MediaEnded, this);
|
||||
obs_source_release(mediasource);
|
||||
return true;
|
||||
}
|
||||
|
||||
void MacroConditionMedia::ClearSignalHandler()
|
||||
{
|
||||
obs_source_t *mediasource = obs_weak_source_get_source(_source);
|
||||
signal_handler_t *sh = obs_source_get_signal_handler(mediasource);
|
||||
signal_handler_disconnect(sh, "media_stopped", MediaStopped, this);
|
||||
signal_handler_disconnect(sh, "media_ended", MediaEnded, this);
|
||||
obs_source_release(mediasource);
|
||||
}
|
||||
|
||||
void MacroConditionMedia::ResetSignalHandler()
|
||||
{
|
||||
obs_source_t *mediasource = obs_weak_source_get_source(_source);
|
||||
signal_handler_t *sh = obs_source_get_signal_handler(mediasource);
|
||||
signal_handler_disconnect(sh, "media_stopped", MediaStopped, this);
|
||||
signal_handler_disconnect(sh, "media_ended", MediaEnded, this);
|
||||
signal_handler_connect(sh, "media_stopped", MediaStopped, this);
|
||||
signal_handler_connect(sh, "media_ended", MediaEnded, this);
|
||||
obs_source_release(mediasource);
|
||||
}
|
||||
|
||||
void MacroConditionMedia::MediaStopped(void *data, calldata_t *)
|
||||
{
|
||||
MacroConditionMedia *media = static_cast<MacroConditionMedia *>(data);
|
||||
media->_stopped = true;
|
||||
}
|
||||
|
||||
void MacroConditionMedia::MediaEnded(void *data, calldata_t *)
|
||||
{
|
||||
MacroConditionMedia *media = static_cast<MacroConditionMedia *>(data);
|
||||
media->_ended = true;
|
||||
}
|
||||
|
||||
static void populateMediaTimeRestrictions(QComboBox *list)
|
||||
{
|
||||
for (auto entry : mediaTimeRestrictions) {
|
||||
list->addItem(obs_module_text(entry.second.c_str()));
|
||||
}
|
||||
}
|
||||
|
||||
static void populateMediaStates(QComboBox *list)
|
||||
{
|
||||
for (auto entry : mediaStates) {
|
||||
list->addItem(obs_module_text(entry.second.c_str()));
|
||||
}
|
||||
}
|
||||
|
||||
MacroConditionMediaEdit::MacroConditionMediaEdit(
|
||||
QWidget *parent, std::shared_ptr<MacroConditionMedia> entryData)
|
||||
: QWidget(parent)
|
||||
{
|
||||
_mediaSources = new QComboBox();
|
||||
_states = new QComboBox();
|
||||
_timeRestrictions = new QComboBox();
|
||||
_time = new DurationSelection();
|
||||
|
||||
QWidget::connect(_mediaSources,
|
||||
SIGNAL(currentTextChanged(const QString &)), this,
|
||||
SLOT(SourceChanged(const QString &)));
|
||||
QWidget::connect(_states, SIGNAL(currentIndexChanged(int)), this,
|
||||
SLOT(StateChanged(int)));
|
||||
QWidget::connect(_timeRestrictions, SIGNAL(currentIndexChanged(int)),
|
||||
this, SLOT(TimeRestrictionChanged(int)));
|
||||
QWidget::connect(_time, SIGNAL(DurationChanged(double)), this,
|
||||
SLOT(TimeChanged(double)));
|
||||
QWidget::connect(_time, SIGNAL(UnitChanged(DurationUnit)), this,
|
||||
SLOT(TimeUnitChanged(DurationUnit)));
|
||||
|
||||
AdvSceneSwitcher::populateMediaSelection(_mediaSources);
|
||||
populateMediaStates(_states);
|
||||
populateMediaTimeRestrictions(_timeRestrictions);
|
||||
|
||||
QHBoxLayout *mainLayout = new QHBoxLayout;
|
||||
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
|
||||
{"{{mediaSources}}", _mediaSources},
|
||||
{"{{states}}", _states},
|
||||
{"{{timeRestrictions}}", _timeRestrictions},
|
||||
{"{{time}}", _time},
|
||||
};
|
||||
placeWidgets(obs_module_text("AdvSceneSwitcher.condition.media.entry"),
|
||||
mainLayout, widgetPlaceholders);
|
||||
setLayout(mainLayout);
|
||||
|
||||
_entryData = entryData;
|
||||
UpdateEntryData();
|
||||
_loading = false;
|
||||
}
|
||||
|
||||
void MacroConditionMediaEdit::SourceChanged(const QString &text)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->ClearSignalHandler();
|
||||
_entryData->_source = GetWeakSourceByQString(text);
|
||||
_entryData->ResetSignalHandler();
|
||||
}
|
||||
|
||||
MediaState getMediaStateFromIdx(int idx)
|
||||
{
|
||||
if (idx < static_cast<int>(MediaState::LAST_OBS_MEDIA_STATE)) {
|
||||
return static_cast<MediaState>(idx);
|
||||
} else {
|
||||
return static_cast<MediaState>(
|
||||
idx -
|
||||
static_cast<int>(MediaState::LAST_OBS_MEDIA_STATE) +
|
||||
custom_media_states_offset);
|
||||
}
|
||||
}
|
||||
|
||||
void MacroConditionMediaEdit::StateChanged(int index)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_state = getMediaStateFromIdx(index);
|
||||
}
|
||||
|
||||
void MacroConditionMediaEdit::TimeRestrictionChanged(int index)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (static_cast<MediaTimeRestriction>(index) ==
|
||||
MediaTimeRestriction::TIME_RESTRICTION_NONE) {
|
||||
_time->setDisabled(true);
|
||||
} else {
|
||||
_time->setDisabled(false);
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_restriction = static_cast<MediaTimeRestriction>(index);
|
||||
}
|
||||
|
||||
void MacroConditionMediaEdit::TimeChanged(double seconds)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_time.seconds = seconds;
|
||||
}
|
||||
|
||||
void MacroConditionMediaEdit::TimeUnitChanged(DurationUnit unit)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_time.displayUnit = unit;
|
||||
}
|
||||
|
||||
int getIdxFromMediaState(MediaState state)
|
||||
{
|
||||
if (state < MediaState::LAST_OBS_MEDIA_STATE) {
|
||||
return static_cast<int>(state);
|
||||
} else {
|
||||
return static_cast<int>(state) - custom_media_states_offset +
|
||||
static_cast<int>(MediaState::LAST_OBS_MEDIA_STATE);
|
||||
}
|
||||
}
|
||||
|
||||
void MacroConditionMediaEdit::UpdateEntryData()
|
||||
{
|
||||
if (!_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
_mediaSources->setCurrentText(
|
||||
GetWeakSourceName(_entryData->_source).c_str());
|
||||
_states->setCurrentIndex(getIdxFromMediaState(_entryData->_state));
|
||||
_timeRestrictions->setCurrentIndex(
|
||||
static_cast<int>(_entryData->_restriction));
|
||||
_time->SetDuration(_entryData->_time);
|
||||
if (_entryData->_restriction ==
|
||||
MediaTimeRestriction::TIME_RESTRICTION_NONE) {
|
||||
_time->setDisabled(true);
|
||||
}
|
||||
}
|
||||
92
src/macro-condition-plugin-state.cpp
Normal file
92
src/macro-condition-plugin-state.cpp
Normal file
@@ -0,0 +1,92 @@
|
||||
#include "headers/macro-condition-edit.hpp"
|
||||
#include "headers/macro-condition-plugin-state.hpp"
|
||||
#include "headers/utility.hpp"
|
||||
#include "headers/advanced-scene-switcher.hpp"
|
||||
|
||||
const int MacroConditionPluginState::id = 11;
|
||||
|
||||
bool MacroConditionPluginState::_registered = MacroConditionFactory::Register(
|
||||
MacroConditionPluginState::id,
|
||||
{MacroConditionPluginState::Create,
|
||||
MacroConditionPluginStateEdit::Create,
|
||||
"AdvSceneSwitcher.condition.pluginState"});
|
||||
|
||||
static std::map<PluginStateCondition, std::string> pluginStateConditionTypes = {
|
||||
{PluginStateCondition::SCENESWITCHED,
|
||||
"AdvSceneSwitcher.condition.pluginState.state.sceneSwitched"},
|
||||
};
|
||||
|
||||
bool MacroConditionPluginState::CheckCondition()
|
||||
{
|
||||
return switcher->macroSceneSwitched;
|
||||
}
|
||||
|
||||
bool MacroConditionPluginState::Save(obs_data_t *obj)
|
||||
{
|
||||
MacroCondition::Save(obj);
|
||||
obs_data_set_int(obj, "condition", static_cast<int>(_condition));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MacroConditionPluginState::Load(obs_data_t *obj)
|
||||
{
|
||||
MacroCondition::Load(obj);
|
||||
_condition = static_cast<PluginStateCondition>(
|
||||
obs_data_get_int(obj, "condition"));
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline void populateConditionSelection(QComboBox *list)
|
||||
{
|
||||
for (auto entry : pluginStateConditionTypes) {
|
||||
list->addItem(obs_module_text(entry.second.c_str()));
|
||||
}
|
||||
}
|
||||
|
||||
MacroConditionPluginStateEdit::MacroConditionPluginStateEdit(
|
||||
QWidget *parent, std::shared_ptr<MacroConditionPluginState> entryData)
|
||||
: QWidget(parent)
|
||||
{
|
||||
_condition = new QComboBox();
|
||||
|
||||
QWidget::connect(_condition, SIGNAL(currentIndexChanged(int)), this,
|
||||
SLOT(ConditionChanged(int)));
|
||||
populateConditionSelection(_condition);
|
||||
|
||||
QHBoxLayout *switchLayout = new QHBoxLayout;
|
||||
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
|
||||
{"{{condition}}", _condition},
|
||||
};
|
||||
placeWidgets(
|
||||
obs_module_text("AdvSceneSwitcher.condition.pluginState.entry"),
|
||||
switchLayout, widgetPlaceholders);
|
||||
|
||||
QVBoxLayout *mainLayout = new QVBoxLayout;
|
||||
|
||||
mainLayout->addLayout(switchLayout);
|
||||
|
||||
setLayout(mainLayout);
|
||||
|
||||
_entryData = entryData;
|
||||
UpdateEntryData();
|
||||
_loading = false;
|
||||
}
|
||||
|
||||
void MacroConditionPluginStateEdit::ConditionChanged(int cond)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_condition = static_cast<PluginStateCondition>(cond);
|
||||
}
|
||||
|
||||
void MacroConditionPluginStateEdit::UpdateEntryData()
|
||||
{
|
||||
if (!_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
_condition->setCurrentIndex(static_cast<int>(_entryData->_condition));
|
||||
}
|
||||
110
src/macro-condition-process.cpp
Normal file
110
src/macro-condition-process.cpp
Normal file
@@ -0,0 +1,110 @@
|
||||
#include "headers/macro-condition-edit.hpp"
|
||||
#include "headers/macro-condition-process.hpp"
|
||||
#include "headers/utility.hpp"
|
||||
#include "headers/advanced-scene-switcher.hpp"
|
||||
|
||||
#include <regex>
|
||||
|
||||
const int MacroConditionProcess::id = 9;
|
||||
|
||||
bool MacroConditionProcess::_registered = MacroConditionFactory::Register(
|
||||
MacroConditionProcess::id,
|
||||
{MacroConditionProcess::Create, MacroConditionProcessEdit::Create,
|
||||
"AdvSceneSwitcher.condition.process"});
|
||||
|
||||
bool MacroConditionProcess::CheckCondition()
|
||||
{
|
||||
std::string title;
|
||||
QStringList runningProcesses;
|
||||
|
||||
QString proc = QString::fromStdString(_process);
|
||||
|
||||
GetCurrentWindowTitle(title);
|
||||
GetProcessList(runningProcesses);
|
||||
|
||||
bool equals = runningProcesses.contains(proc);
|
||||
bool matches = runningProcesses.indexOf(QRegularExpression(proc)) != -1;
|
||||
bool focus = !_focus || isInFocus(proc);
|
||||
|
||||
return (equals || matches) && focus;
|
||||
}
|
||||
|
||||
bool MacroConditionProcess::Save(obs_data_t *obj)
|
||||
{
|
||||
MacroCondition::Save(obj);
|
||||
obs_data_set_string(obj, "process", _process.c_str());
|
||||
obs_data_set_bool(obj, "focus", _focus);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MacroConditionProcess::Load(obs_data_t *obj)
|
||||
{
|
||||
MacroCondition::Load(obj);
|
||||
_process = obs_data_get_string(obj, "process");
|
||||
_focus = obs_data_get_bool(obj, "focus");
|
||||
return true;
|
||||
}
|
||||
|
||||
MacroConditionProcessEdit::MacroConditionProcessEdit(
|
||||
QWidget *parent, std::shared_ptr<MacroConditionProcess> entryData)
|
||||
: QWidget(parent)
|
||||
{
|
||||
_processSelection = new QComboBox();
|
||||
_processSelection->setEditable(true);
|
||||
_processSelection->setMaxVisibleItems(20);
|
||||
|
||||
_focused = new QCheckBox();
|
||||
|
||||
QWidget::connect(_processSelection,
|
||||
SIGNAL(currentTextChanged(const QString &)), this,
|
||||
SLOT(ProcessChanged(const QString &)));
|
||||
QWidget::connect(_focused, SIGNAL(stateChanged(int)), this,
|
||||
SLOT(FocusChanged(int)));
|
||||
|
||||
AdvSceneSwitcher::populateProcessSelection(_processSelection);
|
||||
|
||||
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
|
||||
{"{{processes}}", _processSelection},
|
||||
{"{{focused}}", _focused},
|
||||
};
|
||||
|
||||
QHBoxLayout *mainLayout = new QHBoxLayout;
|
||||
placeWidgets(
|
||||
obs_module_text("AdvSceneSwitcher.condition.process.entry"),
|
||||
mainLayout, widgetPlaceholders);
|
||||
setLayout(mainLayout);
|
||||
|
||||
_entryData = entryData;
|
||||
UpdateEntryData();
|
||||
_loading = false;
|
||||
}
|
||||
|
||||
void MacroConditionProcessEdit::ProcessChanged(const QString &text)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_process = text.toStdString();
|
||||
}
|
||||
|
||||
void MacroConditionProcessEdit::FocusChanged(int state)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_focus = state;
|
||||
}
|
||||
|
||||
void MacroConditionProcessEdit::UpdateEntryData()
|
||||
{
|
||||
if (!_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
_processSelection->setCurrentText(_entryData->_process.c_str());
|
||||
_focused->setChecked(_entryData->_focus);
|
||||
}
|
||||
138
src/macro-condition-recording.cpp
Normal file
138
src/macro-condition-recording.cpp
Normal file
@@ -0,0 +1,138 @@
|
||||
#include "headers/macro-condition-edit.hpp"
|
||||
#include "headers/macro-condition-recording.hpp"
|
||||
#include "headers/utility.hpp"
|
||||
#include "headers/advanced-scene-switcher.hpp"
|
||||
|
||||
const int MacroConditionRecord::id = 8;
|
||||
|
||||
bool MacroConditionRecord::_registered = MacroConditionFactory::Register(
|
||||
MacroConditionRecord::id,
|
||||
{MacroConditionRecord::Create, MacroConditionRecordEdit::Create,
|
||||
"AdvSceneSwitcher.condition.record"});
|
||||
|
||||
static std::map<RecordState, std::string> recordStates = {
|
||||
{RecordState::STOP, "AdvSceneSwitcher.condition.record.state.stop"},
|
||||
{RecordState::PAUSE, "AdvSceneSwitcher.condition.record.state.pause"},
|
||||
{RecordState::START, "AdvSceneSwitcher.condition.record.state.start"},
|
||||
};
|
||||
|
||||
bool MacroConditionRecord::CheckCondition()
|
||||
{
|
||||
bool stateMatch = false;
|
||||
switch (_recordState) {
|
||||
case RecordState::STOP:
|
||||
stateMatch = !obs_frontend_recording_active();
|
||||
break;
|
||||
case RecordState::PAUSE:
|
||||
stateMatch = obs_frontend_recording_paused();
|
||||
break;
|
||||
case RecordState::START:
|
||||
stateMatch = obs_frontend_recording_active();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (!stateMatch) {
|
||||
_duration.Reset();
|
||||
return false;
|
||||
}
|
||||
|
||||
return _duration.DurationReached();
|
||||
}
|
||||
|
||||
bool MacroConditionRecord::Save(obs_data_t *obj)
|
||||
{
|
||||
MacroCondition::Save(obj);
|
||||
obs_data_set_int(obj, "state", static_cast<int>(_recordState));
|
||||
_duration.Save(obj);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MacroConditionRecord::Load(obs_data_t *obj)
|
||||
{
|
||||
MacroCondition::Load(obj);
|
||||
_recordState = static_cast<RecordState>(obs_data_get_int(obj, "state"));
|
||||
_duration.Load(obj);
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline void populateStateSelection(QComboBox *list)
|
||||
{
|
||||
for (auto entry : recordStates) {
|
||||
list->addItem(obs_module_text(entry.second.c_str()));
|
||||
}
|
||||
}
|
||||
|
||||
MacroConditionRecordEdit::MacroConditionRecordEdit(
|
||||
QWidget *parent, std::shared_ptr<MacroConditionRecord> entryData)
|
||||
: QWidget(parent)
|
||||
{
|
||||
_recordState = new QComboBox();
|
||||
_duration = new DurationSelection();
|
||||
|
||||
QWidget::connect(_recordState, SIGNAL(currentIndexChanged(int)), this,
|
||||
SLOT(StateChanged(int)));
|
||||
QWidget::connect(_duration, SIGNAL(DurationChanged(double)), this,
|
||||
SLOT(DurationChanged(double)));
|
||||
QWidget::connect(_duration, SIGNAL(UnitChanged(DurationUnit)), this,
|
||||
SLOT(DurationUnitChanged(DurationUnit)));
|
||||
|
||||
populateStateSelection(_recordState);
|
||||
|
||||
QHBoxLayout *mainLayout = new QHBoxLayout;
|
||||
|
||||
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
|
||||
{"{{recordState}}", _recordState},
|
||||
{"{{duration}}", _duration},
|
||||
};
|
||||
|
||||
placeWidgets(obs_module_text("AdvSceneSwitcher.condition.record.entry"),
|
||||
mainLayout, widgetPlaceholders);
|
||||
setLayout(mainLayout);
|
||||
|
||||
_entryData = entryData;
|
||||
UpdateEntryData();
|
||||
_loading = false;
|
||||
}
|
||||
|
||||
void MacroConditionRecordEdit::StateChanged(int value)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_recordState = static_cast<RecordState>(value);
|
||||
}
|
||||
|
||||
void MacroConditionRecordEdit::DurationChanged(double seconds)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_duration.seconds = seconds;
|
||||
}
|
||||
|
||||
void MacroConditionRecordEdit::DurationUnitChanged(DurationUnit unit)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_duration.displayUnit = unit;
|
||||
}
|
||||
|
||||
void MacroConditionRecordEdit::UpdateEntryData()
|
||||
{
|
||||
if (!_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
_recordState->setCurrentIndex(
|
||||
static_cast<int>(_entryData->_recordState));
|
||||
_duration->SetDuration(_entryData->_duration);
|
||||
}
|
||||
140
src/macro-condition-region.cpp
Normal file
140
src/macro-condition-region.cpp
Normal file
@@ -0,0 +1,140 @@
|
||||
#include "headers/macro-condition-edit.hpp"
|
||||
#include "headers/macro-condition-region.hpp"
|
||||
#include "headers/utility.hpp"
|
||||
#include "headers/advanced-scene-switcher.hpp"
|
||||
|
||||
const int MacroConditionRegion::id = 2;
|
||||
|
||||
bool MacroConditionRegion::_registered = MacroConditionFactory::Register(
|
||||
MacroConditionRegion::id,
|
||||
{MacroConditionRegion::Create, MacroConditionRegionEdit::Create,
|
||||
"AdvSceneSwitcher.condition.region"});
|
||||
|
||||
bool MacroConditionRegion::CheckCondition()
|
||||
{
|
||||
std::pair<int, int> cursorPos = getCursorPos();
|
||||
return cursorPos.first >= _minX && cursorPos.second >= _minY &&
|
||||
cursorPos.first <= _maxX && cursorPos.second <= _maxY;
|
||||
}
|
||||
|
||||
bool MacroConditionRegion::Save(obs_data_t *obj)
|
||||
{
|
||||
MacroCondition::Save(obj);
|
||||
obs_data_set_int(obj, "minX", _minX);
|
||||
obs_data_set_int(obj, "minY", _minY);
|
||||
obs_data_set_int(obj, "maxX", _maxX);
|
||||
obs_data_set_int(obj, "maxY", _maxY);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MacroConditionRegion::Load(obs_data_t *obj)
|
||||
{
|
||||
MacroCondition::Load(obj);
|
||||
_minX = obs_data_get_int(obj, "minX");
|
||||
_minY = obs_data_get_int(obj, "minY");
|
||||
_maxX = obs_data_get_int(obj, "maxX");
|
||||
_maxY = obs_data_get_int(obj, "maxY");
|
||||
return true;
|
||||
}
|
||||
|
||||
MacroConditionRegionEdit::MacroConditionRegionEdit(
|
||||
QWidget *parent, std::shared_ptr<MacroConditionRegion> entryData)
|
||||
: QWidget(parent)
|
||||
{
|
||||
_minX = new QSpinBox();
|
||||
_minY = new QSpinBox();
|
||||
_maxX = new QSpinBox();
|
||||
_maxY = new QSpinBox();
|
||||
|
||||
_minX->setPrefix("Min X: ");
|
||||
_minY->setPrefix("Min Y: ");
|
||||
_maxX->setPrefix("Max X: ");
|
||||
_maxY->setPrefix("Max Y: ");
|
||||
|
||||
_minX->setMinimum(-1000000);
|
||||
_minY->setMinimum(-1000000);
|
||||
_maxX->setMinimum(-1000000);
|
||||
_maxY->setMinimum(-1000000);
|
||||
|
||||
_minX->setMaximum(1000000);
|
||||
_minY->setMaximum(1000000);
|
||||
_maxX->setMaximum(1000000);
|
||||
_maxY->setMaximum(1000000);
|
||||
|
||||
QWidget::connect(_minX, SIGNAL(valueChanged(int)), this,
|
||||
SLOT(MinXChanged(int)));
|
||||
QWidget::connect(_minY, SIGNAL(valueChanged(int)), this,
|
||||
SLOT(MinYChanged(int)));
|
||||
QWidget::connect(_maxX, SIGNAL(valueChanged(int)), this,
|
||||
SLOT(MaxXChanged(int)));
|
||||
QWidget::connect(_maxY, SIGNAL(valueChanged(int)), this,
|
||||
SLOT(MaxYChanged(int)));
|
||||
|
||||
QHBoxLayout *mainLayout = new QHBoxLayout;
|
||||
|
||||
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
|
||||
{"{{minX}}", _minX},
|
||||
{"{{minY}}", _minY},
|
||||
{"{{maxX}}", _maxX},
|
||||
{"{{maxY}}", _maxY}};
|
||||
|
||||
placeWidgets(obs_module_text("AdvSceneSwitcher.condition.region.entry"),
|
||||
mainLayout, widgetPlaceholders);
|
||||
setLayout(mainLayout);
|
||||
|
||||
_entryData = entryData;
|
||||
UpdateEntryData();
|
||||
_loading = false;
|
||||
}
|
||||
|
||||
void MacroConditionRegionEdit::MinXChanged(int pos)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_minX = pos;
|
||||
}
|
||||
|
||||
void MacroConditionRegionEdit::MinYChanged(int pos)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_minY = pos;
|
||||
}
|
||||
|
||||
void MacroConditionRegionEdit::MaxXChanged(int pos)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_maxX = pos;
|
||||
}
|
||||
|
||||
void MacroConditionRegionEdit::MaxYChanged(int pos)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_maxY = pos;
|
||||
}
|
||||
|
||||
void MacroConditionRegionEdit::UpdateEntryData()
|
||||
{
|
||||
if (!_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
_minX->setValue(_entryData->_minX);
|
||||
_minY->setValue(_entryData->_minY);
|
||||
_maxX->setValue(_entryData->_maxX);
|
||||
_maxY->setValue(_entryData->_maxY);
|
||||
}
|
||||
150
src/macro-condition-scene.cpp
Normal file
150
src/macro-condition-scene.cpp
Normal file
@@ -0,0 +1,150 @@
|
||||
#include "headers/macro-condition-edit.hpp"
|
||||
#include "headers/macro-condition-scene.hpp"
|
||||
#include "headers/utility.hpp"
|
||||
#include "headers/advanced-scene-switcher.hpp"
|
||||
|
||||
const int MacroConditionScene::id = 0;
|
||||
|
||||
bool MacroConditionScene::_registered = MacroConditionFactory::Register(
|
||||
MacroConditionScene::id,
|
||||
{MacroConditionScene::Create, MacroConditionSceneEdit::Create,
|
||||
"AdvSceneSwitcher.condition.scene"});
|
||||
|
||||
static std::map<SceneType, std::string> sceneTypes = {
|
||||
{SceneType::CURRENT, "AdvSceneSwitcher.condition.scene.type.current"},
|
||||
{SceneType::PREVIOUS, "AdvSceneSwitcher.condition.scene.type.previous"},
|
||||
};
|
||||
|
||||
bool MacroConditionScene::CheckCondition()
|
||||
{
|
||||
bool sceneMatch = false;
|
||||
if (_type == SceneType::CURRENT) {
|
||||
obs_source_t *rawScene = obs_frontend_get_current_scene();
|
||||
OBSWeakSource currentScene =
|
||||
obs_source_get_weak_source(rawScene);
|
||||
sceneMatch = currentScene == _scene;
|
||||
obs_weak_source_release(currentScene);
|
||||
obs_source_release(rawScene);
|
||||
} else {
|
||||
sceneMatch = switcher->previousScene == _scene;
|
||||
}
|
||||
|
||||
if (!sceneMatch) {
|
||||
_duration.Reset();
|
||||
return false;
|
||||
}
|
||||
return _duration.DurationReached();
|
||||
}
|
||||
|
||||
bool MacroConditionScene::Save(obs_data_t *obj)
|
||||
{
|
||||
MacroCondition::Save(obj);
|
||||
obs_data_set_string(obj, "scene", GetWeakSourceName(_scene).c_str());
|
||||
obs_data_set_int(obj, "type", static_cast<int>(_type));
|
||||
_duration.Save(obj);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MacroConditionScene::Load(obs_data_t *obj)
|
||||
{
|
||||
MacroCondition::Load(obj);
|
||||
_scene = GetWeakSourceByName(obs_data_get_string(obj, "scene"));
|
||||
_type = static_cast<SceneType>(obs_data_get_int(obj, "type"));
|
||||
_duration.Load(obj);
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline void populateTypeSelection(QComboBox *list)
|
||||
{
|
||||
for (auto entry : sceneTypes) {
|
||||
list->addItem(obs_module_text(entry.second.c_str()));
|
||||
}
|
||||
}
|
||||
|
||||
MacroConditionSceneEdit::MacroConditionSceneEdit(
|
||||
QWidget *parent, std::shared_ptr<MacroConditionScene> entryData)
|
||||
: QWidget(parent)
|
||||
{
|
||||
_sceneSelection = new QComboBox();
|
||||
_sceneType = new QComboBox();
|
||||
_duration = new DurationSelection();
|
||||
|
||||
QWidget::connect(_sceneSelection,
|
||||
SIGNAL(currentTextChanged(const QString &)), this,
|
||||
SLOT(SceneChanged(const QString &)));
|
||||
QWidget::connect(_sceneType, SIGNAL(currentIndexChanged(int)), this,
|
||||
SLOT(TypeChanged(int)));
|
||||
QWidget::connect(_duration, SIGNAL(DurationChanged(double)), this,
|
||||
SLOT(DurationChanged(double)));
|
||||
QWidget::connect(_duration, SIGNAL(UnitChanged(DurationUnit)), this,
|
||||
SLOT(DurationUnitChanged(DurationUnit)));
|
||||
|
||||
AdvSceneSwitcher::populateSceneSelection(_sceneSelection);
|
||||
populateTypeSelection(_sceneType);
|
||||
|
||||
QHBoxLayout *mainLayout = new QHBoxLayout;
|
||||
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
|
||||
{"{{scenes}}", _sceneSelection},
|
||||
{"{{sceneType}}", _sceneType},
|
||||
{"{{duration}}", _duration},
|
||||
};
|
||||
placeWidgets(obs_module_text("AdvSceneSwitcher.condition.scene.entry"),
|
||||
mainLayout, widgetPlaceholders);
|
||||
setLayout(mainLayout);
|
||||
|
||||
_entryData = entryData;
|
||||
UpdateEntryData();
|
||||
_loading = false;
|
||||
}
|
||||
|
||||
void MacroConditionSceneEdit::SceneChanged(const QString &text)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_scene = GetWeakSourceByQString(text);
|
||||
}
|
||||
|
||||
void MacroConditionSceneEdit::TypeChanged(int value)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_type = static_cast<SceneType>(value);
|
||||
}
|
||||
|
||||
void MacroConditionSceneEdit::DurationChanged(double seconds)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_duration.seconds = seconds;
|
||||
}
|
||||
|
||||
void MacroConditionSceneEdit::DurationUnitChanged(DurationUnit unit)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_duration.displayUnit = unit;
|
||||
}
|
||||
|
||||
void MacroConditionSceneEdit::UpdateEntryData()
|
||||
{
|
||||
if (!_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
_sceneSelection->setCurrentText(
|
||||
GetWeakSourceName(_entryData->_scene).c_str());
|
||||
_sceneType->setCurrentIndex(static_cast<int>(_entryData->_type));
|
||||
_duration->SetDuration(_entryData->_duration);
|
||||
}
|
||||
134
src/macro-condition-streaming.cpp
Normal file
134
src/macro-condition-streaming.cpp
Normal file
@@ -0,0 +1,134 @@
|
||||
#include "headers/macro-condition-edit.hpp"
|
||||
#include "headers/macro-condition-streaming.hpp"
|
||||
#include "headers/utility.hpp"
|
||||
#include "headers/advanced-scene-switcher.hpp"
|
||||
|
||||
const int MacroConditionStream::id = 7;
|
||||
|
||||
bool MacroConditionStream::_registered = MacroConditionFactory::Register(
|
||||
MacroConditionStream::id,
|
||||
{MacroConditionStream::Create, MacroConditionStreamEdit::Create,
|
||||
"AdvSceneSwitcher.condition.stream"});
|
||||
|
||||
static std::map<StreamState, std::string> streamStates = {
|
||||
{StreamState::STOP, "AdvSceneSwitcher.condition.stream.state.stop"},
|
||||
{StreamState::START, "AdvSceneSwitcher.condition.stream.state.start"},
|
||||
};
|
||||
|
||||
bool MacroConditionStream::CheckCondition()
|
||||
{
|
||||
bool stateMatch = false;
|
||||
switch (_streamState) {
|
||||
case StreamState::STOP:
|
||||
stateMatch = !obs_frontend_streaming_active();
|
||||
break;
|
||||
case StreamState::START:
|
||||
stateMatch = obs_frontend_streaming_active();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (!stateMatch) {
|
||||
_duration.Reset();
|
||||
return false;
|
||||
}
|
||||
|
||||
return _duration.DurationReached();
|
||||
}
|
||||
|
||||
bool MacroConditionStream::Save(obs_data_t *obj)
|
||||
{
|
||||
MacroCondition::Save(obj);
|
||||
obs_data_set_int(obj, "state", static_cast<int>(_streamState));
|
||||
_duration.Save(obj);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MacroConditionStream::Load(obs_data_t *obj)
|
||||
{
|
||||
MacroCondition::Load(obj);
|
||||
_streamState = static_cast<StreamState>(obs_data_get_int(obj, "state"));
|
||||
_duration.Load(obj);
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline void populateStateSelection(QComboBox *list)
|
||||
{
|
||||
for (auto entry : streamStates) {
|
||||
list->addItem(obs_module_text(entry.second.c_str()));
|
||||
}
|
||||
}
|
||||
|
||||
MacroConditionStreamEdit::MacroConditionStreamEdit(
|
||||
QWidget *parent, std::shared_ptr<MacroConditionStream> entryData)
|
||||
: QWidget(parent)
|
||||
{
|
||||
_streamState = new QComboBox();
|
||||
_duration = new DurationSelection();
|
||||
|
||||
QWidget::connect(_streamState, SIGNAL(currentIndexChanged(int)), this,
|
||||
SLOT(StateChanged(int)));
|
||||
QWidget::connect(_duration, SIGNAL(DurationChanged(double)), this,
|
||||
SLOT(DurationChanged(double)));
|
||||
QWidget::connect(_duration, SIGNAL(UnitChanged(DurationUnit)), this,
|
||||
SLOT(DurationUnitChanged(DurationUnit)));
|
||||
|
||||
populateStateSelection(_streamState);
|
||||
|
||||
QHBoxLayout *mainLayout = new QHBoxLayout;
|
||||
|
||||
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
|
||||
{"{{streamState}}", _streamState},
|
||||
{"{{duration}}", _duration},
|
||||
};
|
||||
|
||||
placeWidgets(obs_module_text("AdvSceneSwitcher.condition.stream.entry"),
|
||||
mainLayout, widgetPlaceholders);
|
||||
setLayout(mainLayout);
|
||||
|
||||
_entryData = entryData;
|
||||
UpdateEntryData();
|
||||
_loading = false;
|
||||
}
|
||||
|
||||
void MacroConditionStreamEdit::StateChanged(int value)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_streamState = static_cast<StreamState>(value);
|
||||
}
|
||||
|
||||
void MacroConditionStreamEdit::DurationChanged(double seconds)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_duration.seconds = seconds;
|
||||
}
|
||||
|
||||
void MacroConditionStreamEdit::DurationUnitChanged(DurationUnit unit)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_duration.displayUnit = unit;
|
||||
}
|
||||
|
||||
void MacroConditionStreamEdit::UpdateEntryData()
|
||||
{
|
||||
if (!_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
_streamState->setCurrentIndex(
|
||||
static_cast<int>(_entryData->_streamState));
|
||||
_duration->SetDuration(_entryData->_duration);
|
||||
}
|
||||
363
src/macro-condition-video.cpp
Normal file
363
src/macro-condition-video.cpp
Normal file
@@ -0,0 +1,363 @@
|
||||
#include "headers/macro-condition-edit.hpp"
|
||||
#include "headers/macro-condition-video.hpp"
|
||||
#include "headers/utility.hpp"
|
||||
#include "headers/advanced-scene-switcher.hpp"
|
||||
|
||||
#include <QFileDialog>
|
||||
#include <QBuffer>
|
||||
#include <QToolTip>
|
||||
#include <QMessageBox>
|
||||
|
||||
const int MacroConditionVideo::id = 6;
|
||||
|
||||
bool MacroConditionVideo::_registered = MacroConditionFactory::Register(
|
||||
MacroConditionVideo::id,
|
||||
{MacroConditionVideo::Create, MacroConditionVideoEdit::Create,
|
||||
"AdvSceneSwitcher.condition.video"});
|
||||
|
||||
static std::map<VideoCondition, std::string> conditionTypes = {
|
||||
{VideoCondition::MATCH,
|
||||
"AdvSceneSwitcher.condition.video.condition.match"},
|
||||
{VideoCondition::DIFFER,
|
||||
"AdvSceneSwitcher.condition.video.condition.differ"},
|
||||
{VideoCondition::HAS_NOT_CHANGED,
|
||||
"AdvSceneSwitcher.condition.video.condition.hasNotChanged"},
|
||||
{VideoCondition::HAS_CHANGED,
|
||||
"AdvSceneSwitcher.condition.video.condition.hasChanged"},
|
||||
{VideoCondition::NO_IMAGE,
|
||||
"AdvSceneSwitcher.condition.video.condition.noImage"},
|
||||
};
|
||||
|
||||
bool requiresFileInput(VideoCondition t)
|
||||
{
|
||||
return t == VideoCondition::MATCH || t == VideoCondition::DIFFER;
|
||||
}
|
||||
|
||||
bool MacroConditionVideo::CheckCondition()
|
||||
{
|
||||
bool match = false;
|
||||
|
||||
if (_screenshotData) {
|
||||
if (_screenshotData->done) {
|
||||
bool imageMatch = Compare();
|
||||
|
||||
if (!imageMatch) {
|
||||
_duration.Reset();
|
||||
}
|
||||
|
||||
if (imageMatch && _duration.DurationReached()) {
|
||||
match = true;
|
||||
}
|
||||
|
||||
if (!requiresFileInput(_condition)) {
|
||||
_matchImage = std::move(_screenshotData->image);
|
||||
}
|
||||
_screenshotData.reset(nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
GetScreenshot();
|
||||
return match;
|
||||
}
|
||||
|
||||
bool MacroConditionVideo::Save(obs_data_t *obj)
|
||||
{
|
||||
MacroCondition::Save(obj);
|
||||
obs_data_set_string(obj, "videoSource",
|
||||
GetWeakSourceName(_videoSource).c_str());
|
||||
obs_data_set_int(obj, "condition", static_cast<int>(_condition));
|
||||
_duration.Save(obj);
|
||||
obs_data_set_string(obj, "filePath", _file.c_str());
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MacroConditionVideo::Load(obs_data_t *obj)
|
||||
{
|
||||
const char *videoSourceName = obs_data_get_string(obj, "videoSource");
|
||||
_videoSource = GetWeakSourceByName(videoSourceName);
|
||||
_condition =
|
||||
static_cast<VideoCondition>(obs_data_get_int(obj, "condition"));
|
||||
_duration.Load(obj);
|
||||
_file = obs_data_get_string(obj, "filePath");
|
||||
|
||||
if (requiresFileInput(_condition)) {
|
||||
(void)LoadImageFromFile();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void MacroConditionVideo::GetScreenshot()
|
||||
{
|
||||
auto source = obs_weak_source_get_source(_videoSource);
|
||||
_screenshotData = std::make_unique<AdvSSScreenshotObj>(source);
|
||||
obs_source_release(source);
|
||||
}
|
||||
|
||||
bool MacroConditionVideo::LoadImageFromFile()
|
||||
{
|
||||
if (!_matchImage.load(QString::fromStdString(_file))) {
|
||||
blog(LOG_WARNING, "Cannot load image data from file '%s'",
|
||||
_file.c_str());
|
||||
return false;
|
||||
}
|
||||
_matchImage =
|
||||
_matchImage.convertToFormat(QImage::Format::Format_RGBX8888);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MacroConditionVideo::Compare()
|
||||
{
|
||||
switch (_condition) {
|
||||
case VideoCondition::MATCH:
|
||||
return _screenshotData->image == _matchImage;
|
||||
case VideoCondition::DIFFER:
|
||||
return _screenshotData->image != _matchImage;
|
||||
case VideoCondition::HAS_CHANGED:
|
||||
return _screenshotData->image != _matchImage;
|
||||
case VideoCondition::HAS_NOT_CHANGED:
|
||||
return _screenshotData->image == _matchImage;
|
||||
case VideoCondition::NO_IMAGE:
|
||||
return _screenshotData->image.isNull();
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline void populateConditionSelection(QComboBox *list)
|
||||
{
|
||||
for (auto entry : conditionTypes) {
|
||||
list->addItem(obs_module_text(entry.second.c_str()));
|
||||
}
|
||||
}
|
||||
|
||||
MacroConditionVideoEdit::MacroConditionVideoEdit(
|
||||
QWidget *parent, std::shared_ptr<MacroConditionVideo> entryData)
|
||||
: QWidget(parent)
|
||||
{
|
||||
_videoSelection = new QComboBox();
|
||||
_condition = new QComboBox();
|
||||
_duration = new DurationSelection();
|
||||
_filePath = new QLineEdit();
|
||||
_browseButton =
|
||||
new QPushButton(obs_module_text("AdvSceneSwitcher.browse"));
|
||||
|
||||
_filePath->setFixedWidth(100);
|
||||
|
||||
_browseButton->setStyleSheet("border:1px solid gray;");
|
||||
|
||||
QWidget::connect(_videoSelection,
|
||||
SIGNAL(currentTextChanged(const QString &)), this,
|
||||
SLOT(SourceChanged(const QString &)));
|
||||
QWidget::connect(_condition, SIGNAL(currentIndexChanged(int)), this,
|
||||
SLOT(ConditionChanged(int)));
|
||||
QWidget::connect(_duration, SIGNAL(DurationChanged(double)), this,
|
||||
SLOT(DurationChanged(double)));
|
||||
QWidget::connect(_duration, SIGNAL(UnitChanged(DurationUnit)), this,
|
||||
SLOT(DurationUnitChanged(DurationUnit)));
|
||||
QWidget::connect(_filePath, SIGNAL(editingFinished()), this,
|
||||
SLOT(FilePathChanged()));
|
||||
QWidget::connect(_browseButton, SIGNAL(clicked()), this,
|
||||
SLOT(BrowseButtonClicked()));
|
||||
|
||||
AdvSceneSwitcher::populateVideoSelection(_videoSelection, false);
|
||||
populateConditionSelection(_condition);
|
||||
|
||||
QHBoxLayout *mainLayout = new QHBoxLayout;
|
||||
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
|
||||
{"{{videoSources}}", _videoSelection},
|
||||
{"{{condition}}", _condition},
|
||||
{"{{duration}}", _duration},
|
||||
{"{{filePath}}", _filePath},
|
||||
{"{{browseButton}}", _browseButton},
|
||||
};
|
||||
placeWidgets(obs_module_text("AdvSceneSwitcher.condition.video.entry"),
|
||||
mainLayout, widgetPlaceholders);
|
||||
setLayout(mainLayout);
|
||||
|
||||
_entryData = entryData;
|
||||
UpdateEntryData();
|
||||
_loading = false;
|
||||
}
|
||||
|
||||
void MacroConditionVideoEdit::UpdatePreviewTooltip()
|
||||
{
|
||||
if (!_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!requiresFileInput(_entryData->_condition)) {
|
||||
this->setToolTip("");
|
||||
return;
|
||||
}
|
||||
|
||||
QImage preview = _entryData->GetMatchImage().scaled(
|
||||
{300, 300}, Qt::KeepAspectRatio);
|
||||
|
||||
QByteArray data;
|
||||
QBuffer buffer(&data);
|
||||
if (!preview.save(&buffer, "PNG")) {
|
||||
return;
|
||||
}
|
||||
|
||||
QString html =
|
||||
QString("<html><img src='data:image/png;base64, %0'/></html>")
|
||||
.arg(QString(data.toBase64()));
|
||||
this->setToolTip(html);
|
||||
}
|
||||
|
||||
void MacroConditionVideoEdit::SetFilePath(const QString &text)
|
||||
{
|
||||
_filePath->setText(text);
|
||||
FilePathChanged();
|
||||
}
|
||||
|
||||
void MacroConditionVideoEdit::SourceChanged(const QString &text)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_videoSource = GetWeakSourceByQString(text);
|
||||
}
|
||||
|
||||
void MacroConditionVideoEdit::ConditionChanged(int cond)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_condition = static_cast<VideoCondition>(cond);
|
||||
|
||||
if (requiresFileInput(_entryData->_condition)) {
|
||||
_filePath->show();
|
||||
_browseButton->show();
|
||||
} else {
|
||||
_filePath->hide();
|
||||
_browseButton->hide();
|
||||
}
|
||||
|
||||
// Reload image data to avoid incorrect matches.
|
||||
//
|
||||
// Condition type HAS_NOT_CHANGED will use matchImage to store previous
|
||||
// frame of video source, which will differ from the image stored at
|
||||
// specified file location.
|
||||
if (_entryData->LoadImageFromFile()) {
|
||||
UpdatePreviewTooltip();
|
||||
}
|
||||
}
|
||||
|
||||
void MacroConditionVideoEdit::FilePathChanged()
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_file = _filePath->text().toUtf8().constData();
|
||||
if (_entryData->LoadImageFromFile()) {
|
||||
UpdatePreviewTooltip();
|
||||
}
|
||||
}
|
||||
|
||||
void MacroConditionVideoEdit::BrowseButtonClicked()
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
QString path;
|
||||
bool useExistingFile = false;
|
||||
// Ask whether to create screenshot or to select existing file
|
||||
if (_entryData->_videoSource) {
|
||||
QMessageBox msgBox(
|
||||
QMessageBox::Question,
|
||||
obs_module_text("AdvSceneSwitcher.windowTitle"),
|
||||
obs_module_text(
|
||||
"AdvSceneSwitcher.condition.video.askFileAction"),
|
||||
QMessageBox::Yes | QMessageBox::No);
|
||||
msgBox.setWindowFlags(Qt::Window | Qt::WindowTitleHint |
|
||||
Qt::CustomizeWindowHint);
|
||||
msgBox.setButtonText(
|
||||
QMessageBox::Yes,
|
||||
obs_module_text(
|
||||
"AdvSceneSwitcher.condition.video.askFileAction.file"));
|
||||
msgBox.setButtonText(
|
||||
QMessageBox::No,
|
||||
obs_module_text(
|
||||
"AdvSceneSwitcher.condition.video.askFileAction.screenshot"));
|
||||
useExistingFile = msgBox.exec() == QMessageBox::Yes;
|
||||
}
|
||||
|
||||
if (useExistingFile) {
|
||||
path = QFileDialog::getOpenFileName(this);
|
||||
if (path.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
} else {
|
||||
auto source =
|
||||
obs_weak_source_get_source(_entryData->_videoSource);
|
||||
auto screenshot = std::make_unique<AdvSSScreenshotObj>(source);
|
||||
obs_source_release(source);
|
||||
|
||||
path = QFileDialog::getSaveFileName(this);
|
||||
if (path.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
QFile file(path);
|
||||
if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
|
||||
return;
|
||||
}
|
||||
if (!screenshot->done) { // Screenshot usually completed by now
|
||||
std::this_thread::sleep_for(std::chrono::seconds(1));
|
||||
}
|
||||
if (!screenshot->done) {
|
||||
DisplayMessage("Failed to get screenshot of source!");
|
||||
return;
|
||||
}
|
||||
|
||||
screenshot->image.save(path);
|
||||
}
|
||||
SetFilePath(path);
|
||||
}
|
||||
|
||||
void MacroConditionVideoEdit::DurationChanged(double seconds)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_duration.seconds = seconds;
|
||||
}
|
||||
|
||||
void MacroConditionVideoEdit::DurationUnitChanged(DurationUnit unit)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_duration.displayUnit = unit;
|
||||
}
|
||||
|
||||
void MacroConditionVideoEdit::UpdateEntryData()
|
||||
{
|
||||
if (!_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
_videoSelection->setCurrentText(
|
||||
GetWeakSourceName(_entryData->_videoSource).c_str());
|
||||
_condition->setCurrentIndex(static_cast<int>(_entryData->_condition));
|
||||
_duration->SetDuration(_entryData->_duration);
|
||||
_filePath->setText(QString::fromStdString(_entryData->_file));
|
||||
|
||||
if (!requiresFileInput(_entryData->_condition)) {
|
||||
_filePath->hide();
|
||||
_browseButton->hide();
|
||||
}
|
||||
}
|
||||
195
src/macro-condition-window.cpp
Normal file
195
src/macro-condition-window.cpp
Normal file
@@ -0,0 +1,195 @@
|
||||
#include "headers/macro-condition-edit.hpp"
|
||||
#include "headers/macro-condition-window.hpp"
|
||||
#include "headers/utility.hpp"
|
||||
#include "headers/advanced-scene-switcher.hpp"
|
||||
|
||||
#include <regex>
|
||||
|
||||
const int MacroConditionWindow::id = 1;
|
||||
|
||||
bool MacroConditionWindow::_registered = MacroConditionFactory::Register(
|
||||
MacroConditionWindow::id,
|
||||
{MacroConditionWindow::Create, MacroConditionWindowEdit::Create,
|
||||
"AdvSceneSwitcher.condition.window"});
|
||||
|
||||
bool MacroConditionWindow::CheckWindowTitleSwitchDirect(
|
||||
std::string ¤tWindowTitle)
|
||||
{
|
||||
bool focus = (!_focus || _window == currentWindowTitle);
|
||||
bool fullscreen = (!_fullscreen || isFullscreen(_window));
|
||||
bool max = (!_maximized || isMaximized(_window));
|
||||
|
||||
return focus && fullscreen && max;
|
||||
}
|
||||
|
||||
bool MacroConditionWindow::CheckWindowTitleSwitchRegex(
|
||||
std::string ¤tWindowTitle, std::vector<std::string> &windowList)
|
||||
{
|
||||
bool match = false;
|
||||
for (auto &window : windowList) {
|
||||
try {
|
||||
std::regex expr(_window);
|
||||
if (!std::regex_match(window, expr)) {
|
||||
continue;
|
||||
}
|
||||
} catch (const std::regex_error &) {
|
||||
}
|
||||
|
||||
bool focus = (!_focus || window == currentWindowTitle);
|
||||
bool fullscreen = (!_fullscreen || isFullscreen(window));
|
||||
bool max = (!_maximized || isMaximized(window));
|
||||
|
||||
if (focus && fullscreen && max) {
|
||||
match = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return match;
|
||||
}
|
||||
|
||||
bool MacroConditionWindow::CheckCondition()
|
||||
{
|
||||
std::string currentWindowTitle;
|
||||
GetCurrentWindowTitle(currentWindowTitle);
|
||||
|
||||
std::vector<std::string> windowList;
|
||||
GetWindowList(windowList);
|
||||
|
||||
bool match = false;
|
||||
|
||||
if (std::find(windowList.begin(), windowList.end(), _window) !=
|
||||
windowList.end()) {
|
||||
match = CheckWindowTitleSwitchDirect(currentWindowTitle);
|
||||
} else {
|
||||
match = CheckWindowTitleSwitchRegex(currentWindowTitle,
|
||||
windowList);
|
||||
}
|
||||
|
||||
return match;
|
||||
}
|
||||
|
||||
bool MacroConditionWindow::Save(obs_data_t *obj)
|
||||
{
|
||||
MacroCondition::Save(obj);
|
||||
obs_data_set_string(obj, "window", _window.c_str());
|
||||
obs_data_set_bool(obj, "fullscreen", _fullscreen);
|
||||
obs_data_set_bool(obj, "maximized", _maximized);
|
||||
obs_data_set_bool(obj, "focus", _focus);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MacroConditionWindow::Load(obs_data_t *obj)
|
||||
{
|
||||
MacroCondition::Load(obj);
|
||||
_window = obs_data_get_string(obj, "window");
|
||||
_fullscreen = obs_data_get_bool(obj, "fullscreen");
|
||||
#if __APPLE__
|
||||
// TODO: Implement maximized check on MacOS
|
||||
_maximized = false;
|
||||
#else
|
||||
_maximized = obs_data_get_bool(obj, "maximized");
|
||||
#endif
|
||||
_focus = obs_data_get_bool(obj, "focus");
|
||||
return true;
|
||||
}
|
||||
|
||||
MacroConditionWindowEdit::MacroConditionWindowEdit(
|
||||
QWidget *parent, std::shared_ptr<MacroConditionWindow> entryData)
|
||||
: QWidget(parent)
|
||||
{
|
||||
_windowSelection = new QComboBox();
|
||||
_windowSelection->setEditable(true);
|
||||
_windowSelection->setMaxVisibleItems(20);
|
||||
|
||||
_fullscreen = new QCheckBox();
|
||||
_maximized = new QCheckBox();
|
||||
_focused = new QCheckBox();
|
||||
|
||||
QWidget::connect(_windowSelection,
|
||||
SIGNAL(currentTextChanged(const QString &)), this,
|
||||
SLOT(WindowChanged(const QString &)));
|
||||
QWidget::connect(_fullscreen, SIGNAL(stateChanged(int)), this,
|
||||
SLOT(FullscreenChanged(int)));
|
||||
QWidget::connect(_maximized, SIGNAL(stateChanged(int)), this,
|
||||
SLOT(MaximizedChanged(int)));
|
||||
QWidget::connect(_focused, SIGNAL(stateChanged(int)), this,
|
||||
SLOT(FocusChanged(int)));
|
||||
|
||||
AdvSceneSwitcher::populateWindowSelection(_windowSelection);
|
||||
|
||||
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
|
||||
{"{{windows}}", _windowSelection},
|
||||
{"{{fullscreen}}", _fullscreen},
|
||||
{"{{maximized}}", _maximized},
|
||||
{"{{focused}}", _focused},
|
||||
};
|
||||
|
||||
QVBoxLayout *mainLayout = new QVBoxLayout;
|
||||
QHBoxLayout *line1Layout = new QHBoxLayout;
|
||||
QHBoxLayout *line2Layout = new QHBoxLayout;
|
||||
placeWidgets(obs_module_text(
|
||||
"AdvSceneSwitcher.condition.window.entry.line1"),
|
||||
line1Layout, widgetPlaceholders);
|
||||
placeWidgets(obs_module_text(
|
||||
"AdvSceneSwitcher.condition.window.entry.line2"),
|
||||
line2Layout, widgetPlaceholders);
|
||||
mainLayout->addLayout(line1Layout);
|
||||
mainLayout->addLayout(line2Layout);
|
||||
setLayout(mainLayout);
|
||||
|
||||
_entryData = entryData;
|
||||
UpdateEntryData();
|
||||
_loading = false;
|
||||
}
|
||||
|
||||
void MacroConditionWindowEdit::WindowChanged(const QString &text)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_window = text.toStdString();
|
||||
}
|
||||
|
||||
void MacroConditionWindowEdit::FullscreenChanged(int state)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_fullscreen = state;
|
||||
}
|
||||
|
||||
void MacroConditionWindowEdit::MaximizedChanged(int state)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_maximized = state;
|
||||
}
|
||||
|
||||
void MacroConditionWindowEdit::FocusChanged(int state)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_focus = state;
|
||||
}
|
||||
|
||||
void MacroConditionWindowEdit::UpdateEntryData()
|
||||
{
|
||||
if (!_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
_windowSelection->setCurrentText(_entryData->_window.c_str());
|
||||
_fullscreen->setChecked(_entryData->_fullscreen);
|
||||
_maximized->setChecked(_entryData->_maximized);
|
||||
_focused->setChecked(_entryData->_focus);
|
||||
}
|
||||
290
src/macro-tab.cpp
Normal file
290
src/macro-tab.cpp
Normal file
@@ -0,0 +1,290 @@
|
||||
#include "headers/macro.hpp"
|
||||
#include "headers/macro-action-edit.hpp"
|
||||
#include "headers/macro-condition-edit.hpp"
|
||||
#include "headers/advanced-scene-switcher.hpp"
|
||||
#include "headers/name-dialog.hpp"
|
||||
|
||||
#include <QMenu>
|
||||
|
||||
static QMetaObject::Connection addPulse;
|
||||
|
||||
const auto conditionsCollapseThreshold = 4;
|
||||
const auto actionsCollapseThreshold = 2;
|
||||
|
||||
bool macroNameExists(std::string name)
|
||||
{
|
||||
for (auto &m : switcher->macros) {
|
||||
if (m.Name() == name) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool AdvSceneSwitcher::addNewMacro(std::string &name)
|
||||
{
|
||||
QString format{
|
||||
obs_module_text("AdvSceneSwitcher.macroTab.defaultname")};
|
||||
|
||||
int i = 1;
|
||||
QString placeHolderText = format.arg(i);
|
||||
while ((macroNameExists(placeHolderText.toUtf8().constData()))) {
|
||||
placeHolderText = format.arg(++i);
|
||||
}
|
||||
|
||||
bool accepted = AdvSSNameDialog::AskForName(
|
||||
this, obs_module_text("AdvSceneSwitcher.macroTab.add"),
|
||||
obs_module_text("AdvSceneSwitcher.macroTab.name"), name,
|
||||
placeHolderText);
|
||||
|
||||
if (!accepted) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (name.empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (macroNameExists(name)) {
|
||||
DisplayMessage(
|
||||
obs_module_text("AdvSceneSwitcher.macroTab.exists"));
|
||||
return false;
|
||||
}
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
switcher->macros.emplace_back(name);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::on_macroAdd_clicked()
|
||||
{
|
||||
std::string name;
|
||||
if (!addNewMacro(name)) {
|
||||
return;
|
||||
}
|
||||
QString text = QString::fromStdString(name);
|
||||
|
||||
QListWidgetItem *item = new QListWidgetItem(text, ui->macros);
|
||||
item->setData(Qt::UserRole, text);
|
||||
ui->macros->setCurrentItem(item);
|
||||
|
||||
ui->macroAdd->disconnect(addPulse);
|
||||
ui->macroHelp->setVisible(false);
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::on_macroRemove_clicked()
|
||||
{
|
||||
QListWidgetItem *item = ui->macros->currentItem();
|
||||
if (!item) {
|
||||
return;
|
||||
}
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
int idx = ui->macros->currentRow();
|
||||
switcher->macros.erase(switcher->macros.begin() + idx);
|
||||
}
|
||||
|
||||
delete item;
|
||||
|
||||
if (ui->macros->count() == 0) {
|
||||
ui->macroHelp->setVisible(true);
|
||||
}
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::on_macroUp_clicked()
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
int index = ui->macros->currentRow();
|
||||
if (index != -1 && index != 0) {
|
||||
ui->macros->insertItem(index - 1, ui->macros->takeItem(index));
|
||||
ui->macros->setCurrentRow(index - 1);
|
||||
|
||||
iter_swap(switcher->macros.begin() + index,
|
||||
switcher->macros.begin() + index - 1);
|
||||
}
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::on_macroDown_clicked()
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
int index = ui->macros->currentRow();
|
||||
if (index != -1 && index != ui->macros->count() - 1) {
|
||||
ui->macros->insertItem(index + 1, ui->macros->takeItem(index));
|
||||
ui->macros->setCurrentRow(index + 1);
|
||||
|
||||
iter_swap(switcher->macros.begin() + index,
|
||||
switcher->macros.begin() + index + 1);
|
||||
}
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::on_macroName_editingFinished()
|
||||
{
|
||||
bool nameValid = true;
|
||||
|
||||
Macro *macro = getSelectedMacro();
|
||||
if (!macro) {
|
||||
return;
|
||||
}
|
||||
|
||||
QString newName = ui->macroName->text();
|
||||
QString oldName = QString::fromStdString(macro->Name());
|
||||
|
||||
if (newName.isEmpty() || newName == oldName) {
|
||||
nameValid = false;
|
||||
}
|
||||
|
||||
if (nameValid && macroNameExists(newName.toUtf8().constData())) {
|
||||
DisplayMessage(
|
||||
obs_module_text("AdvSceneSwitcher.macroTab.exists"));
|
||||
nameValid = false;
|
||||
}
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
if (nameValid) {
|
||||
macro->SetName(newName.toUtf8().constData());
|
||||
QListWidgetItem *item = ui->macros->currentItem();
|
||||
item->setData(Qt::UserRole, newName);
|
||||
item->setText(newName);
|
||||
} else {
|
||||
ui->macroName->setText(oldName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::SetEditMacro(Macro &m)
|
||||
{
|
||||
ui->macroName->setText(m.Name().c_str());
|
||||
clearLayout(ui->macroEditConditionLayout);
|
||||
clearLayout(ui->macroEditActionLayout);
|
||||
|
||||
bool collapse = m.Conditions().size() > conditionsCollapseThreshold;
|
||||
bool root = true;
|
||||
for (auto &c : m.Conditions()) {
|
||||
auto newEntry = new MacroConditionEdit(this, &c, c->GetId(),
|
||||
root, collapse);
|
||||
ui->macroEditConditionLayout->addWidget(newEntry);
|
||||
ui->macroEditConditionHelp->setVisible(false);
|
||||
root = false;
|
||||
}
|
||||
|
||||
collapse = m.Actions().size() > actionsCollapseThreshold;
|
||||
for (auto &a : m.Actions()) {
|
||||
auto newEntry =
|
||||
new MacroActionEdit(this, &a, a->GetId(), collapse);
|
||||
ui->macroEditActionLayout->addWidget(newEntry);
|
||||
ui->macroEditActionHelp->setVisible(false);
|
||||
}
|
||||
|
||||
ui->macroEdit->setDisabled(false);
|
||||
|
||||
if (m.Conditions().size() == 0) {
|
||||
ui->macroEditConditionHelp->setVisible(true);
|
||||
} else {
|
||||
ui->macroEditConditionHelp->setVisible(false);
|
||||
}
|
||||
|
||||
if (m.Actions().size() == 0) {
|
||||
ui->macroEditActionHelp->setVisible(true);
|
||||
} else {
|
||||
ui->macroEditActionHelp->setVisible(false);
|
||||
}
|
||||
}
|
||||
|
||||
Macro *AdvSceneSwitcher::getSelectedMacro()
|
||||
{
|
||||
Macro *macro = nullptr;
|
||||
QListWidgetItem *item = ui->macros->currentItem();
|
||||
|
||||
if (!item) {
|
||||
return macro;
|
||||
}
|
||||
|
||||
QString name = item->data(Qt::UserRole).toString();
|
||||
for (auto &m : switcher->macros) {
|
||||
if (name.compare(m.Name().c_str()) == 0) {
|
||||
macro = &m;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return macro;
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::on_macros_currentRowChanged(int idx)
|
||||
{
|
||||
if (loading) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (idx == -1) {
|
||||
ui->macroEdit->setDisabled(true);
|
||||
return;
|
||||
}
|
||||
|
||||
QListWidgetItem *item = ui->macros->item(idx);
|
||||
QString macroName = item->data(Qt::UserRole).toString();
|
||||
|
||||
for (auto &m : switcher->macros) {
|
||||
if (macroName.compare(m.Name().c_str()) == 0) {
|
||||
SetEditMacro(m);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::setupMacroTab()
|
||||
{
|
||||
for (auto &m : switcher->macros) {
|
||||
QString text = QString::fromStdString(m.Name());
|
||||
|
||||
QListWidgetItem *item = new QListWidgetItem(text, ui->macros);
|
||||
item->setData(Qt::UserRole, text);
|
||||
}
|
||||
|
||||
if (switcher->macros.size() == 0) {
|
||||
addPulse = PulseWidget(ui->macroAdd, QColor(Qt::green));
|
||||
ui->macroHelp->setVisible(true);
|
||||
} else {
|
||||
ui->macroHelp->setVisible(false);
|
||||
}
|
||||
|
||||
ui->macros->setContextMenuPolicy(Qt::CustomContextMenu);
|
||||
connect(ui->macros, SIGNAL(customContextMenuRequested(QPoint)), this,
|
||||
SLOT(showMacroContextMenu(QPoint)));
|
||||
|
||||
ui->macroEdit->setDisabled(true);
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::showMacroContextMenu(const QPoint &pos)
|
||||
{
|
||||
QPoint globalPos = ui->macros->mapToGlobal(pos);
|
||||
QMenu myMenu;
|
||||
myMenu.addAction(obs_module_text("AdvSceneSwitcher.macroTab.copy"),
|
||||
this, SLOT(copyMacro()));
|
||||
myMenu.exec(globalPos);
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::copyMacro()
|
||||
{
|
||||
obs_data_t *data = obs_data_create();
|
||||
getSelectedMacro()->Save(data);
|
||||
|
||||
std::string name;
|
||||
if (!addNewMacro(name)) {
|
||||
obs_data_release(data);
|
||||
return;
|
||||
}
|
||||
|
||||
switcher->macros.back().Load(data);
|
||||
switcher->macros.back().SetName(name);
|
||||
obs_data_release(data);
|
||||
|
||||
QString text = QString::fromStdString(name);
|
||||
QListWidgetItem *item = new QListWidgetItem(text, ui->macros);
|
||||
item->setData(Qt::UserRole, text);
|
||||
ui->macros->setCurrentItem(item);
|
||||
}
|
||||
298
src/macro.cpp
Normal file
298
src/macro.cpp
Normal file
@@ -0,0 +1,298 @@
|
||||
#include "headers/macro.hpp"
|
||||
|
||||
#include "headers/macro-action-edit.hpp"
|
||||
#include "headers/macro-condition-edit.hpp"
|
||||
#include "headers/macro-action-switch-scene.hpp"
|
||||
|
||||
const std::map<LogicType, LogicTypeInfo> MacroCondition::logicTypes = {
|
||||
{LogicType::NONE, {"AdvSceneSwitcher.logic.none"}},
|
||||
{LogicType::AND, {"AdvSceneSwitcher.logic.and"}},
|
||||
{LogicType::OR, {"AdvSceneSwitcher.logic.or"}},
|
||||
{LogicType::AND_NOT, {"AdvSceneSwitcher.logic.andNot"}},
|
||||
{LogicType::OR_NOT, {"AdvSceneSwitcher.logic.orNot"}},
|
||||
{LogicType::ROOT_NONE, {"AdvSceneSwitcher.logic.rootNone"}},
|
||||
{LogicType::ROOT_NOT, {"AdvSceneSwitcher.logic.not"}},
|
||||
};
|
||||
|
||||
Macro::Macro(std::string name) : _name(name) {}
|
||||
|
||||
Macro::~Macro() {}
|
||||
|
||||
bool Macro::CeckMatch()
|
||||
{
|
||||
_matched = false;
|
||||
for (auto &c : _conditions) {
|
||||
bool cond = c->CheckCondition();
|
||||
|
||||
switch (c->GetLogicType()) {
|
||||
case LogicType::NONE:
|
||||
vblog(LOG_INFO,
|
||||
"ignoring condition check 'none' for '%s'",
|
||||
_name.c_str());
|
||||
continue;
|
||||
break;
|
||||
case LogicType::AND:
|
||||
_matched = _matched && cond;
|
||||
break;
|
||||
case LogicType::OR:
|
||||
_matched = _matched || cond;
|
||||
break;
|
||||
case LogicType::AND_NOT:
|
||||
_matched = _matched && !cond;
|
||||
break;
|
||||
case LogicType::OR_NOT:
|
||||
_matched = _matched || !cond;
|
||||
break;
|
||||
case LogicType::ROOT_NONE:
|
||||
_matched = cond;
|
||||
break;
|
||||
case LogicType::ROOT_NOT:
|
||||
_matched = !cond;
|
||||
break;
|
||||
default:
|
||||
blog(LOG_WARNING,
|
||||
"ignoring unkown condition check for '%s'",
|
||||
_name.c_str());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return _matched;
|
||||
}
|
||||
|
||||
bool Macro::PerformAction()
|
||||
{
|
||||
bool ret = true;
|
||||
for (auto &a : _actions) {
|
||||
ret = ret && a->PerformAction();
|
||||
a->LogAction();
|
||||
if (!ret) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool Macro::Save(obs_data_t *obj)
|
||||
{
|
||||
obs_data_set_string(obj, "name", _name.c_str());
|
||||
|
||||
obs_data_array_t *conditions = obs_data_array_create();
|
||||
for (auto &c : _conditions) {
|
||||
obs_data_t *array_obj = obs_data_create();
|
||||
|
||||
c->Save(array_obj);
|
||||
obs_data_array_push_back(conditions, array_obj);
|
||||
|
||||
obs_data_release(array_obj);
|
||||
}
|
||||
obs_data_set_array(obj, "conditions", conditions);
|
||||
obs_data_array_release(conditions);
|
||||
|
||||
obs_data_array_t *actions = obs_data_array_create();
|
||||
for (auto &a : _actions) {
|
||||
obs_data_t *array_obj = obs_data_create();
|
||||
|
||||
a->Save(array_obj);
|
||||
obs_data_array_push_back(actions, array_obj);
|
||||
|
||||
obs_data_release(array_obj);
|
||||
}
|
||||
obs_data_set_array(obj, "actions", actions);
|
||||
obs_data_array_release(actions);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool isValidLogic(LogicType t, bool root)
|
||||
{
|
||||
bool isRoot = isRootLogicType(t);
|
||||
if (!isRoot == root) {
|
||||
return false;
|
||||
}
|
||||
if (isRoot) {
|
||||
if (t >= LogicType::ROOT_LAST) {
|
||||
return false;
|
||||
}
|
||||
} else if (t >= LogicType::LAST) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void setValidLogic(MacroCondition *c, bool root, std::string name)
|
||||
{
|
||||
if (isValidLogic(c->GetLogicType(), root)) {
|
||||
return;
|
||||
}
|
||||
if (root) {
|
||||
c->SetLogicType(LogicType::ROOT_NONE);
|
||||
blog(LOG_WARNING,
|
||||
"setting invalid logic selection to 'if' for macro %s",
|
||||
name.c_str());
|
||||
} else {
|
||||
c->SetLogicType(LogicType::NONE);
|
||||
blog(LOG_WARNING,
|
||||
"setting invalid logic selection to 'ignore' for macro %s",
|
||||
name.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
bool Macro::Load(obs_data_t *obj)
|
||||
{
|
||||
_name = obs_data_get_string(obj, "name");
|
||||
bool root = true;
|
||||
|
||||
obs_data_array_t *conditions = obs_data_get_array(obj, "conditions");
|
||||
size_t count = obs_data_array_count(conditions);
|
||||
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
obs_data_t *array_obj = obs_data_array_item(conditions, i);
|
||||
|
||||
int id = obs_data_get_int(array_obj, "id");
|
||||
|
||||
auto newEntry = MacroConditionFactory::Create(id);
|
||||
if (newEntry) {
|
||||
_conditions.emplace_back(newEntry);
|
||||
auto c = _conditions.back().get();
|
||||
c->Load(array_obj);
|
||||
setValidLogic(c, root, _name);
|
||||
} else {
|
||||
blog(LOG_WARNING,
|
||||
"discarding condition entry with unkown id (%d) for macro %s",
|
||||
id, _name.c_str());
|
||||
}
|
||||
|
||||
obs_data_release(array_obj);
|
||||
root = false;
|
||||
}
|
||||
obs_data_array_release(conditions);
|
||||
|
||||
obs_data_array_t *actions = obs_data_get_array(obj, "actions");
|
||||
count = obs_data_array_count(actions);
|
||||
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
obs_data_t *array_obj = obs_data_array_item(actions, i);
|
||||
|
||||
int id = obs_data_get_int(array_obj, "id");
|
||||
|
||||
auto newEntry = MacroActionFactory::Create(id);
|
||||
if (newEntry) {
|
||||
_actions.emplace_back(newEntry);
|
||||
_actions.back()->Load(array_obj);
|
||||
} else {
|
||||
blog(LOG_WARNING,
|
||||
"discarding action entry with unkown id (%d) for macro %s",
|
||||
id, _name.c_str());
|
||||
}
|
||||
|
||||
obs_data_release(array_obj);
|
||||
}
|
||||
obs_data_array_release(actions);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Macro::SwitchesScene()
|
||||
{
|
||||
MacroActionSwitchScene temp;
|
||||
for (auto &a : _actions) {
|
||||
if (a->GetId() == temp.GetId()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool MacroCondition::Save(obs_data_t *obj)
|
||||
{
|
||||
obs_data_set_int(obj, "id", GetId());
|
||||
obs_data_set_int(obj, "logic", static_cast<int>(_logic));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MacroCondition::Load(obs_data_t *obj)
|
||||
{
|
||||
_logic = static_cast<LogicType>(obs_data_get_int(obj, "logic"));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MacroAction::Save(obs_data_t *obj)
|
||||
{
|
||||
obs_data_set_int(obj, "id", GetId());
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MacroAction::Load(obs_data_t *obj)
|
||||
{
|
||||
UNUSED_PARAMETER(obj);
|
||||
return true;
|
||||
}
|
||||
|
||||
void MacroAction::LogAction()
|
||||
{
|
||||
blog(LOG_INFO, "performed action %d", GetId());
|
||||
}
|
||||
|
||||
void SwitcherData::saveMacros(obs_data_t *obj)
|
||||
{
|
||||
obs_data_array_t *macroArray = obs_data_array_create();
|
||||
for (auto &m : macros) {
|
||||
obs_data_t *array_obj = obs_data_create();
|
||||
|
||||
m.Save(array_obj);
|
||||
obs_data_array_push_back(macroArray, array_obj);
|
||||
|
||||
obs_data_release(array_obj);
|
||||
}
|
||||
obs_data_set_array(obj, "macros", macroArray);
|
||||
obs_data_array_release(macroArray);
|
||||
}
|
||||
|
||||
void SwitcherData::loadMacros(obs_data_t *obj)
|
||||
{
|
||||
macros.clear();
|
||||
|
||||
obs_data_array_t *macroArray = obs_data_get_array(obj, "macros");
|
||||
size_t count = obs_data_array_count(macroArray);
|
||||
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
obs_data_t *array_obj = obs_data_array_item(macroArray, i);
|
||||
macros.emplace_back();
|
||||
macros.back().Load(array_obj);
|
||||
obs_data_release(array_obj);
|
||||
}
|
||||
obs_data_array_release(macroArray);
|
||||
}
|
||||
|
||||
bool SwitcherData::checkMacros()
|
||||
{
|
||||
bool ret = false;
|
||||
for (auto &m : macros) {
|
||||
if (m.CeckMatch()) {
|
||||
ret = true;
|
||||
// This has to be performed here for now as actions are
|
||||
// not performed immediately after checking conditions.
|
||||
if (m.SwitchesScene()) {
|
||||
switcher->macroSceneSwitched = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool SwitcherData::runMacros()
|
||||
{
|
||||
for (auto &m : macros) {
|
||||
if (m.Matched()) {
|
||||
blog(LOG_INFO, "running macro: %s", m.Name().c_str());
|
||||
if (!m.PerformAction()) {
|
||||
blog(LOG_WARNING, "abort macro: %s",
|
||||
m.Name().c_str());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
66
src/name-dialog.cpp
Normal file
66
src/name-dialog.cpp
Normal file
@@ -0,0 +1,66 @@
|
||||
#include <QVBoxLayout>
|
||||
#include <QDialogButtonBox>
|
||||
#include "headers/name-dialog.hpp"
|
||||
|
||||
AdvSSNameDialog::AdvSSNameDialog(QWidget *parent) : QDialog(parent)
|
||||
{
|
||||
setModal(true);
|
||||
setWindowModality(Qt::WindowModality::WindowModal);
|
||||
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
|
||||
setFixedWidth(555);
|
||||
setMinimumHeight(100);
|
||||
QVBoxLayout *layout = new QVBoxLayout;
|
||||
setLayout(layout);
|
||||
|
||||
label = new QLabel(this);
|
||||
layout->addWidget(label);
|
||||
label->setText("Set Text");
|
||||
|
||||
userText = new QLineEdit(this);
|
||||
layout->addWidget(userText);
|
||||
|
||||
QDialogButtonBox *buttonbox = new QDialogButtonBox(
|
||||
QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
|
||||
layout->addWidget(buttonbox);
|
||||
buttonbox->setCenterButtons(true);
|
||||
connect(buttonbox, &QDialogButtonBox::accepted, this, &QDialog::accept);
|
||||
connect(buttonbox, &QDialogButtonBox::rejected, this, &QDialog::reject);
|
||||
}
|
||||
|
||||
static bool IsWhitespace(char ch)
|
||||
{
|
||||
return ch == ' ' || ch == '\t';
|
||||
}
|
||||
|
||||
static void CleanWhitespace(std::string &str)
|
||||
{
|
||||
while (str.size() && IsWhitespace(str.back()))
|
||||
str.erase(str.end() - 1);
|
||||
while (str.size() && IsWhitespace(str.front()))
|
||||
str.erase(str.begin());
|
||||
}
|
||||
|
||||
bool AdvSSNameDialog::AskForName(QWidget *parent, const QString &title,
|
||||
const QString &text,
|
||||
std::string &userTextInput,
|
||||
const QString &placeHolder, int maxSize)
|
||||
{
|
||||
if (maxSize <= 0 || maxSize > 32767) {
|
||||
maxSize = 170;
|
||||
}
|
||||
|
||||
AdvSSNameDialog dialog(parent);
|
||||
dialog.setWindowTitle(title);
|
||||
|
||||
dialog.label->setText(text);
|
||||
dialog.userText->setMaxLength(maxSize);
|
||||
dialog.userText->setText(placeHolder);
|
||||
dialog.userText->selectAll();
|
||||
|
||||
if (dialog.exec() != DialogCode::Accepted) {
|
||||
return false;
|
||||
}
|
||||
userTextInput = dialog.userText->text().toUtf8().constData();
|
||||
CleanWhitespace(userTextInput);
|
||||
return true;
|
||||
}
|
||||
@@ -4,7 +4,9 @@
|
||||
#import <Carbon/Carbon.h>
|
||||
#include <Carbon/Carbon.h>
|
||||
#include <util/platform.h>
|
||||
#include "../headers/advanced-scene-switcher.hpp"
|
||||
#include <vector>
|
||||
#include <QStringList>
|
||||
#include <QRegularExpression>
|
||||
|
||||
void GetWindowList(std::vector<std::string> &windows)
|
||||
{
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
#include <QVBoxLayout>
|
||||
#include <QDialogButtonBox>
|
||||
#include <random>
|
||||
|
||||
#include "headers/advanced-scene-switcher.hpp"
|
||||
#include "headers/name-dialog.hpp"
|
||||
#include "headers/utility.hpp"
|
||||
|
||||
static QMetaObject::Connection addPulse;
|
||||
@@ -127,7 +129,7 @@ void AdvSceneSwitcher::on_sceneGroupAdd_clicked()
|
||||
placeHolderText = format.arg(++i);
|
||||
}
|
||||
|
||||
bool accepted = SGNameDialog::AskForName(
|
||||
bool accepted = AdvSSNameDialog::AskForName(
|
||||
this, obs_module_text("AdvSceneSwitcher.sceneGroupTab.add"),
|
||||
obs_module_text("AdvSceneSwitcher.sceneGroupTab.add"), name,
|
||||
placeHolderText);
|
||||
@@ -406,7 +408,7 @@ void AdvSceneSwitcher::on_sceneGroupSceneDown_clicked()
|
||||
void SwitcherData::saveSceneGroups(obs_data_t *obj)
|
||||
{
|
||||
obs_data_array_t *sceneGroupArray = obs_data_array_create();
|
||||
for (SceneGroup &sg : switcher->sceneGroups) {
|
||||
for (SceneGroup &sg : sceneGroups) {
|
||||
obs_data_t *array_obj = obs_data_create();
|
||||
|
||||
obs_data_set_string(array_obj, "name", sg.name.c_str());
|
||||
@@ -445,7 +447,7 @@ void SwitcherData::saveSceneGroups(obs_data_t *obj)
|
||||
|
||||
void SwitcherData::loadSceneGroups(obs_data_t *obj)
|
||||
{
|
||||
switcher->sceneGroups.clear();
|
||||
sceneGroups.clear();
|
||||
|
||||
obs_data_array_t *sceneGroupArray =
|
||||
obs_data_get_array(obj, "sceneGroups");
|
||||
@@ -476,8 +478,8 @@ void SwitcherData::loadSceneGroups(obs_data_t *obj)
|
||||
double time = obs_data_get_double(array_obj, "time");
|
||||
bool repeat = obs_data_get_bool(array_obj, "repeat");
|
||||
|
||||
switcher->sceneGroups.emplace_back(name, type, scenes, count,
|
||||
time, repeat);
|
||||
sceneGroups.emplace_back(name, type, scenes, count, time,
|
||||
repeat);
|
||||
|
||||
obs_data_release(array_obj);
|
||||
}
|
||||
@@ -509,68 +511,6 @@ void AdvSceneSwitcher::setupSceneGroupTab()
|
||||
ui->sceneGroupEdit->setDisabled(true);
|
||||
}
|
||||
|
||||
SGNameDialog::SGNameDialog(QWidget *parent) : QDialog(parent)
|
||||
{
|
||||
setModal(true);
|
||||
setWindowModality(Qt::WindowModality::WindowModal);
|
||||
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
|
||||
setFixedWidth(555);
|
||||
setMinimumHeight(100);
|
||||
QVBoxLayout *layout = new QVBoxLayout;
|
||||
setLayout(layout);
|
||||
|
||||
label = new QLabel(this);
|
||||
layout->addWidget(label);
|
||||
label->setText("Set Text");
|
||||
|
||||
userText = new QLineEdit(this);
|
||||
layout->addWidget(userText);
|
||||
|
||||
QDialogButtonBox *buttonbox = new QDialogButtonBox(
|
||||
QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
|
||||
layout->addWidget(buttonbox);
|
||||
buttonbox->setCenterButtons(true);
|
||||
connect(buttonbox, &QDialogButtonBox::accepted, this, &QDialog::accept);
|
||||
connect(buttonbox, &QDialogButtonBox::rejected, this, &QDialog::reject);
|
||||
}
|
||||
|
||||
static bool IsWhitespace(char ch)
|
||||
{
|
||||
return ch == ' ' || ch == '\t';
|
||||
}
|
||||
|
||||
static void CleanWhitespace(std::string &str)
|
||||
{
|
||||
while (str.size() && IsWhitespace(str.back()))
|
||||
str.erase(str.end() - 1);
|
||||
while (str.size() && IsWhitespace(str.front()))
|
||||
str.erase(str.begin());
|
||||
}
|
||||
|
||||
bool SGNameDialog::AskForName(QWidget *parent, const QString &title,
|
||||
const QString &text, std::string &userTextInput,
|
||||
const QString &placeHolder, int maxSize)
|
||||
{
|
||||
if (maxSize <= 0 || maxSize > 32767) {
|
||||
maxSize = 170;
|
||||
}
|
||||
|
||||
SGNameDialog dialog(parent);
|
||||
dialog.setWindowTitle(title);
|
||||
|
||||
dialog.label->setText(text);
|
||||
dialog.userText->setMaxLength(maxSize);
|
||||
dialog.userText->setText(placeHolder);
|
||||
dialog.userText->selectAll();
|
||||
|
||||
if (dialog.exec() != DialogCode::Accepted) {
|
||||
return false;
|
||||
}
|
||||
userTextInput = dialog.userText->text().toUtf8().constData();
|
||||
CleanWhitespace(userTextInput);
|
||||
return true;
|
||||
}
|
||||
|
||||
void populateTypeSelection(QComboBox *list)
|
||||
{
|
||||
list->addItem(
|
||||
|
||||
@@ -305,7 +305,7 @@ void SwitcherData::checkTriggers()
|
||||
void SwitcherData::saveSceneTriggers(obs_data_t *obj)
|
||||
{
|
||||
obs_data_array_t *triggerArray = obs_data_array_create();
|
||||
for (auto &s : switcher->sceneTriggers) {
|
||||
for (auto &s : sceneTriggers) {
|
||||
obs_data_t *array_obj = obs_data_create();
|
||||
|
||||
s.save(array_obj);
|
||||
@@ -317,93 +317,9 @@ void SwitcherData::saveSceneTriggers(obs_data_t *obj)
|
||||
obs_data_array_release(triggerArray);
|
||||
}
|
||||
|
||||
// To be removed in future version
|
||||
void loadOldAutoStopStart(obs_data_t *obj)
|
||||
{
|
||||
typedef enum {
|
||||
RECORDING = 0,
|
||||
STREAMING = 1,
|
||||
RECORINDGSTREAMING = 2
|
||||
} AutoStartStopType;
|
||||
|
||||
if (obs_data_get_bool(obj, "autoStopEnable")) {
|
||||
std::string autoStopScene =
|
||||
obs_data_get_string(obj, "autoStopSceneName");
|
||||
int action = obs_data_get_int(obj, "autoStopType");
|
||||
|
||||
if (action == RECORDING) {
|
||||
switcher->sceneTriggers.emplace_back();
|
||||
auto &s = switcher->sceneTriggers.back();
|
||||
s.scene = GetWeakSourceByName(autoStopScene.c_str());
|
||||
s.triggerType = sceneTriggerType::SCENE_ACTIVE;
|
||||
s.triggerAction = sceneTriggerAction::STOP_RECORDING;
|
||||
}
|
||||
|
||||
if (action == STREAMING) {
|
||||
switcher->sceneTriggers.emplace_back();
|
||||
auto &s = switcher->sceneTriggers.back();
|
||||
s.scene = GetWeakSourceByName(autoStopScene.c_str());
|
||||
s.triggerType = sceneTriggerType::SCENE_ACTIVE;
|
||||
s.triggerAction = sceneTriggerAction::STOP_STREAMING;
|
||||
}
|
||||
|
||||
if (action == RECORINDGSTREAMING) {
|
||||
switcher->sceneTriggers.emplace_back();
|
||||
auto &s = switcher->sceneTriggers.back();
|
||||
s.scene = GetWeakSourceByName(autoStopScene.c_str());
|
||||
s.triggerType = sceneTriggerType::SCENE_ACTIVE;
|
||||
s.triggerAction = sceneTriggerAction::STOP_RECORDING;
|
||||
|
||||
switcher->sceneTriggers.emplace_back();
|
||||
auto &s2 = switcher->sceneTriggers.back();
|
||||
s2.scene = GetWeakSourceByName(autoStopScene.c_str());
|
||||
s2.triggerType = sceneTriggerType::SCENE_ACTIVE;
|
||||
s2.triggerAction = sceneTriggerAction::STOP_STREAMING;
|
||||
}
|
||||
}
|
||||
|
||||
if (obs_data_get_bool(obj, "autoStartEnable")) {
|
||||
std::string autoStartScene =
|
||||
obs_data_get_string(obj, "autoStartSceneName");
|
||||
int action = obs_data_get_int(obj, "autoStartType");
|
||||
|
||||
if (action == RECORDING) {
|
||||
switcher->sceneTriggers.emplace_back();
|
||||
auto &s = switcher->sceneTriggers.back();
|
||||
s.scene = GetWeakSourceByName(autoStartScene.c_str());
|
||||
s.triggerType = sceneTriggerType::SCENE_ACTIVE;
|
||||
s.triggerAction = sceneTriggerAction::START_RECORDING;
|
||||
}
|
||||
|
||||
if (action == STREAMING) {
|
||||
switcher->sceneTriggers.emplace_back();
|
||||
auto &s = switcher->sceneTriggers.back();
|
||||
s.scene = GetWeakSourceByName(autoStartScene.c_str());
|
||||
s.triggerType = sceneTriggerType::SCENE_ACTIVE;
|
||||
s.triggerAction = sceneTriggerAction::START_STREAMING;
|
||||
}
|
||||
|
||||
if (action == RECORINDGSTREAMING) {
|
||||
switcher->sceneTriggers.emplace_back();
|
||||
auto &s = switcher->sceneTriggers.back();
|
||||
s.scene = GetWeakSourceByName(autoStartScene.c_str());
|
||||
s.triggerType = sceneTriggerType::SCENE_ACTIVE;
|
||||
s.triggerAction = sceneTriggerAction::START_RECORDING;
|
||||
|
||||
switcher->sceneTriggers.emplace_back();
|
||||
auto &s2 = switcher->sceneTriggers.back();
|
||||
s2.scene = GetWeakSourceByName(autoStartScene.c_str());
|
||||
s2.triggerType = sceneTriggerType::SCENE_ACTIVE;
|
||||
s2.triggerAction = sceneTriggerAction::START_STREAMING;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SwitcherData::loadSceneTriggers(obs_data_t *obj)
|
||||
{
|
||||
switcher->sceneTriggers.clear();
|
||||
|
||||
loadOldAutoStopStart(obj);
|
||||
sceneTriggers.clear();
|
||||
|
||||
obs_data_array_t *triggerArray = obs_data_get_array(obj, "triggers");
|
||||
size_t count = obs_data_array_count(triggerArray);
|
||||
@@ -411,7 +327,7 @@ void SwitcherData::loadSceneTriggers(obs_data_t *obj)
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
obs_data_t *array_obj = obs_data_array_item(triggerArray, i);
|
||||
|
||||
switcher->sceneTriggers.emplace_back();
|
||||
sceneTriggers.emplace_back();
|
||||
sceneTriggers.back().load(array_obj);
|
||||
|
||||
obs_data_release(array_obj);
|
||||
|
||||
131
src/screenshot-helper.cpp
Normal file
131
src/screenshot-helper.cpp
Normal file
@@ -0,0 +1,131 @@
|
||||
#include "headers/screenshot-helper.hpp"
|
||||
|
||||
static void ScreenshotTick(void *param, float);
|
||||
|
||||
AdvSSScreenshotObj::AdvSSScreenshotObj(obs_source_t *source)
|
||||
: weakSource(OBSGetWeakRef(source))
|
||||
{
|
||||
obs_add_tick_callback(ScreenshotTick, this);
|
||||
}
|
||||
|
||||
AdvSSScreenshotObj::~AdvSSScreenshotObj()
|
||||
{
|
||||
obs_enter_graphics();
|
||||
gs_stagesurface_destroy(stagesurf);
|
||||
gs_texrender_destroy(texrender);
|
||||
obs_leave_graphics();
|
||||
|
||||
obs_remove_tick_callback(ScreenshotTick, this);
|
||||
}
|
||||
|
||||
void AdvSSScreenshotObj::Screenshot()
|
||||
{
|
||||
OBSSource source = OBSGetStrongRef(weakSource);
|
||||
|
||||
if (source) {
|
||||
cx = obs_source_get_base_width(source);
|
||||
cy = obs_source_get_base_height(source);
|
||||
} else {
|
||||
obs_video_info ovi;
|
||||
obs_get_video_info(&ovi);
|
||||
cx = ovi.base_width;
|
||||
cy = ovi.base_height;
|
||||
}
|
||||
|
||||
if (!cx || !cy) {
|
||||
blog(LOG_WARNING, "Cannot screenshot, invalid target size");
|
||||
obs_remove_tick_callback(ScreenshotTick, this);
|
||||
done = true;
|
||||
return;
|
||||
}
|
||||
|
||||
texrender = gs_texrender_create(GS_RGBA, GS_ZS_NONE);
|
||||
stagesurf = gs_stagesurface_create(cx, cy, GS_RGBA);
|
||||
|
||||
gs_texrender_reset(texrender);
|
||||
if (gs_texrender_begin(texrender, cx, cy)) {
|
||||
vec4 zero;
|
||||
vec4_zero(&zero);
|
||||
|
||||
gs_clear(GS_CLEAR_COLOR, &zero, 0.0f, 0);
|
||||
gs_ortho(0.0f, (float)cx, 0.0f, (float)cy, -100.0f, 100.0f);
|
||||
|
||||
gs_blend_state_push();
|
||||
gs_blend_function(GS_BLEND_ONE, GS_BLEND_ZERO);
|
||||
|
||||
if (source) {
|
||||
obs_source_inc_showing(source);
|
||||
obs_source_video_render(source);
|
||||
obs_source_dec_showing(source);
|
||||
} else {
|
||||
obs_render_main_texture();
|
||||
}
|
||||
|
||||
gs_blend_state_pop();
|
||||
gs_texrender_end(texrender);
|
||||
}
|
||||
}
|
||||
|
||||
void AdvSSScreenshotObj::Download()
|
||||
{
|
||||
gs_stage_texture(stagesurf, gs_texrender_get_texture(texrender));
|
||||
}
|
||||
|
||||
void AdvSSScreenshotObj::Copy()
|
||||
{
|
||||
uint8_t *videoData = nullptr;
|
||||
uint32_t videoLinesize = 0;
|
||||
|
||||
image = QImage(cx, cy, QImage::Format::Format_RGBX8888);
|
||||
|
||||
if (gs_stagesurface_map(stagesurf, &videoData, &videoLinesize)) {
|
||||
int linesize = image.bytesPerLine();
|
||||
for (int y = 0; y < (int)cy; y++)
|
||||
memcpy(image.scanLine(y),
|
||||
videoData + (y * videoLinesize), linesize);
|
||||
|
||||
gs_stagesurface_unmap(stagesurf);
|
||||
}
|
||||
}
|
||||
|
||||
void AdvSSScreenshotObj::MarkDone()
|
||||
{
|
||||
time = std::chrono::high_resolution_clock::now();
|
||||
done = true;
|
||||
}
|
||||
|
||||
#define STAGE_SCREENSHOT 0
|
||||
#define STAGE_DOWNLOAD 1
|
||||
#define STAGE_COPY_AND_SAVE 2
|
||||
#define STAGE_FINISH 3
|
||||
|
||||
static void ScreenshotTick(void *param, float)
|
||||
{
|
||||
AdvSSScreenshotObj *data =
|
||||
reinterpret_cast<AdvSSScreenshotObj *>(param);
|
||||
|
||||
if (data->stage == STAGE_FINISH) {
|
||||
return;
|
||||
}
|
||||
|
||||
obs_enter_graphics();
|
||||
|
||||
switch (data->stage) {
|
||||
case STAGE_SCREENSHOT:
|
||||
data->Screenshot();
|
||||
break;
|
||||
case STAGE_DOWNLOAD:
|
||||
data->Download();
|
||||
break;
|
||||
case STAGE_COPY_AND_SAVE:
|
||||
data->Copy();
|
||||
data->MarkDone();
|
||||
|
||||
obs_remove_tick_callback(ScreenshotTick, data);
|
||||
break;
|
||||
}
|
||||
|
||||
obs_leave_graphics();
|
||||
|
||||
data->stage++;
|
||||
}
|
||||
112
src/section.cpp
Normal file
112
src/section.cpp
Normal file
@@ -0,0 +1,112 @@
|
||||
#include <QPropertyAnimation>
|
||||
#include "headers/section.hpp"
|
||||
#include "headers/utility.hpp"
|
||||
|
||||
Section::Section(const int animationDuration, QWidget *parent)
|
||||
: QWidget(parent), _animationDuration(animationDuration)
|
||||
{
|
||||
_toggleButton = new QToolButton(this);
|
||||
_headerLine = new QFrame(this);
|
||||
_mainLayout = new QGridLayout(this);
|
||||
_headerWidgetLayout = new QHBoxLayout();
|
||||
|
||||
_toggleButton->setStyleSheet("QToolButton {border: none;}");
|
||||
_toggleButton->setToolButtonStyle(Qt::ToolButtonIconOnly);
|
||||
_toggleButton->setArrowType(Qt::ArrowType::RightArrow);
|
||||
_toggleButton->setCheckable(true);
|
||||
_toggleButton->setChecked(true);
|
||||
|
||||
_headerLine->setFrameShape(QFrame::HLine);
|
||||
_headerLine->setFrameShadow(QFrame::Sunken);
|
||||
_headerLine->setSizePolicy(QSizePolicy::Expanding,
|
||||
QSizePolicy::Maximum);
|
||||
|
||||
// Don't waste space
|
||||
_mainLayout->setVerticalSpacing(0);
|
||||
_mainLayout->setContentsMargins(0, 0, 0, 0);
|
||||
|
||||
// But add some spacing for widgets in header
|
||||
_headerWidgetLayout->setSpacing(11);
|
||||
_headerWidgetLayout->addWidget(_toggleButton);
|
||||
|
||||
int row = 0;
|
||||
_mainLayout->addLayout(_headerWidgetLayout, row, 0, 1, 1,
|
||||
Qt::AlignLeft);
|
||||
_mainLayout->addWidget(_headerLine, row++, 2, 1, 1);
|
||||
setLayout(_mainLayout);
|
||||
|
||||
connect(_toggleButton, &QToolButton::toggled, this, &Section::Collapse);
|
||||
}
|
||||
|
||||
void Section::Collapse(bool collapsed)
|
||||
{
|
||||
_toggleButton->setChecked(collapsed);
|
||||
_toggleButton->setArrowType(!collapsed ? Qt::ArrowType::DownArrow
|
||||
: Qt::ArrowType::RightArrow);
|
||||
_toggleAnimation->setDirection(!collapsed
|
||||
? QAbstractAnimation::Forward
|
||||
: QAbstractAnimation::Backward);
|
||||
_toggleAnimation->start();
|
||||
}
|
||||
|
||||
void Section::SetContent(QWidget *w)
|
||||
{
|
||||
// Clean up previous content
|
||||
if (_contentArea) {
|
||||
auto oldLayout = _contentArea->layout();
|
||||
if (oldLayout) {
|
||||
clearLayout(oldLayout);
|
||||
delete oldLayout;
|
||||
}
|
||||
}
|
||||
|
||||
delete _contentArea;
|
||||
delete _toggleAnimation;
|
||||
|
||||
// Setup contentArea
|
||||
_contentArea = new QScrollArea(this);
|
||||
_contentArea->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
|
||||
|
||||
// Start out collapsed
|
||||
_contentArea->setMaximumHeight(0);
|
||||
_contentArea->setMinimumHeight(0);
|
||||
|
||||
auto newLayout = new QVBoxLayout();
|
||||
newLayout->addWidget(w);
|
||||
_contentArea->setLayout(newLayout);
|
||||
_mainLayout->addWidget(_contentArea, 1, 0, 1, 3);
|
||||
|
||||
// Animation Setup
|
||||
_toggleAnimation = new QParallelAnimationGroup(this);
|
||||
_toggleAnimation->addAnimation(
|
||||
new QPropertyAnimation(this, "minimumHeight"));
|
||||
_toggleAnimation->addAnimation(
|
||||
new QPropertyAnimation(this, "maximumHeight"));
|
||||
_toggleAnimation->addAnimation(
|
||||
new QPropertyAnimation(_contentArea, "maximumHeight"));
|
||||
|
||||
const auto collapsedHeight =
|
||||
sizeHint().height() - _contentArea->maximumHeight();
|
||||
auto contentHeight = newLayout->sizeHint().height();
|
||||
|
||||
for (int i = 0; i < _toggleAnimation->animationCount() - 1; ++i) {
|
||||
QPropertyAnimation *SectionAnimation =
|
||||
static_cast<QPropertyAnimation *>(
|
||||
_toggleAnimation->animationAt(i));
|
||||
SectionAnimation->setDuration(_animationDuration);
|
||||
SectionAnimation->setStartValue(collapsedHeight);
|
||||
SectionAnimation->setEndValue(collapsedHeight + contentHeight);
|
||||
}
|
||||
|
||||
QPropertyAnimation *contentAnimation =
|
||||
static_cast<QPropertyAnimation *>(_toggleAnimation->animationAt(
|
||||
_toggleAnimation->animationCount() - 1));
|
||||
contentAnimation->setDuration(_animationDuration);
|
||||
contentAnimation->setStartValue(0);
|
||||
contentAnimation->setEndValue(contentHeight);
|
||||
}
|
||||
|
||||
void Section::AddHeaderWidget(QWidget *w)
|
||||
{
|
||||
_headerWidgetLayout->addWidget(w);
|
||||
}
|
||||
@@ -93,13 +93,7 @@ void AdvSceneSwitcher::on_audioFallback_toggled(bool on)
|
||||
void SwitcherData::checkAudioSwitchFallback(OBSWeakSource &scene,
|
||||
OBSWeakSource &transition)
|
||||
{
|
||||
bool durationReached =
|
||||
((unsigned long long)audioFallback.matchCount * interval) /
|
||||
1000.0 >=
|
||||
audioFallback.duration;
|
||||
|
||||
if (durationReached) {
|
||||
|
||||
if (audioFallback.duration.DurationReached()) {
|
||||
scene = audioFallback.getScene();
|
||||
transition = audioFallback.transition;
|
||||
|
||||
@@ -107,17 +101,16 @@ void SwitcherData::checkAudioSwitchFallback(OBSWeakSource &scene,
|
||||
audioFallback.logMatch();
|
||||
}
|
||||
}
|
||||
|
||||
audioFallback.matchCount++;
|
||||
}
|
||||
|
||||
void SwitcherData::checkAudioSwitch(bool &match, OBSWeakSource &scene,
|
||||
bool SwitcherData::checkAudioSwitch(OBSWeakSource &scene,
|
||||
OBSWeakSource &transition)
|
||||
{
|
||||
if (AudioSwitch::pause) {
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool match = false;
|
||||
bool fallbackChecked = false; // false if only one or no match
|
||||
|
||||
for (AudioSwitch &s : audioSwitches) {
|
||||
@@ -150,18 +143,11 @@ void SwitcherData::checkAudioSwitch(bool &match, OBSWeakSource &scene,
|
||||
// Reset for next check
|
||||
s.peak = -FLT_MAX;
|
||||
|
||||
if (volumeThresholdreached) {
|
||||
s.matchCount++;
|
||||
} else {
|
||||
s.matchCount = 0;
|
||||
if (!volumeThresholdreached) {
|
||||
s.duration.Reset();
|
||||
}
|
||||
|
||||
bool durationReached =
|
||||
((unsigned long long)s.matchCount * interval) /
|
||||
1000.0 >=
|
||||
s.duration;
|
||||
|
||||
if (volumeThresholdreached && durationReached) {
|
||||
if (volumeThresholdreached && s.duration.DurationReached()) {
|
||||
if (match) {
|
||||
checkAudioSwitchFallback(scene, transition);
|
||||
fallbackChecked = true;
|
||||
@@ -183,14 +169,16 @@ void SwitcherData::checkAudioSwitch(bool &match, OBSWeakSource &scene,
|
||||
}
|
||||
|
||||
if (!fallbackChecked) {
|
||||
audioFallback.matchCount = 0;
|
||||
audioFallback.duration.Reset();
|
||||
}
|
||||
|
||||
return match;
|
||||
}
|
||||
|
||||
void SwitcherData::saveAudioSwitches(obs_data_t *obj)
|
||||
{
|
||||
obs_data_array_t *audioArray = obs_data_array_create();
|
||||
for (AudioSwitch &s : switcher->audioSwitches) {
|
||||
for (AudioSwitch &s : audioSwitches) {
|
||||
obs_data_t *array_obj = obs_data_create();
|
||||
|
||||
s.save(array_obj);
|
||||
@@ -206,7 +194,7 @@ void SwitcherData::saveAudioSwitches(obs_data_t *obj)
|
||||
|
||||
void SwitcherData::loadAudioSwitches(obs_data_t *obj)
|
||||
{
|
||||
switcher->audioSwitches.clear();
|
||||
audioSwitches.clear();
|
||||
|
||||
obs_data_array_t *audioArray = obs_data_get_array(obj, "audioSwitches");
|
||||
size_t count = obs_data_array_count(audioArray);
|
||||
@@ -214,7 +202,7 @@ void SwitcherData::loadAudioSwitches(obs_data_t *obj)
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
obs_data_t *array_obj = obs_data_array_item(audioArray, i);
|
||||
|
||||
switcher->audioSwitches.emplace_back();
|
||||
audioSwitches.emplace_back();
|
||||
audioSwitches.back().load(array_obj);
|
||||
|
||||
obs_data_release(array_obj);
|
||||
@@ -307,45 +295,12 @@ void AudioSwitch::save(obs_data_t *obj)
|
||||
|
||||
obs_data_set_int(obj, "volume", volumeThreshold);
|
||||
obs_data_set_int(obj, "condition", condition);
|
||||
obs_data_set_double(obj, "duration", duration);
|
||||
duration.Save(obj, "duration");
|
||||
obs_data_set_bool(obj, "ignoreInactiveSource", ignoreInactiveSource);
|
||||
}
|
||||
|
||||
// To be removed in future version
|
||||
bool loadOldAudio(obs_data_t *obj, AudioSwitch *s)
|
||||
{
|
||||
if (!s) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const char *scene = obs_data_get_string(obj, "scene");
|
||||
|
||||
if (strcmp(scene, "") == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
s->scene = GetWeakSourceByName(scene);
|
||||
|
||||
const char *transition = obs_data_get_string(obj, "transition");
|
||||
s->transition = GetWeakTransitionByName(transition);
|
||||
|
||||
const char *audioSource = obs_data_get_string(obj, "audioSource");
|
||||
s->audioSource = GetWeakSourceByName(audioSource);
|
||||
|
||||
s->volumeThreshold = obs_data_get_int(obj, "volume");
|
||||
s->condition = (audioCondition)obs_data_get_int(obj, "condition");
|
||||
s->duration = obs_data_get_double(obj, "duration");
|
||||
s->usePreviousScene = strcmp(scene, previous_scene_name) == 0;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void AudioSwitch::load(obs_data_t *obj)
|
||||
{
|
||||
if (loadOldAudio(obj, this)) {
|
||||
return;
|
||||
}
|
||||
|
||||
SceneSwitcherEntry::load(obj);
|
||||
|
||||
const char *audioSourceName = obs_data_get_string(obj, "audioSource");
|
||||
@@ -353,7 +308,7 @@ void AudioSwitch::load(obs_data_t *obj)
|
||||
|
||||
volumeThreshold = obs_data_get_int(obj, "volume");
|
||||
condition = (audioCondition)obs_data_get_int(obj, "condition");
|
||||
duration = obs_data_get_double(obj, "duration");
|
||||
duration.Load(obj, "duration");
|
||||
ignoreInactiveSource = obs_data_get_bool(obj, "ignoreInactiveSource");
|
||||
|
||||
volmeter = AddVolmeterToSource(this, audioSource);
|
||||
@@ -366,7 +321,7 @@ void AudioSwitchFallback::save(obs_data_t *obj)
|
||||
"audioFallbackTransition");
|
||||
|
||||
obs_data_set_bool(obj, "audioFallbackEnable", enable);
|
||||
obs_data_set_double(obj, "audioFallbackDuration", duration);
|
||||
duration.Save(obj, "audioFallbackDuration");
|
||||
}
|
||||
|
||||
void AudioSwitchFallback::load(obs_data_t *obj)
|
||||
@@ -376,7 +331,7 @@ void AudioSwitchFallback::load(obs_data_t *obj)
|
||||
"audioFallbackTransition");
|
||||
|
||||
enable = obs_data_get_bool(obj, "audioFallbackEnable");
|
||||
duration = obs_data_get_double(obj, "audioFallbackDuration");
|
||||
duration.Load(obj, "audioFallbackDuration");
|
||||
}
|
||||
|
||||
AudioSwitch::AudioSwitch(const AudioSwitch &other)
|
||||
@@ -461,7 +416,7 @@ AudioSwitchWidget::AudioSwitchWidget(QWidget *parent, AudioSwitch *s)
|
||||
audioSources = new QComboBox();
|
||||
condition = new QComboBox();
|
||||
audioVolumeThreshold = new QSpinBox();
|
||||
duration = new QDoubleSpinBox();
|
||||
duration = new DurationSelection(this, false);
|
||||
ignoreInactiveSource = new QCheckBox(obs_module_text(
|
||||
"AdvSceneSwitcher.audioTab.ignoreInactiveSource"));
|
||||
|
||||
@@ -476,10 +431,6 @@ AudioSwitchWidget::AudioSwitchWidget(QWidget *parent, AudioSwitch *s)
|
||||
audioVolumeThreshold->setMaximum(100);
|
||||
audioVolumeThreshold->setMinimum(0);
|
||||
|
||||
duration->setMinimum(0.0);
|
||||
duration->setMaximum(99.000000);
|
||||
duration->setSuffix("s");
|
||||
|
||||
QWidget::connect(volMeter->GetSlider(), SIGNAL(valueChanged(int)),
|
||||
audioVolumeThreshold, SLOT(setValue(int)));
|
||||
QWidget::connect(audioVolumeThreshold, SIGNAL(valueChanged(int)),
|
||||
@@ -488,7 +439,7 @@ AudioSwitchWidget::AudioSwitchWidget(QWidget *parent, AudioSwitch *s)
|
||||
SLOT(VolumeThresholdChanged(int)));
|
||||
QWidget::connect(condition, SIGNAL(currentIndexChanged(int)), this,
|
||||
SLOT(ConditionChanged(int)));
|
||||
QWidget::connect(duration, SIGNAL(valueChanged(double)), this,
|
||||
QWidget::connect(duration, SIGNAL(DurationChanged(double)), this,
|
||||
SLOT(DurationChanged(double)));
|
||||
QWidget::connect(audioSources,
|
||||
SIGNAL(currentTextChanged(const QString &)), this,
|
||||
@@ -504,7 +455,7 @@ AudioSwitchWidget::AudioSwitchWidget(QWidget *parent, AudioSwitch *s)
|
||||
GetWeakSourceName(s->audioSource).c_str());
|
||||
audioVolumeThreshold->setValue(s->volumeThreshold);
|
||||
condition->setCurrentIndex(s->condition);
|
||||
duration->setValue(s->duration);
|
||||
duration->SetDuration(s->duration);
|
||||
ignoreInactiveSource->setChecked(s->ignoreInactiveSource);
|
||||
}
|
||||
|
||||
@@ -604,14 +555,14 @@ void AudioSwitchWidget::ConditionChanged(int cond)
|
||||
switchData->condition = (audioCondition)cond;
|
||||
}
|
||||
|
||||
void AudioSwitchWidget::DurationChanged(double dur)
|
||||
void AudioSwitchWidget::DurationChanged(double sec)
|
||||
{
|
||||
if (loading || !switchData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
switchData->duration = dur;
|
||||
switchData->duration.seconds = sec;
|
||||
}
|
||||
|
||||
void AudioSwitchWidget::IgnoreInactiveChanged(int state)
|
||||
@@ -628,17 +579,13 @@ AudioSwitchFallbackWidget::AudioSwitchFallbackWidget(QWidget *parent,
|
||||
AudioSwitchFallback *s)
|
||||
: SwitchWidget(parent, s, true, true)
|
||||
{
|
||||
duration = new QDoubleSpinBox();
|
||||
duration = new DurationSelection(this, false);
|
||||
|
||||
duration->setMinimum(0.0);
|
||||
duration->setMaximum(99.000000);
|
||||
duration->setSuffix("s");
|
||||
|
||||
QWidget::connect(duration, SIGNAL(valueChanged(double)), this,
|
||||
QWidget::connect(duration, SIGNAL(DurationChanged(double)), this,
|
||||
SLOT(DurationChanged(double)));
|
||||
|
||||
if (s) {
|
||||
duration->setValue(s->duration);
|
||||
duration->SetDuration(s->duration);
|
||||
}
|
||||
|
||||
QHBoxLayout *mainLayout = new QHBoxLayout;
|
||||
@@ -656,12 +603,12 @@ AudioSwitchFallbackWidget::AudioSwitchFallbackWidget(QWidget *parent,
|
||||
loading = false;
|
||||
}
|
||||
|
||||
void AudioSwitchFallbackWidget::DurationChanged(double dur)
|
||||
void AudioSwitchFallbackWidget::DurationChanged(double sec)
|
||||
{
|
||||
if (loading || !switchData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
switchData->duration = dur;
|
||||
switchData->duration.seconds = sec;
|
||||
}
|
||||
|
||||
@@ -77,16 +77,17 @@ void AdvSceneSwitcher::on_executableDown_clicked()
|
||||
switcher->executableSwitches[index + 1]);
|
||||
}
|
||||
|
||||
void SwitcherData::checkExeSwitch(bool &match, OBSWeakSource &scene,
|
||||
bool SwitcherData::checkExeSwitch(OBSWeakSource &scene,
|
||||
OBSWeakSource &transition)
|
||||
{
|
||||
if (executableSwitches.size() == 0 || ExecutableSwitch::pause) {
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string title;
|
||||
QStringList runningProcesses;
|
||||
bool ignored = false;
|
||||
bool match = false;
|
||||
|
||||
// Check if current window is ignored
|
||||
GetCurrentWindowTitle(title);
|
||||
@@ -135,12 +136,14 @@ void SwitcherData::checkExeSwitch(bool &match, OBSWeakSource &scene,
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return match;
|
||||
}
|
||||
|
||||
void SwitcherData::saveExecutableSwitches(obs_data_t *obj)
|
||||
{
|
||||
obs_data_array_t *executableArray = obs_data_array_create();
|
||||
for (ExecutableSwitch &s : switcher->executableSwitches) {
|
||||
for (ExecutableSwitch &s : executableSwitches) {
|
||||
obs_data_t *array_obj = obs_data_create();
|
||||
|
||||
s.save(array_obj);
|
||||
@@ -154,7 +157,7 @@ void SwitcherData::saveExecutableSwitches(obs_data_t *obj)
|
||||
|
||||
void SwitcherData::loadExecutableSwitches(obs_data_t *obj)
|
||||
{
|
||||
switcher->executableSwitches.clear();
|
||||
executableSwitches.clear();
|
||||
|
||||
obs_data_array_t *executableArray =
|
||||
obs_data_get_array(obj, "executableSwitches");
|
||||
@@ -163,7 +166,7 @@ void SwitcherData::loadExecutableSwitches(obs_data_t *obj)
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
obs_data_t *array_obj = obs_data_array_item(executableArray, i);
|
||||
|
||||
switcher->executableSwitches.emplace_back();
|
||||
executableSwitches.emplace_back();
|
||||
executableSwitches.back().load(array_obj);
|
||||
|
||||
obs_data_release(array_obj);
|
||||
@@ -199,37 +202,8 @@ void ExecutableSwitch::save(obs_data_t *obj)
|
||||
obs_data_set_bool(obj, "infocus", inFocus);
|
||||
}
|
||||
|
||||
// To be removed in future version
|
||||
bool loadOldExe(obs_data_t *obj, ExecutableSwitch *s)
|
||||
{
|
||||
if (!s) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const char *scene = obs_data_get_string(obj, "scene");
|
||||
|
||||
if (strcmp(scene, "") == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
s->scene = GetWeakSourceByName(scene);
|
||||
|
||||
const char *transition = obs_data_get_string(obj, "transition");
|
||||
s->transition = GetWeakTransitionByName(transition);
|
||||
|
||||
s->exe = obs_data_get_string(obj, "exefile");
|
||||
s->inFocus = obs_data_get_bool(obj, "infocus");
|
||||
s->usePreviousScene = strcmp(scene, previous_scene_name) == 0;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ExecutableSwitch::load(obs_data_t *obj)
|
||||
{
|
||||
if (loadOldExe(obj, this)) {
|
||||
return;
|
||||
}
|
||||
|
||||
SceneSwitcherEntry::load(obj);
|
||||
|
||||
exe = obs_data_get_string(obj, "exefile");
|
||||
|
||||
@@ -115,19 +115,20 @@ void SwitcherData::writeToStatusFile(QString msg)
|
||||
file.close();
|
||||
}
|
||||
|
||||
void SwitcherData::checkSwitchInfoFromFile(bool &match, OBSWeakSource &scene,
|
||||
bool SwitcherData::checkSwitchInfoFromFile(OBSWeakSource &scene,
|
||||
OBSWeakSource &transition)
|
||||
{
|
||||
if (!fileIO.readEnabled || fileIO.readPath.empty() ||
|
||||
FileSwitch::pause) {
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
QFile file(QString::fromStdString(fileIO.readPath));
|
||||
if (!file.open(QIODevice::ReadOnly)) {
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool match = false;
|
||||
QTextStream in(&file);
|
||||
|
||||
QString sceneStr = in.readLine();
|
||||
@@ -149,6 +150,8 @@ void SwitcherData::checkSwitchInfoFromFile(bool &match, OBSWeakSource &scene,
|
||||
fileIO.readPath.c_str());
|
||||
}
|
||||
file.close();
|
||||
|
||||
return match;
|
||||
}
|
||||
|
||||
static size_t WriteCallback(void *contents, size_t size, size_t nmemb,
|
||||
@@ -158,7 +161,7 @@ static size_t WriteCallback(void *contents, size_t size, size_t nmemb,
|
||||
return size * nmemb;
|
||||
}
|
||||
|
||||
std::string getRemoteData(std::string &url)
|
||||
static std::string getRemoteData(std::string &url)
|
||||
{
|
||||
std::string readBuffer;
|
||||
|
||||
@@ -172,32 +175,6 @@ std::string getRemoteData(std::string &url)
|
||||
return readBuffer;
|
||||
}
|
||||
|
||||
bool compareIgnoringLineEnding(QString &s1, QString &s2)
|
||||
{
|
||||
/*
|
||||
Im using QTextStream here so the conversion between different lineendings is done by QT.
|
||||
QT itself uses only the linefeed internally so the input by the user is always using that,
|
||||
but the files selected by the user might use different line endings.
|
||||
If you are reading this and know of a cleaner way to do this, please let me know :)
|
||||
*/
|
||||
QTextStream s1stream(&s1);
|
||||
QTextStream s2stream(&s2);
|
||||
|
||||
while (!s1stream.atEnd() || !s2stream.atEnd()) {
|
||||
QString s1s = s1stream.readLine();
|
||||
QString s2s = s2stream.readLine();
|
||||
if (s1s != s2s) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!s1stream.atEnd() && !s2stream.atEnd()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool matchFileContent(QString &filedata, FileSwitch &s)
|
||||
{
|
||||
if (s.onlyMatchIfChanged) {
|
||||
@@ -247,13 +224,14 @@ bool checkLocalFileContent(FileSwitch &s)
|
||||
return match;
|
||||
}
|
||||
|
||||
void SwitcherData::checkFileContent(bool &match, OBSWeakSource &scene,
|
||||
bool SwitcherData::checkFileContent(OBSWeakSource &scene,
|
||||
OBSWeakSource &transition)
|
||||
{
|
||||
if (FileSwitch::pause) {
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool match = false;
|
||||
for (FileSwitch &s : fileSwitches) {
|
||||
if (!s.initialized()) {
|
||||
continue;
|
||||
@@ -277,6 +255,7 @@ void SwitcherData::checkFileContent(bool &match, OBSWeakSource &scene,
|
||||
break;
|
||||
}
|
||||
}
|
||||
return match;
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::on_fileAdd_clicked()
|
||||
@@ -373,7 +352,7 @@ void AdvSceneSwitcher::on_fileDown_clicked()
|
||||
void SwitcherData::saveFileSwitches(obs_data_t *obj)
|
||||
{
|
||||
obs_data_array_t *fileArray = obs_data_array_create();
|
||||
for (FileSwitch &s : switcher->fileSwitches) {
|
||||
for (FileSwitch &s : fileSwitches) {
|
||||
obs_data_t *array_obj = obs_data_create();
|
||||
|
||||
s.save(array_obj);
|
||||
@@ -384,23 +363,22 @@ void SwitcherData::saveFileSwitches(obs_data_t *obj)
|
||||
obs_data_set_array(obj, "fileSwitches", fileArray);
|
||||
obs_data_array_release(fileArray);
|
||||
|
||||
obs_data_set_bool(obj, "readEnabled", switcher->fileIO.readEnabled);
|
||||
obs_data_set_string(obj, "readPath", switcher->fileIO.readPath.c_str());
|
||||
obs_data_set_bool(obj, "writeEnabled", switcher->fileIO.writeEnabled);
|
||||
obs_data_set_string(obj, "writePath",
|
||||
switcher->fileIO.writePath.c_str());
|
||||
obs_data_set_bool(obj, "readEnabled", fileIO.readEnabled);
|
||||
obs_data_set_string(obj, "readPath", fileIO.readPath.c_str());
|
||||
obs_data_set_bool(obj, "writeEnabled", fileIO.writeEnabled);
|
||||
obs_data_set_string(obj, "writePath", fileIO.writePath.c_str());
|
||||
}
|
||||
|
||||
void SwitcherData::loadFileSwitches(obs_data_t *obj)
|
||||
{
|
||||
switcher->fileSwitches.clear();
|
||||
fileSwitches.clear();
|
||||
obs_data_array_t *fileArray = obs_data_get_array(obj, "fileSwitches");
|
||||
size_t count = obs_data_array_count(fileArray);
|
||||
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
obs_data_t *array_obj = obs_data_array_item(fileArray, i);
|
||||
|
||||
switcher->fileSwitches.emplace_back();
|
||||
fileSwitches.emplace_back();
|
||||
fileSwitches.back().load(array_obj);
|
||||
|
||||
obs_data_release(array_obj);
|
||||
@@ -408,11 +386,11 @@ void SwitcherData::loadFileSwitches(obs_data_t *obj)
|
||||
obs_data_array_release(fileArray);
|
||||
|
||||
obs_data_set_default_bool(obj, "readEnabled", false);
|
||||
switcher->fileIO.readEnabled = obs_data_get_bool(obj, "readEnabled");
|
||||
switcher->fileIO.readPath = obs_data_get_string(obj, "readPath");
|
||||
fileIO.readEnabled = obs_data_get_bool(obj, "readEnabled");
|
||||
fileIO.readPath = obs_data_get_string(obj, "readPath");
|
||||
obs_data_set_default_bool(obj, "writeEnabled", false);
|
||||
switcher->fileIO.writeEnabled = obs_data_get_bool(obj, "writeEnabled");
|
||||
switcher->fileIO.writePath = obs_data_get_string(obj, "writePath");
|
||||
fileIO.writeEnabled = obs_data_get_bool(obj, "writeEnabled");
|
||||
fileIO.writePath = obs_data_get_string(obj, "writePath");
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::setupFileTab()
|
||||
@@ -470,41 +448,8 @@ void FileSwitch::save(obs_data_t *obj)
|
||||
obs_data_set_bool(obj, "onlyMatchIfChanged", onlyMatchIfChanged);
|
||||
}
|
||||
|
||||
// To be removed in future version
|
||||
bool loadOldFile(obs_data_t *obj, FileSwitch *s)
|
||||
{
|
||||
if (!s) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const char *scene = obs_data_get_string(obj, "scene");
|
||||
|
||||
if (strcmp(scene, "") == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
s->scene = GetWeakSourceByName(scene);
|
||||
|
||||
const char *transition = obs_data_get_string(obj, "transition");
|
||||
s->transition = GetWeakTransitionByName(transition);
|
||||
|
||||
s->file = obs_data_get_string(obj, "file");
|
||||
s->text = obs_data_get_string(obj, "text");
|
||||
s->remote = obs_data_get_bool(obj, "remote");
|
||||
s->useRegex = obs_data_get_bool(obj, "useRegex");
|
||||
s->useTime = obs_data_get_bool(obj, "useTime");
|
||||
s->onlyMatchIfChanged = obs_data_get_bool(obj, "onlyMatchIfChanged");
|
||||
s->usePreviousScene = strcmp(scene, previous_scene_name) == 0;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void FileSwitch::load(obs_data_t *obj)
|
||||
{
|
||||
if (loadOldFile(obj, this)) {
|
||||
return;
|
||||
}
|
||||
|
||||
SceneSwitcherEntry::load(obj);
|
||||
|
||||
file = obs_data_get_string(obj, "file");
|
||||
|
||||
@@ -1,6 +1,34 @@
|
||||
#include "headers/advanced-scene-switcher.hpp"
|
||||
#include "headers/utility.hpp"
|
||||
|
||||
static inline bool SceneGroupValid(SceneGroup *group)
|
||||
{
|
||||
if (group) {
|
||||
return group->name != invalid_scene_group_name;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline SceneGroup *GetSceneGroupByName(const char *name)
|
||||
{
|
||||
if (!switcher) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
for (SceneGroup &sg : switcher->sceneGroups) {
|
||||
if (sg.name == name) {
|
||||
return &sg;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static inline SceneGroup *GetSceneGroupByQString(const QString &name)
|
||||
{
|
||||
return GetSceneGroupByName(name.toUtf8().constData());
|
||||
}
|
||||
|
||||
bool SceneSwitcherEntry::initialized()
|
||||
{
|
||||
return (usePreviousScene || WeakSourceValid(scene) ||
|
||||
|
||||
@@ -6,16 +6,17 @@
|
||||
bool IdleData::pause = false;
|
||||
IdleWidget *idleWidget = nullptr;
|
||||
|
||||
void SwitcherData::checkIdleSwitch(bool &match, OBSWeakSource &scene,
|
||||
bool SwitcherData::checkIdleSwitch(OBSWeakSource &scene,
|
||||
OBSWeakSource &transition)
|
||||
{
|
||||
if (!idleData.idleEnable || IdleData::pause) {
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string title;
|
||||
bool ignoreIdle = false;
|
||||
GetCurrentWindowTitle(title);
|
||||
bool match = false;
|
||||
|
||||
for (std::string &window : ignoreIdleWindows) {
|
||||
if (window == title) {
|
||||
@@ -40,7 +41,7 @@ void SwitcherData::checkIdleSwitch(bool &match, OBSWeakSource &scene,
|
||||
|
||||
if (!ignoreIdle && secondsSinceLastInput() > idleData.time) {
|
||||
if (idleData.alreadySwitched) {
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
scene = idleData.getScene();
|
||||
transition = idleData.transition;
|
||||
@@ -50,8 +51,11 @@ void SwitcherData::checkIdleSwitch(bool &match, OBSWeakSource &scene,
|
||||
if (verbose) {
|
||||
idleData.logMatch();
|
||||
}
|
||||
} else
|
||||
} else {
|
||||
idleData.alreadySwitched = false;
|
||||
}
|
||||
|
||||
return match;
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::on_idleCheckBox_stateChanged(int state)
|
||||
@@ -164,7 +168,7 @@ int AdvSceneSwitcher::IgnoreIdleWindowsFindByData(const QString &window)
|
||||
void SwitcherData::saveIdleSwitches(obs_data_t *obj)
|
||||
{
|
||||
obs_data_array_t *ignoreIdleWindowsArray = obs_data_array_create();
|
||||
for (std::string &window : switcher->ignoreIdleWindows) {
|
||||
for (std::string &window : ignoreIdleWindows) {
|
||||
obs_data_t *array_obj = obs_data_create();
|
||||
obs_data_set_string(array_obj, "window", window.c_str());
|
||||
obs_data_array_push_back(ignoreIdleWindowsArray, array_obj);
|
||||
@@ -178,7 +182,7 @@ void SwitcherData::saveIdleSwitches(obs_data_t *obj)
|
||||
|
||||
void SwitcherData::loadIdleSwitches(obs_data_t *obj)
|
||||
{
|
||||
switcher->ignoreIdleWindows.clear();
|
||||
ignoreIdleWindows.clear();
|
||||
|
||||
obs_data_array_t *ignoreIdleWindowsArray =
|
||||
obs_data_get_array(obj, "ignoreIdleWindows");
|
||||
@@ -190,7 +194,7 @@ void SwitcherData::loadIdleSwitches(obs_data_t *obj)
|
||||
|
||||
const char *window = obs_data_get_string(array_obj, "window");
|
||||
|
||||
switcher->ignoreIdleWindows.emplace_back(window);
|
||||
ignoreIdleWindows.emplace_back(window);
|
||||
|
||||
obs_data_release(array_obj);
|
||||
}
|
||||
|
||||
@@ -80,13 +80,14 @@ void AdvSceneSwitcher::on_mediaDown_clicked()
|
||||
switcher->mediaSwitches[index + 1]);
|
||||
}
|
||||
|
||||
void SwitcherData::checkMediaSwitch(bool &match, OBSWeakSource &scene,
|
||||
bool SwitcherData::checkMediaSwitch(OBSWeakSource &scene,
|
||||
OBSWeakSource &transition)
|
||||
{
|
||||
if (MediaSwitch::pause) {
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool match = false;
|
||||
for (MediaSwitch &mediaSwitch : mediaSwitches) {
|
||||
if (!mediaSwitch.initialized()) {
|
||||
continue;
|
||||
@@ -196,12 +197,13 @@ void SwitcherData::checkMediaSwitch(bool &match, OBSWeakSource &scene,
|
||||
break;
|
||||
}
|
||||
}
|
||||
return match;
|
||||
}
|
||||
|
||||
void SwitcherData::saveMediaSwitches(obs_data_t *obj)
|
||||
{
|
||||
obs_data_array_t *mediaArray = obs_data_array_create();
|
||||
for (MediaSwitch &s : switcher->mediaSwitches) {
|
||||
for (MediaSwitch &s : mediaSwitches) {
|
||||
obs_data_t *array_obj = obs_data_create();
|
||||
|
||||
s.save(array_obj);
|
||||
@@ -216,13 +218,13 @@ void SwitcherData::saveMediaSwitches(obs_data_t *obj)
|
||||
void SwitcherData::loadMediaSwitches(obs_data_t *obj)
|
||||
{
|
||||
obs_data_array_t *mediaArray = obs_data_get_array(obj, "mediaSwitches");
|
||||
switcher->mediaSwitches.clear();
|
||||
mediaSwitches.clear();
|
||||
size_t count = obs_data_array_count(mediaArray);
|
||||
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
obs_data_t *array_obj = obs_data_array_item(mediaArray, i);
|
||||
|
||||
switcher->mediaSwitches.emplace_back();
|
||||
mediaSwitches.emplace_back();
|
||||
mediaSwitches.back().load(array_obj);
|
||||
|
||||
obs_data_release(array_obj);
|
||||
@@ -271,42 +273,8 @@ void MediaSwitch::save(obs_data_t *obj)
|
||||
obs_data_set_int(obj, "time", time);
|
||||
}
|
||||
|
||||
// To be removed in future version
|
||||
bool loadOldMedia(obs_data_t *obj, MediaSwitch *s)
|
||||
{
|
||||
if (!s) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const char *scene = obs_data_get_string(obj, "scene");
|
||||
|
||||
if (strcmp(scene, "") == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
s->scene = GetWeakSourceByName(scene);
|
||||
|
||||
const char *transition = obs_data_get_string(obj, "transition");
|
||||
s->transition = GetWeakTransitionByName(transition);
|
||||
|
||||
const char *source = obs_data_get_string(obj, "source");
|
||||
s->source = GetWeakSourceByName(source);
|
||||
|
||||
s->state = (obs_media_state)obs_data_get_int(obj, "state");
|
||||
s->restriction = (time_restriction)obs_data_get_int(obj, "restriction");
|
||||
s->time = obs_data_get_int(obj, "time");
|
||||
s->usePreviousScene = strcmp(scene, previous_scene_name) == 0;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void MediaSwitch::load(obs_data_t *obj)
|
||||
{
|
||||
|
||||
if (loadOldMedia(obj, this)) {
|
||||
return;
|
||||
}
|
||||
|
||||
SceneSwitcherEntry::load(obj);
|
||||
|
||||
const char *sourceName = obs_data_get_string(obj, "source");
|
||||
@@ -432,7 +400,7 @@ void swap(MediaSwitch &first, MediaSwitch &second)
|
||||
second.resetSignalHandler();
|
||||
}
|
||||
|
||||
void populateMediaStates(QComboBox *list)
|
||||
static void populateMediaStates(QComboBox *list)
|
||||
{
|
||||
list->addItem(obs_module_text("AdvSceneSwitcher.mediaTab.states.none"));
|
||||
list->addItem(
|
||||
@@ -454,7 +422,7 @@ void populateMediaStates(QComboBox *list)
|
||||
list->addItem(obs_module_text("AdvSceneSwitcher.mediaTab.states.any"));
|
||||
}
|
||||
|
||||
void populateTimeRestrictions(QComboBox *list)
|
||||
static void populateTimeRestrictions(QComboBox *list)
|
||||
{
|
||||
list->addItem(obs_module_text(
|
||||
"AdvSceneSwitcher.mediaTab.timeRestriction.none"));
|
||||
|
||||
@@ -23,6 +23,7 @@ Most of this code is based on https://github.com/Palakis/obs-websocket
|
||||
|
||||
#define SCENE_ENTRY "scene"
|
||||
#define TRANSITION_ENTRY "transition"
|
||||
#define TRANSITION_DURATION "duration"
|
||||
|
||||
using websocketpp::lib::placeholders::_1;
|
||||
using websocketpp::lib::placeholders::_2;
|
||||
@@ -191,17 +192,18 @@ void WSServer::stop()
|
||||
blog(LOG_INFO, "server stopped successfully");
|
||||
}
|
||||
|
||||
void WSServer::sendMessage(OBSWeakSource scene, OBSWeakSource transition)
|
||||
void WSServer::sendMessage(sceneSwitchInfo sceneSwitch)
|
||||
{
|
||||
if (!scene) {
|
||||
if (!sceneSwitch.scene) {
|
||||
return;
|
||||
}
|
||||
|
||||
OBSData data = obs_data_create();
|
||||
obs_data_set_string(data, SCENE_ENTRY,
|
||||
GetWeakSourceName(scene).c_str());
|
||||
GetWeakSourceName(sceneSwitch.scene).c_str());
|
||||
obs_data_set_string(data, TRANSITION_ENTRY,
|
||||
GetWeakSourceName(transition).c_str());
|
||||
GetWeakSourceName(sceneSwitch.transition).c_str());
|
||||
obs_data_set_int(data, TRANSITION_DURATION, sceneSwitch.duration);
|
||||
std::string message = obs_data_get_json(data);
|
||||
obs_data_release(data);
|
||||
|
||||
@@ -245,13 +247,15 @@ std::string processMessage(std::string payload)
|
||||
}
|
||||
|
||||
if (!obs_data_has_user_value(data, SCENE_ENTRY) ||
|
||||
!obs_data_has_user_value(data, TRANSITION_ENTRY)) {
|
||||
!obs_data_has_user_value(data, TRANSITION_ENTRY) ||
|
||||
!obs_data_has_user_value(data, TRANSITION_DURATION)) {
|
||||
return "missing request parameters";
|
||||
}
|
||||
|
||||
std::string sceneName = obs_data_get_string(data, SCENE_ENTRY);
|
||||
std::string transitionName =
|
||||
obs_data_get_string(data, TRANSITION_ENTRY);
|
||||
int duration = obs_data_get_int(data, TRANSITION_DURATION);
|
||||
|
||||
obs_data_release(data);
|
||||
|
||||
@@ -268,8 +272,7 @@ std::string processMessage(std::string payload)
|
||||
"'";
|
||||
}
|
||||
|
||||
switchScene(scene, transition, switcher->tansitionOverrideOverride,
|
||||
switcher->adjustActiveTransitionType, switcher->verbose);
|
||||
switchScene({scene, transition, duration});
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -462,7 +465,7 @@ void SwitcherData::saveNetworkSwitches(obs_data_t *obj)
|
||||
{
|
||||
networkConfig.Save(obj);
|
||||
if (!networkConfig.ServerEnabled) {
|
||||
switcher->server.stop();
|
||||
server.stop();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -90,7 +90,7 @@ void resetPause()
|
||||
VideoSwitch::pause = false;
|
||||
}
|
||||
|
||||
void setPauseTarget(PauseTarget &target, bool &verbose)
|
||||
void setPauseTarget(PauseTarget &target)
|
||||
{
|
||||
switch (target) {
|
||||
case PauseTarget::All:
|
||||
@@ -148,23 +148,23 @@ void setPauseTarget(PauseTarget &target, bool &verbose)
|
||||
}
|
||||
|
||||
bool checkPauseScene(obs_weak_source_t *currentScene, obs_weak_source_t *scene,
|
||||
PauseTarget &target, bool &verbose)
|
||||
PauseTarget &target)
|
||||
{
|
||||
if (currentScene != scene) {
|
||||
return false;
|
||||
}
|
||||
|
||||
setPauseTarget(target, verbose);
|
||||
setPauseTarget(target);
|
||||
return (target == PauseTarget::All);
|
||||
}
|
||||
bool checkPauseWindow(std::string ¤tTitle, std::string &title,
|
||||
PauseTarget &target, bool &verbose)
|
||||
PauseTarget &target)
|
||||
{
|
||||
if (currentTitle != title) {
|
||||
return false;
|
||||
}
|
||||
|
||||
setPauseTarget(target, verbose);
|
||||
setPauseTarget(target);
|
||||
return (target == PauseTarget::All);
|
||||
}
|
||||
|
||||
@@ -182,11 +182,10 @@ bool SwitcherData::checkPause()
|
||||
|
||||
for (PauseEntry &s : pauseEntries) {
|
||||
if (s.pauseType == PauseType::Scene) {
|
||||
pauseAll = checkPauseScene(ws, s.scene, s.pauseTarget,
|
||||
verbose);
|
||||
pauseAll = checkPauseScene(ws, s.scene, s.pauseTarget);
|
||||
} else {
|
||||
pauseAll = checkPauseWindow(title, s.window,
|
||||
s.pauseTarget, verbose);
|
||||
s.pauseTarget);
|
||||
}
|
||||
if (pauseAll) {
|
||||
break;
|
||||
@@ -202,7 +201,7 @@ bool SwitcherData::checkPause()
|
||||
void SwitcherData::savePauseSwitches(obs_data_t *obj)
|
||||
{
|
||||
obs_data_array_t *pauseScenesArray = obs_data_array_create();
|
||||
for (PauseEntry &s : switcher->pauseEntries) {
|
||||
for (PauseEntry &s : pauseEntries) {
|
||||
obs_data_t *array_obj = obs_data_create();
|
||||
|
||||
obs_data_set_int(array_obj, "pauseType",
|
||||
@@ -229,10 +228,7 @@ void SwitcherData::savePauseSwitches(obs_data_t *obj)
|
||||
|
||||
void SwitcherData::loadPauseSwitches(obs_data_t *obj)
|
||||
{
|
||||
switcher->pauseEntries.clear();
|
||||
|
||||
// To be removed in future builds once most users have migrated
|
||||
loadOldPauseSwitches(obj);
|
||||
pauseEntries.clear();
|
||||
|
||||
obs_data_array_t *pauseArray = obs_data_get_array(obj, "pauseEntries");
|
||||
size_t count = obs_data_array_count(pauseArray);
|
||||
@@ -249,58 +245,14 @@ void SwitcherData::loadPauseSwitches(obs_data_t *obj)
|
||||
const char *window =
|
||||
obs_data_get_string(array_obj, "pauseWindow");
|
||||
|
||||
switcher->pauseEntries.emplace_back(GetWeakSourceByName(scene),
|
||||
type, target, window);
|
||||
pauseEntries.emplace_back(GetWeakSourceByName(scene), type,
|
||||
target, window);
|
||||
|
||||
obs_data_release(array_obj);
|
||||
}
|
||||
obs_data_array_release(pauseArray);
|
||||
}
|
||||
|
||||
void SwitcherData::loadOldPauseSwitches(obs_data_t *obj)
|
||||
{
|
||||
if (obs_data_get_int(obj, "oldPauseValuesImported")) {
|
||||
return;
|
||||
}
|
||||
|
||||
obs_data_array_t *pauseScenesArray =
|
||||
obs_data_get_array(obj, "pauseScenes");
|
||||
size_t count = obs_data_array_count(pauseScenesArray);
|
||||
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
obs_data_t *array_obj =
|
||||
obs_data_array_item(pauseScenesArray, i);
|
||||
|
||||
const char *scene =
|
||||
obs_data_get_string(array_obj, "pauseScene");
|
||||
|
||||
switcher->pauseEntries.emplace_back(GetWeakSourceByName(scene),
|
||||
PauseType::Scene,
|
||||
PauseTarget::All, "");
|
||||
|
||||
obs_data_release(array_obj);
|
||||
}
|
||||
obs_data_array_release(pauseScenesArray);
|
||||
|
||||
obs_data_array_t *pauseWindowsArray =
|
||||
obs_data_get_array(obj, "pauseWindows");
|
||||
count = obs_data_array_count(pauseWindowsArray);
|
||||
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
obs_data_t *array_obj =
|
||||
obs_data_array_item(pauseWindowsArray, i);
|
||||
|
||||
const char *window =
|
||||
obs_data_get_string(array_obj, "pauseWindow");
|
||||
|
||||
switcher->pauseEntries.emplace_back(nullptr, PauseType::Window,
|
||||
PauseTarget::All, window);
|
||||
|
||||
obs_data_release(array_obj);
|
||||
}
|
||||
obs_data_array_release(pauseWindowsArray);
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::setupPauseTab()
|
||||
{
|
||||
for (auto &s : switcher->pauseEntries) {
|
||||
|
||||
@@ -66,7 +66,7 @@ bool SwitcherData::prioFuncsValid()
|
||||
}
|
||||
|
||||
for (int p : functionNamesByPriority) {
|
||||
if (p < 0 || p > 9) {
|
||||
if (p < 0 || p > 10) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,17 +36,18 @@ void AdvSceneSwitcher::on_randomRemove_clicked()
|
||||
delete item;
|
||||
}
|
||||
|
||||
void SwitcherData::checkRandom(bool &match, OBSWeakSource &scene,
|
||||
OBSWeakSource &transition, int &delay)
|
||||
bool SwitcherData::checkRandom(OBSWeakSource &scene, OBSWeakSource &transition,
|
||||
int &delay)
|
||||
{
|
||||
if (randomSwitches.size() == 0 || RandomSwitch::pause) {
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
std::deque<RandomSwitch> rs(randomSwitches);
|
||||
std::random_device rng;
|
||||
std::mt19937 urng(rng());
|
||||
std::shuffle(rs.begin(), rs.end(), urng);
|
||||
bool match = false;
|
||||
for (RandomSwitch &r : rs) {
|
||||
if (!r.initialized()) {
|
||||
continue;
|
||||
@@ -75,12 +76,13 @@ void SwitcherData::checkRandom(bool &match, OBSWeakSource &scene,
|
||||
}
|
||||
break;
|
||||
}
|
||||
return match;
|
||||
}
|
||||
|
||||
void SwitcherData::saveRandomSwitches(obs_data_t *obj)
|
||||
{
|
||||
obs_data_array_t *randomArray = obs_data_array_create();
|
||||
for (RandomSwitch &s : switcher->randomSwitches) {
|
||||
for (RandomSwitch &s : randomSwitches) {
|
||||
obs_data_t *array_obj = obs_data_create();
|
||||
|
||||
s.save(array_obj);
|
||||
@@ -94,7 +96,7 @@ void SwitcherData::saveRandomSwitches(obs_data_t *obj)
|
||||
|
||||
void SwitcherData::loadRandomSwitches(obs_data_t *obj)
|
||||
{
|
||||
switcher->randomSwitches.clear();
|
||||
randomSwitches.clear();
|
||||
|
||||
obs_data_array_t *randomArray =
|
||||
obs_data_get_array(obj, "randomSwitches");
|
||||
@@ -103,7 +105,7 @@ void SwitcherData::loadRandomSwitches(obs_data_t *obj)
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
obs_data_t *array_obj = obs_data_array_item(randomArray, i);
|
||||
|
||||
switcher->randomSwitches.emplace_back();
|
||||
randomSwitches.emplace_back();
|
||||
randomSwitches.back().load(array_obj);
|
||||
|
||||
obs_data_release(array_obj);
|
||||
|
||||
@@ -151,15 +151,16 @@ bool shouldIgnoreSceneSwitch(ScreenRegionSwitch &matchingRegion)
|
||||
return matchingRegion.excludeScene == ws;
|
||||
}
|
||||
|
||||
void SwitcherData::checkScreenRegionSwitch(bool &match, OBSWeakSource &scene,
|
||||
bool SwitcherData::checkScreenRegionSwitch(OBSWeakSource &scene,
|
||||
OBSWeakSource &transition)
|
||||
{
|
||||
if (ScreenRegionSwitch::pause) {
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
std::pair<int, int> cursorPos = getCursorPos();
|
||||
int minRegionSize = 99999;
|
||||
bool match = false;
|
||||
|
||||
for (auto &s : screenRegionSwitches) {
|
||||
if (!s.initialized()) {
|
||||
@@ -173,7 +174,7 @@ void SwitcherData::checkScreenRegionSwitch(bool &match, OBSWeakSource &scene,
|
||||
if (shouldIgnoreSceneSwitch(s)) {
|
||||
// We technically have a match.
|
||||
// But just ignore it.
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
match = true;
|
||||
scene = s.getScene();
|
||||
@@ -187,6 +188,7 @@ void SwitcherData::checkScreenRegionSwitch(bool &match, OBSWeakSource &scene,
|
||||
}
|
||||
}
|
||||
}
|
||||
return match;
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::updateScreenRegionCursorPos()
|
||||
@@ -199,7 +201,7 @@ void AdvSceneSwitcher::updateScreenRegionCursorPos()
|
||||
void SwitcherData::saveScreenRegionSwitches(obs_data_t *obj)
|
||||
{
|
||||
obs_data_array_t *screenRegionArray = obs_data_array_create();
|
||||
for (ScreenRegionSwitch &s : switcher->screenRegionSwitches) {
|
||||
for (ScreenRegionSwitch &s : screenRegionSwitches) {
|
||||
obs_data_t *array_obj = obs_data_create();
|
||||
|
||||
s.save(array_obj);
|
||||
@@ -213,7 +215,7 @@ void SwitcherData::saveScreenRegionSwitches(obs_data_t *obj)
|
||||
|
||||
void SwitcherData::loadScreenRegionSwitches(obs_data_t *obj)
|
||||
{
|
||||
switcher->screenRegionSwitches.clear();
|
||||
screenRegionSwitches.clear();
|
||||
|
||||
obs_data_array_t *screenRegionArray =
|
||||
obs_data_get_array(obj, "screenRegion");
|
||||
@@ -223,7 +225,7 @@ void SwitcherData::loadScreenRegionSwitches(obs_data_t *obj)
|
||||
obs_data_t *array_obj =
|
||||
obs_data_array_item(screenRegionArray, i);
|
||||
|
||||
switcher->screenRegionSwitches.emplace_back();
|
||||
screenRegionSwitches.emplace_back();
|
||||
screenRegionSwitches.back().load(array_obj);
|
||||
|
||||
obs_data_release(array_obj);
|
||||
@@ -269,40 +271,8 @@ void ScreenRegionSwitch::save(obs_data_t *obj)
|
||||
obs_data_set_int(obj, "maxY", maxY);
|
||||
}
|
||||
|
||||
// To be removed in future version
|
||||
bool loadOldRegion(obs_data_t *obj, ScreenRegionSwitch *s)
|
||||
{
|
||||
if (!s) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const char *scene = obs_data_get_string(obj, "screenRegionScene");
|
||||
|
||||
if (strcmp(scene, "") == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
s->scene = GetWeakSourceByName(scene);
|
||||
|
||||
const char *transition = obs_data_get_string(obj, "transition");
|
||||
s->transition = GetWeakTransitionByName(transition);
|
||||
|
||||
s->minX = obs_data_get_int(obj, "minX");
|
||||
s->minY = obs_data_get_int(obj, "minY");
|
||||
s->maxX = obs_data_get_int(obj, "maxX");
|
||||
s->maxY = obs_data_get_int(obj, "maxY");
|
||||
|
||||
s->usePreviousScene = strcmp(scene, previous_scene_name) == 0;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ScreenRegionSwitch::load(obs_data_t *obj)
|
||||
{
|
||||
if (loadOldRegion(obj, this)) {
|
||||
return;
|
||||
}
|
||||
|
||||
SceneSwitcherEntry::load(obj);
|
||||
|
||||
const char *excludeSceneName = obs_data_get_string(obj, "excludeScene");
|
||||
|
||||
@@ -179,16 +179,18 @@ void AdvSceneSwitcher::on_sceneSequenceSwitches_itemDoubleClicked(
|
||||
OpenSequenceExtendEdit(currentWidget);
|
||||
}
|
||||
|
||||
void SwitcherData::checkSceneSequence(bool &match, OBSWeakSource &scene,
|
||||
OBSWeakSource &transition, int &linger)
|
||||
bool SwitcherData::checkSceneSequence(OBSWeakSource &scene,
|
||||
OBSWeakSource &transition, int &linger,
|
||||
bool &setPrevSceneAfterLinger)
|
||||
{
|
||||
if (SceneSequenceSwitch::pause) {
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
obs_source_t *currentSceneSource = obs_frontend_get_current_scene();
|
||||
obs_weak_source_t *currentScene =
|
||||
obs_source_get_weak_source(currentSceneSource);
|
||||
bool match = false;
|
||||
|
||||
for (SceneSequenceSwitch &s : sceneSequenceSwitches) {
|
||||
// Continue the active uninterruptible sequence and skip others
|
||||
@@ -205,9 +207,12 @@ void SwitcherData::checkSceneSequence(bool &match, OBSWeakSource &scene,
|
||||
if (s.activeSequence) {
|
||||
scene = s.activeSequence->getScene();
|
||||
transition = s.activeSequence->transition;
|
||||
setPrevSceneAfterLinger =
|
||||
s.activeSequence->usePreviousScene;
|
||||
} else {
|
||||
scene = s.getScene();
|
||||
transition = s.transition;
|
||||
setPrevSceneAfterLinger = s.usePreviousScene;
|
||||
if (verbose) {
|
||||
s.logMatch();
|
||||
}
|
||||
@@ -233,12 +238,14 @@ void SwitcherData::checkSceneSequence(bool &match, OBSWeakSource &scene,
|
||||
|
||||
obs_source_release(currentSceneSource);
|
||||
obs_weak_source_release(currentScene);
|
||||
|
||||
return match;
|
||||
}
|
||||
|
||||
void SwitcherData::saveSceneSequenceSwitches(obs_data_t *obj)
|
||||
{
|
||||
obs_data_array_t *sceneSequenceArray = obs_data_array_create();
|
||||
for (SceneSequenceSwitch &s : switcher->sceneSequenceSwitches) {
|
||||
for (SceneSequenceSwitch &s : sceneSequenceSwitches) {
|
||||
obs_data_t *array_obj = obs_data_create();
|
||||
|
||||
s.save(array_obj);
|
||||
@@ -252,7 +259,7 @@ void SwitcherData::saveSceneSequenceSwitches(obs_data_t *obj)
|
||||
|
||||
void SwitcherData::loadSceneSequenceSwitches(obs_data_t *obj)
|
||||
{
|
||||
switcher->sceneSequenceSwitches.clear();
|
||||
sceneSequenceSwitches.clear();
|
||||
|
||||
obs_data_array_t *sceneSequenceArray =
|
||||
obs_data_get_array(obj, "sceneRoundTrip");
|
||||
@@ -262,7 +269,7 @@ void SwitcherData::loadSceneSequenceSwitches(obs_data_t *obj)
|
||||
obs_data_t *array_obj =
|
||||
obs_data_array_item(sceneSequenceArray, i);
|
||||
|
||||
switcher->sceneSequenceSwitches.emplace_back();
|
||||
sceneSequenceSwitches.emplace_back();
|
||||
sceneSequenceSwitches.back().load(array_obj);
|
||||
|
||||
obs_data_release(array_obj);
|
||||
@@ -308,8 +315,7 @@ void SceneSequenceSwitch::save(obs_data_t *obj, bool saveExt)
|
||||
static_cast<int>(startTargetType));
|
||||
obs_data_set_string(obj, "startScene",
|
||||
GetWeakSourceName(startScene).c_str());
|
||||
obs_data_set_double(obj, "delay", delay);
|
||||
obs_data_set_int(obj, "delayMultiplier", delayMultiplier);
|
||||
delay.Save(obj, "delay");
|
||||
obs_data_set_bool(obj, "interruptible", interruptible);
|
||||
|
||||
if (saveExt) {
|
||||
@@ -328,60 +334,14 @@ void SceneSequenceSwitch::save(obs_data_t *obj, bool saveExt)
|
||||
}
|
||||
}
|
||||
|
||||
// To be removed in future version
|
||||
bool loadOldScequence(obs_data_t *obj, SceneSequenceSwitch *s)
|
||||
{
|
||||
if (!s) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const char *scene1 = obs_data_get_string(obj, "sceneRoundTripScene1");
|
||||
|
||||
if (strcmp(scene1, "") == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
s->startScene = GetWeakSourceByName(scene1);
|
||||
|
||||
const char *scene2 = obs_data_get_string(obj, "sceneRoundTripScene2");
|
||||
s->scene = GetWeakSourceByName(scene2);
|
||||
|
||||
const char *transition = obs_data_get_string(obj, "transition");
|
||||
s->transition = GetWeakTransitionByName(transition);
|
||||
|
||||
s->delay = obs_data_get_double(obj, "delay");
|
||||
|
||||
int delayMultiplier = obs_data_get_int(obj, "delayMultiplier");
|
||||
if (delayMultiplier == 0 ||
|
||||
(delayMultiplier != 1 && delayMultiplier % 60 != 0))
|
||||
delayMultiplier = 1;
|
||||
s->delayMultiplier = delayMultiplier;
|
||||
|
||||
s->interruptible = obs_data_get_bool(obj, "interruptible");
|
||||
|
||||
s->usePreviousScene = strcmp(scene2, previous_scene_name) == 0;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void SceneSequenceSwitch::load(obs_data_t *obj, bool saveExt)
|
||||
{
|
||||
if (loadOldScequence(obj, this)) {
|
||||
return;
|
||||
}
|
||||
|
||||
SceneSwitcherEntry::load(obj);
|
||||
startTargetType = static_cast<SwitchTargetType>(
|
||||
obs_data_get_int(obj, "startTargetType"));
|
||||
const char *scene = obs_data_get_string(obj, "startScene");
|
||||
startScene = GetWeakSourceByName(scene);
|
||||
|
||||
delay = obs_data_get_double(obj, "delay");
|
||||
|
||||
delayMultiplier = obs_data_get_int(obj, "delayMultiplier");
|
||||
if (delayMultiplier == 0 ||
|
||||
(delayMultiplier != 1 && delayMultiplier % 60 != 0))
|
||||
delayMultiplier = 1;
|
||||
delay.Load(obj, "delay");
|
||||
|
||||
interruptible = obs_data_get_bool(obj, "interruptible");
|
||||
|
||||
@@ -455,7 +415,7 @@ bool SceneSequenceSwitch::checkMatch(OBSWeakSource currentScene, int &linger,
|
||||
prepareUninterruptibleMatch(currentScene, linger);
|
||||
}
|
||||
} else {
|
||||
matchCount = 0;
|
||||
delay.Reset();
|
||||
|
||||
if (root) {
|
||||
root->activeSequence = nullptr;
|
||||
@@ -468,11 +428,8 @@ bool SceneSequenceSwitch::checkMatch(OBSWeakSource currentScene, int &linger,
|
||||
|
||||
bool SceneSequenceSwitch::checkDurationMatchInterruptible()
|
||||
{
|
||||
bool durationReached = matchCount * (switcher->interval / 1000.0) >=
|
||||
delay;
|
||||
matchCount++;
|
||||
if (durationReached) {
|
||||
matchCount = 0;
|
||||
if (delay.DurationReached()) {
|
||||
delay.Reset();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@@ -481,7 +438,7 @@ bool SceneSequenceSwitch::checkDurationMatchInterruptible()
|
||||
void SceneSequenceSwitch::prepareUninterruptibleMatch(
|
||||
OBSWeakSource currentScene, int &linger)
|
||||
{
|
||||
int dur = delay * 1000;
|
||||
int dur = delay.seconds * 1000;
|
||||
if (dur > 0) {
|
||||
switcher->waitScene = obs_weak_source_get_source(currentScene);
|
||||
obs_source_release(switcher->waitScene);
|
||||
@@ -526,8 +483,8 @@ void SceneSequenceSwitch::advanceActiveSequence()
|
||||
return;
|
||||
}
|
||||
|
||||
// Reinit old matchCount value in case it was previously set
|
||||
activeSequence->matchCount = 0;
|
||||
// Reinit delay in case it was previously set
|
||||
activeSequence->delay.Reset();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -554,19 +511,6 @@ void SceneSequenceSwitch::logSequenceCanceled()
|
||||
blog(LOG_INFO, "unexpected scene change - cancel sequence");
|
||||
}
|
||||
|
||||
QString delayMultiplierToString(int delayMultiplier)
|
||||
{
|
||||
switch (delayMultiplier) {
|
||||
case 1:
|
||||
return obs_module_text("AdvSceneSwitcher.unit.secends");
|
||||
case 60:
|
||||
return obs_module_text("AdvSceneSwitcher.unit.minutes");
|
||||
case 60 * 60:
|
||||
return obs_module_text("AdvSceneSwitcher.unit.hours");
|
||||
}
|
||||
return "???";
|
||||
}
|
||||
|
||||
QString makeExtendText(SceneSequenceSwitch *s, int curLen = 0)
|
||||
{
|
||||
if (!s) {
|
||||
@@ -575,8 +519,7 @@ QString makeExtendText(SceneSequenceSwitch *s, int curLen = 0)
|
||||
|
||||
QString ext = "";
|
||||
|
||||
ext = QString::number(s->delay / s->delayMultiplier) + " ";
|
||||
ext += delayMultiplierToString(s->delayMultiplier);
|
||||
ext = QString::fromStdString(s->delay.ToString()) + " ";
|
||||
|
||||
QString sceneName = GetWeakSourceName(s->scene).c_str();
|
||||
if (s->targetType == SwitchTargetType::SceneGroup && s->group) {
|
||||
@@ -600,13 +543,6 @@ QString makeExtendText(SceneSequenceSwitch *s, int curLen = 0)
|
||||
}
|
||||
}
|
||||
|
||||
void populateDelayUnits(QComboBox *list)
|
||||
{
|
||||
list->addItem(obs_module_text("AdvSceneSwitcher.unit.secends"));
|
||||
list->addItem(obs_module_text("AdvSceneSwitcher.unit.minutes"));
|
||||
list->addItem(obs_module_text("AdvSceneSwitcher.unit.hours"));
|
||||
}
|
||||
|
||||
SequenceWidget::SequenceWidget(QWidget *parent, SceneSequenceSwitch *s,
|
||||
bool extendSequence, bool editExtendMode,
|
||||
bool showExtendText)
|
||||
@@ -614,8 +550,7 @@ SequenceWidget::SequenceWidget(QWidget *parent, SceneSequenceSwitch *s,
|
||||
{
|
||||
this->setParent(parent);
|
||||
|
||||
delay = new QDoubleSpinBox();
|
||||
delayUnits = new QComboBox();
|
||||
delay = new DurationSelection();
|
||||
startScenes = new QComboBox();
|
||||
interruptible = new QCheckBox(obs_module_text(
|
||||
"AdvSceneSwitcher.sceneSequenceTab.interruptible"));
|
||||
@@ -638,10 +573,10 @@ SequenceWidget::SequenceWidget(QWidget *parent, SceneSequenceSwitch *s,
|
||||
QWidget::connect(scenes, SIGNAL(currentTextChanged(const QString &)),
|
||||
this, SLOT(SceneChanged(const QString &)));
|
||||
|
||||
QWidget::connect(delay, SIGNAL(valueChanged(double)), this,
|
||||
QWidget::connect(delay, SIGNAL(DurationChanged(double)), this,
|
||||
SLOT(DelayChanged(double)));
|
||||
QWidget::connect(delayUnits, SIGNAL(currentIndexChanged(int)), this,
|
||||
SLOT(DelayUnitsChanged(int)));
|
||||
QWidget::connect(delay, SIGNAL(UnitChanged(DurationUnit)), this,
|
||||
SLOT(DelayUnitsChanged(DurationUnit)));
|
||||
QWidget::connect(startScenes,
|
||||
SIGNAL(currentTextChanged(const QString &)), this,
|
||||
SLOT(StartSceneChanged(const QString &)));
|
||||
@@ -653,9 +588,7 @@ SequenceWidget::SequenceWidget(QWidget *parent, SceneSequenceSwitch *s,
|
||||
QWidget::connect(reduce, SIGNAL(clicked()), this,
|
||||
SLOT(ReduceClicked()));
|
||||
|
||||
delay->setMaximum(99999.000000);
|
||||
AdvSceneSwitcher::populateSceneSelection(startScenes, false);
|
||||
populateDelayUnits(delayUnits);
|
||||
interruptible->setToolTip(obs_module_text(
|
||||
"AdvSceneSwitcher.sceneSequenceTab.interruptibleHint"));
|
||||
|
||||
@@ -666,7 +599,6 @@ SequenceWidget::SequenceWidget(QWidget *parent, SceneSequenceSwitch *s,
|
||||
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
|
||||
{"{{scenes}}", scenes},
|
||||
{"{{delay}}", delay},
|
||||
{"{{delayUnits}}", delayUnits},
|
||||
{"{{transitions}}", transitions}};
|
||||
placeWidgets(
|
||||
obs_module_text(
|
||||
@@ -679,7 +611,6 @@ SequenceWidget::SequenceWidget(QWidget *parent, SceneSequenceSwitch *s,
|
||||
{"{{startScenes}}", startScenes},
|
||||
{"{{scenes}}", scenes},
|
||||
{"{{delay}}", delay},
|
||||
{"{{delayUnits}}", delayUnits},
|
||||
{"{{transitions}}", transitions},
|
||||
{"{{interruptible}}", interruptible}};
|
||||
placeWidgets(obs_module_text(
|
||||
@@ -743,46 +674,24 @@ void SequenceWidget::swapSwitchData(SequenceWidget *s1, SequenceWidget *s2)
|
||||
s2->setSwitchData(t);
|
||||
}
|
||||
|
||||
void SequenceWidget::UpdateDelay()
|
||||
{
|
||||
if (switchData) {
|
||||
delay->setValue(switchData->delay /
|
||||
switchData->delayMultiplier);
|
||||
}
|
||||
}
|
||||
|
||||
void SequenceWidget::DelayChanged(double delay)
|
||||
void SequenceWidget::DelayChanged(double sec)
|
||||
{
|
||||
if (loading || !switchData) {
|
||||
return;
|
||||
}
|
||||
|
||||
switchData->delay = delay * switchData->delayMultiplier;
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
switchData->delay.seconds = sec;
|
||||
}
|
||||
|
||||
void SequenceWidget::DelayUnitsChanged(int idx)
|
||||
void SequenceWidget::DelayUnitsChanged(DurationUnit unit)
|
||||
{
|
||||
if (loading || !switchData) {
|
||||
return;
|
||||
}
|
||||
|
||||
delay_units unit = (delay_units)idx;
|
||||
|
||||
switch (unit) {
|
||||
case SECONDS:
|
||||
switchData->delayMultiplier = 1;
|
||||
break;
|
||||
case MINUTES:
|
||||
switchData->delayMultiplier = 60;
|
||||
break;
|
||||
case HOURS:
|
||||
switchData->delayMultiplier = 60 * 60;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
UpdateDelay();
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
switchData->delay.displayUnit = unit;
|
||||
}
|
||||
|
||||
void SequenceWidget::setExtendedSequenceStartScene()
|
||||
@@ -844,21 +753,7 @@ void SequenceWidget::UpdateWidgetStatus(bool showExtendText)
|
||||
makeExtendText(switchData->extendedSequence.get()));
|
||||
}
|
||||
|
||||
switch (switchData->delayMultiplier) {
|
||||
case 1:
|
||||
delayUnits->setCurrentIndex(0);
|
||||
break;
|
||||
case 60:
|
||||
delayUnits->setCurrentIndex(1);
|
||||
break;
|
||||
case 3600:
|
||||
delayUnits->setCurrentIndex(2);
|
||||
break;
|
||||
default:
|
||||
delayUnits->setCurrentIndex(0);
|
||||
}
|
||||
UpdateDelay();
|
||||
|
||||
delay->SetDuration(switchData->delay);
|
||||
startScenes->setCurrentText(
|
||||
GetWeakSourceName(switchData->startScene).c_str());
|
||||
interruptible->setChecked(switchData->interruptible);
|
||||
|
||||
@@ -113,13 +113,14 @@ bool checkRegularTime(TimeSwitch &s, int &interval)
|
||||
return timesAreInInterval(s.time, now, interval);
|
||||
}
|
||||
|
||||
void SwitcherData::checkTimeSwitch(bool &match, OBSWeakSource &scene,
|
||||
bool SwitcherData::checkTimeSwitch(OBSWeakSource &scene,
|
||||
OBSWeakSource &transition)
|
||||
{
|
||||
if (TimeSwitch::pause) {
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool match = false;
|
||||
for (TimeSwitch &s : timeSwitches) {
|
||||
if (!s.initialized()) {
|
||||
continue;
|
||||
@@ -142,12 +143,13 @@ void SwitcherData::checkTimeSwitch(bool &match, OBSWeakSource &scene,
|
||||
break;
|
||||
}
|
||||
}
|
||||
return match;
|
||||
}
|
||||
|
||||
void SwitcherData::saveTimeSwitches(obs_data_t *obj)
|
||||
{
|
||||
obs_data_array_t *timeArray = obs_data_array_create();
|
||||
for (TimeSwitch &s : switcher->timeSwitches) {
|
||||
for (TimeSwitch &s : timeSwitches) {
|
||||
obs_data_t *array_obj = obs_data_create();
|
||||
|
||||
s.save(array_obj);
|
||||
@@ -161,7 +163,7 @@ void SwitcherData::saveTimeSwitches(obs_data_t *obj)
|
||||
|
||||
void SwitcherData::loadTimeSwitches(obs_data_t *obj)
|
||||
{
|
||||
switcher->timeSwitches.clear();
|
||||
timeSwitches.clear();
|
||||
|
||||
obs_data_array_t *timeArray = obs_data_get_array(obj, "timeSwitches");
|
||||
size_t count = obs_data_array_count(timeArray);
|
||||
@@ -169,7 +171,7 @@ void SwitcherData::loadTimeSwitches(obs_data_t *obj)
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
obs_data_t *array_obj = obs_data_array_item(timeArray, i);
|
||||
|
||||
switcher->timeSwitches.emplace_back();
|
||||
timeSwitches.emplace_back();
|
||||
timeSwitches.back().load(array_obj);
|
||||
|
||||
obs_data_release(array_obj);
|
||||
@@ -204,37 +206,8 @@ void TimeSwitch::save(obs_data_t *obj)
|
||||
obs_data_set_string(obj, "time", time.toString().toStdString().c_str());
|
||||
}
|
||||
|
||||
// To be removed in future version
|
||||
bool loadOldTime(obs_data_t *obj, TimeSwitch *s)
|
||||
{
|
||||
if (!s) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const char *scene = obs_data_get_string(obj, "scene");
|
||||
|
||||
if (strcmp(scene, "") == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
s->scene = GetWeakSourceByName(scene);
|
||||
|
||||
const char *transition = obs_data_get_string(obj, "transition");
|
||||
s->transition = GetWeakTransitionByName(transition);
|
||||
|
||||
s->trigger = (timeTrigger)obs_data_get_int(obj, "trigger");
|
||||
s->time = QTime::fromString(obs_data_get_string(obj, "time"));
|
||||
s->usePreviousScene = strcmp(scene, previous_scene_name) == 0;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void TimeSwitch::load(obs_data_t *obj)
|
||||
{
|
||||
if (loadOldTime(obj, this)) {
|
||||
return;
|
||||
}
|
||||
|
||||
SceneSwitcherEntry::load(obj);
|
||||
|
||||
trigger = (timeTrigger)obs_data_get_int(obj, "trigger");
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
#include "headers/advanced-scene-switcher.hpp"
|
||||
#include "headers/utility.hpp"
|
||||
|
||||
constexpr auto default_def_transition_dealy = 50;
|
||||
constexpr auto default_def_transition_dealy = 300;
|
||||
bool DefaultSceneTransition::pause = false;
|
||||
unsigned int DefaultSceneTransition::delay = default_def_transition_dealy;
|
||||
|
||||
@@ -189,7 +189,7 @@ void AdvSceneSwitcher::on_transitionOverridecheckBox_stateChanged(int state)
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
switcher->tansitionOverrideOverride = state;
|
||||
switcher->transitionOverrideOverride = state;
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::on_adjustActiveTransitionType_stateChanged(int state)
|
||||
@@ -199,7 +199,7 @@ void AdvSceneSwitcher::on_adjustActiveTransitionType_stateChanged(int state)
|
||||
}
|
||||
|
||||
// This option only makes sense if we are allowed to use transition overrides
|
||||
if (!state && !switcher->tansitionOverrideOverride) {
|
||||
if (!state && !switcher->transitionOverrideOverride) {
|
||||
DisplayMessage(obs_module_text(
|
||||
"AdvSceneSwitcher.transitionTab.transitionBehaviorSelectionError"));
|
||||
ui->transitionOverridecheckBox->setChecked(true);
|
||||
@@ -219,10 +219,11 @@ void AdvSceneSwitcher::defTransitionDelayValueChanged(int value)
|
||||
DefaultSceneTransition::delay = value;
|
||||
}
|
||||
|
||||
obs_weak_source_t *getNextTransition(obs_weak_source_t *scene1,
|
||||
obs_weak_source_t *scene2)
|
||||
std::pair<obs_weak_source_t *, int> getNextTransition(obs_weak_source_t *scene1,
|
||||
obs_weak_source_t *scene2)
|
||||
{
|
||||
obs_weak_source_t *ws = nullptr;
|
||||
int duration = 0;
|
||||
if (scene1 && scene2) {
|
||||
for (SceneTransition &t : switcher->sceneTransitions) {
|
||||
if (!t.initialized()) {
|
||||
@@ -231,28 +232,26 @@ obs_weak_source_t *getNextTransition(obs_weak_source_t *scene1,
|
||||
|
||||
if (t.scene == scene1 && t.scene2 == scene2) {
|
||||
ws = t.transition;
|
||||
duration = t.duration * 1000;
|
||||
break;
|
||||
}
|
||||
}
|
||||
obs_weak_source_addref(ws);
|
||||
}
|
||||
return ws;
|
||||
return std::make_pair(ws, duration);
|
||||
}
|
||||
|
||||
void overwriteTransitionOverride(obs_weak_source_t *sceneWs,
|
||||
obs_source_t *transition, transitionData &td)
|
||||
void overwriteTransitionOverride(sceneSwitchInfo ssi, transitionData &td)
|
||||
{
|
||||
obs_source_t *scene = obs_weak_source_get_source(sceneWs);
|
||||
obs_source_t *scene = obs_weak_source_get_source(ssi.scene);
|
||||
obs_data_t *data = obs_source_get_private_settings(scene);
|
||||
|
||||
td.name = obs_data_get_string(data, "transition");
|
||||
td.duration = obs_data_get_int(data, "duration");
|
||||
td.duration = obs_data_get_int(data, "transition_duration");
|
||||
|
||||
const char *name = obs_source_get_name(transition);
|
||||
int duration = obs_frontend_get_transition_duration();
|
||||
std::string name = GetWeakSourceName(ssi.transition);
|
||||
|
||||
obs_data_set_string(data, "transition", name);
|
||||
obs_data_set_int(data, "transition_duration", duration);
|
||||
obs_data_set_string(data, "transition", name.c_str());
|
||||
obs_data_set_int(data, "transition_duration", ssi.duration);
|
||||
|
||||
obs_data_release(data);
|
||||
obs_source_release(scene);
|
||||
@@ -268,94 +267,88 @@ void restoreTransitionOverride(obs_source_t *scene, transitionData td)
|
||||
obs_data_release(data);
|
||||
}
|
||||
|
||||
void setNextTransition(OBSWeakSource &targetScene, obs_source_t *currentSource,
|
||||
OBSWeakSource &transition,
|
||||
bool transitionOverrideOverride,
|
||||
bool adjustActiveTransitionType, transitionData &td)
|
||||
void setNextTransition(sceneSwitchInfo &sceneSwitch,
|
||||
obs_source_t *currentSource, transitionData &td)
|
||||
{
|
||||
// Priority:
|
||||
// 1. Transition tab
|
||||
// 2. Individual switcher entry
|
||||
// 3. Current transition settings
|
||||
|
||||
// Transition Tab
|
||||
obs_weak_source_t *currentScene =
|
||||
obs_source_get_weak_source(currentSource);
|
||||
obs_weak_source_t *nextTransitionWs =
|
||||
getNextTransition(currentScene, targetScene);
|
||||
auto tinfo = getNextTransition(currentScene, sceneSwitch.scene);
|
||||
obs_weak_source_release(currentScene);
|
||||
|
||||
obs_source_t *nextTransition = nullptr;
|
||||
if (nextTransitionWs) {
|
||||
nextTransition = obs_weak_source_get_source(nextTransitionWs);
|
||||
} else if (transition) {
|
||||
nextTransition = obs_weak_source_get_source(transition);
|
||||
OBSWeakSource nextTransition = tinfo.first;
|
||||
int nextTransitionDuration = tinfo.second;
|
||||
|
||||
// Individual switcher entry
|
||||
if (!nextTransition) {
|
||||
nextTransition = sceneSwitch.transition;
|
||||
}
|
||||
if (!nextTransitionDuration) {
|
||||
nextTransitionDuration = sceneSwitch.duration;
|
||||
}
|
||||
|
||||
if (nextTransition && adjustActiveTransitionType) {
|
||||
obs_frontend_set_current_transition(nextTransition);
|
||||
// Current transition settings
|
||||
if (!nextTransition) {
|
||||
auto ct = obs_frontend_get_current_transition();
|
||||
nextTransition = obs_source_get_weak_source(ct);
|
||||
obs_weak_source_release(nextTransition);
|
||||
obs_source_release(ct);
|
||||
}
|
||||
if (!nextTransitionDuration) {
|
||||
nextTransitionDuration = obs_frontend_get_transition_duration();
|
||||
}
|
||||
|
||||
if (transitionOverrideOverride) {
|
||||
overwriteTransitionOverride(targetScene, nextTransition, td);
|
||||
if (switcher->adjustActiveTransitionType) {
|
||||
obs_frontend_set_transition_duration(nextTransitionDuration);
|
||||
auto t = obs_weak_source_get_source(nextTransition);
|
||||
obs_frontend_set_current_transition(t);
|
||||
obs_source_release(t);
|
||||
}
|
||||
|
||||
obs_weak_source_release(nextTransitionWs);
|
||||
obs_source_release(nextTransition);
|
||||
if (switcher->transitionOverrideOverride) {
|
||||
overwriteTransitionOverride({sceneSwitch.scene, nextTransition,
|
||||
nextTransitionDuration},
|
||||
td);
|
||||
}
|
||||
}
|
||||
|
||||
void SwitcherData::saveSceneTransitions(obs_data_t *obj)
|
||||
{
|
||||
obs_data_array_t *sceneTransitionsArray = obs_data_array_create();
|
||||
for (SceneTransition &s : switcher->sceneTransitions) {
|
||||
for (SceneTransition &s : sceneTransitions) {
|
||||
obs_data_t *array_obj = obs_data_create();
|
||||
|
||||
obs_source_t *source1 = obs_weak_source_get_source(s.scene);
|
||||
obs_source_t *source2 = obs_weak_source_get_source(s.scene2);
|
||||
obs_source_t *transition =
|
||||
obs_weak_source_get_source(s.transition);
|
||||
if (source1 && source2 && transition) {
|
||||
const char *sceneName1 = obs_source_get_name(source1);
|
||||
const char *sceneName2 = obs_source_get_name(source2);
|
||||
const char *transitionName =
|
||||
obs_source_get_name(transition);
|
||||
obs_data_set_string(array_obj, "Scene1", sceneName1);
|
||||
obs_data_set_string(array_obj, "Scene2", sceneName2);
|
||||
obs_data_set_string(array_obj, "transition",
|
||||
transitionName);
|
||||
obs_data_array_push_back(sceneTransitionsArray,
|
||||
array_obj);
|
||||
}
|
||||
obs_source_release(source1);
|
||||
obs_source_release(source2);
|
||||
obs_source_release(transition);
|
||||
s.save(array_obj);
|
||||
obs_data_array_push_back(sceneTransitionsArray, array_obj);
|
||||
|
||||
obs_data_release(array_obj);
|
||||
}
|
||||
obs_data_set_array(obj, "sceneTransitions", sceneTransitionsArray);
|
||||
obs_data_array_release(sceneTransitionsArray);
|
||||
|
||||
obs_data_array_t *defaultTransitionsArray = obs_data_array_create();
|
||||
for (DefaultSceneTransition &s : switcher->defaultSceneTransitions) {
|
||||
for (DefaultSceneTransition &s : defaultSceneTransitions) {
|
||||
obs_data_t *array_obj = obs_data_create();
|
||||
|
||||
obs_source_t *source = obs_weak_source_get_source(s.scene);
|
||||
obs_source_t *transition =
|
||||
obs_weak_source_get_source(s.transition);
|
||||
if (source && transition) {
|
||||
const char *sceneName = obs_source_get_name(source);
|
||||
const char *transitionName =
|
||||
obs_source_get_name(transition);
|
||||
obs_data_set_string(array_obj, "Scene", sceneName);
|
||||
obs_data_set_string(array_obj, "transition",
|
||||
transitionName);
|
||||
obs_data_array_push_back(defaultTransitionsArray,
|
||||
array_obj);
|
||||
}
|
||||
obs_source_release(source);
|
||||
obs_source_release(transition);
|
||||
s.save(array_obj);
|
||||
obs_data_array_push_back(defaultTransitionsArray, array_obj);
|
||||
|
||||
obs_data_release(array_obj);
|
||||
}
|
||||
obs_data_set_array(obj, "defaultTransitions", defaultTransitionsArray);
|
||||
obs_data_array_release(defaultTransitionsArray);
|
||||
|
||||
obs_data_set_bool(obj, "tansitionOverrideOverride",
|
||||
switcher->tansitionOverrideOverride);
|
||||
transitionOverrideOverride);
|
||||
obs_data_set_default_bool(obj, "adjustActiveTransitionType",
|
||||
adjustActiveTransitionType);
|
||||
obs_data_set_bool(obj, "adjustActiveTransitionType",
|
||||
switcher->adjustActiveTransitionType);
|
||||
adjustActiveTransitionType);
|
||||
obs_data_set_default_int(obj, "defTransitionDelay",
|
||||
default_def_transition_dealy);
|
||||
obs_data_set_int(obj, "defTransitionDelay",
|
||||
@@ -364,7 +357,7 @@ void SwitcherData::saveSceneTransitions(obs_data_t *obj)
|
||||
|
||||
void SwitcherData::loadSceneTransitions(obs_data_t *obj)
|
||||
{
|
||||
switcher->sceneTransitions.clear();
|
||||
sceneTransitions.clear();
|
||||
|
||||
obs_data_array_t *sceneTransitionsArray =
|
||||
obs_data_get_array(obj, "sceneTransitions");
|
||||
@@ -373,22 +366,13 @@ void SwitcherData::loadSceneTransitions(obs_data_t *obj)
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
obs_data_t *array_obj =
|
||||
obs_data_array_item(sceneTransitionsArray, i);
|
||||
|
||||
const char *scene1 = obs_data_get_string(array_obj, "Scene1");
|
||||
const char *scene2 = obs_data_get_string(array_obj, "Scene2");
|
||||
const char *transition =
|
||||
obs_data_get_string(array_obj, "transition");
|
||||
|
||||
switcher->sceneTransitions.emplace_back(
|
||||
GetWeakSourceByName(scene1),
|
||||
GetWeakSourceByName(scene2),
|
||||
GetWeakTransitionByName(transition));
|
||||
|
||||
sceneTransitions.emplace_back();
|
||||
sceneTransitions.back().load(array_obj);
|
||||
obs_data_release(array_obj);
|
||||
}
|
||||
obs_data_array_release(sceneTransitionsArray);
|
||||
|
||||
switcher->defaultSceneTransitions.clear();
|
||||
defaultSceneTransitions.clear();
|
||||
|
||||
obs_data_array_t *defaultTransitionsArray =
|
||||
obs_data_get_array(obj, "defaultTransitions");
|
||||
@@ -397,22 +381,15 @@ void SwitcherData::loadSceneTransitions(obs_data_t *obj)
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
obs_data_t *array_obj =
|
||||
obs_data_array_item(defaultTransitionsArray, i);
|
||||
|
||||
const char *scene = obs_data_get_string(array_obj, "Scene");
|
||||
const char *transition =
|
||||
obs_data_get_string(array_obj, "transition");
|
||||
|
||||
switcher->defaultSceneTransitions.emplace_back(
|
||||
GetWeakSourceByName(scene),
|
||||
GetWeakTransitionByName(transition));
|
||||
|
||||
defaultSceneTransitions.emplace_back();
|
||||
defaultSceneTransitions.back().load(array_obj);
|
||||
obs_data_release(array_obj);
|
||||
}
|
||||
obs_data_array_release(defaultTransitionsArray);
|
||||
|
||||
switcher->tansitionOverrideOverride =
|
||||
transitionOverrideOverride =
|
||||
obs_data_get_bool(obj, "tansitionOverrideOverride");
|
||||
switcher->adjustActiveTransitionType =
|
||||
adjustActiveTransitionType =
|
||||
obs_data_get_bool(obj, "adjustActiveTransitionType");
|
||||
DefaultSceneTransition::delay =
|
||||
obs_data_get_int(obj, "defTransitionDelay");
|
||||
@@ -453,7 +430,7 @@ void AdvSceneSwitcher::setupTransitionsTab()
|
||||
}
|
||||
|
||||
ui->transitionOverridecheckBox->setChecked(
|
||||
switcher->tansitionOverrideOverride);
|
||||
switcher->transitionOverrideOverride);
|
||||
ui->adjustActiveTransitionType->setChecked(
|
||||
switcher->adjustActiveTransitionType);
|
||||
|
||||
@@ -487,25 +464,49 @@ bool SceneTransition::valid()
|
||||
(SceneSwitcherEntry::valid() && WeakSourceValid(scene2));
|
||||
}
|
||||
|
||||
void SceneTransition::save(obs_data_t *obj)
|
||||
{
|
||||
SceneSwitcherEntry::save(obj, "targetType", "Scene1");
|
||||
obs_data_set_string(obj, "Scene2", GetWeakSourceName(scene2).c_str());
|
||||
obs_data_set_double(obj, "duration", duration);
|
||||
}
|
||||
|
||||
void SceneTransition::load(obs_data_t *obj)
|
||||
{
|
||||
SceneSwitcherEntry::load(obj, "targetType", "Scene1");
|
||||
const char *sourceName = obs_data_get_string(obj, "Scene2");
|
||||
scene2 = GetWeakSourceByName(sourceName);
|
||||
duration = obs_data_get_double(obj, "duration");
|
||||
}
|
||||
|
||||
TransitionSwitchWidget::TransitionSwitchWidget(QWidget *parent,
|
||||
SceneTransition *s)
|
||||
: SwitchWidget(parent, s, false, false, false)
|
||||
{
|
||||
scenes2 = new QComboBox();
|
||||
duration = new QDoubleSpinBox();
|
||||
|
||||
duration->setMinimum(0.0);
|
||||
duration->setMaximum(99.000000);
|
||||
duration->setSuffix("s");
|
||||
|
||||
QWidget::connect(scenes2, SIGNAL(currentTextChanged(const QString &)),
|
||||
this, SLOT(Scene2Changed(const QString &)));
|
||||
QWidget::connect(duration, SIGNAL(valueChanged(double)), this,
|
||||
SLOT(DurationChanged(double)));
|
||||
|
||||
AdvSceneSwitcher::populateSceneSelection(scenes2, false);
|
||||
|
||||
if (s) {
|
||||
scenes2->setCurrentText(GetWeakSourceName(s->scene2).c_str());
|
||||
duration->setValue(s->duration);
|
||||
}
|
||||
|
||||
QHBoxLayout *mainLayout = new QHBoxLayout;
|
||||
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
|
||||
{"{{scenes}}", scenes},
|
||||
{"{{scenes2}}", scenes2},
|
||||
{"{{duration}}", duration},
|
||||
{"{{transitions}}", transitions}};
|
||||
placeWidgets(obs_module_text("AdvSceneSwitcher.transitionTab.entry"),
|
||||
mainLayout, widgetPlaceholders);
|
||||
@@ -546,6 +547,16 @@ void TransitionSwitchWidget::Scene2Changed(const QString &text)
|
||||
switchData->scene2 = GetWeakSourceByQString(text);
|
||||
}
|
||||
|
||||
void TransitionSwitchWidget::DurationChanged(double dur)
|
||||
{
|
||||
if (loading || !switchData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
switchData->duration = dur;
|
||||
}
|
||||
|
||||
DefTransitionSwitchWidget::DefTransitionSwitchWidget(QWidget *parent,
|
||||
DefaultSceneTransition *s)
|
||||
: SwitchWidget(parent, s, false, false, false)
|
||||
@@ -584,6 +595,16 @@ void DefTransitionSwitchWidget::swapSwitchData(DefTransitionSwitchWidget *s1,
|
||||
s2->setSwitchData(t);
|
||||
}
|
||||
|
||||
void DefaultSceneTransition::save(obs_data_t *obj)
|
||||
{
|
||||
SceneSwitcherEntry::save(obj, "targetType", "Scene");
|
||||
}
|
||||
|
||||
void DefaultSceneTransition::load(obs_data_t *obj)
|
||||
{
|
||||
SceneSwitcherEntry::load(obj, "targetType", "Scene");
|
||||
}
|
||||
|
||||
bool DefaultSceneTransition::checkMatch(OBSWeakSource currentScene)
|
||||
{
|
||||
return scene == currentScene;
|
||||
|
||||
@@ -9,136 +9,6 @@
|
||||
bool VideoSwitch::pause = false;
|
||||
static QMetaObject::Connection addPulse;
|
||||
|
||||
static void ScreenshotTick(void *param, float);
|
||||
|
||||
AdvSSScreenshotObj::AdvSSScreenshotObj(obs_source_t *source)
|
||||
: weakSource(OBSGetWeakRef(source))
|
||||
{
|
||||
obs_add_tick_callback(ScreenshotTick, this);
|
||||
}
|
||||
|
||||
AdvSSScreenshotObj::~AdvSSScreenshotObj()
|
||||
{
|
||||
obs_enter_graphics();
|
||||
gs_stagesurface_destroy(stagesurf);
|
||||
gs_texrender_destroy(texrender);
|
||||
obs_leave_graphics();
|
||||
|
||||
obs_remove_tick_callback(ScreenshotTick, this);
|
||||
}
|
||||
|
||||
void AdvSSScreenshotObj::Screenshot()
|
||||
{
|
||||
OBSSource source = OBSGetStrongRef(weakSource);
|
||||
|
||||
if (source) {
|
||||
cx = obs_source_get_base_width(source);
|
||||
cy = obs_source_get_base_height(source);
|
||||
} else {
|
||||
obs_video_info ovi;
|
||||
obs_get_video_info(&ovi);
|
||||
cx = ovi.base_width;
|
||||
cy = ovi.base_height;
|
||||
}
|
||||
|
||||
if (!cx || !cy) {
|
||||
blog(LOG_WARNING, "Cannot screenshot, invalid target size");
|
||||
obs_remove_tick_callback(ScreenshotTick, this);
|
||||
done = true;
|
||||
return;
|
||||
}
|
||||
|
||||
texrender = gs_texrender_create(GS_RGBA, GS_ZS_NONE);
|
||||
stagesurf = gs_stagesurface_create(cx, cy, GS_RGBA);
|
||||
|
||||
gs_texrender_reset(texrender);
|
||||
if (gs_texrender_begin(texrender, cx, cy)) {
|
||||
vec4 zero;
|
||||
vec4_zero(&zero);
|
||||
|
||||
gs_clear(GS_CLEAR_COLOR, &zero, 0.0f, 0);
|
||||
gs_ortho(0.0f, (float)cx, 0.0f, (float)cy, -100.0f, 100.0f);
|
||||
|
||||
gs_blend_state_push();
|
||||
gs_blend_function(GS_BLEND_ONE, GS_BLEND_ZERO);
|
||||
|
||||
if (source) {
|
||||
obs_source_inc_showing(source);
|
||||
obs_source_video_render(source);
|
||||
obs_source_dec_showing(source);
|
||||
} else {
|
||||
obs_render_main_texture();
|
||||
}
|
||||
|
||||
gs_blend_state_pop();
|
||||
gs_texrender_end(texrender);
|
||||
}
|
||||
}
|
||||
|
||||
void AdvSSScreenshotObj::Download()
|
||||
{
|
||||
gs_stage_texture(stagesurf, gs_texrender_get_texture(texrender));
|
||||
}
|
||||
|
||||
void AdvSSScreenshotObj::Copy()
|
||||
{
|
||||
uint8_t *videoData = nullptr;
|
||||
uint32_t videoLinesize = 0;
|
||||
|
||||
image = QImage(cx, cy, QImage::Format::Format_RGBX8888);
|
||||
|
||||
if (gs_stagesurface_map(stagesurf, &videoData, &videoLinesize)) {
|
||||
int linesize = image.bytesPerLine();
|
||||
for (int y = 0; y < (int)cy; y++)
|
||||
memcpy(image.scanLine(y),
|
||||
videoData + (y * videoLinesize), linesize);
|
||||
|
||||
gs_stagesurface_unmap(stagesurf);
|
||||
}
|
||||
}
|
||||
|
||||
void AdvSSScreenshotObj::MarkDone()
|
||||
{
|
||||
time = std::chrono::high_resolution_clock::now();
|
||||
done = true;
|
||||
}
|
||||
|
||||
#define STAGE_SCREENSHOT 0
|
||||
#define STAGE_DOWNLOAD 1
|
||||
#define STAGE_COPY_AND_SAVE 2
|
||||
#define STAGE_FINISH 3
|
||||
|
||||
static void ScreenshotTick(void *param, float)
|
||||
{
|
||||
AdvSSScreenshotObj *data =
|
||||
reinterpret_cast<AdvSSScreenshotObj *>(param);
|
||||
|
||||
if (data->stage == STAGE_FINISH) {
|
||||
return;
|
||||
}
|
||||
|
||||
obs_enter_graphics();
|
||||
|
||||
switch (data->stage) {
|
||||
case STAGE_SCREENSHOT:
|
||||
data->Screenshot();
|
||||
break;
|
||||
case STAGE_DOWNLOAD:
|
||||
data->Download();
|
||||
break;
|
||||
case STAGE_COPY_AND_SAVE:
|
||||
data->Copy();
|
||||
data->MarkDone();
|
||||
|
||||
obs_remove_tick_callback(ScreenshotTick, data);
|
||||
break;
|
||||
}
|
||||
|
||||
obs_leave_graphics();
|
||||
|
||||
data->stage++;
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::on_videoAdd_clicked()
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
@@ -257,13 +127,14 @@ void AdvSceneSwitcher::on_getScreenshot_clicked()
|
||||
sw->SetFilePath(file.fileName());
|
||||
}
|
||||
|
||||
void SwitcherData::checkVideoSwitch(bool &match, OBSWeakSource &scene,
|
||||
bool SwitcherData::checkVideoSwitch(OBSWeakSource &scene,
|
||||
OBSWeakSource &transition)
|
||||
{
|
||||
if (VideoSwitch::pause) {
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool match = false;
|
||||
for (auto &s : videoSwitches) {
|
||||
bool matched = s.checkMatch();
|
||||
if (!match && matched) {
|
||||
@@ -275,6 +146,7 @@ void SwitcherData::checkVideoSwitch(bool &match, OBSWeakSource &scene,
|
||||
}
|
||||
}
|
||||
}
|
||||
return match;
|
||||
}
|
||||
|
||||
void SwitcherData::saveVideoSwitches(obs_data_t *obj)
|
||||
@@ -294,7 +166,7 @@ void SwitcherData::saveVideoSwitches(obs_data_t *obj)
|
||||
|
||||
void SwitcherData::loadVideoSwitches(obs_data_t *obj)
|
||||
{
|
||||
switcher->videoSwitches.clear();
|
||||
videoSwitches.clear();
|
||||
|
||||
obs_data_array_t *videoArray = obs_data_get_array(obj, "videoSwitches");
|
||||
size_t count = obs_data_array_count(videoArray);
|
||||
@@ -302,7 +174,7 @@ void SwitcherData::loadVideoSwitches(obs_data_t *obj)
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
obs_data_t *array_obj = obs_data_array_item(videoArray, i);
|
||||
|
||||
switcher->videoSwitches.emplace_back();
|
||||
videoSwitches.emplace_back();
|
||||
videoSwitches.back().load(array_obj);
|
||||
|
||||
obs_data_release(array_obj);
|
||||
@@ -355,6 +227,11 @@ void VideoSwitch::save(obs_data_t *obj)
|
||||
obs_data_set_bool(obj, "ignoreInactiveSource", ignoreInactiveSource);
|
||||
}
|
||||
|
||||
bool requiresFileInput(videoSwitchType t)
|
||||
{
|
||||
return t == videoSwitchType::MATCH || t == videoSwitchType::DIFFER;
|
||||
}
|
||||
|
||||
void VideoSwitch::load(obs_data_t *obj)
|
||||
{
|
||||
SceneSwitcherEntry::load(obj);
|
||||
@@ -367,7 +244,7 @@ void VideoSwitch::load(obs_data_t *obj)
|
||||
file = obs_data_get_string(obj, "filePath");
|
||||
ignoreInactiveSource = obs_data_get_bool(obj, "ignoreInactiveSource");
|
||||
|
||||
if (condition != videoSwitchType::HAS_NOT_CHANGED) {
|
||||
if (requiresFileInput(condition)) {
|
||||
(void)loadImageFromFile();
|
||||
}
|
||||
}
|
||||
@@ -423,6 +300,10 @@ bool VideoSwitch::checkMatch()
|
||||
conditionMatch = screenshotData->image ==
|
||||
matchImage;
|
||||
break;
|
||||
case videoSwitchType::HAS_CHANGED:
|
||||
conditionMatch = screenshotData->image !=
|
||||
matchImage;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -444,7 +325,7 @@ bool VideoSwitch::checkMatch()
|
||||
match = true;
|
||||
}
|
||||
|
||||
if (condition == videoSwitchType::HAS_NOT_CHANGED) {
|
||||
if (!requiresFileInput(condition)) {
|
||||
matchImage = std::move(screenshotData->image);
|
||||
}
|
||||
previousTime = std::move(screenshotData->time);
|
||||
@@ -480,6 +361,8 @@ static inline void populateConditionSelection(QComboBox *list)
|
||||
obs_module_text("AdvSceneSwitcher.videoTab.condition.differ"));
|
||||
list->addItem(obs_module_text(
|
||||
"AdvSceneSwitcher.videoTab.condition.hasNotChanged"));
|
||||
list->addItem(obs_module_text(
|
||||
"AdvSceneSwitcher.videoTab.condition.hasChanged"));
|
||||
}
|
||||
|
||||
VideoSwitchWidget::VideoSwitchWidget(QWidget *parent, VideoSwitch *s)
|
||||
@@ -530,7 +413,7 @@ VideoSwitchWidget::VideoSwitchWidget(QWidget *parent, VideoSwitch *s)
|
||||
filePath->setText(QString::fromStdString(s->file));
|
||||
ignoreInactiveSource->setChecked(s->ignoreInactiveSource);
|
||||
|
||||
if (s->condition == videoSwitchType::HAS_NOT_CHANGED) {
|
||||
if (!requiresFileInput(s->condition)) {
|
||||
filePath->hide();
|
||||
browseButton->hide();
|
||||
}
|
||||
@@ -582,8 +465,7 @@ void VideoSwitchWidget::swapSwitchData(VideoSwitchWidget *s1,
|
||||
|
||||
void VideoSwitchWidget::UpdatePreviewTooltip()
|
||||
{
|
||||
if (!switchData ||
|
||||
switchData->condition == videoSwitchType::HAS_NOT_CHANGED) {
|
||||
if (!switchData || !requiresFileInput(switchData->condition)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -627,12 +509,12 @@ void VideoSwitchWidget::ConditionChanged(int cond)
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
switchData->condition = static_cast<videoSwitchType>(cond);
|
||||
|
||||
if (switchData->condition == videoSwitchType::HAS_NOT_CHANGED) {
|
||||
filePath->hide();
|
||||
browseButton->hide();
|
||||
} else {
|
||||
if (requiresFileInput(switchData->condition)) {
|
||||
filePath->show();
|
||||
browseButton->show();
|
||||
} else {
|
||||
filePath->hide();
|
||||
browseButton->hide();
|
||||
}
|
||||
|
||||
// Reload image data to avoid incorrect matches.
|
||||
|
||||
@@ -214,15 +214,16 @@ void checkWindowTitleSwitchRegex(WindowSwitch &s,
|
||||
}
|
||||
}
|
||||
|
||||
void SwitcherData::checkWindowTitleSwitch(bool &match, OBSWeakSource &scene,
|
||||
bool SwitcherData::checkWindowTitleSwitch(OBSWeakSource &scene,
|
||||
OBSWeakSource &transition)
|
||||
{
|
||||
if (WindowSwitch::pause) {
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string currentWindowTitle;
|
||||
GetCurrentWindowTitle(currentWindowTitle);
|
||||
bool match = false;
|
||||
|
||||
// Check if current window is ignored
|
||||
for (auto &window : ignoreWindowsSwitches) {
|
||||
@@ -268,12 +269,13 @@ void SwitcherData::checkWindowTitleSwitch(bool &match, OBSWeakSource &scene,
|
||||
break;
|
||||
}
|
||||
}
|
||||
return match;
|
||||
}
|
||||
|
||||
void SwitcherData::saveWindowTitleSwitches(obs_data_t *obj)
|
||||
{
|
||||
obs_data_array_t *windowTitleArray = obs_data_array_create();
|
||||
for (WindowSwitch &s : switcher->windowSwitches) {
|
||||
for (WindowSwitch &s : windowSwitches) {
|
||||
obs_data_t *array_obj = obs_data_create();
|
||||
|
||||
s.save(array_obj);
|
||||
@@ -285,7 +287,7 @@ void SwitcherData::saveWindowTitleSwitches(obs_data_t *obj)
|
||||
obs_data_array_release(windowTitleArray);
|
||||
|
||||
obs_data_array_t *ignoreWindowsArray = obs_data_array_create();
|
||||
for (std::string &window : switcher->ignoreWindowsSwitches) {
|
||||
for (std::string &window : ignoreWindowsSwitches) {
|
||||
obs_data_t *array_obj = obs_data_create();
|
||||
obs_data_set_string(array_obj, "ignoreWindow", window.c_str());
|
||||
obs_data_array_push_back(ignoreWindowsArray, array_obj);
|
||||
@@ -297,7 +299,7 @@ void SwitcherData::saveWindowTitleSwitches(obs_data_t *obj)
|
||||
|
||||
void SwitcherData::loadWindowTitleSwitches(obs_data_t *obj)
|
||||
{
|
||||
switcher->windowSwitches.clear();
|
||||
windowSwitches.clear();
|
||||
|
||||
obs_data_array_t *windowTitleArray =
|
||||
obs_data_get_array(obj, "switches");
|
||||
@@ -307,14 +309,14 @@ void SwitcherData::loadWindowTitleSwitches(obs_data_t *obj)
|
||||
obs_data_t *array_obj =
|
||||
obs_data_array_item(windowTitleArray, i);
|
||||
|
||||
switcher->windowSwitches.emplace_back();
|
||||
windowSwitches.emplace_back();
|
||||
windowSwitches.back().load(array_obj);
|
||||
|
||||
obs_data_release(array_obj);
|
||||
}
|
||||
obs_data_array_release(windowTitleArray);
|
||||
|
||||
switcher->ignoreWindowsSwitches.clear();
|
||||
ignoreWindowsSwitches.clear();
|
||||
|
||||
obs_data_array_t *ignoreWindowsArray =
|
||||
obs_data_get_array(obj, "ignoreWindows");
|
||||
@@ -327,7 +329,7 @@ void SwitcherData::loadWindowTitleSwitches(obs_data_t *obj)
|
||||
const char *window =
|
||||
obs_data_get_string(array_obj, "ignoreWindow");
|
||||
|
||||
switcher->ignoreWindowsSwitches.emplace_back(window);
|
||||
ignoreWindowsSwitches.emplace_back(window);
|
||||
|
||||
obs_data_release(array_obj);
|
||||
}
|
||||
@@ -379,46 +381,8 @@ void WindowSwitch::save(obs_data_t *obj)
|
||||
obs_data_set_bool(obj, "focus", focus);
|
||||
}
|
||||
|
||||
// To be removed in future version
|
||||
bool loadOldWindow(obs_data_t *obj, WindowSwitch *s)
|
||||
{
|
||||
if (!s) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const char *scene = obs_data_get_string(obj, "scene");
|
||||
|
||||
if (strcmp(scene, "") == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
s->scene = GetWeakSourceByName(scene);
|
||||
|
||||
const char *transition = obs_data_get_string(obj, "transition");
|
||||
s->transition = GetWeakTransitionByName(transition);
|
||||
|
||||
s->window = obs_data_get_string(obj, "window_title");
|
||||
s->fullscreen = obs_data_get_bool(obj, "fullscreen");
|
||||
#if __APPLE__
|
||||
// TODO:
|
||||
// not implemented on MacOS as I cannot test it
|
||||
s->maximized = false;
|
||||
#else
|
||||
s->maximized = obs_data_get_bool(obj, "maximized");
|
||||
#endif
|
||||
s->focus = obs_data_get_bool(obj, "focus") ||
|
||||
!obs_data_has_user_value(obj, "focus");
|
||||
s->usePreviousScene = strcmp(scene, previous_scene_name) == 0;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void WindowSwitch::load(obs_data_t *obj)
|
||||
{
|
||||
if (loadOldWindow(obj, this)) {
|
||||
return;
|
||||
}
|
||||
|
||||
SceneSwitcherEntry::load(obj);
|
||||
|
||||
window = obs_data_get_string(obj, "windowTitle");
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
#include <util/platform.h>
|
||||
#include "../headers/advanced-scene-switcher.hpp"
|
||||
#include <TlHelp32.h>
|
||||
#include <Psapi.h>
|
||||
#include <locale>
|
||||
#include <codecvt>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <QStringList>
|
||||
#include <QRegularExpression>
|
||||
|
||||
#define MAX_SEARCH 1000
|
||||
|
||||
|
||||
Reference in New Issue
Block a user