Compare commits

..

77 Commits

Author SHA1 Message Date
WarmUpTill
7d381b9ba1 Adjust to merge 2021-06-06 19:24:10 +02:00
WarmUpTill
d66a2dc408 Merge branch 'master' into virtual-desktop 2021-06-06 19:14:14 +02:00
WarmUpTill
047ca93486 Clean up 2021-06-06 19:05:55 +02:00
WarmUpTill
bd00a3f97e Remove preprocessing / todo 2021-06-04 08:31:28 -07:00
WarmUpTill
72f67705ed Collapse time restriction to clock symbol if no restriction is set 2021-06-03 10:19:19 -07:00
WarmUpTill
38161f63a3 Add helper getDataFilePath()
Used to get absolute file path to files in the plugin's data directory
2021-06-03 10:19:19 -07:00
WarmUpTill
b9f6e47a49 Add backwads compatability check to load() of DurationConstraint 2021-06-03 10:19:19 -07:00
WarmUpTill
c82c56dbb7 Remove {{duration}} placeholder text where longer needed 2021-06-03 10:19:19 -07:00
WarmUpTill
326cc0c46f Add generic time constraint to macro conditions
Almost every macro condition can benefit from having the option to
specify a time constraint.
Instead of adding it to each condition separately add it to all
conditions with the option of excluding it when registering a new
condition type.
2021-06-03 10:19:19 -07:00
WarmUpTill
d9997d374d Add option to un-/pause macros using hotkeys 2021-05-30 11:05:44 -07:00
WarmUpTill
c18d799f4a Remove comment 2021-05-30 11:05:12 -07:00
WarmUpTill
fdb72629db Do not trigger resize if section is collapsed 2021-05-30 11:05:12 -07:00
WarmUpTill
7fda5e3820 Fix empty selection not showing correctly for visibility and filter 2021-05-30 11:05:12 -07:00
WarmUpTill
78966e456a Fix empty selection not showing correctly 2021-05-29 15:51:22 -07:00
WarmUpTill
49a042039e Add ability to set source settings to macro action "source" 2021-05-29 06:48:18 -07:00
WarmUpTill
adb266927f Move getSourceSettings() to utility 2021-05-29 06:48:18 -07:00
WarmUpTill
703f24b8fc Remove unused include 2021-05-29 06:48:18 -07:00
WarmUpTill
de6120c401 Save and load source settings 2021-05-29 06:48:18 -07:00
WarmUpTill
b1f7a255d5 Use same collapse threshold for actions and sources
Actions and conditions are no longer in fixed positions and instead
divided by a separator.
2021-05-29 06:47:16 -07:00
WarmUpTill
d112e92d33 Add scenes with filters to source selection for filter action 2021-05-29 06:47:16 -07:00
WarmUpTill
5c4dd6dae1 Set "select <xyz>" to disabled and sort selection alphabetically 2021-05-29 06:47:16 -07:00
WarmUpTill
546a83ee07 Add macro action "plugin-state" 2021-05-28 10:23:35 -07:00
WarmUpTill
09ab29b4ba Trigger resize of widget 2021-05-28 10:23:24 -07:00
WarmUpTill
86d4e59dc2 Adjust to section widget rework 2021-05-28 10:23:24 -07:00
WarmUpTill
bf03edd391 Adjust size constraints of macro condition / action layouts
The reason for this change is that the macro condition "source" was
introduced which can change its required size drastically depending on
which condition type is selected.
2021-05-28 10:23:24 -07:00
WarmUpTill
aa7714210a Rework section widget to support dynamically sized content 2021-05-28 10:23:24 -07:00
WarmUpTill
2454ea8fa5 Add macro condition "source" 2021-05-28 10:23:24 -07:00
WarmUpTill
a5edbc8b88 Reset macro counters when stopping the plugin 2021-05-27 12:05:44 -07:00
WarmUpTill
ec6cffcd6a Rename MacroActionPause and add action to reset macro counter 2021-05-27 12:05:44 -07:00
WarmUpTill
89849a0792 Rename files 2021-05-27 12:05:44 -07:00
WarmUpTill
48e6bcbb7a Fix conditions and actions referencing macros not loading correctly
The reason is that while loading the required macro might not have been
loaded resulting in the pointer to the macro never being set correctly
in the loading phase.
2021-05-27 12:05:44 -07:00
WarmUpTill
6893d9539e Add MacroSelection widget 2021-05-27 12:05:44 -07:00
WarmUpTill
4a84bede37 Add macro condition "count" 2021-05-27 12:05:44 -07:00
WarmUpTill
77642c54d8 Add macro count member
Used to indicate the number of times the actions were executed.
2021-05-27 12:05:44 -07:00
WarmUpTill
069b0f89d0 Adjust locale 2021-05-27 12:05:32 -07:00
WarmUpTill
e517fa7b39 Sort source selection alphabetically 2021-05-27 12:05:32 -07:00
WarmUpTill
9301713c49 Add option to not scene change msg via network tab 2021-05-25 10:18:36 -07:00
WarmUpTill
66285a0d3d Add option to synchronize preview scene via network tab 2021-05-25 10:18:36 -07:00
WarmUpTill
e83a9e8d94 Clean up
No need to pass switcher as parameter
2021-05-25 10:18:36 -07:00
WarmUpTill
881e197cb2 Fix visibility of scene items in groups not changing
Also allow groups to be selected as target for visibility action
2021-05-24 07:30:24 -07:00
WarmUpTill
e7f031641f Add warning for macro action source 2021-05-24 07:29:56 -07:00
WarmUpTill
198a5dc1ec Move AskBackup() 2021-05-24 07:29:56 -07:00
WarmUpTill
5496c256c0 Move functions not required in advanced-scene-switcher.hpp
Create header for platform specific functions.
Move some UI helper functions to utility.hpp.
2021-05-24 07:29:56 -07:00
WarmUpTill
a93cec2bb9 Fix typo 2021-05-24 07:29:56 -07:00
WarmUpTill
32101627a6 Remove unused settings value 2021-05-23 17:52:15 -07:00
WarmUpTill
c2d2a27d44 Use more const refs where applicable 2021-05-23 17:52:15 -07:00
WarmUpTill
5eb8378612 Run condition check for paused macros
Otherwise conditions might behave in unexpected ways when resuming.
For example, audio could immediately match after unpause, when it
matched before it was paused due to timers not being updated.
2021-05-23 12:15:04 -07:00
WarmUpTill
961ea655fd Macro pause (#201)
* Add option to pause individual macros
* Add macro action to pause and unpause macros
2021-05-23 08:54:59 +02:00
WarmUpTill
c1704f2164 Fix rapid scene switches by random tab
Wait scene was not set while waiting for scene changes on the random
tab.
If wait scene was ever set by other means (sequence) the scene switcher
would immediately wake up as the wait scene is not the expected value.
2021-05-22 10:49:31 -07:00
WarmUpTill
e4b060976b Add macro action "media" 2021-05-22 10:48:52 -07:00
WarmUpTill
186198bfd4 Reduce macro logging in non-verbose mode 2021-05-22 12:14:56 +02:00
WarmUpTill
283025f1a8 Add helper function to convert to new id format 2021-05-22 03:07:09 -07:00
WarmUpTill
5b4ddfa669 Adjust logging to use new ids 2021-05-22 03:07:09 -07:00
WarmUpTill
572b974b82 Switch to using string as ids for actions and conditions
Using int is an id was getting messy with more and more conditions and
actions beeing added.
It also made the order of conditions and actions in the respective
comboboxes unchangable as there was a 1 to 1 relation of index and id.
2021-05-22 03:07:09 -07:00
WarmUpTill
ae71b24c60 Adjust locale 2021-05-21 16:50:05 -07:00
WarmUpTill
e803ba75f9 CI: Use new windows deps version 2021-05-21 16:49:47 -07:00
WarmUpTill
72bd905e7c Improve logging for macros 2021-05-20 11:36:08 -07:00
WarmUpTill
51680adc20 Add macro condition "interval" 2021-05-19 11:47:32 -07:00
WarmUpTill
4b84850f00 Use splitter to separate actions and conditions 2021-05-19 11:47:22 -07:00
WarmUpTill
6a998934d0 Add macro action "source" 2021-05-18 11:12:22 -07:00
WarmUpTill
b80c16a446 Add macro action filter 2021-05-17 13:16:13 -07:00
WarmUpTill
f4d11dd6e6 Add filter helper functions 2021-05-17 13:16:13 -07:00
WarmUpTill
4248b86705 Add macro action to control scene item visibility 2021-05-17 12:20:54 -07:00
WarmUpTill
305ce96ad0 Adjust includes to new file name 2021-05-17 12:20:54 -07:00
WarmUpTill
74dfd9af30 Rename 2021-05-17 12:20:54 -07:00
WarmUpTill
c911862d8c Add Russian locale 2021-05-17 11:57:12 -07:00
WarmUpTill
2a73583793 Fix fullscreen and implement maximized checks 2021-05-17 11:08:26 -07:00
WarmUpTill
8f16d05a28 Fix potential crash in audio macro action
UpdateEntryData() could be called from ActionChanged() leading to a
potential double lock of switcher->m when triggerin other slots of this
widget
2021-05-16 13:00:06 -07:00
WarmUpTill
2ac1a226e2 Implement virtual desktop funcs for windows 2021-05-16 14:58:43 +02:00
WarmUpTill
f56b0b56a7 Merge branch 'master' into virtual-desktop 2021-05-16 13:42:13 +02:00
WarmUpTill
6c1eb3ffab Fix random order of various macro entries 2021-05-15 00:40:43 +02:00
WarmUpTill
e9d6067db2 Show currently active virtual desktop 2021-05-15 00:19:00 +02:00
WarmUpTill
d691492a31 Populate virt desktop selection 2021-05-15 00:04:49 +02:00
WarmUpTill
950dfe8688 Request correct information to get virt desktop count 2021-05-15 00:04:35 +02:00
WarmUpTill
fb38f60609 Drop static used for testing ... 2021-05-14 23:55:23 +02:00
WarmUpTill
3c283a0818 Format 2021-05-14 23:35:57 +02:00
WarmUpTill
271bbcbe95 Add macro condition "virtual desktop" (draft) 2021-05-14 23:34:39 +02:00
107 changed files with 5459 additions and 1444 deletions

View File

@@ -200,7 +200,7 @@ jobs:
QT_VERSION: 5.10.1
CMAKE_GENERATOR: "Visual Studio 16 2019"
CMAKE_SYSTEM_VERSION: "10.0.18363.657"
WINDOWS_DEPS_VERSION: '2017'
WINDOWS_DEPS_VERSION: '2019'
steps:
- name: Add msbuild to PATH
uses: microsoft/setup-msbuild@v1.0.2

View File

@@ -62,7 +62,6 @@ set(advanced-scene-switcher_HEADERS
${advanced-scene-switcher_HEADERS}
src/headers/advanced-scene-switcher.hpp
src/headers/switcher-data-structs.hpp
src/headers/utility.hpp
src/headers/scene-group.hpp
src/headers/scene-trigger.hpp
src/headers/switch-audio.hpp
@@ -82,31 +81,44 @@ set(advanced-scene-switcher_HEADERS
src/headers/switch-generic.hpp
src/headers/macro-action-edit.hpp
src/headers/macro-action-audio.hpp
src/headers/macro-action-filter.hpp
src/headers/macro-action-macro.hpp
src/headers/macro-action-media.hpp
src/headers/macro-action-plugin-state.hpp
src/headers/macro-action-recording.hpp
src/headers/macro-action-replay-buffer.hpp
src/headers/macro-action-run.hpp
src/headers/macro-action-scene-switch.hpp
src/headers/macro-action-scene-visibility.hpp
src/headers/macro-action-source.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-counter.hpp
src/headers/macro-condition-file.hpp
src/headers/macro-condition-idle.hpp
src/headers/macro-condition-interval.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-source.hpp
src/headers/macro-condition-streaming.hpp
src/headers/macro-condition-video.hpp
src/headers/macro-condition-virt-desktop.hpp
src/headers/macro-condition-window.hpp
src/headers/macro.hpp
src/headers/macro-selection.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/platform-funcs.hpp
src/headers/utility.hpp
src/headers/volume-control.hpp
src/headers/version.h
)
@@ -138,32 +150,44 @@ set(advanced-scene-switcher_SOURCES
src/switch-generic.cpp
src/macro-action-edit.cpp
src/macro-action-audio.cpp
src/macro-action-filter.cpp
src/macro-action-macro.cpp
src/macro-action-media.cpp
src/macro-action-plugin-state.cpp
src/macro-action-recording.cpp
src/macro-action-replay-buffer.cpp
src/macro-action-run.cpp
src/macro-action-scene-switch.cpp
src/macro-action-scene-visibility.cpp
src/macro-action-source.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-counter.cpp
src/macro-condition-file.cpp
src/macro-condition-idle.cpp
src/macro-condition-interval.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-source.cpp
src/macro-condition-streaming.cpp
src/macro-condition-video.cpp
src/macro-condition-virt-desktop.cpp
src/macro-condition-window.cpp
src/macro.cpp
src/macro-selection.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/utility.cpp
src/volume-control.cpp
src/version.cpp
)

View File

@@ -227,7 +227,9 @@ AdvSceneSwitcher.networkTab.Disabledwarning="Diese Funktionalität musste unter
AdvSceneSwitcher.networkTab.server="Server starten (Sendet Szenenwechselnachrichten zu allen verbundenen Clients)"
AdvSceneSwitcher.networkTab.server.port="Port"
AdvSceneSwitcher.networkTab.server.lockToIPv4="Nur IPv4 verwenden (deaktiviert IPv6)"
AdvSceneSwitcher.networkTab.server.restrictSendToAutomatedSwitches="Sende nur Szenenwechselnachrichten für automatisierte Szenenwechsel"
AdvSceneSwitcher.networkTab.server.sendSceneChange="Sende Nachrichten für Szenenwechsel"
AdvSceneSwitcher.networkTab.server.restrictSendToAutomatedSwitches="Sende nur Szenenwechselnachrichten für automatisierte Szenenwechsel"
AdvSceneSwitcher.networkTab.server.sendPreview="Sende Nachrichten für Vorschau Szenenwechsel im Studio-Modus"
AdvSceneSwitcher.networkTab.startFailed.message="Der WebSocket-Server konnte nicht gestartet werden, mögliche Gründe:\n - Der TCP-Port %1 wird möglicherweise gerade von einem anderen Programm verwendet. Versuchen Sie einen anderen Port in den Websocket-Servereinstellungen zu setzen oder alle Programme zu beenden, die den Port möglicherweise verwenden.\n - Fehler: %2"
AdvSceneSwitcher.networkTab.server.status.currentStatus="Aktueller Status"
AdvSceneSwitcher.networkTab.server.status.notRunning="Nicht gestartet"

View File

@@ -57,8 +57,8 @@ AdvSceneSwitcher.generalTab.priority.macro="Macro"
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.editConditionHelp="This section allows you to define Macro conditions.\n\nSelect an existing or add a new Macro on the left.\nThen click the plus button below to add a new condition."
AdvSceneSwitcher.macroTab.editActionHelp="This section allows you to define Macro actions.\n\nSelect an existing or add a new Macro on the left.\nThen click the plus button below to add a new action."
AdvSceneSwitcher.macroTab.edit="Edit macro"
AdvSceneSwitcher.macroTab.edit.logic="Logic type:"
AdvSceneSwitcher.macroTab.edit.condition="Condition type:"
@@ -68,6 +68,7 @@ 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"
@@ -76,17 +77,18 @@ 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.audio.state.below="Below"
AdvSceneSwitcher.condition.audio.state.above="Above"
AdvSceneSwitcher.condition.audio.entry="Volume of {{audioSources}} is {{condition}} {{volume}}"
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.scene.entry="{{sceneType}} scene is {{scenes}}"
AdvSceneSwitcher.condition.window="Window"
AdvSceneSwitcher.condition.window.entry.line1="{{windows}} exist and ..."
AdvSceneSwitcher.condition.window.entry.line2="... is {{fullscreen}} fullscreen {{maximized}} maximized {{focused}} focused"
@@ -105,16 +107,16 @@ 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.video.entry="{{videoSources}} {{condition}} {{filePath}} {{browseButton}}"
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.stream.entry="{{streamState}}"
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.record.entry="{{recordState}}"
AdvSceneSwitcher.condition.process="Process"
AdvSceneSwitcher.condition.process.entry="{{processes}} is running {{focused}} and is focused"
AdvSceneSwitcher.condition.idle="Idle"
@@ -122,6 +124,28 @@ AdvSceneSwitcher.condition.idle.entry="No keyboard or mouse inputs for {{duratio
AdvSceneSwitcher.condition.pluginState="Plugin state"
AdvSceneSwitcher.condition.pluginState.state.sceneSwitched="Automated scene change was triggered in this interval"
AdvSceneSwitcher.condition.pluginState.entry="{{condition}}"
AdvSceneSwitcher.condition.virtDesktop="Virtual desktop"
AdvSceneSwitcher.condition.virtDesktop.notAvailable="Not availbale"
AdvSceneSwitcher.condition.virtDesktop.entry.line1="Active virtual desktop is {{virtDesktops}}"
AdvSceneSwitcher.condition.virtDesktop.entry.line2="Current: {{currentDesktop}}"
AdvSceneSwitcher.condition.interval="Interval"
AdvSceneSwitcher.condition.interval.entry="{{duration}} have passed"
AdvSceneSwitcher.condition.counter="Count"
AdvSceneSwitcher.condition.counter.type.below="Less than"
AdvSceneSwitcher.condition.counter.type.above="More than"
AdvSceneSwitcher.condition.counter.type.equal="Exactly"
AdvSceneSwitcher.condition.counter.reset="Reset"
AdvSceneSwitcher.condition.counter.entry.line1="{{macros}} was executed {{conditions}} {{count}} times"
AdvSceneSwitcher.condition.counter.entry.line2="Current count: {{currentCount}} {{resetCount}}"
AdvSceneSwitcher.condition.source="Source"
AdvSceneSwitcher.condition.source.type.active="Is active"
AdvSceneSwitcher.condition.source.type.showing="Is showing"
AdvSceneSwitcher.condition.source.type.settings="Settings match"
AdvSceneSwitcher.condition.source.regex="Use regular expressions"
AdvSceneSwitcher.condition.source.getSettings="Get current settings"
AdvSceneSwitcher.condition.source.entry.line1="{{sources}} {{conditions}}"
AdvSceneSwitcher.condition.source.entry.line2="{{settings}}"
AdvSceneSwitcher.condition.source.entry.line3="{{regex}} {{getSettings}}"
; Macro Actions
AdvSceneSwitcher.action.switchScene="Switch scene"
@@ -155,7 +179,37 @@ AdvSceneSwitcher.action.streaming.type.start="Start streaming"
AdvSceneSwitcher.action.streaming.entry="{{actions}}"
AdvSceneSwitcher.action.run="Run"
AdvSceneSwitcher.action.run.entry="Run {{filePath}} {{browseButton}}"
AdvSceneSwitcher.action.sceneVisibility="Scene item visibility"
AdvSceneSwitcher.action.sceneVisibility.type.show="Show"
AdvSceneSwitcher.action.sceneVisibility.type.hide="Hide"
AdvSceneSwitcher.action.sceneVisibility.entry="On {{scenes}} {{actions}} {{sources}}"
AdvSceneSwitcher.action.filter="Filter"
AdvSceneSwitcher.action.filter.type.enable="Enable"
AdvSceneSwitcher.action.filter.type.disable="Disable"
AdvSceneSwitcher.action.filter.entry="On {{sources}} {{actions}} {{filters}}"
AdvSceneSwitcher.action.source="Source"
AdvSceneSwitcher.action.source.type.enable="Enable"
AdvSceneSwitcher.action.source.type.disable="Disable"
AdvSceneSwitcher.action.source.type.settings="Set settings"
AdvSceneSwitcher.action.source.entry="{{actions}} {{sources}}"
AdvSceneSwitcher.action.source.warning="Warning: Enabling and disabling sources globally cannot be controlled by the OBS UI"
AdvSceneSwitcher.action.source.getSettings="Get current settings"
AdvSceneSwitcher.action.media="Media"
AdvSceneSwitcher.action.media.type.play="Play"
AdvSceneSwitcher.action.media.type.pause="Pause"
AdvSceneSwitcher.action.media.type.stop="Stop"
AdvSceneSwitcher.action.media.type.restart="Restart"
AdvSceneSwitcher.action.media.type.next="Next"
AdvSceneSwitcher.action.media.type.previous="Previous"
AdvSceneSwitcher.action.media.entry="{{actions}} {{mediaSources}}"
AdvSceneSwitcher.action.macro="Macro"
AdvSceneSwitcher.action.macro.type.pause="Pause"
AdvSceneSwitcher.action.macro.type.unpause="Unpause"
AdvSceneSwitcher.action.macro.type.resetCounter="Reset counter"
AdvSceneSwitcher.action.macro.entry="{{actions}} {{macros}}"
AdvSceneSwitcher.action.pluginState="Plugin state"
AdvSceneSwitcher.action.pluginState.type.stop="Stop the Advanced Scene Switcher plugin"
AdvSceneSwitcher.action.pluginState.entry="{{actions}}"
; Transition Tab
AdvSceneSwitcher.transitionTab.title="Transition"
@@ -332,7 +386,9 @@ AdvSceneSwitcher.networkTab.DisabledWarning="This functionality unfortunately ha
AdvSceneSwitcher.networkTab.server="Start server (Sends scene switch messages to all connected clients)"
AdvSceneSwitcher.networkTab.server.port="Port"
AdvSceneSwitcher.networkTab.server.lockToIPv4="Lock server to only using IPv4"
AdvSceneSwitcher.networkTab.server.sendSceneChange="Send messages for scene changes"
AdvSceneSwitcher.networkTab.server.restrictSendToAutomatedSwitches="Only send messages for automated scene switches"
AdvSceneSwitcher.networkTab.server.sendPreview="Send messages for preview scene change when running in Studio mode"
AdvSceneSwitcher.networkTab.startFailed.message="The WebSockets server failed to start, maybe because:\n - TCP port %1 may currently be in use elsewhere on this system, possibly by another application. Try setting a different TCP port in the WebSocket server settings, or stop any application that could be using this port.\n - Error message: %2"
AdvSceneSwitcher.networkTab.server.status.currentStatus="Current status"
AdvSceneSwitcher.networkTab.server.status.notRunning="Not running"
@@ -394,6 +450,8 @@ AdvSceneSwitcher.sceneTriggerTab.help="This tab allows you to trigger actions on
AdvSceneSwitcher.hotkey.startSwitcherHotkey="Start the Advanced Scene Switcher"
AdvSceneSwitcher.hotkey.stopSwitcherHotkey="Stop the Advanced Scene Switcher"
AdvSceneSwitcher.hotkey.startStopToggleSwitcherHotkey="Toggle Start/Stop for the Advanced Scene Switcher"
AdvSceneSwitcher.hotkey.macro.pause="Pause macro %1"
AdvSceneSwitcher.hotkey.macro.unpause="Unpause macro %1"
AdvSceneSwitcher.askBackup="Detected a new version of the Advanced Scene Switcher.\nShould a backup of the old settings be created?"
@@ -405,10 +463,14 @@ AdvSceneSwitcher.selectPreviousScene="Previous Scene"
AdvSceneSwitcher.currentTransition="Current Transition"
AdvSceneSwitcher.selectTransition="--select transition--"
AdvSceneSwitcher.selectWindow="--select window--"
AdvSceneSwitcher.selectSource="--select source--"
AdvSceneSwitcher.selectAudioSource="--select audio source--"
AdvSceneSwitcher.selectVideoSource="--select video source--"
AdvSceneSwitcher.selectMediaSource="--select media source--"
AdvSceneSwitcher.selectProcess="--select process--"
AdvSceneSwitcher.selectFilter="--select filter--"
AdvSceneSwitcher.selectMacro="--select macro--"
AdvSceneSwitcher.selectItem="--select item--"
AdvSceneSwitcher.enterPath="--enter path--"
AdvSceneSwitcher.enterText="--enter text--"
AdvSceneSwitcher.invaildEntriesWillNotBeSaved="invalid entries will not be saved"
@@ -421,3 +483,7 @@ AdvSceneSwitcher.unit.milliseconds="milliseconds"
AdvSceneSwitcher.unit.secends="seconds"
AdvSceneSwitcher.unit.minutes="minutes"
AdvSceneSwitcher.unit.hours="hours"
AdvSceneSwitcher.duration.condition.none="No time constraint"
AdvSceneSwitcher.duration.condition.more="For at least"
AdvSceneSwitcher.duration.condition.equal="For exactly"
AdvSceneSwitcher.duration.condition.less="For at most"

423
data/locale/ru-RU.ini Normal file
View File

@@ -0,0 +1,423 @@
AdvSceneSwitcher.pluginName="Advanced Scene Switcher"
AdvSceneSwitcher.windowTitle="Advanced Scene Switcher"
; General Tab
AdvSceneSwitcher.generalTab.title="Общие"
AdvSceneSwitcher.generalTab.status="Статус"
AdvSceneSwitcher.generalTab.status.hotkeytips="Горячие клавиши можно определить в настройках OBS"
AdvSceneSwitcher.generalTab.status.currentStatus="Advanced Scene Switcher сейчас:"
AdvSceneSwitcher.generalTab.status.onStartup="При запуске OBS:"
AdvSceneSwitcher.generalTab.status.onStartup.asLastRun="Запустить переключатель сцен, если он был запущен"
AdvSceneSwitcher.generalTab.status.onStartup.alwaysStart="Всегда запускать переключатель сцен"
AdvSceneSwitcher.generalTab.status.onStartup.doNotStart="Не запускать переключатель сцен"
AdvSceneSwitcher.generalTab.status.start="Старт"
AdvSceneSwitcher.generalTab.status.stop="Стоп"
AdvSceneSwitcher.generalTab.status.autoStart="Автоматически запускать переключатель сцен, когда:"
AdvSceneSwitcher.generalTab.status.autoStart.never="Никогда"
AdvSceneSwitcher.generalTab.status.autoStart.recording="Запись"
AdvSceneSwitcher.generalTab.status.autoStart.streaming="Вещание"
AdvSceneSwitcher.generalTab.status.autoStart.recordingAndStreaming="Запись или потоковое вещание"
AdvSceneSwitcher.generalTab.status.checkInterval="Проверять условия переключения каждый раз"
AdvSceneSwitcher.generalTab.generalBehavior="Общее поведение"
AdvSceneSwitcher.generalTab.generalBehavior.onNoMet="Если не выполняется условие переключения для"
AdvSceneSwitcher.generalTab.generalBehavior.onNoMetDelayTooltip="Будет только настолько точным, насколько настроен интервал проверки."
AdvSceneSwitcher.generalTab.generalBehavior.onNoMet.dontSwitch="Не переключаться"
AdvSceneSwitcher.generalTab.generalBehavior.onNoMet.switchToRandom="Переключиться на любую сцену на вкладке Random"
AdvSceneSwitcher.generalTab.generalBehavior.onNoMet.switchTo="Переключиться на:"
AdvSceneSwitcher.generalTab.generalBehavior.cooldown="После совпадения не переключаться между сценами в течение"
AdvSceneSwitcher.generalTab.generalBehavior.cooldownHint="В течение этого времени потенциальные совпадения будут игнорироваться!"
AdvSceneSwitcher.generalTab.generalBehavior.verboseLogging="Включить ведение подробного журнала"
AdvSceneSwitcher.generalTab.generalBehavior.saveWindowGeo="Сохранять положение и размер окна"
AdvSceneSwitcher.generalTab.generalBehavior.disableUIHints="Отключить подсказки пользовательского интерфейса"
AdvSceneSwitcher.generalTab.priority="Приоритет"
AdvSceneSwitcher.generalTab.priority.description="Приоритет методов переключения (самый высокий приоритет - вверху)"
AdvSceneSwitcher.generalTab.priority.threadPriority="Использовать приоритет потока"
AdvSceneSwitcher.generalTab.priority.threadPriorityNotice="(Поднимать приоритет выше \"Нормальный\" не рекомендуется)"
AdvSceneSwitcher.generalTab.saveOrLoadsettings="Сохранить / загрузить настройки"
AdvSceneSwitcher.generalTab.saveOrLoadsettings.export="Экспорт"
AdvSceneSwitcher.generalTab.saveOrLoadsettings.import="Импорт"
AdvSceneSwitcher.generalTab.saveOrLoadsettings.exportWindowTitle="Экспортировать настройки расширенного переключателя сцен в файл ..."
AdvSceneSwitcher.generalTab.saveOrLoadsettings.importWindowTitle="Импортировать настройки Advanced Scene Switcher из файла ..."
AdvSceneSwitcher.generalTab.saveOrLoadsettings.textType="Текстовые файлы (*.txt)"
AdvSceneSwitcher.generalTab.saveOrLoadsettings.loadFail="Advanced Scene Switcher не удалось импортировать настройки"
AdvSceneSwitcher.generalTab.saveOrLoadsettings.loadSuccess="Настройки Advanced Scene Switcher импортированы успешно"
AdvSceneSwitcher.generalTab.priority.fileContent="Содержание файла"
AdvSceneSwitcher.generalTab.priority.sceneSequence="Последовательность сцены"
AdvSceneSwitcher.generalTab.priority.idleDetection="Обнаружение простоя"
AdvSceneSwitcher.generalTab.priority.executable="Исполняемый файл"
AdvSceneSwitcher.generalTab.priority.screenRegion="Область экрана"
AdvSceneSwitcher.generalTab.priority.windowTitle="Заголовок окна"
AdvSceneSwitcher.generalTab.priority.media="Медиа"
AdvSceneSwitcher.generalTab.priority.time="Время"
AdvSceneSwitcher.generalTab.priority.audio="Аудио"
AdvSceneSwitcher.generalTab.priority.video="Видео"
AdvSceneSwitcher.generalTab.priority.macro="Макрос"
; Macro Tab
AdvSceneSwitcher.macroTab.title="Макрос"
AdvSceneSwitcher.macroTab.macros="Макросы"
AdvSceneSwitcher.macroTab.help="Макросы позволяют выполнять ряд действий в зависимости от нескольких условий.\n\nНажмите на выделенный символ плюса, чтобы добавить новый макрос."
AdvSceneSwitcher.macroTab.editConditionHelp="Этот раздел позволяет определить условия макроса.\n\nВыберите существующий или добавьте новый макрос слева."
AdvSceneSwitcher.macroTab.editActionHelp="Этот раздел позволяет определить действия макроса.\n\n\nВыберите существующий или добавьте новый макрос слева."
AdvSceneSwitcher.macroTab.edit="Редактировать макрос"
AdvSceneSwitcher.macroTab.edit.logic="Тип логики:"
AdvSceneSwitcher.macroTab.edit.condition="Тип условия:"
AdvSceneSwitcher.macroTab.edit.action="Тип действия:"
AdvSceneSwitcher.macroTab.add="Добавить новый макрос"
AdvSceneSwitcher.macroTab.name="Имя:"
AdvSceneSwitcher.macroTab.defaultname="Макрос %1"
AdvSceneSwitcher.macroTab.exists="Имя макроса уже существует"
AdvSceneSwitcher.macroTab.copy="Создать копию"
; Macro Logic
AdvSceneSwitcher.logic.none="Игнорировать вход"
AdvSceneSwitcher.logic.and="И"
AdvSceneSwitcher.logic.or="Или"
AdvSceneSwitcher.logic.andNot="И не"
AdvSceneSwitcher.logic.orNot="Или нет"
AdvSceneSwitcher.logic.rootNone="Если"
AdvSceneSwitcher.logic.not="Если нет"
; Macro Conditions
AdvSceneSwitcher.condition.audio="Аудио"
AdvSceneSwitcher.ondition.audio.state.below="Ниже"
AdvSceneSwitcher.ondition.audio.state.above="Выше"
AdvSceneSwitcher.condition.audio.entry="Громкость {{audioSources}} равна {{condition}} {{volume}} в течении {{duration}} секунд"
AdvSceneSwitcher.condition.region="Область экрана"
AdvSceneSwitcher.condition.region.entry="Курсор находится в {{minX}} {{minY}} x {{maxX}} {{maxY}}"
AdvSceneSwitcher.condition.scene="Сцена"
AdvSceneSwitcher.condition.scene.type.current="Текущий"
AdvSceneSwitcher.condition.scene.type.previous="Предыдущий"
AdvSceneSwitcher.condition.scene.entry="{{sceneType}} сцена является {{scenes}} на {{duration}}"
AdvSceneSwitcher.condition.window="Окно"
AdvSceneSwitcher.condition.window.entry.line1="{{windows}} существует и ..."
AdvSceneSwitcher.condition.window.entry.line2="... это {{fullscreen}} полноэкранный {{maximized}} максимизированный {{focused}} сфокусированный"
AdvSceneSwitcher.condition.file="Файл"
AdvSceneSwitcher.condition.file.entry.line1="Содержимое {{fileType}} {{filePath}} {{browseButton}} соответствует:"
AdvSceneSwitcher.condition.file.entry.line2="{{matchText}}"
AdvSceneSwitcher.condition.file.entry.line3="{{useRegex}} {{checkModificationDate}} {{checkFileContent}}"
AdvSceneSwitcher.condition.media="Медиа"
AdvSceneSwitcher.condition.media.entry="{{mediaSources}} состояние {{states}} и {{timeRestrictions}} {{time}}"
AdvSceneSwitcher.condition.video="Видео"
AdvSceneSwitcher.condition.video.condition.match="точно соответствует"
AdvSceneSwitcher.condition.video.condition.differ="не совпадает"
AdvSceneSwitcher.condition.video.condition.hasChanged="изменилось"
AdvSceneSwitcher.condition.video.condition.hasNotChanged="не изменилось"
AdvSceneSwitcher.condition.video.condition.noImage="не имеет вывода"
AdvSceneSwitcher.condition.video.askFileAction="Вы хотите использовать существующий файл или создать скриншот текущего выбранного источника?"
AdvSceneSwitcher.condition.video.askFileAction.file="Использовать существующий файл"
AdvSceneSwitcher.condition.video.askFileAction.screenshot="Создать скриншот"
AdvSceneSwitcher.condition.video.entry="{{videoSources}} {{condition}} {{filePath}} {{browseButton}} для {{duration}}"
AdvSceneSwitcher.condition.stream="Потоковое вещание"
AdvSceneSwitcher.condition.stream.state.start="Поток запущен"
AdvSceneSwitcher.condition.stream.state.stop="Поток остановлен"
AdvSceneSwitcher.condition.stream.entry="{{streamState}} для {{duration}}"
AdvSceneSwitcher.condition.record="Запись"
AdvSceneSwitcher.condition.record.state.start="Запись запущена"
AdvSceneSwitcher.condition.record.state.pause="Запись приостановлена"
AdvSceneSwitcher.condition.record.state.stop="Запись остановлена"
AdvSceneSwitcher.condition.record.entry="{{recordState}} для {{duration}}"
AdvSceneSwitcher.condition.process="Процесс"
AdvSceneSwitcher.condition.process.entry="{{processes}} запущен {{focused}} и сфокусирован"
AdvSceneSwitcher.condition.idle="Простой"
AdvSceneSwitcher.condition.idle.entry="Не было ни клавиатуры, ни мыши в течении{{duration}}"
AdvSceneSwitcher.condition.pluginState="Состояние плагина"
AdvSceneSwitcher.condition.pluginState.state.sceneSwitched="Автоматическая смена сцены была запущена в этом интервале"
AdvSceneSwitcher.condition.pluginState.entry="{{condition}}"
; Macro Actions
AdvSceneSwitcher.action.switchScene="Переключить сцену"
AdvSceneSwitcher.action.scene.entry="Перейти к сцене {{scenes}} используя {{transitions}} с продолжительностью {{duration}} секунд"
AdvSceneSwitcher.action.wait="Подождать"
AdvSceneSwitcher.action.wait.type.fixed="фиксированный"
AdvSceneSwitcher.action.wait.type.random="случайный"
AdvSceneSwitcher.action.wait.entry.fixed="Ожидать {{waitType}} в течении {{duration}}"
AdvSceneSwitcher.action.wait.entry.random="Ожидание {{waitType}} длительностью от {{duration}} до {{duration2}}"
AdvSceneSwitcher.action.audio="Аудио"
AdvSceneSwitcher.action.audio.type.mute="Замьютить"
AdvSceneSwitcher.action.audio.type.unmute="Размьютить"
AdvSceneSwitcher.action.audio.type.sourceVolume="Установить громкость источника"
AdvSceneSwitcher.action.audio.type.masterVolume="Установить главную громкость"
AdvSceneSwitcher.action.audio.entry="{{actions}} {{audioSources}} {{volume}}"
AdvSceneSwitcher.action.recording="Запись"
AdvSceneSwitcher.action.recording.type.stop="Остановить запись"
AdvSceneSwitcher.action.recording.type.start="Начать запись"
AdvSceneSwitcher.action.recording.type.pause="Пауза записи"
AdvSceneSwitcher.action.recording.type.unpause="Снять запись с паузы"
AdvSceneSwitcher.action.recording.pause.hint="Обратите внимание, что в зависимости от настроек записи вы можете не иметь возможности приостановить запись"
AdvSceneSwitcher.action.recording.entry="{{actions}}{{pauseHint}}"
AdvSceneSwitcher.action.replay="Буфер воспроизведения"
AdvSceneSwitcher.action.replay.type.stop="Остановить буфер воспроизведения"
AdvSceneSwitcher.action.replay.type.start="Начать воспроизведение буфера"
AdvSceneSwitcher.action.replay.type.save="Сохранить буфер воспроизведения"
AdvSceneSwitcher.action.replay.entry="{{actions}}"
AdvSceneSwitcher.action.streaming="Потоковое вещание"
AdvSceneSwitcher.action.streaming.type.stop="Остановить потоковое вещание"
AdvSceneSwitcher.action.streaming.type.start="Начать потоковое вещание"
AdvSceneSwitcher.action.streaming.entry="{{actions}}"
AdvSceneSwitcher.action.run="Запустить"
AdvSceneSwitcher.action.run.entry="Запустить {{filePath}} {{browseButton}}"
; Transition Tab
AdvSceneSwitcher.transitionTab.title="Переход"
AdvSceneSwitcher.transitionTab.setTransitionBy="При изменении переходов:"
AdvSceneSwitcher.transitionTab.transitionOverride="Установить переопределение переходов"
AdvSceneSwitcher.transitionTab.adjustActiveTransitionType="Изменить тип активного перехода"
AdvSceneSwitcher.transitionTab.transitionBehaviorSelectionError="Должна быть включена хотя бы одна опция:\n\n - Использовать переопределения переходов\n\n - Изменить тип активного перехода"
AdvSceneSwitcher.transitionTab.transitionForAToB="Использовать переход для автоматического переключения сцены со сцены A на сцену B"
AdvSceneSwitcher.transitionTab.transitionsHelp="<html><head/><body><p>Эти настройки <span style=\"font-style:italic;\">только</span> влияют на переходы, вызванные переключателем сцен - Проверьте <a href=\"https://obsproject.com/forum/resources/transition-table.1174/\"><span style=\" text-decoration: underline; color:#268bd2;\">Transition Table</span></a> если вы хотите настроить его для ручного изменения сцены.<br/>Настройки, определенные здесь, имеют приоритет над настройками перехода, сконфигурированными в других местах переключателя сцен.<br/><br/>Нажмите на символ плюса ниже, чтобы добавить новую запись.</p></body></html>"
AdvSceneSwitcher.transitionTab.defaultTransition="Изменить переход, если сцена активна"
AdvSceneSwitcher.transitionTab.entry="Переход от {{scenes}} к {{scenes2}} с помощью {{transitions}} с длительностью {{duration}}"
AdvSceneSwitcher.transitionTab.defaultTransitionEntry="Когда сцена {{scenes}} активна, изменить переход сцены по умолчанию на {{transitions}}"
AdvSceneSwitcher.transitionTab.defaultTransitionsHelp="Нажмите на символ плюса, чтобы добавить запись."
AdvSceneSwitcher.transitionTab.defaultTransition.delay="Переключить переход {{defTransitionDelay}} после смены сцены."
AdvSceneSwitcher.transitionTab.defaultTransition.delay.help="Задержка используется для того, чтобы избежать отмены переключения сцены, что может произойти, если тип перехода будет изменен, когда переход еще продолжается."
; Pause Scenes Tab
AdvSceneSwitcher.pauseTab.title="Пауза"
AdvSceneSwitcher.pauseTab.pauseOnScene="Приостановить Scene Switcher на сцене"
AdvSceneSwitcher.pauseTab.pauseInFocus1="Приостановить Scene Switcher когда "
AdvSceneSwitcher.pauseTab.pauseInFocus2="находится в фокусе"
AdvSceneSwitcher.pauseTab.pauseTypeScene="сцена активна"
AdvSceneSwitcher.pauseTab.pauseTypeWindow="окно в фокусе"
AdvSceneSwitcher.pauseTab.pauseTargetAll="все"
AdvSceneSwitcher.pauseTab.pauseEntry="Пауза {{pauseTargets}} проверяет, когда {{pauseTypes}} {{scenes}} {{windows}}"
AdvSceneSwitcher.pauseTab.help="На этой вкладке вы можете настроить приостановку отдельных методов переключения, если сцена активна или окно находится в фокусе.\n\nНажмите на выделенный символ плюса, чтобы продолжить."
; Window Title Tab
AdvSceneSwitcher.windowTitleTab.title="Название"
AdvSceneSwitcher.windowTitleTab.regexrDescription="<html><head/><body><p>Введите либо прямые заголовки окон, либо правильное регулярное выражение. Вы можете проверить синтаксис и соответствие регулярных выражений, используя <a href=\"https://regexr.com\"><span style=\" text-decoration: underline; color:#268bd2;\">RegExr</span></a></p></body></html>"
AdvSceneSwitcher.windowTitleTab.stayInFocus1="Игнорировать имя этого окна"
AdvSceneSwitcher.windowTitleTab.stayInFocus2=" "
AdvSceneSwitcher.windowTitleTab.fullscreen="if fullscreen"
AdvSceneSwitcher.windowTitleTab.maximized="if maximized"
AdvSceneSwitcher.windowTitleTab.focused="if focused"
AdvSceneSwitcher.windowTitleTab.entry="{{windows}} {{scenes}} {{transitions}} {{fullscreen}} {{maximized}} {{focused}}"
AdvSceneSwitcher.windowTitleTab.windowsHelp="Переключение сцен на основе заголовков окон запущенных приложений.\nВы можете выбрать следующие дополнительные условия:\nОкно полноэкранное\nОкно максимизированное\nОкно сфокусированное\n\nНажмите на выделенный символ плюса, чтобы продолжить."
AdvSceneSwitcher.windowTitleTab.ignoreWindowsHelp="Если заголовок окна игнорируется, переключатель сцен будет действовать так, как будто ранее выбранное окно все еще в фокусе.\nЭто позволит вам избежать переключения сцен, если вы часто переключаетесь на другое окно, которое не должно вызывать смену сцены.\n\nВыберите окно или введите заголовок окна выше и нажмите на символ плюса ниже, чтобы добавить его в список."
; Executable Tab
AdvSceneSwitcher.executableTab.title="Исполняемый"
AdvSceneSwitcher.executableTab.implemented="Implemented by dasOven"
AdvSceneSwitcher.executableTab.requiresFocus="только если сфокусирован"
AdvSceneSwitcher.executableTab.entry="Когда {{processes}} запущены, переключить на {{scenes}} используя {{transitions}} {{requiresFocus}}"
AdvSceneSwitcher.executableTab.help="Эта вкладка позволит вам автоматически переключаться между сценами, если запущен процесс.\nЭто может быть полезно в ситуациях, когда имя окна может измениться или неизвестно.\n\nНажмите на выделенный символ плюса, чтобы продолжить."
; Screen Region Tab
AdvSceneSwitcher.screenRegionTab.title="Регион"
AdvSceneSwitcher.screenRegionTab.currentPosition="Курсор в данный момент находится в:"
AdvSceneSwitcher.screenRegionTab.showGuideFrames="Показать направляющие кадры"
AdvSceneSwitcher.screenRegionTab.hideGuideFrames="Скрыть направляющие рамки"
AdvSceneSwitcher.screenRegionTab.excludeScenes.None="Нет выбора"
AdvSceneSwitcher.screenRegionTab.entry="Если курсор находится в {{minX}} {{minY}} x {{maxX}} {{maxY}} переключить на {{scenes}} используя {{transitions}} если не указано {{excludeScenes}}"
AdvSceneSwitcher.screenRegionTab.help="Эта вкладка позволит вам автоматически переключать сцены, основываясь на текущем положении курсора мыши.\n\nНажмите на выделенный символ плюса, чтобы продолжить."
; Media Tab
AdvSceneSwitcher.mediaTab.title="Медиа"
AdvSceneSwitcher.mediaTab.implemented="Implemented by Exeldro"
AdvSceneSwitcher.mediaTab.states.none="Нет"
AdvSceneSwitcher.mediaTab.states.playing="Играет"
AdvSceneSwitcher.mediaTab.states.opening="Открытие"
AdvSceneSwitcher.mediaTab.states.buffering="Буферизация"
AdvSceneSwitcher.mediaTab.states.Paused="Приостановлено"
AdvSceneSwitcher.mediaTab.states.stopped="Остановлено"
AdvSceneSwitcher.mediaTab.states.ended="Закончилось"
AdvSceneSwitcher.mediaTab.states.error="Ошибка"
AdvSceneSwitcher.mediaTab.states.playedToEnd="Воспроизведено до конца"
AdvSceneSwitcher.mediaTab.states.any="Любой"
AdvSceneSwitcher.mediaTab.timeRestriction.none="Нет"
AdvSceneSwitcher.mediaTab.timeRestriction.shorter="Время короче"
AdvSceneSwitcher.mediaTab.timeRestriction.longer="Время больше"
AdvSceneSwitcher.mediaTab.timeRestriction.remainShorter="Оставшееся время короче"
AdvSceneSwitcher.mediaTab.timeRestriction.remainLonger="Время, оставшееся дольше"
AdvSceneSwitcher.mediaTab.entry="Когда состояние {{mediaSources}} равно {{states}} и {{timeRestrictions}} {{time}} переключиться на {{scenes}} используя {{transitions}}"
AdvSceneSwitcher.mediaTab.help="Эта вкладка позволит вам переключать сцены на основе состояний медиаисточников.\nНапример, вы можете автоматически переключиться на предыдущую сцену, когда выбранный медиаисточник закончил свое воспроизведение.\n\nНажмите на выделенный символ плюса, чтобы продолжить."
; File Tab
AdvSceneSwitcher.fileTab.title="Файл"
AdvSceneSwitcher.fileTab.readWriteSceneFile="Считать / записать сцену из / в файл"
AdvSceneSwitcher.fileTab.currentSceneOutputFile="Записать имя текущей сцены в этот файл:"
AdvSceneSwitcher.fileTab.switchSceneBaseOnFile="Включить переключение сцен на основе ввода файла"
AdvSceneSwitcher.fileTab.switchSceneNameInputFile="Считать имя сцены, на которую нужно переключиться, из этого файла:"
AdvSceneSwitcher.fileTab.switchSceneBaseOnFileContent="Переключать сцену на основе содержимого файла"
AdvSceneSwitcher.fileTab.remoteFileWarning="Обратите внимание, что при выборе опции remote переключатель сцен будет пытаться получить доступ к удаленному местоположению каждые x мс, как указано на вкладке Общие"
AdvSceneSwitcher.fileTab.remoteFileWarning1="Обратите внимание, что переключатель сцены будет пытаться получить доступ к удаленному местоположению каждые "
AdvSceneSwitcher.fileTab.remoteFileWarning2="ms"
AdvSceneSwitcher.fileTab.libcurlWarning="Не удалось загрузить libcurl! Доступ к удаленным файлам невозможен!"
AdvSceneSwitcher.fileTab.selectWrite="Выберите файл для записи в ..."
AdvSceneSwitcher.fileTab.selectRead="Выберите файл для чтения из ..."
AdvSceneSwitcher.fileTab.textFileType="Текстовые файлы (*.txt)"
AdvSceneSwitcher.fileTab.anyFileType="Любые файлы (*.*)"
AdvSceneSwitcher.fileTab.remote="удаленный файл"
AdvSceneSwitcher.fileTab.local="локальный файл"
AdvSceneSwitcher.fileTab.useRegExp="использовать регулярные выражения (сопоставление шаблонов)"
AdvSceneSwitcher.fileTab.checkfileContentTime="если дата модификации изменена"
AdvSceneSwitcher.fileTab.checkfileContent="если содержимое изменено"
AdvSceneSwitcher.fileTab.entry="Переключиться на {{scenes}} используя {{transitions}} если содержимое {{fileType}} {{filePath}} {{browseButton}} совпадает:"
AdvSceneSwitcher.fileTab.entry2="{{matchText}}"
AdvSceneSwitcher.fileTab.entry3="{{useRegex}} {{checkModificationDate}} {{checkFileContent}}"
AdvSceneSwitcher.fileTab.help="Эта вкладка позволит вам автоматически переключать сцены на основе содержимого удаленных или локальных файлов.\n\nНажмите на выделенный символ плюса, чтобы продолжить."
; Random Tab
AdvSceneSwitcher.randomTab.title="Рандом"
AdvSceneSwitcher.randomTab.randomDisabledWarning="Функциональность отключена - для активации выберите \"Если условие переключения не выполнено, переключиться на любую сцену на вкладке Random\" на вкладке Общие"
AdvSceneSwitcher.randomTab.entry="Если условие переключения не выполнено, переключиться на {{scenes}} используя {{transitions}} для {{delay}}"
AdvSceneSwitcher.randomTab.help="Переключатель сцен будет случайным образом выбирать запись на этой вкладке для переключения на заданное время.\nЗаметьте, что одна и та же запись не будет выбрана дважды подряд.\n\nНажмите на выделенный символ плюса, чтобы продолжить."
; Time Tab
AdvSceneSwitcher.timeTab.title="Время"
AdvSceneSwitcher.timeTab.anyDay="В любой день"
AdvSceneSwitcher.timeTab.mondays="По понедельникам"
AdvSceneSwitcher.timeTab.tuesdays="По вторникам"
AdvSceneSwitcher.timeTab.wednesdays="По средам"
AdvSceneSwitcher.timeTab.thursdays="По четвергам"
AdvSceneSwitcher.timeTab.fridays="По пятницам"
AdvSceneSwitcher.timeTab.saturdays="По субботам"
AdvSceneSwitcher.timeTab.sundays="По воскресеньям"
AdvSceneSwitcher.timeTab.afterstart="После начала потокового вещания/записи"
AdvSceneSwitcher.timeTab.afterstart.tip="Будет использоваться время относительно начала потокового вещания/записи"
AdvSceneSwitcher.timeTab.entry="{{triggers}} в {{time}} переключаются на {{scenes}} используя {{transitions}}"
AdvSceneSwitcher.timeTab.help="Эта вкладка позволит вам автоматически переключаться на другую сцену на основе текущего местного времени.\n\nЗаметьте, что переключатель сцен будет переключаться только в точное время, которое вы указали.\nУбедитесь, что вы настроили параметры приоритета на вкладке Общие по своему вкусу, чтобы выбранная временная точка не была пропущена из-за других методов переключения, имеющих более высокий приоритет.\n\nНажмите на выделенный символ плюса, чтобы продолжить."
; Idle Tab
AdvSceneSwitcher.idleTab.title="Бездействие"
AdvSceneSwitcher.idleTab.enable="Включить обнаружение простоя"
AdvSceneSwitcher.idleTab.idleswitch="После {{duration}} отсутствия ввода с клавиатуры или мыши переключиться на сцену {{scenes}} используя {{transitions}}"
AdvSceneSwitcher.idleTab.dontSwitchIfFocus1="Не переключаться, если"
AdvSceneSwitcher.idleTab.dontSwitchIfFocus2="находится в фокусе"
; Scene Sequence Tab
AdvSceneSwitcher.sceneSequenceTab.title="Последовательность"
AdvSceneSwitcher.sceneSequenceTab.description="Последовательность автоматического переключения сцен может быть отменена либо паузой/остановкой переключателя сцен, либо ручным переключением на другую сцену"
AdvSceneSwitcher.sceneSequenceTab.save="Сохранить последовательность сцен в файл"
AdvSceneSwitcher.sceneSequenceTab.load="Загрузить последовательности сцен из файла"
AdvSceneSwitcher.sceneSequenceTab.saveTitle="Сохранить последовательность сцен в файл ..."
AdvSceneSwitcher.sceneSequenceTab.loadTitle="Выберите файл для чтения последовательности сцен из файла ..."
AdvSceneSwitcher.sceneSequenceTab.loadFail="Advanced Scene Switcher не удалось импортировать настройки!"
AdvSceneSwitcher.sceneSequenceTab.loadSuccess="Настройки Advanced Scene Switcher импортированы успешно!"
AdvSceneSwitcher.sceneSequenceTab.fileType="Текстовые файлы (*.txt)"
AdvSceneSwitcher.sceneSequenceTab.interruptible="прервать"
AdvSceneSwitcher.sceneSequenceTab.interruptibleHint="Другие методы переключения разрешены для прерывания этой последовательности сцен"
AdvSceneSwitcher.sceneSequenceTab.entry="Когда {{startScenes}} активен, переключить на {{scenes}} после {{delay}} используя {{transitions}} {{interruptible}}"
AdvSceneSwitcher.sceneSequenceTab.extendEdit="Расширить последовательность"
AdvSceneSwitcher.sceneSequenceTab.extendEntry="После {{delay}} переключиться на {{scenes}} используя {{transitions}}"
AdvSceneSwitcher.sceneSequenceTab.help="Эта вкладка позволит вам автоматически переключаться на другую сцену, если сцена была активна в течение заданного периода времени.\nНапример, вы можете автоматически переключаться между двумя сценами.\n\nНажмите на выделенный символ плюса, чтобы продолжить."
; Audio Tab
AdvSceneSwitcher.audioTab.title="Аудио"
AdvSceneSwitcher.audioTab.condition.above="выше"
AdvSceneSwitcher.audioTab.condition.below="ниже"
AdvSceneSwitcher.audioTab.ignoreInactiveSource="если источник неактивен"
AdvSceneSwitcher.audioTab.entry="Когда громкость {{audioSources}} равна {{condition}} {{volumeWidget}} в течение {{duration}} секунд переключить на {{scenes}} используя {{transitions}} {{ignoreInactiveSource}}"
AdvSceneSwitcher.audioTab.multiMatchfallbackCondition="Если несколько записей совпадают ..."
AdvSceneSwitcher.audioTab.multiMatchfallback="... в течение {{duration}} секунд переключить на {{scenes}} используя {{transitions}}"
AdvSceneSwitcher.audioTab.help="Эта вкладка позволит вам переключать сцены в зависимости от громкости источников.\nНапример, вы можете автоматически переключиться на другую сцену, если громкость вашего микрофона достигнет определенного порога.\n\nНажмите на выделенный символ плюса, чтобы продолжить."
; Video Tab
AdvSceneSwitcher.videoTab.title="Видео"
AdvSceneSwitcher.videoTab.getScreenshot="Получить снимок экрана для выбранной записи"
AdvSceneSwitcher.videoTab.getScreenshotHelp="Получить скриншот источника видео текущей выбранной записи и автоматически установить его в качестве целевого изображени"
AdvSceneSwitcher.videoTab.condition.match="точно соответствует"
AdvSceneSwitcher.videoTab.condition.match.tooltip="Точное совпадение требует, чтобы целевое и исходное изображение имели одинаковое разрешение.\nДаже каждый пиксель должен совпадать, поэтому не рекомендуется использовать форматы изображений, которые используют сжатие (например, .JPG)!"
AdvSceneSwitcher.videoTab.condition.differ="не совпадает"
AdvSceneSwitcher.videoTab.condition.hasNotChanged="не изменилось"
AdvSceneSwitcher.videoTab.condition.hasChanged="изменилось"
AdvSceneSwitcher.videoTab.ignoreInactiveSource="если источник неактивен"
AdvSceneSwitcher.videoTab.entry="Когда {{videoSources}} {{condition}} {{filePath}} {{browseButton}} для {{duration}} переключиться на {{scenes}} используя {{transitions}} {{ignoreInactiveSource}}"
AdvSceneSwitcher.videoTab.help="<html><head/><body><p>Эта вкладка позволит вам переключать сцены на основе текущего видеовыхода выбранных источников.<br/>Обязательно проверьте <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/> Нажмите на выделенный символ плюса, чтобы продолжить.</p></body></html>"
; Network Tab
AdvSceneSwitcher.networkTab.title="Сеть"
AdvSceneSwitcher.networkTab.warning="Запуск сервера вне локальной сети позволит третьим лицам читать активную сцену."
AdvSceneSwitcher.networkTab.DisabledWarning="ту функциональность, к сожалению, пришлось отключить на macOS из-за несовместимости библиотек при параллельном запуске плагина obs-websocket."
AdvSceneSwitcher.networkTab.server="Запустить сервер (отправляет сообщения о переключении сцены всем подключенным клиентам)"
AdvSceneSwitcher.networkTab.server.port="Порт"
AdvSceneSwitcher.networkTab.server.lockToIPv4="Заблокировать сервер на использование только IPv4"
AdvSceneSwitcher.networkTab.server.restrictSendToAutomatedSwitches="Отправлять сообщения только для автоматических переключателей сцен"
AdvSceneSwitcher.networkTab.startFailed.message="Сервер WebSockets не удалось запустить, возможно потому, что:\n - TCP порт %1 может в настоящее время использоваться в другом месте в этой системе, возможно, другим приложением. Попробуйте установить другой TCP порт в настройках сервера WebSocket, или остановите любое приложение, которое может использовать этот порт.\n - Сообщение об ошибке: %2"
AdvSceneSwitcher.networkTab.server.status.currentStatus="Текущий статус"
AdvSceneSwitcher.networkTab.server.status.notRunning="Не запущен"
AdvSceneSwitcher.networkTab.server.status.starting="Запуск"
AdvSceneSwitcher.networkTab.server.status.running="Работает"
AdvSceneSwitcher.networkTab.server.restart="Перезапустить сервер"
AdvSceneSwitcher.networkTab.client="Запустить клиент (Получает сообщения о переключении сцен)"
AdvSceneSwitcher.networkTab.client.address="Имя хоста или IP-адрес"
AdvSceneSwitcher.networkTab.client.port="Порт"
AdvSceneSwitcher.networkTab.client.status.currentStatus="Текущий статус"
AdvSceneSwitcher.networkTab.client.status.disconnected="Отключено"
AdvSceneSwitcher.networkTab.client.status.connecting="Подключение"
AdvSceneSwitcher.networkTab.client.status.connected="Подключено"
AdvSceneSwitcher.networkTab.client.reconnect="Принудительное переподключение"
; Scene Group Tab
AdvSceneSwitcher.sceneGroupTab.title="Группа сцен"
AdvSceneSwitcher.sceneGroupTab.list="Группы сцен"
AdvSceneSwitcher.sceneGroupTab.edit="Редактировать группы сцен"
AdvSceneSwitcher.sceneGroupTab.edit.name="Имя:"
AdvSceneSwitcher.sceneGroupTab.edit.type="Тип: {{type}}"
AdvSceneSwitcher.sceneGroupTab.type.count="Количество"
AdvSceneSwitcher.sceneGroupTab.type.time="Время"
AdvSceneSwitcher.sceneGroupTab.type.random="Случайно"
AdvSceneSwitcher.sceneGroupTab.edit.count="Переход к следующей сцене в списке после {{count}} совпадений"
AdvSceneSwitcher.sceneGroupTab.edit.time="Переход к следующей сцене в списке по истечении {{time}}"
AdvSceneSwitcher.sceneGroupTab.edit.random="Выбирать следующую сцену в списке случайным образом"
AdvSceneSwitcher.sceneGroupTab.edit.repeat="Начинать с начала, если достигнут конец списка сцен"
AdvSceneSwitcher.sceneGroupTab.edit.addScene="Добавить сцену"
AdvSceneSwitcher.sceneGroupTab.add="Добавить группу сцен"
AdvSceneSwitcher.sceneGroupTab.defaultname="Группа сцен %1"
AdvSceneSwitcher.sceneGroupTab.exists="Группа сцен или название сцены уже существует"
AdvSceneSwitcher.sceneGroupTab.help="Группы сцен могут быть выбраны в качестве цели так же, как и обычные сцены.\n\nКак следует из названия, группа сцен представляет собой набор из нескольких сцен.\nГруппа сцен будет продвигаться по списку назначенных ей сцен в зависимости от настроенных параметров, которые можно найти справа.\n\nВы можете настроить группу сцен на переход к следующей сцене в списке:\nПосле определенного количества раз, когда группа сцен выбрана в качестве цели.\nПо истечении определенного времени.\nИли случайным образом.\n\nНапример, группа сцен, содержащая сцены ...\nScene 1\nScene 2\nScene 3 \n... активирует \"Scene 1\" в первый раз, когда он выбран в качестве цели.\nВо второй раз он активируется \"Scene 2\".\nОставшееся время \"Scene 3\" будет активирован.\n\nНажмите на выделенный символ плюса ниже, чтобы добавить новую группу сцен."
AdvSceneSwitcher.sceneGroupTab.scenes.help="Выберите группу сцен, которую вы хотите изменить слева.\n\nВыберите сцену для добавления в эту группу сцен, выбрав сцену выше и нажав на символ плюса ниже.\n\nСцена может быть добавлена несколько раз в одну и ту же группу сцен."
; Scene Trigger Tab
AdvSceneSwitcher.sceneTriggerTab.title="Триггеры сцены"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerType.none="--выбрать триггер--"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerType.sceneActive="активен"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerType.sceneInactive="не активен"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerType.sceneLeave="переключился с"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.none="--выбрать действие--"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.startRecording="начать запись"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.pauseRecording="приостановить запись"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.unpauseRecording="отменить паузу записи"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.stopRecording="остановить запись"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.stopStreaming="остановить потоковое вещание"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.startStreaming="начать потоковое вещание"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.startReplayBuffer="запустить буфер воспроизведения"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.stopReplayBuffer="остановить буфер воспроизведения"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.muteSource="отключить источник"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.unmuteSource="включить источник"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.startSwitcher="запустить переключатель сцены"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.stopSwitcher="остановить переключатель сцены"
AdvSceneSwitcher.sceneTriggerTab.entry="Когда {{scenes}} {{triggers}} {{actions}} {{audioSources}} после {{duration}}"
AdvSceneSwitcher.sceneTriggerTab.help="Эта вкладка позволяет запускать действия при изменении сцены, например, остановку записи или потоковой передачи."
; Hotkey
AdvSceneSwitcher.hotkey.startSwitcherHotkey="Запустить Advanced Scene Switcher"
AdvSceneSwitcher.hotkey.stopSwitcherHotkey="становить Advanced Scene Switcher"
AdvSceneSwitcher.hotkey.startStopToggleSwitcherHotkey="Переключить старт/стоп для Advanced Scene Switcher"
AdvSceneSwitcher.askBackup="Обнаружена новая версия Advanced Scene Switcher.\nНужно ли создать резервную копию старых настроек?"
AdvSceneSwitcher.close="Закрыть"
AdvSceneSwitcher.browse="Обзор"
AdvSceneSwitcher.selectScene="--выбрать сцену--"
AdvSceneSwitcher.selectPreviousScene="Предыдущая сцена"
AdvSceneSwitcher.currentTransition="Текущий переход"
AdvSceneSwitcher.selectTransition="--выбрать переход--"
AdvSceneSwitcher.selectWindow="--выбрать окно--"
AdvSceneSwitcher.selectAudioSource="--выбрать источник звука--"
AdvSceneSwitcher.selectVideoSource="--sвыбрать источник видео--"
AdvSceneSwitcher.selectMediaSource="--выбрать источник мультимедиа--"
AdvSceneSwitcher.selectProcess="--выбрать процесс--"
AdvSceneSwitcher.enterPath="--ввести путь--"
AdvSceneSwitcher.enterText="--ввести текст--"
AdvSceneSwitcher.invaildEntriesWillNotBeSaved="недействительные записи не будут сохранены"
AdvSceneSwitcher.selectWindowTip="Используйте \"OBS\" для указания окна OBS\nИспользуйте \"Переключение задач\"для указания ALT + TAB"
AdvSceneSwitcher.status.active="Активный"
AdvSceneSwitcher.status.inactive="Неактивен"
AdvSceneSwitcher.unit.milliseconds="миллисекунды"
AdvSceneSwitcher.unit.secends="секунды"
AdvSceneSwitcher.unit.minutes="минуты"
AdvSceneSwitcher.unit.hours="часы"

3
data/res/time.svg Normal file
View File

@@ -0,0 +1,3 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" preserveAspectRatio="xMidYMid meet" viewBox="0 0 640 640" width="640" height="640"><defs><path d="M599.62 325.49C599.62 477.26 476.41 600.47 324.64 600.47C172.88 600.47 49.67 477.26 49.67 325.49C49.67 173.73 172.88 50.52 324.64 50.52C476.41 50.52 599.62 173.73 599.62 325.49Z" id="c3Wyq0kfYS"></path><mask id="maskanTyyngos" x="-20.33" y="-19.48" width="689.95" height="689.95" maskUnits="userSpaceOnUse"><rect x="-20.33" y="-19.48" width="689.95" height="689.95" fill="white"></rect><use xlink:href="#c3Wyq0kfYS" opacity="1" fill="black"></use></mask><path d="M334.97 96.07C339.51 96.07 343.2 99.75 343.2 104.3C343.2 151.24 343.2 283.84 343.2 330.78C343.2 335.32 339.51 339 334.97 339C329.2 339 320.09 339 314.31 339C309.77 339 306.09 335.32 306.09 330.78C306.09 283.84 306.09 151.24 306.09 104.3C306.09 99.75 309.77 96.07 314.31 96.07C320.09 96.07 329.2 96.07 334.97 96.07Z" id="fxMeu932c"></path><path d="M471.96 338.21C471.97 341.31 469.46 343.83 466.37 343.84C434.35 343.93 343.92 344.21 311.91 344.31C308.81 344.32 306.3 341.82 306.29 338.72C306.27 332.42 306.23 319.13 306.21 312.83C306.2 309.73 308.7 307.21 311.8 307.2C343.81 307.11 434.24 306.83 466.25 306.73C469.35 306.72 471.87 309.23 471.88 312.32C471.9 318.62 471.94 331.91 471.96 338.21Z" id="h65nGmP2BL"></path></defs><g><g><g><g mask="url(#maskanTyyngos)"><use xlink:href="#c3Wyq0kfYS" opacity="1" fill-opacity="0" stroke="#d2d2d2" stroke-width="70" stroke-opacity="1"></use></g></g><g><use xlink:href="#fxMeu932c" opacity="1" fill="#d2d2d2" fill-opacity="1"></use></g><g><use xlink:href="#h65nGmP2BL" opacity="1" fill="#d2d2d2" fill-opacity="1"></use></g></g></g></svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

View File

@@ -723,7 +723,7 @@
<property name="title">
<string>AdvSceneSwitcher.macroTab.edit</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_34" stretch="1,20,10">
<layout class="QVBoxLayout" name="verticalLayout_34">
<item>
<layout class="QHBoxLayout" name="horizontalLayout_18" stretch="0,0,0">
<item>
@@ -752,184 +752,204 @@
</layout>
</item>
<item>
<widget class="QScrollArea" name="macroConditions">
<property name="widgetResizable">
<bool>true</bool>
<widget class="QSplitter" name="splitter">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<widget class="QWidget" name="scrollAreaWidgetContents_2">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>767</width>
<height>293</height>
</rect>
<widget class="QScrollArea" name="macroConditions">
<property name="widgetResizable">
<bool>true</bool>
</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 class="QWidget" name="scrollAreaWidgetContents_2">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>767</width>
<height>220</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout_35" stretch="10,1">
<property name="sizeConstraint">
<enum>QLayout::SetMinAndMaxSize</enum>
</property>
<item>
<layout class="QGridLayout" name="gridLayout_27">
<property name="sizeConstraint">
<enum>QLayout::SetMinAndMaxSize</enum>
</property>
<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">
<property name="sizeConstraint">
<enum>QLayout::SetFixedSize</enum>
</property>
</layout>
</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>
</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>
<widget class="QScrollArea" name="macroActions">
<property name="widgetResizable">
<bool>true</bool>
</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 class="QWidget" name="scrollAreaWidgetContents_3">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>767</width>
<height>219</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout_33" stretch="10,1">
<property name="sizeConstraint">
<enum>QLayout::SetMinAndMaxSize</enum>
</property>
<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">
<property name="sizeConstraint">
<enum>QLayout::SetMinAndMaxSize</enum>
</property>
</layout>
</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>
</widget>
</item>
@@ -3724,6 +3744,27 @@
<layout class="QVBoxLayout" name="verticalLayout_31">
<item>
<layout class="QGridLayout" name="gridLayout_24">
<item row="3" column="1">
<widget class="QCheckBox" name="restrictSend">
<property name="text">
<string>AdvSceneSwitcher.networkTab.server.restrictSendToAutomatedSwitches</string>
</property>
</widget>
</item>
<item row="5" column="0">
<widget class="QLabel" name="label_19">
<property name="text">
<string>AdvSceneSwitcher.networkTab.server.status.currentStatus</string>
</property>
</widget>
</item>
<item row="5" column="1">
<widget class="QLabel" name="serverStatus">
<property name="text">
<string>AdvSceneSwitcher.networkTab.server.status.notRunning</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QSpinBox" name="serverPort">
<property name="minimum">
@@ -3751,24 +3792,17 @@
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_19">
<item row="4" column="1">
<widget class="QCheckBox" name="sendPreview">
<property name="text">
<string>AdvSceneSwitcher.networkTab.server.status.currentStatus</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QLabel" name="serverStatus">
<property name="text">
<string>AdvSceneSwitcher.networkTab.server.status.notRunning</string>
<string>AdvSceneSwitcher.networkTab.server.sendPreview</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QCheckBox" name="restrictSend">
<widget class="QCheckBox" name="sendSceneChange">
<property name="text">
<string>AdvSceneSwitcher.networkTab.server.restrictSendToAutomatedSwitches</string>
<string>AdvSceneSwitcher.networkTab.server.sendSceneChange</string>
</property>
</widget>
</item>

View File

@@ -1,13 +1,9 @@
#include <QMainWindow>
#include <QAction>
#include <QtGui/qstandarditemmodel.h>
#include <QPropertyAnimation>
#include <QGraphicsColorizeEffect>
#include <QMessageBox>
#include <QFileDialog>
#include <obs-module.h>
#include <obs-frontend-api.h>
#include <util/util.hpp>
#include "headers/advanced-scene-switcher.hpp"
#include "headers/curl-helper.hpp"
@@ -73,331 +69,36 @@ void AdvSceneSwitcher::loadUI()
}
/******************************************************************************
* UI helpers
* Saving and loading
******************************************************************************/
void AdvSceneSwitcher::addSelectionEntry(QComboBox *sel,
const char *description,
bool selectable, const char *tooltip)
void AskBackup(obs_data_t *obj)
{
sel->addItem(description);
bool backupSettings = DisplayMessage(
obs_module_text("AdvSceneSwitcher.askBackup"), true);
if (strcmp(tooltip, "") != 0) {
sel->setItemData(0, tooltip, Qt::ToolTipRole);
}
QStandardItemModel *model =
qobject_cast<QStandardItemModel *>(sel->model());
QModelIndex firstIndex =
model->index(0, sel->modelColumn(), sel->rootModelIndex());
QStandardItem *firstItem = model->itemFromIndex(firstIndex);
if (!selectable) {
firstItem->setSelectable(false);
firstItem->setEnabled(false);
}
}
void AdvSceneSwitcher::populateSceneSelection(QComboBox *sel, bool addPrevious,
bool addSceneGroup,
bool addSelect,
std::string selectText,
bool selectable)
{
if (addSelect) {
if (selectText.empty()) {
addSelectionEntry(
sel,
obs_module_text("AdvSceneSwitcher.selectScene"),
selectable,
obs_module_text(
"AdvSceneSwitcher.invaildEntriesWillNotBeSaved"));
} else {
addSelectionEntry(sel, selectText.c_str(), selectable);
}
}
BPtr<char *> scenes = obs_frontend_get_scene_names();
char **temp = scenes;
while (*temp) {
const char *name = *temp;
sel->addItem(name);
temp++;
}
if (addPrevious) {
sel->addItem(obs_module_text(
"AdvSceneSwitcher.selectPreviousScene"));
}
if (addSceneGroup) {
for (auto &sg : switcher->sceneGroups) {
sel->addItem(QString::fromStdString(sg.name));
}
}
}
void AdvSceneSwitcher::populateTransitionSelection(QComboBox *sel,
bool addCurrent,
bool addSelect,
bool selectable)
{
if (addSelect) {
addSelectionEntry(
sel,
obs_module_text("AdvSceneSwitcher.selectTransition"),
selectable);
}
if (addCurrent) {
sel->addItem(
obs_module_text("AdvSceneSwitcher.currentTransition"));
}
obs_frontend_source_list *transitions = new obs_frontend_source_list();
obs_frontend_get_transitions(transitions);
for (size_t i = 0; i < transitions->sources.num; i++) {
const char *name =
obs_source_get_name(transitions->sources.array[i]);
sel->addItem(name);
}
obs_frontend_source_list_free(transitions);
}
void AdvSceneSwitcher::populateWindowSelection(QComboBox *sel, bool addSelect)
{
if (addSelect) {
addSelectionEntry(
sel, obs_module_text("AdvSceneSwitcher.selectWindow"));
}
std::vector<std::string> windows;
GetWindowList(windows);
sort(windows.begin(), windows.end());
for (std::string &window : windows) {
sel->addItem(window.c_str());
}
#ifdef WIN32
sel->setItemData(0, obs_module_text("AdvSceneSwitcher.selectWindowTip"),
Qt::ToolTipRole);
#endif
}
void AdvSceneSwitcher::populateAudioSelection(QComboBox *sel, bool addSelect)
{
if (addSelect) {
addSelectionEntry(
sel,
obs_module_text("AdvSceneSwitcher.selectAudioSource"),
false,
obs_module_text(
"AdvSceneSwitcher.invaildEntriesWillNotBeSaved"));
}
auto sourceEnum = [](void *data, obs_source_t *source) -> bool /* -- */
{
std::vector<std::string> *list =
reinterpret_cast<std::vector<std::string> *>(data);
uint32_t flags = obs_source_get_output_flags(source);
if ((flags & OBS_SOURCE_AUDIO) != 0) {
list->push_back(obs_source_get_name(source));
}
return true;
};
std::vector<std::string> audioSources;
obs_enum_sources(sourceEnum, &audioSources);
sort(audioSources.begin(), audioSources.end());
for (std::string &source : audioSources) {
sel->addItem(source.c_str());
}
}
void AdvSceneSwitcher::populateVideoSelection(QComboBox *sel, bool addScenes,
bool addSelect)
{
if (addSelect) {
addSelectionEntry(
sel,
obs_module_text("AdvSceneSwitcher.selectVideoSource"),
false,
obs_module_text(
"AdvSceneSwitcher.invaildEntriesWillNotBeSaved"));
}
auto sourceEnum = [](void *data, obs_source_t *source) -> bool /* -- */
{
std::vector<std::string> *list =
reinterpret_cast<std::vector<std::string> *>(data);
uint32_t flags = obs_source_get_output_flags(source);
std::string test = obs_source_get_name(source);
if ((flags & (OBS_SOURCE_VIDEO | OBS_SOURCE_ASYNC)) != 0) {
list->push_back(obs_source_get_name(source));
}
return true;
};
std::vector<std::string> videoSources;
obs_enum_sources(sourceEnum, &videoSources);
sort(videoSources.begin(), videoSources.end());
for (std::string &source : videoSources) {
sel->addItem(source.c_str());
}
if (addScenes) {
populateSceneSelection(sel, false, false, false);
}
}
void AdvSceneSwitcher::populateMediaSelection(QComboBox *sel, bool addSelect)
{
if (addSelect) {
addSelectionEntry(
sel,
obs_module_text("AdvSceneSwitcher.selectMediaSource"),
false,
obs_module_text(
"AdvSceneSwitcher.invaildEntriesWillNotBeSaved"));
}
auto sourceEnum = [](void *data, obs_source_t *source) -> bool /* -- */
{
std::vector<std::string> *list =
reinterpret_cast<std::vector<std::string> *>(data);
std::string sourceId = obs_source_get_id(source);
if (sourceId.compare("ffmpeg_source") == 0 ||
sourceId.compare("vlc_source") == 0) {
list->push_back(obs_source_get_name(source));
}
return true;
};
std::vector<std::string> mediaSources;
obs_enum_sources(sourceEnum, &mediaSources);
sort(mediaSources.begin(), mediaSources.end());
for (std::string &source : mediaSources) {
sel->addItem(source.c_str());
}
}
void AdvSceneSwitcher::populateProcessSelection(QComboBox *sel, bool addSelect)
{
if (addSelect) {
addSelectionEntry(
sel, obs_module_text("AdvSceneSwitcher.selectProcess"));
}
QStringList processes;
GetProcessList(processes);
processes.sort();
for (QString &process : processes)
sel->addItem(process);
}
void AdvSceneSwitcher::listAddClicked(QListWidget *list,
SwitchWidget *newWidget,
QPushButton *addButton,
QMetaObject::Connection *addHighlight)
{
if (!list || !newWidget) {
blog(LOG_WARNING,
"listAddClicked called without valid list or widget");
if (!backupSettings) {
return;
}
if (addButton && addHighlight) {
addButton->disconnect(*addHighlight);
QString directory = QFileDialog::getSaveFileName(
nullptr,
obs_module_text(
"AdvSceneSwitcher.generalTab.saveOrLoadsettings.importWindowTitle"),
QDir::currentPath(),
obs_module_text(
"AdvSceneSwitcher.generalTab.saveOrLoadsettings.textType"));
if (directory.isEmpty()) {
return;
}
QListWidgetItem *item;
item = new QListWidgetItem(list);
list->addItem(item);
item->setSizeHint(newWidget->minimumSizeHint());
list->setItemWidget(item, newWidget);
list->scrollToItem(item);
}
bool AdvSceneSwitcher::listMoveUp(QListWidget *list)
{
int index = list->currentRow();
if (index == -1 || index == 0) {
return false;
QFile file(directory);
if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
return;
}
QWidget *row = list->itemWidget(list->currentItem());
QListWidgetItem *itemN = list->currentItem()->clone();
list->insertItem(index - 1, itemN);
list->setItemWidget(itemN, row);
list->takeItem(index + 1);
list->setCurrentRow(index - 1);
return true;
obs_data_save_json(obj, file.fileName().toUtf8().constData());
}
bool AdvSceneSwitcher::listMoveDown(QListWidget *list)
{
int index = list->currentRow();
if (index == -1 || index == list->count() - 1) {
return false;
}
QWidget *row = list->itemWidget(list->currentItem());
QListWidgetItem *itemN = list->currentItem()->clone();
list->insertItem(index + 2, itemN);
list->setItemWidget(itemN, row);
list->takeItem(index);
list->setCurrentRow(index + 1);
return true;
}
QMetaObject::Connection AdvSceneSwitcher::PulseWidget(QWidget *widget,
QColor endColor,
QColor startColor,
QString specifier)
{
if (switcher->disableHints) {
return QMetaObject::Connection();
}
widget->setStyleSheet(specifier + "{ \
border-style: outset; \
border-width: 2px; \
border-radius: 10px; \
border-color: rgb(0,0,0,0) \
}");
QGraphicsColorizeEffect *eEffect = new QGraphicsColorizeEffect(widget);
widget->setGraphicsEffect(eEffect);
QPropertyAnimation *paAnimation =
new QPropertyAnimation(eEffect, "color");
paAnimation->setStartValue(startColor);
paAnimation->setEndValue(endColor);
paAnimation->setDuration(1000);
// Play backwards to return to original state on timer end
paAnimation->setDirection(QAbstractAnimation::Backward);
auto con = QWidget::connect(
paAnimation, &QPropertyAnimation::finished, [paAnimation]() {
QTimer::singleShot(1000, [paAnimation] {
paAnimation->start();
});
});
paAnimation->start();
return con;
}
/******************************************************************************
* Saving and loading
******************************************************************************/
static void SaveSceneSwitcher(obs_data_t *save_data, bool saving, void *)
{
if (saving) {
@@ -422,7 +123,7 @@ static void SaveSceneSwitcher(obs_data_t *save_data, bool saving, void *)
obj = obs_data_create();
}
if (switcher->versionChanged(obj, g_GIT_SHA1)) {
AdvSceneSwitcher::AskBackup(obj);
AskBackup(obj);
}
switcher->loadSettings(obj);
@@ -484,6 +185,7 @@ void SwitcherData::Thread()
}
vblog(LOG_INFO, "try to sleep for %ld", duration.count());
setWaitScene();
cv.wait_for(lock, duration);
startTime = std::chrono::high_resolution_clock::now();
@@ -510,6 +212,7 @@ void SwitcherData::Thread()
vblog(LOG_INFO, "sleep for %ld before switching scene",
duration.count());
setWaitScene();
cv.wait_for(lock, duration);
if (stop) {
@@ -616,7 +319,7 @@ bool SwitcherData::checkForMatch(OBSWeakSource &scene,
return match;
}
void switchScene(sceneSwitchInfo sceneSwitch)
void switchScene(const sceneSwitchInfo &sceneSwitch)
{
if (!sceneSwitch.scene && switcher->verbose) {
blog(LOG_INFO, "nothing to switch to");
@@ -640,7 +343,7 @@ void switchScene(sceneSwitchInfo sceneSwitch)
blog(LOG_INFO, "switched scene");
}
if (switcher->networkConfig.ServerEnabled) {
if (switcher->networkConfig.ShouldSendSceneChange()) {
switcher->server.sendMessage(sceneSwitch);
}
}
@@ -648,6 +351,13 @@ void switchScene(sceneSwitchInfo sceneSwitch)
obs_source_release(source);
}
void switchPreviewScene(const OBSWeakSource &ws)
{
auto source = obs_weak_source_get_source(ws);
obs_frontend_set_current_preview_scene(source);
obs_source_release(source);
}
void SwitcherData::Start()
{
if (!(th && th->isRunning())) {
@@ -669,6 +379,13 @@ void SwitcherData::Start()
}
}
void ResetMacroCounters()
{
for (auto &m : switcher->macros) {
m.ResetCount();
}
}
void SwitcherData::Stop()
{
if (th && th->isRunning()) {
@@ -680,12 +397,19 @@ void SwitcherData::Stop()
th = nullptr;
writeToStatusFile("Advanced Scene Switcher stopped");
ResetMacroCounters();
}
server.stop();
client.disconnect();
}
void SwitcherData::setWaitScene()
{
waitScene = obs_frontend_get_current_scene();
obs_source_release(waitScene);
}
bool SwitcherData::sceneChangedDuringWait()
{
obs_source_t *currentSource = obs_frontend_get_current_scene();
@@ -713,11 +437,11 @@ extern "C" void FreeSceneSwitcher()
switcher = nullptr;
}
void handleSceneChange(SwitcherData *s)
void handleSceneChange()
{
// Stop waiting if scene was changed
if (s->sceneChangedDuringWait()) {
s->cv.notify_one();
if (switcher->sceneChangedDuringWait()) {
switcher->cv.notify_one();
}
// Set previous scene
@@ -725,42 +449,52 @@ void handleSceneChange(SwitcherData *s)
obs_weak_source_t *ws = obs_source_get_weak_source(source);
obs_source_release(source);
obs_weak_source_release(ws);
if (source && s->previousSceneHelper != ws) {
s->previousScene = s->previousSceneHelper;
s->previousSceneHelper = ws;
if (source && switcher->previousSceneHelper != ws) {
switcher->previousScene = switcher->previousSceneHelper;
switcher->previousSceneHelper = ws;
}
s->checkTriggers();
s->checkDefaultSceneTransitions();
switcher->checkTriggers();
switcher->checkDefaultSceneTransitions();
if (switcher->networkConfig.ServerEnabled &&
switcher->networkConfig.SendAll) {
if (switcher->networkConfig.ShouldSendFrontendSceneChange()) {
switcher->server.sendMessage({ws, nullptr, 0});
}
}
void setLiveTime(SwitcherData *s)
void setLiveTime()
{
s->liveTime = QDateTime::currentDateTime();
switcher->liveTime = QDateTime::currentDateTime();
}
void resetLiveTime(SwitcherData *s)
void resetLiveTime()
{
s->liveTime = QDateTime();
switcher->liveTime = QDateTime();
}
void checkAutoStartRecording(SwitcherData *s)
void checkAutoStartRecording()
{
if (s->autoStartEvent == AutoStartEvent::RECORDING ||
s->autoStartEvent == AutoStartEvent::RECORINDG_OR_STREAMING)
s->Start();
if (switcher->autoStartEvent == AutoStartEvent::RECORDING ||
switcher->autoStartEvent == AutoStartEvent::RECORINDG_OR_STREAMING)
switcher->Start();
}
void checkAutoStartStreaming(SwitcherData *s)
void checkAutoStartStreaming()
{
if (s->autoStartEvent == AutoStartEvent::STREAMING ||
s->autoStartEvent == AutoStartEvent::RECORINDG_OR_STREAMING)
s->Start();
if (switcher->autoStartEvent == AutoStartEvent::STREAMING ||
switcher->autoStartEvent == AutoStartEvent::RECORINDG_OR_STREAMING)
switcher->Start();
}
void handlePeviewSceneChange()
{
if (switcher->networkConfig.ShouldSendPrviewSceneChange()) {
auto source = obs_frontend_get_current_preview_scene();
auto weak = obs_source_get_weak_source(source);
switcher->server.sendMessage({weak, nullptr, 0}, true);
obs_weak_source_release(weak);
obs_source_release(source);
}
}
// Note to future self:
@@ -768,24 +502,31 @@ void checkAutoStartStreaming(SwitcherData *s)
// frontend functions such as obs_frontend_set_current_scene()
static void OBSEvent(enum obs_frontend_event event, void *switcher)
{
if (!switcher) {
return;
}
switch (event) {
case OBS_FRONTEND_EVENT_EXIT:
FreeSceneSwitcher();
break;
case OBS_FRONTEND_EVENT_SCENE_CHANGED:
handleSceneChange((SwitcherData *)switcher);
handleSceneChange();
break;
case OBS_FRONTEND_EVENT_PREVIEW_SCENE_CHANGED:
handlePeviewSceneChange();
break;
case OBS_FRONTEND_EVENT_RECORDING_STARTED:
setLiveTime((SwitcherData *)switcher);
checkAutoStartRecording((SwitcherData *)switcher);
setLiveTime();
checkAutoStartRecording();
break;
case OBS_FRONTEND_EVENT_STREAMING_STARTED:
setLiveTime((SwitcherData *)switcher);
checkAutoStartStreaming((SwitcherData *)switcher);
setLiveTime();
checkAutoStartStreaming();
break;
case OBS_FRONTEND_EVENT_RECORDING_STOPPED:
case OBS_FRONTEND_EVENT_STREAMING_STOPPED:
resetLiveTime((SwitcherData *)switcher);
resetLiveTime();
break;
default:
break;

View File

@@ -1,4 +1,5 @@
#include "headers/duration-control.hpp"
#include "headers/utility.hpp"
#include "obs-module.h"
#include <sstream>
@@ -99,6 +100,8 @@ DurationSelection::DurationSelection(QWidget *parent, bool showUnitSelection)
SLOT(_UnitChanged(int)));
QHBoxLayout *layout = new QHBoxLayout;
layout->setContentsMargins(0, 0, 0, 0);
layout->setSpacing(11);
layout->addWidget(_duration);
if (showUnitSelection) {
layout->addWidget(_unitSelection);
@@ -137,3 +140,131 @@ void DurationSelection::_UnitChanged(int idx)
emit UnitChanged(unit);
}
void DurationConstraint::Save(obs_data_t *obj, const char *condName,
const char *secondsName, const char *unitName)
{
obs_data_set_int(obj, condName, static_cast<int>(_type));
_dur.Save(obj, secondsName, unitName);
}
void DurationConstraint::Load(obs_data_t *obj, const char *condName,
const char *secondsName, const char *unitName)
{
// For backwards compatability check if duration value exist without
// time constraint condition - if so assume DurationCondition::MORE
if (!obs_data_has_user_value(obj, condName) &&
obs_data_has_user_value(obj, secondsName)) {
obs_data_set_int(obj, condName,
static_cast<int>(DurationCondition::MORE));
}
_type = static_cast<DurationCondition>(obs_data_get_int(obj, condName));
_dur.Load(obj, secondsName, unitName);
}
bool DurationConstraint::DurationReached()
{
switch (_type) {
case DurationCondition::NONE:
return true;
break;
case DurationCondition::MORE:
return _dur.DurationReached();
break;
case DurationCondition::EQUAL:
if (_dur.DurationReached() && !_timeReached) {
_timeReached = true;
return true;
}
break;
case DurationCondition::LESS:
return !_dur.DurationReached();
break;
default:
break;
}
return false;
}
void DurationConstraint::Reset()
{
_timeReached = false;
_dur.Reset();
}
static void populateConditions(QComboBox *list)
{
list->addItem(
obs_module_text("AdvSceneSwitcher.duration.condition.none"));
list->addItem(
obs_module_text("AdvSceneSwitcher.duration.condition.more"));
list->addItem(
obs_module_text("AdvSceneSwitcher.duration.condition.equal"));
list->addItem(
obs_module_text("AdvSceneSwitcher.duration.condition.less"));
}
DurationConstraintEdit::DurationConstraintEdit(QWidget *parent)
{
_condition = new QComboBox(parent);
_duration = new DurationSelection(parent);
_toggle = new QPushButton(parent);
_toggle->setMaximumSize(22, 22);
_toggle->setIcon(
QIcon(QString::fromStdString(getDataFilePath("res/time.svg"))));
populateConditions(_condition);
QWidget::connect(_condition, SIGNAL(currentIndexChanged(int)), this,
SLOT(_ConditionChanged(int)));
QObject::connect(_duration, &DurationSelection::DurationChanged, this,
&DurationConstraintEdit::DurationChanged);
QObject::connect(_duration, &DurationSelection::UnitChanged, this,
&DurationConstraintEdit::UnitChanged);
QWidget::connect(_toggle, SIGNAL(clicked()), this,
SLOT(ToggleClicked()));
QHBoxLayout *layout = new QHBoxLayout;
layout->setContentsMargins(0, 0, 0, 0);
layout->setSpacing(11);
layout->addWidget(_toggle);
layout->addWidget(_condition);
layout->addWidget(_duration);
setLayout(layout);
Collapse(true);
}
void DurationConstraintEdit::SetValue(DurationConstraint &value)
{
_duration->SetDuration(value.GetDuration());
_condition->setCurrentIndex(static_cast<int>(value.GetCondition()));
_duration->setVisible(value.GetCondition() != DurationCondition::NONE);
}
void DurationConstraintEdit::SetUnit(DurationUnit u)
{
_duration->SetUnit(u);
}
void DurationConstraintEdit::SetDuration(const Duration &d)
{
_duration->SetDuration(d);
}
void DurationConstraintEdit::_ConditionChanged(int value)
{
auto cond = static_cast<DurationCondition>(value);
Collapse(cond == DurationCondition::NONE);
emit ConditionChanged(cond);
}
void DurationConstraintEdit::ToggleClicked()
{
Collapse(false);
}
void DurationConstraintEdit::Collapse(bool collapse)
{
_toggle->setVisible(collapse);
_duration->setVisible(!collapse);
_condition->setVisible(!collapse);
}

View File

@@ -1,9 +1,9 @@
#include <QFileDialog>
#include "headers/advanced-scene-switcher.hpp"
#include "headers/utility.hpp"
#include "headers/version.h"
#include <QFileDialog>
QMetaObject::Connection inactivePluse;
void AdvSceneSwitcher::on_close_clicked()
@@ -156,8 +156,11 @@ void AdvSceneSwitcher::SetStopped()
obs_module_text("AdvSceneSwitcher.generalTab.status.start"));
ui->pluginRunningText->setText(
obs_module_text("AdvSceneSwitcher.status.inactive"));
inactivePluse = PulseWidget(ui->pluginRunningText, QColor(Qt::red),
QColor(0, 0, 0, 0), "QLabel ");
if (!switcher->disableHints) {
inactivePluse = PulseWidget(ui->pluginRunningText,
QColor(Qt::red), QColor(0, 0, 0, 0),
"QLabel ");
}
currentStatusActive = false;
}
@@ -207,34 +210,6 @@ void AdvSceneSwitcher::on_uiHintsDisable_stateChanged(int state)
switcher->disableHints = state;
}
void AdvSceneSwitcher::AskBackup(obs_data_t *obj)
{
bool backupSettings = DisplayMessage(
obs_module_text("AdvSceneSwitcher.askBackup"), true);
if (!backupSettings) {
return;
}
QString directory = QFileDialog::getSaveFileName(
nullptr,
obs_module_text(
"AdvSceneSwitcher.generalTab.saveOrLoadsettings.importWindowTitle"),
QDir::currentPath(),
obs_module_text(
"AdvSceneSwitcher.generalTab.saveOrLoadsettings.textType"));
if (directory.isEmpty()) {
return;
}
QFile file(directory);
if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
return;
}
obs_data_save_json(obj, file.fileName().toUtf8().constData());
}
void AdvSceneSwitcher::on_exportSettings_clicked()
{
QString directory = QFileDialog::getSaveFileName(

View File

@@ -5,6 +5,7 @@
#include "ui_advanced-scene-switcher.h"
#endif
#include "switcher-data-structs.hpp"
#include "platform-funcs.hpp"
#define blog(level, msg, ...) blog(level, "[adv-ss] " msg, ##__VA_ARGS__)
#define vblog(level, msg, ...) \
@@ -69,44 +70,10 @@ public:
void setTabOrder();
void restoreWindowGeo();
static void AskBackup(obs_data_t *obj);
static void addSelectionEntry(QComboBox *sel, const char *description,
bool selectable = false,
const char *tooltip = "");
static void populateSceneSelection(QComboBox *sel,
bool addPrevious = false,
bool addSceneGroup = false,
bool addSelect = true,
std::string selectText = "",
bool selectable = false);
static void populateTransitionSelection(QComboBox *sel,
bool addCurrent = true,
bool addSelect = true,
bool selectable = false);
static void populateWindowSelection(QComboBox *sel,
bool addSelect = true);
static void populateAudioSelection(QComboBox *sel,
bool addSelect = true);
static void populateVideoSelection(QComboBox *sel,
bool addScenes = true,
bool addSelect = true);
static void populateMediaSelection(QComboBox *sel,
bool addSelect = true);
static void populateProcessSelection(QComboBox *sel,
bool addSelect = true);
QMetaObject::Connection PulseWidget(QWidget *widget, QColor endColor,
QColor = QColor(0, 0, 0, 0),
QString specifier = "QLabel ");
void listAddClicked(QListWidget *list, SwitchWidget *newWidget,
QPushButton *addButton = nullptr,
QMetaObject::Connection *addHighlight = nullptr);
bool listMoveUp(QListWidget *list);
bool listMoveDown(QListWidget *list);
signals:
void MacroAdded(const QString &name);
void MacroRemoved(const QString &name);
void MacroRenamed(const QString &oldName, const QString newName);
void SceneGroupAdded(const QString &name);
void SceneGroupRemoved(const QString &name);
void SceneGroupRenamed(const QString &oldName, const QString newName);
@@ -137,6 +104,7 @@ public slots:
void on_macroDown_clicked();
void on_macroName_editingFinished();
void on_macros_currentRowChanged(int idx);
void on_macros_itemChanged(QListWidgetItem *);
void on_conditionAdd_clicked();
void on_conditionRemove_clicked();
void on_actionAdd_clicked();
@@ -243,7 +211,9 @@ public slots:
void on_clientSettings_toggled(bool on);
void on_clientHostname_textChanged(const QString &text);
void on_clientPort_valueChanged(int value);
void on_sendSceneChange_stateChanged(int state);
void on_restrictSend_stateChanged(int state);
void on_sendPreview_stateChanged(int state);
void on_clientReconnect_clicked();
void updateClientStatus();
@@ -276,40 +246,17 @@ public slots:
private:
};
/******************************************************************************
* Windowtitle helper
******************************************************************************/
void GetWindowList(std::vector<std::string> &windows);
void GetWindowList(QStringList &windows);
void GetCurrentWindowTitle(std::string &title);
bool isFullscreen(std::string &title);
bool isMaximized(std::string &title);
/******************************************************************************
* Screenregion helper
******************************************************************************/
std::pair<int, int> getCursorPos();
/******************************************************************************
* Idle detection helper
******************************************************************************/
int secondsSinceLastInput();
/******************************************************************************
* Executable helper
******************************************************************************/
void GetProcessList(QStringList &processes);
bool isInFocus(const QString &executable);
/******************************************************************************
* Sceneswitch helper
******************************************************************************/
void setNextTransition(sceneSwitchInfo &ssi, obs_source_t *currentSource,
void setNextTransition(const sceneSwitchInfo &ssi, obs_source_t *currentSource,
transitionData &td);
void overwriteTransitionOverride(sceneSwitchInfo ssi, transitionData &td);
void restoreTransitionOverride(obs_source_t *scene, transitionData td);
void switchScene(sceneSwitchInfo ssi);
void overwriteTransitionOverride(const sceneSwitchInfo &ssi,
transitionData &td);
void restoreTransitionOverride(obs_source_t *scene, const transitionData &td);
void switchScene(const sceneSwitchInfo &ssi);
void switchPreviewScene(const OBSWeakSource &ws);
/******************************************************************************
* Main SwitcherData

View File

@@ -2,6 +2,7 @@
#include <QWidget>
#include <QDoubleSpinBox>
#include <QComboBox>
#include <QPushButton>
#include <chrono>
#include "obs-data.h"
@@ -53,3 +54,57 @@ private:
double _unitMultiplier;
};
enum class DurationCondition {
NONE,
MORE,
EQUAL,
LESS,
};
class DurationConstraint {
public:
void Save(obs_data_t *obj, const char *condName = "time_constraint",
const char *secondsName = "seconds",
const char *unitName = "displayUnit");
void Load(obs_data_t *obj, const char *condName = "time_constraint",
const char *secondsName = "seconds",
const char *unitName = "displayUnit");
void SetCondition(DurationCondition cond) { _type = cond; }
void SetDuration(const Duration &dur) { _dur = dur; }
void SetValue(double value) { _dur.seconds = value; }
void SetUnit(DurationUnit u) { _dur.displayUnit = u; }
DurationCondition GetCondition() { return _type; }
Duration GetDuration() { return _dur; }
bool DurationReached();
void Reset();
private:
DurationCondition _type = DurationCondition::NONE;
Duration _dur;
bool _timeReached = false;
};
class DurationConstraintEdit : public QWidget {
Q_OBJECT
public:
DurationConstraintEdit(QWidget *parent = nullptr);
void SetValue(DurationConstraint &value);
void SetUnit(DurationUnit u);
void SetDuration(const Duration &d);
private slots:
void _ConditionChanged(int value);
void ToggleClicked();
signals:
void DurationChanged(double value);
void UnitChanged(DurationUnit u);
void ConditionChanged(DurationCondition value);
private:
void Collapse(bool collapse);
DurationSelection *_duration;
QComboBox *_condition;
QPushButton *_toggle;
};

View File

@@ -15,7 +15,7 @@ public:
void LogAction();
bool Save(obs_data_t *obj);
bool Load(obs_data_t *obj);
int GetId() { return id; };
std::string GetId() { return id; };
static std::shared_ptr<MacroAction> Create()
{
return std::make_shared<MacroActionAudio>();
@@ -27,7 +27,7 @@ public:
private:
static bool _registered;
static const int id;
static const std::string id;
};
class MacroActionAudioEdit : public QWidget {
@@ -37,6 +37,7 @@ public:
MacroActionAudioEdit(
QWidget *parent,
std::shared_ptr<MacroActionAudio> entryData = nullptr);
void SetWidgetVisibility();
void UpdateEntryData();
static QWidget *Create(QWidget *parent,
std::shared_ptr<MacroAction> action)

View File

@@ -20,14 +20,16 @@ struct MacroActionInfo {
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,
static bool Register(const std::string &id, MacroActionInfo);
static std::shared_ptr<MacroAction> Create(const std::string &id);
static QWidget *CreateWidget(const std::string &id, QWidget *parent,
std::shared_ptr<MacroAction> action);
static auto GetActionTypes() { return _methods; }
static std::string GetActionName(const std::string &id);
static std::string GetIdByName(const QString &name);
private:
static std::map<int, MacroActionInfo> _methods;
static std::map<std::string, MacroActionInfo> _methods;
};
class MacroActionEdit : public QWidget {
@@ -35,13 +37,13 @@ class MacroActionEdit : public QWidget {
public:
MacroActionEdit(QWidget *parent = nullptr,
std::shared_ptr<MacroAction> * = nullptr, int type = 0,
std::shared_ptr<MacroAction> * = nullptr,
const std::string &id = "scene_switch",
bool startCollapsed = false);
void UpdateEntryData(int type);
void Collapse(bool collapsed);
void UpdateEntryData(const std::string &id, bool collapse);
private slots:
void ActionSelectionChanged(int idx);
void ActionSelectionChanged(const QString &text);
protected:
QComboBox *_actionSelection;

View File

@@ -0,0 +1,61 @@
#pragma once
#include <QSpinBox>
#include "macro-action-edit.hpp"
enum class FilterAction {
ENABLE,
DISABLE,
};
class MacroActionFilter : public MacroAction {
public:
bool PerformAction();
void LogAction();
bool Save(obs_data_t *obj);
bool Load(obs_data_t *obj);
std::string GetId() { return id; };
static std::shared_ptr<MacroAction> Create()
{
return std::make_shared<MacroActionFilter>();
}
OBSWeakSource _source;
OBSWeakSource _filter;
FilterAction _action = FilterAction::ENABLE;
private:
static bool _registered;
static const std::string id;
};
class MacroActionFilterEdit : public QWidget {
Q_OBJECT
public:
MacroActionFilterEdit(
QWidget *parent,
std::shared_ptr<MacroActionFilter> entryData = nullptr);
void UpdateEntryData();
static QWidget *Create(QWidget *parent,
std::shared_ptr<MacroAction> action)
{
return new MacroActionFilterEdit(
parent,
std::dynamic_pointer_cast<MacroActionFilter>(action));
}
private slots:
void SourceChanged(const QString &text);
void FilterChanged(const QString &text);
void ActionChanged(int value);
protected:
QComboBox *_sources;
QComboBox *_filters;
QComboBox *_actions;
std::shared_ptr<MacroActionFilter> _entryData;
private:
QHBoxLayout *_mainLayout;
bool _loading = true;
};

View File

@@ -0,0 +1,60 @@
#pragma once
#include <QDoubleSpinBox>
#include "macro-action-edit.hpp"
#include "macro-selection.hpp"
enum class PerformMacroAction {
PAUSE,
UNPAUSE,
RESET_COUNTER,
};
class MacroActionMacro : public MacroRefAction {
public:
bool PerformAction();
void LogAction();
bool Save(obs_data_t *obj);
bool Load(obs_data_t *obj);
std::string GetId() { return id; };
static std::shared_ptr<MacroAction> Create()
{
return std::make_shared<MacroActionMacro>();
}
PerformMacroAction _action = PerformMacroAction::PAUSE;
private:
static bool _registered;
static const std::string id;
};
class MacroActionMacroEdit : public QWidget {
Q_OBJECT
public:
MacroActionMacroEdit(
QWidget *parent,
std::shared_ptr<MacroActionMacro> entryData = nullptr);
void UpdateEntryData();
static QWidget *Create(QWidget *parent,
std::shared_ptr<MacroAction> action)
{
return new MacroActionMacroEdit(
parent,
std::dynamic_pointer_cast<MacroActionMacro>(action));
}
private slots:
void MacroChanged(const QString &text);
void MacroRemove(const QString &name);
void ActionChanged(int value);
protected:
MacroSelection *_macros;
QComboBox *_actions;
std::shared_ptr<MacroActionMacro> _entryData;
private:
QHBoxLayout *_mainLayout;
bool _loading = true;
};

View File

@@ -0,0 +1,62 @@
#pragma once
#include <QSpinBox>
#include "macro-action-edit.hpp"
enum class MediaAction {
PLAY,
PAUSE,
STOP,
RESTART,
NEXT,
PREVIOUS,
};
class MacroActionMedia : public MacroAction {
public:
bool PerformAction();
void LogAction();
bool Save(obs_data_t *obj);
bool Load(obs_data_t *obj);
std::string GetId() { return id; };
static std::shared_ptr<MacroAction> Create()
{
return std::make_shared<MacroActionMedia>();
}
OBSWeakSource _mediaSource;
MediaAction _action = MediaAction::PLAY;
private:
static bool _registered;
static const std::string id;
};
class MacroActionMediaEdit : public QWidget {
Q_OBJECT
public:
MacroActionMediaEdit(
QWidget *parent,
std::shared_ptr<MacroActionMedia> entryData = nullptr);
void UpdateEntryData();
static QWidget *Create(QWidget *parent,
std::shared_ptr<MacroAction> action)
{
return new MacroActionMediaEdit(
parent,
std::dynamic_pointer_cast<MacroActionMedia>(action));
}
private slots:
void SourceChanged(const QString &text);
void ActionChanged(int value);
protected:
QComboBox *_mediaSources;
QComboBox *_actions;
std::shared_ptr<MacroActionMedia> _entryData;
private:
QHBoxLayout *_mainLayout;
bool _loading = true;
};

View File

@@ -0,0 +1,55 @@
#pragma once
#include <QDoubleSpinBox>
#include "macro-action-edit.hpp"
enum class PluginStateAction {
STOP,
};
class MacroActionPluginState : public MacroAction {
public:
bool PerformAction();
void LogAction();
bool Save(obs_data_t *obj);
bool Load(obs_data_t *obj);
std::string GetId() { return id; };
static std::shared_ptr<MacroAction> Create()
{
return std::make_shared<MacroActionPluginState>();
}
PluginStateAction _action = PluginStateAction::STOP;
private:
static bool _registered;
static const std::string id;
};
class MacroActionPluginStateEdit : public QWidget {
Q_OBJECT
public:
MacroActionPluginStateEdit(
QWidget *parent,
std::shared_ptr<MacroActionPluginState> entryData = nullptr);
void UpdateEntryData();
static QWidget *Create(QWidget *parent,
std::shared_ptr<MacroAction> action)
{
return new MacroActionPluginStateEdit(
parent,
std::dynamic_pointer_cast<MacroActionPluginState>(
action));
}
private slots:
void ActionChanged(int value);
protected:
QComboBox *_actions;
std::shared_ptr<MacroActionPluginState> _entryData;
private:
QHBoxLayout *_mainLayout;
bool _loading = true;
};

View File

@@ -16,7 +16,7 @@ public:
void LogAction();
bool Save(obs_data_t *obj);
bool Load(obs_data_t *obj);
int GetId() { return id; };
std::string GetId() { return id; };
static std::shared_ptr<MacroAction> Create()
{
return std::make_shared<MacroActionRecord>();
@@ -26,7 +26,7 @@ public:
private:
static bool _registered;
static const int id;
static const std::string id;
};
class MacroActionRecordEdit : public QWidget {

View File

@@ -15,7 +15,7 @@ public:
void LogAction();
bool Save(obs_data_t *obj);
bool Load(obs_data_t *obj);
int GetId() { return id; };
std::string GetId() { return id; };
static std::shared_ptr<MacroAction> Create()
{
return std::make_shared<MacroActionReplayBuffer>();
@@ -33,7 +33,7 @@ private:
Duration _duration;
static bool _registered;
static const int id;
static const std::string id;
};
class MacroActionReplayBufferEdit : public QWidget {

View File

@@ -10,7 +10,7 @@ public:
void LogAction();
bool Save(obs_data_t *obj);
bool Load(obs_data_t *obj);
int GetId() { return id; };
std::string GetId() { return id; };
static std::shared_ptr<MacroAction> Create()
{
return std::make_shared<MacroActionRun>();
@@ -20,7 +20,7 @@ public:
private:
static bool _registered;
static const int id;
static const std::string id;
};
class MacroActionRunEdit : public QWidget {

View File

@@ -9,7 +9,7 @@ public:
void LogAction();
bool Save(obs_data_t *obj);
bool Load(obs_data_t *obj);
int GetId() { return id; };
std::string GetId() { return id; };
static std::shared_ptr<MacroAction> Create()
{
@@ -21,7 +21,7 @@ private:
const char *getType() { return "MacroActionSwitchScene"; }
static bool _registered;
static const int id;
static const std::string id;
};
class MacroActionSwitchSceneEdit : public SwitchWidget {

View File

@@ -0,0 +1,62 @@
#pragma once
#include <QSpinBox>
#include "macro-action-edit.hpp"
enum class SceneVisibilityAction {
SHOW,
HIDE,
};
class MacroActionSceneVisibility : public MacroAction {
public:
bool PerformAction();
void LogAction();
bool Save(obs_data_t *obj);
bool Load(obs_data_t *obj);
std::string GetId() { return id; };
static std::shared_ptr<MacroAction> Create()
{
return std::make_shared<MacroActionSceneVisibility>();
}
OBSWeakSource _scene;
OBSWeakSource _source;
SceneVisibilityAction _action = SceneVisibilityAction::SHOW;
private:
static bool _registered;
static const std::string id;
};
class MacroActionSceneVisibilityEdit : public QWidget {
Q_OBJECT
public:
MacroActionSceneVisibilityEdit(
QWidget *parent,
std::shared_ptr<MacroActionSceneVisibility> entryData = nullptr);
void UpdateEntryData();
static QWidget *Create(QWidget *parent,
std::shared_ptr<MacroAction> action)
{
return new MacroActionSceneVisibilityEdit(
parent,
std::dynamic_pointer_cast<MacroActionSceneVisibility>(
action));
}
private slots:
void SceneChanged(const QString &text);
void SourceChanged(const QString &text);
void ActionChanged(int value);
protected:
QComboBox *_scenes;
QComboBox *_sources;
QComboBox *_actions;
std::shared_ptr<MacroActionSceneVisibility> _entryData;
private:
QHBoxLayout *_mainLayout;
bool _loading = true;
};

View File

@@ -0,0 +1,70 @@
#pragma once
#include "macro-action-edit.hpp"
#include <QSpinBox>
#include <QLabel>
#include <QPushButton>
#include <QPlainTextEdit>
enum class SourceAction {
ENABLE,
DISABLE,
SETTINGS,
};
class MacroActionSource : public MacroAction {
public:
bool PerformAction();
void LogAction();
bool Save(obs_data_t *obj);
bool Load(obs_data_t *obj);
std::string GetId() { return id; };
static std::shared_ptr<MacroAction> Create()
{
return std::make_shared<MacroActionSource>();
}
OBSWeakSource _source;
std::string _settings = "";
SourceAction _action = SourceAction::ENABLE;
private:
static bool _registered;
static const std::string id;
};
class MacroActionSourceEdit : public QWidget {
Q_OBJECT
public:
MacroActionSourceEdit(
QWidget *parent,
std::shared_ptr<MacroActionSource> entryData = nullptr);
void UpdateEntryData();
static QWidget *Create(QWidget *parent,
std::shared_ptr<MacroAction> action)
{
return new MacroActionSourceEdit(
parent,
std::dynamic_pointer_cast<MacroActionSource>(action));
}
private slots:
void SourceChanged(const QString &text);
void ActionChanged(int value);
void GetSettingsClicked();
void SettingsChanged();
protected:
QComboBox *_sources;
QComboBox *_actions;
QPushButton *_getSettings;
QPlainTextEdit *_settings;
QLabel *_warning;
std::shared_ptr<MacroActionSource> _entryData;
private:
void SetWidgetVisibility(bool);
QHBoxLayout *_mainLayout;
bool _loading = true;
};

View File

@@ -14,7 +14,7 @@ public:
void LogAction();
bool Save(obs_data_t *obj);
bool Load(obs_data_t *obj);
int GetId() { return id; };
std::string GetId() { return id; };
static std::shared_ptr<MacroAction> Create()
{
return std::make_shared<MacroActionStream>();
@@ -28,7 +28,7 @@ private:
Duration _retryCooldown;
static bool _registered;
static const int id;
static const std::string id;
};
class MacroActionStreamEdit : public QWidget {

View File

@@ -13,7 +13,7 @@ public:
bool PerformAction();
bool Save(obs_data_t *obj);
bool Load(obs_data_t *obj);
int GetId() { return id; };
std::string GetId() { return id; };
static std::shared_ptr<MacroAction> Create()
{
return std::make_shared<MacroActionWait>();
@@ -24,7 +24,7 @@ public:
private:
static bool _registered;
static const int id;
static const std::string id;
};
class MacroActionWaitEdit : public QWidget {

View File

@@ -6,8 +6,6 @@
#include <QComboBox>
#include <chrono>
#include "duration-control.hpp"
enum class AudioCondition {
ABOVE,
BELOW,
@@ -19,7 +17,7 @@ public:
bool CheckCondition();
bool Save(obs_data_t *obj);
bool Load(obs_data_t *obj);
int GetId() { return id; };
std::string GetId() { return id; };
static std::shared_ptr<MacroCondition> Create()
{
return std::make_shared<MacroConditionAudio>();
@@ -33,13 +31,12 @@ public:
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;
static const std::string id;
};
class MacroConditionAudioEdit : public QWidget {
@@ -63,13 +60,11 @@ 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;

View File

@@ -0,0 +1,70 @@
#pragma once
#include "macro.hpp"
#include "macro-selection.hpp"
#include <QWidget>
#include <QSpinBox>
#include <QLabel>
#include <QTimer>
enum class CounterCondition {
BELOW,
ABOVE,
EQUAL,
};
class MacroConditionCounter : public MacroRefCondition {
public:
bool CheckCondition();
bool Save(obs_data_t *obj);
bool Load(obs_data_t *obj);
std::string GetId() { return id; };
static std::shared_ptr<MacroCondition> Create()
{
return std::make_shared<MacroConditionCounter>();
}
CounterCondition _condition = CounterCondition::BELOW;
int _count = 0;
private:
static bool _registered;
static const std::string id;
};
class MacroConditionCounterEdit : public QWidget {
Q_OBJECT
public:
MacroConditionCounterEdit(
QWidget *parent,
std::shared_ptr<MacroConditionCounter> cond = nullptr);
void UpdateEntryData();
static QWidget *Create(QWidget *parent,
std::shared_ptr<MacroCondition> cond)
{
return new MacroConditionCounterEdit(
parent,
std::dynamic_pointer_cast<MacroConditionCounter>(cond));
}
private slots:
void MacroChanged(const QString &text);
void MacroRemove(const QString &name);
void CountChanged(int value);
void ConditionChanged(int cond);
void ResetClicked();
void UpdateCount();
protected:
MacroSelection *_macros;
QComboBox *_conditions;
QSpinBox *_count;
QLabel *_currentCount;
QPushButton *_resetCount;
std::unique_ptr<QTimer> _timer;
std::shared_ptr<MacroConditionCounter> _entryData;
private:
void ResetTimer();
bool _loading = true;
};

View File

@@ -15,19 +15,23 @@ struct MacroConditionInfo {
TCreateMethod _createFunc;
TCreateWidgetMethod _createWidgetFunc;
std::string _name;
bool _useDurationConstraint = true;
};
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,
static bool Register(const std::string &, MacroConditionInfo);
static std::shared_ptr<MacroCondition> Create(const std::string &);
static QWidget *CreateWidget(const std::string &id, QWidget *parent,
std::shared_ptr<MacroCondition>);
static auto GetConditionTypes() { return _methods; }
static std::string GetConditionName(const std::string &);
static std::string GetIdByName(const QString &name);
static bool UsesDurationConstraint(const std::string &id);
private:
static std::map<int, MacroConditionInfo> _methods;
static std::map<std::string, MacroConditionInfo> _methods;
};
class MacroConditionEdit : public QWidget {
@@ -36,20 +40,23 @@ class MacroConditionEdit : public QWidget {
public:
MacroConditionEdit(QWidget *parent = nullptr,
std::shared_ptr<MacroCondition> * = nullptr,
int type = 0, bool root = true,
const std::string &id = "scene", bool root = true,
bool startCollapsed = false);
bool IsRootNode();
void UpdateEntryData(int type);
void Collapse(bool collapsed);
void UpdateEntryData(const std::string &id, bool collapse);
private slots:
void LogicSelectionChanged(int idx);
void ConditionSelectionChanged(int idx);
void ConditionSelectionChanged(const QString &text);
void DurationChanged(double seconds);
void DurationConditionChanged(DurationCondition cond);
void DurationUnitChanged(DurationUnit unit);
protected:
QComboBox *_logicSelection;
QComboBox *_conditionSelection;
Section *_section;
DurationConstraintEdit *_dur;
std::shared_ptr<MacroCondition> *_entryData;

View File

@@ -18,7 +18,7 @@ public:
bool CheckCondition();
bool Save(obs_data_t *obj);
bool Load(obs_data_t *obj);
int GetId() { return id; };
std::string GetId() { return id; };
static std::shared_ptr<MacroCondition> Create()
{
return std::make_shared<MacroConditionFile>();
@@ -39,7 +39,7 @@ private:
QDateTime _lastMod;
size_t _lastHash = 0;
static bool _registered;
static const int id;
static const std::string id;
};
class MacroConditionFileEdit : public QWidget {

View File

@@ -1,15 +1,16 @@
#pragma once
#include "macro.hpp"
#include "duration-control.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; };
std::string GetId() { return id; };
static std::shared_ptr<MacroCondition> Create()
{
return std::make_shared<MacroConditionIdle>();
@@ -19,7 +20,7 @@ public:
private:
static bool _registered;
static const int id;
static const std::string id;
};
class MacroConditionIdleEdit : public QWidget {

View File

@@ -0,0 +1,52 @@
#pragma once
#include "macro.hpp"
#include <QWidget>
#include <QComboBox>
#include "duration-control.hpp"
class MacroConditionInterval : public MacroCondition {
public:
bool CheckCondition();
bool Save(obs_data_t *obj);
bool Load(obs_data_t *obj);
std::string GetId() { return id; };
static std::shared_ptr<MacroCondition> Create()
{
return std::make_shared<MacroConditionInterval>();
}
Duration _duration;
private:
static bool _registered;
static const std::string id;
};
class MacroConditionIntervalEdit : public QWidget {
Q_OBJECT
public:
MacroConditionIntervalEdit(
QWidget *parent,
std::shared_ptr<MacroConditionInterval> cond = nullptr);
void UpdateEntryData();
static QWidget *Create(QWidget *parent,
std::shared_ptr<MacroCondition> cond)
{
return new MacroConditionIntervalEdit(
parent,
std::dynamic_pointer_cast<MacroConditionInterval>(
cond));
}
private slots:
void DurationChanged(double seconds);
void DurationUnitChanged(DurationUnit unit);
protected:
DurationSelection *_duration;
std::shared_ptr<MacroConditionInterval> _entryData;
private:
bool _loading = true;
};

View File

@@ -39,7 +39,7 @@ public:
bool CheckCondition();
bool Save(obs_data_t *obj);
bool Load(obs_data_t *obj);
int GetId() { return id; };
std::string GetId() { return id; };
static std::shared_ptr<MacroCondition> Create()
{
return std::make_shared<MacroConditionMedia>();
@@ -65,7 +65,7 @@ private:
bool _playedToEnd = false;
static bool _registered;
static const int id;
static const std::string id;
};
class MacroConditionMediaEdit : public QWidget {

View File

@@ -12,7 +12,7 @@ public:
bool CheckCondition();
bool Save(obs_data_t *obj);
bool Load(obs_data_t *obj);
int GetId() { return id; };
std::string GetId() { return id; };
static std::shared_ptr<MacroCondition> Create()
{
return std::make_shared<MacroConditionPluginState>();
@@ -22,7 +22,7 @@ public:
private:
static bool _registered;
static const int id;
static const std::string id;
};
class MacroConditionPluginStateEdit : public QWidget {

View File

@@ -8,7 +8,7 @@ public:
bool CheckCondition();
bool Save(obs_data_t *obj);
bool Load(obs_data_t *obj);
int GetId() { return id; };
std::string GetId() { return id; };
static std::shared_ptr<MacroCondition> Create()
{
return std::make_shared<MacroConditionProcess>();
@@ -19,7 +19,7 @@ public:
private:
static bool _registered;
static const int id;
static const std::string id;
};
class MacroConditionProcessEdit : public QWidget {

View File

@@ -2,7 +2,6 @@
#include "macro.hpp"
#include <QWidget>
#include <QComboBox>
#include "duration-control.hpp"
enum class RecordState {
STOP,
@@ -15,18 +14,17 @@ public:
bool CheckCondition();
bool Save(obs_data_t *obj);
bool Load(obs_data_t *obj);
int GetId() { return id; };
std::string 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;
static const std::string id;
};
class MacroConditionRecordEdit : public QWidget {
@@ -47,12 +45,9 @@ public:
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:

View File

@@ -8,7 +8,7 @@ public:
bool CheckCondition();
bool Save(obs_data_t *obj);
bool Load(obs_data_t *obj);
int GetId() { return id; };
std::string GetId() { return id; };
static std::shared_ptr<MacroCondition> Create()
{
return std::make_shared<MacroConditionRegion>();
@@ -18,7 +18,7 @@ public:
private:
static bool _registered;
static const int id;
static const std::string id;
};
class MacroConditionRegionEdit : public QWidget {

View File

@@ -2,7 +2,6 @@
#include "macro.hpp"
#include <QWidget>
#include <QComboBox>
#include "duration-control.hpp"
enum class SceneType {
CURRENT,
@@ -14,7 +13,7 @@ public:
bool CheckCondition();
bool Save(obs_data_t *obj);
bool Load(obs_data_t *obj);
int GetId() { return id; };
std::string GetId() { return id; };
static std::shared_ptr<MacroCondition> Create()
{
return std::make_shared<MacroConditionScene>();
@@ -22,11 +21,10 @@ public:
OBSWeakSource _scene;
SceneType _type;
Duration _duration;
private:
static bool _registered;
static const int id;
static const std::string id;
};
class MacroConditionSceneEdit : public QWidget {
@@ -48,13 +46,10 @@ public:
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:

View File

@@ -0,0 +1,68 @@
#pragma once
#include "macro.hpp"
#include <QWidget>
#include <QComboBox>
enum class SourceCondition {
ACTIVE,
SHOWING,
SETTINGS,
};
class MacroConditionSource : public MacroCondition {
public:
bool CheckCondition();
bool Save(obs_data_t *obj);
bool Load(obs_data_t *obj);
std::string GetId() { return id; };
static std::shared_ptr<MacroCondition> Create()
{
return std::make_shared<MacroConditionSource>();
}
OBSWeakSource _source = nullptr;
SourceCondition _condition = SourceCondition::ACTIVE;
std::string _settings = "";
bool _regex = false;
private:
static bool _registered;
static const std::string id;
};
class MacroConditionSourceEdit : public QWidget {
Q_OBJECT
public:
MacroConditionSourceEdit(
QWidget *parent,
std::shared_ptr<MacroConditionSource> cond = nullptr);
void UpdateEntryData();
static QWidget *Create(QWidget *parent,
std::shared_ptr<MacroCondition> cond)
{
return new MacroConditionSourceEdit(
parent,
std::dynamic_pointer_cast<MacroConditionSource>(cond));
}
private slots:
void SourceChanged(const QString &text);
void ConditionChanged(int cond);
void GetSettingsClicked();
void SettingsChanged();
void RegexChanged(int);
protected:
QComboBox *_sources;
QComboBox *_conditions;
QPushButton *_getSettings;
QPlainTextEdit *_settings;
QCheckBox *_regex;
std::shared_ptr<MacroConditionSource> _entryData;
private:
void SetSettingsSelectionVisible(bool visible);
bool _loading = true;
};

View File

@@ -2,7 +2,6 @@
#include "macro.hpp"
#include <QWidget>
#include <QComboBox>
#include "duration-control.hpp"
enum class StreamState {
STOP,
@@ -14,18 +13,17 @@ public:
bool CheckCondition();
bool Save(obs_data_t *obj);
bool Load(obs_data_t *obj);
int GetId() { return id; };
std::string 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;
static const std::string id;
};
class MacroConditionStreamEdit : public QWidget {
@@ -46,12 +44,9 @@ public:
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:

View File

@@ -1,10 +1,10 @@
#pragma once
#include "macro.hpp"
#include "screenshot-helper.hpp"
#include <QWidget>
#include <QComboBox>
#include <chrono>
#include "duration-control.hpp"
#include "screenshot-helper.hpp"
enum class VideoCondition {
MATCH,
@@ -19,7 +19,7 @@ public:
bool CheckCondition();
bool Save(obs_data_t *obj);
bool Load(obs_data_t *obj);
int GetId() { return id; };
std::string GetId() { return id; };
QImage GetMatchImage() { return _matchImage; };
static std::shared_ptr<MacroCondition> Create()
{
@@ -30,7 +30,6 @@ public:
OBSWeakSource _videoSource;
VideoCondition _condition = VideoCondition::MATCH;
Duration _duration;
std::string _file = obs_module_text("AdvSceneSwitcher.enterPath");
private:
@@ -39,7 +38,7 @@ private:
std::unique_ptr<AdvSSScreenshotObj> _screenshotData = nullptr;
QImage _matchImage;
static bool _registered;
static const int id;
static const std::string id;
};
class MacroConditionVideoEdit : public QWidget {
@@ -66,12 +65,9 @@ private slots:
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;

View File

@@ -0,0 +1,51 @@
#pragma once
#include "macro.hpp"
#include <QWidget>
#include <QComboBox>
class MacroConditionVirtDesktop : public MacroCondition {
public:
bool CheckCondition();
bool Save(obs_data_t *obj);
bool Load(obs_data_t *obj);
std::string GetId() { return id; };
static std::shared_ptr<MacroCondition> Create()
{
return std::make_shared<MacroConditionVirtDesktop>();
}
long _desktop = 0;
private:
static bool _registered;
static const std::string id;
};
class MacroConditionVirtDesktopEdit : public QWidget {
Q_OBJECT
public:
MacroConditionVirtDesktopEdit(
QWidget *parent,
std::shared_ptr<MacroConditionVirtDesktop> cond = nullptr);
void UpdateEntryData();
static QWidget *Create(QWidget *parent,
std::shared_ptr<MacroCondition> cond)
{
return new MacroConditionVirtDesktopEdit(
parent,
std::dynamic_pointer_cast<MacroConditionVirtDesktop>(
cond));
}
private slots:
void DesktopChanged(int value);
void UpdateCurrentDesktop();
protected:
QComboBox *_virtDesktops;
QLabel *_currentDesktop;
std::shared_ptr<MacroConditionVirtDesktop> _entryData;
private:
bool _loading = true;
};

View File

@@ -8,7 +8,7 @@ public:
bool CheckCondition();
bool Save(obs_data_t *obj);
bool Load(obs_data_t *obj);
int GetId() { return id; };
std::string GetId() { return id; };
static std::shared_ptr<MacroCondition> Create()
{
return std::make_shared<MacroConditionWindow>();
@@ -27,7 +27,7 @@ public:
private:
static bool _registered;
static const int id;
static const std::string id;
};
class MacroConditionWindowEdit : public QWidget {

View File

@@ -0,0 +1,17 @@
#pragma once
#include <QComboBox>
class Macro;
class MacroSelection : public QComboBox {
Q_OBJECT
public:
MacroSelection(QWidget *parent);
void SetCurrentMacro(Macro *);
private slots:
void MacroAdd(const QString &name);
void MacroRemove(const QString &name);
void MacroRename(const QString &oldName, const QString &newName);
};

View File

@@ -1,10 +1,13 @@
#pragma once
#include "duration-control.hpp"
#include <string>
#include <deque>
#include <memory>
#include <map>
#include <obs.hpp>
#include <obs-module.h>
#include <QString>
constexpr auto macro_func = 10;
constexpr auto default_priority_10 = macro_func;
@@ -38,14 +41,23 @@ 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;
virtual std::string GetId() = 0;
LogicType GetLogicType() { return _logic; }
void SetLogicType(LogicType logic) { _logic = logic; }
static const std::map<LogicType, LogicTypeInfo> logicTypes;
bool DurationReached() { return _duration.DurationReached(); }
void ResetDuration() { _duration.Reset(); }
DurationConstraint GetDurationConstraint() { return _duration; }
void SetDurationConstraint(const DurationConstraint &dur);
void SetDurationCondition(DurationCondition cond);
void SetDurationUnit(DurationUnit u);
void SetDuration(double seconds);
private:
LogicType _logic;
DurationConstraint _duration;
};
class MacroAction {
@@ -53,21 +65,24 @@ 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 std::string GetId() = 0;
virtual void LogAction();
};
class Macro {
public:
Macro(std::string name = "");
Macro(const 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; }
void SetName(const std::string &name);
void SetPaused(bool pause = true) { _paused = pause; }
bool Paused() { return _paused; }
int GetCount() { return _count; };
void ResetCount() { _count = 0; };
std::deque<std::shared_ptr<MacroCondition>> &Conditions()
{
return _conditions;
@@ -76,13 +91,56 @@ public:
bool Save(obs_data_t *obj);
bool Load(obs_data_t *obj);
// Some macros can refer to other macros, which are not yet loaded.
// Use this function to set these references after loading is complete.
void ResolveMacroRef();
// Helper function for plugin state condition regarding scene change
bool SwitchesScene();
private:
void SetupHotkeys();
void ClearHotkeys();
void SetHotkeysDesc();
std::string _name = "";
std::deque<std::shared_ptr<MacroCondition>> _conditions;
std::deque<std::shared_ptr<MacroAction>> _actions;
bool _matched = false;
bool _paused = false;
int _count = 0;
obs_hotkey_id _pauseHotkey = OBS_INVALID_HOTKEY_ID;
obs_hotkey_id _unpauseHotkey = OBS_INVALID_HOTKEY_ID;
};
Macro *GetMacroByName(const char *name);
Macro *GetMacroByQString(const QString &name);
class MacroRef {
public:
MacroRef(){};
MacroRef(std::string name);
void UpdateRef();
void UpdateRef(std::string name);
void UpdateRef(QString name);
void Save(obs_data_t *obj);
void Load(obs_data_t *obj);
Macro *get();
Macro *operator->();
private:
std::string _name = "";
Macro *_ref = nullptr;
};
class MacroRefCondition : public MacroCondition {
public:
void ResolveMacroRef();
MacroRef _macro;
};
class MacroRefAction : public MacroAction {
public:
void ResolveMacroRef();
MacroRef _macro;
};

View File

@@ -0,0 +1,13 @@
#pragma once
void GetWindowList(std::vector<std::string> &windows);
void GetWindowList(QStringList &windows);
void GetCurrentWindowTitle(std::string &title);
bool isFullscreen(const std::string &title);
bool isMaximized(const std::string &title);
std::pair<int, int> getCursorPos();
int secondsSinceLastInput();
void GetProcessList(QStringList &processes);
bool isInFocus(const QString &executable);
bool GetCurrentVirtualDesktop(long &desktop);
bool GetVirtualDesktopCount(long &ndesktops);

View File

@@ -40,9 +40,9 @@ struct SceneGroup {
int lastRandomScene = -1;
inline SceneGroup(){};
inline SceneGroup(std::string name_) : name(name_){};
inline SceneGroup(std::string name_, AdvanceCondition type_,
std::vector<OBSWeakSource> scenes_, int count_,
inline SceneGroup(const std::string &name_) : name(name_){};
inline SceneGroup(const std::string &name_, AdvanceCondition type_,
const std::vector<OBSWeakSource> &scenes_, int count_,
double time_, bool repeat_)
: name(name_),
type(type_),

View File

@@ -14,18 +14,31 @@ public:
explicit Section(const int animationDuration = 300,
QWidget *parent = 0);
void SetContent(QWidget *w);
void SetContent(QWidget *w, bool collapsed = true);
void AddHeaderWidget(QWidget *);
public slots:
void Collapse(bool collapsed);
protected:
bool eventFilter(QObject *obj, QEvent *event) override;
private slots:
void AnimationFinished();
void Collapse(bool collapse);
private:
void SetupAnimations();
void CleanUpPreviousContent();
QGridLayout *_mainLayout;
QHBoxLayout *_headerWidgetLayout;
QToolButton *_toggleButton;
QFrame *_headerLine;
QParallelAnimationGroup *_toggleAnimation = nullptr;
QParallelAnimationGroup *_contentAnimation = nullptr;
QScrollArea *_contentArea = nullptr;
QWidget *_content = nullptr;
int _animationDuration;
std::atomic_bool _transitioning = {false};
std::atomic_bool _collapsed = {false};
int _headerHeight = 0;
int _contentHeight = 0;
};

View File

@@ -33,6 +33,10 @@ public:
std::string GetClientUri();
bool ShouldSendSceneChange();
bool ShouldSendFrontendSceneChange();
bool ShouldSendPrviewSceneChange();
// Server
bool ServerEnabled;
uint64_t ServerPort;
@@ -42,7 +46,9 @@ public:
bool ClientEnabled;
std::string Address;
uint64_t ClientPort;
bool SendAll;
bool SendSceneChange;
bool SendSceneChangeAll;
bool SendPreview;
};
class WSServer : public QObject {
@@ -53,7 +59,7 @@ public:
virtual ~WSServer();
void start(quint16 port, bool lockToIPv4);
void stop();
void sendMessage(sceneSwitchInfo sceneSwitch);
void sendMessage(sceneSwitchInfo sceneSwitch, bool preview = false);
QThreadPool *threadPool() { return &_threadPool; }
private:

View File

@@ -192,6 +192,7 @@ struct SwitcherData {
void Start();
void Stop();
void setWaitScene();
bool sceneChangedDuringWait();
bool prioFuncsValid();
@@ -200,7 +201,7 @@ struct SwitcherData {
void resetTabOrder();
void writeSceneInfoToFile();
void writeToStatusFile(QString status);
void writeToStatusFile(const QString &msg);
bool checkForMatch(OBSWeakSource &scene, OBSWeakSource &transition,
int &linger, bool &setPreviousSceneAsMatch,
@@ -253,7 +254,7 @@ struct SwitcherData {
void saveNetworkSwitches(obs_data_t *obj);
void saveGeneralSettings(obs_data_t *obj);
void saveHotkeys(obs_data_t *obj);
void saveVersion(obs_data_t *obj, std::string currentVersion);
void saveVersion(obs_data_t *obj, const std::string &currentVersion);
void loadSettings(obs_data_t *obj);
void loadMacros(obs_data_t *obj);

View File

@@ -1,118 +1,28 @@
#pragma once
#include <QString>
#include <QTextStream>
#include <QLayout>
#include <QLabel>
#include <QMessageBox>
#include <unordered_map>
#include <QComboBox>
#include <QMetaObject>
#include <QListWidget>
#include <QPushButton>
#include <QColor>
#include <obs.hpp>
#include <obs-module.h>
#include <obs-frontend-api.h>
#include <deque>
#include <unordered_map>
#include "scene-group.hpp"
static inline bool WeakSourceValid(obs_weak_source_t *ws)
{
obs_source_t *source = obs_weak_source_get_source(ws);
if (source) {
obs_source_release(source);
}
return !!source;
}
static inline std::string GetWeakSourceName(obs_weak_source_t *weak_source)
{
std::string name;
obs_source_t *source = obs_weak_source_get_source(weak_source);
if (source) {
name = obs_source_get_name(source);
obs_source_release(source);
}
return name;
}
static inline OBSWeakSource GetWeakSourceByName(const char *name)
{
OBSWeakSource weak;
obs_source_t *source = obs_get_source_by_name(name);
if (source) {
weak = obs_source_get_weak_source(source);
obs_weak_source_release(weak);
obs_source_release(source);
}
return weak;
}
static inline OBSWeakSource GetWeakSourceByQString(const QString &name)
{
return GetWeakSourceByName(name.toUtf8().constData());
}
static inline OBSWeakSource GetWeakTransitionByName(const char *transitionName)
{
OBSWeakSource weak;
obs_source_t *source = nullptr;
if (strcmp(transitionName, "Default") == 0) {
source = obs_frontend_get_current_transition();
weak = obs_source_get_weak_source(source);
obs_source_release(source);
obs_weak_source_release(weak);
return weak;
}
obs_frontend_source_list *transitions = new obs_frontend_source_list();
obs_frontend_get_transitions(transitions);
bool match = false;
for (size_t i = 0; i < transitions->sources.num; i++) {
const char *name =
obs_source_get_name(transitions->sources.array[i]);
if (strcmp(transitionName, name) == 0) {
match = true;
source = transitions->sources.array[i];
break;
}
}
if (match) {
weak = obs_source_get_weak_source(source);
obs_weak_source_release(weak);
}
obs_frontend_source_list_free(transitions);
return weak;
}
static inline OBSWeakSource GetWeakTransitionByQString(const QString &name)
{
return GetWeakTransitionByName(name.toUtf8().constData());
}
static inline std::string
getNextDelim(std::string text,
std::unordered_map<std::string, QWidget *> placeholders)
{
size_t pos = std::string::npos;
std::string res = "";
for (const auto &ph : placeholders) {
size_t newPos = text.find(ph.first);
if (newPos <= pos) {
pos = newPos;
res = ph.first;
}
}
if (pos == std::string::npos) {
return "";
}
return res;
}
bool WeakSourceValid(obs_weak_source_t *ws);
std::string GetWeakSourceName(obs_weak_source_t *weak_source);
OBSWeakSource GetWeakSourceByName(const char *name);
OBSWeakSource GetWeakSourceByQString(const QString &name);
OBSWeakSource GetWeakTransitionByName(const char *transitionName);
OBSWeakSource GetWeakTransitionByQString(const QString &name);
OBSWeakSource GetWeakFilterByName(OBSWeakSource source, const char *name);
OBSWeakSource GetWeakFilterByQString(OBSWeakSource source, const QString &name);
bool compareIgnoringLineEnding(QString &s1, QString &s2);
std::string getSourceSettings(OBSWeakSource ws);
std::string getDataFilePath(const std::string &file);
/**
* Populate layout with labels and widgets based on provided text
@@ -122,95 +32,32 @@ getNextDelim(std::string text,
* @param placeholders Map containing a mapping of placeholder strings to widgets.
* @param addStretch Add addStretch() to layout.
*/
static inline void
placeWidgets(std::string text, QBoxLayout *layout,
std::unordered_map<std::string, QWidget *> placeholders,
bool addStretch = true)
{
std::vector<std::pair<std::string, QWidget *>> labelsWidgetsPairs;
std::string delim = getNextDelim(text, placeholders);
while (delim != "") {
size_t pos = text.find(delim);
if (pos != std::string::npos) {
labelsWidgetsPairs.emplace_back(text.substr(0, pos),
placeholders[delim]);
text.erase(0, pos + delim.length());
}
delim = getNextDelim(text, placeholders);
}
if (text != "") {
labelsWidgetsPairs.emplace_back(text, nullptr);
}
for (auto &lw : labelsWidgetsPairs) {
if (lw.first != "") {
layout->addWidget(new QLabel(lw.first.c_str()));
}
if (lw.second) {
layout->addWidget(lw.second);
}
}
if (addStretch) {
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;
}
void placeWidgets(std::string text, QBoxLayout *layout,
std::unordered_map<std::string, QWidget *> placeholders,
bool addStretch = true);
void clearLayout(QLayout *layout);
QMetaObject::Connection PulseWidget(QWidget *widget, QColor endColor,
QColor = QColor(0, 0, 0, 0),
QString specifier = "QLabel ");
void listAddClicked(QListWidget *list, QWidget *newWidget,
QPushButton *addButton = nullptr,
QMetaObject::Connection *addHighlight = nullptr);
bool listMoveUp(QListWidget *list);
bool listMoveDown(QListWidget *list);
bool DisplayMessage(const QString &msg, bool question = false);
void addSelectionEntry(QComboBox *sel, const char *description,
bool selectable = false, const char *tooltip = "");
void populateTransitionSelection(QComboBox *sel, bool addCurrent = true,
bool addSelect = true,
bool selectable = false);
void populateWindowSelection(QComboBox *sel, bool addSelect = true);
void populateAudioSelection(QComboBox *sel, bool addSelect = true);
void populateVideoSelection(QComboBox *sel, bool addSelect = true);
void populateMediaSelection(QComboBox *sel, bool addSelect = true);
void populateProcessSelection(QComboBox *sel, bool addSelect = true);
void populateSourceSelection(QComboBox *list, bool addSelect = true);
void populateSceneSelection(QComboBox *sel, bool addPrevious = false,
bool addSceneGroup = false,
std::deque<SceneGroup> *sceneGroups = nullptr,
bool addSelect = true, std::string selectText = "",
bool selectable = false);

View File

@@ -253,7 +253,7 @@ std::pair<int, int> getCursorPos()
return pos;
}
bool isMaximized(std::string &title)
bool isMaximized(const std::string &title)
{
if (!ewmhIsSupported())
return false;
@@ -302,7 +302,7 @@ bool isMaximized(std::string &title)
return false;
}
bool isFullscreen(std::string &title)
bool isFullscreen(const std::string &title)
{
if (!ewmhIsSupported())
return false;
@@ -393,3 +393,67 @@ int secondsSinceLastInput()
return idle_time;
}
bool GetCurrentVirtualDesktop(long &desktop)
{
Atom type;
long unsigned nitems;
long unsigned int bytes = 0;
int format = 0;
unsigned char *data;
Window root;
Atom request;
Display *display = disp();
//TODO check for _NET_CURRENT_DESKTOP
if (!ewmhIsSupported()) {
return false;
}
request = XInternAtom(display, "_NET_CURRENT_DESKTOP", False);
root = XDefaultRootWindow(display);
int status = XGetWindowProperty(display, root, request, 0L, 1L, false,
XA_CARDINAL, &type, &format, &nitems,
&bytes, &data);
if (status == Success && nitems > 0) {
desktop = *((long *)data);
} else {
desktop = -1;
}
free(data);
return desktop != -1;
}
bool GetVirtualDesktopCount(long &ndesktops)
{
Atom type;
long unsigned nitems;
long unsigned int bytes = 0;
int format = 0;
unsigned char *data;
Window root;
Atom request;
Display *display = disp();
//TODO check for _NET_NUMBER_OF_DESKTOPS
if (!ewmhIsSupported()) {
return false;
}
request = XInternAtom(display, "_NET_NUMBER_OF_DESKTOPS", False);
root = XDefaultRootWindow(display);
int status = XGetWindowProperty(display, root, request, 0L, 1L, false,
XA_CARDINAL, &type, &format, &nitems,
&bytes, &data);
if (status == Success && nitems > 0) {
ndesktops = *((long *)data);
} else {
ndesktops = 0;
}
free(data);
return ndesktops != 0;
}

View File

@@ -2,7 +2,7 @@
#include "headers/advanced-scene-switcher.hpp"
#include "headers/utility.hpp"
const int MacroActionAudio::id = 2;
const std::string MacroActionAudio::id = "audio";
bool MacroActionAudio::_registered = MacroActionFactory::Register(
MacroActionAudio::id,
@@ -94,7 +94,7 @@ MacroActionAudioEdit::MacroActionAudioEdit(
_volumePercent->setSuffix("%");
populateActionSelection(_actions);
AdvSceneSwitcher::populateAudioSelection(_audioSources);
populateAudioSelection(_audioSources);
QWidget::connect(_actions, SIGNAL(currentIndexChanged(int)), this,
SLOT(ActionChanged(int)));
@@ -130,17 +130,8 @@ bool hasSourceControl(AudioAction action)
return action != AudioAction::MASTER_VOLUME;
}
void MacroActionAudioEdit::UpdateEntryData()
void MacroActionAudioEdit::SetWidgetVisibility()
{
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 {
@@ -154,6 +145,20 @@ void MacroActionAudioEdit::UpdateEntryData()
}
}
void MacroActionAudioEdit::UpdateEntryData()
{
if (!_entryData) {
return;
}
_audioSources->setCurrentText(
GetWeakSourceName(_entryData->_audioSource).c_str());
_actions->setCurrentIndex(static_cast<int>(_entryData->_action));
_volumePercent->setValue(_entryData->_volume);
SetWidgetVisibility();
}
void MacroActionAudioEdit::SourceChanged(const QString &text)
{
if (_loading || !_entryData) {
@@ -172,7 +177,7 @@ void MacroActionAudioEdit::ActionChanged(int value)
std::lock_guard<std::mutex> lock(switcher->m);
_entryData->_action = static_cast<AudioAction>(value);
UpdateEntryData();
SetWidgetVisibility();
}
void MacroActionAudioEdit::VolumeChanged(int value)

View File

@@ -1,11 +1,11 @@
#include "headers/macro-action-edit.hpp"
#include "headers/macro-action-switch-scene.hpp"
#include "headers/macro-action-scene-switch.hpp"
#include "headers/utility.hpp"
#include "headers/advanced-scene-switcher.hpp"
std::map<int, MacroActionInfo> MacroActionFactory::_methods;
std::map<std::string, MacroActionInfo> MacroActionFactory::_methods;
bool MacroActionFactory::Register(int id, MacroActionInfo info)
bool MacroActionFactory::Register(const std::string &id, MacroActionInfo info)
{
if (auto it = _methods.find(id); it == _methods.end()) {
_methods[id] = info;
@@ -14,7 +14,7 @@ bool MacroActionFactory::Register(int id, MacroActionInfo info)
return false;
}
std::shared_ptr<MacroAction> MacroActionFactory::Create(const int id)
std::shared_ptr<MacroAction> MacroActionFactory::Create(const std::string &id)
{
if (auto it = _methods.find(id); it != _methods.end())
return it->second._createFunc();
@@ -22,7 +22,8 @@ std::shared_ptr<MacroAction> MacroActionFactory::Create(const int id)
return nullptr;
}
QWidget *MacroActionFactory::CreateWidget(const int id, QWidget *parent,
QWidget *MacroActionFactory::CreateWidget(const std::string &id,
QWidget *parent,
std::shared_ptr<MacroAction> action)
{
if (auto it = _methods.find(id); it != _methods.end())
@@ -31,6 +32,24 @@ QWidget *MacroActionFactory::CreateWidget(const int id, QWidget *parent,
return nullptr;
}
std::string MacroActionFactory::GetActionName(const std::string &id)
{
if (auto it = _methods.find(id); it != _methods.end()) {
return it->second._name;
}
return "unknown action";
}
std::string MacroActionFactory::GetIdByName(const QString &name)
{
for (auto it : _methods) {
if (name == obs_module_text(it.second._name.c_str())) {
return it.first;
}
}
return "";
}
static inline void populateActionSelection(QComboBox *list)
{
for (auto entry : MacroActionFactory::GetActionTypes()) {
@@ -40,14 +59,15 @@ static inline void populateActionSelection(QComboBox *list)
MacroActionEdit::MacroActionEdit(QWidget *parent,
std::shared_ptr<MacroAction> *entryData,
int type, bool startCollapsed)
const std::string &id, bool startCollapsed)
: QWidget(parent)
{
_actionSelection = new QComboBox();
_section = new Section(300);
QWidget::connect(_actionSelection, SIGNAL(currentIndexChanged(int)),
this, SLOT(ActionSelectionChanged(int)));
QWidget::connect(_actionSelection,
SIGNAL(currentTextChanged(const QString &)), this,
SLOT(ActionSelectionChanged(const QString &)));
populateActionSelection(_actionSelection);
@@ -58,38 +78,34 @@ MacroActionEdit::MacroActionEdit(QWidget *parent,
setLayout(mainLayout);
_entryData = entryData;
UpdateEntryData(type);
UpdateEntryData(id, startCollapsed);
_loading = false;
_section->Collapse(startCollapsed);
}
void MacroActionEdit::ActionSelectionChanged(int idx)
void MacroActionEdit::ActionSelectionChanged(const QString &text)
{
if (_loading || !_entryData) {
return;
}
std::string id = MacroActionFactory::GetIdByName(text);
std::lock_guard<std::mutex> lock(switcher->m);
_entryData->reset();
*_entryData = MacroActionFactory::Create(idx);
*_entryData = MacroActionFactory::Create(id);
auto widget =
MacroActionFactory::CreateWidget(idx, window(), *_entryData);
_section->SetContent(widget);
_section->Collapse(false);
MacroActionFactory::CreateWidget(id, window(), *_entryData);
_section->SetContent(widget, false);
}
void MacroActionEdit::UpdateEntryData(int type)
void MacroActionEdit::UpdateEntryData(const std::string &id, bool collapse)
{
_actionSelection->setCurrentIndex(type);
_actionSelection->setCurrentText(
obs_module_text(MacroActionFactory::GetActionName(id).c_str()));
auto widget =
MacroActionFactory::CreateWidget(type, window(), *_entryData);
_section->SetContent(widget);
}
void MacroActionEdit::Collapse(bool collapsed)
{
_section->Collapse(collapsed);
MacroActionFactory::CreateWidget(id, window(), *_entryData);
_section->SetContent(widget, collapse);
}
void AdvSceneSwitcher::on_actionAdd_clicked()
@@ -98,12 +114,16 @@ void AdvSceneSwitcher::on_actionAdd_clicked()
if (!macro) {
return;
}
MacroActionSwitchScene temp;
std::string id = temp.GetId();
std::lock_guard<std::mutex> lock(switcher->m);
macro->Actions().emplace_back(MacroActionFactory::Create(0));
auto newEntry = new MacroActionEdit(this, &macro->Actions().back(), 0);
macro->Actions().emplace_back(MacroActionFactory::Create(id));
auto newEntry =
new MacroActionEdit(this, &macro->Actions().back(), id, false);
ui->macroEditActionLayout->addWidget(newEntry);
ui->macroEditActionHelp->setVisible(false);
newEntry->Collapse(false);
}
void AdvSceneSwitcher::on_actionRemove_clicked()

203
src/macro-action-filter.cpp Normal file
View File

@@ -0,0 +1,203 @@
#include "headers/macro-action-filter.hpp"
#include "headers/advanced-scene-switcher.hpp"
#include "headers/utility.hpp"
const std::string MacroActionFilter::id = "filter";
bool MacroActionFilter::_registered = MacroActionFactory::Register(
MacroActionFilter::id,
{MacroActionFilter::Create, MacroActionFilterEdit::Create,
"AdvSceneSwitcher.action.filter"});
const static std::map<FilterAction, std::string> actionTypes = {
{FilterAction::ENABLE, "AdvSceneSwitcher.action.filter.type.enable"},
{FilterAction::DISABLE, "AdvSceneSwitcher.action.filter.type.disable"},
};
bool MacroActionFilter::PerformAction()
{
auto s = obs_weak_source_get_source(_filter);
switch (_action) {
case FilterAction::ENABLE:
obs_source_set_enabled(s, true);
break;
case FilterAction::DISABLE:
obs_source_set_enabled(s, false);
break;
default:
break;
}
obs_source_release(s);
return true;
}
void MacroActionFilter::LogAction()
{
auto it = actionTypes.find(_action);
if (it != actionTypes.end()) {
vblog(LOG_INFO,
"performed action \"%s\" for filter \"%s\" on source \"%s\"",
it->second.c_str(), GetWeakSourceName(_filter).c_str(),
GetWeakSourceName(_source).c_str());
} else {
blog(LOG_WARNING, "ignored unknown filter action %d",
static_cast<int>(_action));
}
}
bool MacroActionFilter::Save(obs_data_t *obj)
{
MacroAction::Save(obj);
obs_data_set_string(obj, "source", GetWeakSourceName(_source).c_str());
obs_data_set_string(obj, "filter", GetWeakSourceName(_filter).c_str());
obs_data_set_int(obj, "action", static_cast<int>(_action));
return true;
}
bool MacroActionFilter::Load(obs_data_t *obj)
{
MacroAction::Load(obj);
const char *sourceName = obs_data_get_string(obj, "source");
_source = GetWeakSourceByName(sourceName);
const char *filterName = obs_data_get_string(obj, "filter");
_filter = GetWeakFilterByQString(_source, filterName);
_action = static_cast<FilterAction>(obs_data_get_int(obj, "action"));
return true;
}
static inline void populateActionSelection(QComboBox *list)
{
for (auto entry : actionTypes) {
list->addItem(obs_module_text(entry.second.c_str()));
}
}
static inline void populateFilters(QComboBox *list,
OBSWeakSource weakSource = nullptr)
{
auto enumFilters = [](obs_source_t *, obs_source_t *filter, void *ptr) {
QComboBox *list = reinterpret_cast<QComboBox *>(ptr);
auto name = obs_source_get_name(filter);
list->addItem(name);
};
auto s = obs_weak_source_get_source(weakSource);
obs_source_enum_filters(s, enumFilters, list);
list->model()->sort(0);
addSelectionEntry(list,
obs_module_text("AdvSceneSwitcher.selectFilter"));
obs_source_release(s);
list->setCurrentIndex(0);
}
static inline void hasFilterEnum(obs_source_t *, obs_source_t *filter,
void *ptr)
{
if (!filter) {
return;
}
bool *hasFilter = reinterpret_cast<bool *>(ptr);
*hasFilter = true;
}
static inline void populateSourcesWithFilter(QComboBox *list)
{
auto enumSourcesWithFilters = [](void *param, obs_source_t *source) {
if (!source) {
return true;
}
QComboBox *list = reinterpret_cast<QComboBox *>(param);
bool hasFilter = false;
obs_source_enum_filters(source, hasFilterEnum, &hasFilter);
if (hasFilter) {
list->addItem(obs_source_get_name(source));
}
return true;
};
obs_enum_sources(enumSourcesWithFilters, list);
obs_enum_scenes(enumSourcesWithFilters, list);
list->model()->sort(0);
addSelectionEntry(list,
obs_module_text("AdvSceneSwitcher.selectSource"));
list->setCurrentIndex(0);
}
MacroActionFilterEdit::MacroActionFilterEdit(
QWidget *parent, std::shared_ptr<MacroActionFilter> entryData)
: QWidget(parent)
{
_sources = new QComboBox();
_filters = new QComboBox();
_actions = new QComboBox();
populateActionSelection(_actions);
populateSourcesWithFilter(_sources);
QWidget::connect(_actions, SIGNAL(currentIndexChanged(int)), this,
SLOT(ActionChanged(int)));
QWidget::connect(_sources, SIGNAL(currentTextChanged(const QString &)),
this, SLOT(SourceChanged(const QString &)));
QWidget::connect(_filters, SIGNAL(currentTextChanged(const QString &)),
this, SLOT(FilterChanged(const QString &)));
QHBoxLayout *mainLayout = new QHBoxLayout;
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
{"{{sources}}", _sources},
{"{{filters}}", _filters},
{"{{actions}}", _actions},
};
placeWidgets(obs_module_text("AdvSceneSwitcher.action.filter.entry"),
mainLayout, widgetPlaceholders);
setLayout(mainLayout);
_entryData = entryData;
UpdateEntryData();
_loading = false;
}
void MacroActionFilterEdit::UpdateEntryData()
{
if (!_entryData) {
return;
}
_actions->setCurrentIndex(static_cast<int>(_entryData->_action));
_sources->setCurrentText(
GetWeakSourceName(_entryData->_source).c_str());
populateFilters(_filters, _entryData->_source);
_filters->setCurrentText(
GetWeakSourceName(_entryData->_filter).c_str());
}
void MacroActionFilterEdit::SourceChanged(const QString &text)
{
if (_loading || !_entryData) {
return;
}
{
std::lock_guard<std::mutex> lock(switcher->m);
_entryData->_source = GetWeakSourceByQString(text);
}
_filters->clear();
populateFilters(_filters, _entryData->_source);
}
void MacroActionFilterEdit::FilterChanged(const QString &text)
{
if (_loading || !_entryData) {
return;
}
std::lock_guard<std::mutex> lock(switcher->m);
_entryData->_filter = GetWeakFilterByQString(_entryData->_source, text);
}
void MacroActionFilterEdit::ActionChanged(int value)
{
if (_loading || !_entryData) {
return;
}
std::lock_guard<std::mutex> lock(switcher->m);
_entryData->_action = static_cast<FilterAction>(value);
}

152
src/macro-action-macro.cpp Normal file
View File

@@ -0,0 +1,152 @@
#include "headers/macro-action-macro.hpp"
#include "headers/advanced-scene-switcher.hpp"
#include "headers/utility.hpp"
const std::string MacroActionMacro::id = "macro";
bool MacroActionMacro::_registered = MacroActionFactory::Register(
MacroActionMacro::id,
{MacroActionMacro::Create, MacroActionMacroEdit::Create,
"AdvSceneSwitcher.action.macro"});
const static std::map<PerformMacroAction, std::string> actionTypes = {
{PerformMacroAction::PAUSE, "AdvSceneSwitcher.action.macro.type.pause"},
{PerformMacroAction::UNPAUSE,
"AdvSceneSwitcher.action.macro.type.unpause"},
{PerformMacroAction::RESET_COUNTER,
"AdvSceneSwitcher.action.macro.type.resetCounter"},
};
bool MacroActionMacro::PerformAction()
{
if (!_macro.get()) {
return true;
}
switch (_action) {
case PerformMacroAction::PAUSE:
_macro->SetPaused();
break;
case PerformMacroAction::UNPAUSE:
_macro->SetPaused(false);
break;
case PerformMacroAction::RESET_COUNTER:
_macro->ResetCount();
break;
default:
break;
}
return true;
}
void MacroActionMacro::LogAction()
{
if (!_macro.get()) {
return;
}
switch (_action) {
case PerformMacroAction::PAUSE:
vblog(LOG_INFO, "paused \"%s\"", _macro->Name().c_str());
break;
case PerformMacroAction::UNPAUSE:
vblog(LOG_INFO, "unpaused \"%s\"", _macro->Name().c_str());
break;
case PerformMacroAction::RESET_COUNTER:
vblog(LOG_INFO, "reset counter for \"%s\"",
_macro->Name().c_str());
break;
default:
break;
}
}
bool MacroActionMacro::Save(obs_data_t *obj)
{
MacroAction::Save(obj);
_macro.Save(obj);
obs_data_set_int(obj, "action", static_cast<int>(_action));
return true;
}
bool MacroActionMacro::Load(obs_data_t *obj)
{
MacroAction::Load(obj);
_macro.Load(obj);
_action = static_cast<PerformMacroAction>(
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()));
}
}
MacroActionMacroEdit::MacroActionMacroEdit(
QWidget *parent, std::shared_ptr<MacroActionMacro> entryData)
: QWidget(parent)
{
_macros = new MacroSelection(parent);
_actions = new QComboBox();
populateActionSelection(_actions);
QWidget::connect(_macros, SIGNAL(currentTextChanged(const QString &)),
this, SLOT(MacroChanged(const QString &)));
QWidget::connect(parent, SIGNAL(MacroRemoved(const QString &)), this,
SLOT(MacroRemove(const QString &)));
QWidget::connect(_actions, SIGNAL(currentIndexChanged(int)), this,
SLOT(ActionChanged(int)));
QHBoxLayout *mainLayout = new QHBoxLayout;
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
{"{{actions}}", _actions},
{"{{macros}}", _macros},
};
placeWidgets(obs_module_text("AdvSceneSwitcher.action.macro.entry"),
mainLayout, widgetPlaceholders);
setLayout(mainLayout);
_entryData = entryData;
UpdateEntryData();
_loading = false;
}
void MacroActionMacroEdit::UpdateEntryData()
{
if (!_entryData) {
return;
}
_actions->setCurrentIndex(static_cast<int>(_entryData->_action));
_macros->SetCurrentMacro(_entryData->_macro.get());
}
void MacroActionMacroEdit::MacroChanged(const QString &text)
{
if (_loading || !_entryData) {
return;
}
std::lock_guard<std::mutex> lock(switcher->m);
_entryData->_macro.UpdateRef(text);
}
void MacroActionMacroEdit::ActionChanged(int value)
{
if (_loading || !_entryData) {
return;
}
std::lock_guard<std::mutex> lock(switcher->m);
_entryData->_action = static_cast<PerformMacroAction>(value);
}
void MacroActionMacroEdit::MacroRemove(const QString &name)
{
UNUSED_PARAMETER(name);
if (_entryData) {
_entryData->_macro.UpdateRef();
}
}

153
src/macro-action-media.cpp Normal file
View File

@@ -0,0 +1,153 @@
#include "headers/macro-action-media.hpp"
#include "headers/advanced-scene-switcher.hpp"
#include "headers/utility.hpp"
const std::string MacroActionMedia::id = "media";
bool MacroActionMedia::_registered = MacroActionFactory::Register(
MacroActionMedia::id,
{MacroActionMedia::Create, MacroActionMediaEdit::Create,
"AdvSceneSwitcher.action.media"});
const static std::map<MediaAction, std::string> actionTypes = {
{MediaAction::PLAY, "AdvSceneSwitcher.action.media.type.play"},
{MediaAction::PAUSE, "AdvSceneSwitcher.action.media.type.pause"},
{MediaAction::STOP, "AdvSceneSwitcher.action.media.type.stop"},
{MediaAction::RESTART, "AdvSceneSwitcher.action.media.type.restart"},
{MediaAction::NEXT, "AdvSceneSwitcher.action.media.type.next"},
{MediaAction::PREVIOUS, "AdvSceneSwitcher.action.media.type.previous"},
};
bool MacroActionMedia::PerformAction()
{
auto source = obs_weak_source_get_source(_mediaSource);
obs_media_state state = obs_source_media_get_state(source);
switch (_action) {
case MediaAction::PLAY:
if (state == OBS_MEDIA_STATE_STOPPED ||
state == OBS_MEDIA_STATE_ENDED) {
obs_source_media_restart(source);
} else {
obs_source_media_play_pause(source, false);
}
break;
case MediaAction::PAUSE:
obs_source_media_play_pause(source, true);
break;
case MediaAction::STOP:
obs_source_media_stop(source);
break;
case MediaAction::RESTART:
obs_source_media_restart(source);
break;
case MediaAction::NEXT:
obs_source_media_next(source);
break;
case MediaAction::PREVIOUS:
obs_source_media_previous(source);
break;
default:
break;
}
obs_source_release(source);
return true;
}
void MacroActionMedia::LogAction()
{
auto it = actionTypes.find(_action);
if (it != actionTypes.end()) {
vblog(LOG_INFO, "performed action \"%s\" for source \"%s\"",
it->second.c_str(),
GetWeakSourceName(_mediaSource).c_str());
} else {
blog(LOG_WARNING, "ignored unknown media action %d",
static_cast<int>(_action));
}
}
bool MacroActionMedia::Save(obs_data_t *obj)
{
MacroAction::Save(obj);
obs_data_set_string(obj, "mediaSource",
GetWeakSourceName(_mediaSource).c_str());
obs_data_set_int(obj, "action", static_cast<int>(_action));
return true;
}
bool MacroActionMedia::Load(obs_data_t *obj)
{
MacroAction::Load(obj);
const char *MediaSourceName = obs_data_get_string(obj, "mediaSource");
_mediaSource = GetWeakSourceByName(MediaSourceName);
_action = static_cast<MediaAction>(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()));
}
}
MacroActionMediaEdit::MacroActionMediaEdit(
QWidget *parent, std::shared_ptr<MacroActionMedia> entryData)
: QWidget(parent)
{
_mediaSources = new QComboBox();
_actions = new QComboBox();
populateActionSelection(_actions);
populateMediaSelection(_mediaSources);
QWidget::connect(_actions, SIGNAL(currentIndexChanged(int)), this,
SLOT(ActionChanged(int)));
QWidget::connect(_mediaSources,
SIGNAL(currentTextChanged(const QString &)), this,
SLOT(SourceChanged(const QString &)));
QHBoxLayout *mainLayout = new QHBoxLayout;
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
{"{{mediaSources}}", _mediaSources},
{"{{actions}}", _actions},
};
placeWidgets(obs_module_text("AdvSceneSwitcher.action.media.entry"),
mainLayout, widgetPlaceholders);
setLayout(mainLayout);
_entryData = entryData;
UpdateEntryData();
_loading = false;
}
void MacroActionMediaEdit::UpdateEntryData()
{
if (!_entryData) {
return;
}
_mediaSources->setCurrentText(
GetWeakSourceName(_entryData->_mediaSource).c_str());
_actions->setCurrentIndex(static_cast<int>(_entryData->_action));
}
void MacroActionMediaEdit::SourceChanged(const QString &text)
{
if (_loading || !_entryData) {
return;
}
std::lock_guard<std::mutex> lock(switcher->m);
_entryData->_mediaSource = GetWeakSourceByQString(text);
}
void MacroActionMediaEdit::ActionChanged(int value)
{
if (_loading || !_entryData) {
return;
}
std::lock_guard<std::mutex> lock(switcher->m);
_entryData->_action = static_cast<MediaAction>(value);
}

View File

@@ -0,0 +1,114 @@
#include "headers/macro-action-plugin-state.hpp"
#include "headers/advanced-scene-switcher.hpp"
#include "headers/utility.hpp"
#include <thread>
const std::string MacroActionPluginState::id = "plugin_state";
bool MacroActionPluginState::_registered = MacroActionFactory::Register(
MacroActionPluginState::id,
{MacroActionPluginState::Create, MacroActionPluginStateEdit::Create,
"AdvSceneSwitcher.action.PluginState"});
const static std::map<PluginStateAction, std::string> actionTypes = {
{PluginStateAction::STOP,
"AdvSceneSwitcher.action.pluginState.type.stop"},
};
void stopPlugin()
{
std::thread t([]() { switcher->Stop(); });
t.detach();
}
bool MacroActionPluginState::PerformAction()
{
switch (_action) {
case PluginStateAction::STOP:
stopPlugin();
break;
default:
break;
}
return true;
}
void MacroActionPluginState::LogAction()
{
auto it = actionTypes.find(_action);
switch (_action) {
case PluginStateAction::STOP:
blog(LOG_INFO, "stop() called by macro", it->second.c_str());
break;
default:
blog(LOG_WARNING, "ignored unknown pluginState action %d",
static_cast<int>(_action));
break;
}
}
bool MacroActionPluginState::Save(obs_data_t *obj)
{
MacroAction::Save(obj);
obs_data_set_int(obj, "action", static_cast<int>(_action));
return true;
}
bool MacroActionPluginState::Load(obs_data_t *obj)
{
MacroAction::Load(obj);
_action =
static_cast<PluginStateAction>(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()));
}
}
MacroActionPluginStateEdit::MacroActionPluginStateEdit(
QWidget *parent, std::shared_ptr<MacroActionPluginState> 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.pluginState.entry"),
mainLayout, widgetPlaceholders);
setLayout(mainLayout);
_entryData = entryData;
UpdateEntryData();
_loading = false;
}
void MacroActionPluginStateEdit::UpdateEntryData()
{
if (!_entryData) {
return;
}
_actions->setCurrentIndex(static_cast<int>(_entryData->_action));
}
void MacroActionPluginStateEdit::ActionChanged(int value)
{
if (_loading || !_entryData) {
return;
}
std::lock_guard<std::mutex> lock(switcher->m);
_entryData->_action = static_cast<PluginStateAction>(value);
}

View File

@@ -2,7 +2,7 @@
#include "headers/advanced-scene-switcher.hpp"
#include "headers/utility.hpp"
const int MacroActionRecord::id = 4;
const std::string MacroActionRecord::id = "recording";
bool MacroActionRecord::_registered = MacroActionFactory::Register(
MacroActionRecord::id,

View File

@@ -2,7 +2,7 @@
#include "headers/advanced-scene-switcher.hpp"
#include "headers/utility.hpp"
const int MacroActionReplayBuffer::id = 5;
const std::string MacroActionReplayBuffer::id = "replay_buffer";
bool MacroActionReplayBuffer::_registered = MacroActionFactory::Register(
MacroActionReplayBuffer::id,

View File

@@ -5,7 +5,7 @@
#include <QProcess>
#include <QFileDialog>
const int MacroActionRun::id = 6;
const std::string MacroActionRun::id = "run";
bool MacroActionRun::_registered = MacroActionFactory::Register(
MacroActionRun::id, {MacroActionRun::Create, MacroActionRunEdit::Create,

View File

@@ -1,8 +1,8 @@
#include "headers/macro-action-switch-scene.hpp"
#include "headers/macro-action-scene-switch.hpp"
#include "headers/advanced-scene-switcher.hpp"
#include "headers/utility.hpp"
const int MacroActionSwitchScene::id = 0;
const std::string MacroActionSwitchScene::id = "scene_switch";
bool MacroActionSwitchScene::_registered = MacroActionFactory::Register(
MacroActionSwitchScene::id,

View File

@@ -0,0 +1,212 @@
#include "headers/macro-action-scene-visibility.hpp"
#include "headers/advanced-scene-switcher.hpp"
#include "headers/utility.hpp"
const std::string MacroActionSceneVisibility::id = "scene_visibility";
bool MacroActionSceneVisibility::_registered = MacroActionFactory::Register(
MacroActionSceneVisibility::id,
{MacroActionSceneVisibility::Create,
MacroActionSceneVisibilityEdit::Create,
"AdvSceneSwitcher.action.sceneVisibility"});
const static std::map<SceneVisibilityAction, std::string> actionTypes = {
{SceneVisibilityAction::SHOW,
"AdvSceneSwitcher.action.sceneVisibility.type.show"},
{SceneVisibilityAction::HIDE,
"AdvSceneSwitcher.action.sceneVisibility.type.hide"},
};
struct VisInfo {
std::string name;
bool visible;
};
static bool visibilityEnum(obs_scene_t *, obs_sceneitem_t *item, void *ptr)
{
VisInfo *vInfo = reinterpret_cast<VisInfo *>(ptr);
auto sourceName = obs_source_get_name(obs_sceneitem_get_source(item));
if (vInfo->name == sourceName) {
obs_sceneitem_set_visible(item, vInfo->visible);
}
if (obs_sceneitem_is_group(item)) {
obs_scene_t *scene = obs_sceneitem_group_get_scene(item);
obs_scene_enum_items(scene, visibilityEnum, ptr);
}
return true;
}
bool MacroActionSceneVisibility::PerformAction()
{
auto s = obs_weak_source_get_source(_scene);
auto scene = obs_scene_from_source(s);
auto sourceName = GetWeakSourceName(_source);
VisInfo vInfo = {sourceName, _action == SceneVisibilityAction::SHOW};
switch (_action) {
case SceneVisibilityAction::SHOW:
obs_scene_enum_items(scene, visibilityEnum, &vInfo);
break;
case SceneVisibilityAction::HIDE:
obs_scene_enum_items(scene, visibilityEnum, &vInfo);
break;
default:
break;
}
obs_source_release(s);
return true;
}
void MacroActionSceneVisibility::LogAction()
{
auto it = actionTypes.find(_action);
if (it != actionTypes.end()) {
vblog(LOG_INFO,
"performed action \"%s\" for source \"%s\" on scene \"%s\"",
it->second.c_str(), GetWeakSourceName(_scene).c_str(),
GetWeakSourceName(_scene).c_str());
} else {
blog(LOG_WARNING, "ignored unknown SceneVisibility action %d",
static_cast<int>(_action));
}
}
bool MacroActionSceneVisibility::Save(obs_data_t *obj)
{
MacroAction::Save(obj);
obs_data_set_string(obj, "scene", GetWeakSourceName(_scene).c_str());
obs_data_set_string(obj, "source", GetWeakSourceName(_source).c_str());
obs_data_set_int(obj, "action", static_cast<int>(_action));
return true;
}
bool MacroActionSceneVisibility::Load(obs_data_t *obj)
{
MacroAction::Load(obj);
const char *sceneName = obs_data_get_string(obj, "scene");
_scene = GetWeakSourceByName(sceneName);
const char *sourceName = obs_data_get_string(obj, "source");
_source = GetWeakSourceByName(sourceName);
_action = static_cast<SceneVisibilityAction>(
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()));
}
}
static bool enumItem(obs_scene_t *, obs_sceneitem_t *item, void *ptr)
{
std::set<QString> *names = reinterpret_cast<std::set<QString> *>(ptr);
if (obs_sceneitem_is_group(item)) {
obs_scene_t *scene = obs_sceneitem_group_get_scene(item);
obs_scene_enum_items(scene, enumItem, ptr);
}
auto name = obs_source_get_name(obs_sceneitem_get_source(item));
names->emplace(name);
return true;
}
static inline void populateSceneItems(QComboBox *list,
OBSWeakSource sceneWeakSource = nullptr)
{
std::set<QString> names;
auto s = obs_weak_source_get_source(sceneWeakSource);
auto scene = obs_scene_from_source(s);
obs_scene_enum_items(scene, enumItem, &names);
obs_source_release(s);
for (auto &name : names) {
list->addItem(name);
}
list->model()->sort(0);
addSelectionEntry(list, obs_module_text("AdvSceneSwitcher.selectItem"));
list->setCurrentIndex(0);
}
MacroActionSceneVisibilityEdit::MacroActionSceneVisibilityEdit(
QWidget *parent, std::shared_ptr<MacroActionSceneVisibility> entryData)
: QWidget(parent)
{
_scenes = new QComboBox();
_sources = new QComboBox();
_actions = new QComboBox();
populateActionSelection(_actions);
populateSceneSelection(_scenes);
QWidget::connect(_actions, SIGNAL(currentIndexChanged(int)), this,
SLOT(ActionChanged(int)));
QWidget::connect(_scenes, SIGNAL(currentTextChanged(const QString &)),
this, SLOT(SceneChanged(const QString &)));
QWidget::connect(_sources, SIGNAL(currentTextChanged(const QString &)),
this, SLOT(SourceChanged(const QString &)));
QHBoxLayout *mainLayout = new QHBoxLayout;
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
{"{{scenes}}", _scenes},
{"{{sources}}", _sources},
{"{{actions}}", _actions},
};
placeWidgets(obs_module_text(
"AdvSceneSwitcher.action.SceneVisibility.entry"),
mainLayout, widgetPlaceholders);
setLayout(mainLayout);
_entryData = entryData;
UpdateEntryData();
_loading = false;
}
void MacroActionSceneVisibilityEdit::UpdateEntryData()
{
if (!_entryData) {
return;
}
_actions->setCurrentIndex(static_cast<int>(_entryData->_action));
_scenes->setCurrentText(GetWeakSourceName(_entryData->_scene).c_str());
populateSceneItems(_sources, _entryData->_scene);
_sources->setCurrentText(
GetWeakSourceName(_entryData->_source).c_str());
}
void MacroActionSceneVisibilityEdit::SceneChanged(const QString &text)
{
if (_loading || !_entryData) {
return;
}
{
std::lock_guard<std::mutex> lock(switcher->m);
_entryData->_scene = GetWeakSourceByQString(text);
}
_sources->clear();
populateSceneItems(_sources, _entryData->_scene);
}
void MacroActionSceneVisibilityEdit::SourceChanged(const QString &text)
{
if (_loading || !_entryData) {
return;
}
std::lock_guard<std::mutex> lock(switcher->m);
_entryData->_source = GetWeakSourceByQString(text);
}
void MacroActionSceneVisibilityEdit::ActionChanged(int value)
{
if (_loading || !_entryData) {
return;
}
std::lock_guard<std::mutex> lock(switcher->m);
_entryData->_action = static_cast<SceneVisibilityAction>(value);
}

201
src/macro-action-source.cpp Normal file
View File

@@ -0,0 +1,201 @@
#include "headers/macro-action-source.hpp"
#include "headers/advanced-scene-switcher.hpp"
#include "headers/utility.hpp"
const std::string MacroActionSource::id = "source";
bool MacroActionSource::_registered = MacroActionFactory::Register(
MacroActionSource::id,
{MacroActionSource::Create, MacroActionSourceEdit::Create,
"AdvSceneSwitcher.action.source"});
const static std::map<SourceAction, std::string> actionTypes = {
{SourceAction::ENABLE, "AdvSceneSwitcher.action.source.type.enable"},
{SourceAction::DISABLE, "AdvSceneSwitcher.action.source.type.disable"},
{SourceAction::SETTINGS,
"AdvSceneSwitcher.action.source.type.settings"},
};
void setSourceSettings(obs_source_t *s, const std::string &settings)
{
if (settings.empty()) {
return;
}
obs_data_t *data = obs_data_create_from_json(settings.c_str());
if (!data) {
blog(LOG_WARNING, "invalid source settings provided: \n%s",
settings.c_str());
return;
}
obs_source_update(s, data);
obs_data_release(data);
}
bool MacroActionSource::PerformAction()
{
auto s = obs_weak_source_get_source(_source);
switch (_action) {
case SourceAction::ENABLE:
obs_source_set_enabled(s, true);
break;
case SourceAction::DISABLE:
obs_source_set_enabled(s, false);
break;
case SourceAction::SETTINGS:
setSourceSettings(s, _settings);
break;
default:
break;
}
obs_source_release(s);
return true;
}
void MacroActionSource::LogAction()
{
auto it = actionTypes.find(_action);
if (it != actionTypes.end()) {
vblog(LOG_INFO, "performed action \"%s\" for Source \"%s\"",
it->second.c_str(), GetWeakSourceName(_source).c_str());
} else {
blog(LOG_WARNING, "ignored unknown source action %d",
static_cast<int>(_action));
}
}
bool MacroActionSource::Save(obs_data_t *obj)
{
MacroAction::Save(obj);
obs_data_set_string(obj, "source", GetWeakSourceName(_source).c_str());
obs_data_set_int(obj, "action", static_cast<int>(_action));
obs_data_set_string(obj, "settings", _settings.c_str());
return true;
}
bool MacroActionSource::Load(obs_data_t *obj)
{
MacroAction::Load(obj);
const char *sourceName = obs_data_get_string(obj, "source");
_source = GetWeakSourceByName(sourceName);
_action = static_cast<SourceAction>(obs_data_get_int(obj, "action"));
_settings = obs_data_get_string(obj, "settings");
return true;
}
static inline void populateActionSelection(QComboBox *list)
{
for (auto entry : actionTypes) {
list->addItem(obs_module_text(entry.second.c_str()));
}
}
MacroActionSourceEdit::MacroActionSourceEdit(
QWidget *parent, std::shared_ptr<MacroActionSource> entryData)
: QWidget(parent)
{
_sources = new QComboBox();
_actions = new QComboBox();
_getSettings = new QPushButton(
obs_module_text("AdvSceneSwitcher.action.source.getSettings"));
_settings = new QPlainTextEdit();
_warning = new QLabel(
obs_module_text("AdvSceneSwitcher.action.source.warning"));
populateActionSelection(_actions);
populateSourceSelection(_sources);
QWidget::connect(_actions, SIGNAL(currentIndexChanged(int)), this,
SLOT(ActionChanged(int)));
QWidget::connect(_sources, SIGNAL(currentTextChanged(const QString &)),
this, SLOT(SourceChanged(const QString &)));
QWidget::connect(_getSettings, SIGNAL(clicked()), this,
SLOT(GetSettingsClicked()));
QWidget::connect(_settings, SIGNAL(textChanged()), this,
SLOT(SettingsChanged()));
QVBoxLayout *mainLayout = new QVBoxLayout;
QHBoxLayout *entryLayout = new QHBoxLayout;
QHBoxLayout *buttonLayout = new QHBoxLayout;
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
{"{{sources}}", _sources},
{"{{actions}}", _actions},
{"{{settings}}", _settings},
{"{{getSettings}}", _getSettings},
};
placeWidgets(obs_module_text("AdvSceneSwitcher.action.source.entry"),
entryLayout, widgetPlaceholders);
mainLayout->addLayout(entryLayout);
mainLayout->addWidget(_warning);
mainLayout->addWidget(_settings);
buttonLayout->addWidget(_getSettings);
buttonLayout->addStretch();
mainLayout->addLayout(buttonLayout);
setLayout(mainLayout);
_entryData = entryData;
UpdateEntryData();
_loading = false;
}
void MacroActionSourceEdit::UpdateEntryData()
{
if (!_entryData) {
return;
}
_actions->setCurrentIndex(static_cast<int>(_entryData->_action));
_sources->setCurrentText(
GetWeakSourceName(_entryData->_source).c_str());
_settings->setPlainText(QString::fromStdString(_entryData->_settings));
SetWidgetVisibility(_entryData->_action == SourceAction::SETTINGS);
}
void MacroActionSourceEdit::SourceChanged(const QString &text)
{
if (_loading || !_entryData) {
return;
}
std::lock_guard<std::mutex> lock(switcher->m);
_entryData->_source = GetWeakSourceByQString(text);
}
void MacroActionSourceEdit::ActionChanged(int value)
{
if (_loading || !_entryData) {
return;
}
std::lock_guard<std::mutex> lock(switcher->m);
_entryData->_action = static_cast<SourceAction>(value);
SetWidgetVisibility(_entryData->_action == SourceAction::SETTINGS);
}
void MacroActionSourceEdit::GetSettingsClicked()
{
if (_loading || !_entryData || !_entryData->_source) {
return;
}
_settings->setPlainText(
QString::fromStdString(getSourceSettings(_entryData->_source)));
}
void MacroActionSourceEdit::SettingsChanged()
{
if (_loading || !_entryData) {
return;
}
std::lock_guard<std::mutex> lock(switcher->m);
_entryData->_settings = _settings->toPlainText().toStdString();
}
void MacroActionSourceEdit::SetWidgetVisibility(bool showSettings)
{
_settings->setVisible(showSettings);
_getSettings->setVisible(showSettings);
_warning->setVisible(!showSettings);
adjustSize();
}

View File

@@ -2,7 +2,7 @@
#include "headers/advanced-scene-switcher.hpp"
#include "headers/utility.hpp"
const int MacroActionStream::id = 3;
const std::string MacroActionStream::id = "streaming";
bool MacroActionStream::_registered = MacroActionFactory::Register(
MacroActionStream::id,

View File

@@ -4,7 +4,7 @@
#include <random>
const int MacroActionWait::id = 1;
const std::string MacroActionWait::id = "wait";
bool MacroActionWait::_registered = MacroActionFactory::Register(
MacroActionWait::id,

View File

@@ -3,7 +3,7 @@
#include "headers/utility.hpp"
#include "headers/advanced-scene-switcher.hpp"
const int MacroConditionAudio::id = 3;
const std::string MacroConditionAudio::id = "audio";
bool MacroConditionAudio::_registered = MacroConditionFactory::Register(
MacroConditionAudio::id,
@@ -11,8 +11,8 @@ bool MacroConditionAudio::_registered = MacroConditionFactory::Register(
"AdvSceneSwitcher.condition.audio"});
static std::map<AudioCondition, std::string> audioConditionTypes = {
{AudioCondition::ABOVE, "AdvSceneSwitcher.ondition.audio.state.above"},
{AudioCondition::BELOW, "AdvSceneSwitcher.ondition.audio.state.below"},
{AudioCondition::ABOVE, "AdvSceneSwitcher.condition.audio.state.above"},
{AudioCondition::BELOW, "AdvSceneSwitcher.condition.audio.state.below"},
};
MacroConditionAudio::~MacroConditionAudio()
@@ -35,11 +35,7 @@ bool MacroConditionAudio::CheckCondition()
// Reset for next check
_peak = -std::numeric_limits<float>::infinity();
if (!volumeThresholdreached) {
_duration.Reset();
return false;
}
return _duration.DurationReached();
return volumeThresholdreached;
}
bool MacroConditionAudio::Save(obs_data_t *obj)
@@ -49,7 +45,6 @@ bool MacroConditionAudio::Save(obs_data_t *obj)
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;
}
@@ -75,12 +70,9 @@ 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;
}
@@ -123,7 +115,6 @@ MacroConditionAudioEdit::MacroConditionAudioEdit(
_audioSources = new QComboBox();
_condition = new QComboBox();
_volume = new QSpinBox();
_duration = new DurationSelection(parent, false);
_volume->setSuffix("%");
_volume->setMaximum(100);
@@ -133,13 +124,11 @@ MacroConditionAudioEdit::MacroConditionAudioEdit(
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);
populateAudioSelection(_audioSources);
populateConditionSelection(_condition);
QHBoxLayout *switchLayout = new QHBoxLayout;
@@ -147,7 +136,7 @@ MacroConditionAudioEdit::MacroConditionAudioEdit(
{"{{audioSources}}", _audioSources},
{"{{volume}}", _volume},
{"{{condition}}", _condition},
{"{{duration}}", _duration}};
};
placeWidgets(obs_module_text("AdvSceneSwitcher.condition.audio.entry"),
switchLayout, widgetPlaceholders);
@@ -214,16 +203,6 @@ void MacroConditionAudioEdit::ConditionChanged(int cond)
_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) {
@@ -234,6 +213,5 @@ void MacroConditionAudioEdit::UpdateEntryData()
GetWeakSourceName(_entryData->_audioSource).c_str());
_volume->setValue(_entryData->_volume);
_condition->setCurrentIndex(static_cast<int>(_entryData->_condition));
_duration->SetDuration(_entryData->_duration);
UpdateVolmeterSource();
}

