Compare commits

..

156 Commits
1.11.2 ... 1.14

Author SHA1 Message Date
WarmUpTill
7e3e621ae5 Log long condition checks 2021-07-03 09:12:30 -07:00
WarmUpTill
9dfbd266fc Set timeout for libcurl call 2021-07-03 09:12:30 -07:00
WarmUpTill
8d02f63c36 Adjust scene order condition to support current scene 2021-07-03 08:47:08 -07:00
WarmUpTill
f31c54fd72 Adjust scene condition to use SceneSelection 2021-07-03 08:47:08 -07:00
WarmUpTill
7719087065 Add TODO 2021-07-03 08:47:08 -07:00
WarmUpTill
0b175a41d1 Adjust scene transform action to support current scene 2021-07-03 08:47:08 -07:00
WarmUpTill
0824abc4f5 Adjust scene order action to support current scene 2021-07-03 08:47:08 -07:00
WarmUpTill
efef29603b Display all scene items when selecting "current scene" 2021-07-03 08:47:08 -07:00
WarmUpTill
034a1b0c93 Set current scene in handleSceneChange() and reuse it 2021-07-03 08:47:08 -07:00
WarmUpTill
86fefe1e9e Adjust scene visibility action to support current scene 2021-07-03 08:47:08 -07:00
WarmUpTill
0bc15b4c2d Add support for "current scene" to populateSceneSelection() 2021-07-03 08:47:08 -07:00
WarmUpTill
7cfa956b80 Move GetSceneGroupByName functions to scene-group.hpp 2021-07-03 08:47:08 -07:00
WarmUpTill
9670a01e1a Add scene selection class and widget 2021-07-03 08:47:08 -07:00
WarmUpTill
371f6daf5a Add macro condition "date" 2021-06-26 10:23:18 -07:00
WarmUpTill
1c141abd06 Fix plugin state action values being empty 2021-06-26 10:23:09 -07:00
WarmUpTill
13788c2dfa Fix warning 2021-06-26 10:23:09 -07:00
WarmUpTill
daf36365a8 Only use hardcoded libcurl paths as fallback 2021-06-26 10:23:09 -07:00
WarmUpTill
cea4df90b1 Check for invalid transition settings 2021-06-21 11:01:25 -07:00
WarmUpTill
80b824e0a7 Fix Macro slots not being connected properly
Problem was introduced when adding header info.

To avoid the issue in future forward signals of the main plugin window
via condition and action edit widgets instead of relying on parent()
being the main window.
2021-06-20 11:05:29 -07:00
WarmUpTill
0bfdc3890b Add macro action "file" 2021-06-19 12:30:47 -07:00
WarmUpTill
cc067de021 Expand macro action plugin-state to support changing no-match behaviour 2021-06-19 11:29:35 -07:00
WarmUpTill
57c0734ba6 Display more information about macros in collapsed state 2021-06-19 06:58:06 -07:00
WarmUpTill
913682e128 Add support for arguments for macro action "run" 2021-06-19 05:23:10 -07:00
WarmUpTill
92711c5798 Make cleaning of user input optional 2021-06-19 05:23:10 -07:00
WarmUpTill
80334088d0 Add option to only trigger window condition if foreground window changed 2021-06-19 05:22:50 -07:00
WarmUpTill
bdd9d12257 Add setPreconditions()
The idea is to avoid repeated calls to the same functions which lead to
the same outcome, but just calling them once in the precondition checks.