View File

@@ -0,0 +1,194 @@
#include "headers/macro-condition-edit.hpp"
#include "headers/macro-condition-counter.hpp"
#include "headers/utility.hpp"
#include "headers/advanced-scene-switcher.hpp"
const std::string MacroConditionCounter::id = "counter";
bool MacroConditionCounter::_registered = MacroConditionFactory::Register(
MacroConditionCounter::id,
{MacroConditionCounter::Create, MacroConditionCounterEdit::Create,
"AdvSceneSwitcher.condition.counter"});
static std::map<CounterCondition, std::string> counterConditionTypes = {
{CounterCondition::BELOW,
"AdvSceneSwitcher.condition.counter.type.below"},
{CounterCondition::ABOVE,
"AdvSceneSwitcher.condition.counter.type.above"},
{CounterCondition::EQUAL,
"AdvSceneSwitcher.condition.counter.type.equal"},
};
bool MacroConditionCounter::CheckCondition()
{
if (!_macro.get()) {
return false;
}
switch (_condition) {
case CounterCondition::BELOW:
return _macro->GetCount() < _count;
case CounterCondition::ABOVE:
return _macro->GetCount() > _count;
case CounterCondition::EQUAL:
return _macro->GetCount() == _count;
default:
break;
}
return false;
}
bool MacroConditionCounter::Save(obs_data_t *obj)
{
MacroCondition::Save(obj);
_macro.Save(obj);
obs_data_set_int(obj, "condition", static_cast<int>(_condition));
obs_data_set_int(obj, "count", _count);
return true;
}
bool MacroConditionCounter::Load(obs_data_t *obj)
{
MacroCondition::Load(obj);
_macro.Load(obj);
_condition = static_cast<CounterCondition>(
obs_data_get_int(obj, "condition"));
_count = obs_data_get_int(obj, "count");
return true;
}
static inline void populateConditionSelection(QComboBox *list)
{
for (auto entry : counterConditionTypes) {
list->addItem(obs_module_text(entry.second.c_str()));
}
}
MacroConditionCounterEdit::MacroConditionCounterEdit(
QWidget *parent, std::shared_ptr<MacroConditionCounter> entryData)
: QWidget(parent)
{
_macros = new MacroSelection(parent);
_conditions = new QComboBox();
_count = new QSpinBox();
_currentCount = new QLabel();
_resetCount = new QPushButton(
obs_module_text("AdvSceneSwitcher.condition.counter.reset"));
_count->setMaximum(10000000);
populateConditionSelection(_conditions);
QWidget::connect(_macros, SIGNAL(currentTextChanged(const QString &)),
this, SLOT(MacroChanged(const QString &)));
QWidget::connect(parent, SIGNAL(MacroRemoved(const QString &)), this,
SLOT(MacroRemove(const QString &)));
QWidget::connect(_conditions, SIGNAL(currentIndexChanged(int)), this,
SLOT(ConditionChanged(int)));
QWidget::connect(_count, SIGNAL(valueChanged(int)), this,
SLOT(CountChanged(int)));
QWidget::connect(_resetCount, SIGNAL(clicked()), this,
SLOT(ResetClicked()));
QVBoxLayout *mainLayout = new QVBoxLayout;
QHBoxLayout *line1Layout = new QHBoxLayout;
QHBoxLayout *line2Layout = new QHBoxLayout;
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
{"{{macros}}", _macros},
{"{{conditions}}", _conditions},
{"{{count}}", _count},
{"{{currentCount}}", _currentCount},
{"{{resetCount}}", _resetCount},
};
placeWidgets(obs_module_text(
"AdvSceneSwitcher.condition.counter.entry.line1"),
line1Layout, widgetPlaceholders);
placeWidgets(obs_module_text(
"AdvSceneSwitcher.condition.counter.entry.line2"),
line2Layout, widgetPlaceholders);
mainLayout->addLayout(line1Layout);
mainLayout->addLayout(line2Layout);
setLayout(mainLayout);
_entryData = entryData;
UpdateEntryData();
_loading = false;
}
void MacroConditionCounterEdit::UpdateEntryData()
{
if (!_entryData) {
return;
}
_macros->SetCurrentMacro(_entryData->_macro.get());
_conditions->setCurrentIndex(static_cast<int>(_entryData->_condition));
_count->setValue(_entryData->_count);
ResetTimer();
}
void MacroConditionCounterEdit::MacroChanged(const QString &text)
{
if (_loading || !_entryData) {
return;
}
std::lock_guard<std::mutex> lock(switcher->m);
_entryData->_macro.UpdateRef(text);
ResetTimer();
}
void MacroConditionCounterEdit::CountChanged(int value)
{
if (_loading || !_entryData) {
return;
}
std::lock_guard<std::mutex> lock(switcher->m);
_entryData->_count = value;
}
void MacroConditionCounterEdit::ConditionChanged(int cond)
{
if (_loading || !_entryData) {
return;
}
std::lock_guard<std::mutex> lock(switcher->m);
_entryData->_condition = static_cast<CounterCondition>(cond);
}
void MacroConditionCounterEdit::MacroRemove(const QString &name)
{
UNUSED_PARAMETER(name);
if (_entryData) {
_entryData->_macro.UpdateRef();
}
}
void MacroConditionCounterEdit::ResetClicked()
{
if (_loading || !_entryData || !_entryData->_macro.get()) {
return;
}
_entryData->_macro->ResetCount();
ResetTimer();
}
void MacroConditionCounterEdit::UpdateCount()
{
if (_entryData && _entryData->_macro.get()) {
_currentCount->setText(
QString::number(_entryData->_macro->GetCount()));
} else {
_currentCount->setText("-");
}
}
void MacroConditionCounterEdit::ResetTimer()
{
_timer.reset(new QTimer(this));
connect(_timer.get(), SIGNAL(timeout()), this, SLOT(UpdateCount()));
_timer->start(1000);
}

View File

@@ -1,8 +1,10 @@
#include "headers/macro-condition-edit.hpp"
#include "headers/macro-condition-scene.hpp"
std::map<int, MacroConditionInfo> MacroConditionFactory::_methods;
std::map<std::string, MacroConditionInfo> MacroConditionFactory::_methods;
bool MacroConditionFactory::Register(int id, MacroConditionInfo info)
bool MacroConditionFactory::Register(const std::string &id,
MacroConditionInfo info)
{
if (auto it = _methods.find(id); it == _methods.end()) {
_methods[id] = info;
@@ -11,24 +13,51 @@ bool MacroConditionFactory::Register(int id, MacroConditionInfo info)
return false;
}
std::shared_ptr<MacroCondition> MacroConditionFactory::Create(const int id)
std::shared_ptr<MacroCondition>
MacroConditionFactory::Create(const std::string &id)
{
if (auto it = _methods.find(id); it != _methods.end())
if (auto it = _methods.find(id); it != _methods.end()) {
return it->second._createFunc();
}
return nullptr;
}
QWidget *
MacroConditionFactory::CreateWidget(const int id, QWidget *parent,
MacroConditionFactory::CreateWidget(const std::string &id, QWidget *parent,
std::shared_ptr<MacroCondition> cond)
{
if (auto it = _methods.find(id); it != _methods.end())
if (auto it = _methods.find(id); it != _methods.end()) {
return it->second._createWidgetFunc(parent, cond);
}
return nullptr;
}
std::string MacroConditionFactory::GetConditionName(const std::string &id)
{
if (auto it = _methods.find(id); it != _methods.end()) {
return it->second._name;
}
return "unknown condition";
}
std::string MacroConditionFactory::GetIdByName(const QString &name)
{
for (auto it : _methods) {
if (name == obs_module_text(it.second._name.c_str())) {
return it.first;
}
}
return "";
}
bool MacroConditionFactory::UsesDurationConstraint(const std::string &id)
{
if (auto it = _methods.find(id); it != _methods.end()) {
return it->second._useDurationConstraint;
}
return false;
}
static inline void populateLogicSelection(QComboBox *list, bool root = false)
{
if (root) {
@@ -57,24 +86,34 @@ static inline void populateConditionSelection(QComboBox *list)
}
MacroConditionEdit::MacroConditionEdit(
QWidget *parent, std::shared_ptr<MacroCondition> *entryData, int type,
bool root, bool startCollapsed)
QWidget *parent, std::shared_ptr<MacroCondition> *entryData,
const std::string &id, bool root, bool startCollapsed)
: QWidget(parent)
{
_logicSelection = new QComboBox();
_conditionSelection = new QComboBox();
_section = new Section(300);
_dur = new DurationConstraintEdit();
QWidget::connect(_logicSelection, SIGNAL(currentIndexChanged(int)),
this, SLOT(LogicSelectionChanged(int)));
QWidget::connect(_conditionSelection, SIGNAL(currentIndexChanged(int)),
this, SLOT(ConditionSelectionChanged(int)));
QWidget::connect(_conditionSelection,
SIGNAL(currentTextChanged(const QString &)), this,
SLOT(ConditionSelectionChanged(const QString &)));
QWidget::connect(_dur, SIGNAL(DurationChanged(double)), this,
SLOT(DurationChanged(double)));
QWidget::connect(_dur, SIGNAL(UnitChanged(DurationUnit)), this,
SLOT(DurationUnitChanged(DurationUnit)));
QWidget::connect(_dur, SIGNAL(ConditionChanged(DurationCondition)),
this,
SLOT(DurationConditionChanged(DurationCondition)));
populateLogicSelection(_logicSelection, root);
populateConditionSelection(_conditionSelection);
_section->AddHeaderWidget(_logicSelection);
_section->AddHeaderWidget(_conditionSelection);
_section->AddHeaderWidget(_dur);
QVBoxLayout *mainLayout = new QVBoxLayout;
mainLayout->addWidget(_section);
@@ -82,9 +121,8 @@ MacroConditionEdit::MacroConditionEdit(
_entryData = entryData;
_isRoot = root;
UpdateEntryData(type);
UpdateEntryData(id, startCollapsed);
_loading = false;
_section->Collapse(startCollapsed);
}
void MacroConditionEdit::LogicSelectionChanged(int idx)
@@ -109,11 +147,12 @@ bool MacroConditionEdit::IsRootNode()
return _isRoot;
}
void MacroConditionEdit::UpdateEntryData(int type)
void MacroConditionEdit::UpdateEntryData(const std::string &id, bool collapse)
{
_conditionSelection->setCurrentIndex(type);
auto widget = MacroConditionFactory::CreateWidget(type, window(),
*_entryData);
_conditionSelection->setCurrentText(obs_module_text(
MacroConditionFactory::GetConditionName(id).c_str()));
auto widget =
MacroConditionFactory::CreateWidget(id, window(), *_entryData);
auto logic = (*_entryData)->GetLogicType();
if (IsRootNode()) {
_logicSelection->setCurrentIndex(static_cast<int>(logic));
@@ -121,29 +160,63 @@ void MacroConditionEdit::UpdateEntryData(int type)
_logicSelection->setCurrentIndex(static_cast<int>(logic) -
logic_root_offset);
}
_section->SetContent(widget);
_section->SetContent(widget, collapse);
_dur->setVisible(MacroConditionFactory::UsesDurationConstraint(id));
auto constraint = (*_entryData)->GetDurationConstraint();
_dur->SetValue(constraint);
}
void MacroConditionEdit::Collapse(bool collapsed)
void MacroConditionEdit::ConditionSelectionChanged(const QString &text)
{
_section->Collapse(collapsed);
if (_loading || !_entryData) {
return;
}
std::string id = MacroConditionFactory::GetIdByName(text);
auto temp = DurationConstraint();
_dur->SetValue(temp);
std::lock_guard<std::mutex> lock(switcher->m);
auto logic = (*_entryData)->GetLogicType();
_entryData->reset();
*_entryData = MacroConditionFactory::Create(id);
(*_entryData)->SetLogicType(logic);
auto widget =
MacroConditionFactory::CreateWidget(id, window(), *_entryData);
_section->SetContent(widget, false);
_dur->setVisible(MacroConditionFactory::UsesDurationConstraint(id));
}
void MacroConditionEdit::ConditionSelectionChanged(int idx)
void MacroConditionEdit::DurationChanged(double seconds)
{
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);
(*_entryData)->SetDuration(seconds);
}
void MacroConditionEdit::DurationConditionChanged(DurationCondition cond)
{
if (_loading || !_entryData) {
return;
}
std::lock_guard<std::mutex> lock(switcher->m);
(*_entryData)->SetDurationCondition(cond);
}
void MacroConditionEdit::DurationUnitChanged(DurationUnit unit)
{
if (_loading || !_entryData) {
return;
}
std::lock_guard<std::mutex> lock(switcher->m);
(*_entryData)->SetDurationUnit(unit);
}
void AdvSceneSwitcher::on_conditionAdd_clicked()
@@ -152,17 +225,18 @@ void AdvSceneSwitcher::on_conditionAdd_clicked()
if (!macro) {
return;
}
MacroConditionScene temp;
std::string id = temp.GetId();
std::lock_guard<std::mutex> lock(switcher->m);
bool root = macro->Conditions().size() == 0;
macro->Conditions().emplace_back(MacroConditionFactory::Create(0));
macro->Conditions().emplace_back(MacroConditionFactory::Create(id));
auto logic = root ? LogicType::ROOT_NONE : LogicType::NONE;
macro->Conditions().back()->SetLogicType(logic);
auto newEntry = new MacroConditionEdit(
this, &macro->Conditions().back(), 0, root);
this, &macro->Conditions().back(), id, root, false);
ui->macroEditConditionLayout->addWidget(newEntry);
ui->macroEditConditionHelp->setVisible(false);
newEntry->Collapse(false);
}
void AdvSceneSwitcher::on_conditionRemove_clicked()