For now move repeated calls of GetCurrentWindowTitle() and ignoreWindow
checks to this function.
2021-06-19 05:22:50 -07:00
WarmUpTill
decee4647b Add macro action 'scene_transform' 2021-06-18 11:15:15 -07:00
WarmUpTill
4991e74a72 Format json string before showing it to user 2021-06-18 11:15:15 -07:00
WarmUpTill
baa158bb33 Add helper fromatJsonString()
obs_data_get_json() will now dump the json data in JSON_COMPACT format
which makes it rather difficult to read.
2021-06-18 11:15:15 -07:00
WarmUpTill
0975ffabfa Adjust log 2021-06-18 11:15:15 -07:00
WarmUpTill
beec7d5939 Move getSceneItemsWithName() to utility 2021-06-18 11:15:15 -07:00
WarmUpTill
1bf2a154e2 Add macro condition "replay_buffer" 2021-06-18 11:15:03 -07:00
WarmUpTill
aa691cf51c Remove outdated comment 2021-06-18 11:15:03 -07:00
WarmUpTill
8fc12bee99 Initialize hotkeys to OBS_INVALID_HOTKEY_ID 2021-06-18 11:15:03 -07:00
WarmUpTill
5e8a0f92ee Add note regarding planned removal of tabs 2021-06-18 11:14:47 -07:00
WarmUpTill
86029e8e0e Add macro condition "Hotkey" 2021-06-15 13:44:33 -07:00
WarmUpTill
a86d6f1f76 Fix macro refs pointing to wrong entry when reordering 2021-06-15 06:16:04 -07:00
WarmUpTill
8ef798c70c Add macro condition "scene item order" 2021-06-15 06:15:37 -07:00
WarmUpTill
efc8726054 Add macro action "scene item order" 2021-06-15 06:15:37 -07:00
WarmUpTill
7b72abb100 Move populateSceneItemSelection() to utility 2021-06-15 06:15:37 -07:00
WarmUpTill
311ee5d0a0 Add macro condition "timer" (previously known as "interval") 2021-06-15 06:15:25 -07:00
WarmUpTill
5e4258968a Allow matching of empty files 2021-06-14 01:39:27 -07:00
WarmUpTill
ae4e861f7f Add macro action "hotkey" (#226) 2021-06-14 01:21:23 +02:00
WarmUpTill
d692444687 Add / extend macro filter condition / action 2021-06-13 15:14:57 -07:00
WarmUpTill
6976c98fd8 Fix regex value not saving / loading 2021-06-13 15:14:57 -07:00
WarmUpTill
75f8d0cb07 Move filter / source functions to utility 2021-06-13 15:14:57 -07:00
WarmUpTill
a7e03245bd Add macro condition "Macro" (previously "Count")
The macro condition "Macro" contains the condition type previously named
"Count" and also add the options to check the condition state of other
macros.

This allows nesting of macro conditions.
For example the following can now be configured using this new option:

"If (scene 1 or scene 2 or scene 3 is active) for at least x amount of
time"
2021-06-13 10:25:48 -07:00
WarmUpTill
056e7ef737 Rename files 2021-06-13 10:25:48 -07:00
WarmUpTill
675c23d7cc Place extended sequence widget in scroll area
The previously fixed size only allowed extending the sequence depending
on the available screen space
2021-06-13 10:25:31 -07:00
WarmUpTill
c4b66ef7f6 Fix warnings 2021-06-13 10:25:31 -07:00
WarmUpTill
5cae1bca41 Add macro condition "virtual camera" 2021-06-13 10:25:20 -07:00
WarmUpTill
407d338b8b Add macro action "virtual camera" 2021-06-13 10:25:20 -07:00
WarmUpTill
edd9f18de0 Add scene trigger to control virtual camera (#162) 2021-06-13 19:25:02 +02:00
WarmUpTill
c9769a799d Abort macro if wait was aborted by manual scene change 2021-06-09 12:51:19 -07: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
2ce0b35921 Add volume to control to audio action 2021-05-15 09:51:20 -07:00
WarmUpTill
33d2def126 Explicitly cast enum class to int 2021-05-15 09:05:43 -07:00
WarmUpTill
c44b0a2ac2 Remove unused verbose parameter 2021-05-15 09:05:43 -07:00
WarmUpTill
3abeb936b3 Fix random order of various macro entries 2021-05-15 01:03:07 -07:00
WarmUpTill
cef513f775 Adjust plugin state macro condition id 2021-05-14 14:03:04 -07:00
WarmUpTill
a66961eac1 Add macro condition "idle" 2021-05-14 14:03:04 -07:00
WarmUpTill
92c8767e1a Move plugin state condition to end 2021-05-14 09:44:49 -07:00
WarmUpTill
5b12468a46 Add macro condition process 2021-05-14 09:44:49 -07:00
WarmUpTill
675021c284 Add Macro tab (#170)
* add action switch scene

* add window condition

* Add macro action wait

* Add macro condition region

* Add audio macro condition

* Add file based macro condition

* Add macro condition media

* Add video macro condition

* Add macro action audio

* Add macro action streaming

* Add macro action recording

* Add replay buffer macro action

* Add stream macro condition

* Add macro condition recording

* Add macro condition plugin state

* Add macro action to run an arbitrary command
2021-05-14 16:37:01 +02:00
WarmUpTill
6bf122d0e8 Add support for transition duration in switchScene() (#178) 2021-05-12 19:50:50 +02:00
Pander
8b8e479098 added system-wide Flatpak paths (#175) 2021-05-06 20:38:11 +02:00
WarmUpTill
717d41a527 Adjust default setting values of transition tab 2021-05-04 11:32:45 -07:00
WarmUpTill
ad5587e084 Fix transition overrides not restoring duration 2021-05-04 11:32:45 -07:00
WarmUpTill
f37f384b5a Move DisplayMessage() to utility.hpp 2021-05-04 11:32:45 -07:00
WarmUpTill
dad36689c4 Move AdvSSScreenshotObj to separate files 2021-05-04 11:32:45 -07:00
WarmUpTill
0bd7d004ad Reduce includes of platform specific files
This is done to avoid build issues in future.
For example previously the class "Duration" class caused build issues on
the MacOS platform due to a name conflict.
2021-05-04 11:32:45 -07:00
WarmUpTill
f8c589c7a2 Only send scene switch msg if scene was switched
This change is to avoid spamming the connected clients with scene change
messages which might not even do anything as the scene change already
took place.
Additionally it allows scene switch messages to be sent by just calling
the switchScene() function.
2021-05-04 11:32:45 -07:00
WarmUpTill
e714df7ad0 Use Duration class and DurationSelection widget 2021-05-04 11:32:45 -07:00
WarmUpTill
6277da1bbd Add Duration class and DurationSelection widget 2021-05-04 11:32:45 -07:00
WarmUpTill
1bef309264 Only try to change transition overrides if needed 2021-05-04 11:32:45 -07:00
WarmUpTill
2e1d660e17 Clean up loading of transition tab settings 2021-05-04 11:32:45 -07:00
WarmUpTill
3f492f468a Move compareIgnoringLineEnding() to utility.hpp 2021-05-04 11:32:45 -07:00
WarmUpTill
c86bba84b3 Add scene-group.hpp include to switch-generic
This is to allow easier use of generic scene switch structs and widgets
in other areas. (Macro tab)
2021-05-04 11:32:45 -07:00
WarmUpTill
c468b0698b Move definition of SwitchTargetType 2021-05-04 11:32:45 -07:00
WarmUpTill
2af82fe22a Fix ignoring match of checkSwitchInfoFromFile() if checkFileContent() does not match 2021-05-04 11:32:45 -07:00
WarmUpTill
5d720219bf Clean up transition tab save and load 2021-05-04 11:32:45 -07:00
WarmUpTill
484e1aa191 Remove unnused function 2021-05-04 11:32:45 -07:00
WarmUpTill
0fdfe757b4 Reset tab order if config is invalid 2021-05-04 11:32:45 -07:00
WarmUpTill
bc5a04651f Remove unnecessary indirection 2021-05-04 11:32:45 -07:00
WarmUpTill
d9dfc1913d Fix previous scene not working for some sequences
If a duration for a transition is used that is longer than the scene
switcher check interval it could happen that the "incorrect" previous
scene could be chosen for uninterruptible scene sequences, as at the
time the sequence check was preformed the "previous scene" variable
was not yet updated as the transition was still ongoing.
2021-05-04 11:32:45 -07:00
WarmUpTill
1310d0b3f3 Add comment 2021-05-04 11:32:45 -07:00
WarmUpTill
139df8a641 Adjust function signature
Don't pass bool by reference if not necessary.
2021-05-04 11:32:45 -07:00
WarmUpTill
acf1ae63aa Drop support for old settings format 2021-05-04 11:32:45 -07:00
WarmUpTill
041a9e7f19 Add option to set transition duration (#171) 2021-05-01 00:20:25 +02:00
Pander
427ed34a1b added Flatpak paths for Ubuntu (#172) 2021-04-27 18:35:04 +02:00
WarmUpTill
45fa4dac8f Add option "has changed" to video tab (#168) 2021-04-22 20:51:35 +02:00
157 changed files with 17050 additions and 2285 deletions

View File

@@ -160,6 +160,7 @@ jobs:
libxcb-xinerama0-dev \
libxcomposite-dev \
libxinerama-dev \
libxtst-dev \
libmbedtls-dev \
pkg-config \
python3-dev \
@@ -200,7 +201,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

@@ -27,6 +27,8 @@ Examples are ...
/usr/lib/x86_64-linux-gnu/obs-plugins/
/usr/share/obs/obs-plugins/
~/.config/obs-studio/plugins/advanced-scene-switcher/bin/64bit/
~/.local/share/flatpak/app/com.obsproject.Studio/x86_64/stable/active/files/lib/obs-plugins/
/var/lib/flatpak/app/com.obsproject.Studio/x86_64/stable/active/files/lib/obs-plugins/
Unfortunately the expected location of the locale, which can be found in the data folder, can vary also.
The data folder usually should be copied to:
@@ -34,5 +36,7 @@ The data folder usually should be copied to:
If this does not work you can try to copy the "locale" folder found inside the data folder to:
/usr/share/obs/obs-plugins/advanced-scene-switcher/locale
~/.local/share/flatpak/app/com.obsproject.Studio/x86_64/stable/active/files/share/obs/obs-plugins/advanced-scene-switcher/locale
/var/lib/flatpak/app/com.obsproject.Studio/x86_64/stable/active/files/share/obs/obs-plugins/advanced-scene-switcher/locale
In doubt please check where other "en-US.ini" files are located on your system.

View File

@@ -62,9 +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/curl-helper.hpp
src/headers/volume-control.hpp
src/headers/scene-group.hpp
src/headers/scene-trigger.hpp
src/headers/switch-audio.hpp
@@ -82,6 +79,59 @@ set(advanced-scene-switcher_HEADERS
src/headers/switch-sequence.hpp
src/headers/switch-video.hpp
src/headers/switch-generic.hpp
src/headers/macro-action-edit.hpp
src/headers/macro-action-audio.hpp
src/headers/macro-action-file.hpp
src/headers/macro-action-filter.hpp
src/headers/macro-action-hotkey.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-order.hpp
src/headers/macro-action-scene-switch.hpp
src/headers/macro-action-scene-transform.hpp
src/headers/macro-action-scene-visibility.hpp
src/headers/macro-action-source.hpp
src/headers/macro-action-streaming.hpp
src/headers/macro-action-virtual-cam.hpp
src/headers/macro-action-wait.hpp
src/headers/macro-condition-edit.hpp
src/headers/macro-condition-audio.hpp
src/headers/macro-condition-date.hpp
src/headers/macro-condition-macro.hpp
src/headers/macro-condition-file.hpp
src/headers/macro-condition-filter.hpp
src/headers/macro-condition-hotkey.hpp
src/headers/macro-condition-idle.hpp
src/headers/macro-condition-media.hpp
src/headers/macro-condition-plugin-state.hpp
src/headers/macro-condition-process.hpp
src/headers/macro-condition-recording.hpp
src/headers/macro-condition-region.hpp
src/headers/macro-condition-replay-buffer.hpp
src/headers/macro-condition-scene-order.hpp
src/headers/macro-condition-scene.hpp
src/headers/macro-condition-source.hpp
src/headers/macro-condition-streaming.hpp
src/headers/macro-condition-timer.hpp
src/headers/macro-condition-video.hpp
src/headers/macro-condition-virtual-cam.hpp
src/headers/macro-condition-window.hpp
src/headers/macro.hpp
src/headers/macro-selection.hpp
src/headers/curl-helper.hpp
src/headers/hotkey.hpp
src/headers/scene-selection.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
)
@@ -110,7 +160,57 @@ set(advanced-scene-switcher_SOURCES
src/switch-audio.cpp
src/switch-video.cpp
src/switch-generic.cpp
src/macro-action-edit.cpp
src/macro-action-audio.cpp
src/macro-action-file.cpp
src/macro-action-filter.cpp
src/macro-action-hotkey.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-order.cpp
src/macro-action-scene-switch.cpp
src/macro-action-scene-transform.cpp
src/macro-action-scene-visibility.cpp
src/macro-action-source.cpp
src/macro-action-streaming.cpp
src/macro-action-virtual-cam.cpp
src/macro-action-wait.cpp
src/macro-condition-edit.cpp
src/macro-condition-audio.cpp
src/macro-condition-date.cpp
src/macro-condition-macro.cpp
src/macro-condition-file.cpp
src/macro-condition-filter.cpp
src/macro-condition-hotkey.cpp
src/macro-condition-idle.cpp
src/macro-condition-media.cpp
src/macro-condition-plugin-state.cpp
src/macro-condition-process.cpp
src/macro-condition-recording.cpp
src/macro-condition-region.cpp
src/macro-condition-replay-buffer.cpp
src/macro-condition-scene-order.cpp
src/macro-condition-scene.cpp
src/macro-condition-source.cpp
src/macro-condition-streaming.cpp
src/macro-condition-timer.cpp
src/macro-condition-video.cpp
src/macro-condition-virtual-cam.cpp
src/macro-condition-window.cpp
src/macro.cpp
src/macro-selection.cpp
src/macro-tab.cpp
src/curl-helper.cpp
src/scene-selection.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

@@ -1,7 +1,7 @@
# SceneSwitcher
An automated scene switcher for OBS Studio
More information can be found on https://obsproject.com/forum/resources/automatic-scene-switching.395/
More information can be found on https://obsproject.com/forum/resources/automatic-scene-switching.395/ and see https://github.com/WarmUpTill/SceneSwitcher/blob/master/CI/release/README.txt for installing releases.
## Compiling in tree (recommended)
Add the "SceneSwitcher" source directory to your obs-studio source directory under obs-studio/UI/frontend-plugins/:

View File

@@ -61,7 +61,7 @@ AdvSceneSwitcher.transitionTab.transitionBehaviorSelectionError="Mindestens eine
AdvSceneSwitcher.transitionTab.transitionForAToB="Szenenübergänge für automatisierte Szenenwechsel von Szene A zu Szene B"
AdvSceneSwitcher.transitionTab.transitionsHelp="<html><head/><body><p>Diese Einstellungen beeinflussen <span style=\"font-style:italic;\">nur</span> vom Szenenwechsler ausgelöste Szenenübergänge - Siehe <a href=\"https://obsproject.com/forum/resources/transition-table.1174/\"><span style=\" text-decoration: underline; color:#268bd2;\">Transition Table</span></a> um auch manuelle Szenenübergänge zu konfigurieren.<br/>Einstellungen auf diesem Tab haben Vorrang, vor denen welche auf den übrigen Tabs konfiguriert wurden.<br/><br/>Klicke auf das Plus Symbol, um einen neuen Eintrag hinzuzufügen.</p></body></html>"
AdvSceneSwitcher.transitionTab.defaultTransition="Ändere den Szenenübergang wenn eine Szene aktiv ist"
AdvSceneSwitcher.transitionTab.entry="Wechsle von {{scenes}} zu {{scenes2}} mit {{transitions}}"
AdvSceneSwitcher.transitionTab.entry="Wechsle von {{scenes}} zu {{scenes2}} mit {{transitions}} mit einer Dauer von {{duration}}"
AdvSceneSwitcher.transitionTab.defaultTransitionEntry="Wenn {{scenes}} aktiv ist ändere den Szenenübergang zu {{transitions}}"
AdvSceneSwitcher.transitionTab.defaultTransitionsHelp="Klicke auf das Plus Symbol, um einen neuen Eintrag hinzuzufügen."
AdvSceneSwitcher.transitionTab.defaultTransition.delay="Wechsle Szenenübergang {{defTransitionDelay}} nach Szenenwechsel."
@@ -193,8 +193,8 @@ AdvSceneSwitcher.sceneSequenceTab.fileType="Text Dateien (*.txt)"
AdvSceneSwitcher.sceneSequenceTab.interruptible="kann unterbrochen werden"
AdvSceneSwitcher.sceneSequenceTab.interruptibleHint="Andere Szenenwechselmethoden können diese Sequenz unterbrechen"
AdvSceneSwitcher.sceneSequenceTab.extendEdit="Szenenfolge erweitern"
AdvSceneSwitcher.sceneSequenceTab.extendEntry="Nach {{delay}} {{delayUnits}} wechsle zu {{scenes}} mit {{transitions}}"
AdvSceneSwitcher.sceneSequenceTab.entry="Wenn {{startScenes}} aktiv ist wechsle zu {{scenes}} nach {{delay}} {{delayUnits}} mit {{transitions}} {{interruptible}}"
AdvSceneSwitcher.sceneSequenceTab.extendEntry="Nach {{delay}} wechsle zu {{scenes}} mit {{transitions}}"
AdvSceneSwitcher.sceneSequenceTab.entry="Wenn {{startScenes}} aktiv ist wechsle zu {{scenes}} nach {{delay}} mit {{transitions}} {{interruptible}}"
AdvSceneSwitcher.sceneSequenceTab.help="Dieser Tab ermöglicht es automatisiert zu einer anderen Szene zu wechseln, nachdem eine Szene für die konfigurierte Zeit aktiv war.\nZum Beispiel kann so automatisch zwischen zwei Szenen hin und her gewechselt werden.\n\nKlicke auf das markierte Plus Symbol, um einen neuen Eintrag hinzuzufügen."
; Audio Tab
@@ -202,9 +202,9 @@ AdvSceneSwitcher.audioTab.title="Audio"
AdvSceneSwitcher.audioTab.condition.above="über"
AdvSceneSwitcher.audioTab.condition.below="unter"
AdvSceneSwitcher.audioTab.ignoreInactiveSource="außer Audio Quelle ist inaktiv"
AdvSceneSwitcher.audioTab.entry="Wenn die Lautstärke von {{audioSources}} {{condition}} {{volumeWidget}} ist für {{duration}} wechsle zu {{scenes}} mit {{transitions}} {{ignoreInactiveSource}}"
AdvSceneSwitcher.audioTab.entry="Wenn die Lautstärke von {{audioSources}} {{condition}} {{volumeWidget}} ist für {{duration}} Sekunden wechsle zu {{scenes}} mit {{transitions}} {{ignoreInactiveSource}}"
AdvSceneSwitcher.audioTab.multiMatchfallbackCondition="Wenn mehrere Einträge zutreffen ..."
AdvSceneSwitcher.audioTab.multiMatchfallback="... für {{duration}} wechsle zu {{scenes}} mit {{transitions}}"
AdvSceneSwitcher.audioTab.multiMatchfallback="... für {{duration}} Sekunden wechsle zu {{scenes}} mit {{transitions}}"
AdvSceneSwitcher.audioTab.help="Dieser Tab ermöglicht es basierend auf der Lautstärke von Audioquellen Szenen zu wechseln.\nSo kann zum Beispiel automatisch zu einer Szene gewechselt werden, wenn die Lautstärke eines Mikrofons eine konfigurierte Schwelle überschreitet.\n\nKlicke auf das markierte Plus Symbol, um einen neuen Eintrag hinzuzufügen."
; Video Tab
@@ -215,6 +215,7 @@ AdvSceneSwitcher.videoTab.condition.match="genau übereinstimmt mit"
AdvSceneSwitcher.videoTab.condition.match.tooltip="Eine genaue Übereinstimmung setzt voraus, dass sowohl Zielquelle als auch das Bild die gleiche Auflösung besitzen.\nZusätzlich muss jeder einzelne Pixel zwischen beiden Bildquellen übereinstimmen weshalb Bildformate, welche das Bild komprimieren (z. B. .JPG) nicht zu empfehlen sind."
AdvSceneSwitcher.videoTab.condition.differ="nicht übereinstimmt mit"
AdvSceneSwitcher.videoTab.condition.hasNotChanged="sich nicht verändert"
AdvSceneSwitcher.videoTab.condition.hasNotChanged="sich verändert"
AdvSceneSwitcher.videoTab.ignoreInactiveSource="außer Video Quelle ist inaktiv"
AdvSceneSwitcher.videoTab.entry="Wenn {{videoSources}} {{condition}} {{filePath}} {{browseButton}} für {{duration}} wechsle zu {{scenes}} mit {{transitions}} {{ignoreInactiveSource}}"
AdvSceneSwitcher.videoTab.help="<html><head/><body><p>Dieser Tab ermöglicht es basierend auf der Videoausgabe von Quellen scenen zu wechseln.<br/>Für eine noch bessere Implementierung dieser Funktionalität siehe <a href=\"https://obsproject.com/forum/resources/pixel-match-switcher.1202/\"><span style=\" text-decoration: underline; color:#268bd2;\">Pixel Match Switcher</span></a>.<br/><br/>Klicke auf das markierte Plus Symbol, um einen neuen Eintrag hinzuzufügen..</p></body></html>"
@@ -226,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"
@@ -281,6 +284,8 @@ AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.muteSource="Audio Quelle stu
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.unmuteSource="Audio Quelle aktiv schalten"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.startSwitcher="Szenenwechsler starten"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.stopSwitcher="Szenenwechsler stoppen"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.startVirtualCamera="Virtuelle Kamera starten"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.stopVirtualCamera="Virtuelle Kamera stoppen"
AdvSceneSwitcher.sceneTriggerTab.entry="Wenn {{scenes}} {{triggers}} {{actions}} {{audioSources}} nach {{duration}} "
AdvSceneSwitcher.sceneTriggerTab.help="Dieser Tab ermöglicht es automatisiert Handlungen, wie etwa das Stoppen von einer Aufnahme oder des Streamens, bei einem Szenenwechsel auszuführen."

View File

@@ -51,6 +51,234 @@ AdvSceneSwitcher.generalTab.priority.media="Media"
AdvSceneSwitcher.generalTab.priority.time="Time"
AdvSceneSwitcher.generalTab.priority.audio="Audio"
AdvSceneSwitcher.generalTab.priority.video="Video"
AdvSceneSwitcher.generalTab.priority.macro="Macro"
; Macro Tab
AdvSceneSwitcher.macroTab.title="Macro"
AdvSceneSwitcher.macroTab.macros="Macros"
AdvSceneSwitcher.macroTab.help="Macros allow you to execute a string of actions depending on multiple conditions.\n\nClick on the highlighted plus symbol to add a new Macro."
AdvSceneSwitcher.macroTab.editConditionHelp="This section allows you to define Macro conditions.\n\nSelect an existing or add a new Macro on the left.\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:"
AdvSceneSwitcher.macroTab.edit.action="Action type:"
AdvSceneSwitcher.macroTab.add="Add new macro"
AdvSceneSwitcher.macroTab.name="Name:"
AdvSceneSwitcher.macroTab.defaultname="Macro %1"
AdvSceneSwitcher.macroTab.exists="Macro name exists already"
AdvSceneSwitcher.macroTab.copy="Create copy"
; Macro Logic
AdvSceneSwitcher.logic.none="Ignore entry"
AdvSceneSwitcher.logic.and="And"
AdvSceneSwitcher.logic.or="Or"
AdvSceneSwitcher.logic.andNot="And not"
AdvSceneSwitcher.logic.orNot="Or not"
AdvSceneSwitcher.logic.rootNone="If"
AdvSceneSwitcher.logic.not="If not"
; Macro Conditions
AdvSceneSwitcher.condition.audio="Audio"
AdvSceneSwitcher.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}}"
AdvSceneSwitcher.condition.window="Window"
AdvSceneSwitcher.condition.window.entry.line1="{{windows}} exist and ..."
AdvSceneSwitcher.condition.window.entry.line2="... is {{fullscreen}} fullscreen {{maximized}} maximized {{focused}} focused {{windowFocusChanged}} foreground window changed"
AdvSceneSwitcher.condition.file="File"
AdvSceneSwitcher.condition.file.entry.line1="Content of {{fileType}} {{filePath}} {{browseButton}} matches:"
AdvSceneSwitcher.condition.file.entry.line2="{{matchText}}"
AdvSceneSwitcher.condition.file.entry.line3="{{useRegex}} {{checkModificationDate}} {{checkFileContent}}"
AdvSceneSwitcher.condition.media="Media"
AdvSceneSwitcher.condition.media.entry="{{mediaSources}} state is {{states}} and {{timeRestrictions}} {{time}}"
AdvSceneSwitcher.condition.video="Video"
AdvSceneSwitcher.condition.video.condition.match="exactly matches"
AdvSceneSwitcher.condition.video.condition.differ="does not match"
AdvSceneSwitcher.condition.video.condition.hasChanged="has changed"
AdvSceneSwitcher.condition.video.condition.hasNotChanged="has not changed"
AdvSceneSwitcher.condition.video.condition.noImage="has no output"
AdvSceneSwitcher.condition.video.askFileAction="Do you want to use an existing file or create a screenshot of the currently selected source?"
AdvSceneSwitcher.condition.video.askFileAction.file="Use existing file"
AdvSceneSwitcher.condition.video.askFileAction.screenshot="Create screenshot"
AdvSceneSwitcher.condition.video.entry="{{videoSources}} {{condition}} {{filePath}} {{browseButton}}"
AdvSceneSwitcher.condition.stream="Streaming"
AdvSceneSwitcher.condition.stream.state.start="Stream running"
AdvSceneSwitcher.condition.stream.state.stop="Stream stopped"
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}}"
AdvSceneSwitcher.condition.process="Process"
AdvSceneSwitcher.condition.process.entry="{{processes}} is running {{focused}} and is focused"
AdvSceneSwitcher.condition.idle="Idle"
AdvSceneSwitcher.condition.idle.entry="No keyboard or mouse inputs for {{duration}}"
AdvSceneSwitcher.condition.pluginState="Plugin state"
AdvSceneSwitcher.condition.pluginState.state.sceneSwitched="Automated scene change was triggered in this interval"
AdvSceneSwitcher.condition.pluginState.entry="{{condition}}"
AdvSceneSwitcher.condition.timer="Timer"
AdvSceneSwitcher.condition.timer.entry.line1="{{duration}} have passed"
AdvSceneSwitcher.condition.timer.entry.line2="Time remaining: {{remaining}} seconds"
AdvSceneSwitcher.condition.timer.entry.line3="{{reset}} {{autoReset}} Automatically reset timer after duration was reached"
AdvSceneSwitcher.condition.timer.reset="Reset"
AdvSceneSwitcher.condition.macro="Macro"
AdvSceneSwitcher.condition.macro.type.count="Count"
AdvSceneSwitcher.condition.macro.type.state="State"
AdvSceneSwitcher.condition.macro.type.selection="Condition type: {{types}}"
AdvSceneSwitcher.condition.macro.count.type.below="Less than"
AdvSceneSwitcher.condition.macro.count.type.above="More than"
AdvSceneSwitcher.condition.macro.count.type.equal="Exactly"
AdvSceneSwitcher.condition.macro.count.reset="Reset"
AdvSceneSwitcher.condition.macro.state.entry="Conditions of {{macros}} are true"
AdvSceneSwitcher.condition.macro.count.entry.line1="{{macros}} was executed {{conditions}} {{count}} times"
AdvSceneSwitcher.condition.macro.count.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}}"
AdvSceneSwitcher.condition.virtualCamera="Virtual camera"
AdvSceneSwitcher.condition.virtualCamera.state.start="Virtual camera started"
AdvSceneSwitcher.condition.virtualCamera.state.stop="Virtual camera stopped"
AdvSceneSwitcher.condition.virtualCamera.entry="{{states}}"
AdvSceneSwitcher.condition.filter="Filter"
AdvSceneSwitcher.condition.filter.type.active="Is enabled"
AdvSceneSwitcher.condition.filter.type.showing="Is disabled"
AdvSceneSwitcher.condition.filter.type.settings="Settings match"
AdvSceneSwitcher.condition.filter.regex="Use regular expressions"
AdvSceneSwitcher.condition.filter.getSettings="Get current settings"
AdvSceneSwitcher.condition.filter.entry.line1="On {{sources}} {{filters}} {{conditions}}"
AdvSceneSwitcher.condition.filter.entry.line2="{{settings}}"
AdvSceneSwitcher.condition.filter.entry.line3="{{regex}} {{getSettings}}"
AdvSceneSwitcher.condition.sceneOrder="Scene item order"
AdvSceneSwitcher.condition.sceneOrder.type.above="Is above"
AdvSceneSwitcher.condition.sceneOrder.type.below="Is below"
AdvSceneSwitcher.condition.sceneOrder.type.position="Is at position"
AdvSceneSwitcher.condition.sceneOrder.positionInfo="The position value starts at the bottom with 0 and increases by one for each scene item including the ones in scene groups"
AdvSceneSwitcher.condition.sceneOrder.entry="On {{scenes}} {{sources}} {{conditions}} {{sources2}} {{position}}"
AdvSceneSwitcher.condition.hotkey="Hotkey"
AdvSceneSwitcher.condition.hotkey.name="Macro trigger hotkey"
AdvSceneSwitcher.condition.hotkey.tip="Note: You can configure the keybindings for this hotkey in the OBS settings window"
AdvSceneSwitcher.condition.hotkey.entry="Name: {{name}}"
AdvSceneSwitcher.condition.replay="Replay buffer"
AdvSceneSwitcher.condition.replay.state.stopped="Replay buffer stopped"
AdvSceneSwitcher.condition.replay.state.started="Replay buffer started"
AdvSceneSwitcher.condition.replay.state.saved="Replay buffer saved"
AdvSceneSwitcher.condition.replay.entry="{{state}}"
AdvSceneSwitcher.condition.date="Date"
AdvSceneSwitcher.condition.date.entry.line1="{{dateTime}} {{ignoreDate}} Ignore date selection {{ignoreTime}} Ignore time selection"
AdvSceneSwitcher.condition.date.entry.line2="{{repeat}} Repeat every {{duration}}"
; Macro Actions
AdvSceneSwitcher.action.switchScene="Switch scene"
AdvSceneSwitcher.action.scene.entry="Switch to scene {{scenes}} using {{transitions}} with a duration of {{duration}} seconds"
AdvSceneSwitcher.action.wait="Wait"
AdvSceneSwitcher.action.wait.type.fixed="fixed"
AdvSceneSwitcher.action.wait.type.random="random"
AdvSceneSwitcher.action.wait.entry.fixed="Wait for {{waitType}} duration of {{duration}}"
AdvSceneSwitcher.action.wait.entry.random="Wait for {{waitType}} duration from {{duration}} to {{duration2}}"
AdvSceneSwitcher.action.audio="Audio"
AdvSceneSwitcher.action.audio.type.mute="Mute"
AdvSceneSwitcher.action.audio.type.unmute="Unmute"
AdvSceneSwitcher.action.audio.type.sourceVolume="Set source volume"
AdvSceneSwitcher.action.audio.type.masterVolume="Set master volume"
AdvSceneSwitcher.action.audio.entry="{{actions}} {{audioSources}} {{volume}}"
AdvSceneSwitcher.action.recording="Recording"
AdvSceneSwitcher.action.recording.type.stop="Stop recording"
AdvSceneSwitcher.action.recording.type.start="Start recording"
AdvSceneSwitcher.action.recording.type.pause="Pause recording"
AdvSceneSwitcher.action.recording.type.unpause="Unpause recording"
AdvSceneSwitcher.action.recording.pause.hint="Note that depending on your recording settings you might not be able to pause recording"
AdvSceneSwitcher.action.recording.entry="{{actions}}{{pauseHint}}"
AdvSceneSwitcher.action.replay="Replay buffer"
AdvSceneSwitcher.action.replay.type.stop="Stop replay buffer"
AdvSceneSwitcher.action.replay.type.start="Start replay buffer"
AdvSceneSwitcher.action.replay.type.save="Save replay buffer"
AdvSceneSwitcher.action.replay.entry="{{actions}}"
AdvSceneSwitcher.action.streaming="Streaming"
AdvSceneSwitcher.action.streaming.type.stop="Stop streaming"
AdvSceneSwitcher.action.streaming.type.start="Start streaming"
AdvSceneSwitcher.action.streaming.entry="{{actions}}"
AdvSceneSwitcher.action.run="Run"
AdvSceneSwitcher.action.run.arguments="Arguments:"
AdvSceneSwitcher.action.run.addArgument="Add argument"
AdvSceneSwitcher.action.run.addArgumentDescription="Add new argument:"
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.type.settings="Set settings"
AdvSceneSwitcher.action.filter.entry="On {{sources}} {{actions}} {{filters}}"
AdvSceneSwitcher.action.filter.getSettings="Get current settings"
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.type.noMatch="Change the no-match behaviour:"
AdvSceneSwitcher.action.pluginState.entry="{{actions}} {{values}} {{scenes}}"
AdvSceneSwitcher.action.virtualCamera="Virtual camera"
AdvSceneSwitcher.action.virtualCamera.type.stop="Stop virtual camera"
AdvSceneSwitcher.action.virtualCamera.type.start="Start virtual camera"
AdvSceneSwitcher.action.virtualCamera.entry="{{actions}}"
AdvSceneSwitcher.action.hotkey="Hotkey"
AdvSceneSwitcher.action.hotkey.leftShift="Left Shift"
AdvSceneSwitcher.action.hotkey.rightShift="Right Shift"
AdvSceneSwitcher.action.hotkey.leftCtrl="Left Ctrl"
AdvSceneSwitcher.action.hotkey.rightCtrl="Right Ctrl"
AdvSceneSwitcher.action.hotkey.leftAlt="Left Alt"
AdvSceneSwitcher.action.hotkey.rightAlt="Right Alt"
AdvSceneSwitcher.action.hotkey.leftMeta="Left Meta"
AdvSceneSwitcher.action.hotkey.rightMeta="Right Meta"
AdvSceneSwitcher.action.hotkey.disabled="Cannot simulate key presses - functionality disabled!"
AdvSceneSwitcher.action.hotkey.entry="Press {{keys}}"
AdvSceneSwitcher.action.sceneOrder="Scene item order"
AdvSceneSwitcher.action.sceneOrder.type.moveUp="Move up"
AdvSceneSwitcher.action.sceneOrder.type.moveDown="Move down"
AdvSceneSwitcher.action.sceneOrder.type.moveTop="Move to top"
AdvSceneSwitcher.action.sceneOrder.type.moveBottom="Move to bottom"
AdvSceneSwitcher.action.sceneOrder.type.movePosition="Move to position"
AdvSceneSwitcher.action.sceneOrder.entry="On {{scenes}} {{actions}} {{sources}} {{position}}"
AdvSceneSwitcher.action.sceneTransform="Scene item transform"
AdvSceneSwitcher.action.sceneTransform.getTransform="Get transform"
AdvSceneSwitcher.action.sceneTransform.entry="On {{scenes}} transform {{sources}}"
AdvSceneSwitcher.action.file="File"
AdvSceneSwitcher.action.file.type.write="Write"
AdvSceneSwitcher.action.file.type.append="Append"
AdvSceneSwitcher.action.file.entry="{{actions}} to {{filePath}} {{browseButton}}:"
; Transition Tab
AdvSceneSwitcher.transitionTab.title="Transition"
@@ -61,7 +289,7 @@ AdvSceneSwitcher.transitionTab.transitionBehaviorSelectionError="At least one op
AdvSceneSwitcher.transitionTab.transitionForAToB="Use transition for automated scene switch from scene A to scene B"
AdvSceneSwitcher.transitionTab.transitionsHelp="<html><head/><body><p>These settings <span style=\"font-style:italic;\">only</span> affect transitions caused by the scene switcher - Check out <a href=\"https://obsproject.com/forum/resources/transition-table.1174/\"><span style=\" text-decoration: underline; color:#268bd2;\">Transition Table</span></a> if you want to configure this for manual scene changes.<br/>Settings defined here take priority over transition settings configured elsewhere in the scene switcher.<br/><br/>Click the plus symbol below to add a new entry.</p></body></html>"
AdvSceneSwitcher.transitionTab.defaultTransition="Change transition if scene is active"
AdvSceneSwitcher.transitionTab.entry="Switch from {{scenes}} to {{scenes2}} using {{transitions}}"
AdvSceneSwitcher.transitionTab.entry="Switch from {{scenes}} to {{scenes2}} using {{transitions}} with a duration of {{duration}}"
AdvSceneSwitcher.transitionTab.defaultTransitionEntry="When scene {{scenes}} is active change default scene transition to {{transitions}}"
AdvSceneSwitcher.transitionTab.defaultTransitionsHelp="Click on the plus symbol to add an entry."
AdvSceneSwitcher.transitionTab.defaultTransition.delay="Switch transition {{defTransitionDelay}} after scene change."
@@ -192,9 +420,9 @@ AdvSceneSwitcher.sceneSequenceTab.loadSuccess="Advanced Scene Switcher settings
AdvSceneSwitcher.sceneSequenceTab.fileType="Text files (*.txt)"
AdvSceneSwitcher.sceneSequenceTab.interruptible="interruptible"
AdvSceneSwitcher.sceneSequenceTab.interruptibleHint="Other switching methods are allowed to interrupt this scene sequence"
AdvSceneSwitcher.sceneSequenceTab.entry="When {{startScenes}} is active switch to {{scenes}} after {{delay}} {{delayUnits}} using {{transitions}} {{interruptible}}"
AdvSceneSwitcher.sceneSequenceTab.entry="When {{startScenes}} is active switch to {{scenes}} after {{delay}} using {{transitions}} {{interruptible}}"
AdvSceneSwitcher.sceneSequenceTab.extendEdit="Extend Sequence"
AdvSceneSwitcher.sceneSequenceTab.extendEntry="After {{delay}} {{delayUnits}} switch to {{scenes}} using {{transitions}}"
AdvSceneSwitcher.sceneSequenceTab.extendEntry="After {{delay}} switch to {{scenes}} using {{transitions}}"
AdvSceneSwitcher.sceneSequenceTab.help="This tab will allow you to automatically switch to a different scene if a scene was active for a configured period of time.\nFor example, you could automatically cycle back and forth between two scenes automatically.\n\nClick on the highlighted plus symbol to continue."
; Audio Tab
@@ -202,9 +430,9 @@ AdvSceneSwitcher.audioTab.title="Audio"
AdvSceneSwitcher.audioTab.condition.above="above"
AdvSceneSwitcher.audioTab.condition.below="below"
AdvSceneSwitcher.audioTab.ignoreInactiveSource="unless source is inactive"
AdvSceneSwitcher.audioTab.entry="When the volume of {{audioSources}} is {{condition}} {{volumeWidget}} for {{duration}} switch to {{scenes}} using {{transitions}} {{ignoreInactiveSource}}"
AdvSceneSwitcher.audioTab.entry="When the volume of {{audioSources}} is {{condition}} {{volumeWidget}} for {{duration}} seconds switch to {{scenes}} using {{transitions}} {{ignoreInactiveSource}}"
AdvSceneSwitcher.audioTab.multiMatchfallbackCondition="If multiple entries match ..."
AdvSceneSwitcher.audioTab.multiMatchfallback="... for {{duration}} switch to {{scenes}} using {{transitions}}"
AdvSceneSwitcher.audioTab.multiMatchfallback="... for {{duration}} seconds switch to {{scenes}} using {{transitions}}"
AdvSceneSwitcher.audioTab.help="This tab will allow you to switch scenes based on the volume of sources.\nFor example, you could automatically switch to a different scene if the volume of your microphone reaches a certain threshold.\n\nClick on the highlighted plus symbol to continue."
; Video Tab
@@ -215,6 +443,7 @@ AdvSceneSwitcher.videoTab.condition.match="exactly matches"
AdvSceneSwitcher.videoTab.condition.match.tooltip="An exact match requires the target and the source image to be of the same resolution.\nAdditionally every single pixel needs to match, which is why use of image formats which use compression (e.g. .JPG) is not recommended!"
AdvSceneSwitcher.videoTab.condition.differ="does not match"
AdvSceneSwitcher.videoTab.condition.hasNotChanged="has not changed"
AdvSceneSwitcher.videoTab.condition.hasChanged="has changed"
AdvSceneSwitcher.videoTab.ignoreInactiveSource="unless source is inactive"
AdvSceneSwitcher.videoTab.entry="When {{videoSources}} {{condition}} {{filePath}} {{browseButton}} for {{duration}} switch to {{scenes}} using {{transitions}} {{ignoreInactiveSource}}"
AdvSceneSwitcher.videoTab.help="<html><head/><body><p>This tab will allow you to switch scenes based on the current video output of selected sources.<br/>Make sure to check out <a href=\"https://obsproject.com/forum/resources/pixel-match-switcher.1202/\"><span style=\" text-decoration: underline; color:#268bd2;\">Pixel Match Switcher</span></a> for an even better implementation of this functionality.<br/><br/> Click on the highlighted plus symbol to continue.</p></body></html>"
@@ -226,7 +455,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"
@@ -281,6 +512,8 @@ AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.muteSource="mute source"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.unmuteSource="unmute source"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.startSwitcher="start the scene switcher"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.stopSwitcher="stop the scene switcher"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.startVirtualCamera="start virtual camera"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.stopVirtualCamera="stop virtual camera"
AdvSceneSwitcher.sceneTriggerTab.entry="When {{scenes}} {{triggers}} {{actions}} {{audioSources}} after {{duration}}"
AdvSceneSwitcher.sceneTriggerTab.help="This tab allows you to trigger actions on scene changes, like stopping recording or streaming."
@@ -288,6 +521,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?"
@@ -296,13 +531,19 @@ AdvSceneSwitcher.browse="Browse"
AdvSceneSwitcher.selectScene="--select scene--"
AdvSceneSwitcher.selectPreviousScene="Previous Scene"
AdvSceneSwitcher.selectCurrentScene="Current Scene"
AdvSceneSwitcher.selectAnyScene="Any 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"
@@ -315,3 +556,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="часы"

View File

@@ -168,7 +168,7 @@ AdvSceneSwitcher.sceneSequenceTab.loadTitle="从文件加载序列 ..."
AdvSceneSwitcher.sceneSequenceTab.loadFail="导入失败"
AdvSceneSwitcher.sceneSequenceTab.loadSuccess="导入失败成功"
AdvSceneSwitcher.sceneSequenceTab.fileType="文本文件 (*.txt)"
AdvSceneSwitcher.sceneSequenceTab.entry="当场景 {{startScenes}} 被激活 {{delay}} {{delayUnits}}后使用转场特效 {{transitions}} 切换到场景 {{scenes}} {{interruptible}}"
AdvSceneSwitcher.sceneSequenceTab.entry="当场景 {{startScenes}} 被激活 {{delay}} 后使用转场特效 {{transitions}} 切换到场景 {{scenes}} {{interruptible}}"
; Audio Tab
AdvSceneSwitcher.audioTab.title="音频"

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

@@ -226,7 +226,7 @@
</layout>
</item>
<item row="1" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_46">
<layout class="QHBoxLayout" name="noMatchLayout">
<item>
<widget class="QLabel" name="label_2">
<property name="sizePolicy">
@@ -243,22 +243,12 @@
</property>
</widget>
</item>
<item>
<widget class="QDoubleSpinBox" name="noMatchDelay">
<property name="suffix">
<string>s</string>
</property>
<property name="maximum">
<double>999999.989999999990687</double>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<layout class="QHBoxLayout" name="cooldownLayout">
<item>
<widget class="QLabel" name="label">
<property name="text">
@@ -266,26 +256,6 @@
</property>
</widget>
</item>
<item>
<widget class="QDoubleSpinBox" name="cooldownTime">
<property name="suffix">
<string>s</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
@@ -560,6 +530,434 @@
</item>
</layout>
</widget>
<widget class="QWidget" name="macroTab">
<attribute name="title">
<string>AdvSceneSwitcher.macroTab.title</string>
</attribute>
<layout class="QHBoxLayout" name="horizontalLayout_11" stretch="1,3">
<item>
<widget class="QGroupBox" name="groupBox_5">
<property name="title">
<string>AdvSceneSwitcher.macroTab.macros</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_36">
<item>
<layout class="QGridLayout" name="gridLayout_26">
<item row="0" column="0">
<widget class="QListWidget" name="macros">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="sortingEnabled">
<bool>false</bool>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="macroHelp">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>AdvSceneSwitcher.macroTab.help</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_61">
<item>
<widget class="QPushButton" name="macroAdd">
<property name="maximumSize">
<size>
<width>22</width>
<height>22</height>
</size>
</property>
<property name="flat">
<bool>true</bool>
</property>
<property name="themeID" stdset="0">
<string notr="true">addIconSmall</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="macroRemove">
<property name="maximumSize">
<size>
<width>22</width>
<height>22</height>
</size>
</property>
<property name="flat">
<bool>true</bool>
</property>
<property name="themeID" stdset="0">
<string notr="true">removeIconSmall</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_130">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>5</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="Line" name="line_46">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_131">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>5</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="macroUp">
<property name="maximumSize">
<size>
<width>22</width>
<height>22</height>
</size>
</property>
<property name="text">
<string/>
</property>
<property name="icon">
<iconset>
<normaloff>../../../forms/images/up.svg</normaloff>../../../forms/images/up.svg</iconset>
</property>
<property name="flat">
<bool>true</bool>
</property>
<property name="themeID" stdset="0">
<string>upArrowIconSmall</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="macroDown">
<property name="maximumSize">
<size>
<width>22</width>
<height>22</height>
</size>
</property>
<property name="text">
<string/>
</property>
<property name="icon">
<iconset>
<normaloff>../../../forms/images/down.svg</normaloff>../../../forms/images/down.svg</iconset>
</property>
<property name="flat">
<bool>true</bool>
</property>
<property name="themeID" stdset="0">
<string>downArrowIconSmall</string>
</property>
</widget>
</item>
<item>
<widget class="Line" name="line_4">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_65">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="macroEdit">
<property name="title">
<string>AdvSceneSwitcher.macroTab.edit</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_34">
<item>
<layout class="QHBoxLayout" name="horizontalLayout_18" stretch="0,0,0">
<item>
<widget class="QLabel" name="label_20">
<property name="text">
<string>AdvSceneSwitcher.macroTab.name</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="macroName"/>
</item>
<item>
<spacer name="horizontalSpacer_13">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<widget class="QSplitter" name="splitter">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<widget class="QScrollArea" name="macroConditions">
<property name="widgetResizable">
<bool>true</bool>
</property>
<widget class="QWidget" name="scrollAreaWidgetContents_2">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>767</width>
<height>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 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>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>
</layout>
</widget>
</item>
</layout>
</widget>
<widget class="QWidget" name="transitionsTab">
<attribute name="title">
<string>AdvSceneSwitcher.transitionTab.title</string>
@@ -3346,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">
@@ -3373,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,16 +1,14 @@
#include <QMainWindow>
#include <QAction>
#include <QtGui/qstandarditemmodel.h>
#include <QPropertyAnimation>
#include <QGraphicsColorizeEffect>
#include <QMessageBox>
#include <QFileDialog>
#include <regex>
#include <obs-module.h>
#include <obs-frontend-api.h>
#include <util/util.hpp>
#include "headers/advanced-scene-switcher.hpp"
#include "headers/curl-helper.hpp"
#include "headers/utility.hpp"
#include "headers/version.h"
SwitcherData *switcher = nullptr;
@@ -63,6 +61,7 @@ void AdvSceneSwitcher::loadUI()
setupNetworkTab();
setupSceneGroupTab();
setupTriggerTab();
setupMacroTab();
setTabOrder();
restoreWindowGeo();
@@ -71,353 +70,36 @@ void AdvSceneSwitcher::loadUI()
}
/******************************************************************************
* UI helpers
* Saving and loading
******************************************************************************/
bool AdvSceneSwitcher::DisplayMessage(QString msg, bool question)
void AskBackup(obs_data_t *obj)
{
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();
}
bool backupSettings = DisplayMessage(
obs_module_text("AdvSceneSwitcher.askBackup"), true);
return false;
}
void AdvSceneSwitcher::addSelectionEntry(QComboBox *sel,
const char *description,
bool selectable, const char *tooltip)
{
sel->addItem(description);
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) {
@@ -442,7 +124,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);
@@ -479,6 +161,11 @@ void SwitcherData::Thread()
bool match = false;
OBSWeakSource scene;
OBSWeakSource transition;
// The previous scene might have changed during the linger duration,
// if a longer transition is used than the configured check interval
bool setPrevSceneAfterLinger = false;
bool macroMatch = false;
macroSceneSwitched = false;
endTime = std::chrono::high_resolution_clock::now();
@@ -499,21 +186,24 @@ 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();
sleep = 0;
linger = 0;
switcher->Prune();
if (switcher->stop) {
Prune();
if (stop) {
break;
}
if (checkPause()) {
continue;
}
match = checkForMatch(scene, transition, linger);
if (switcher->stop) {
setPreconditions();
match = checkForMatch(scene, transition, linger,
setPrevSceneAfterLinger, macroMatch);
if (stop) {
break;
}
checkNoMatchSwitch(match, scene, transition, sleep);
@@ -524,9 +214,10 @@ void SwitcherData::Thread()
vblog(LOG_INFO, "sleep for %ld before switching scene",
duration.count());
setWaitScene();
cv.wait_for(lock, duration);
if (switcher->stop) {
if (stop) {
break;
}
@@ -536,6 +227,8 @@ void SwitcherData::Thread()
match = false;
linger = 0;
} else if (setPrevSceneAfterLinger) {
scene = previousScene;
}
}
@@ -548,12 +241,11 @@ void SwitcherData::Thread()
lock.unlock();
if (match) {
if (networkConfig.ServerEnabled) {
server.sendMessage(scene, transition);
if (macroMatch) {
runMacros();
} else {
switchScene({scene, transition, 0});
}
switchScene(scene, transition,
tansitionOverrideOverride,
adjustActiveTransitionType, verbose);
}
writeSceneInfoToFile();
@@ -562,13 +254,41 @@ void SwitcherData::Thread()
blog(LOG_INFO, "stopped");
}
void SwitcherData::setPreconditions()
{
// Window title
lastTitle = currentTitle;
std::string title;
GetCurrentWindowTitle(title);
for (auto &window : ignoreWindowsSwitches) {
bool equals = (title == window);
bool matches = false;
if (!equals) {
try {
std::regex expr(window);
matches = std::regex_match(title, expr);
} catch (const std::regex_error &) {
}
}
if (equals || matches) {
title = lastTitle;
break;
}
}
currentTitle = title;
}
bool SwitcherData::checkForMatch(OBSWeakSource &scene,
OBSWeakSource &transition, int &linger)
OBSWeakSource &transition, int &linger,
bool &setPrevSceneAfterLinger,
bool &macroMatch)
{
bool match = false;
if (uninterruptibleSceneSequenceActive) {
checkSceneSequence(match, scene, transition, linger);
match = checkSceneSequence(scene, transition, linger,
setPrevSceneAfterLinger);
if (match) {
return match;
}
@@ -577,39 +297,46 @@ bool SwitcherData::checkForMatch(OBSWeakSource &scene,
for (int switchFuncName : functionNamesByPriority) {
switch (switchFuncName) {
case read_file_func:
checkSwitchInfoFromFile(match, scene, transition);
checkFileContent(match, scene, transition);
match = checkSwitchInfoFromFile(scene, transition) ||
checkFileContent(scene, transition);
break;
case idle_func:
checkIdleSwitch(match, scene, transition);
match = checkIdleSwitch(scene, transition);
break;
case exe_func:
checkExeSwitch(match, scene, transition);
match = checkExeSwitch(scene, transition);
break;
case screen_region_func:
checkScreenRegionSwitch(match, scene, transition);
match = checkScreenRegionSwitch(scene, transition);
break;
case window_title_func:
checkWindowTitleSwitch(match, scene, transition);
match = checkWindowTitleSwitch(scene, transition);
break;
case round_trip_func:
checkSceneSequence(match, scene, transition, linger);
match = checkSceneSequence(scene, transition, linger,
setPrevSceneAfterLinger);
break;
case media_func:
checkMediaSwitch(match, scene, transition);
match = checkMediaSwitch(scene, transition);
break;
case time_func:
checkTimeSwitch(match, scene, transition);
match = checkTimeSwitch(scene, transition);
break;
case audio_func:
checkAudioSwitch(match, scene, transition);
match = checkAudioSwitch(scene, transition);
break;
case video_func:
checkVideoSwitch(match, scene, transition);
match = checkVideoSwitch(scene, transition);
break;
case macro_func:
if (checkMacros()) {
match = true;
macroMatch = true;
}
break;
}
if (switcher->stop) {
if (stop) {
return false;
}
if (match) {
@@ -619,43 +346,52 @@ bool SwitcherData::checkForMatch(OBSWeakSource &scene,
return match;
}
void switchScene(OBSWeakSource &scene, OBSWeakSource &transition,
bool transitionOverrideOverride,
bool adjustActiveTransitionType, bool verbose)
void switchScene(const sceneSwitchInfo &sceneSwitch)
{
if (!scene && verbose) {
if (!sceneSwitch.scene && switcher->verbose) {
blog(LOG_INFO, "nothing to switch to");
return;
}
obs_source_t *source = obs_weak_source_get_source(scene);
obs_source_t *currentSource = obs_frontend_get_current_scene();
obs_source_t *source = obs_weak_source_get_source(sceneSwitch.scene);
obs_source_t *currentSource =
obs_weak_source_get_source(switcher->currentScene);
if (source && source != currentSource) {
transitionData td;
setNextTransition(scene, currentSource, transition,
transitionOverrideOverride,
adjustActiveTransitionType, td);
transitionData currentTransitionData;
setNextTransition(sceneSwitch, currentSource,
currentTransitionData);
obs_frontend_set_current_scene(source);
if (transitionOverrideOverride) {
restoreTransitionOverride(source, td);
if (switcher->transitionOverrideOverride) {
restoreTransitionOverride(source,
currentTransitionData);
}
if (verbose) {
if (switcher->verbose) {
blog(LOG_INFO, "switched scene");
}
if (switcher->networkConfig.ShouldSendSceneChange()) {
switcher->server.sendMessage(sceneSwitch);
}
}
obs_source_release(currentSource);
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())) {
stop = false;
switcher->th = new SwitcherThread();
switcher->th->start(
(QThread::Priority)switcher->threadPriority);
th = new SwitcherThread();
th->start((QThread::Priority)threadPriority);
// Will be overwritten quickly but might be useful
writeToStatusFile("Advanced Scene Switcher running");
@@ -671,10 +407,17 @@ void SwitcherData::Start()
}
}
void ResetMacroCounters()
{
for (auto &m : switcher->macros) {
m.ResetCount();
}
}
void SwitcherData::Stop()
{
if (th && th->isRunning()) {
switcher->stop = true;
stop = true;
transitionCv.notify_one();
cv.notify_one();
th->wait();
@@ -682,12 +425,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();
@@ -711,58 +461,81 @@ extern "C" void FreeSceneSwitcher()
loaded_curl_lib = nullptr;
}
PlatformCleanup();
delete switcher;
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
// Set current and previous scene
obs_source_t *source = obs_frontend_get_current_scene();
obs_weak_source_t *ws = obs_source_get_weak_source(source);
if (ws && ws != switcher->currentScene) {
switcher->previousScene = switcher->currentScene;
switcher->currentScene = ws;
vblog(LOG_INFO, "current scene: %s",
GetWeakSourceName(switcher->currentScene).c_str());
vblog(LOG_INFO, "previous scene: %s",
GetWeakSourceName(switcher->previousScene).c_str());
}
obs_source_release(source);
obs_weak_source_release(ws);
if (source && s->previousSceneHelper != ws) {
s->previousScene = s->previousSceneHelper;
s->previousSceneHelper = ws;
}
s->checkTriggers();
s->checkDefaultSceneTransitions();
switcher->checkTriggers();
switcher->checkDefaultSceneTransitions();
if (switcher->networkConfig.ServerEnabled &&
switcher->networkConfig.SendAll) {
switcher->server.sendMessage(ws, nullptr);
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);
}
}
void setReplayBufferSaved()
{
switcher->replayBufferSaved = true;
}
// Note to future self:
@@ -770,24 +543,34 @@ 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;
case OBS_FRONTEND_EVENT_REPLAY_BUFFER_SAVED:
setReplayBufferSaved();
break;
default:
break;
@@ -807,6 +590,8 @@ extern "C" void InitSceneSwitcher()
switcher->curl = f_curl_init();
}
PlatformInit();
auto cb = []() {
QMainWindow *window =
(QMainWindow *)obs_frontend_get_main_window();

View File

@@ -33,6 +33,17 @@ bool resolveCurl()
bool loadCurl()
{
loaded_curl_lib = new QLibrary(curl_library_name, nullptr);
if (resolveCurl()) {
blog(LOG_INFO, "[adv-ss] found curl library");
return true;
} else {
delete loaded_curl_lib;
loaded_curl_lib = nullptr;
blog(LOG_WARNING,
"[adv-ss] couldn't find the curl library in PATH");
}
QStringList locations;
locations << QDir::currentPath();
#if defined(__linux__) || defined(__APPLE__)

284
src/duration-control.cpp Normal file
View File

@@ -0,0 +1,284 @@
#include "headers/duration-control.hpp"
#include "headers/utility.hpp"
#include "obs-module.h"
#include <sstream>
#include <iomanip>
#include <QHBoxLayout>
void Duration::Save(obs_data_t *obj, const char *secondsName,
const char *unitName)
{
obs_data_set_double(obj, secondsName, seconds);
obs_data_set_int(obj, unitName, static_cast<int>(displayUnit));
}
void Duration::Load(obs_data_t *obj, const char *secondsName,
const char *unitName)
{
seconds = obs_data_get_double(obj, secondsName);
displayUnit =
static_cast<DurationUnit>(obs_data_get_int(obj, unitName));
}
bool Duration::DurationReached()
{
if (_startTime.time_since_epoch().count() == 0) {
_startTime = std::chrono::high_resolution_clock::now();
}
auto runTime = std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::high_resolution_clock::now() - _startTime);
return runTime.count() >= seconds * 1000;
}
double Duration::TimeRemaining()
{
if (_startTime.time_since_epoch().count() == 0) {
return seconds;
}
auto runTime = std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::high_resolution_clock::now() - _startTime);
if (runTime.count() >= seconds * 1000) {
return 0;
}
return (seconds * 1000 - runTime.count()) / 1000.;
}
void Duration::Reset()
{
_startTime = {};
}
int durationUnitToMultiplier(DurationUnit u)
{
switch (u) {
case DurationUnit::SECONDS:
return 1;
case DurationUnit::MINUTES:
return 60;
case DurationUnit::HOURS:
return 3600;
default:
break;
}
return 0;
}
std::string durationUnitToString(DurationUnit u)
{
switch (u) {
case DurationUnit::SECONDS:
return obs_module_text("AdvSceneSwitcher.unit.secends");
case DurationUnit::MINUTES:
return obs_module_text("AdvSceneSwitcher.unit.minutes");
case DurationUnit::HOURS:
return obs_module_text("AdvSceneSwitcher.unit.hours");
default:
break;
}
return "";
}
std::string Duration::ToString()
{
std::ostringstream ss;
ss << std::fixed << std::setprecision(2)
<< seconds / durationUnitToMultiplier(displayUnit) << " "
<< durationUnitToString(displayUnit);
return ss.str();
}
static void populateUnits(QComboBox *list)
{
list->addItem(obs_module_text("AdvSceneSwitcher.unit.secends"));
list->addItem(obs_module_text("AdvSceneSwitcher.unit.minutes"));
list->addItem(obs_module_text("AdvSceneSwitcher.unit.hours"));
}
DurationSelection::DurationSelection(QWidget *parent, bool showUnitSelection)
: QWidget(parent), _unitMultiplier(1)
{
_duration = new QDoubleSpinBox(parent);
_duration->setMaximum(86400); // 24 hours
_unitSelection = new QComboBox();
populateUnits(_unitSelection);
QWidget::connect(_duration, SIGNAL(valueChanged(double)), this,
SLOT(_DurationChanged(double)));
QWidget::connect(_unitSelection, SIGNAL(currentIndexChanged(int)), this,
SLOT(_UnitChanged(int)));
QHBoxLayout *layout = new QHBoxLayout;
layout->setContentsMargins(0, 0, 0, 0);
layout->setSpacing(11);
layout->addWidget(_duration);
if (showUnitSelection) {
layout->addWidget(_unitSelection);
}
setLayout(layout);
}
void DurationSelection::SetValue(double value)
{
_duration->setValue(value / _unitMultiplier);
}
void DurationSelection::SetUnit(DurationUnit u)
{
_unitSelection->setCurrentIndex(static_cast<int>(u));
}
void DurationSelection::SetDuration(Duration d)
{
SetUnit(d.displayUnit);
SetValue(d.seconds);
}
void DurationSelection::_DurationChanged(double value)
{
emit DurationChanged(value * _unitMultiplier);
}
void DurationSelection::_UnitChanged(int idx)
{
DurationUnit unit = static_cast<DurationUnit>(idx);
double prevMultiplier = _unitMultiplier;
_unitMultiplier = durationUnitToMultiplier(unit);
_duration->setValue(_duration->value() *
(prevMultiplier / _unitMultiplier));
emit UnitChanged(unit);
}
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,10 +1,8 @@
#include <QFileDialog>
#include "headers/advanced-scene-switcher.hpp"
#include "headers/utility.hpp"
#include "headers/version.h"
constexpr auto tab_count = 17;
#include <QFileDialog>
QMetaObject::Connection inactivePluse;
@@ -61,14 +59,44 @@ void AdvSceneSwitcher::on_noMatchRandomSwitch_clicked()
ui->randomDisabledWarning->setVisible(false);
}
void AdvSceneSwitcher::on_noMatchDelay_valueChanged(double i)
void AdvSceneSwitcher::NoMatchDelayDurationChanged(double sec)
{
if (loading) {
return;
}
std::lock_guard<std::mutex> lock(switcher->m);
switcher->noMatchDelay = i;
switcher->noMatchDelay.seconds = sec;
}
void AdvSceneSwitcher::NoMatchDelayUnitChanged(DurationUnit unit)
{
if (loading) {
return;
}
std::lock_guard<std::mutex> lock(switcher->m);
switcher->noMatchDelay.displayUnit = unit;
}
void AdvSceneSwitcher::CooldownDurationChanged(double sec)
{
if (loading) {
return;
}
std::lock_guard<std::mutex> lock(switcher->m);
switcher->cooldown.seconds = sec;
}
void AdvSceneSwitcher::CooldownUnitChanged(DurationUnit unit)
{
if (loading) {
return;
}
std::lock_guard<std::mutex> lock(switcher->m);
switcher->cooldown.displayUnit = unit;
}
void AdvSceneSwitcher::on_startupBehavior_currentIndexChanged(int index)
@@ -102,16 +130,6 @@ void AdvSceneSwitcher::on_noMatchSwitchScene_currentTextChanged(
UpdateNonMatchingScene(text);
}
void AdvSceneSwitcher::on_cooldownTime_valueChanged(double i)
{
if (loading) {
return;
}
std::lock_guard<std::mutex> lock(switcher->m);
switcher->cooldown = i;
}
void AdvSceneSwitcher::on_checkInterval_valueChanged(int value)
{
if (loading) {
@@ -138,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;
}
@@ -189,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(
@@ -302,51 +295,54 @@ int findTabIndex(QTabWidget *tabWidget, int pos)
tabName = "generalTab";
break;
case 1:
tabName = "transitionsTab";
tabName = "macroTab";
break;
case 2:
tabName = "pauseTab";
tabName = "transitionsTab";
break;
case 3:
tabName = "windowTitleTab";
tabName = "pauseTab";
break;
case 4:
tabName = "executableTab";
tabName = "windowTitleTab";
break;
case 5:
tabName = "screenRegionTab";
tabName = "executableTab";
break;
case 6:
tabName = "mediaTab";
tabName = "screenRegionTab";
break;
case 7:
tabName = "fileTab";
tabName = "mediaTab";
break;
case 8:
tabName = "randomTab";
tabName = "fileTab";
break;
case 9:
tabName = "timeTab";
tabName = "randomTab";
break;
case 10:
tabName = "idleTab";
tabName = "timeTab";
break;
case 11:
tabName = "sceneSequenceTab";
tabName = "idleTab";
break;
case 12:
tabName = "audioTab";
tabName = "sceneSequenceTab";
break;
case 13:
tabName = "videoTab";
tabName = "audioTab";
break;
case 14:
tabName = "networkTab";
tabName = "videoTab";
break;
case 15:
tabName = "sceneGroupTab";
tabName = "networkTab";
break;
case 16:
tabName = "sceneGroupTab";
break;
case 17:
tabName = "sceneTriggerTab";
break;
}
@@ -365,6 +361,10 @@ int findTabIndex(QTabWidget *tabWidget, int pos)
void AdvSceneSwitcher::setTabOrder()
{
if (!switcher->tabOrderValid()) {
switcher->resetTabOrder();
}
QTabBar *bar = ui->tabWidget->tabBar();
for (int i = 0; i < bar->count(); ++i) {
int curPos = findTabIndex(ui->tabWidget, switcher->tabOrder[i]);
@@ -410,6 +410,7 @@ void SwitcherData::loadSettings(obs_data_t *obj)
}
loadSceneGroups(obj);
loadMacros(obj);
loadWindowTitleSwitches(obj);
loadScreenRegionSwitches(obj);
loadPauseSwitches(obj);
@@ -435,7 +436,11 @@ void SwitcherData::saveSettings(obs_data_t *obj)
return;
}
// Needs to be loaded before any entries which might rely on scene group
// selections to be available.
saveSceneGroups(obj);
saveMacros(obj);
saveWindowTitleSwitches(obj);
saveScreenRegionSwitches(obj);
savePauseSwitches(obj);
@@ -458,112 +463,95 @@ void SwitcherData::saveSettings(obs_data_t *obj)
void SwitcherData::saveGeneralSettings(obs_data_t *obj)
{
obs_data_set_int(obj, "interval", switcher->interval);
obs_data_set_int(obj, "interval", interval);
std::string nonMatchingSceneName =
GetWeakSourceName(switcher->nonMatchingScene);
std::string nonMatchingSceneName = GetWeakSourceName(nonMatchingScene);
obs_data_set_string(obj, "non_matching_scene",
nonMatchingSceneName.c_str());
obs_data_set_int(obj, "switch_if_not_matching",
switcher->switchIfNotMatching);
obs_data_set_double(obj, "noMatchDelay", switcher->noMatchDelay);
obs_data_set_int(obj, "switch_if_not_matching", switchIfNotMatching);
noMatchDelay.Save(obj, "noMatchDelay", "noMatchDelayUnit");
obs_data_set_double(obj, "cooldown", switcher->cooldown);
cooldown.Save(obj, "cooldown", "cooldownUnit");
obs_data_set_bool(obj, "active", !switcher->stop);
obs_data_set_int(obj, "startup_behavior", switcher->startupBehavior);
obs_data_set_bool(obj, "active", !stop);
obs_data_set_int(obj, "startup_behavior", startupBehavior);
obs_data_set_int(obj, "autoStartEvent",
static_cast<int>(switcher->autoStartEvent));
static_cast<int>(autoStartEvent));
obs_data_set_bool(obj, "verbose", switcher->verbose);
obs_data_set_bool(obj, "disableHints", switcher->disableHints);
obs_data_set_bool(obj, "verbose", verbose);
obs_data_set_bool(obj, "disableHints", disableHints);
obs_data_set_int(obj, "priority0",
switcher->functionNamesByPriority[0]);
obs_data_set_int(obj, "priority1",
switcher->functionNamesByPriority[1]);
obs_data_set_int(obj, "priority2",
switcher->functionNamesByPriority[2]);
obs_data_set_int(obj, "priority3",
switcher->functionNamesByPriority[3]);
obs_data_set_int(obj, "priority4",
switcher->functionNamesByPriority[4]);
obs_data_set_int(obj, "priority5",
switcher->functionNamesByPriority[5]);
obs_data_set_int(obj, "priority6",
switcher->functionNamesByPriority[6]);
obs_data_set_int(obj, "priority7",
switcher->functionNamesByPriority[7]);
obs_data_set_int(obj, "priority8",
switcher->functionNamesByPriority[8]);
obs_data_set_int(obj, "priority9",
switcher->functionNamesByPriority[9]);
obs_data_set_int(obj, "priority0", functionNamesByPriority[0]);
obs_data_set_int(obj, "priority1", functionNamesByPriority[1]);
obs_data_set_int(obj, "priority2", functionNamesByPriority[2]);
obs_data_set_int(obj, "priority3", functionNamesByPriority[3]);
obs_data_set_int(obj, "priority4", functionNamesByPriority[4]);
obs_data_set_int(obj, "priority5", functionNamesByPriority[5]);
obs_data_set_int(obj, "priority6", functionNamesByPriority[6]);
obs_data_set_int(obj, "priority7", functionNamesByPriority[7]);
obs_data_set_int(obj, "priority8", functionNamesByPriority[8]);
obs_data_set_int(obj, "priority9", functionNamesByPriority[9]);
obs_data_set_int(obj, "priority10", functionNamesByPriority[10]);
obs_data_set_int(obj, "threadPriority", switcher->threadPriority);
obs_data_set_int(obj, "threadPriority", threadPriority);
// After fresh install of OBS the vector can be empty
// as save() might be called before first load()
if (tabOrder.size() < tab_count) {
resetTabOrder();
}
obs_data_set_int(obj, "generalTabPos", switcher->tabOrder[0]);
obs_data_set_int(obj, "transitionTabPos", switcher->tabOrder[1]);
obs_data_set_int(obj, "pauseTabPos", switcher->tabOrder[2]);
obs_data_set_int(obj, "titleTabPos", switcher->tabOrder[3]);
obs_data_set_int(obj, "exeTabPos", switcher->tabOrder[4]);
obs_data_set_int(obj, "regionTabPos", switcher->tabOrder[5]);
obs_data_set_int(obj, "mediaTabPos", switcher->tabOrder[6]);
obs_data_set_int(obj, "fileTabPos", switcher->tabOrder[7]);
obs_data_set_int(obj, "randomTabPos", switcher->tabOrder[8]);
obs_data_set_int(obj, "timeTabPos", switcher->tabOrder[9]);
obs_data_set_int(obj, "idleTabPos", switcher->tabOrder[10]);
obs_data_set_int(obj, "sequenceTabPos", switcher->tabOrder[11]);
obs_data_set_int(obj, "audioTabPos", switcher->tabOrder[12]);
obs_data_set_int(obj, "videoTabPos", switcher->tabOrder[13]);
obs_data_set_int(obj, "networkTabPos", switcher->tabOrder[14]);
obs_data_set_int(obj, "sceneGroupTabPos", switcher->tabOrder[15]);
obs_data_set_int(obj, "triggerTabPos", switcher->tabOrder[16]);
obs_data_set_int(obj, "generalTabPos", tabOrder[0]);
obs_data_set_int(obj, "macroTabPos", tabOrder[1]);
obs_data_set_int(obj, "transitionTabPos", tabOrder[2]);
obs_data_set_int(obj, "pauseTabPos", tabOrder[3]);
obs_data_set_int(obj, "titleTabPos", tabOrder[4]);
obs_data_set_int(obj, "exeTabPos", tabOrder[5]);
obs_data_set_int(obj, "regionTabPos", tabOrder[6]);
obs_data_set_int(obj, "mediaTabPos", tabOrder[7]);
obs_data_set_int(obj, "fileTabPos", tabOrder[8]);
obs_data_set_int(obj, "randomTabPos", tabOrder[9]);
obs_data_set_int(obj, "timeTabPos", tabOrder[10]);
obs_data_set_int(obj, "idleTabPos", tabOrder[11]);
obs_data_set_int(obj, "sequenceTabPos", tabOrder[12]);
obs_data_set_int(obj, "audioTabPos", tabOrder[13]);
obs_data_set_int(obj, "videoTabPos", tabOrder[14]);
obs_data_set_int(obj, "networkTabPos", tabOrder[15]);
obs_data_set_int(obj, "sceneGroupTabPos", tabOrder[16]);
obs_data_set_int(obj, "triggerTabPos", tabOrder[17]);
obs_data_set_bool(obj, "saveWindowGeo", saveWindowGeo);
obs_data_set_int(obj, "windowPosX", switcher->windowPos.x());
obs_data_set_int(obj, "windowPosY", switcher->windowPos.y());
obs_data_set_int(obj, "windowWidth", switcher->windowSize.width());
obs_data_set_int(obj, "windowHeight", switcher->windowSize.height());
obs_data_set_int(obj, "windowPosX", windowPos.x());
obs_data_set_int(obj, "windowPosY", windowPos.y());
obs_data_set_int(obj, "windowWidth", windowSize.width());
obs_data_set_int(obj, "windowHeight", windowSize.height());
}
void SwitcherData::loadGeneralSettings(obs_data_t *obj)
{
obs_data_set_default_int(obj, "interval", default_interval);
switcher->interval = obs_data_get_int(obj, "interval");
interval = obs_data_get_int(obj, "interval");
obs_data_set_default_int(obj, "switch_if_not_matching", NO_SWITCH);
switcher->switchIfNotMatching =
switchIfNotMatching =
(NoMatch)obs_data_get_int(obj, "switch_if_not_matching");
std::string nonMatchingScene =
std::string nonMatchingSceneName =
obs_data_get_string(obj, "non_matching_scene");
switcher->nonMatchingScene =
GetWeakSourceByName(nonMatchingScene.c_str());
switcher->noMatchDelay = obs_data_get_double(obj, "noMatchDelay");
nonMatchingScene = GetWeakSourceByName(nonMatchingSceneName.c_str());
noMatchDelay.Load(obj, "noMatchDelay", "noMatchDelayUnit");
switcher->cooldown = obs_data_get_double(obj, "cooldown");
cooldown.Load(obj, "cooldown", "cooldownUnit");
switcher->stop = !obs_data_get_bool(obj, "active");
switcher->startupBehavior =
stop = !obs_data_get_bool(obj, "active");
startupBehavior =
(StartupBehavior)obs_data_get_int(obj, "startup_behavior");
if (switcher->startupBehavior == START) {
switcher->stop = false;
if (startupBehavior == START) {
stop = false;
}
if (switcher->startupBehavior == STOP) {
switcher->stop = true;
if (startupBehavior == STOP) {
stop = true;
}
switcher->autoStartEvent = static_cast<AutoStartEvent>(
autoStartEvent = static_cast<AutoStartEvent>(
obs_data_get_int(obj, "autoStartEvent"));
switcher->verbose = obs_data_get_bool(obj, "verbose");
switcher->disableHints = obs_data_get_bool(obj, "disableHints");
verbose = obs_data_get_bool(obj, "verbose");
disableHints = obs_data_get_bool(obj, "disableHints");
obs_data_set_default_int(obj, "priority0", default_priority_0);
obs_data_set_default_int(obj, "priority1", default_priority_1);
@@ -575,95 +563,73 @@ void SwitcherData::loadGeneralSettings(obs_data_t *obj)
obs_data_set_default_int(obj, "priority7", default_priority_7);
obs_data_set_default_int(obj, "priority8", default_priority_8);
switcher->functionNamesByPriority[0] =
(obs_data_get_int(obj, "priority0"));
switcher->functionNamesByPriority[1] =
(obs_data_get_int(obj, "priority1"));
switcher->functionNamesByPriority[2] =
(obs_data_get_int(obj, "priority2"));
switcher->functionNamesByPriority[3] =
(obs_data_get_int(obj, "priority3"));
switcher->functionNamesByPriority[4] =
(obs_data_get_int(obj, "priority4"));
switcher->functionNamesByPriority[5] =
(obs_data_get_int(obj, "priority5"));
switcher->functionNamesByPriority[6] =
(obs_data_get_int(obj, "priority6"));
switcher->functionNamesByPriority[7] =
(obs_data_get_int(obj, "priority7"));
switcher->functionNamesByPriority[8] =
(obs_data_get_int(obj, "priority8"));
switcher->functionNamesByPriority[9] =
(obs_data_get_int(obj, "priority9"));
if (!switcher->prioFuncsValid()) {
switcher->functionNamesByPriority[0] = (default_priority_0);
switcher->functionNamesByPriority[1] = (default_priority_1);
switcher->functionNamesByPriority[2] = (default_priority_2);
switcher->functionNamesByPriority[3] = (default_priority_3);
switcher->functionNamesByPriority[4] = (default_priority_4);
switcher->functionNamesByPriority[5] = (default_priority_5);
switcher->functionNamesByPriority[6] = (default_priority_6);
switcher->functionNamesByPriority[7] = (default_priority_7);
switcher->functionNamesByPriority[8] = (default_priority_8);
switcher->functionNamesByPriority[9] = (default_priority_9);
functionNamesByPriority[0] = (obs_data_get_int(obj, "priority0"));
functionNamesByPriority[1] = (obs_data_get_int(obj, "priority1"));
functionNamesByPriority[2] = (obs_data_get_int(obj, "priority2"));
functionNamesByPriority[3] = (obs_data_get_int(obj, "priority3"));
functionNamesByPriority[4] = (obs_data_get_int(obj, "priority4"));
functionNamesByPriority[5] = (obs_data_get_int(obj, "priority5"));
functionNamesByPriority[6] = (obs_data_get_int(obj, "priority6"));
functionNamesByPriority[7] = (obs_data_get_int(obj, "priority7"));
functionNamesByPriority[8] = (obs_data_get_int(obj, "priority8"));
functionNamesByPriority[9] = (obs_data_get_int(obj, "priority9"));
functionNamesByPriority[10] = (obs_data_get_int(obj, "priority10"));
if (!prioFuncsValid()) {
functionNamesByPriority[0] = (default_priority_0);
functionNamesByPriority[1] = (default_priority_1);
functionNamesByPriority[2] = (default_priority_2);
functionNamesByPriority[3] = (default_priority_3);
functionNamesByPriority[4] = (default_priority_4);
functionNamesByPriority[5] = (default_priority_5);
functionNamesByPriority[6] = (default_priority_6);
functionNamesByPriority[7] = (default_priority_7);
functionNamesByPriority[8] = (default_priority_8);
functionNamesByPriority[9] = (default_priority_9);
functionNamesByPriority[10] = (default_priority_10);
}
obs_data_set_default_int(obj, "threadPriority",
QThread::NormalPriority);
switcher->threadPriority = obs_data_get_int(obj, "threadPriority");
threadPriority = obs_data_get_int(obj, "threadPriority");
obs_data_set_default_int(obj, "generalTabPos", 0);
obs_data_set_default_int(obj, "transitionTabPos", 1);
obs_data_set_default_int(obj, "pauseTabPos", 2);
obs_data_set_default_int(obj, "titleTabPos", 3);
obs_data_set_default_int(obj, "exeTabPos", 4);
obs_data_set_default_int(obj, "regionTabPos", 5);
obs_data_set_default_int(obj, "mediaTabPos", 6);
obs_data_set_default_int(obj, "fileTabPos", 7);
obs_data_set_default_int(obj, "randomTabPos", 8);
obs_data_set_default_int(obj, "timeTabPos", 9);
obs_data_set_default_int(obj, "idleTabPos", 10);
obs_data_set_default_int(obj, "sequenceTabPos", 11);
obs_data_set_default_int(obj, "audioTabPos", 12);
obs_data_set_default_int(obj, "videoTabPos", 13);
obs_data_set_default_int(obj, "networkTabPos", 14);
obs_data_set_default_int(obj, "sceneGroupTabPos", 15);
obs_data_set_default_int(obj, "triggerTabPos", 16);
obs_data_set_default_int(obj, "macroTabPos", 1);
obs_data_set_default_int(obj, "transitionTabPos", 2);
obs_data_set_default_int(obj, "pauseTabPos", 3);
obs_data_set_default_int(obj, "titleTabPos", 4);
obs_data_set_default_int(obj, "exeTabPos", 5);
obs_data_set_default_int(obj, "regionTabPos", 6);
obs_data_set_default_int(obj, "mediaTabPos", 7);
obs_data_set_default_int(obj, "fileTabPos", 8);
obs_data_set_default_int(obj, "randomTabPos", 9);
obs_data_set_default_int(obj, "timeTabPos", 10);
obs_data_set_default_int(obj, "idleTabPos", 11);
obs_data_set_default_int(obj, "sequenceTabPos", 12);
obs_data_set_default_int(obj, "audioTabPos", 13);
obs_data_set_default_int(obj, "videoTabPos", 14);
obs_data_set_default_int(obj, "networkTabPos", 15);
obs_data_set_default_int(obj, "sceneGroupTabPos", 16);
obs_data_set_default_int(obj, "triggerTabPos", 17);
switcher->tabOrder.emplace_back(
(int)(obs_data_get_int(obj, "generalTabPos")));
switcher->tabOrder.emplace_back(
(int)(obs_data_get_int(obj, "transitionTabPos")));
switcher->tabOrder.emplace_back(
(int)(obs_data_get_int(obj, "pauseTabPos")));
switcher->tabOrder.emplace_back(
(int)(obs_data_get_int(obj, "titleTabPos")));
switcher->tabOrder.emplace_back(
(int)(obs_data_get_int(obj, "exeTabPos")));
switcher->tabOrder.emplace_back(
(int)(obs_data_get_int(obj, "regionTabPos")));
switcher->tabOrder.emplace_back(
(int)(obs_data_get_int(obj, "mediaTabPos")));
switcher->tabOrder.emplace_back(
(int)(obs_data_get_int(obj, "fileTabPos")));
switcher->tabOrder.emplace_back(
(int)(obs_data_get_int(obj, "randomTabPos")));
switcher->tabOrder.emplace_back(
(int)(obs_data_get_int(obj, "timeTabPos")));
switcher->tabOrder.emplace_back(
(int)(obs_data_get_int(obj, "idleTabPos")));
switcher->tabOrder.emplace_back(
(int)(obs_data_get_int(obj, "sequenceTabPos")));
switcher->tabOrder.emplace_back(
(int)(obs_data_get_int(obj, "audioTabPos")));
switcher->tabOrder.emplace_back(
(int)(obs_data_get_int(obj, "videoTabPos")));
switcher->tabOrder.emplace_back(
(int)(obs_data_get_int(obj, "networkTabPos")));
switcher->tabOrder.emplace_back(
(int)(obs_data_get_int(obj, "sceneGroupTabPos")));
switcher->tabOrder.emplace_back(
(int)(obs_data_get_int(obj, "triggerTabPos")));
tabOrder.clear();
tabOrder.emplace_back((int)(obs_data_get_int(obj, "generalTabPos")));
tabOrder.emplace_back((int)(obs_data_get_int(obj, "macroTabPos")));
tabOrder.emplace_back((int)(obs_data_get_int(obj, "transitionTabPos")));
tabOrder.emplace_back((int)(obs_data_get_int(obj, "pauseTabPos")));
tabOrder.emplace_back((int)(obs_data_get_int(obj, "titleTabPos")));
tabOrder.emplace_back((int)(obs_data_get_int(obj, "exeTabPos")));
tabOrder.emplace_back((int)(obs_data_get_int(obj, "regionTabPos")));
tabOrder.emplace_back((int)(obs_data_get_int(obj, "mediaTabPos")));
tabOrder.emplace_back((int)(obs_data_get_int(obj, "fileTabPos")));
tabOrder.emplace_back((int)(obs_data_get_int(obj, "randomTabPos")));
tabOrder.emplace_back((int)(obs_data_get_int(obj, "timeTabPos")));
tabOrder.emplace_back((int)(obs_data_get_int(obj, "idleTabPos")));
tabOrder.emplace_back((int)(obs_data_get_int(obj, "sequenceTabPos")));
tabOrder.emplace_back((int)(obs_data_get_int(obj, "audioTabPos")));
tabOrder.emplace_back((int)(obs_data_get_int(obj, "videoTabPos")));
tabOrder.emplace_back((int)(obs_data_get_int(obj, "networkTabPos")));
tabOrder.emplace_back((int)(obs_data_get_int(obj, "sceneGroupTabPos")));
tabOrder.emplace_back((int)(obs_data_get_int(obj, "triggerTabPos")));
if (!tabOrderValid()) {
resetTabOrder();
@@ -678,28 +644,33 @@ void SwitcherData::loadGeneralSettings(obs_data_t *obj)
bool SwitcherData::tabOrderValid()
{
auto tmp = tabOrder;
std::sort(tmp.begin(), tmp.end());
auto it = std::unique(tmp.begin(), tmp.end());
return it == tmp.end();
auto tmp = std::vector<int>(tab_count);
std::iota(tmp.begin(), tmp.end(), 0);
for (auto &p : tmp) {
auto it = std::find(tabOrder.begin(), tabOrder.end(), p);
if (it == tabOrder.end()) {
return false;
}
}
return true;
}
void SwitcherData::resetTabOrder()
{
tabOrder = std::vector<int>(tab_count);
std::iota(switcher->tabOrder.begin(), switcher->tabOrder.end(), 0);
std::iota(tabOrder.begin(), tabOrder.end(), 0);
}
void SwitcherData::checkNoMatchSwitch(bool &match, OBSWeakSource &scene,
OBSWeakSource &transition, int &sleep)
{
if (match) {
noMatchCount = 0;
noMatchDelay.Reset();
return;
}
if ((noMatchCount * interval) / 1000.0 < noMatchDelay) {
noMatchCount++;
if (!noMatchDelay.DurationReached()) {
return;
}
@@ -709,27 +680,22 @@ void SwitcherData::checkNoMatchSwitch(bool &match, OBSWeakSource &scene,
transition = nullptr;
}
if (switchIfNotMatching == RANDOM_SWITCH) {
checkRandom(match, scene, transition, sleep);
match = checkRandom(scene, transition, sleep);
}
}
void SwitcherData::checkSwitchCooldown(bool &match)
{
if (!match || cooldown == 0.) {
if (!match) {
return;
}
auto now = std::chrono::high_resolution_clock::now();
auto timePassed = std::chrono::duration_cast<std::chrono::milliseconds>(
now - lastMatchTime);
if (timePassed.count() > cooldown * 1000) {
lastMatchTime = now;
return;
if (cooldown.DurationReached()) {
cooldown.Reset();
} else {
match = false;
vblog(LOG_INFO, "cooldown active - ignoring match");
}
match = false;
vblog(LOG_INFO, "cooldown active - ignoring match");
}
void populateStartupBehavior(QComboBox *cb)
@@ -785,14 +751,29 @@ void AdvSceneSwitcher::setupGeneralTab()
}
ui->noMatchSwitchScene->setCurrentText(
GetWeakSourceName(switcher->nonMatchingScene).c_str());
ui->noMatchDelay->setValue(switcher->noMatchDelay);
ui->noMatchDelay->setToolTip(obs_module_text(
DurationSelection *noMatchDelay = new DurationSelection();
noMatchDelay->SetDuration(switcher->noMatchDelay);
noMatchDelay->setToolTip(obs_module_text(
"AdvSceneSwitcher.generalTab.generalBehavior.onNoMetDelayTooltip"));
ui->noMatchLayout->addWidget(noMatchDelay);
QWidget::connect(noMatchDelay, SIGNAL(DurationChanged(double)), this,
SLOT(NoMatchDelayDurationChanged(double)));
QWidget::connect(noMatchDelay, SIGNAL(UnitChanged(DurationUnit)), this,
SLOT(NoMatchDelayUnitChanged(DurationUnit)));
ui->checkInterval->setValue(switcher->interval);
ui->cooldownTime->setValue(switcher->cooldown);
ui->cooldownTime->setToolTip(obs_module_text(
DurationSelection *cooldownTime = new DurationSelection();
cooldownTime->SetDuration(switcher->cooldown);
cooldownTime->setToolTip(obs_module_text(
"AdvSceneSwitcher.generalTab.generalBehavior.cooldownHint"));
ui->cooldownLayout->addWidget(cooldownTime);
ui->cooldownLayout->addStretch();
QWidget::connect(cooldownTime, SIGNAL(DurationChanged(double)), this,
SLOT(CooldownDurationChanged(double)));
QWidget::connect(cooldownTime, SIGNAL(UnitChanged(DurationUnit)), this,
SLOT(CooldownUnitChanged(DurationUnit)));
ui->verboseLogging->setChecked(switcher->verbose);
ui->saveWindowGeo->setChecked(switcher->saveWindowGeo);
@@ -841,6 +822,10 @@ void AdvSceneSwitcher::setupGeneralTab()
s = obs_module_text(
"AdvSceneSwitcher.generalTab.priority.video");
break;
case macro_func:
s = obs_module_text(
"AdvSceneSwitcher.generalTab.priority.macro");
break;
}
QString text(s.c_str());
QListWidgetItem *item =

View File

@@ -5,10 +5,11 @@
#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, ...) \
if (verbose) { \
if (switcher->verbose) { \
blog(level, msg, ##__VA_ARGS__); \
}
@@ -43,6 +44,10 @@ public:
void OpenSequenceExtendEdit(SequenceWidget *sw);
void SetEditSceneGroup(SceneGroup &sg);
bool addNewMacro(std::string &name);
Macro *getSelectedMacro();
void SetEditMacro(Macro &m);
void loadUI();
void setupGeneralTab();
void setupTitleTab();
@@ -61,48 +66,14 @@ public:
void setupTriggerTab();
void setupVideoTab();
void setupNetworkTab();
void setupMacroTab();
void setTabOrder();
void restoreWindowGeo();
static bool DisplayMessage(QString msg, bool question = false);
static void AskBackup(obs_data_t *obj);
static void addSelectionEntry(QComboBox *sel, const char *description,
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);
@@ -115,8 +86,10 @@ public slots:
void on_noMatchDontSwitch_clicked();
void on_noMatchSwitch_clicked();
void on_noMatchRandomSwitch_clicked();
void on_noMatchDelay_valueChanged(double i);
void on_cooldownTime_valueChanged(double i);
void NoMatchDelayDurationChanged(double);
void NoMatchDelayUnitChanged(DurationUnit);
void CooldownDurationChanged(double);
void CooldownUnitChanged(DurationUnit);
void on_startupBehavior_currentIndexChanged(int index);
void on_autoStartEvent_currentIndexChanged(int index);
void on_noMatchSwitchScene_currentTextChanged(const QString &text);
@@ -125,6 +98,20 @@ public slots:
void on_tabMoved(int from, int to);
void on_tabWidget_currentChanged(int index);
void on_macroAdd_clicked();
void on_macroRemove_clicked();
void on_macroUp_clicked();
void on_macroDown_clicked();
void on_macroName_editingFinished();
void on_macros_currentRowChanged(int idx);
void on_macros_itemChanged(QListWidgetItem *);
void on_conditionAdd_clicked();
void on_conditionRemove_clicked();
void on_actionAdd_clicked();
void on_actionRemove_clicked();
void showMacroContextMenu(const QPoint &);
void copyMacro();
void on_screenRegionSwitches_currentRowChanged(int idx);
void on_showFrame_clicked();
void on_screenRegionAdd_clicked();
@@ -224,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();
@@ -257,45 +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(OBSWeakSource &targetScene, obs_source_t *currentSource,
OBSWeakSource &transition,
bool transitionOverrideOverride,
bool adjustActiveTransitionType, transitionData &td);
void overwriteTransitionOverride(obs_weak_source_t *sceneWs,
obs_source_t *transition, transitionData &td);
void restoreTransitionOverride(obs_source_t *scene, transitionData td);
void switchScene(OBSWeakSource &scene, OBSWeakSource &transition,
bool transitionOverrideOverride,
bool adjustActiveTransitionType, bool verbose);
void setNextTransition(const sceneSwitchInfo &ssi, obs_source_t *currentSource,
transitionData &td);
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

@@ -5,9 +5,9 @@
#if defined(WIN32)
constexpr auto curl_library_name = "libcurl.dll";
#elif __APPLE__
#define curl_library_name "libcurl.4.dylib"
constexpr auto curl_library_name = "libcurl.4.dylib";
#else
#define curl_library_name "libcurl.so.4"
constexpr auto curl_library_name = "libcurl.so.4";
#endif
typedef CURL *(*initFunction)(void);

View File

@@ -0,0 +1,111 @@
#pragma once
#include <QWidget>
#include <QDoubleSpinBox>
#include <QComboBox>
#include <QPushButton>
#include <chrono>
#include "obs-data.h"
enum class DurationUnit {
SECONDS,
MINUTES,
HOURS,
};
class Duration {
public:
void Save(obs_data_t *obj, const char *secondsName = "seconds",
const char *unitName = "displayUnit");
void Load(obs_data_t *obj, const char *secondsName = "seconds",
const char *unitName = "displayUnit");
bool DurationReached();
double TimeRemaining();
void Reset();
std::string ToString();
double seconds = 0.;
// only used for UI
DurationUnit displayUnit = DurationUnit::SECONDS;
private:
std::chrono::high_resolution_clock::time_point _startTime;
};
class DurationSelection : public QWidget {
Q_OBJECT
public:
DurationSelection(QWidget *parent = nullptr,
bool showUnitSelection = true);
void SetValue(double value);
void SetUnit(DurationUnit u);
void SetDuration(Duration d);
private slots:
void _DurationChanged(double value);
void _UnitChanged(int idx);
signals:
void DurationChanged(double value); // always reutrn value in seconds
void UnitChanged(DurationUnit u);
private:
QDoubleSpinBox *_duration;
QComboBox *_unitSelection;
double _unitMultiplier;
};
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;
};

125
src/headers/hotkey.hpp Normal file
View File

@@ -0,0 +1,125 @@
#pragma once
extern bool canSimulateKeyPresses;
enum class HotkeyType {
Key_NoKey = 0,
Key_A,
Key_B,
Key_C,
Key_D,
Key_E,
Key_F,
Key_G,
Key_H,
Key_I,
Key_J,
Key_K,
Key_L,
Key_M,
Key_N,
Key_O,
Key_P,
Key_Q,
Key_R,
Key_S,
Key_T,
Key_U,
Key_V,
Key_W,
Key_X,
Key_Y,
Key_Z,
Key_0,
Key_1,
Key_2,
Key_3,
Key_4,
Key_5,
Key_6,
Key_7,
Key_8,
Key_9,
Key_F1,
Key_F2,
Key_F3,
Key_F4,
Key_F5,
Key_F6,
Key_F7,
Key_F8,
Key_F9,
Key_F10,
Key_F11,
Key_F12,
Key_F13,
Key_F14,
Key_F15,
Key_F16,
Key_F17,
Key_F18,
Key_F19,
Key_F20,
Key_F21,
Key_F22,
Key_F23,
Key_F24,
Key_Escape,
Key_Space,
Key_Return,
Key_Backspace,
Key_Tab,
Key_Shift_L,
Key_Shift_R,
Key_Control_L,
Key_Control_R,
Key_Alt_L,
Key_Alt_R,
Key_Win_L,
Key_Win_R,
Key_Apps,
Key_CapsLock,
Key_NumLock,
Key_ScrollLock,
Key_PrintScreen,
Key_Pause,
Key_Insert,
Key_Delete,
Key_PageUP,
Key_PageDown,
Key_Home,
Key_End,
Key_Left,
Key_Right,
Key_Up,
Key_Down,
Key_Numpad0,
Key_Numpad1,
Key_Numpad2,
Key_Numpad3,
Key_Numpad4,
Key_Numpad5,
Key_Numpad6,
Key_Numpad7,
Key_Numpad8,
Key_Numpad9,
Key_NumpadAdd,
Key_NumpadSubtract,
Key_NumpadMultiply,
Key_NumpadDivide,
Key_NumpadDecimal,
Key_NumpadEnter
};
void registerHotkeys();

View File

@@ -0,0 +1,67 @@
#pragma once
#include <QSpinBox>
#include "macro-action-edit.hpp"
enum class AudioAction {
MUTE,
UNMUTE,
SOURCE_VOLUME,
MASTER_VOLUME,
};
class MacroActionAudio : public MacroAction {
public:
bool PerformAction();
void LogAction();
bool Save(obs_data_t *obj);
bool Load(obs_data_t *obj);
std::string GetShortDesc();
std::string GetId() { return id; };
static std::shared_ptr<MacroAction> Create()
{
return std::make_shared<MacroActionAudio>();
}
OBSWeakSource _audioSource;
AudioAction _action = AudioAction::MUTE;
int _volume = 0;
private:
static bool _registered;
static const std::string id;
};
class MacroActionAudioEdit : public QWidget {
Q_OBJECT
public:
MacroActionAudioEdit(
QWidget *parent,
std::shared_ptr<MacroActionAudio> entryData = nullptr);
void SetWidgetVisibility();
void UpdateEntryData();
static QWidget *Create(QWidget *parent,
std::shared_ptr<MacroAction> action)
{
return new MacroActionAudioEdit(
parent,
std::dynamic_pointer_cast<MacroActionAudio>(action));
}
private slots:
void SourceChanged(const QString &text);
void ActionChanged(int value);
void VolumeChanged(int value);
signals:
void HeaderInfoChanged(const QString &);
protected:
QComboBox *_audioSources;
QComboBox *_actions;
QSpinBox *_volumePercent;
std::shared_ptr<MacroActionAudio> _entryData;
private:
QHBoxLayout *_mainLayout;
bool _loading = true;
};

View File

@@ -0,0 +1,66 @@
#pragma once
#include <QWidget>
#include <QComboBox>
#include <QVBoxLayout>
#include <QGroupBox>
#include <QLabel>
#include <deque>
#include "macro.hpp"
#include "section.hpp"
struct MacroActionInfo {
using TCreateMethod = std::shared_ptr<MacroAction> (*)();
using TCreateWidgetMethod = QWidget *(*)(QWidget *parent,
std::shared_ptr<MacroAction>);
TCreateMethod _createFunc;
TCreateWidgetMethod _createWidgetFunc;
std::string _name;
};
class MacroActionFactory {
public:
MacroActionFactory() = delete;
static bool Register(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<std::string, MacroActionInfo> _methods;
};
class MacroActionEdit : public QWidget {
Q_OBJECT
public:
MacroActionEdit(QWidget *parent = nullptr,
std::shared_ptr<MacroAction> * = nullptr,
const std::string &id = "scene_switch",
bool startCollapsed = false);
void UpdateEntryData(const std::string &id, bool collapse);
private slots:
void ActionSelectionChanged(const QString &text);
void HeaderInfoChanged(const QString &);
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);
protected:
QComboBox *_actionSelection;
Section *_section;
QLabel *_headerInfo;
std::shared_ptr<MacroAction> *_entryData;
private:
bool _loading = true;
};

View File

@@ -0,0 +1,66 @@
#pragma once
#include <QSpinBox>
#include <QPlainTextEdit>
#include "macro-action-edit.hpp"
enum class FileAction {
WRITE,
APPEND,
};
class MacroActionFile : public MacroAction {
public:
bool PerformAction();
void LogAction();
bool Save(obs_data_t *obj);
bool Load(obs_data_t *obj);
std::string GetShortDesc();
std::string GetId() { return id; };
static std::shared_ptr<MacroAction> Create()
{
return std::make_shared<MacroActionFile>();
}
std::string _file = obs_module_text("AdvSceneSwitcher.enterPath");
std::string _text = obs_module_text("AdvSceneSwitcher.enterText");
FileAction _action = FileAction::WRITE;
private:
static bool _registered;
static const std::string id;
};
class MacroActionFileEdit : public QWidget {
Q_OBJECT
public:
MacroActionFileEdit(
QWidget *parent,
std::shared_ptr<MacroActionFile> entryData = nullptr);
void UpdateEntryData();
static QWidget *Create(QWidget *parent,
std::shared_ptr<MacroAction> action)
{
return new MacroActionFileEdit(
parent,
std::dynamic_pointer_cast<MacroActionFile>(action));
}
private slots:
void FilePathChanged();
void BrowseButtonClicked();
void TextChanged();
void ActionChanged(int value);
signals:
void HeaderInfoChanged(const QString &);
protected:
QLineEdit *_filePath;
QPushButton *_browseButton;
QPlainTextEdit *_text;
QComboBox *_actions;
std::shared_ptr<MacroActionFile> _entryData;
private:
bool _loading = true;
};

View File

@@ -0,0 +1,71 @@
#pragma once
#include <QSpinBox>
#include <QPlainTextEdit>
#include "macro-action-edit.hpp"
enum class FilterAction {
ENABLE,
DISABLE,
SETTINGS,
};
class MacroActionFilter : public MacroAction {
public:
bool PerformAction();
void LogAction();
bool Save(obs_data_t *obj);
bool Load(obs_data_t *obj);
std::string GetShortDesc();
std::string GetId() { return id; };
static std::shared_ptr<MacroAction> Create()
{
return std::make_shared<MacroActionFilter>();
}
OBSWeakSource _source;
OBSWeakSource _filter;
FilterAction _action = FilterAction::ENABLE;
std::string _settings = "";
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);
void GetSettingsClicked();
void SettingsChanged();
signals:
void HeaderInfoChanged(const QString &);
protected:
QComboBox *_sources;
QComboBox *_filters;
QComboBox *_actions;
QPushButton *_getSettings;
QPlainTextEdit *_settings;
std::shared_ptr<MacroActionFilter> _entryData;
private:
void SetWidgetVisibility(bool);
bool _loading = true;
};

View File

@@ -0,0 +1,78 @@
#pragma once
#include "macro-action-edit.hpp"
#include "hotkey.hpp"
#include <QCheckBox>
class MacroActionHotkey : 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<MacroActionHotkey>();
}
OBSWeakSource _HotkeySource;
HotkeyType _key = HotkeyType::Key_NoKey;
bool _leftShift = false;
bool _rightShift = false;
bool _leftCtrl = false;
bool _rightCtrl = false;
bool _leftAlt = false;
bool _rightAlt = false;
bool _leftMeta = false;
bool _rightMeta = false;
private:
static bool _registered;
static const std::string id;
};
class MacroActionHotkeyEdit : public QWidget {
Q_OBJECT
public:
MacroActionHotkeyEdit(
QWidget *parent,
std::shared_ptr<MacroActionHotkey> entryData = nullptr);
void UpdateEntryData();
static QWidget *Create(QWidget *parent,
std::shared_ptr<MacroAction> action)
{
return new MacroActionHotkeyEdit(
parent,
std::dynamic_pointer_cast<MacroActionHotkey>(action));
}
private slots:
void KeyChanged(int key);
void LShiftChanged(int state);
void RShiftChanged(int state);
void LCtrlChanged(int state);
void RCtrlChanged(int state);
void LAltChanged(int state);
void RAltChanged(int state);
void LMetaChanged(int state);
void RMetaChanged(int state);
protected:
QComboBox *_keys;
QCheckBox *_leftShift;
QCheckBox *_rightShift;
QCheckBox *_leftCtrl;
QCheckBox *_rightCtrl;
QCheckBox *_leftAlt;
QCheckBox *_rightAlt;
QCheckBox *_leftMeta;
QCheckBox *_rightMeta;
std::shared_ptr<MacroActionHotkey> _entryData;
private:
QHBoxLayout *_mainLayout;
bool _loading = true;
};

View File

@@ -0,0 +1,63 @@
#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 GetShortDesc();
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);
signals:
void HeaderInfoChanged(const QString &);
protected:
MacroSelection *_macros;
QComboBox *_actions;
std::shared_ptr<MacroActionMacro> _entryData;
private:
QHBoxLayout *_mainLayout;
bool _loading = true;
};

View File

@@ -0,0 +1,65 @@
#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 GetShortDesc();
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);
signals:
void HeaderInfoChanged(const QString &);
protected:
QComboBox *_mediaSources;
QComboBox *_actions;
std::shared_ptr<MacroActionMedia> _entryData;
private:
QHBoxLayout *_mainLayout;
bool _loading = true;
};

View File

@@ -0,0 +1,63 @@
#pragma once
#include <QDoubleSpinBox>
#include "macro-action-edit.hpp"
enum class PluginStateAction {
STOP,
NO_MATCH_BEHAVIOUR,
};
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;
int _value = 0;
OBSWeakSource _scene;
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);
void ValueChanged(int value);
void SceneChanged(const QString &text);
protected:
QComboBox *_actions;
QComboBox *_values;
QComboBox *_scenes;
std::shared_ptr<MacroActionPluginState> _entryData;
private:
void SetWidgetVisibility(PluginStateAction, int);
QHBoxLayout *_mainLayout;
bool _loading = true;
};

View File

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

View File

@@ -0,0 +1,66 @@
#pragma once
#include <QDoubleSpinBox>
#include "macro-action-edit.hpp"
#include "duration-control.hpp"
enum class ReplayBufferAction {
STOP,
START,
SAVE,
};
class MacroActionReplayBuffer : public MacroAction {
public:
bool PerformAction();
void LogAction();
bool Save(obs_data_t *obj);
bool Load(obs_data_t *obj);
std::string GetId() { return id; };
static std::shared_ptr<MacroAction> Create()
{
return std::make_shared<MacroActionReplayBuffer>();
}
ReplayBufferAction _action = ReplayBufferAction::STOP;
private:
// Add artifical delay before trying to save the replay buffer again.
//
// Continiously calling obs_frontend_replay_buffer_save() does not
// result in any output actually being written.
// OBS_FRONTEND_EVENT_REPLAY_BUFFER_SAVED also seems to be sent before
// any data is written.
Duration _duration;
static bool _registered;
static const std::string id;
};
class MacroActionReplayBufferEdit : public QWidget {
Q_OBJECT
public:
MacroActionReplayBufferEdit(
QWidget *parent,
std::shared_ptr<MacroActionReplayBuffer> entryData = nullptr);
void UpdateEntryData();
static QWidget *Create(QWidget *parent,
std::shared_ptr<MacroAction> action)
{
return new MacroActionReplayBufferEdit(
parent,
std::dynamic_pointer_cast<MacroActionReplayBuffer>(
action));
}
private slots:
void ActionChanged(int value);
protected:
QComboBox *_actions;
std::shared_ptr<MacroActionReplayBuffer> _entryData;
private:
QHBoxLayout *_mainLayout;
bool _loading = true;
};

View File

@@ -0,0 +1,67 @@
#pragma once
#include "macro-action-edit.hpp"
#include <QLineEdit>
#include <QPushButton>
#include <QListWidget>
#include <QStringList>
class MacroActionRun : public MacroAction {
public:
bool PerformAction();
void LogAction();
bool Save(obs_data_t *obj);
bool Load(obs_data_t *obj);
std::string GetShortDesc();
std::string GetId() { return id; };
static std::shared_ptr<MacroAction> Create()
{
return std::make_shared<MacroActionRun>();
}
std::string _path = obs_module_text("AdvSceneSwitcher.enterPath");
QStringList _args;
private:
static bool _registered;
static const std::string id;
};
class MacroActionRunEdit : public QWidget {
Q_OBJECT
public:
MacroActionRunEdit(QWidget *parent,
std::shared_ptr<MacroActionRun> entryData = nullptr);
void UpdateEntryData();
static QWidget *Create(QWidget *parent,
std::shared_ptr<MacroAction> action)
{
return new MacroActionRunEdit(
parent,
std::dynamic_pointer_cast<MacroActionRun>(action));
}
private slots:
void FilePathChanged();
void BrowseButtonClicked();
void AddArg();
void RemoveArg();
void ArgUp();
void ArgDown();
signals:
void HeaderInfoChanged(const QString &);
protected:
std::shared_ptr<MacroActionRun> _entryData;
private:
QLineEdit *_filePath;
QPushButton *_browseButton;
QListWidget *_argList;
QPushButton *_addArg;
QPushButton *_removeArg;
QPushButton *_argUp;
QPushButton *_argDown;
bool _loading = true;
};

View File

@@ -0,0 +1,72 @@
#pragma once
#include <QSpinBox>
#include "macro-action-edit.hpp"
#include "scene-selection.hpp"
enum class SceneOrderAction {
MOVE_UP,
MOVE_DOWN,
MOVE_TOP,
MOVE_BOTTOM,
POSITION,
};
class MacroActionSceneOrder : public MacroAction {
public:
bool PerformAction();
void LogAction();
bool Save(obs_data_t *obj);
bool Load(obs_data_t *obj);
std::string GetShortDesc();
std::string GetId() { return id; };
static std::shared_ptr<MacroAction> Create()
{
return std::make_shared<MacroActionSceneOrder>();
}
SceneSelection _scene;
OBSWeakSource _source;
SceneOrderAction _action = SceneOrderAction::MOVE_UP;
int _position = 0;
private:
static bool _registered;
static const std::string id;
};
class MacroActionSceneOrderEdit : public QWidget {
Q_OBJECT
public:
MacroActionSceneOrderEdit(
QWidget *parent,
std::shared_ptr<MacroActionSceneOrder> entryData = nullptr);
void UpdateEntryData();
static QWidget *Create(QWidget *parent,
std::shared_ptr<MacroAction> action)
{
return new MacroActionSceneOrderEdit(
parent,
std::dynamic_pointer_cast<MacroActionSceneOrder>(
action));
}
private slots:
void SceneChanged(const SceneSelection &);
void SourceChanged(const QString &text);
void ActionChanged(int value);
void PositionChanged(int value);
signals:
void HeaderInfoChanged(const QString &);
protected:
SceneSelectionWidget *_scenes;
QComboBox *_sources;
QComboBox *_actions;
QSpinBox *_position;
std::shared_ptr<MacroActionSceneOrder> _entryData;
private:
QHBoxLayout *_mainLayout;
bool _loading = true;
};

View File

@@ -0,0 +1,55 @@
#pragma once
#include "macro-action-edit.hpp"
#include "switch-generic.hpp"
#include "duration-control.hpp"
// TODO: Switch to using SceneSelection class and widget instead
class MacroActionSwitchScene : public MacroAction, public SceneSwitcherEntry {
public:
bool PerformAction();
void LogAction();
bool Save(obs_data_t *obj);
bool Load(obs_data_t *obj);
std::string GetShortDesc();
std::string GetId() { return id; };
static std::shared_ptr<MacroAction> Create()
{
return std::make_shared<MacroActionSwitchScene>();
}
Duration _duration;
private:
const char *getType() { return "MacroActionSwitchScene"; }
static bool _registered;
static const std::string id;
};
class MacroActionSwitchSceneEdit : public SwitchWidget {
Q_OBJECT
public:
MacroActionSwitchSceneEdit(
QWidget *parent,
std::shared_ptr<MacroActionSwitchScene> entryData = nullptr);
static QWidget *Create(QWidget *parent,
std::shared_ptr<MacroAction> action)
{
return new MacroActionSwitchSceneEdit(
parent,
std::dynamic_pointer_cast<MacroActionSwitchScene>(
action));
}
private slots:
void DurationChanged(double seconds);
void ChangeHeaderInfo(const QString &);
signals:
void HeaderInfoChanged(const QString &);
protected:
DurationSelection *_duration;
std::shared_ptr<MacroActionSwitchScene> _entryData;
};

View File

@@ -0,0 +1,66 @@
#pragma once
#include <QSpinBox>
#include <QPlainTextEdit>
#include "macro-action-edit.hpp"
#include "scene-selection.hpp"
class MacroActionSceneTransform : public MacroAction {
public:
bool PerformAction();
void LogAction();
bool Save(obs_data_t *obj);
bool Load(obs_data_t *obj);
std::string GetShortDesc();
std::string GetId() { return id; };
static std::shared_ptr<MacroAction> Create()
{
return std::make_shared<MacroActionSceneTransform>();
}
std::string GetSettings();
void SetSettings(std::string &);
SceneSelection _scene;
OBSWeakSource _source;
struct obs_transform_info _info;
struct obs_sceneitem_crop _crop;
private:
static bool _registered;
static const std::string id;
};
class MacroActionSceneTransformEdit : public QWidget {
Q_OBJECT
public:
MacroActionSceneTransformEdit(
QWidget *parent,
std::shared_ptr<MacroActionSceneTransform> entryData = nullptr);
void UpdateEntryData();
static QWidget *Create(QWidget *parent,
std::shared_ptr<MacroAction> action)
{
return new MacroActionSceneTransformEdit(
parent,
std::dynamic_pointer_cast<MacroActionSceneTransform>(
action));
}
private slots:
void SceneChanged(const SceneSelection &);
void SourceChanged(const QString &text);
void GetSettingsClicked();
void SettingsChanged();
signals:
void HeaderInfoChanged(const QString &);
protected:
SceneSelectionWidget *_scenes;
QComboBox *_sources;
QPushButton *_getSettings;
QPlainTextEdit *_settings;
std::shared_ptr<MacroActionSceneTransform> _entryData;
private:
bool _loading = true;
};

View File

@@ -0,0 +1,66 @@
#pragma once
#include <QSpinBox>
#include "macro-action-edit.hpp"
#include "scene-selection.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 GetShortDesc();
std::string GetId() { return id; };
static std::shared_ptr<MacroAction> Create()
{
return std::make_shared<MacroActionSceneVisibility>();
}
SceneSelection _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 SceneSelection &);
void SourceChanged(const QString &text);
void ActionChanged(int value);
signals:
void HeaderInfoChanged(const QString &);
protected:
SceneSelectionWidget *_scenes;
QComboBox *_sources;
QComboBox *_actions;
std::shared_ptr<MacroActionSceneVisibility> _entryData;
private:
QHBoxLayout *_mainLayout;
bool _loading = true;
};

View File

@@ -0,0 +1,72 @@
#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 GetShortDesc();
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();
signals:
void HeaderInfoChanged(const QString &);
protected:
QComboBox *_sources;
QComboBox *_actions;
QPushButton *_getSettings;
QPlainTextEdit *_settings;
QLabel *_warning;
std::shared_ptr<MacroActionSource> _entryData;
private:
void SetWidgetVisibility(bool);
bool _loading = true;
};

View File

@@ -0,0 +1,60 @@
#pragma once
#include <QDoubleSpinBox>
#include "macro-action-edit.hpp"
#include "duration-control.hpp"
enum class StreamAction {
STOP,
START,
};
class MacroActionStream : public MacroAction {
public:
bool PerformAction();
void LogAction();
bool Save(obs_data_t *obj);
bool Load(obs_data_t *obj);
std::string GetId() { return id; };
static std::shared_ptr<MacroAction> Create()
{
return std::make_shared<MacroActionStream>();
}
StreamAction _action = StreamAction::STOP;
private:
// Acts as a safeguard for misconfigured streaming setups leading to an
// endless error spam.
Duration _retryCooldown;
static bool _registered;
static const std::string id;
};
class MacroActionStreamEdit : public QWidget {
Q_OBJECT
public:
MacroActionStreamEdit(
QWidget *parent,
std::shared_ptr<MacroActionStream> entryData = nullptr);
void UpdateEntryData();
static QWidget *Create(QWidget *parent,
std::shared_ptr<MacroAction> action)
{
return new MacroActionStreamEdit(
parent,
std::dynamic_pointer_cast<MacroActionStream>(action));
}
private slots:
void ActionChanged(int value);
protected:
QComboBox *_actions;
std::shared_ptr<MacroActionStream> _entryData;
private:
QHBoxLayout *_mainLayout;
bool _loading = true;
};

View File

@@ -0,0 +1,55 @@
#pragma once
#include <QDoubleSpinBox>
#include "macro-action-edit.hpp"
enum class VCamAction {
STOP,
START,
};
class MacroActionVCam : 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<MacroActionVCam>();
}
VCamAction _action = VCamAction::STOP;
private:
static bool _registered;
static const std::string id;
};
class MacroActionVCamEdit : public QWidget {
Q_OBJECT
public:
MacroActionVCamEdit(
QWidget *parent,
std::shared_ptr<MacroActionVCam> entryData = nullptr);
void UpdateEntryData();
static QWidget *Create(QWidget *parent,
std::shared_ptr<MacroAction> action)
{
return new MacroActionVCamEdit(
parent,
std::dynamic_pointer_cast<MacroActionVCam>(action));
}
private slots:
void ActionChanged(int value);
protected:
QComboBox *_actions;
std::shared_ptr<MacroActionVCam> _entryData;
private:
QHBoxLayout *_mainLayout;
bool _loading = true;
};

View File

@@ -0,0 +1,64 @@
#pragma once
#include <QDoubleSpinBox>
#include "macro-action-edit.hpp"
#include "duration-control.hpp"
enum class WaitType {
FIXED,
RANDOM,
};
class MacroActionWait : public MacroAction {
public:
bool PerformAction();
bool Save(obs_data_t *obj);
bool Load(obs_data_t *obj);
std::string GetId() { return id; };
static std::shared_ptr<MacroAction> Create()
{
return std::make_shared<MacroActionWait>();
}
Duration _duration;
Duration _duration2;
WaitType _waitType = WaitType::FIXED;
private:
static bool _registered;
static const std::string id;
};
class MacroActionWaitEdit : public QWidget {
Q_OBJECT
public:
MacroActionWaitEdit(
QWidget *parent,
std::shared_ptr<MacroActionWait> entryData = nullptr);
void UpdateEntryData();
void SetupFixedDurationEdit();
void SetupRandomDurationEdit();
static QWidget *Create(QWidget *parent,
std::shared_ptr<MacroAction> action)
{
return new MacroActionWaitEdit(
parent,
std::dynamic_pointer_cast<MacroActionWait>(action));
}
private slots:
void DurationChanged(double value);
void DurationUnitChanged(DurationUnit unit);
void Duration2Changed(double value);
void Duration2UnitChanged(DurationUnit unit);
void TypeChanged(int value);
protected:
DurationSelection *_duration;
DurationSelection *_duration2;
QComboBox *_waitType;
std::shared_ptr<MacroActionWait> _entryData;
private:
QHBoxLayout *_mainLayout;
bool _loading = true;
};

View File

@@ -0,0 +1,76 @@
#pragma once
#include "macro.hpp"
#include "volume-control.hpp"
#include <limits>
#include <QWidget>
#include <QComboBox>
#include <chrono>
enum class AudioCondition {
ABOVE,
BELOW,
};
class MacroConditionAudio : public MacroCondition {
public:
~MacroConditionAudio();
bool CheckCondition();
bool Save(obs_data_t *obj);
bool Load(obs_data_t *obj);
std::string GetShortDesc();
std::string GetId() { return id; };
static std::shared_ptr<MacroCondition> Create()
{
return std::make_shared<MacroConditionAudio>();
}
static void SetVolumeLevel(void *data,
const float magnitude[MAX_AUDIO_CHANNELS],
const float peak[MAX_AUDIO_CHANNELS],
const float inputPeak[MAX_AUDIO_CHANNELS]);
void ResetVolmeter();
OBSWeakSource _audioSource;
int _volume = 0;
AudioCondition _condition = AudioCondition::ABOVE;
obs_volmeter_t *_volmeter = nullptr;
private:
float _peak = -std::numeric_limits<float>::infinity();
static bool _registered;
static const std::string id;
};
class MacroConditionAudioEdit : public QWidget {
Q_OBJECT
public:
MacroConditionAudioEdit(
QWidget *parent,
std::shared_ptr<MacroConditionAudio> cond = nullptr);
void UpdateEntryData();
static QWidget *Create(QWidget *parent,
std::shared_ptr<MacroCondition> cond)
{
return new MacroConditionAudioEdit(
parent,
std::dynamic_pointer_cast<MacroConditionAudio>(cond));
}
void UpdateVolmeterSource();
private slots:
void SourceChanged(const QString &text);
void VolumeThresholdChanged(int vol);
void ConditionChanged(int cond);
signals:
void HeaderInfoChanged(const QString &);
protected:
QComboBox *_audioSources;
QComboBox *_condition;
QSpinBox *_volume;
VolControl *_volMeter = nullptr;
std::shared_ptr<MacroConditionAudio> _entryData;
private:
bool _loading = true;
};

View File

@@ -0,0 +1,67 @@
#pragma once
#include "macro.hpp"
#include <QWidget>
#include <QComboBox>
#include <QDateTimeEdit>
#include "duration-control.hpp"
class MacroConditionDate : public MacroCondition {
public:
bool CheckCondition();
bool Save(obs_data_t *obj);
bool Load(obs_data_t *obj);
std::string GetShortDesc();
std::string GetId() { return id; };
static std::shared_ptr<MacroCondition> Create()
{
return std::make_shared<MacroConditionDate>();
}
QDateTime _dateTime;
bool _ignoreDate = false;
bool _ignoreTime = false;
bool _repeat = false;
Duration _duration;
private:
static bool _registered;
static const std::string id;
};
class MacroConditionDateEdit : public QWidget {
Q_OBJECT
public:
MacroConditionDateEdit(
QWidget *parent,
std::shared_ptr<MacroConditionDate> cond = nullptr);
void UpdateEntryData();
static QWidget *Create(QWidget *parent,
std::shared_ptr<MacroCondition> cond)
{
return new MacroConditionDateEdit(
parent,
std::dynamic_pointer_cast<MacroConditionDate>(cond));
}
private slots:
void DateTimeChanged(const QDateTime &datetime);
void IgnoreDateChanged(int state);
void IgnoreTimeChanged(int state);
void RepeatChanged(int state);
void DurationChanged(double seconds);
void DurationUnitChanged(DurationUnit unit);
signals:
void HeaderInfoChanged(const QString &);
protected:
QDateTimeEdit *_dateTime;
QCheckBox *_ignoreDate;
QCheckBox *_ignoreTime;
QCheckBox *_repeat;
DurationSelection *_duration;
std::shared_ptr<MacroConditionDate> _entryData;
private:
bool _loading = true;
};

View File

@@ -0,0 +1,75 @@
#pragma once
#include "advanced-scene-switcher.hpp"
#include "macro.hpp"
#include "macro-condition-scene.hpp"
#include "section.hpp"
#include "utility.hpp"
#include <QGroupBox>
struct MacroConditionInfo {
using TCreateMethod = std::shared_ptr<MacroCondition> (*)();
using TCreateWidgetMethod =
QWidget *(*)(QWidget *parent, std::shared_ptr<MacroCondition>);
TCreateMethod _createFunc;
TCreateWidgetMethod _createWidgetFunc;
std::string _name;
bool _useDurationConstraint = true;
};
class MacroConditionFactory {
public:
MacroConditionFactory() = delete;
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<std::string, MacroConditionInfo> _methods;
};
class MacroConditionEdit : public QWidget {
Q_OBJECT
public:
MacroConditionEdit(QWidget *parent = nullptr,
std::shared_ptr<MacroCondition> * = nullptr,
const std::string &id = "scene", bool root = true,
bool startCollapsed = false);
bool IsRootNode();
void UpdateEntryData(const std::string &id, bool collapse);
private slots:
void LogicSelectionChanged(int idx);
void ConditionSelectionChanged(const QString &text);
void DurationChanged(double seconds);
void DurationConditionChanged(DurationCondition cond);
void DurationUnitChanged(DurationUnit unit);
void HeaderInfoChanged(const QString &);
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);
protected:
QComboBox *_logicSelection;
QComboBox *_conditionSelection;
Section *_section;
QLabel *_headerInfo;
DurationConstraintEdit *_dur;
std::shared_ptr<MacroCondition> *_entryData;
private:
bool _isRoot = true;
bool _loading = true;
};

View File

@@ -0,0 +1,85 @@
#pragma once
#include "macro.hpp"
#include <QWidget>
#include <QComboBox>
#include <QDateTime>
#include <QLineEdit>
#include <QPushButton>
#include <QPlainTextEdit>
#include <QCheckBox>
enum class FileType {
LOCAL,
REMOTE,
};
class MacroConditionFile : public MacroCondition {
public:
bool CheckCondition();
bool Save(obs_data_t *obj);
bool Load(obs_data_t *obj);
std::string GetShortDesc();
std::string GetId() { return id; };
static std::shared_ptr<MacroCondition> Create()
{
return std::make_shared<MacroConditionFile>();
}
std::string _file = obs_module_text("AdvSceneSwitcher.enterPath");
std::string _text = obs_module_text("AdvSceneSwitcher.enterText");
FileType _fileType = FileType::LOCAL;
bool _useRegex = false;
bool _useTime = false;
bool _onlyMatchIfChanged = false;
private:
bool matchFileContent(QString &filedata);
bool checkRemoteFileContent();
bool checkLocalFileContent();
QDateTime _lastMod;
size_t _lastHash = 0;
static bool _registered;
static const std::string id;
};
class MacroConditionFileEdit : public QWidget {
Q_OBJECT
public:
MacroConditionFileEdit(
QWidget *parent,
std::shared_ptr<MacroConditionFile> cond = nullptr);
void UpdateEntryData();
static QWidget *Create(QWidget *parent,
std::shared_ptr<MacroCondition> cond)
{
return new MacroConditionFileEdit(
parent,
std::dynamic_pointer_cast<MacroConditionFile>(cond));
}
private slots:
void FileTypeChanged(int index);
void FilePathChanged();
void BrowseButtonClicked();
void MatchTextChanged();
void UseRegexChanged(int state);
void CheckModificationDateChanged(int state);
void OnlyMatchIfChangedChanged(int state);
signals:
void HeaderInfoChanged(const QString &);
protected:
QComboBox *_fileType;
QLineEdit *_filePath;
QPushButton *_browseButton;
QPlainTextEdit *_matchText;
QCheckBox *_useRegex;
QCheckBox *_checkModificationDate;
QCheckBox *_checkFileContent;
std::shared_ptr<MacroConditionFile> _entryData;
private:
bool _loading = true;
};

View File

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

View File

@@ -0,0 +1,54 @@
#pragma once
#include "macro.hpp"
#include <QWidget>
#include <QLineEdit>
class MacroConditionHotkey : public MacroCondition {
public:
MacroConditionHotkey();
~MacroConditionHotkey();
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<MacroConditionHotkey>();
}
void SetPressed() { _pressed = true; }
std::string _name;
obs_hotkey_id _hotkeyID = OBS_INVALID_HOTKEY_ID;
private:
bool _pressed = false;
static bool _registered;
static const std::string id;
};
class MacroConditionHotkeyEdit : public QWidget {
Q_OBJECT
public:
MacroConditionHotkeyEdit(
QWidget *parent,
std::shared_ptr<MacroConditionHotkey> cond = nullptr);
void UpdateEntryData();
static QWidget *Create(QWidget *parent,
std::shared_ptr<MacroCondition> cond)
{
return new MacroConditionHotkeyEdit(
parent,
std::dynamic_pointer_cast<MacroConditionHotkey>(cond));
}
private slots:
void NameChanged();
protected:
QLineEdit *_name;
std::shared_ptr<MacroConditionHotkey> _entryData;
private:
bool _loading = true;
};

View File

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

View File

@@ -0,0 +1,89 @@
#pragma once
#include "macro.hpp"
#include "macro-selection.hpp"
#include <QWidget>
#include <QSpinBox>
#include <QLabel>
#include <QTimer>
enum class MacroConditionMacroType {
COUNT,
STATE,
};
enum class CounterCondition {
BELOW,
ABOVE,
EQUAL,
};
class MacroConditionMacro : public MacroRefCondition {
public:
bool CheckCondition();
bool Save(obs_data_t *obj);
bool Load(obs_data_t *obj);
std::string GetShortDesc();
std::string GetId() { return id; };
static std::shared_ptr<MacroCondition> Create()
{
return std::make_shared<MacroConditionMacro>();
}
MacroConditionMacroType _type = MacroConditionMacroType::STATE;
CounterCondition _counterCondition = CounterCondition::BELOW;
int _count = 0;
private:
bool CheckCountCondition();
bool CheckStateCondition();
static bool _registered;
static const std::string id;
};
class MacroConditionMacroEdit : public QWidget {
Q_OBJECT
public:
MacroConditionMacroEdit(
QWidget *parent,
std::shared_ptr<MacroConditionMacro> cond = nullptr);
void UpdateEntryData();
static QWidget *Create(QWidget *parent,
std::shared_ptr<MacroCondition> cond)
{
return new MacroConditionMacroEdit(
parent,
std::dynamic_pointer_cast<MacroConditionMacro>(cond));
}
private slots:
void MacroChanged(const QString &text);
void MacroRemove(const QString &name);
void TypeChanged(int type);
void CountChanged(int value);
void ConditionChanged(int cond);
void ResetClicked();
void UpdateCount();
signals:
void HeaderInfoChanged(const QString &);
protected:
MacroSelection *_macros;
QComboBox *_types;
QComboBox *_counterConditions;
QSpinBox *_count;
QLabel *_currentCount;
QPushButton *_resetCount;
QHBoxLayout *_settingsLine1;
QHBoxLayout *_settingsLine2;
std::unique_ptr<QTimer> _timer;
std::shared_ptr<MacroConditionMacro> _entryData;
private:
void ClearLayouts();
void SetupStateWidgets();
void SetupCountWidgets();
void ResetTimer();
bool _loading = true;
};

View File

@@ -0,0 +1,106 @@
#pragma once
#include "macro.hpp"
#include <limits>
#include <QWidget>
#include <QComboBox>
#include "duration-control.hpp"
enum class MediaTimeRestriction {
TIME_RESTRICTION_NONE,
TIME_RESTRICTION_SHORTER,
TIME_RESTRICTION_LONGER,
TIME_RESTRICTION_REMAINING_SHORTER,
TIME_RESTRICTION_REMAINING_LONGER,
};
constexpr auto custom_media_states_offset = 100;
enum class MediaState {
// OBS's internal states
OBS_MEDIA_STATE_NONE,
OBS_MEDIA_STATE_PLAYING,
OBS_MEDIA_STATE_OPENING,
OBS_MEDIA_STATE_BUFFERING,
OBS_MEDIA_STATE_PAUSED,
OBS_MEDIA_STATE_STOPPED,
OBS_MEDIA_STATE_ENDED,
OBS_MEDIA_STATE_ERROR,
// Just a marker
LAST_OBS_MEDIA_STATE,
// states added for use in the plugin
PLAYED_TO_END = custom_media_states_offset,
ANY,
};
class MacroConditionMedia : public MacroCondition {
public:
~MacroConditionMedia();
bool CheckCondition();
bool Save(obs_data_t *obj);
bool Load(obs_data_t *obj);
std::string GetShortDesc();
std::string GetId() { return id; };
static std::shared_ptr<MacroCondition> Create()
{
return std::make_shared<MacroConditionMedia>();
}
void ClearSignalHandler();
void ResetSignalHandler();
static void MediaStopped(void *data, calldata_t *);
static void MediaEnded(void *data, calldata_t *);
OBSWeakSource _source = nullptr;
MediaState _state = MediaState::OBS_MEDIA_STATE_NONE;
MediaTimeRestriction _restriction =
MediaTimeRestriction::TIME_RESTRICTION_NONE;
Duration _time;
private:
std::atomic_bool _stopped = {false};
std::atomic_bool _ended = {false};
// Trigger scene change only once even if media state might trigger repeatedly
bool _alreadyMatched = false;
// Workaround to enable use of "ended" to specify end of VLC playlist
bool _previousStateEnded = false;
bool _playedToEnd = false;
static bool _registered;
static const std::string id;
};
class MacroConditionMediaEdit : public QWidget {
Q_OBJECT
public:
MacroConditionMediaEdit(
QWidget *parent,
std::shared_ptr<MacroConditionMedia> cond = nullptr);
void UpdateEntryData();
static QWidget *Create(QWidget *parent,
std::shared_ptr<MacroCondition> cond)
{
return new MacroConditionMediaEdit(
parent,
std::dynamic_pointer_cast<MacroConditionMedia>(cond));
}
private slots:
void SourceChanged(const QString &text);
void StateChanged(int index);
void TimeRestrictionChanged(int index);
void TimeChanged(double seconds);
void TimeUnitChanged(DurationUnit unit);
signals:
void HeaderInfoChanged(const QString &);
protected:
QComboBox *_mediaSources;
QComboBox *_states;
QComboBox *_timeRestrictions;
DurationSelection *_time;
std::shared_ptr<MacroConditionMedia> _entryData;
private:
bool _loading = true;
};

View File

@@ -0,0 +1,54 @@
#pragma once
#include "macro.hpp"
#include <QWidget>
#include <QComboBox>
enum class PluginStateCondition {
SCENESWITCHED,
};
class MacroConditionPluginState : public MacroCondition {
public:
bool CheckCondition();
bool Save(obs_data_t *obj);
bool Load(obs_data_t *obj);
std::string GetId() { return id; };
static std::shared_ptr<MacroCondition> Create()
{
return std::make_shared<MacroConditionPluginState>();
}
PluginStateCondition _condition = PluginStateCondition::SCENESWITCHED;
private:
static bool _registered;
static const std::string id;
};
class MacroConditionPluginStateEdit : public QWidget {
Q_OBJECT
public:
MacroConditionPluginStateEdit(
QWidget *parent,
std::shared_ptr<MacroConditionPluginState> cond = nullptr);
void UpdateEntryData();
static QWidget *Create(QWidget *parent,
std::shared_ptr<MacroCondition> cond)
{
return new MacroConditionPluginStateEdit(
parent,
std::dynamic_pointer_cast<MacroConditionPluginState>(
cond));
}
private slots:
void ConditionChanged(int cond);
protected:
QComboBox *_condition;
std::shared_ptr<MacroConditionPluginState> _entryData;
private:
bool _loading = true;
};

View File

@@ -0,0 +1,55 @@
#pragma once
#include "macro.hpp"
#include <QWidget>
#include <QComboBox>
class MacroConditionProcess : public MacroCondition {
public:
bool CheckCondition();
bool Save(obs_data_t *obj);
bool Load(obs_data_t *obj);
std::string GetShortDesc();
std::string GetId() { return id; };
static std::shared_ptr<MacroCondition> Create()
{
return std::make_shared<MacroConditionProcess>();
}
std::string _process;
bool _focus = true;
private:
static bool _registered;
static const std::string id;
};
class MacroConditionProcessEdit : public QWidget {
Q_OBJECT
public:
MacroConditionProcessEdit(
QWidget *parent,
std::shared_ptr<MacroConditionProcess> cond = nullptr);
void UpdateEntryData();
static QWidget *Create(QWidget *parent,
std::shared_ptr<MacroCondition> cond)
{
return new MacroConditionProcessEdit(
parent,
std::dynamic_pointer_cast<MacroConditionProcess>(cond));
}
private slots:
void ProcessChanged(const QString &text);
void FocusChanged(int state);
signals:
void HeaderInfoChanged(const QString &);
protected:
QComboBox *_processSelection;
QCheckBox *_focused;
std::shared_ptr<MacroConditionProcess> _entryData;
private:
bool _loading = true;
};

View File

@@ -0,0 +1,55 @@
#pragma once
#include "macro.hpp"
#include <QWidget>
#include <QComboBox>
enum class RecordState {
STOP,
PAUSE,
START,
};
class MacroConditionRecord : public MacroCondition {
public:
bool CheckCondition();
bool Save(obs_data_t *obj);
bool Load(obs_data_t *obj);
std::string GetId() { return id; };
static std::shared_ptr<MacroCondition> Create()
{
return std::make_shared<MacroConditionRecord>();
}
RecordState _recordState;
private:
static bool _registered;
static const std::string id;
};
class MacroConditionRecordEdit : public QWidget {
Q_OBJECT
public:
MacroConditionRecordEdit(
QWidget *parent,
std::shared_ptr<MacroConditionRecord> cond = nullptr);
void UpdateEntryData();
static QWidget *Create(QWidget *parent,
std::shared_ptr<MacroCondition> cond)
{
return new MacroConditionRecordEdit(
parent,
std::dynamic_pointer_cast<MacroConditionRecord>(cond));
}
private slots:
void StateChanged(int value);
protected:
QComboBox *_recordState;
std::shared_ptr<MacroConditionRecord> _entryData;
private:
bool _loading = true;
};

View File

@@ -0,0 +1,55 @@
#pragma once
#include "macro.hpp"
#include <QWidget>
#include <QComboBox>
class MacroConditionRegion : public MacroCondition {
public:
bool CheckCondition();
bool Save(obs_data_t *obj);
bool Load(obs_data_t *obj);
std::string GetId() { return id; };
static std::shared_ptr<MacroCondition> Create()
{
return std::make_shared<MacroConditionRegion>();
}
int _minX = 0, _minY = 0, _maxX = 0, _maxY = 0;
private:
static bool _registered;
static const std::string id;
};
class MacroConditionRegionEdit : public QWidget {
Q_OBJECT
public:
MacroConditionRegionEdit(
QWidget *parent,
std::shared_ptr<MacroConditionRegion> cond = nullptr);
void UpdateEntryData();
static QWidget *Create(QWidget *parent,
std::shared_ptr<MacroCondition> cond)
{
return new MacroConditionRegionEdit(
parent,
std::dynamic_pointer_cast<MacroConditionRegion>(cond));
}
private slots:
void MinXChanged(int pos);
void MinYChanged(int pos);
void MaxXChanged(int pos);
void MaxYChanged(int pos);
protected:
QSpinBox *_minX;
QSpinBox *_minY;
QSpinBox *_maxX;
QSpinBox *_maxY;
std::shared_ptr<MacroConditionRegion> _entryData;
private:
bool _loading = true;
};

View File

@@ -0,0 +1,56 @@
#pragma once
#include "macro.hpp"
#include <QWidget>
#include <QComboBox>
enum class ReplayBufferState {
STOP,
START,
SAVE,
};
class MacroConditionReplayBuffer : 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<MacroConditionReplayBuffer>();
}
ReplayBufferState _state;
private:
static bool _registered;
static const std::string id;
};
class MacroConditionReplayBufferEdit : public QWidget {
Q_OBJECT
public:
MacroConditionReplayBufferEdit(
QWidget *parent,
std::shared_ptr<MacroConditionReplayBuffer> cond = nullptr);
void UpdateEntryData();
static QWidget *Create(QWidget *parent,
std::shared_ptr<MacroCondition> cond)
{
return new MacroConditionReplayBufferEdit(
parent,
std::dynamic_pointer_cast<MacroConditionReplayBuffer>(
cond));
}
private slots:
void StateChanged(int value);
protected:
QComboBox *_state;
std::shared_ptr<MacroConditionReplayBuffer> _entryData;
private:
bool _loading = true;
};

View File

@@ -0,0 +1,76 @@
#pragma once
#include "macro.hpp"
#include "scene-selection.hpp"
#include <QWidget>
#include <QComboBox>
enum class SceneOrderCondition {
ABOVE,
BELOW,
POSITION,
};
class MacroConditionSceneOrder : public MacroCondition {
public:
bool CheckCondition();
bool Save(obs_data_t *obj);
bool Load(obs_data_t *obj);
std::string GetShortDesc();
std::string GetId() { return id; };
static std::shared_ptr<MacroCondition> Create()
{
return std::make_shared<MacroConditionSceneOrder>();
}
SceneSelection _scene;
OBSWeakSource _source;
OBSWeakSource _source2;
int _position = 0;
SceneOrderCondition _condition = SceneOrderCondition::ABOVE;
private:
static bool _registered;
static const std::string id;
};
class MacroConditionSceneOrderEdit : public QWidget {
Q_OBJECT
public:
MacroConditionSceneOrderEdit(
QWidget *parent,
std::shared_ptr<MacroConditionSceneOrder> cond = nullptr);
void UpdateEntryData();
static QWidget *Create(QWidget *parent,
std::shared_ptr<MacroCondition> cond)
{
return new MacroConditionSceneOrderEdit(
parent,
std::dynamic_pointer_cast<MacroConditionSceneOrder>(
cond));
}
private slots:
void SceneChanged(const SceneSelection &);
void SourceChanged(const QString &text);
void Source2Changed(const QString &text);
void ConditionChanged(int cond);
void PositionChanged(int cond);
signals:
void HeaderInfoChanged(const QString &);
protected:
SceneSelectionWidget *_scenes;
QComboBox *_conditions;
QComboBox *_sources;
QComboBox *_sources2;
QSpinBox *_position;
QLabel *_posInfo;
std::shared_ptr<MacroConditionSceneOrder> _entryData;
private:
void SetWidgetVisibility(bool showPos);
bool _loading = true;
};