View File

@@ -7,7 +7,7 @@
#include <QTextStream>
#include <QFileDialog>
const int MacroConditionFile::id = 4;
const std::string MacroConditionFile::id = "file";
bool MacroConditionFile::_registered = MacroConditionFactory::Register(
MacroConditionFile::id,

View File

@@ -3,12 +3,12 @@
#include "headers/utility.hpp"
#include "headers/advanced-scene-switcher.hpp"
const int MacroConditionIdle::id = 10;
const std::string MacroConditionIdle::id = "idle";
bool MacroConditionIdle::_registered = MacroConditionFactory::Register(
MacroConditionIdle::id,
{MacroConditionIdle::Create, MacroConditionIdleEdit::Create,
"AdvSceneSwitcher.condition.idle"});
"AdvSceneSwitcher.condition.idle", false});
bool MacroConditionIdle::CheckCondition()
{

View File

@@ -0,0 +1,88 @@
#include "headers/macro-condition-edit.hpp"
#include "headers/macro-condition-interval.hpp"
#include "headers/utility.hpp"
#include "headers/advanced-scene-switcher.hpp"
const std::string MacroConditionInterval::id = "interval";
bool MacroConditionInterval::_registered = MacroConditionFactory::Register(
MacroConditionInterval::id,
{MacroConditionInterval::Create, MacroConditionIntervalEdit::Create,
"AdvSceneSwitcher.condition.interval", false});
bool MacroConditionInterval::CheckCondition()
{
if (_duration.DurationReached()) {
_duration.Reset();
return true;
}
return false;
}
bool MacroConditionInterval::Save(obs_data_t *obj)
{
MacroCondition::Save(obj);
_duration.Save(obj);
return true;
}
bool MacroConditionInterval::Load(obs_data_t *obj)
{
MacroCondition::Load(obj);
_duration.Load(obj);
return true;
}
MacroConditionIntervalEdit::MacroConditionIntervalEdit(
QWidget *parent, std::shared_ptr<MacroConditionInterval> 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.interval.entry"),
mainLayout, widgetPlaceholders);
setLayout(mainLayout);
_entryData = entryData;
UpdateEntryData();
_loading = false;
}
void MacroConditionIntervalEdit::DurationChanged(double seconds)
{
if (_loading || !_entryData) {
return;
}
std::lock_guard<std::mutex> lock(switcher->m);
_entryData->_duration.seconds = seconds;
}
void MacroConditionIntervalEdit::DurationUnitChanged(DurationUnit unit)
{
if (_loading || !_entryData) {
return;
}
std::lock_guard<std::mutex> lock(switcher->m);
_entryData->_duration.displayUnit = unit;
}
void MacroConditionIntervalEdit::UpdateEntryData()
{
if (!_entryData) {
return;
}
_duration->SetDuration(_entryData->_duration);
}