View File

@@ -0,0 +1,62 @@
#pragma once
#include "macro.hpp"
#include "scene-selection.hpp"
#include <QWidget>
#include <QComboBox>
enum class SceneType {
CURRENT,
PREVIOUS,
};
class MacroConditionScene : public MacroCondition {
public:
bool CheckCondition();
bool Save(obs_data_t *obj);
bool Load(obs_data_t *obj);
std::string GetShortDesc();
std::string GetId() { return id; };
static std::shared_ptr<MacroCondition> Create()
{
return std::make_shared<MacroConditionScene>();
}
SceneSelection _scene;
SceneType _type;
private:
static bool _registered;
static const std::string id;
};
class MacroConditionSceneEdit : public QWidget {
Q_OBJECT
public:
MacroConditionSceneEdit(
QWidget *parent,
std::shared_ptr<MacroConditionScene> cond = nullptr);
void UpdateEntryData();
static QWidget *Create(QWidget *parent,
std::shared_ptr<MacroCondition> cond)
{
return new MacroConditionSceneEdit(
parent,
std::dynamic_pointer_cast<MacroConditionScene>(cond));
}
private slots:
void SceneChanged(const SceneSelection &);
void TypeChanged(int value);
signals:
void HeaderInfoChanged(const QString &);
protected:
SceneSelectionWidget *_scenes;
QComboBox *_sceneType;
std::shared_ptr<MacroConditionScene> _entryData;
private:
bool _loading = true;
};

View File

@@ -0,0 +1,71 @@
#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 GetShortDesc();
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);
signals:
void HeaderInfoChanged(const QString &);
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

@@ -0,0 +1,54 @@
#pragma once
#include "macro.hpp"
#include <QWidget>
#include <QComboBox>
enum class StreamState {
STOP,
START,
};
class MacroConditionStream : 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<MacroConditionStream>();
}
StreamState _streamState;
private:
static bool _registered;
static const std::string id;
};
class MacroConditionStreamEdit : public QWidget {
Q_OBJECT
public:
MacroConditionStreamEdit(
QWidget *parent,
std::shared_ptr<MacroConditionStream> cond = nullptr);
void UpdateEntryData();
static QWidget *Create(QWidget *parent,
std::shared_ptr<MacroCondition> cond)
{
return new MacroConditionStreamEdit(
parent,
std::dynamic_pointer_cast<MacroConditionStream>(cond));
}
private slots:
void StateChanged(int value);
protected:
QComboBox *_streamState;
std::shared_ptr<MacroConditionStream> _entryData;
private:
bool _loading = true;
};

View File

@@ -0,0 +1,60 @@
#pragma once
#include "macro.hpp"
#include <QWidget>
#include <QComboBox>
#include <QTimer>
#include "duration-control.hpp"
class MacroConditionTimer : 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<MacroConditionTimer>();
}
Duration _duration;
bool _oneshot = false;
private:
static bool _registered;
static const std::string id;
};
class MacroConditionTimerEdit : public QWidget {
Q_OBJECT
public:
MacroConditionTimerEdit(
QWidget *parent,
std::shared_ptr<MacroConditionTimer> cond = nullptr);
void UpdateEntryData();
static QWidget *Create(QWidget *parent,
std::shared_ptr<MacroCondition> cond)
{
return new MacroConditionTimerEdit(
parent,
std::dynamic_pointer_cast<MacroConditionTimer>(cond));
}
private slots:
void DurationChanged(double seconds);
void DurationUnitChanged(DurationUnit unit);
void AutoResetChanged(int state);
void ResetClicked();
void UpdateTimeRemaining();
protected:
DurationSelection *_duration;
QCheckBox *_autoReset;
QPushButton *_reset;
QLabel *_remaining;
std::shared_ptr<MacroConditionTimer> _entryData;
private:
QTimer timer;
bool _loading = true;
};

View File

@@ -0,0 +1,81 @@
#pragma once
#include "macro.hpp"
#include "screenshot-helper.hpp"
#include <QWidget>
#include <QComboBox>
#include <chrono>
enum class VideoCondition {
MATCH,
DIFFER,
HAS_NOT_CHANGED,
HAS_CHANGED,
NO_IMAGE,
};
class MacroConditionVideo : public MacroCondition {
public:
bool CheckCondition();
bool Save(obs_data_t *obj);
bool Load(obs_data_t *obj);
std::string GetShortDesc();
std::string GetId() { return id; };
QImage GetMatchImage() { return _matchImage; };
static std::shared_ptr<MacroCondition> Create()
{
return std::make_shared<MacroConditionVideo>();
}
void GetScreenshot();
bool LoadImageFromFile();
OBSWeakSource _videoSource;
VideoCondition _condition = VideoCondition::MATCH;
std::string _file = obs_module_text("AdvSceneSwitcher.enterPath");
private:
bool Compare();
std::unique_ptr<AdvSSScreenshotObj> _screenshotData = nullptr;
QImage _matchImage;
static bool _registered;
static const std::string id;
};
class MacroConditionVideoEdit : public QWidget {
Q_OBJECT
public:
MacroConditionVideoEdit(
QWidget *parent,
std::shared_ptr<MacroConditionVideo> cond = nullptr);
void UpdateEntryData();
static QWidget *Create(QWidget *parent,
std::shared_ptr<MacroCondition> cond)
{
return new MacroConditionVideoEdit(
parent,
std::dynamic_pointer_cast<MacroConditionVideo>(cond));
}
void UpdatePreviewTooltip();
void SetFilePath(const QString &text);
private slots:
void SourceChanged(const QString &text);
void ConditionChanged(int cond);
void FilePathChanged();
void BrowseButtonClicked();
signals:
void HeaderInfoChanged(const QString &);
protected:
QComboBox *_videoSelection;
QComboBox *_condition;
QLineEdit *_filePath;
QPushButton *_browseButton;
std::shared_ptr<MacroConditionVideo> _entryData;
private:
bool _loading = true;
};

View File

@@ -0,0 +1,54 @@
#pragma once
#include "macro.hpp"
#include <QWidget>
#include <QComboBox>
enum class VCamState {
STOP,
START,
};
class MacroConditionVCam : 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<MacroConditionVCam>();
}
VCamState _state;
private:
static bool _registered;
static const std::string id;
};
class MacroConditionVCamEdit : public QWidget {
Q_OBJECT
public:
MacroConditionVCamEdit(
QWidget *parent,
std::shared_ptr<MacroConditionVCam> cond = nullptr);
void UpdateEntryData();
static QWidget *Create(QWidget *parent,
std::shared_ptr<MacroCondition> cond)
{
return new MacroConditionVCamEdit(
parent,
std::dynamic_pointer_cast<MacroConditionVCam>(cond));
}
private slots:
void StateChanged(int value);
protected:
QComboBox *_states;
std::shared_ptr<MacroConditionVCam> _entryData;
private:
bool _loading = true;
};

View File

@@ -0,0 +1,70 @@
#pragma once
#include "macro.hpp"
#include <QWidget>
#include <QComboBox>
class MacroConditionWindow : public MacroCondition {
public:
bool CheckCondition();
bool Save(obs_data_t *obj);
bool Load(obs_data_t *obj);
std::string GetShortDesc();
std::string GetId() { return id; };
static std::shared_ptr<MacroCondition> Create()
{
return std::make_shared<MacroConditionWindow>();
}
private:
bool CheckWindowTitleSwitchDirect(std::string &currentWindowTitle);
bool CheckWindowTitleSwitchRegex(std::string &currentWindowTitle,
std::vector<std::string> &windowList);
public:
std::string _window;
bool _fullscreen = false;
bool _maximized = false;
bool _focus = true;
bool _windowFocusChanged = false;
private:
static bool _registered;
static const std::string id;
};
class MacroConditionWindowEdit : public QWidget {
Q_OBJECT
public:
MacroConditionWindowEdit(
QWidget *parent,
std::shared_ptr<MacroConditionWindow> cond = nullptr);
void UpdateEntryData();
static QWidget *Create(QWidget *parent,
std::shared_ptr<MacroCondition> cond)
{
return new MacroConditionWindowEdit(
parent,
std::dynamic_pointer_cast<MacroConditionWindow>(cond));
}
private slots:
void WindowChanged(const QString &text);
void FullscreenChanged(int state);
void MaximizedChanged(int state);
void FocusedChanged(int state);
void WindowFocusChanged(int state);
signals:
void HeaderInfoChanged(const QString &);
protected:
QComboBox *_windowSelection;
QCheckBox *_fullscreen;
QCheckBox *_maximized;
QCheckBox *_focused;
QCheckBox *_windowFocusChanged;
std::shared_ptr<MacroConditionWindow> _entryData;
private:
bool _loading = true;
};

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);
};

148
src/headers/macro.hpp Normal file
View File

@@ -0,0 +1,148 @@
#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;
constexpr auto logic_root_offset = 100;
enum class LogicType {
ROOT_NONE = 0,
ROOT_NOT,
ROOT_LAST,
// leave some space for potential expansion
NONE = 100,
AND,
OR,
AND_NOT,
OR_NOT,
LAST,
};
static inline bool isRootLogicType(LogicType l)
{
return static_cast<int>(l) < logic_root_offset;
}
struct LogicTypeInfo {
std::string _name;
};
class MacroCondition {
public:
virtual bool CheckCondition() = 0;
virtual bool Save(obs_data_t *obj) = 0;
virtual bool Load(obs_data_t *obj) = 0;
virtual std::string GetShortDesc();
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 {
public:
virtual bool PerformAction() = 0;
virtual bool Save(obs_data_t *obj) = 0;
virtual bool Load(obs_data_t *obj) = 0;
virtual std::string GetShortDesc();
virtual std::string GetId() = 0;
virtual void LogAction();
};
class Macro {
public:
Macro(const std::string &name = "");
virtual ~Macro();
bool CeckMatch();
bool PerformAction();
bool Matched() { return _matched; }
std::string Name() { return _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;
}
std::deque<std::shared_ptr<MacroAction>> &Actions() { return _actions; }
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,24 @@
#pragma once
#include <QDialog>
#include <QLabel>
#include <QLineEdit>
// Based on OBS's NameDialog
class AdvSSNameDialog : public QDialog {
Q_OBJECT
public:
AdvSSNameDialog(QWidget *parent);
// Returns true if user clicks OK, false otherwise
// userTextInput returns string that user typed into dialog
static bool AskForName(QWidget *parent, const QString &title,
const QString &text, std::string &userTextInput,
const QString &placeHolder = QString(""),
int maxSize = 170, bool clean = true);
private:
QLabel *label;
QLineEdit *userText;
};

View File

@@ -0,0 +1,15 @@
#pragma once
#include "hotkey.hpp"
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);
void PressKeys(const std::vector<HotkeyType> keys);
void PlatformInit();
void PlatformCleanup();

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_),
@@ -54,11 +54,6 @@ struct SceneGroup {
}
};
enum class SwitchTargetType {
Scene,
SceneGroup,
};
class SceneGroupEditWidget : public QWidget {
Q_OBJECT
@@ -88,21 +83,5 @@ private:
SceneGroup *sceneGroup = nullptr;
};
// Based on OBS's NameDialog
class SGNameDialog : public QDialog {
Q_OBJECT
public:
SGNameDialog(QWidget *parent);
// Returns true if user clicks OK, false otherwise
// userTextInput returns string that user typed into dialog
static bool AskForName(QWidget *parent, const QString &title,
const QString &text, std::string &userTextInput,
const QString &placeHolder = QString(""),
int maxSize = 170);
private:
QLabel *label;
QLineEdit *userText;
};
SceneGroup *GetSceneGroupByName(const char *name);
SceneGroup *GetSceneGroupByQString(const QString &name);

View File

@@ -0,0 +1,51 @@
#pragma once
#include "scene-group.hpp"
#include "utility.hpp"
#include <QComboBox>
enum class SceneSelectionType {
SCENE,
GROUP,
PREVIOUS,
CURRENT,
};
class SceneSelection {
public:
void Save(obs_data_t *obj, const char *name = "scene",
const char *typeName = "sceneType");
void Load(obs_data_t *obj, const char *name = "scene",
const char *typeName = "sceneType");
SceneSelectionType GetType() { return _type; }
OBSWeakSource GetScene(bool advance = true);
std::string ToString();
private:
OBSWeakSource _scene;
SceneGroup *_group = nullptr;
SceneSelectionType _type = SceneSelectionType::SCENE;
friend class SceneSelectionWidget;
};
class SceneSelectionWidget : public QComboBox {
Q_OBJECT
public:
SceneSelectionWidget(QWidget *parent, bool sceneGroups = false,
bool previous = false, bool current = false);
void SetScene(SceneSelection &);
signals:
void SceneChanged(const SceneSelection &);
private slots:
void SelectionChanged(const QString &name);
void SceneGroupAdd(const QString &name);
void SceneGroupRemove(const QString &name);
void SceneGroupRename(const QString &oldName, const QString &newName);
private:
bool IsCurrentSceneSelected(const QString &name);
bool IsPreviousSceneSelected(const QString &name);
};

View File