View File

@@ -3,7 +3,7 @@
#include "headers/utility.hpp"
#include "headers/advanced-scene-switcher.hpp"
const int MacroConditionMedia::id = 5;
const std::string MacroConditionMedia::id = "media";
bool MacroConditionMedia::_registered = MacroConditionFactory::Register(
MacroConditionMedia::id,
@@ -256,7 +256,7 @@ MacroConditionMediaEdit::MacroConditionMediaEdit(
QWidget::connect(_time, SIGNAL(UnitChanged(DurationUnit)), this,
SLOT(TimeUnitChanged(DurationUnit)));
AdvSceneSwitcher::populateMediaSelection(_mediaSources);
populateMediaSelection(_mediaSources);
populateMediaStates(_states);
populateMediaTimeRestrictions(_timeRestrictions);

View File

@@ -3,13 +3,13 @@
#include "headers/utility.hpp"
#include "headers/advanced-scene-switcher.hpp"
const int MacroConditionPluginState::id = 11;
const std::string MacroConditionPluginState::id = "plugin_state";
bool MacroConditionPluginState::_registered = MacroConditionFactory::Register(
MacroConditionPluginState::id,
{MacroConditionPluginState::Create,
MacroConditionPluginStateEdit::Create,
"AdvSceneSwitcher.condition.pluginState"});
"AdvSceneSwitcher.condition.pluginState", false});
static std::map<PluginStateCondition, std::string> pluginStateConditionTypes = {
{PluginStateCondition::SCENESWITCHED,

View File

@@ -5,7 +5,7 @@
#include <regex>
const int MacroConditionProcess::id = 9;
const std::string MacroConditionProcess::id = "process";
bool MacroConditionProcess::_registered = MacroConditionFactory::Register(
MacroConditionProcess::id,
@@ -61,7 +61,7 @@ MacroConditionProcessEdit::MacroConditionProcessEdit(
QWidget::connect(_focused, SIGNAL(stateChanged(int)), this,
SLOT(FocusChanged(int)));
AdvSceneSwitcher::populateProcessSelection(_processSelection);
populateProcessSelection(_processSelection);
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
{"{{processes}}", _processSelection},

View File

@@ -3,7 +3,7 @@
#include "headers/utility.hpp"
#include "headers/advanced-scene-switcher.hpp"
const int MacroConditionRecord::id = 8;
const std::string MacroConditionRecord::id = "recording";
bool MacroConditionRecord::_registered = MacroConditionFactory::Register(
MacroConditionRecord::id,
@@ -32,20 +32,13 @@ bool MacroConditionRecord::CheckCondition()
default:
break;
}
if (!stateMatch) {
_duration.Reset();
return false;
}
return _duration.DurationReached();
return stateMatch;
}
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;
}
@@ -53,7 +46,6 @@ 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;
}
@@ -69,14 +61,9 @@ MacroConditionRecordEdit::MacroConditionRecordEdit(
: 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);
@@ -84,7 +71,6 @@ MacroConditionRecordEdit::MacroConditionRecordEdit(
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
{"{{recordState}}", _recordState},
{"{{duration}}", _duration},
};
placeWidgets(obs_module_text("AdvSceneSwitcher.condition.record.entry"),
@@ -106,26 +92,6 @@ void MacroConditionRecordEdit::StateChanged(int value)
_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) {
@@ -134,5 +100,4 @@ void MacroConditionRecordEdit::UpdateEntryData()
_recordState->setCurrentIndex(
static_cast<int>(_entryData->_recordState));
_duration->SetDuration(_entryData->_duration);
}

View File

@@ -3,7 +3,7 @@
#include "headers/utility.hpp"
#include "headers/advanced-scene-switcher.hpp"
const int MacroConditionRegion::id = 2;
const std::string MacroConditionRegion::id = "region";
bool MacroConditionRegion::_registered = MacroConditionFactory::Register(
MacroConditionRegion::id,

View File

@@ -3,7 +3,7 @@
#include "headers/utility.hpp"
#include "headers/advanced-scene-switcher.hpp"
const int MacroConditionScene::id = 0;
const std::string MacroConditionScene::id = "scene";
bool MacroConditionScene::_registered = MacroConditionFactory::Register(
MacroConditionScene::id,
@@ -29,11 +29,7 @@ bool MacroConditionScene::CheckCondition()
sceneMatch = switcher->previousScene == _scene;
}
if (!sceneMatch) {
_duration.Reset();
return false;
}
return _duration.DurationReached();
return sceneMatch;
}
bool MacroConditionScene::Save(obs_data_t *obj)
@@ -41,7 +37,6 @@ 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;
}
@@ -50,7 +45,6 @@ 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;
}
@@ -67,26 +61,20 @@ MacroConditionSceneEdit::MacroConditionSceneEdit(
{
_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);
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);
@@ -117,26 +105,6 @@ void MacroConditionSceneEdit::TypeChanged(int value)
_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) {
@@ -146,5 +114,4 @@ void MacroConditionSceneEdit::UpdateEntryData()
_sceneSelection->setCurrentText(
GetWeakSourceName(_entryData->_scene).c_str());
_sceneType->setCurrentIndex(static_cast<int>(_entryData->_type));
_duration->SetDuration(_entryData->_duration);
}

View File

@@ -0,0 +1,223 @@
#include "headers/macro-condition-edit.hpp"
#include "headers/macro-condition-source.hpp"
#include "headers/utility.hpp"
#include "headers/advanced-scene-switcher.hpp"
#include <regex>
const std::string MacroConditionSource::id = "source";
bool MacroConditionSource::_registered = MacroConditionFactory::Register(
MacroConditionSource::id,
{MacroConditionSource::Create, MacroConditionSourceEdit::Create,
"AdvSceneSwitcher.condition.source"});
static std::map<SourceCondition, std::string> sourceConditionTypes = {
{SourceCondition::ACTIVE,
"AdvSceneSwitcher.condition.source.type.active"},
{SourceCondition::SHOWING,
"AdvSceneSwitcher.condition.source.type.showing"},
{SourceCondition::SETTINGS,
"AdvSceneSwitcher.condition.source.type.settings"},
};
bool checkSettings(const OBSWeakSource &source, const std::string &settings,
bool useRegex)
{
bool ret = false;
std::string currentSettings = getSourceSettings(source);
if (useRegex) {
try {
std::regex expr(settings);
ret = std::regex_match(currentSettings, expr);
} catch (const std::regex_error &) {
}
} else {
ret = currentSettings == settings;
}
return ret;
}
bool MacroConditionSource::CheckCondition()
{
if (!_source) {
return false;
}
bool ret = false;
auto s = obs_weak_source_get_source(_source);
switch (_condition) {
case SourceCondition::ACTIVE:
ret = obs_source_active(s);
break;
case SourceCondition::SHOWING:
ret = obs_source_showing(s);
break;
case SourceCondition::SETTINGS:
ret = checkSettings(_source, _settings, _regex);
break;
default:
break;
}
obs_source_release(s);
return ret;
}
bool MacroConditionSource::Save(obs_data_t *obj)
{
MacroCondition::Save(obj);
obs_data_set_string(obj, "source", GetWeakSourceName(_source).c_str());
obs_data_set_int(obj, "condition", static_cast<int>(_condition));
obs_data_set_string(obj, "settings", _settings.c_str());
return true;
}
bool MacroConditionSource::Load(obs_data_t *obj)
{
MacroCondition::Load(obj);
const char *sourceName = obs_data_get_string(obj, "source");
_source = GetWeakSourceByName(sourceName);
_condition = static_cast<SourceCondition>(
obs_data_get_int(obj, "condition"));
_settings = obs_data_get_string(obj, "settings");
return true;
}
static inline void populateConditionSelection(QComboBox *list)
{
for (auto entry : sourceConditionTypes) {
list->addItem(obs_module_text(entry.second.c_str()));
}
}
MacroConditionSourceEdit::MacroConditionSourceEdit(
QWidget *parent, std::shared_ptr<MacroConditionSource> entryData)
: QWidget(parent)
{
_sources = new QComboBox();
_conditions = new QComboBox();
_getSettings = new QPushButton(obs_module_text(
"AdvSceneSwitcher.condition.source.getSettings"));
_settings = new QPlainTextEdit();
_regex = new QCheckBox(
obs_module_text("AdvSceneSwitcher.condition.source.regex"));
populateConditionSelection(_conditions);
populateSourceSelection(_sources);
QWidget::connect(_sources, SIGNAL(currentTextChanged(const QString &)),
this, SLOT(SourceChanged(const QString &)));
QWidget::connect(_conditions, SIGNAL(currentIndexChanged(int)), this,
SLOT(ConditionChanged(int)));
QWidget::connect(_getSettings, SIGNAL(clicked()), this,
SLOT(GetSettingsClicked()));
QWidget::connect(_settings, SIGNAL(textChanged()), this,
SLOT(SettingsChanged()));
QWidget::connect(_regex, SIGNAL(stateChanged(int)), this,
SLOT(RegexChanged(int)));
QVBoxLayout *mainLayout = new QVBoxLayout;
QHBoxLayout *line1Layout = new QHBoxLayout;
QHBoxLayout *line2Layout = new QHBoxLayout;
QHBoxLayout *line3Layout = new QHBoxLayout;
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
{"{{sources}}", _sources}, {"{{conditions}}", _conditions},
{"{{settings}}", _settings}, {"{{getSettings}}", _getSettings},
{"{{regex}}", _regex},
};
placeWidgets(obs_module_text(
"AdvSceneSwitcher.condition.source.entry.line1"),
line1Layout, widgetPlaceholders);
placeWidgets(obs_module_text(
"AdvSceneSwitcher.condition.source.entry.line2"),
line2Layout, widgetPlaceholders, false);
placeWidgets(obs_module_text(
"AdvSceneSwitcher.condition.source.entry.line3"),
line3Layout, widgetPlaceholders);
mainLayout->addLayout(line1Layout);
mainLayout->addLayout(line2Layout);
mainLayout->addLayout(line3Layout);
setLayout(mainLayout);
_entryData = entryData;
UpdateEntryData();
_loading = false;
}
void MacroConditionSourceEdit::SourceChanged(const QString &text)
{
if (_loading || !_entryData) {
return;
}
std::lock_guard<std::mutex> lock(switcher->m);
_entryData->_source = GetWeakSourceByQString(text);
}
void MacroConditionSourceEdit::ConditionChanged(int index)
{
if (_loading || !_entryData) {
return;
}
std::lock_guard<std::mutex> lock(switcher->m);
_entryData->_condition = static_cast<SourceCondition>(index);
SetSettingsSelectionVisible(_entryData->_condition ==
SourceCondition::SETTINGS);
}
void MacroConditionSourceEdit::GetSettingsClicked()
{
if (_loading || !_entryData || !_entryData->_source) {
return;
}
_settings->setPlainText(
QString::fromStdString(getSourceSettings(_entryData->_source)));
}
void MacroConditionSourceEdit::SettingsChanged()
{
if (_loading || !_entryData) {
return;
}
std::lock_guard<std::mutex> lock(switcher->m);
_entryData->_settings = _settings->toPlainText().toStdString();
}
void MacroConditionSourceEdit::RegexChanged(int state)
{
if (_loading || !_entryData) {
return;
}
std::lock_guard<std::mutex> lock(switcher->m);
_entryData->_regex = state;
}
void MacroConditionSourceEdit::SetSettingsSelectionVisible(bool visible)
{
_settings->setVisible(visible);
_getSettings->setVisible(visible);
_regex->setVisible(visible);
adjustSize();
}
void MacroConditionSourceEdit::UpdateEntryData()
{
if (!_entryData) {
return;
}
_sources->setCurrentText(
GetWeakSourceName(_entryData->_source).c_str());
_conditions->setCurrentIndex(static_cast<int>(_entryData->_condition));
_settings->setPlainText(QString::fromStdString(_entryData->_settings));
_regex->setChecked(_entryData->_regex);
SetSettingsSelectionVisible(_entryData->_condition ==
SourceCondition::SETTINGS);
}