@@ -1,3 +1,10 @@
/******************************************************************************
Note: Long-term goal is to remove this tab / file.
Most functionality shall be moved to the Macro tab instead.
So if you plan to make changes here, please consider applying them to the
corresponding macro tab functionality instead.
******************************************************************************/
#pragma once
#include "switch-generic.hpp"
@@ -27,6 +34,9 @@ enum class sceneTriggerAction {
START_SWITCHER,
STOP_SWITCHER,
START_VCAM,
STOP_VCAM,
};
struct SceneTrigger : SceneSwitcherEntry {

View File

@@ -0,0 +1,29 @@
#pragma once
#include <obs.hpp>
#include <string>
#include <QImage>
#include <chrono>
class AdvSSScreenshotObj {
public:
AdvSSScreenshotObj(obs_source_t *source);
~AdvSSScreenshotObj();
void Screenshot();
void Download();
void Copy();
void MarkDone();
gs_texrender_t *texrender = nullptr;
gs_stagesurf_t *stagesurf = nullptr;
OBSWeakSource weakSource;
std::string path;
QImage image;
uint32_t cx = 0;
uint32_t cy = 0;
int stage = 0;
bool done = false;
std::chrono::high_resolution_clock::time_point time;
};

44
src/headers/section.hpp Normal file
View File

@@ -0,0 +1,44 @@
#pragma once
#include <QFrame>
#include <QGridLayout>
#include <QParallelAnimationGroup>
#include <QScrollArea>
#include <QToolButton>
#include <QWidget>
class Section : public QWidget {
Q_OBJECT
public:
explicit Section(const int animationDuration = 300,
QWidget *parent = 0);
void SetContent(QWidget *w, bool collapsed = true);
void AddHeaderWidget(QWidget *);
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

@@ -1,8 +1,16 @@
/******************************************************************************
Note: Long-term goal is to remove this tab / file.
Most functionality shall be moved to the Macro tab instead.
So if you plan to make changes here, please consider applying them to the
corresponding macro tab functionality instead.
******************************************************************************/
#pragma once
#include <QSpinBox>
#include <limits>
#include "switch-generic.hpp"
#include "duration-control.hpp"
#include "volume-control.hpp"
constexpr auto audio_func = 8;
@@ -18,9 +26,8 @@ struct AudioSwitch : virtual SceneSwitcherEntry {
OBSWeakSource audioSource = nullptr;
int volumeThreshold = 0;
audioCondition condition = ABOVE;
double duration = 0;
Duration duration;
bool ignoreInactiveSource = true;
unsigned int matchCount = 0;
float peak = -std::numeric_limits<float>::infinity();
obs_volmeter_t *volmeter = nullptr;
@@ -50,8 +57,7 @@ struct AudioSwitchFallback : virtual SceneSwitcherEntry {
void load(obs_data_t *obj);
bool enable = false;
double duration = 0;
unsigned int matchCount = 0;
Duration duration;
};
class AudioSwitchWidget : public SwitchWidget {
@@ -77,7 +83,7 @@ private:
QComboBox *audioSources;
QComboBox *condition;
QSpinBox *audioVolumeThreshold;
QDoubleSpinBox *duration;
DurationSelection *duration;
QCheckBox *ignoreInactiveSource;
VolControl *volMeter;
@@ -94,7 +100,7 @@ private slots:
void DurationChanged(double dur);
private:
QDoubleSpinBox *duration;
DurationSelection *duration;
AudioSwitchFallback *switchData;
};

View File

@@ -1,3 +1,10 @@
/******************************************************************************
Note: Long-term goal is to remove this tab / file.
Most functionality shall be moved to the Macro tab instead.
So if you plan to make changes here, please consider applying them to the
corresponding macro tab functionality instead.
******************************************************************************/
#pragma once
#include <QCheckBox>
#include "switch-generic.hpp"

View File

@@ -1,3 +1,10 @@
/******************************************************************************
Note: Long-term goal is to remove this tab / file.
Most functionality shall be moved to the Macro tab instead.
So if you plan to make changes here, please consider applying them to the
corresponding macro tab functionality instead.
******************************************************************************/
#pragma once
#include "switch-generic.hpp"
#include <QPlainTextEdit>

View File

@@ -2,6 +2,13 @@
#include <obs.hpp>
#include <QComboBox>
#include "scene-group.hpp"
enum class SwitchTargetType {
Scene,
SceneGroup,
};
struct SceneSwitcherEntry {
SwitchTargetType targetType = SwitchTargetType::Scene;
SceneGroup *group = nullptr;

View File

@@ -1,3 +1,10 @@
/******************************************************************************
Note: Long-term goal is to remove this tab / file.
Most functionality shall be moved to the Macro tab instead.
So if you plan to make changes here, please consider applying them to the
corresponding macro tab functionality instead.
******************************************************************************/
#pragma once
#include "switch-generic.hpp"

View File

@@ -1,3 +1,10 @@
/******************************************************************************
Note: Long-term goal is to remove this tab / file.
Most functionality shall be moved to the Macro tab instead.
So if you plan to make changes here, please consider applying them to the
corresponding macro tab functionality instead.
******************************************************************************/
#pragma once
#include "switch-generic.hpp"

View File

@@ -22,6 +22,8 @@ using websocketpp::connection_hdl;
typedef websocketpp::server<websocketpp::config::asio> server;
typedef websocketpp::client<websocketpp::config::asio_client> client;
struct sceneSwitchInfo;
class NetworkConfig {
public:
NetworkConfig();
@@ -31,6 +33,10 @@ public:
std::string GetClientUri();
bool ShouldSendSceneChange();
bool ShouldSendFrontendSceneChange();
bool ShouldSendPrviewSceneChange();
// Server
bool ServerEnabled;
uint64_t ServerPort;
@@ -40,7 +46,9 @@ public:
bool ClientEnabled;
std::string Address;
uint64_t ClientPort;
bool SendAll;
bool SendSceneChange;
bool SendSceneChangeAll;
bool SendPreview;
};
class WSServer : public QObject {
@@ -51,7 +59,7 @@ public:
virtual ~WSServer();
void start(quint16 port, bool lockToIPv4);
void stop();
void sendMessage(OBSWeakSource scene, OBSWeakSource transition);
void sendMessage(sceneSwitchInfo sceneSwitch, bool preview = false);
QThreadPool *threadPool() { return &_threadPool; }
private:

View File

@@ -1,3 +1,10 @@
/******************************************************************************
Note: Long-term goal is to remove this tab / file.
Most functionality shall be moved to the Macro tab instead.
So if you plan to make changes here, please consider applying them to the
corresponding macro tab functionality instead.
******************************************************************************/
#pragma once
#include "switch-generic.hpp"

View File

@@ -1,3 +1,10 @@
/******************************************************************************
Note: Long-term goal is to remove this tab / file.
Most functionality shall be moved to the Macro tab instead.
So if you plan to make changes here, please consider applying them to the
corresponding macro tab functionality instead.
******************************************************************************/
#pragma once
#include "switch-generic.hpp"

View File

@@ -1,3 +1,10 @@
/******************************************************************************
Note: Long-term goal is to remove this tab / file.
Most functionality shall be moved to the Macro tab instead.
So if you plan to make changes here, please consider applying them to the
corresponding macro tab functionality instead.
******************************************************************************/
#pragma once
#include <QSpinBox>
#include "switch-generic.hpp"

View File

@@ -1,25 +1,25 @@
/******************************************************************************
Note: Long-term goal is to remove this tab / file.
Most functionality shall be moved to the Macro tab instead.
So if you plan to make changes here, please consider applying them to the
corresponding macro tab functionality instead.
******************************************************************************/
#pragma once
#include <QVBoxLayout>
#include "switch-generic.hpp"
#include "duration-control.hpp"
constexpr auto round_trip_func = 1;
constexpr auto default_priority_1 = round_trip_func;
typedef enum {
SECONDS,
MINUTES,
HOURS,
} delay_units;
struct SceneSequenceSwitch : SceneSwitcherEntry {
static bool pause;
SwitchTargetType startTargetType = SwitchTargetType::Scene;
OBSWeakSource startScene = nullptr;
double delay = 0;
int delayMultiplier = 1;
Duration delay;
bool interruptible = false;
unsigned int matchCount = 0;
// nullptr marks start point and reaching end of extended sequence
SceneSequenceSwitch *activeSequence = nullptr;
@@ -38,8 +38,7 @@ struct SceneSequenceSwitch : SceneSwitcherEntry {
bool checkMatch(OBSWeakSource currentScene, int &linger,
SceneSequenceSwitch *root = nullptr);
bool checkDurationMatchInterruptible();
void prepareUninterruptibleMatch(OBSWeakSource currentScene,
int &linger);
void prepareUninterruptibleMatch(int &linger);
void advanceActiveSequence();
void logAdvanceSequence();
void logSequenceCanceled();
@@ -57,22 +56,20 @@ public:
static void swapSwitchData(SequenceWidget *s1, SequenceWidget *s2);
void UpdateDelay();
void UpdateWidgetStatus(bool showExtendText);
void setExtendedSequenceStartScene();
private slots:
void SceneChanged(const QString &text);
void DelayChanged(double delay);
void DelayUnitsChanged(int idx);
void DelayUnitsChanged(DurationUnit);
void StartSceneChanged(const QString &text);
void InterruptibleChanged(int state);
void ExtendClicked();
void ReduceClicked();
protected:
QDoubleSpinBox *delay;
QComboBox *delayUnits;
DurationSelection *delay;
QComboBox *startScenes;
QCheckBox *interruptible;
QVBoxLayout *extendSequenceLayout;

View File

@@ -1,3 +1,10 @@
/******************************************************************************
Note: Long-term goal is to remove this tab / file.
Most functionality shall be moved to the Macro tab instead.
So if you plan to make changes here, please consider applying them to the
corresponding macro tab functionality instead.
******************************************************************************/
#pragma once
#include <QTimeEdit>

View File

@@ -1,17 +1,24 @@
#pragma once
#include <QDoubleSpinBox>
#include "switch-generic.hpp"
struct SceneTransition : SceneSwitcherEntry {
OBSWeakSource scene2 = nullptr;
double duration = 0.3;
const char *getType() { return "transition"; }
bool initialized();
bool valid();
void save(obs_data_t *obj);
void load(obs_data_t *obj);
inline SceneTransition(){};
inline SceneTransition(OBSWeakSource scene_, OBSWeakSource scene2_,
OBSWeakSource transition_)
: SceneSwitcherEntry(scene_, transition_), scene2(scene2_)
OBSWeakSource transition_, double duration_)
: SceneSwitcherEntry(scene_, transition_),
scene2(scene2_),
duration(duration_)
{
}
};
@@ -21,6 +28,8 @@ struct DefaultSceneTransition : SceneSwitcherEntry {
static unsigned int delay;
const char *getType() { return "def_transition"; }
void save(obs_data_t *obj);
void load(obs_data_t *obj);
bool checkMatch(OBSWeakSource currentScene);
void setTransition();
@@ -45,9 +54,11 @@ public:
private slots:
void Scene2Changed(const QString &text);
void DurationChanged(double dur);
private:
QComboBox *scenes2;
QDoubleSpinBox *duration;
SceneTransition *switchData;
};

View File

@@ -1,7 +1,15 @@
/******************************************************************************
Note: Long-term goal is to remove this tab / file.
Most functionality shall be moved to the Macro tab instead.
So if you plan to make changes here, please consider applying them to the
corresponding macro tab functionality instead.
******************************************************************************/
#pragma once
#include <QSpinBox>
#include "switch-generic.hpp"
#include "screenshot-helper.hpp"
constexpr auto video_func = 9;
constexpr auto default_priority_9 = video_func;
@@ -10,30 +18,7 @@ enum class videoSwitchType {
MATCH,
DIFFER,
HAS_NOT_CHANGED,
};
class AdvSSScreenshotObj {
public:
AdvSSScreenshotObj(obs_source_t *source);
~AdvSSScreenshotObj();
void Screenshot();
void Download();
void Copy();
void MarkDone();
gs_texrender_t *texrender = nullptr;
gs_stagesurf_t *stagesurf = nullptr;
OBSWeakSource weakSource;
std::string path;
QImage image;
uint32_t cx = 0;
uint32_t cy = 0;
int stage = 0;
bool done = false;
std::chrono::high_resolution_clock::time_point time;
HAS_CHANGED,
};
struct VideoSwitch : virtual SceneSwitcherEntry {

View File

@@ -1,3 +1,10 @@
/******************************************************************************
Note: Long-term goal is to remove this tab / file.
Most functionality shall be moved to the Macro tab instead.
So if you plan to make changes here, please consider applying them to the
corresponding macro tab functionality instead.
******************************************************************************/
#pragma once
#include "switch-generic.hpp"

View File

@@ -24,9 +24,13 @@
#include "switch-video.hpp"
#include "switch-network.hpp"
#include "macro.hpp"
#include "duration-control.hpp"
constexpr auto default_interval = 300;
constexpr auto previous_scene_name = "Previous Scene";
constexpr auto current_transition_name = "Current Transition";
constexpr auto tab_count = 18;
typedef enum { NO_SWITCH = 0, SWITCH = 1, RANDOM_SWITCH = 2 } NoMatch;
typedef enum { PERSIST = 0, START = 1, STOP = 2 } StartupBehavior;
@@ -38,6 +42,13 @@ enum class AutoStartEvent {
RECORINDG_OR_STREAMING,
};
typedef struct sceneSwitchInfo {
OBSWeakSource scene;
OBSWeakSource transition;
int duration = 0;
} sceneSwitchInfo;
typedef struct transitionData {
std::string name = "";
int duration = 0;
@@ -61,29 +72,33 @@ struct SwitcherData {
bool verbose = false;
bool disableHints = false;
bool showFrame = false;
bool tansitionOverrideOverride = false;
bool transitionOverrideOverride = false;
bool adjustActiveTransitionType = true;
int interval = default_interval;
obs_source_t *waitScene = nullptr;
OBSWeakSource currentScene = nullptr;
OBSWeakSource previousScene = nullptr;
OBSWeakSource previousSceneHelper = nullptr;
OBSWeakSource lastRandomScene;
SceneGroup *lastRandomSceneGroup;
OBSWeakSource nonMatchingScene;
NoMatch switchIfNotMatching = NO_SWITCH;
double noMatchDelay = 0;
double noMatchCount = 0;
Duration noMatchDelay;
StartupBehavior startupBehavior = PERSIST;
AutoStartEvent autoStartEvent = AutoStartEvent::NEVER;
double cooldown = 0.;
Duration cooldown;
std::chrono::high_resolution_clock::time_point lastMatchTime;
std::deque<Macro> macros;
bool macroSceneSwitched = false;
bool replayBufferSaved = false;
std::deque<WindowSwitch> windowSwitches;
std::vector<std::string> ignoreIdleWindows;
std::string lastTitle;
std::string currentTitle;
std::deque<ScreenRegionSwitch> screenRegionSwitches;
@@ -131,7 +146,7 @@ struct SwitcherData {
default_priority_0, default_priority_1, default_priority_2,
default_priority_3, default_priority_4, default_priority_5,
default_priority_6, default_priority_7, default_priority_8,
default_priority_9};
default_priority_9, default_priority_10};
struct ThreadPrio {
std::string name;
@@ -160,12 +175,12 @@ struct SwitcherData {
uint32_t threadPriority = QThread::NormalPriority;
std::vector<int> tabOrder;
std::vector<int> tabOrder = std::vector<int>(tab_count);
bool hotkeysRegistered = false;
obs_hotkey_id startHotkey = 0;
obs_hotkey_id stopHotkey = 0;
obs_hotkey_id toggleHotkey = 0;
obs_hotkey_id startHotkey = OBS_INVALID_HOTKEY_ID;
obs_hotkey_id stopHotkey = OBS_INVALID_HOTKEY_ID;
obs_hotkey_id toggleHotkey = OBS_INVALID_HOTKEY_ID;
bool saveWindowGeo = false;
QPoint windowPos = {};
@@ -177,6 +192,7 @@ struct SwitcherData {
void Start();
void Stop();
void setWaitScene();
bool sceneChangedDuringWait();
bool prioFuncsValid();
@@ -185,36 +201,33 @@ struct SwitcherData {
void resetTabOrder();
void writeSceneInfoToFile();
void writeToStatusFile(QString status);
void writeToStatusFile(const QString &msg);
void setPreconditions();
bool checkForMatch(OBSWeakSource &scene, OBSWeakSource &transition,
int &linger);
void checkSceneSequence(bool &match, OBSWeakSource &scene,
OBSWeakSource &transition, int &linger);
void checkIdleSwitch(bool &match, OBSWeakSource &scene,
OBSWeakSource &transition);
void checkWindowTitleSwitch(bool &match, OBSWeakSource &scene,
int &linger, bool &setPreviousSceneAsMatch,
bool &macroMatch);
bool checkMacros();
bool runMacros();
bool checkSceneSequence(OBSWeakSource &scene, OBSWeakSource &transition,
int &linger, bool &setPrevSceneAfterLinger);
bool checkIdleSwitch(OBSWeakSource &scene, OBSWeakSource &transition);
bool checkWindowTitleSwitch(OBSWeakSource &scene,
OBSWeakSource &transition);
void checkExeSwitch(bool &match, OBSWeakSource &scene,
OBSWeakSource &transition);
void checkScreenRegionSwitch(bool &match, OBSWeakSource &scene,
bool checkExeSwitch(OBSWeakSource &scene, OBSWeakSource &transition);
bool checkScreenRegionSwitch(OBSWeakSource &scene,
OBSWeakSource &transition);
void checkSwitchInfoFromFile(bool &match, OBSWeakSource &scene,
bool checkSwitchInfoFromFile(OBSWeakSource &scene,
OBSWeakSource &transition);
void checkFileContent(bool &match, OBSWeakSource &scene,
OBSWeakSource &transition);
void checkRandom(bool &match, OBSWeakSource &scene,
OBSWeakSource &transition, int &delay);
void checkMediaSwitch(bool &match, OBSWeakSource &scene,
OBSWeakSource &transition);
void checkTimeSwitch(bool &match, OBSWeakSource &scene,
OBSWeakSource &transition);
void checkAudioSwitch(bool &match, OBSWeakSource &scene,
OBSWeakSource &transition);
bool checkFileContent(OBSWeakSource &scene, OBSWeakSource &transition);
bool checkRandom(OBSWeakSource &scene, OBSWeakSource &transition,
int &delay);
bool checkMediaSwitch(OBSWeakSource &scene, OBSWeakSource &transition);
bool checkTimeSwitch(OBSWeakSource &scene, OBSWeakSource &transition);
bool checkAudioSwitch(OBSWeakSource &scene, OBSWeakSource &transition);
void checkAudioSwitchFallback(OBSWeakSource &scene,
OBSWeakSource &transition);
void checkVideoSwitch(bool &match, OBSWeakSource &scene,
OBSWeakSource &transition);
bool checkVideoSwitch(OBSWeakSource &scene, OBSWeakSource &transition);
void checkNoMatchSwitch(bool &match, OBSWeakSource &scene,
OBSWeakSource &transition, int &sleep);
void checkSwitchCooldown(bool &match);
@@ -223,6 +236,7 @@ struct SwitcherData {
void checkDefaultSceneTransitions();
void saveSettings(obs_data_t *obj);
void saveMacros(obs_data_t *obj);
void saveWindowTitleSwitches(obs_data_t *obj);
void saveScreenRegionSwitches(obs_data_t *obj);
void savePauseSwitches(obs_data_t *obj);
@@ -241,13 +255,13 @@ 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);
void loadWindowTitleSwitches(obs_data_t *obj);
void loadScreenRegionSwitches(obs_data_t *obj);
void loadPauseSwitches(obs_data_t *obj);
void loadOldPauseSwitches(obs_data_t *obj);
void loadSceneSequenceSwitches(obs_data_t *obj);
void loadSceneTransitions(obs_data_t *obj);
void loadIdleSwitches(obs_data_t *obj);

View File

@@ -1,144 +1,37 @@
#pragma once
#include <QString>
#include <QLayout>
#include <QLabel>
#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 SceneGroupValid(SceneGroup *group)
{
if (group) {
return group->name != invalid_scene_group_name;
}
return false;
}
class SceneSelection;
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 SceneGroup *GetSceneGroupByName(const char *name)
{
if (!switcher) {
return nullptr;
}
for (SceneGroup &sg : switcher->sceneGroups) {
if (sg.name == name) {
return &sg;
}
}
return nullptr;
}
static inline SceneGroup *GetSceneGroupByQString(const QString &name)
{
return GetSceneGroupByName(name.toUtf8().constData());
}
static inline OBSWeakSource GetWeakSourceByName(const char *name)
{
OBSWeakSource weak;
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);
void setSourceSettings(obs_source_t *s, const std::string &settings);
bool compareSourceSettings(const OBSWeakSource &source,
const std::string &settings, bool regex);
std::vector<obs_scene_item *> getSceneItemsWithName(OBSScene scene,
std::string &name);
std::string getDataFilePath(const std::string &file);
QString fromatJsonString(std::string);
QString fromatJsonString(const char *);
/**
* Populate layout with labels and widgets based on provided text
@@ -148,37 +41,39 @@ 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();
}
}
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 addCurrent = false, bool addAny = false,
bool addSceneGroup = false,
std::deque<SceneGroup> *sceneGroups = nullptr,
bool addSelect = true, std::string selectText = "",
bool selectable = false);
void populateSourcesWithFilterSelection(QComboBox *list);
void populateFilterSelection(QComboBox *list,
OBSWeakSource weakSource = nullptr);
void populateSceneItemSelection(QComboBox *list,
OBSWeakSource sceneWeakSource = nullptr);
void populateSceneItemSelection(QComboBox *list, SceneSelection &s);

View File

@@ -1,7 +1,8 @@
#include "headers/hotkey.hpp"
#include "headers/advanced-scene-switcher.hpp"
#include <obs-module.h>
#include <fstream>
#include <regex>
#include "headers/advanced-scene-switcher.hpp"
void startHotkeyFunc(void *data, obs_hotkey_id id, obs_hotkey_t *hotkey,
bool pressed)
@@ -68,41 +69,38 @@ void registerHotkeys()
void SwitcherData::saveHotkeys(obs_data_t *obj)
{
obs_data_array_t *startHotkeyArrray =
obs_hotkey_save(switcher->startHotkey);
obs_data_array_t *startHotkeyArrray = obs_hotkey_save(startHotkey);
obs_data_set_array(obj, "startHotkey", startHotkeyArrray);
obs_data_array_release(startHotkeyArrray);
obs_data_array_t *stopHotkeyArrray =
obs_hotkey_save(switcher->stopHotkey);
obs_data_array_t *stopHotkeyArrray = obs_hotkey_save(stopHotkey);
obs_data_set_array(obj, "stopHotkey", stopHotkeyArrray);
obs_data_array_release(stopHotkeyArrray);
obs_data_array_t *toggleHotkeyArrray =
obs_hotkey_save(switcher->toggleHotkey);
obs_data_array_t *toggleHotkeyArrray = obs_hotkey_save(toggleHotkey);
obs_data_set_array(obj, "toggleHotkey", toggleHotkeyArrray);
obs_data_array_release(toggleHotkeyArrray);
}
void SwitcherData::loadHotkeys(obs_data_t *obj)
{
if (!switcher->hotkeysRegistered) {
if (!hotkeysRegistered) {
registerHotkeys();
}
obs_data_array_t *startHotkeyArrray =
obs_data_get_array(obj, "startHotkey");
obs_hotkey_load(switcher->startHotkey, startHotkeyArrray);
obs_hotkey_load(startHotkey, startHotkeyArrray);
obs_data_array_release(startHotkeyArrray);
obs_data_array_t *stopHotkeyArrray =
obs_data_get_array(obj, "stopHotkey");
obs_hotkey_load(switcher->stopHotkey, stopHotkeyArrray);
obs_hotkey_load(stopHotkey, stopHotkeyArrray);
obs_data_array_release(stopHotkeyArrray);
obs_data_array_t *toggleHotkeyArrray =
obs_data_get_array(obj, "toggleHotkey");
obs_hotkey_load(switcher->toggleHotkey, toggleHotkeyArrray);
obs_hotkey_load(toggleHotkey, toggleHotkeyArrray);
obs_data_array_release(toggleHotkeyArrray);
}

View File

@@ -1,7 +1,11 @@
#include "../headers/hotkey.hpp"
#include <X11/Xlib.h>
#include <X11/Xatom.h>
#include <X11/Xutil.h>
#include <X11/keysym.h>
#include <X11/extensions/scrnsaver.h>
#include <X11/extensions/XTest.h>
#undef Bool
#undef CursorShape
#undef Expose
@@ -14,10 +18,19 @@
#undef Status
#undef Unsorted
#include <util/platform.h>
#include "../headers/advanced-scene-switcher.hpp"
#include <vector>
#include <thread>
#include <QStringList>
#include <QRegularExpression>
#include <QLibrary>
static Display *xdisplay = 0;
static QLibrary *libXtstHandle = nullptr;
typedef int (*keyPressFunc)(Display *, unsigned int, bool, unsigned long);
static keyPressFunc pressFunc = nullptr;
bool canSimulateKeyPresses = false;
Display *disp()
{
if (!xdisplay)
@@ -240,18 +253,17 @@ std::pair<int, int> getCursorPos()
int win_y;
unsigned int mask;
dpy = XOpenDisplay(NULL);
dpy = disp();
root = XDefaultRootWindow(dpy);
if (XQueryPointer(dpy, root, &ret_root, &ret_child, &root_x, &root_y,
&win_x, &win_y, &mask)) {
pos = std::pair<int, int>(root_x, root_y);
}
XCloseDisplay(dpy);
return pos;
}
bool isMaximized(std::string &title)
bool isMaximized(const std::string &title)
{
if (!ewmhIsSupported())
return false;
@@ -300,7 +312,7 @@ bool isMaximized(std::string &title)
return false;
}
bool isFullscreen(std::string &title)
bool isFullscreen(const std::string &title)
{
if (!ewmhIsSupported())
return false;
@@ -380,14 +392,186 @@ int secondsSinceLastInput()
mit_info = XScreenSaverAllocInfo();
if ((display = XOpenDisplay(NULL)) == NULL) {
return (-1);
if ((display = disp()) == NULL) {
return -1;
}
screen = DefaultScreen(display);
XScreenSaverQueryInfo(display, RootWindow(display, screen), mit_info);
idle_time = (mit_info->idle) / 1000;
XFree(mit_info);
XCloseDisplay(display);
return idle_time;
}
static std::unordered_map<HotkeyType, long> keyTable = {
// Chars
{HotkeyType::Key_A, XK_A},
{HotkeyType::Key_B, XK_B},
{HotkeyType::Key_C, XK_C},
{HotkeyType::Key_D, XK_D},
{HotkeyType::Key_E, XK_E},
{HotkeyType::Key_F, XK_F},
{HotkeyType::Key_G, XK_G},
{HotkeyType::Key_H, XK_H},
{HotkeyType::Key_I, XK_I},
{HotkeyType::Key_J, XK_J},
{HotkeyType::Key_K, XK_K},
{HotkeyType::Key_L, XK_L},
{HotkeyType::Key_M, XK_M},
{HotkeyType::Key_N, XK_N},
{HotkeyType::Key_O, XK_O},
{HotkeyType::Key_P, XK_P},
{HotkeyType::Key_Q, XK_Q},
{HotkeyType::Key_R, XK_R},
{HotkeyType::Key_S, XK_S},
{HotkeyType::Key_T, XK_T},
{HotkeyType::Key_U, XK_U},
{HotkeyType::Key_V, XK_V},
{HotkeyType::Key_W, XK_W},
{HotkeyType::Key_X, XK_X},
{HotkeyType::Key_Y, XK_Y},
{HotkeyType::Key_Z, XK_Z},
// Numbers
{HotkeyType::Key_0, XK_0},
{HotkeyType::Key_1, XK_1},
{HotkeyType::Key_2, XK_2},
{HotkeyType::Key_3, XK_3},
{HotkeyType::Key_4, XK_4},
{HotkeyType::Key_5, XK_5},
{HotkeyType::Key_6, XK_6},
{HotkeyType::Key_7, XK_7},
{HotkeyType::Key_8, XK_8},
{HotkeyType::Key_9, XK_9},
{HotkeyType::Key_F1, XK_F1},
{HotkeyType::Key_F2, XK_F2},
{HotkeyType::Key_F3, XK_F3},
{HotkeyType::Key_F4, XK_F4},
{HotkeyType::Key_F5, XK_F5},
{HotkeyType::Key_F6, XK_F6},
{HotkeyType::Key_F7, XK_F7},
{HotkeyType::Key_F8, XK_F8},
{HotkeyType::Key_F9, XK_F9},
{HotkeyType::Key_F10, XK_F10},
{HotkeyType::Key_F11, XK_F11},
{HotkeyType::Key_F12, XK_F12},
{HotkeyType::Key_F13, XK_F13},
{HotkeyType::Key_F14, XK_F14},
{HotkeyType::Key_F15, XK_F15},
{HotkeyType::Key_F16, XK_F16},
{HotkeyType::Key_F17, XK_F17},
{HotkeyType::Key_F18, XK_F18},
{HotkeyType::Key_F19, XK_F19},
{HotkeyType::Key_F20, XK_F20},
{HotkeyType::Key_F21, XK_F21},
{HotkeyType::Key_F22, XK_F22},
{HotkeyType::Key_F23, XK_F23},
{HotkeyType::Key_F24, XK_F24},
{HotkeyType::Key_Escape, XK_Escape},
{HotkeyType::Key_Space, XK_space},
{HotkeyType::Key_Return, XK_Return},
{HotkeyType::Key_Backspace, XK_BackSpace},
{HotkeyType::Key_Tab, XK_Tab},
{HotkeyType::Key_Shift_L, XK_Shift_L},
{HotkeyType::Key_Shift_R, XK_Shift_R},
{HotkeyType::Key_Control_L, XK_Control_L},
{HotkeyType::Key_Control_R, XK_Control_R},
{HotkeyType::Key_Alt_L, XK_Alt_L},
{HotkeyType::Key_Alt_R, XK_Alt_R},
{HotkeyType::Key_Win_L, XK_Super_L},
{HotkeyType::Key_Win_R, XK_Super_R},
{HotkeyType::Key_Apps, XK_Hyper_L},
{HotkeyType::Key_CapsLock, XK_Caps_Lock},
{HotkeyType::Key_NumLock, XK_Num_Lock},
{HotkeyType::Key_ScrollLock, XK_Scroll_Lock},
{HotkeyType::Key_PrintScreen, XK_Print},
{HotkeyType::Key_Pause, XK_Pause},
{HotkeyType::Key_Insert, XK_Insert},
{HotkeyType::Key_Delete, XK_Delete},
{HotkeyType::Key_PageUP, XK_Page_Up},
{HotkeyType::Key_PageDown, XK_Page_Down},
{HotkeyType::Key_Home, XK_Home},
{HotkeyType::Key_End, XK_End},
{HotkeyType::Key_Left, XK_Left},
{HotkeyType::Key_Up, XK_Up},
{HotkeyType::Key_Right, XK_Right},
{HotkeyType::Key_Down, XK_Down},
{HotkeyType::Key_Numpad0, XK_KP_0},
{HotkeyType::Key_Numpad1, XK_KP_1},
{HotkeyType::Key_Numpad2, XK_KP_2},
{HotkeyType::Key_Numpad3, XK_KP_3},
{HotkeyType::Key_Numpad4, XK_KP_4},
{HotkeyType::Key_Numpad5, XK_KP_5},
{HotkeyType::Key_Numpad6, XK_KP_6},
{HotkeyType::Key_Numpad7, XK_KP_7},
{HotkeyType::Key_Numpad8, XK_KP_8},
{HotkeyType::Key_Numpad9, XK_KP_9},
{HotkeyType::Key_NumpadAdd, XK_KP_Add},
{HotkeyType::Key_NumpadSubtract, XK_KP_Subtract},
{HotkeyType::Key_NumpadMultiply, XK_KP_Multiply},
{HotkeyType::Key_NumpadDivide, XK_KP_Divide},
{HotkeyType::Key_NumpadDecimal, XK_KP_Decimal},
{HotkeyType::Key_NumpadEnter, XK_KP_Enter},
};
void PressKeys(const std::vector<HotkeyType> keys)
{
if (!canSimulateKeyPresses) {
return;
}
Display *display = disp();
if (display == NULL)
return;
// Press keys
for (auto &key : keys) {
auto it = keyTable.find(key);
if (it == keyTable.end()) {
continue;
}
pressFunc(display, XKeysymToKeycode(display, it->second), true,
CurrentTime);
}
XFlush(display);
std::this_thread::sleep_for(std::chrono::milliseconds(300));
// Release keys
for (auto &key : keys) {
auto it = keyTable.find(key);
if (it == keyTable.end()) {
continue;
}
pressFunc(display, XKeysymToKeycode(display, it->second), false,
CurrentTime);
}
XFlush(display);
}
void PlatformInit()
{
libXtstHandle = new QLibrary("libXtst.so", nullptr);
pressFunc = (keyPressFunc)libXtstHandle->resolve("XTestFakeKeyEvent");
int _;
canSimulateKeyPresses = pressFunc &&
!XQueryExtension(disp(), "XTEST", &_, &_, &_);
}
void PlatformCleanup()
{
delete libXtstHandle;
libXtstHandle = nullptr;
cleanupDisplay();
}

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

@@ -0,0 +1,201 @@
#include "headers/macro-action-audio.hpp"
#include "headers/advanced-scene-switcher.hpp"
#include "headers/utility.hpp"
const std::string MacroActionAudio::id = "audio";
bool MacroActionAudio::_registered = MacroActionFactory::Register(
MacroActionAudio::id,
{MacroActionAudio::Create, MacroActionAudioEdit::Create,
"AdvSceneSwitcher.action.audio"});
const static std::map<AudioAction, std::string> actionTypes = {
{AudioAction::MUTE, "AdvSceneSwitcher.action.audio.type.mute"},
{AudioAction::UNMUTE, "AdvSceneSwitcher.action.audio.type.unmute"},
{AudioAction::SOURCE_VOLUME,
"AdvSceneSwitcher.action.audio.type.sourceVolume"},
{AudioAction::MASTER_VOLUME,
"AdvSceneSwitcher.action.audio.type.masterVolume"},
};
bool MacroActionAudio::PerformAction()
{
auto s = obs_weak_source_get_source(_audioSource);
switch (_action) {
case AudioAction::MUTE:
obs_source_set_muted(s, true);
break;
case AudioAction::UNMUTE:
obs_source_set_muted(s, false);
break;
case AudioAction::SOURCE_VOLUME:
obs_source_set_volume(s, (float)_volume / 100.0f);
break;
case AudioAction::MASTER_VOLUME:
obs_set_master_volume((float)_volume / 100.0f);
break;
default:
break;
}
obs_source_release(s);
return true;
}
void MacroActionAudio::LogAction()
{
auto it = actionTypes.find(_action);
if (it != actionTypes.end()) {
vblog(LOG_INFO,
"performed action \"%s\" for source \"%s\" with volume %d",
it->second.c_str(),
GetWeakSourceName(_audioSource).c_str(), _volume);
} else {
blog(LOG_WARNING, "ignored unknown audio action %d",
static_cast<int>(_action));
}
}
bool MacroActionAudio::Save(obs_data_t *obj)
{
MacroAction::Save(obj);
obs_data_set_string(obj, "audioSource",
GetWeakSourceName(_audioSource).c_str());
obs_data_set_int(obj, "action", static_cast<int>(_action));
obs_data_set_int(obj, "volume", _volume);
return true;
}
bool MacroActionAudio::Load(obs_data_t *obj)
{
MacroAction::Load(obj);
const char *audioSourceName = obs_data_get_string(obj, "audioSource");
_audioSource = GetWeakSourceByName(audioSourceName);
_action = static_cast<AudioAction>(obs_data_get_int(obj, "action"));
_volume = obs_data_get_int(obj, "volume");
return true;
}
std::string MacroActionAudio::GetShortDesc()
{
if (_audioSource) {
return GetWeakSourceName(_audioSource);
}
return "";
}
static inline void populateActionSelection(QComboBox *list)
{
for (auto entry : actionTypes) {
list->addItem(obs_module_text(entry.second.c_str()));
}
}
MacroActionAudioEdit::MacroActionAudioEdit(
QWidget *parent, std::shared_ptr<MacroActionAudio> entryData)
: QWidget(parent)
{
_audioSources = new QComboBox();
_actions = new QComboBox();
_volumePercent = new QSpinBox();
_volumePercent->setMinimum(0);
_volumePercent->setMaximum(2000);
_volumePercent->setSuffix("%");
populateActionSelection(_actions);
populateAudioSelection(_audioSources);
QWidget::connect(_actions, SIGNAL(currentIndexChanged(int)), this,
SLOT(ActionChanged(int)));
QWidget::connect(_audioSources,
SIGNAL(currentTextChanged(const QString &)), this,
SLOT(SourceChanged(const QString &)));
QWidget::connect(_volumePercent, SIGNAL(valueChanged(int)), this,
SLOT(VolumeChanged(int)));
QHBoxLayout *mainLayout = new QHBoxLayout;
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
{"{{audioSources}}", _audioSources},
{"{{actions}}", _actions},
{"{{volume}}", _volumePercent},
};
placeWidgets(obs_module_text("AdvSceneSwitcher.action.audio.entry"),
mainLayout, widgetPlaceholders);
setLayout(mainLayout);
_entryData = entryData;
UpdateEntryData();
_loading = false;
}
bool hasVolumeControl(AudioAction action)
{
return action == AudioAction::SOURCE_VOLUME ||
action == AudioAction::MASTER_VOLUME;
}
bool hasSourceControl(AudioAction action)
{
return action != AudioAction::MASTER_VOLUME;
}
void MacroActionAudioEdit::SetWidgetVisibility()
{
if (hasVolumeControl(_entryData->_action)) {
_volumePercent->show();
} else {
_volumePercent->hide();
}
if (hasSourceControl(_entryData->_action)) {
_audioSources->show();
} else {
_audioSources->hide();
}
}
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) {
return;
}
std::lock_guard<std::mutex> lock(switcher->m);
_entryData->_audioSource = GetWeakSourceByQString(text);
emit HeaderInfoChanged(
QString::fromStdString(_entryData->GetShortDesc()));
}
void MacroActionAudioEdit::ActionChanged(int value)
{
if (_loading || !_entryData) {
return;
}
std::lock_guard<std::mutex> lock(switcher->m);
_entryData->_action = static_cast<AudioAction>(value);
SetWidgetVisibility();
}
void MacroActionAudioEdit::VolumeChanged(int value)
{
if (_loading || !_entryData) {
return;
}
std::lock_guard<std::mutex> lock(switcher->m);
_entryData->_volume = value;
}

184
src/macro-action-edit.cpp Normal file
View File

@@ -0,0 +1,184 @@
#include "headers/macro-action-edit.hpp"
#include "headers/macro-action-scene-switch.hpp"
#include "headers/utility.hpp"
#include "headers/advanced-scene-switcher.hpp"
std::map<std::string, MacroActionInfo> MacroActionFactory::_methods;
bool MacroActionFactory::Register(const std::string &id, MacroActionInfo info)
{
if (auto it = _methods.find(id); it == _methods.end()) {
_methods[id] = info;
return true;
}
return false;
}
std::shared_ptr<MacroAction> MacroActionFactory::Create(const std::string &id)
{
if (auto it = _methods.find(id); it != _methods.end())
return it->second._createFunc();
return nullptr;
}
QWidget *MacroActionFactory::CreateWidget(const std::string &id,
QWidget *parent,
std::shared_ptr<MacroAction> action)
{
if (auto it = _methods.find(id); it != _methods.end())
return it->second._createWidgetFunc(parent, action);
return nullptr;
}
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()) {
list->addItem(obs_module_text(entry.second._name.c_str()));
}
}
MacroActionEdit::MacroActionEdit(QWidget *parent,
std::shared_ptr<MacroAction> *entryData,
const std::string &id, bool startCollapsed)
: QWidget(parent)
{
_actionSelection = new QComboBox();
_section = new Section(300);
_headerInfo = new QLabel();
QWidget::connect(_actionSelection,
SIGNAL(currentTextChanged(const QString &)), this,
SLOT(ActionSelectionChanged(const QString &)));
QWidget::connect(parent, SIGNAL(MacroAdded(const QString &)), this,
SIGNAL(MacroAdded(const QString &)));
QWidget::connect(parent, SIGNAL(MacroRemoved(const QString &)), this,
SIGNAL(MacroRemoved(const QString &)));
QWidget::connect(parent,
SIGNAL(MacroRenamed(const QString &, const QString)),
this,
SIGNAL(MacroRenamed(const QString &, const QString)));
QWidget::connect(parent, SIGNAL(SceneGroupAdded(const QString &)), this,
SIGNAL(SceneGroupAdded(const QString &)));
QWidget::connect(parent, SIGNAL(SceneGroupRemoved(const QString &)),
this, SIGNAL(SceneGroupRemoved(const QString &)));
QWidget::connect(
parent,
SIGNAL(SceneGroupRenamed(const QString &, const QString)), this,
SIGNAL(SceneGroupRenamed(const QString &, const QString)));
populateActionSelection(_actionSelection);
_section->AddHeaderWidget(_actionSelection);
_section->AddHeaderWidget(_headerInfo);
QVBoxLayout *mainLayout = new QVBoxLayout;
mainLayout->addWidget(_section);
setLayout(mainLayout);
_entryData = entryData;
UpdateEntryData(id, startCollapsed);
_loading = false;
}
void MacroActionEdit::ActionSelectionChanged(const QString &text)
{
if (_loading || !_entryData) {
return;
}
std::string id = MacroActionFactory::GetIdByName(text);
HeaderInfoChanged("");
std::lock_guard<std::mutex> lock(switcher->m);
_entryData->reset();
*_entryData = MacroActionFactory::Create(id);
auto widget = MacroActionFactory::CreateWidget(id, this, *_entryData);
QWidget::connect(widget, SIGNAL(HeaderInfoChanged(const QString &)),
this, SLOT(HeaderInfoChanged(const QString &)));
_section->SetContent(widget, false);
}
void MacroActionEdit::UpdateEntryData(const std::string &id, bool collapse)
{
_actionSelection->setCurrentText(
obs_module_text(MacroActionFactory::GetActionName(id).c_str()));
auto widget = MacroActionFactory::CreateWidget(id, this, *_entryData);
QWidget::connect(widget, SIGNAL(HeaderInfoChanged(const QString &)),
this, SLOT(HeaderInfoChanged(const QString &)));
HeaderInfoChanged(
QString::fromStdString((*_entryData)->GetShortDesc()));
_section->SetContent(widget, collapse);
}
void MacroActionEdit::HeaderInfoChanged(const QString &text)
{
_headerInfo->setVisible(!text.isEmpty());
_headerInfo->setText(text);
}
void AdvSceneSwitcher::on_actionAdd_clicked()
{
auto macro = getSelectedMacro();
if (!macro) {
return;
}
MacroActionSwitchScene temp;
std::string id = temp.GetId();
std::lock_guard<std::mutex> lock(switcher->m);
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);
}
void AdvSceneSwitcher::on_actionRemove_clicked()
{
auto macro = getSelectedMacro();
if (!macro) {
return;
}
std::lock_guard<std::mutex> lock(switcher->m);
if (macro->Actions().empty()) {
return;
}
macro->Actions().pop_back();
int count = ui->macroEditActionLayout->count();
auto item = ui->macroEditActionLayout->takeAt(count - 1);
if (item) {
auto widget = item->widget();
if (widget) {
widget->setVisible(false);
}
delete item;
}
if (count == 1) {
ui->macroEditActionHelp->setVisible(true);
}
}

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

@@ -0,0 +1,187 @@
#include "headers/macro-action-file.hpp"
#include "headers/advanced-scene-switcher.hpp"
#include "headers/utility.hpp"
#include <QFile>
#include <QTextStream>
#include <QFileDialog>
const std::string MacroActionFile::id = "file";
bool MacroActionFile::_registered = MacroActionFactory::Register(
MacroActionFile::id,
{MacroActionFile::Create, MacroActionFileEdit::Create,
"AdvSceneSwitcher.action.file"});
const static std::map<FileAction, std::string> actionTypes = {
{FileAction::WRITE, "AdvSceneSwitcher.action.file.type.write"},
{FileAction::APPEND, "AdvSceneSwitcher.action.file.type.append"},
};
bool MacroActionFile::PerformAction()
{
QString path = QString::fromStdString(_file);
QFile file(path);
bool open = false;
switch (_action) {
case FileAction::WRITE:
open = file.open(QIODevice::WriteOnly);
break;
case FileAction::APPEND:
open = file.open(QIODevice::WriteOnly | QIODevice::Append);
break;
default:
break;
}
if (open) {
QTextStream out(&file);
out << QString::fromStdString(_text);
}
return true;
}
void MacroActionFile::LogAction()
{
auto it = actionTypes.find(_action);
if (it != actionTypes.end()) {
vblog(LOG_INFO, "performed action \"%s\" for file \"%s\"",
it->second.c_str(), _file.c_str());
} else {
blog(LOG_WARNING, "ignored unknown file action %d",
static_cast<int>(_action));
}
}
bool MacroActionFile::Save(obs_data_t *obj)
{
MacroAction::Save(obj);
obs_data_set_string(obj, "file", _file.c_str());
obs_data_set_string(obj, "text", _text.c_str());
obs_data_set_int(obj, "action", static_cast<int>(_action));
return true;
}
bool MacroActionFile::Load(obs_data_t *obj)
{
MacroAction::Load(obj);
_file = obs_data_get_string(obj, "file");
_text = obs_data_get_string(obj, "text");
_action = static_cast<FileAction>(obs_data_get_int(obj, "action"));
return true;
}
std::string MacroActionFile::GetShortDesc()
{
return _file;
}
static inline void populateActionSelection(QComboBox *list)
{
for (auto entry : actionTypes) {
list->addItem(obs_module_text(entry.second.c_str()));
}
}
MacroActionFileEdit::MacroActionFileEdit(
QWidget *parent, std::shared_ptr<MacroActionFile> entryData)
: QWidget(parent)
{
_filePath = new QLineEdit();
_browseButton =
new QPushButton(obs_module_text("AdvSceneSwitcher.browse"));
_text = new QPlainTextEdit();
_actions = new QComboBox();
populateActionSelection(_actions);
QWidget::connect(_actions, SIGNAL(currentIndexChanged(int)), this,
SLOT(ActionChanged(int)));
QWidget::connect(_filePath, SIGNAL(editingFinished()), this,
SLOT(FilePathChanged()));
QWidget::connect(_browseButton, SIGNAL(clicked()), this,
SLOT(BrowseButtonClicked()));
QWidget::connect(_text, SIGNAL(textChanged()), this,
SLOT(TextChanged()));
;
QHBoxLayout *entryLayout = new QHBoxLayout;
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
{"{{filePath}}", _filePath},
{"{{browseButton}}", _browseButton},
{"{{matchText}}", _text},
{"{{actions}}", _actions},
};
placeWidgets(obs_module_text("AdvSceneSwitcher.action.file.entry"),
entryLayout, widgetPlaceholders);
QVBoxLayout *mainLayout = new QVBoxLayout;
mainLayout->addLayout(entryLayout);
mainLayout->addWidget(_text);
setLayout(mainLayout);
_entryData = entryData;
UpdateEntryData();
_loading = false;
}
void MacroActionFileEdit::UpdateEntryData()
{
if (!_entryData) {
return;
}
_actions->setCurrentIndex(static_cast<int>(_entryData->_action));
_filePath->setText(QString::fromStdString(_entryData->_file));
_text->setPlainText(QString::fromStdString(_entryData->_text));
}
void MacroActionFileEdit::FilePathChanged()
{
if (_loading || !_entryData) {
return;
}
std::lock_guard<std::mutex> lock(switcher->m);
_entryData->_file = _filePath->text().toUtf8().constData();
emit HeaderInfoChanged(
QString::fromStdString(_entryData->GetShortDesc()));
}
void MacroActionFileEdit::BrowseButtonClicked()
{
if (_loading || !_entryData) {
return;
}
QString path = QFileDialog::getSaveFileName(
this,
tr(obs_module_text("AdvSceneSwitcher.fileTab.selectWrite")),
QDir::currentPath(),
tr(obs_module_text("AdvSceneSwitcher.fileTab.anyFileType")));
if (path.isEmpty()) {
return;
}
_filePath->setText(path);
FilePathChanged();
}
void MacroActionFileEdit::TextChanged()
{
if (_loading || !_entryData) {
return;
}
std::lock_guard<std::mutex> lock(switcher->m);
_entryData->_text = _text->toPlainText().toStdString();
}
void MacroActionFileEdit::ActionChanged(int value)
{
if (_loading || !_entryData) {
return;
}
std::lock_guard<std::mutex> lock(switcher->m);
_entryData->_action = static_cast<FileAction>(value);
}

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

@@ -0,0 +1,219 @@
#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"},
{FilterAction::SETTINGS,
"AdvSceneSwitcher.action.filter.type.settings"},
};
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;
case FilterAction::SETTINGS:
setSourceSettings(s, _settings);
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));
obs_data_set_string(obj, "settings", _settings.c_str());
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"));
_settings = obs_data_get_string(obj, "settings");
return true;
}
std::string MacroActionFilter::GetShortDesc()
{
if (_filter && _source) {
return GetWeakSourceName(_source) + " - " +
GetWeakSourceName(_filter);
}
return "";
}
static inline void populateActionSelection(QComboBox *list)
{
for (auto entry : actionTypes) {
list->addItem(obs_module_text(entry.second.c_str()));
}
}
MacroActionFilterEdit::MacroActionFilterEdit(
QWidget *parent, std::shared_ptr<MacroActionFilter> entryData)
: QWidget(parent)
{
_sources = new QComboBox();
_filters = new QComboBox();
_actions = new QComboBox();
_getSettings = new QPushButton(
obs_module_text("AdvSceneSwitcher.action.filter.getSettings"));
_settings = new QPlainTextEdit();
populateActionSelection(_actions);
populateSourcesWithFilterSelection(_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 &)));
QWidget::connect(_getSettings, SIGNAL(clicked()), this,
SLOT(GetSettingsClicked()));
QWidget::connect(_settings, SIGNAL(textChanged()), this,
SLOT(SettingsChanged()));
QHBoxLayout *entryLayout = new QHBoxLayout;
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
{"{{sources}}", _sources}, {"{{filters}}", _filters},
{"{{actions}}", _actions}, {"{{settings}}", _settings},
{"{{getSettings}}", _getSettings},
};
placeWidgets(obs_module_text("AdvSceneSwitcher.action.filter.entry"),
entryLayout, widgetPlaceholders);
QHBoxLayout *buttonLayout = new QHBoxLayout;
buttonLayout->addWidget(_getSettings);
buttonLayout->addStretch();
QVBoxLayout *mainLayout = new QVBoxLayout;
mainLayout->addLayout(entryLayout);
mainLayout->addWidget(_settings);
mainLayout->addLayout(buttonLayout);
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());
populateFilterSelection(_filters, _entryData->_source);
_filters->setCurrentText(
GetWeakSourceName(_entryData->_filter).c_str());
_settings->setPlainText(QString::fromStdString(_entryData->_settings));
SetWidgetVisibility(_entryData->_action == FilterAction::SETTINGS);
}
void MacroActionFilterEdit::SourceChanged(const QString &text)
{
if (_loading || !_entryData) {
return;
}
{
std::lock_guard<std::mutex> lock(switcher->m);
_entryData->_source = GetWeakSourceByQString(text);
}
_filters->clear();
populateFilterSelection(_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);
emit HeaderInfoChanged(
QString::fromStdString(_entryData->GetShortDesc()));
}
void MacroActionFilterEdit::ActionChanged(int value)
{
if (_loading || !_entryData) {
return;
}
std::lock_guard<std::mutex> lock(switcher->m);
_entryData->_action = static_cast<FilterAction>(value);
SetWidgetVisibility(_entryData->_action == FilterAction::SETTINGS);
}
void MacroActionFilterEdit::GetSettingsClicked()
{
if (_loading || !_entryData || !_entryData->_source ||
!_entryData->_filter) {
return;
}
_settings->setPlainText(
fromatJsonString(getSourceSettings(_entryData->_filter)));
}
void MacroActionFilterEdit::SettingsChanged()
{
if (_loading || !_entryData) {
return;
}
std::lock_guard<std::mutex> lock(switcher->m);
_entryData->_settings = _settings->toPlainText().toStdString();
}
void MacroActionFilterEdit::SetWidgetVisibility(bool showSettings)
{
_settings->setVisible(showSettings);
_getSettings->setVisible(showSettings);
adjustSize();
}

377
src/macro-action-hotkey.cpp Normal file
View File

@@ -0,0 +1,377 @@
#include "headers/macro-action-hotkey.hpp"
#include "headers/advanced-scene-switcher.hpp"
#include "headers/utility.hpp"
const std::string MacroActionHotkey::id = "hotkey";
// TODO:
// I can't seem to get this to work so drop support for this functionality
// on MacOS
#if !__APPLE__
bool MacroActionHotkey::_registered = MacroActionFactory::Register(
MacroActionHotkey::id,
{MacroActionHotkey::Create, MacroActionHotkeyEdit::Create,
"AdvSceneSwitcher.action.hotkey"});
#endif
bool MacroActionHotkey::PerformAction()
{
std::vector<HotkeyType> keys;
if (_leftShift) {
keys.push_back(HotkeyType::Key_Shift_L);
}
if (_rightShift) {
keys.push_back(HotkeyType::Key_Shift_R);
}
if (_leftCtrl) {
keys.push_back(HotkeyType::Key_Control_L);
}
if (_rightCtrl) {
keys.push_back(HotkeyType::Key_Control_R);
}
if (_leftAlt) {
keys.push_back(HotkeyType::Key_Alt_L);
}
if (_rightAlt) {
keys.push_back(HotkeyType::Key_Alt_R);
}
if (_key != HotkeyType::Key_NoKey) {
keys.push_back(_key);
}
if (!keys.empty()) {
std::thread t([keys]() { PressKeys(keys); });
t.detach();
}
return true;
}
void MacroActionHotkey::LogAction()
{
vblog(LOG_INFO, "sent hotkey");
}
bool MacroActionHotkey::Save(obs_data_t *obj)
{
MacroAction::Save(obj);
obs_data_set_int(obj, "key", static_cast<int>(_key));
obs_data_set_bool(obj, "left_shift", _leftShift);
obs_data_set_bool(obj, "right_shift", _rightShift);
obs_data_set_bool(obj, "left_ctrl", _leftCtrl);
obs_data_set_bool(obj, "right_ctrl", _rightCtrl);
obs_data_set_bool(obj, "left_alt", _leftAlt);
obs_data_set_bool(obj, "right_alt", _rightAlt);
obs_data_set_bool(obj, "left_meta", _leftMeta);
obs_data_set_bool(obj, "right_meta", _rightMeta);
return true;
}
bool MacroActionHotkey::Load(obs_data_t *obj)
{
MacroAction::Load(obj);
_key = static_cast<HotkeyType>(obs_data_get_int(obj, "key"));
_leftShift = obs_data_get_bool(obj, "left_shift");
_rightShift = obs_data_get_bool(obj, "right_shift");
_leftCtrl = obs_data_get_bool(obj, "left_ctrl");
_rightCtrl = obs_data_get_bool(obj, "right_ctrl");
_leftAlt = obs_data_get_bool(obj, "left_alt");
_rightAlt = obs_data_get_bool(obj, "right_alt");
_leftMeta = obs_data_get_bool(obj, "left_meta");
_rightMeta = obs_data_get_bool(obj, "right_meta");
return true;
}
static inline void populateKeySelection(QComboBox *list)
{
list->addItem("No key");
list->addItem("A");
list->addItem("B");
list->addItem("C");
list->addItem("D");
list->addItem("E");
list->addItem("F");
list->addItem("G");
list->addItem("H");
list->addItem("I");
list->addItem("J");
list->addItem("K");
list->addItem("L");
list->addItem("M");
list->addItem("N");
list->addItem("O");
list->addItem("P");
list->addItem("Q");
list->addItem("R");
list->addItem("S");
list->addItem("T");
list->addItem("U");
list->addItem("V");
list->addItem("W");
list->addItem("X");
list->addItem("Y");
list->addItem("Z");
list->addItem("0");
list->addItem("1");
list->addItem("2");
list->addItem("3");
list->addItem("4");
list->addItem("5");
list->addItem("6");
list->addItem("7");
list->addItem("8");
list->addItem("9");
list->addItem("F1");
list->addItem("F2");
list->addItem("F3");
list->addItem("F4");
list->addItem("F5");
list->addItem("F6");
list->addItem("F7");
list->addItem("F8");
list->addItem("F9");
list->addItem("F10");
list->addItem("F11");
list->addItem("F12");
list->addItem("F13");
list->addItem("F14");
list->addItem("F15");
list->addItem("F16");
list->addItem("F17");
list->addItem("F18");
list->addItem("F19");
list->addItem("F20");
list->addItem("F21");
list->addItem("F22");
list->addItem("F23");
list->addItem("F24");
list->addItem("Escape");
list->addItem("Space");
list->addItem("Return");
list->addItem("Backspace");
list->addItem("Tab");
list->addItem("Shift_L");
list->addItem("Shift_R");
list->addItem("Control_L");
list->addItem("Control_R");
list->addItem("Alt_L");
list->addItem("Alt_R");
list->addItem("Win_L");
list->addItem("Win_R");
list->addItem("Apps");
list->addItem("CapsLock");
list->addItem("NumLock");
list->addItem("ScrollLock");
list->addItem("PrintScreen");
list->addItem("Pause");
list->addItem("Insert");
list->addItem("Delete");
list->addItem("PageUP");
list->addItem("PageDown");
list->addItem("Home");
list->addItem("End");
list->addItem("Left");
list->addItem("Right");
list->addItem("Up");
list->addItem("Down");
list->addItem("Numpad0");
list->addItem("Numpad1");
list->addItem("Numpad2");
list->addItem("Numpad3");
list->addItem("Numpad4");
list->addItem("Numpad5");
list->addItem("Numpad6");
list->addItem("Numpad7");
list->addItem("Numpad8");
list->addItem("Numpad9");
list->addItem("NumpadAdd");
list->addItem("NumpadSubtract");
list->addItem("NumpadMultiply");
list->addItem("NumpadDivide");
list->addItem("NumpadDecimal");
list->addItem("NumpadEnter");
}
MacroActionHotkeyEdit::MacroActionHotkeyEdit(
QWidget *parent, std::shared_ptr<MacroActionHotkey> entryData)
: QWidget(parent)
{
_keys = new QComboBox();
_leftShift = new QCheckBox(
obs_module_text("AdvSceneSwitcher.action.hotkey.leftShift"));
_rightShift = new QCheckBox(
obs_module_text("AdvSceneSwitcher.action.hotkey.rightShift"));
_leftCtrl = new QCheckBox(
obs_module_text("AdvSceneSwitcher.action.hotkey.leftCtrl"));
_rightCtrl = new QCheckBox(
obs_module_text("AdvSceneSwitcher.action.hotkey.rightCtrl"));
_leftAlt = new QCheckBox(
obs_module_text("AdvSceneSwitcher.action.hotkey.leftAlt"));
_rightAlt = new QCheckBox(
obs_module_text("AdvSceneSwitcher.action.hotkey.rightAlt"));
_leftMeta = new QCheckBox(
obs_module_text("AdvSceneSwitcher.action.hotkey.leftMeta"));
_rightMeta = new QCheckBox(
obs_module_text("AdvSceneSwitcher.action.hotkey.rightMeta"));
populateKeySelection(_keys);
QWidget::connect(_keys, SIGNAL(currentIndexChanged(int)), this,
SLOT(KeyChanged(int)));
QWidget::connect(_leftShift, SIGNAL(stateChanged(int)), this,
SLOT(LShiftChanged(int)));
QWidget::connect(_rightShift, SIGNAL(stateChanged(int)), this,
SLOT(RShiftChanged(int)));
QWidget::connect(_leftCtrl, SIGNAL(stateChanged(int)), this,
SLOT(LCtrlChanged(int)));
QWidget::connect(_rightCtrl, SIGNAL(stateChanged(int)), this,
SLOT(RCtrlChanged(int)));
QWidget::connect(_leftAlt, SIGNAL(stateChanged(int)), this,
SLOT(LAltChanged(int)));
QWidget::connect(_rightAlt, SIGNAL(stateChanged(int)), this,
SLOT(RAltChanged(int)));
QWidget::connect(_leftMeta, SIGNAL(stateChanged(int)), this,
SLOT(LMetaChanged(int)));
QWidget::connect(_rightMeta, SIGNAL(stateChanged(int)), this,
SLOT(RMetaChanged(int)));
QHBoxLayout *line1Layout = new QHBoxLayout;
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
{"{{keys}}", _keys},
};
placeWidgets(obs_module_text("AdvSceneSwitcher.action.hotkey.entry"),
line1Layout, widgetPlaceholders);
QHBoxLayout *line2Layout = new QHBoxLayout;
line2Layout->addWidget(_leftShift);
line2Layout->addWidget(_rightShift);
line2Layout->addWidget(_leftCtrl);
line2Layout->addWidget(_rightCtrl);
line2Layout->addWidget(_leftAlt);
line2Layout->addWidget(_rightAlt);
line2Layout->addWidget(_leftMeta);
line2Layout->addWidget(_rightMeta);
line2Layout->addStretch();
QVBoxLayout *mainLayout = new QVBoxLayout;
mainLayout->addLayout(line1Layout);
mainLayout->addLayout(line2Layout);
if (!canSimulateKeyPresses) {
mainLayout->addWidget(new QLabel(obs_module_text(
"AdvSceneSwitcher.action.hotkey.disabled")));
}
setLayout(mainLayout);
_entryData = entryData;
UpdateEntryData();
_loading = false;
}
void MacroActionHotkeyEdit::UpdateEntryData()
{
if (!_entryData) {
return;
}
_keys->setCurrentIndex(static_cast<int>(_entryData->_key));
_leftShift->setChecked(_entryData->_leftShift);
_rightShift->setChecked(_entryData->_rightShift);
_leftCtrl->setChecked(_entryData->_leftCtrl);
_rightCtrl->setChecked(_entryData->_rightCtrl);
_leftAlt->setChecked(_entryData->_leftAlt);
_rightAlt->setChecked(_entryData->_rightAlt);
_leftMeta->setChecked(_entryData->_leftMeta);
_rightMeta->setChecked(_entryData->_rightMeta);
}
void MacroActionHotkeyEdit::LShiftChanged(int state)
{
if (_loading || !_entryData) {
return;
}
std::lock_guard<std::mutex> lock(switcher->m);
_entryData->_leftShift = state;
}
void MacroActionHotkeyEdit::RShiftChanged(int state)
{
if (_loading || !_entryData) {
return;
}
std::lock_guard<std::mutex> lock(switcher->m);
_entryData->_rightShift = state;
}
void MacroActionHotkeyEdit::LCtrlChanged(int state)
{
if (_loading || !_entryData) {
return;
}
std::lock_guard<std::mutex> lock(switcher->m);
_entryData->_leftCtrl = state;
}
void MacroActionHotkeyEdit::RCtrlChanged(int state)
{
if (_loading || !_entryData) {
return;
}
std::lock_guard<std::mutex> lock(switcher->m);
_entryData->_rightCtrl = state;
}
void MacroActionHotkeyEdit::LAltChanged(int state)
{
if (_loading || !_entryData) {
return;
}
std::lock_guard<std::mutex> lock(switcher->m);
_entryData->_leftAlt = state;
}
void MacroActionHotkeyEdit::RAltChanged(int state)
{
if (_loading || !_entryData) {
return;
}
std::lock_guard<std::mutex> lock(switcher->m);
_entryData->_rightAlt = state;
}
void MacroActionHotkeyEdit::LMetaChanged(int state)
{
if (_loading || !_entryData) {
return;
}
std::lock_guard<std::mutex> lock(switcher->m);
_entryData->_leftMeta = state;
}
void MacroActionHotkeyEdit::RMetaChanged(int state)
{
if (_loading || !_entryData) {
return;
}
std::lock_guard<std::mutex> lock(switcher->m);
_entryData->_rightMeta = state;
}
void MacroActionHotkeyEdit::KeyChanged(int key)
{
if (_loading || !_entryData) {
return;
}
std::lock_guard<std::mutex> lock(switcher->m);
_entryData->_key = static_cast<HotkeyType>(key);
}

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

@@ -0,0 +1,162 @@
#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;
}
std::string MacroActionMacro::GetShortDesc()
{
if (_macro.get()) {
return _macro->Name();
}
return "";
}
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);
emit HeaderInfoChanged(
QString::fromStdString(_entryData->GetShortDesc()));
}
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();
}
}

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

@@ -0,0 +1,163 @@
#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;
}
std::string MacroActionMedia::GetShortDesc()
{
if (_mediaSource) {
return GetWeakSourceName(_mediaSource);
}
return "";
}
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);
emit HeaderInfoChanged(
QString::fromStdString(_entryData->GetShortDesc()));
}
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,203 @@
#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"},
{PluginStateAction::NO_MATCH_BEHAVIOUR,
"AdvSceneSwitcher.action.pluginState.type.noMatch"},
};
const static std::map<NoMatch, std::string> noMatchValues = {
{NO_SWITCH,
"AdvSceneSwitcher.generalTab.generalBehavior.onNoMet.dontSwitch"},
{SWITCH,
"AdvSceneSwitcher.generalTab.generalBehavior.onNoMet.switchTo"},
{RANDOM_SWITCH,
"AdvSceneSwitcher.generalTab.generalBehavior.onNoMet.switchToRandom"},
};
void stopPlugin()
{
std::thread t([]() { switcher->Stop(); });
t.detach();
}
void setNoMatchBehaviour(int value, OBSWeakSource &scene)
{
switcher->switchIfNotMatching = static_cast<NoMatch>(value);
if (switcher->switchIfNotMatching == SWITCH) {
switcher->nonMatchingScene = scene;
}
}
bool MacroActionPluginState::PerformAction()
{
switch (_action) {
case PluginStateAction::STOP:
stopPlugin();
break;
case PluginStateAction::NO_MATCH_BEHAVIOUR:
setNoMatchBehaviour(_value, _scene);
break;
default:
break;
}
return true;
}
void MacroActionPluginState::LogAction()
{
switch (_action) {
case PluginStateAction::STOP:
blog(LOG_INFO, "stop() called by macro");
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));
obs_data_set_int(obj, "value", _value);
obs_data_set_string(obj, "scene", GetWeakSourceName(_scene).c_str());
return true;
}
bool MacroActionPluginState::Load(obs_data_t *obj)
{
MacroAction::Load(obj);
_action =
static_cast<PluginStateAction>(obs_data_get_int(obj, "action"));
_value = obs_data_get_int(obj, "value");
const char *sceneName = obs_data_get_string(obj, "scene");
_scene = GetWeakSourceByName(sceneName);
return true;
}
static inline void populateActionSelection(QComboBox *list)
{
for (auto entry : actionTypes) {
list->addItem(obs_module_text(entry.second.c_str()));
}
}
static inline void populateValueSelection(QComboBox *list,
PluginStateAction action)
{
if (action == PluginStateAction::NO_MATCH_BEHAVIOUR) {
for (auto entry : noMatchValues) {
list->addItem(obs_module_text(entry.second.c_str()));
}
}
}
MacroActionPluginStateEdit::MacroActionPluginStateEdit(
QWidget *parent, std::shared_ptr<MacroActionPluginState> entryData)
: QWidget(parent)
{
_actions = new QComboBox();
_values = new QComboBox();
_scenes = new QComboBox();
populateActionSelection(_actions);
populateSceneSelection(_scenes);
QWidget::connect(_actions, SIGNAL(currentIndexChanged(int)), this,
SLOT(ActionChanged(int)));
QWidget::connect(_values, SIGNAL(currentIndexChanged(int)), this,
SLOT(ValueChanged(int)));
QWidget::connect(_scenes, SIGNAL(currentTextChanged(const QString &)),
this, SLOT(SceneChanged(const QString &)));
QHBoxLayout *mainLayout = new QHBoxLayout;
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
{"{{actions}}", _actions},
{"{{values}}", _values},
{"{{scenes}}", _scenes},
};
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));
populateValueSelection(_values, _entryData->_action);
_values->setCurrentIndex(_entryData->_value);
_scenes->setCurrentText(GetWeakSourceName(_entryData->_scene).c_str());
SetWidgetVisibility(_entryData->_action, _entryData->_value);
}
void MacroActionPluginStateEdit::ActionChanged(int value)
{
if (_loading || !_entryData) {
return;
}
{
std::lock_guard<std::mutex> lock(switcher->m);
_entryData->_action = static_cast<PluginStateAction>(value);
SetWidgetVisibility(_entryData->_action, _entryData->_value);
}
_values->clear();
populateValueSelection(_values, _entryData->_action);
}
void MacroActionPluginStateEdit::ValueChanged(int value)
{
if (_loading || !_entryData) {
return;
}
std::lock_guard<std::mutex> lock(switcher->m);
_entryData->_value = value;
SetWidgetVisibility(_entryData->_action, _entryData->_value);
}
void MacroActionPluginStateEdit::SceneChanged(const QString &text)
{
if (_loading || !_entryData) {
return;
}
std::lock_guard<std::mutex> lock(switcher->m);
_entryData->_scene = GetWeakSourceByQString(text);
}
void MacroActionPluginStateEdit::SetWidgetVisibility(PluginStateAction action,
int value)
{
_values->hide();
_scenes->hide();
if (action == PluginStateAction::NO_MATCH_BEHAVIOUR) {
_values->show();
if ((NoMatch)value == SWITCH) {
_scenes->show();
}
}
}

View File

@@ -0,0 +1,133 @@
#include "headers/macro-action-recording.hpp"
#include "headers/advanced-scene-switcher.hpp"
#include "headers/utility.hpp"
const std::string MacroActionRecord::id = "recording";
bool MacroActionRecord::_registered = MacroActionFactory::Register(
MacroActionRecord::id,
{MacroActionRecord::Create, MacroActionRecordEdit::Create,
"AdvSceneSwitcher.action.recording"});
const static std::map<RecordAction, std::string> actionTypes = {
{RecordAction::STOP, "AdvSceneSwitcher.action.recording.type.stop"},
{RecordAction::START, "AdvSceneSwitcher.action.recording.type.start"},
{RecordAction::PAUSE, "AdvSceneSwitcher.action.recording.type.pause"},
{RecordAction::UNPAUSE,
"AdvSceneSwitcher.action.recording.type.unpause"},
};
bool MacroActionRecord::PerformAction()
{
switch (_action) {
case RecordAction::STOP:
if (obs_frontend_recording_active()) {
obs_frontend_recording_stop();
}
break;
case RecordAction::START:
if (!obs_frontend_recording_active()) {
obs_frontend_recording_start();
}
break;
case RecordAction::PAUSE:
if (obs_frontend_recording_active() &&
!obs_frontend_recording_paused()) {
obs_frontend_recording_pause(true);
}
break;
case RecordAction::UNPAUSE:
if (obs_frontend_recording_active() &&
obs_frontend_recording_paused()) {
obs_frontend_recording_pause(false);
}
break;
default:
break;
}
return true;
}
void MacroActionRecord::LogAction()
{
auto it = actionTypes.find(_action);
if (it != actionTypes.end()) {
vblog(LOG_INFO, "performed action \"%s\"", it->second.c_str());
} else {
blog(LOG_WARNING, "ignored unknown recording action %d",
static_cast<int>(_action));
}
}
bool MacroActionRecord::Save(obs_data_t *obj)
{
MacroAction::Save(obj);
obs_data_set_int(obj, "action", static_cast<int>(_action));
return true;
}
bool MacroActionRecord::Load(obs_data_t *obj)
{
MacroAction::Load(obj);
_action = static_cast<RecordAction>(obs_data_get_int(obj, "action"));
return true;
}
static inline void populateActionSelection(QComboBox *list)
{
for (auto entry : actionTypes) {
list->addItem(obs_module_text(entry.second.c_str()));
}
}
MacroActionRecordEdit::MacroActionRecordEdit(
QWidget *parent, std::shared_ptr<MacroActionRecord> entryData)
: QWidget(parent)
{
_actions = new QComboBox();
_pauseHint = new QLabel(obs_module_text(
"AdvSceneSwitcher.action.recording.pause.hint"));
populateActionSelection(_actions);
QWidget::connect(_actions, SIGNAL(currentIndexChanged(int)), this,
SLOT(ActionChanged(int)));
QHBoxLayout *mainLayout = new QHBoxLayout;
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
{"{{actions}}", _actions},
{"{{pauseHint}}", _pauseHint},
};
placeWidgets(obs_module_text("AdvSceneSwitcher.action.recording.entry"),
mainLayout, widgetPlaceholders);
setLayout(mainLayout);
_entryData = entryData;
UpdateEntryData();
_loading = false;
}
bool isPauseAction(RecordAction a)
{
return a == RecordAction::PAUSE || a == RecordAction::UNPAUSE;
}
void MacroActionRecordEdit::UpdateEntryData()
{
if (!_entryData) {
return;
}
_actions->setCurrentIndex(static_cast<int>(_entryData->_action));
_pauseHint->setVisible(isPauseAction(_entryData->_action));
}
void MacroActionRecordEdit::ActionChanged(int value)
{
if (_loading || !_entryData) {
return;
}
std::lock_guard<std::mutex> lock(switcher->m);
_entryData->_action = static_cast<RecordAction>(value);
_pauseHint->setVisible(isPauseAction(_entryData->_action));
}

View File

@@ -0,0 +1,121 @@
#include "headers/macro-action-replay-buffer.hpp"
#include "headers/advanced-scene-switcher.hpp"
#include "headers/utility.hpp"
const std::string MacroActionReplayBuffer::id = "replay_buffer";
bool MacroActionReplayBuffer::_registered = MacroActionFactory::Register(
MacroActionReplayBuffer::id,
{MacroActionReplayBuffer::Create, MacroActionReplayBufferEdit::Create,
"AdvSceneSwitcher.action.replay"});
const static std::map<ReplayBufferAction, std::string> actionTypes = {
{ReplayBufferAction::STOP, "AdvSceneSwitcher.action.replay.type.stop"},
{ReplayBufferAction::START,
"AdvSceneSwitcher.action.replay.type.start"},
{ReplayBufferAction::SAVE, "AdvSceneSwitcher.action.replay.type.save"},
};
bool MacroActionReplayBuffer::PerformAction()
{
switch (_action) {
case ReplayBufferAction::STOP:
if (obs_frontend_replay_buffer_active()) {
obs_frontend_replay_buffer_stop();
}
break;
case ReplayBufferAction::START:
if (!obs_frontend_replay_buffer_active()) {
obs_frontend_replay_buffer_start();
}
break;
case ReplayBufferAction::SAVE:
if (obs_frontend_replay_buffer_active() &&
_duration.DurationReached()) {
obs_frontend_replay_buffer_save();
// Default buffer size is 20s so waiting for 10s before
// trying to save again seems reasonable
_duration.seconds = 10;
_duration.Reset();
}
break;
default:
break;
}
return true;
}
void MacroActionReplayBuffer::LogAction()
{
auto it = actionTypes.find(_action);
if (it != actionTypes.end()) {
vblog(LOG_INFO, "performed action \"%s\"", it->second.c_str());
} else {
blog(LOG_WARNING, "ignored unknown replay buffer action %d",
static_cast<int>(_action));
}
}
bool MacroActionReplayBuffer::Save(obs_data_t *obj)
{
MacroAction::Save(obj);
obs_data_set_int(obj, "action", static_cast<int>(_action));
return true;
}
bool MacroActionReplayBuffer::Load(obs_data_t *obj)
{
MacroAction::Load(obj);
_action = static_cast<ReplayBufferAction>(
obs_data_get_int(obj, "action"));
return true;
}
static inline void populateActionSelection(QComboBox *list)
{
for (auto entry : actionTypes) {
list->addItem(obs_module_text(entry.second.c_str()));
}
}
MacroActionReplayBufferEdit::MacroActionReplayBufferEdit(
QWidget *parent, std::shared_ptr<MacroActionReplayBuffer> entryData)
: QWidget(parent)
{
_actions = new QComboBox();
populateActionSelection(_actions);
QWidget::connect(_actions, SIGNAL(currentIndexChanged(int)), this,
SLOT(ActionChanged(int)));
QHBoxLayout *mainLayout = new QHBoxLayout;
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
{"{{actions}}", _actions},
};
placeWidgets(obs_module_text("AdvSceneSwitcher.action.replay.entry"),
mainLayout, widgetPlaceholders);
setLayout(mainLayout);
_entryData = entryData;
UpdateEntryData();
_loading = false;
}
void MacroActionReplayBufferEdit::UpdateEntryData()
{
if (!_entryData) {
return;
}
_actions->setCurrentIndex(static_cast<int>(_entryData->_action));
}
void MacroActionReplayBufferEdit::ActionChanged(int value)
{
if (_loading || !_entryData) {
return;
}
std::lock_guard<std::mutex> lock(switcher->m);
_entryData->_action = static_cast<ReplayBufferAction>(value);
}

242
src/macro-action-run.cpp Normal file
View File

@@ -0,0 +1,242 @@
#include "headers/macro-action-run.hpp"
#include "headers/advanced-scene-switcher.hpp"
#include "headers/name-dialog.hpp"
#include "headers/utility.hpp"
#include <QProcess>
#include <QFileDialog>
const std::string MacroActionRun::id = "run";
bool MacroActionRun::_registered = MacroActionFactory::Register(
MacroActionRun::id, {MacroActionRun::Create, MacroActionRunEdit::Create,
"AdvSceneSwitcher.action.run"});
bool MacroActionRun::PerformAction()
{
QProcess::startDetached(QString::fromStdString(_path), _args);
return true;
}
void MacroActionRun::LogAction()
{
vblog(LOG_INFO, "run \"%s\"", _path.c_str());
}
bool MacroActionRun::Save(obs_data_t *obj)
{
MacroAction::Save(obj);
obs_data_set_string(obj, "path", _path.c_str());
obs_data_array_t *args = obs_data_array_create();
for (auto &arg : _args) {
obs_data_t *array_obj = obs_data_create();
obs_data_set_string(array_obj, "arg",
arg.toStdString().c_str());
obs_data_array_push_back(args, array_obj);
obs_data_release(array_obj);
}
obs_data_set_array(obj, "args", args);
obs_data_array_release(args);
return true;
}
bool MacroActionRun::Load(obs_data_t *obj)
{
MacroAction::Load(obj);
_path = obs_data_get_string(obj, "path");
obs_data_array_t *args = obs_data_get_array(obj, "args");
size_t count = obs_data_array_count(args);
for (size_t i = 0; i < count; i++) {
obs_data_t *array_obj = obs_data_array_item(args, i);
_args << QString::fromStdString(
obs_data_get_string(array_obj, "arg"));
obs_data_release(array_obj);
}
obs_data_array_release(args);
return true;
}
std::string MacroActionRun::GetShortDesc()
{
return _path;
}
MacroActionRunEdit::MacroActionRunEdit(
QWidget *parent, std::shared_ptr<MacroActionRun> entryData)
: QWidget(parent)
{
_filePath = new QLineEdit();
_browseButton =
new QPushButton(obs_module_text("AdvSceneSwitcher.browse"));
_argList = new QListWidget();
_addArg = new QPushButton();
_addArg->setMaximumSize(QSize(22, 22));
_addArg->setProperty("themeID",
QVariant(QString::fromUtf8("addIconSmall")));
_removeArg = new QPushButton();
_removeArg->setMaximumSize(QSize(22, 22));
_removeArg->setProperty("themeID",
QVariant(QString::fromUtf8("removeIconSmall")));
_argUp = new QPushButton();
_argUp->setMaximumSize(QSize(22, 22));
_argUp->setProperty("themeID",
QVariant(QString::fromUtf8("upArrowIconSmall")));
_argDown = new QPushButton();
_argDown->setMaximumSize(QSize(22, 22));
_argDown->setProperty(
"themeID", QVariant(QString::fromUtf8("downArrowIconSmall")));
QWidget::connect(_filePath, SIGNAL(editingFinished()), this,
SLOT(FilePathChanged()));
QWidget::connect(_browseButton, SIGNAL(clicked()), this,
SLOT(BrowseButtonClicked()));
QWidget::connect(_addArg, SIGNAL(clicked()), this, SLOT(AddArg()));
QWidget::connect(_removeArg, SIGNAL(clicked()), this,
SLOT(RemoveArg()));
QWidget::connect(_argUp, SIGNAL(clicked()), this, SLOT(ArgUp()));
QWidget::connect(_argDown, SIGNAL(clicked()), this, SLOT(ArgDown()));
auto *entryLayout = new QHBoxLayout;
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
{"{{filePath}}", _filePath},
{"{{browseButton}}", _browseButton},
};
placeWidgets(obs_module_text("AdvSceneSwitcher.action.run.entry"),
entryLayout, widgetPlaceholders);
auto *argButtonLayout = new QHBoxLayout;
argButtonLayout->addWidget(_addArg);
argButtonLayout->addWidget(_removeArg);
QFrame *line = new QFrame();
line->setFrameShape(QFrame::VLine);
line->setFrameShadow(QFrame::Sunken);
argButtonLayout->addWidget(line);
argButtonLayout->addWidget(_argUp);
argButtonLayout->addWidget(_argDown);
argButtonLayout->addStretch();
auto *mainLayout = new QVBoxLayout;
mainLayout->addLayout(entryLayout);
mainLayout->addWidget(new QLabel(
obs_module_text("AdvSceneSwitcher.action.run.arguments")));
mainLayout->addWidget(_argList);
mainLayout->addLayout(argButtonLayout);
setLayout(mainLayout);
_entryData = entryData;
UpdateEntryData();
_loading = false;
}
void MacroActionRunEdit::UpdateEntryData()
{
if (!_entryData) {
return;
}
_filePath->setText(QString::fromStdString(_entryData->_path));
for (auto &arg : _entryData->_args) {
QListWidgetItem *item = new QListWidgetItem(arg, _argList);
item->setData(Qt::UserRole, arg);
}
}
void MacroActionRunEdit::FilePathChanged()
{
if (_loading || !_entryData) {
return;
}
std::lock_guard<std::mutex> lock(switcher->m);
_entryData->_path = _filePath->text().toUtf8().constData();
emit HeaderInfoChanged(
QString::fromStdString(_entryData->GetShortDesc()));
}
void MacroActionRunEdit::BrowseButtonClicked()
{
if (_loading || !_entryData) {
return;
}
QString path = QFileDialog::getOpenFileName(this);
if (path.isEmpty()) {
return;
}
_filePath->setText(path);
FilePathChanged();
}
void MacroActionRunEdit::AddArg()
{
if (_loading || !_entryData) {
return;
}
std::string name;
bool accepted = AdvSSNameDialog::AskForName(
this,
obs_module_text("AdvSceneSwitcher.action.run.addArgument"),
obs_module_text(
"AdvSceneSwitcher.action.run.addArgumentDescription"),
name, "", 170, false);
if (!accepted || name.empty()) {
return;
}
auto arg = QString::fromStdString(name);
QVariant v = QVariant::fromValue(arg);
QListWidgetItem *item = new QListWidgetItem(arg, _argList);
item->setData(Qt::UserRole, arg);
std::lock_guard<std::mutex> lock(switcher->m);
_entryData->_args << arg;
}
void MacroActionRunEdit::RemoveArg()
{
if (_loading || !_entryData) {
return;
}
std::lock_guard<std::mutex> lock(switcher->m);
int idx = _argList->currentRow();
if (idx == -1) {
return;
}
_entryData->_args.removeAt(idx);
QListWidgetItem *item = _argList->currentItem();
if (!item) {
return;
}
delete item;
}
void MacroActionRunEdit::ArgUp()
{
if (_loading || !_entryData) {
return;
}
int idx = _argList->currentRow();
if (idx != -1 && idx != 0) {
_argList->insertItem(idx - 1, _argList->takeItem(idx));
_argList->setCurrentRow(idx - 1);
std::lock_guard<std::mutex> lock(switcher->m);
_entryData->_args.move(idx, idx - 1);
}
}
void MacroActionRunEdit::ArgDown()
{
int idx = _argList->currentRow();
if (idx != -1 && idx != _argList->count() - 1) {
_argList->insertItem(idx + 1, _argList->takeItem(idx));
_argList->setCurrentRow(idx + 1);
std::lock_guard<std::mutex> lock(switcher->m);
_entryData->_args.move(idx, idx + 1);
}
}

View File

@@ -0,0 +1,208 @@
#include "headers/macro-action-scene-order.hpp"
#include "headers/advanced-scene-switcher.hpp"
#include "headers/utility.hpp"
const std::string MacroActionSceneOrder::id = "scene_order";
bool MacroActionSceneOrder::_registered = MacroActionFactory::Register(
MacroActionSceneOrder::id,
{MacroActionSceneOrder::Create, MacroActionSceneOrderEdit::Create,
"AdvSceneSwitcher.action.sceneOrder"});
const static std::map<SceneOrderAction, std::string> actionTypes = {
{SceneOrderAction::MOVE_UP,
"AdvSceneSwitcher.action.sceneOrder.type.moveUp"},
{SceneOrderAction::MOVE_DOWN,
"AdvSceneSwitcher.action.sceneOrder.type.moveDown"},
{SceneOrderAction::MOVE_TOP,
"AdvSceneSwitcher.action.sceneOrder.type.moveTop"},
{SceneOrderAction::MOVE_BOTTOM,
"AdvSceneSwitcher.action.sceneOrder.type.moveBottom"},
{SceneOrderAction::POSITION,
"AdvSceneSwitcher.action.sceneOrder.type.movePosition"},
};
bool MacroActionSceneOrder::PerformAction()
{
auto s = obs_weak_source_get_source(_scene.GetScene());
auto scene = obs_scene_from_source(s);
auto name = GetWeakSourceName(_source);
auto items = getSceneItemsWithName(scene, name);
for (auto &i : items) {
switch (_action) {
case SceneOrderAction::MOVE_UP:
obs_sceneitem_set_order(i, OBS_ORDER_MOVE_UP);
break;
case SceneOrderAction::MOVE_DOWN:
obs_sceneitem_set_order(i, OBS_ORDER_MOVE_DOWN);
break;
case SceneOrderAction::MOVE_TOP:
obs_sceneitem_set_order(i, OBS_ORDER_MOVE_TOP);
break;
case SceneOrderAction::MOVE_BOTTOM:
obs_sceneitem_set_order(i, OBS_ORDER_MOVE_BOTTOM);
break;
case SceneOrderAction::POSITION:
obs_sceneitem_set_order_position(i, _position);
break;
default:
break;
}
obs_sceneitem_release(i);
}
obs_source_release(s);
return true;
}
void MacroActionSceneOrder::LogAction()
{
auto it = actionTypes.find(_action);
if (it != actionTypes.end()) {
vblog(LOG_INFO,
"performed order action \"%s\" for source \"%s\" on scene \"%s\"",
it->second.c_str(), GetWeakSourceName(_source).c_str(),
_scene.ToString().c_str());
} else {
blog(LOG_WARNING, "ignored unknown scene order action %d",
static_cast<int>(_action));
}
}
bool MacroActionSceneOrder::Save(obs_data_t *obj)
{
MacroAction::Save(obj);
_scene.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_int(obj, "position", _position);
return true;
}
bool MacroActionSceneOrder::Load(obs_data_t *obj)
{
MacroAction::Load(obj);
_scene.Load(obj);
const char *sourceName = obs_data_get_string(obj, "source");
_source = GetWeakSourceByName(sourceName);
_action =
static_cast<SceneOrderAction>(obs_data_get_int(obj, "action"));
_position = obs_data_get_int(obj, "position");
return true;
}
std::string MacroActionSceneOrder::GetShortDesc()
{
if (_source) {
return _scene.ToString() + " - " + GetWeakSourceName(_source);
}
return "";
}
static inline void populateActionSelection(QComboBox *list)
{
for (auto entry : actionTypes) {
list->addItem(obs_module_text(entry.second.c_str()));
}
}
MacroActionSceneOrderEdit::MacroActionSceneOrderEdit(
QWidget *parent, std::shared_ptr<MacroActionSceneOrder> entryData)
: QWidget(parent)
{
_scenes = new SceneSelectionWidget(window(), false, false, true);
_sources = new QComboBox();
_actions = new QComboBox();
_position = new QSpinBox();
populateActionSelection(_actions);
QWidget::connect(_actions, SIGNAL(currentIndexChanged(int)), this,
SLOT(ActionChanged(int)));
QWidget::connect(_scenes, SIGNAL(SceneChanged(const SceneSelection &)),
this, SLOT(SceneChanged(const SceneSelection &)));
QWidget::connect(_sources, SIGNAL(currentTextChanged(const QString &)),
this, SLOT(SourceChanged(const QString &)));
QWidget::connect(_position, SIGNAL(valueChanged(int)), this,
SLOT(PositionChanged(int)));
QHBoxLayout *mainLayout = new QHBoxLayout;
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
{"{{scenes}}", _scenes},
{"{{sources}}", _sources},
{"{{actions}}", _actions},
{"{{position}}", _position},
};
placeWidgets(
obs_module_text("AdvSceneSwitcher.action.sceneOrder.entry"),
mainLayout, widgetPlaceholders);
setLayout(mainLayout);
_entryData = entryData;
UpdateEntryData();
_loading = false;
}
void MacroActionSceneOrderEdit::UpdateEntryData()
{
if (!_entryData) {
return;
}
_actions->setCurrentIndex(static_cast<int>(_entryData->_action));
_scenes->SetScene(_entryData->_scene);
populateSceneItemSelection(_sources, _entryData->_scene);
_sources->setCurrentText(
GetWeakSourceName(_entryData->_source).c_str());
_position->setValue(_entryData->_position);
_position->setVisible(_entryData->_action ==
SceneOrderAction::POSITION);
}
void MacroActionSceneOrderEdit::SceneChanged(const SceneSelection &s)
{
if (_loading || !_entryData) {
return;
}
{
std::lock_guard<std::mutex> lock(switcher->m);
_entryData->_scene = s;
}
_sources->clear();
populateSceneItemSelection(_sources, _entryData->_scene);
}
void MacroActionSceneOrderEdit::SourceChanged(const QString &text)
{
if (_loading || !_entryData) {
return;
}
std::lock_guard<std::mutex> lock(switcher->m);
_entryData->_source = GetWeakSourceByQString(text);
emit HeaderInfoChanged(
QString::fromStdString(_entryData->GetShortDesc()));
}
void MacroActionSceneOrderEdit::ActionChanged(int value)
{
if (_loading || !_entryData) {
return;
}
std::lock_guard<std::mutex> lock(switcher->m);
_entryData->_action = static_cast<SceneOrderAction>(value);
_position->setVisible(_entryData->_action ==
SceneOrderAction::POSITION);
}
void MacroActionSceneOrderEdit::PositionChanged(int value)
{
if (_loading || !_entryData) {
return;
}
std::lock_guard<std::mutex> lock(switcher->m);
_entryData->_position = value;
}

View File

@@ -0,0 +1,92 @@
#include "headers/macro-action-scene-switch.hpp"
#include "headers/advanced-scene-switcher.hpp"
#include "headers/utility.hpp"
const std::string MacroActionSwitchScene::id = "scene_switch";
bool MacroActionSwitchScene::_registered = MacroActionFactory::Register(
MacroActionSwitchScene::id,
{MacroActionSwitchScene::Create, MacroActionSwitchSceneEdit::Create,
"AdvSceneSwitcher.action.switchScene"});
bool MacroActionSwitchScene::PerformAction()
{
OBSWeakSource scene = getScene();
switchScene({scene, transition, (int)(_duration.seconds * 1000)});
return true;
}
void MacroActionSwitchScene::LogAction()
{
if (switcher->verbose) {
logMatch();
}
}
bool MacroActionSwitchScene::Save(obs_data_t *obj)
{
MacroAction::Save(obj);
SceneSwitcherEntry::save(obj);
_duration.Save(obj);
return true;
}
bool MacroActionSwitchScene::Load(obs_data_t *obj)
{
MacroAction::Load(obj);
SceneSwitcherEntry::load(obj);
_duration.Load(obj);
return true;
}
std::string MacroActionSwitchScene::GetShortDesc()
{
if (targetType == SwitchTargetType::Scene && scene) {
return GetWeakSourceName(scene);
}
if (targetType == SwitchTargetType::SceneGroup && group) {
return group->name;
}
return "";
}
MacroActionSwitchSceneEdit::MacroActionSwitchSceneEdit(
QWidget *parent, std::shared_ptr<MacroActionSwitchScene> entryData)
: SwitchWidget(parent, entryData.get(), true, true)
{
_duration = new DurationSelection(parent, false);
QWidget::connect(_duration, SIGNAL(DurationChanged(double)), this,
SLOT(DurationChanged(double)));
QWidget::connect(scenes, SIGNAL(currentTextChanged(const QString &)),
this, SLOT(ChangeHeaderInfo(const QString &)));
QHBoxLayout *mainLayout = new QHBoxLayout;
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
{"{{scenes}}", scenes},
{"{{transitions}}", transitions},
{"{{duration}}", _duration},
};
placeWidgets(obs_module_text("AdvSceneSwitcher.action.scene.entry"),
mainLayout, widgetPlaceholders);
setLayout(mainLayout);
_entryData = entryData;
_duration->SetDuration(_entryData->_duration);
SwitchWidget::loading = false;
}
void MacroActionSwitchSceneEdit::DurationChanged(double seconds)
{
if (SwitchWidget::loading || !_entryData) {
return;
}
std::lock_guard<std::mutex> lock(switcher->m);
_entryData->_duration.seconds = seconds;
}
void MacroActionSwitchSceneEdit::ChangeHeaderInfo(const QString &)
{
emit HeaderInfoChanged(
QString::fromStdString(_entryData->GetShortDesc()));
}

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