View File

@@ -3,7 +3,7 @@
#include "headers/utility.hpp"
#include "headers/advanced-scene-switcher.hpp"
const int MacroConditionStream::id = 7;
const std::string MacroConditionStream::id = "streaming";
bool MacroConditionStream::_registered = MacroConditionFactory::Register(
MacroConditionStream::id,
@@ -28,20 +28,13 @@ bool MacroConditionStream::CheckCondition()
default:
break;
}
if (!stateMatch) {
_duration.Reset();
return false;
}
return _duration.DurationReached();
return stateMatch;
}
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;
}
@@ -49,7 +42,6 @@ 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;
}
@@ -65,14 +57,9 @@ MacroConditionStreamEdit::MacroConditionStreamEdit(
: 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);
@@ -80,7 +67,6 @@ MacroConditionStreamEdit::MacroConditionStreamEdit(
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
{"{{streamState}}", _streamState},
{"{{duration}}", _duration},
};
placeWidgets(obs_module_text("AdvSceneSwitcher.condition.stream.entry"),
@@ -102,26 +88,6 @@ void MacroConditionStreamEdit::StateChanged(int value)
_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) {
@@ -130,5 +96,4 @@ void MacroConditionStreamEdit::UpdateEntryData()
_streamState->setCurrentIndex(
static_cast<int>(_entryData->_streamState));
_duration->SetDuration(_entryData->_duration);
}

View File

@@ -8,7 +8,7 @@
#include <QToolTip>
#include <QMessageBox>
const int MacroConditionVideo::id = 6;
const std::string MacroConditionVideo::id = "video";
bool MacroConditionVideo::_registered = MacroConditionFactory::Register(
MacroConditionVideo::id,
@@ -39,15 +39,7 @@ bool MacroConditionVideo::CheckCondition()
if (_screenshotData) {
if (_screenshotData->done) {
bool imageMatch = Compare();
if (!imageMatch) {
_duration.Reset();
}
if (imageMatch && _duration.DurationReached()) {
match = true;
}
match = Compare();
if (!requiresFileInput(_condition)) {
_matchImage = std::move(_screenshotData->image);
@@ -66,7 +58,6 @@ bool MacroConditionVideo::Save(obs_data_t *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;
}
@@ -77,7 +68,6 @@ bool MacroConditionVideo::Load(obs_data_t *obj)
_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)) {
@@ -137,7 +127,6 @@ MacroConditionVideoEdit::MacroConditionVideoEdit(
{
_videoSelection = new QComboBox();
_condition = new QComboBox();
_duration = new DurationSelection();
_filePath = new QLineEdit();
_browseButton =
new QPushButton(obs_module_text("AdvSceneSwitcher.browse"));
@@ -151,23 +140,18 @@ MacroConditionVideoEdit::MacroConditionVideoEdit(
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);
populateVideoSelection(_videoSelection);
populateConditionSelection(_condition);
QHBoxLayout *mainLayout = new QHBoxLayout;
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
{"{{videoSources}}", _videoSelection},
{"{{condition}}", _condition},
{"{{duration}}", _duration},
{"{{filePath}}", _filePath},
{"{{browseButton}}", _browseButton},
};
@@ -324,26 +308,6 @@ void MacroConditionVideoEdit::BrowseButtonClicked()
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) {
@@ -353,7 +317,6 @@ void MacroConditionVideoEdit::UpdateEntryData()
_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)) {

View File

@@ -0,0 +1,113 @@
#include "headers/macro-condition-edit.hpp"
#include "headers/macro-condition-virt-desktop.hpp"
#include "headers/utility.hpp"
#include "headers/advanced-scene-switcher.hpp"
const std::string MacroConditionVirtDesktop::id = "virtual_desktop";
bool MacroConditionVirtDesktop::_registered = MacroConditionFactory::Register(
MacroConditionVirtDesktop::id,
{MacroConditionVirtDesktop::Create,
MacroConditionVirtDesktopEdit::Create,
"AdvSceneSwitcher.condition.virtDesktop"});
bool MacroConditionVirtDesktop::CheckCondition()
{
long curDesktop;
GetCurrentVirtualDesktop(curDesktop);
return _desktop == curDesktop;
}
bool MacroConditionVirtDesktop::Save(obs_data_t *obj)
{
MacroCondition::Save(obj);
obs_data_set_int(obj, "desktop", static_cast<int>(_desktop));
return true;
}
bool MacroConditionVirtDesktop::Load(obs_data_t *obj)
{
MacroCondition::Load(obj);
_desktop = obs_data_get_int(obj, "desktop");
return true;
}
static void populateVirtualDesktopSelection(QComboBox *list)
{
long count;
GetVirtualDesktopCount(count);
for (long i = 0; i < count; i++) {
list->addItem(std::to_string(i).c_str());
}
}
MacroConditionVirtDesktopEdit::MacroConditionVirtDesktopEdit(
QWidget *parent, std::shared_ptr<MacroConditionVirtDesktop> entryData)
: QWidget(parent)
{
_currentDesktop = new QLabel();
_currentDesktop->setTextInteractionFlags(Qt::TextSelectableByMouse);
_virtDesktops = new QComboBox();
QWidget::connect(_virtDesktops, SIGNAL(currentIndexChanged(int)), this,
SLOT(DesktopChanged(int)));
populateVirtualDesktopSelection(_virtDesktops);
QVBoxLayout *mainLayout = new QVBoxLayout;
QHBoxLayout *line1Layout = new QHBoxLayout;
QHBoxLayout *line2Layout = new QHBoxLayout;
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
{"{{virtDesktops}}", _virtDesktops},
{"{{currentDesktop}}", _currentDesktop},
};
placeWidgets(
obs_module_text(
"AdvSceneSwitcher.condition.virtDesktop.entry.line1"),
line1Layout, widgetPlaceholders);
placeWidgets(
obs_module_text(
"AdvSceneSwitcher.condition.virtDesktop.entry.line2"),
line2Layout, widgetPlaceholders);
mainLayout->addLayout(line1Layout);
mainLayout->addLayout(line2Layout);
setLayout(mainLayout);
// Current virtual desktop position
QTimer *timer = new QTimer(this);
connect(timer, SIGNAL(timeout()), this, SLOT(UpdateCurrentDesktop()));
timer->start(1000);
_entryData = entryData;
UpdateEntryData();
_loading = false;
}
void MacroConditionVirtDesktopEdit::DesktopChanged(int value)
{
if (_loading || !_entryData) {
return;
}
std::lock_guard<std::mutex> lock(switcher->m);
_entryData->_desktop = value;
}
void MacroConditionVirtDesktopEdit::UpdateEntryData()
{
if (!_entryData) {
return;
}
_virtDesktops->setCurrentIndex(_entryData->_desktop);
}
void MacroConditionVirtDesktopEdit::UpdateCurrentDesktop()
{
long curDesktop;
if (GetCurrentVirtualDesktop(curDesktop)) {
_currentDesktop->setText(QString::number(curDesktop));
} else {
_currentDesktop->setText(obs_module_text(
"AdvSceneSwitcher.condition.virtDesktop.notAvailable"));
}
}

View File

@@ -5,7 +5,7 @@
#include <regex>
const int MacroConditionWindow::id = 1;
const std::string MacroConditionWindow::id = "window";
bool MacroConditionWindow::_registered = MacroConditionFactory::Register(
MacroConditionWindow::id,
@@ -83,12 +83,7 @@ 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;
}
@@ -115,7 +110,7 @@ MacroConditionWindowEdit::MacroConditionWindowEdit(
QWidget::connect(_focused, SIGNAL(stateChanged(int)), this,
SLOT(FocusChanged(int)));
AdvSceneSwitcher::populateWindowSelection(_windowSelection);
populateWindowSelection(_windowSelection);
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
{"{{windows}}", _windowSelection},

68
src/macro-selection.cpp Normal file
View File

@@ -0,0 +1,68 @@
#include "headers/macro-selection.hpp"
#include "headers/advanced-scene-switcher.hpp"
#include <QStandardItemModel>
MacroSelection::MacroSelection(QWidget *parent) : QComboBox(parent)
{
addItem(obs_module_text("AdvSceneSwitcher.selectMacro"));
QStandardItemModel *model =
qobject_cast<QStandardItemModel *>(this->model());
QModelIndex firstIndex =
model->index(0, modelColumn(), rootModelIndex());
QStandardItem *firstItem = model->itemFromIndex(firstIndex);
firstItem->setSelectable(false);
firstItem->setEnabled(false);
for (auto &m : switcher->macros) {
addItem(QString::fromStdString(m.Name()));
}
QWidget::connect(parent, SIGNAL(MacroAdded(const QString &)), this,
SLOT(MacroAdd(const QString &)));
QWidget::connect(parent, SIGNAL(MacroRemoved(const QString &)), this,
SLOT(MacroRemove(const QString &)));
QWidget::connect(parent,
SIGNAL(MacroRenamed(const QString &, const QString &)),
this,
SLOT(MacroRename(const QString &, const QString &)));
}
void MacroSelection::MacroAdd(const QString &name)
{
addItem(name);
}
void MacroSelection::SetCurrentMacro(Macro *m)
{
if (!m) {
this->setCurrentIndex(0);
} else {
this->setCurrentText(QString::fromStdString(m->Name()));
}
}
void MacroSelection::MacroRemove(const QString &name)
{
int idx = findText(name);
if (idx == -1) {
return;
}
removeItem(idx);
setCurrentIndex(0);
}
void MacroSelection::MacroRename(const QString &oldName, const QString &newName)
{
bool renameSelected = currentText() == oldName;
int idx = findText(oldName);
if (idx == -1) {
return;
}
removeItem(idx);
insertItem(idx, newName);
if (renameSelected) {
setCurrentIndex(findText(newName));
}
}

View File

@@ -9,17 +9,11 @@
static QMetaObject::Connection addPulse;
const auto conditionsCollapseThreshold = 4;
const auto actionsCollapseThreshold = 2;
const auto actionsCollapseThreshold = 4;
bool macroNameExists(std::string name)
{
for (auto &m : switcher->macros) {
if (m.Name() == name) {
return true;
}
}
return false;
return !!GetMacroByName(name.c_str());
}
bool AdvSceneSwitcher::addNewMacro(std::string &name)
@@ -69,10 +63,14 @@ void AdvSceneSwitcher::on_macroAdd_clicked()
QListWidgetItem *item = new QListWidgetItem(text, ui->macros);
item->setData(Qt::UserRole, text);
item->setFlags(item->flags() | Qt::ItemIsUserCheckable);
item->setCheckState(Qt::Checked);
ui->macros->setCurrentItem(item);
ui->macroAdd->disconnect(addPulse);
ui->macroHelp->setVisible(false);
emit MacroAdded(QString::fromStdString(name));
}
void AdvSceneSwitcher::on_macroRemove_clicked()
@@ -81,9 +79,11 @@ void AdvSceneSwitcher::on_macroRemove_clicked()
if (!item) {
return;
}
QString name;
{
std::lock_guard<std::mutex> lock(switcher->m);
int idx = ui->macros->currentRow();
QString::fromStdString(switcher->macros[idx].Name());
switcher->macros.erase(switcher->macros.begin() + idx);
}
@@ -92,6 +92,7 @@ void AdvSceneSwitcher::on_macroRemove_clicked()
if (ui->macros->count() == 0) {
ui->macroHelp->setVisible(true);
}
emit MacroRemoved(name);
}
void AdvSceneSwitcher::on_macroUp_clicked()
@@ -147,12 +148,17 @@ void AdvSceneSwitcher::on_macroName_editingFinished()
if (nameValid) {
macro->SetName(newName.toUtf8().constData());
QListWidgetItem *item = ui->macros->currentItem();
item->setData(Qt::UserRole, newName);
// Don't trigger itemChanged()
// pause state remains as is
ui->macros->blockSignals(true);
item->setText(newName);
ui->macros->blockSignals(false);
} else {
ui->macroName->setText(oldName);
}
}
emit MacroRenamed(oldName, newName);
}
void AdvSceneSwitcher::SetEditMacro(Macro &m)
@@ -196,22 +202,14 @@ void AdvSceneSwitcher::SetEditMacro(Macro &m)
Macro *AdvSceneSwitcher::getSelectedMacro()
{
Macro *macro = nullptr;
QListWidgetItem *item = ui->macros->currentItem();
if (!item) {
return macro;
return nullptr;
}
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;
QString name = item->text();
return GetMacroByQString(name);
}
void AdvSceneSwitcher::on_macros_currentRowChanged(int idx)
@@ -226,13 +224,26 @@ void AdvSceneSwitcher::on_macros_currentRowChanged(int idx)
}
QListWidgetItem *item = ui->macros->item(idx);
QString macroName = item->data(Qt::UserRole).toString();
QString macroName = item->text();
for (auto &m : switcher->macros) {
if (macroName.compare(m.Name().c_str()) == 0) {
SetEditMacro(m);
break;
}
auto macro = GetMacroByQString(macroName);
if (macro) {
SetEditMacro(*macro);
}
}
void AdvSceneSwitcher::on_macros_itemChanged(QListWidgetItem *item)
{
if (loading) {
return;
}
std::lock_guard<std::mutex> lock(switcher->m);
QString name = item->text();
auto m = GetMacroByQString(name);
if (m) {
m->SetPaused(item->checkState() != Qt::Checked);
}
}
@@ -243,10 +254,18 @@ void AdvSceneSwitcher::setupMacroTab()
QListWidgetItem *item = new QListWidgetItem(text, ui->macros);
item->setData(Qt::UserRole, text);
item->setFlags(item->flags() | Qt::ItemIsUserCheckable);
if (m.Paused()) {
item->setCheckState(Qt::Unchecked);
} else {
item->setCheckState(Qt::Checked);
}
}
if (switcher->macros.size() == 0) {
addPulse = PulseWidget(ui->macroAdd, QColor(Qt::green));
if (!switcher->disableHints) {
addPulse = PulseWidget(ui->macroAdd, QColor(Qt::green));
}
ui->macroHelp->setVisible(true);
} else {
ui->macroHelp->setVisible(false);
@@ -286,5 +305,7 @@ void AdvSceneSwitcher::copyMacro()
QString text = QString::fromStdString(name);
QListWidgetItem *item = new QListWidgetItem(text, ui->macros);
item->setData(Qt::UserRole, text);
item->setFlags(item->flags() | Qt::ItemIsUserCheckable);
item->setCheckState(Qt::Checked);
ui->macros->setCurrentItem(item);
}

View File

@@ -2,7 +2,10 @@
#include "headers/macro-action-edit.hpp"
#include "headers/macro-condition-edit.hpp"
#include "headers/macro-action-switch-scene.hpp"
#include "headers/macro-action-scene-switch.hpp"
#include <limits>
#undef max
const std::map<LogicType, LogicTypeInfo> MacroCondition::logicTypes = {
{LogicType::NONE, {"AdvSceneSwitcher.logic.none"}},
@@ -14,15 +17,26 @@ const std::map<LogicType, LogicTypeInfo> MacroCondition::logicTypes = {
{LogicType::ROOT_NOT, {"AdvSceneSwitcher.logic.not"}},
};
Macro::Macro(std::string name) : _name(name) {}
Macro::Macro(const std::string &name)
{
SetupHotkeys();
SetName(name);
}
Macro::~Macro() {}
Macro::~Macro()
{
ClearHotkeys();
}
bool Macro::CeckMatch()
{
_matched = false;
for (auto &c : _conditions) {
bool cond = c->CheckCondition();
if (!cond) {
c->ResetDuration();
}
cond = cond && c->DurationReached();
switch (c->GetLogicType()) {
case LogicType::NONE:
@@ -55,6 +69,21 @@ bool Macro::CeckMatch()
_name.c_str());
break;
}
vblog(LOG_INFO, "condition %s returned %d", c->GetId().c_str(),
cond);
}
vblog(LOG_INFO, "Macro %s returned %d", _name.c_str(), _matched);
// Condition checks shall still be run even if macro is paused.
// Otherwise conditions might behave in unexpected ways when resuming.
//
// For example, audio could immediately match after unpause, when it
// matched before it was paused due to timers not being updated.
if (_paused) {
vblog(LOG_INFO, "Macro %s is paused", _name.c_str());
_matched = false;
return false;
}
return _matched;
@@ -70,13 +99,29 @@ bool Macro::PerformAction()
return false;
}
}
if (ret && _count != std::numeric_limits<int>::max()) {
_count++;
}
return ret;
}
void Macro::SetName(const std::string &name)
{
_name = name;
SetHotkeysDesc();
}
bool Macro::Save(obs_data_t *obj)
{
obs_data_set_string(obj, "name", _name.c_str());
obs_data_set_bool(obj, "pause", _paused);
obs_data_array_t *pauseHotkey = obs_hotkey_save(_pauseHotkey);
obs_data_set_array(obj, "pauseHotkey", pauseHotkey);
obs_data_array_release(pauseHotkey);
obs_data_array_t *unpauseHotkey = obs_hotkey_save(_unpauseHotkey);
obs_data_set_array(obj, "unpauseHotkey", unpauseHotkey);
obs_data_array_release(unpauseHotkey);
obs_data_array_t *conditions = obs_data_array_create();
for (auto &c : _conditions) {
@@ -142,15 +187,27 @@ void setValidLogic(MacroCondition *c, bool root, std::string name)
bool Macro::Load(obs_data_t *obj)
{
_name = obs_data_get_string(obj, "name");
bool root = true;
_paused = obs_data_get_bool(obj, "pause");
obs_data_array_t *pauseHotkey = obs_data_get_array(obj, "pauseHotkey");
obs_hotkey_load(_pauseHotkey, pauseHotkey);
obs_data_array_release(pauseHotkey);
obs_data_array_t *unpauseHotkey =
obs_data_get_array(obj, "unpauseHotkey");
obs_hotkey_load(_unpauseHotkey, unpauseHotkey);
obs_data_array_release(unpauseHotkey);
SetHotkeysDesc();
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");
std::string id = obs_data_get_string(array_obj, "id");
auto newEntry = MacroConditionFactory::Create(id);
if (newEntry) {
@@ -160,8 +217,8 @@ bool Macro::Load(obs_data_t *obj)
setValidLogic(c, root, _name);
} else {
blog(LOG_WARNING,
"discarding condition entry with unkown id (%d) for macro %s",
id, _name.c_str());
"discarding condition entry with unkown id (%s) for macro %s",
id.c_str(), _name.c_str());
}
obs_data_release(array_obj);
@@ -175,7 +232,7 @@ bool Macro::Load(obs_data_t *obj)
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");
std::string id = obs_data_get_string(array_obj, "id");
auto newEntry = MacroActionFactory::Create(id);
if (newEntry) {
@@ -183,44 +240,154 @@ bool Macro::Load(obs_data_t *obj)
_actions.back()->Load(array_obj);
} else {
blog(LOG_WARNING,
"discarding action entry with unkown id (%d) for macro %s",
id, _name.c_str());
"discarding action entry with unkown id (%s) for macro %s",
id.c_str(), _name.c_str());
}
obs_data_release(array_obj);
}
obs_data_array_release(actions);
return true;
}
void Macro::ResolveMacroRef()
{
for (auto &c : _conditions) {
MacroRefCondition *ref =
dynamic_cast<MacroRefCondition *>(c.get());
if (ref) {
ref->_macro.UpdateRef();
}
}
for (auto &a : _actions) {
MacroRefAction *ref = dynamic_cast<MacroRefAction *>(a.get());
if (ref) {
ref->_macro.UpdateRef();
}
}
}
bool Macro::SwitchesScene()
{
MacroActionSwitchScene temp;
auto sceneSwitchId = temp.GetId();
for (auto &a : _actions) {
if (a->GetId() == temp.GetId()) {
if (a->GetId() == sceneSwitchId) {
return true;
}
}
return false;
}
static void pauseCB(void *data, obs_hotkey_id id, obs_hotkey_t *hotkey,
bool pressed)
{
UNUSED_PARAMETER(id);
UNUSED_PARAMETER(data);
UNUSED_PARAMETER(hotkey);
if (pressed) {
auto m = static_cast<Macro *>(data);
m->SetPaused(true);
}
}
static void unpauseCB(void *data, obs_hotkey_id id, obs_hotkey_t *hotkey,
bool pressed)
{
UNUSED_PARAMETER(id);
UNUSED_PARAMETER(data);
UNUSED_PARAMETER(hotkey);
if (pressed) {
auto m = static_cast<Macro *>(data);
m->SetPaused(false);
}
}
static int macroHotkeyID = 0;
void Macro::SetupHotkeys()
{
if (_pauseHotkey != OBS_INVALID_HOTKEY_ID ||
_unpauseHotkey != OBS_INVALID_HOTKEY_ID) {
ClearHotkeys();
}
macroHotkeyID++;
std::string hotkeyName =
"macro_pause_hotkey_" + std::to_string(macroHotkeyID);
QString format{obs_module_text("AdvSceneSwitcher.hotkey.macro.pause")};
QString hotkeyDesc = format.arg(QString::fromStdString(_name));
_pauseHotkey = obs_hotkey_register_frontend(
hotkeyName.c_str(), hotkeyDesc.toStdString().c_str(), pauseCB,
this);
macroHotkeyID++;
hotkeyName = "macro_pause_hotkey_" + _name;
format = {obs_module_text("AdvSceneSwitcher.hotkey.macro.unpause")};
hotkeyDesc = format.arg(QString::fromStdString(_name));
_unpauseHotkey = obs_hotkey_register_frontend(
hotkeyName.c_str(), hotkeyDesc.toStdString().c_str(), unpauseCB,
this);
}
void Macro::ClearHotkeys()
{
obs_hotkey_unregister(_pauseHotkey);
obs_hotkey_unregister(_unpauseHotkey);
}
void Macro::SetHotkeysDesc()
{
QString format{obs_module_text("AdvSceneSwitcher.hotkey.macro.pause")};
QString hotkeyDesc = format.arg(QString::fromStdString(_name));
obs_hotkey_set_description(_pauseHotkey,
hotkeyDesc.toStdString().c_str());
format = {obs_module_text("AdvSceneSwitcher.hotkey.macro.unpause")};
hotkeyDesc = format.arg(QString::fromStdString(_name));
obs_hotkey_set_description(_unpauseHotkey,
hotkeyDesc.toStdString().c_str());
}
bool MacroCondition::Save(obs_data_t *obj)
{
obs_data_set_int(obj, "id", GetId());
obs_data_set_string(obj, "id", GetId().c_str());
obs_data_set_int(obj, "logic", static_cast<int>(_logic));
_duration.Save(obj);
return true;
}
bool MacroCondition::Load(obs_data_t *obj)
{
_logic = static_cast<LogicType>(obs_data_get_int(obj, "logic"));
_duration.Load(obj);
return true;
}
void MacroCondition::SetDurationConstraint(const DurationConstraint &dur)
{
_duration = dur;
}
void MacroCondition::SetDurationCondition(DurationCondition cond)
{
_duration.SetCondition(cond);
}
void MacroCondition::SetDurationUnit(DurationUnit u)
{
_duration.SetUnit(u);
}
void MacroCondition::SetDuration(double seconds)
{
_duration.SetValue(seconds);
}
bool MacroAction::Save(obs_data_t *obj)
{
obs_data_set_int(obj, "id", GetId());
obs_data_set_string(obj, "id", GetId().c_str());
return true;
}
@@ -232,7 +399,7 @@ bool MacroAction::Load(obs_data_t *obj)
void MacroAction::LogAction()
{
blog(LOG_INFO, "performed action %d", GetId());
vblog(LOG_INFO, "performed action %s", GetId().c_str());
}
void SwitcherData::saveMacros(obs_data_t *obj)
@@ -250,8 +417,71 @@ void SwitcherData::saveMacros(obs_data_t *obj)
obs_data_array_release(macroArray);
}
// Temporary helper functions to convert old settings format to new one
static std::unordered_map<int, std::string> actionIntToActionString = {
{2, "audio"}, {4, "recording"}, {5, "replay_buffer"}, {6, "run"},
{3, "streaming"}, {0, "scene_switch"}, {1, "wait"},
};
static void replaceActionIds(obs_data_t *obj)
{
obs_data_array_t *actions = obs_data_get_array(obj, "actions");
size_t 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);
auto oldId = obs_data_get_int(array_obj, "id");
obs_data_set_string(array_obj, "id",
actionIntToActionString[oldId].c_str());
obs_data_release(array_obj);
}
obs_data_array_release(actions);
}
static std::unordered_map<int, std::string> conditionIntToConditionString = {
{3, "audio"}, {4, "file"}, {10, "idle"}, {5, "media"},
{11, "plugin_state"}, {9, "process"}, {8, "recording"}, {2, "region"},
{0, "scene"}, {7, "streaming"}, {6, "video"}, {1, "window"},
};
static void replaceConditionIds(obs_data_t *obj)
{
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);
auto oldId = obs_data_get_int(array_obj, "id");
obs_data_set_string(
array_obj, "id",
conditionIntToConditionString[oldId].c_str());
obs_data_release(array_obj);
}
obs_data_array_release(conditions);
}
static void convertOldMacroIdsToString(obs_data_t *obj)
{
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);
replaceActionIds(array_obj);
replaceConditionIds(array_obj);
obs_data_release(array_obj);
}
obs_data_array_release(macroArray);
}
void SwitcherData::loadMacros(obs_data_t *obj)
{
// TODO: Remove conversion helper in future version
std::string previousVersion = obs_data_get_string(obj, "version");
if (previousVersion == "2ce0b35921be892c987c7dbb5fc90db38f15f0a6") {
convertOldMacroIdsToString(obj);
}
macros.clear();
obs_data_array_t *macroArray = obs_data_get_array(obj, "macros");
@@ -264,6 +494,10 @@ void SwitcherData::loadMacros(obs_data_t *obj)
obs_data_release(array_obj);
}
obs_data_array_release(macroArray);
for (auto &m : macros) {
m.ResolveMacroRef();
}
}
bool SwitcherData::checkMacros()
@@ -286,7 +520,7 @@ bool SwitcherData::runMacros()
{
for (auto &m : macros) {
if (m.Matched()) {
blog(LOG_INFO, "running macro: %s", m.Name().c_str());
vblog(LOG_INFO, "running macro: %s", m.Name().c_str());
if (!m.PerformAction()) {
blog(LOG_WARNING, "abort macro: %s",
m.Name().c_str());
@@ -296,3 +530,69 @@ bool SwitcherData::runMacros()
}
return true;
}
Macro *GetMacroByName(const char *name)
{
for (auto &m : switcher->macros) {
if (m.Name() == name) {
return &m;
}
}
return nullptr;
}
Macro *GetMacroByQString(const QString &name)
{
return GetMacroByName(name.toUtf8().constData());
}
MacroRef::MacroRef(std::string name) : _name(name)
{
UpdateRef();
}
void MacroRef::UpdateRef()
{
_ref = GetMacroByName(_name.c_str());
}
void MacroRef::UpdateRef(std::string newName)
{
_name = newName;
UpdateRef();
}
void MacroRef::UpdateRef(QString newName)
{
_name = newName.toStdString();
UpdateRef();
}
void MacroRef::Save(obs_data_t *obj)
{
if (_ref) {
obs_data_set_string(obj, "macro", _ref->Name().c_str());
}
}
void MacroRef::Load(obs_data_t *obj)
{
_name = obs_data_get_string(obj, "macro");
UpdateRef();
}
Macro *MacroRef::get()
{
return _ref;
}
Macro *MacroRef::operator->()
{
return _ref;
}
void MacroRefCondition::ResolveMacroRef()
{
_macro.UpdateRef();
}
void MacroRefAction::ResolveMacroRef()
{
_macro.UpdateRef();
}

View File

@@ -17,7 +17,6 @@ void GetWindowList(std::vector<std::string> &windows)
(__bridge NSMutableArray *)CGWindowListCopyWindowInfo(
kCGWindowListOptionAll, kCGNullWindowID);
for (NSDictionary *app in apps) {
// Construct string from NSString accounting for nil
std::string name([[app objectForKey:@"kCGWindowName"]
UTF8String],
[[app objectForKey:@"kCGWindowName"]
@@ -30,53 +29,26 @@ void GetWindowList(std::vector<std::string> &windows)
lengthOfBytesUsingEncoding:
NSUTF8StringEncoding]);
// Check if name exists
if (!name.empty() &&
find(windows.begin(), windows.end(), name) ==
windows.end())
windows.emplace_back(name);
// Check if owner exists
else if (!owner.empty() &&
find(windows.begin(), windows.end(), owner) ==
windows.end())
if (!owner.empty() &&
find(windows.begin(), windows.end(), owner) ==
windows.end())
windows.emplace_back(owner);
}
}
}
// Overloaded
void GetWindowList(QStringList &windows)
{
windows.clear();
@autoreleasepool {
NSMutableArray *apps =
(__bridge NSMutableArray *)CGWindowListCopyWindowInfo(
kCGWindowListOptionAll, kCGNullWindowID);
for (NSDictionary *app in apps) {
// Construct string from NSString accounting for nil
std::string name([[app objectForKey:@"kCGWindowName"]
UTF8String],
[[app objectForKey:@"kCGWindowName"]
lengthOfBytesUsingEncoding:
NSUTF8StringEncoding]);
std::string owner(
[[app objectForKey:@"kCGWindowOwnerName"]
UTF8String],
[[app objectForKey:@"kCGWindowOwnerName"]
lengthOfBytesUsingEncoding:
NSUTF8StringEncoding]);
// Check if name exists
if (!name.empty() &&
!windows.contains(QString::fromStdString(name)))
windows << QString::fromStdString(name);
// Check if owner exists
else if (!owner.empty() &&
!windows.contains(
QString::fromStdString(name)))
windows << QString::fromStdString(owner);
}
std::vector<std::string> temp;
GetWindowList(temp);
for (auto &w : temp) {
windows << QString::fromStdString(w);
}
}
@@ -94,7 +66,6 @@ void GetCurrentWindowTitle(std::string &title)
[[app objectForKey:@"kCGWindowLayer"] intValue];
// True if window is frontmost
if (layer == 0) {
// Construct string from NSString accounting for nil
std::string name(
[[app objectForKey:@"kCGWindowName"]
UTF8String],
@@ -130,23 +101,68 @@ std::pair<int, int> getCursorPos()
return pos;
}
// TODO:
// not implemented on MacOS as I cannot test it
bool isMaximized(std::string &title)
bool isWindowOriginOnScreen(NSDictionary *app, NSScreen *screen,
bool fullscreen = false)
{
return false;
NSArray *screens = [NSScreen screens];
NSRect mainScreenFrame = [screens[0] frame];
NSRect screenFrame;
if (fullscreen) {
screenFrame = [screen frame];
} else {
screenFrame = [screen visibleFrame];
}
NSRect windowBounds;
CGRectMakeWithDictionaryRepresentation(
(CFDictionaryRef)[app objectForKey:@"kCGWindowBounds"],
&windowBounds);
return (windowBounds.origin.x == screenFrame.origin.x &&
(mainScreenFrame.size.height - screenFrame.size.height -
windowBounds.origin.y ==
screenFrame.origin.y));
}
bool isFullscreen(std::string &title)
bool isWindowMaximizedOnScreen(NSDictionary *app, NSScreen *screen)
{
double maximizedTolerance = 0.99;
NSRect screenFrame = [screen frame];
NSRect windowBounds;
CGRectMakeWithDictionaryRepresentation(
(CFDictionaryRef)[app objectForKey:@"kCGWindowBounds"],
&windowBounds);
int sumX = windowBounds.origin.x + windowBounds.size.width;
int sumY = windowBounds.origin.y + windowBounds.size.height;
// Return false if window spans over multiple screens
if (sumX > screenFrame.size.width) {
return false;
}
if (sumY > screenFrame.size.height) {
return false;
}
return ((double)sumX / (double)screenFrame.size.width) >
maximizedTolerance &&
((double)sumY / (double)screenFrame.size.height) >
maximizedTolerance;
}
bool nameMachesPattern(std::string windowName, std::string pattern)
{
return QString::fromStdString(windowName)
.contains(QRegularExpression(QString::fromStdString(pattern)));
}
bool isMaximized(const std::string &title)
{
// Check for match
@autoreleasepool {
NSArray *screens = [NSScreen screens];
NSMutableArray *apps =
(__bridge NSMutableArray *)CGWindowListCopyWindowInfo(
kCGWindowListOptionAll, kCGNullWindowID);
for (NSDictionary *app in apps) {
// Construct string from NSString accounting for nil
std::string name([[app objectForKey:@"kCGWindowName"]
UTF8String],
[[app objectForKey:@"kCGWindowName"]
@@ -159,51 +175,69 @@ bool isFullscreen(std::string &title)
lengthOfBytesUsingEncoding:
NSUTF8StringEncoding]);
// True if switch equals app
bool equals = (title == name || title == owner);
// True if switch matches app
bool matches = (QString::fromStdString(name).contains(
QRegularExpression(
QString::fromStdString(
title))) ||
QString::fromStdString(owner).contains(
QRegularExpression(
QString::fromStdString(
title))));
// If found, check if fullscreen
bool matches = nameMachesPattern(name, title) ||
nameMachesPattern(owner, title);
if (equals || matches) {
// Get window bounds
NSRect bounds;
CGRectMakeWithDictionaryRepresentation(
(CFDictionaryRef)[app
objectForKey:@"kCGWindowBounds"],
&bounds);
// Compare to screen bounds
for (NSScreen *screen in screens) {
NSRect frame = [screen visibleFrame];
if (isWindowOriginOnScreen(app,
screen) &&
isWindowMaximizedOnScreen(app,
screen)) {
return true;
}
}
}
}
}
return false;
}
// True if flipped window origin equals screen origin
bool origin =
(bounds.origin.x ==
frame.origin.x &&
([screens[0] visibleFrame]
.size.height -
frame.size.height -
bounds.origin.y ==
frame.origin.y));
// True if window size equals screen size
bool size = NSEqualSizes(bounds.size,
frame.size);
bool isWindowFullscreenOnScreen(NSDictionary *app, NSScreen *screen)
{
NSRect screenFrame = [screen frame];
NSRect windowBounds;
CGRectMakeWithDictionaryRepresentation(
(CFDictionaryRef)[app objectForKey:@"kCGWindowBounds"],
&windowBounds);
if (origin && size)
return NSEqualSizes(windowBounds.size, screenFrame.size);
}
bool isFullscreen(const std::string &title)
{
@autoreleasepool {
NSArray *screens = [NSScreen screens];
NSMutableArray *apps =
(__bridge NSMutableArray *)CGWindowListCopyWindowInfo(
kCGWindowListOptionAll, kCGNullWindowID);
for (NSDictionary *app in apps) {
std::string name([[app objectForKey:@"kCGWindowName"]
UTF8String],
[[app objectForKey:@"kCGWindowName"]
lengthOfBytesUsingEncoding:
NSUTF8StringEncoding]);
std::string owner(
[[app objectForKey:@"kCGWindowOwnerName"]
UTF8String],
[[app objectForKey:@"kCGWindowOwnerName"]
lengthOfBytesUsingEncoding:
NSUTF8StringEncoding]);
bool equals = (title == name || title == owner);
bool matches = nameMachesPattern(name, title) ||
nameMachesPattern(owner, title);
if (equals || matches) {
for (NSScreen *screen in screens) {
if (isWindowOriginOnScreen(app, screen,
true) &&
isWindowFullscreenOnScreen(app,
screen))
return true;
}
}
}
}
return false;
}
@@ -247,3 +281,13 @@ bool isInFocus(const QString &executable)
return (equals || matches);
}
bool GetCurrentVirtualDesktop(long &desktop)
{
return false;
}
bool GetVirtualDesktopCount(long &ndesktops)
{
return false;
}

View File

@@ -499,7 +499,10 @@ void AdvSceneSwitcher::setupSceneGroupTab()
}
if (switcher->sceneGroups.size() == 0) {
addPulse = PulseWidget(ui->sceneGroupAdd, QColor(Qt::green));
if (!switcher->disableHints) {
addPulse = PulseWidget(ui->sceneGroupAdd,
QColor(Qt::green));
}
ui->sceneGroupHelp->setVisible(true);
} else {
ui->sceneGroupHelp->setVisible(false);

View File

@@ -347,7 +347,10 @@ void AdvSceneSwitcher::setupTriggerTab()
}
if (switcher->sceneTriggers.size() == 0) {
addPulse = PulseWidget(ui->triggerAdd, QColor(Qt::green));
if (!switcher->disableHints) {
addPulse =
PulseWidget(ui->triggerAdd, QColor(Qt::green));
}
ui->triggerHelp->setVisible(true);
} else {
ui->triggerHelp->setVisible(false);
@@ -381,7 +384,7 @@ void SceneTrigger::load(obs_data_t *obj)
static inline void populateTriggers(QComboBox *list)
{
AdvSceneSwitcher::addSelectionEntry(
addSelectionEntry(
list,
obs_module_text(
"AdvSceneSwitcher.sceneTriggerTab.sceneTriggerType.none"));
@@ -396,7 +399,7 @@ static inline void populateTriggers(QComboBox *list)
inline void populateActions(QComboBox *list)
{
AdvSceneSwitcher::addSelectionEntry(
addSelectionEntry(
list,
obs_module_text(
"AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.none"));
@@ -451,7 +454,7 @@ SceneTriggerWidget::SceneTriggerWidget(QWidget *parent, SceneTrigger *s)
populateTriggers(triggers);
populateActions(actions);
AdvSceneSwitcher::populateAudioSelection(audioSources);
populateAudioSelection(audioSources);
if (s) {
triggers->setCurrentIndex(static_cast<int>(s->triggerType));

View File

@@ -1,7 +1,9 @@
#include <QPropertyAnimation>
#include "headers/section.hpp"
#include "headers/utility.hpp"
#include <QPropertyAnimation>
#include <QEvent>
Section::Section(const int animationDuration, QWidget *parent)
: QWidget(parent), _animationDuration(animationDuration)
{
@@ -38,45 +40,74 @@ Section::Section(const int animationDuration, QWidget *parent)
connect(_toggleButton, &QToolButton::toggled, this, &Section::Collapse);
}
void Section::Collapse(bool collapsed)
void Section::Collapse(bool collapse)
{
_toggleButton->setChecked(collapsed);
_toggleButton->setArrowType(!collapsed ? Qt::ArrowType::DownArrow
: Qt::ArrowType::RightArrow);
_toggleAnimation->setDirection(!collapsed
_toggleButton->setChecked(collapse);
_toggleButton->setArrowType(!collapse ? Qt::ArrowType::DownArrow
: Qt::ArrowType::RightArrow);
_toggleAnimation->setDirection(!collapse
? QAbstractAnimation::Forward
: QAbstractAnimation::Backward);
_transitioning = true;
_collapsed = collapse;
_toggleAnimation->start();
}
void Section::SetContent(QWidget *w)
void Section::SetContent(QWidget *w, bool collapsed)
{
// Clean up previous content
if (_contentArea) {
auto oldLayout = _contentArea->layout();
if (oldLayout) {
clearLayout(oldLayout);
delete oldLayout;
}
}
CleanUpPreviousContent();
delete _contentArea;
delete _toggleAnimation;
// Setup contentArea
_contentArea = new QScrollArea(this);
_contentArea->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
// Start out collapsed
_contentArea->setStyleSheet("QScrollArea { border: none; }");
_contentArea->setMaximumHeight(0);
_contentArea->setMinimumHeight(0);
w->installEventFilter(this);
_content = w;
auto newLayout = new QVBoxLayout();
newLayout->setContentsMargins(0, 0, 0, 0);
newLayout->addWidget(w);
_contentArea->setLayout(newLayout);
_mainLayout->addWidget(_contentArea, 1, 0, 1, 3);
// Animation Setup
_headerHeight = sizeHint().height() - _contentArea->maximumHeight();
_contentHeight = newLayout->sizeHint().height();
if (collapsed) {
this->setMinimumHeight(_headerHeight);
_contentArea->setMaximumHeight(0);
} else {
this->setMinimumHeight(_headerHeight + _contentHeight);
_contentArea->setMaximumHeight(_contentHeight);
}
SetupAnimations();
Collapse(collapsed);
}
void Section::AddHeaderWidget(QWidget *w)
{
_headerWidgetLayout->addWidget(w);
}
bool Section::eventFilter(QObject *obj, QEvent *event)
{
if (event->type() == QEvent::Resize && !_transitioning && !_collapsed) {
_contentHeight = _content->sizeHint().height();
setMaximumHeight(_headerHeight + _contentHeight);
setMinimumHeight(_headerHeight + _contentHeight);
_contentArea->setMaximumHeight(_contentHeight);
SetupAnimations();
}
return QObject::eventFilter(obj, event);
}
void Section::SetupAnimations()
{
delete _toggleAnimation;
_toggleAnimation = new QParallelAnimationGroup(this);
_toggleAnimation->addAnimation(
new QPropertyAnimation(this, "minimumHeight"));
@@ -85,17 +116,13 @@ void Section::SetContent(QWidget *w)
_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);
SectionAnimation->setStartValue(_headerHeight);
SectionAnimation->setEndValue(_headerHeight + _contentHeight);
}
QPropertyAnimation *contentAnimation =
@@ -103,10 +130,24 @@ void Section::SetContent(QWidget *w)
_toggleAnimation->animationCount() - 1));
contentAnimation->setDuration(_animationDuration);
contentAnimation->setStartValue(0);
contentAnimation->setEndValue(contentHeight);
contentAnimation->setEndValue(_contentHeight);
QWidget::connect(_toggleAnimation, SIGNAL(finished()), this,
SLOT(AnimationFinished()));
}
void Section::AddHeaderWidget(QWidget *w)
void Section::CleanUpPreviousContent()
{
_headerWidgetLayout->addWidget(w);
if (_contentArea) {
auto oldLayout = _contentArea->layout();
if (oldLayout) {
clearLayout(oldLayout);
delete oldLayout;
}
}
}
void Section::AnimationFinished()
{
_transitioning = false;
}

View File

@@ -224,7 +224,9 @@ void AdvSceneSwitcher::setupAudioTab()
}
if (switcher->audioSwitches.size() == 0) {
addPulse = PulseWidget(ui->audioAdd, QColor(Qt::green));
if (!switcher->disableHints) {
addPulse = PulseWidget(ui->audioAdd, QColor(Qt::green));
}
ui->audioHelp->setVisible(true);
} else {
ui->audioHelp->setVisible(false);
@@ -447,7 +449,7 @@ AudioSwitchWidget::AudioSwitchWidget(QWidget *parent, AudioSwitch *s)
QWidget::connect(ignoreInactiveSource, SIGNAL(stateChanged(int)), this,
SLOT(IgnoreInactiveChanged(int)));
AdvSceneSwitcher::populateAudioSelection(audioSources);
populateAudioSelection(audioSources);
populateConditionSelection(condition);
if (s) {

View File

@@ -187,7 +187,10 @@ void AdvSceneSwitcher::setupExecutableTab()
}
if (switcher->executableSwitches.size() == 0) {
addPulse = PulseWidget(ui->executableAdd, QColor(Qt::green));
if (!switcher->disableHints) {
addPulse = PulseWidget(ui->executableAdd,
QColor(Qt::green));
}
ui->exeHelp->setVisible(true);
} else {
ui->exeHelp->setVisible(false);
@@ -223,7 +226,7 @@ ExecutableSwitchWidget::ExecutableSwitchWidget(QWidget *parent,
QWidget::connect(requiresFocus, SIGNAL(stateChanged(int)), this,
SLOT(FocusChanged(int)));
AdvSceneSwitcher::populateProcessSelection(processes);
populateProcessSelection(processes);
processes->setEditable(true);
processes->setMaxVisibleItems(20);

View File

@@ -101,7 +101,7 @@ void SwitcherData::writeSceneInfoToFile()
obs_source_release(currentSource);
}
void SwitcherData::writeToStatusFile(QString msg)
void SwitcherData::writeToStatusFile(const QString &msg)
{
if (!fileIO.writeEnabled || fileIO.writePath.empty()) {
return;
@@ -415,7 +415,9 @@ void AdvSceneSwitcher::setupFileTab()
}
if (switcher->fileSwitches.size() == 0) {
addPulse = PulseWidget(ui->fileAdd, QColor(Qt::green));
if (!switcher->disableHints) {
addPulse = PulseWidget(ui->fileAdd, QColor(Qt::green));
}
ui->fileHelp->setVisible(true);
} else {
ui->fileHelp->setVisible(false);

View File

@@ -228,10 +228,9 @@ SwitchWidget::SwitchWidget(QWidget *parent, SceneSwitcherEntry *s,
SIGNAL(SceneGroupRenamed(const QString &, const QString &)),
this, SLOT(SceneGroupRename(const QString &, const QString &)));
AdvSceneSwitcher::populateSceneSelection(scenes, usePreviousScene,
addSceneGroup);
AdvSceneSwitcher::populateTransitionSelection(transitions,
addCurrentTransition);
populateSceneSelection(scenes, usePreviousScene, addSceneGroup,
&switcher->sceneGroups);
populateTransitionSelection(transitions, addCurrentTransition);
switchData = s;
showSwitchData();

View File

@@ -244,7 +244,9 @@ void AdvSceneSwitcher::setupMediaTab()
}
if (switcher->mediaSwitches.size() == 0) {
addPulse = PulseWidget(ui->mediaAdd, QColor(Qt::green));
if (!switcher->disableHints) {
addPulse = PulseWidget(ui->mediaAdd, QColor(Qt::green));
}
ui->mediaHelp->setVisible(true);
} else {
ui->mediaHelp->setVisible(false);
@@ -458,7 +460,7 @@ MediaSwitchWidget::MediaSwitchWidget(QWidget *parent, MediaSwitch *s)
QWidget::connect(time, SIGNAL(valueChanged(int)), this,
SLOT(TimeChanged(int)));
AdvSceneSwitcher::populateMediaSelection(mediaSources);
populateMediaSelection(mediaSources);
populateMediaStates(states);
populateTimeRestrictions(timeRestrictions);

View File

@@ -17,13 +17,16 @@ Most of this code is based on https://github.com/Palakis/obs-websocket
#define PARAM_CLIENT_ENABLE "ClientEnabled"
#define PARAM_CLIENT_PORT "ClientPort"
#define PARAM_ADDRESS "Address"
#define PARAM_CLIENT_SENDALL "SendAll"
#define PARAM_CLIENT_SEND_SCENE_CHANGE "SendSceneChange"
#define PARAM_CLIENT_SEND_SCENE_CHANGE_ALL "SendSceneChangeAll"
#define PARAM_CLIENT_SENDPREVIEW "SendPreview"
#define RECONNECT_DELAY 10
#define SCENE_ENTRY "scene"
#define TRANSITION_ENTRY "transition"
#define TRANSITION_DURATION "duration"
#define SET_PREVIEW "preview"
using websocketpp::lib::placeholders::_1;
using websocketpp::lib::placeholders::_2;
@@ -36,7 +39,9 @@ NetworkConfig::NetworkConfig()
ClientEnabled(false),
Address(""),
ClientPort(55555),
SendAll(true)
SendSceneChange(true),
SendSceneChangeAll(true),
SendPreview(true)
{
}
@@ -51,7 +56,11 @@ void NetworkConfig::Load(obs_data_t *obj)
ClientEnabled = obs_data_get_bool(obj, PARAM_CLIENT_ENABLE);
Address = obs_data_get_string(obj, PARAM_ADDRESS);
ClientPort = obs_data_get_int(obj, PARAM_CLIENT_PORT);
SendAll = obs_data_get_bool(obj, PARAM_CLIENT_SENDALL);
SendSceneChange =
obs_data_get_bool(obj, PARAM_CLIENT_SEND_SCENE_CHANGE);
SendSceneChangeAll =
obs_data_get_bool(obj, PARAM_CLIENT_SEND_SCENE_CHANGE_ALL);
SendPreview = obs_data_get_bool(obj, PARAM_CLIENT_SENDPREVIEW);
}
void NetworkConfig::Save(obs_data_t *obj)
@@ -63,7 +72,10 @@ void NetworkConfig::Save(obs_data_t *obj)
obs_data_set_bool(obj, PARAM_CLIENT_ENABLE, ClientEnabled);
obs_data_set_string(obj, PARAM_ADDRESS, Address.c_str());
obs_data_set_int(obj, PARAM_CLIENT_PORT, ClientPort);
obs_data_set_bool(obj, PARAM_CLIENT_SENDALL, SendAll);
obs_data_set_bool(obj, PARAM_CLIENT_SEND_SCENE_CHANGE, SendSceneChange);
obs_data_set_bool(obj, PARAM_CLIENT_SEND_SCENE_CHANGE_ALL,
SendSceneChangeAll);
obs_data_set_bool(obj, PARAM_CLIENT_SENDPREVIEW, SendPreview);
}
void NetworkConfig::SetDefaults(obs_data_t *obj)
@@ -75,7 +87,11 @@ void NetworkConfig::SetDefaults(obs_data_t *obj)
obs_data_set_default_bool(obj, PARAM_CLIENT_ENABLE, ClientEnabled);
obs_data_set_default_string(obj, PARAM_ADDRESS, Address.c_str());
obs_data_set_default_int(obj, PARAM_CLIENT_PORT, ClientPort);
obs_data_set_default_bool(obj, PARAM_CLIENT_SENDALL, SendAll);
obs_data_set_default_bool(obj, PARAM_CLIENT_SEND_SCENE_CHANGE,
SendSceneChange);
obs_data_set_default_bool(obj, PARAM_CLIENT_SEND_SCENE_CHANGE_ALL,
SendSceneChangeAll);
obs_data_set_default_bool(obj, PARAM_CLIENT_SENDPREVIEW, SendPreview);
}
std::string NetworkConfig::GetClientUri()
@@ -83,6 +99,21 @@ std::string NetworkConfig::GetClientUri()
return "ws://" + Address + ":" + std::to_string(ClientPort);
}
bool NetworkConfig::ShouldSendSceneChange()
{
return ServerEnabled && SendSceneChange;
}
bool NetworkConfig::ShouldSendFrontendSceneChange()
{
return ShouldSendSceneChange() && SendSceneChangeAll;
}
bool NetworkConfig::ShouldSendPrviewSceneChange()
{
return ServerEnabled && SendPreview;
}
WSServer::WSServer()
: QObject(nullptr), _connections(), _clMutex(QMutex::Recursive)
{
@@ -192,7 +223,7 @@ void WSServer::stop()
blog(LOG_INFO, "server stopped successfully");
}
void WSServer::sendMessage(sceneSwitchInfo sceneSwitch)
void WSServer::sendMessage(sceneSwitchInfo sceneSwitch, bool preview)
{
if (!sceneSwitch.scene) {
return;
@@ -204,6 +235,7 @@ void WSServer::sendMessage(sceneSwitchInfo sceneSwitch)
obs_data_set_string(data, TRANSITION_ENTRY,
GetWeakSourceName(sceneSwitch.transition).c_str());
obs_data_set_int(data, TRANSITION_DURATION, sceneSwitch.duration);
obs_data_set_bool(data, SET_PREVIEW, preview);
std::string message = obs_data_get_json(data);
obs_data_release(data);
@@ -248,7 +280,8 @@ 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_DURATION)) {
!obs_data_has_user_value(data, TRANSITION_DURATION) ||
!obs_data_has_user_value(data, SET_PREVIEW)) {
return "missing request parameters";
}
@@ -256,6 +289,7 @@ std::string processMessage(std::string payload)
std::string transitionName =
obs_data_get_string(data, TRANSITION_ENTRY);
int duration = obs_data_get_int(data, TRANSITION_DURATION);
bool preview = obs_data_get_bool(data, SET_PREVIEW);
obs_data_release(data);
@@ -271,8 +305,11 @@ std::string processMessage(std::string payload)
ret += " - ignoring invalid transition: '" + transitionName +
"'";
}
switchScene({scene, transition, duration});
if (preview) {
switchPreviewScene(scene);
} else {
switchScene({scene, transition, duration});
}
return ret;
}
@@ -478,7 +515,12 @@ void AdvSceneSwitcher::setupNetworkTab()
ui->clientSettings->setChecked(switcher->networkConfig.ClientEnabled);
ui->clientHostname->setText(switcher->networkConfig.Address.c_str());
ui->clientPort->setValue(switcher->networkConfig.ClientPort);
ui->restrictSend->setChecked(!switcher->networkConfig.SendAll);
ui->sendSceneChange->setChecked(
switcher->networkConfig.SendSceneChange);
ui->restrictSend->setChecked(
!switcher->networkConfig.SendSceneChangeAll);
ui->sendPreview->setChecked(switcher->networkConfig.SendPreview);
ui->restrictSend->setDisabled(!switcher->networkConfig.SendSceneChange);
QTimer *statusTimer = new QTimer(this);
connect(statusTimer, SIGNAL(timeout()), this,
@@ -592,6 +634,17 @@ void AdvSceneSwitcher::on_clientPort_valueChanged(int value)
switcher->networkConfig.ClientPort = value;
}
void AdvSceneSwitcher::on_sendSceneChange_stateChanged(int state)
{
if (loading) {
return;
}
std::lock_guard<std::mutex> lock(switcher->m);
switcher->networkConfig.SendSceneChange = state;
ui->restrictSend->setDisabled(!state);
}
void AdvSceneSwitcher::on_restrictSend_stateChanged(int state)
{
if (loading) {
@@ -599,7 +652,17 @@ void AdvSceneSwitcher::on_restrictSend_stateChanged(int state)
}
std::lock_guard<std::mutex> lock(switcher->m);
switcher->networkConfig.SendAll = !state;
switcher->networkConfig.SendSceneChangeAll = !state;
}
void AdvSceneSwitcher::on_sendPreview_stateChanged(int state)
{
if (loading) {
return;
}
std::lock_guard<std::mutex> lock(switcher->m);
switcher->networkConfig.SendPreview = state;
}
void AdvSceneSwitcher::on_clientReconnect_clicked()

View File

@@ -222,7 +222,6 @@ void SwitcherData::savePauseSwitches(obs_data_t *obj)
obs_data_release(array_obj);
}
obs_data_set_array(obj, "pauseEntries", pauseScenesArray);
obs_data_set_int(obj, "oldPauseValuesImported", 1);
obs_data_array_release(pauseScenesArray);
}
@@ -265,7 +264,9 @@ void AdvSceneSwitcher::setupPauseTab()
}
if (switcher->pauseEntries.size() == 0) {
addPulse = PulseWidget(ui->pauseAdd, QColor(Qt::green));
if (!switcher->disableHints) {
addPulse = PulseWidget(ui->pauseAdd, QColor(Qt::green));
}
ui->pauseHelp->setVisible(true);
} else {
ui->pauseHelp->setVisible(false);
@@ -316,7 +317,7 @@ PauseEntryWidget::PauseEntryWidget(QWidget *parent, PauseEntry *s)
populatePauseTypes(pauseTypes);
populatePauseTargets(pauseTargets);
AdvSceneSwitcher::populateWindowSelection(windows);
populateWindowSelection(windows);
windows->setEditable(true);
windows->setMaxVisibleItems(20);

View File

@@ -125,15 +125,20 @@ void AdvSceneSwitcher::setupRandomTab()
}
if (switcher->randomSwitches.size() == 0) {
addPulse = PulseWidget(ui->randomAdd, QColor(Qt::green));
if (!switcher->disableHints) {
addPulse =
PulseWidget(ui->randomAdd, QColor(Qt::green));
}
ui->randomHelp->setVisible(true);
} else {
ui->randomHelp->setVisible(false);
}
if (switcher->switchIfNotMatching != RANDOM_SWITCH) {
PulseWidget(ui->randomDisabledWarning, QColor(Qt::red),
QColor(0, 0, 0, 0), "QLabel ");
if (!switcher->disableHints) {
PulseWidget(ui->randomDisabledWarning, QColor(Qt::red),
QColor(0, 0, 0, 0), "QLabel ");
}
} else {
ui->randomDisabledWarning->setVisible(false);
}

View File

@@ -245,7 +245,10 @@ void AdvSceneSwitcher::setupRegionTab()
}
if (switcher->screenRegionSwitches.size() == 0) {
addPulse = PulseWidget(ui->screenRegionAdd, QColor(Qt::green));
if (!switcher->disableHints) {
addPulse = PulseWidget(ui->screenRegionAdd,
QColor(Qt::green));
}
ui->regionHelp->setVisible(true);
} else {
ui->regionHelp->setVisible(false);
@@ -320,8 +323,8 @@ ScreenRegionWidget::ScreenRegionWidget(QWidget *parent, ScreenRegionSwitch *s)
QWidget::connect(maxY, SIGNAL(valueChanged(int)), this,
SLOT(MaxYChanged(int)));
AdvSceneSwitcher::populateSceneSelection(
excludeScenes, false, false, true,
populateSceneSelection(
excludeScenes, false, false, nullptr, true,
obs_module_text(
"AdvSceneSwitcher.screenRegionTab.excludeScenes.None"),
true);

View File

@@ -289,7 +289,10 @@ void AdvSceneSwitcher::setupSequenceTab()
}
if (switcher->sceneSequenceSwitches.size() == 0) {
addPulse = PulseWidget(ui->sceneSequenceAdd, QColor(Qt::green));
if (!switcher->disableHints) {
addPulse = PulseWidget(ui->sceneSequenceAdd,
QColor(Qt::green));
}
ui->sequenceHelp->setVisible(true);
} else {
ui->sequenceHelp->setVisible(false);
@@ -440,8 +443,6 @@ void SceneSequenceSwitch::prepareUninterruptibleMatch(
{
int dur = delay.seconds * 1000;
if (dur > 0) {
switcher->waitScene = obs_weak_source_get_source(currentScene);
obs_source_release(switcher->waitScene);
linger = dur;
}
}
@@ -588,7 +589,7 @@ SequenceWidget::SequenceWidget(QWidget *parent, SceneSequenceSwitch *s,
QWidget::connect(reduce, SIGNAL(clicked()), this,
SLOT(ReduceClicked()));
AdvSceneSwitcher::populateSceneSelection(startScenes, false);
populateSceneSelection(startScenes);
interruptible->setToolTip(obs_module_text(
"AdvSceneSwitcher.sceneSequenceTab.interruptibleHint"));

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